From 95b5ed85261902ee7413f5cdbe3af455d7e97978 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 22 Jul 2024 10:18:38 -0400 Subject: [PATCH 01/23] testing for classes --- mypy/stubgenc.py | 8 +++++++- mypy/stubutil.py | 30 +++++++++++++++++++++++++++++- mypy/test/teststubgen.py | 16 +++++++++++++++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 7ab500b4fe12..e5f7546f3ffd 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -33,6 +33,7 @@ ClassInfo, FunctionContext, SignatureGenerator, + generate_inline_generic, infer_method_arg_types, infer_method_ret_type, ) @@ -847,10 +848,15 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> else: attrs.append((attr, value)) + inline_generic = "" + for attr, value in attrs: if attr == "__hash__" and value is None: # special case for __hash__ continue + elif attr == "__type_params__": + inline_generic = generate_inline_generic(value) + 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}] = ...") @@ -882,7 +888,7 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> for line in ro_properties: output.append(line) else: - output.append(f"{self._indent}class {class_name}{bases_str}: ...") + output.append(f"{self._indent}class {class_name}{inline_generic}{bases_str}: ...") def generate_variable_stub(self, name: str, obj: object, output: list[str]) -> None: """Generate stub for a single variable using runtime introspection. diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 2f2db0dbbe53..665d46cdaa7c 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -8,7 +8,7 @@ from abc import abstractmethod from collections import defaultdict from contextlib import contextmanager -from typing import Final, Iterable, Iterator, Mapping +from typing import Final, Iterable, Iterator, Mapping, ParamSpec, TypeVar, TypeVarTuple, cast from typing_extensions import overload from mypy_extensions import mypyc_attr @@ -842,3 +842,31 @@ def should_reexport(self, name: str, full_module: str, name_is_alias: bool) -> b if self._all_: return name in self._all_ return True + + +def generate_inline_generic(type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]) -> str: + """Generate stub for inline generic from __type_params__""" + + generic_arg_list: list[str] = [] + + for type_param in type_params: + # Not done with isinstance checks so compiled code doesn't necessarily need to use + # typing.TypeVar, just something with similar duck typing. + if hasattr(type_param, "__constraints__"): + # Is TypeVar + typevar = cast(TypeVar, type_param) + if typevar.__bound__: + generic_arg_list.append(f'{typevar.__name__}: {typevar.__bound__}') + else: + generic_arg_list.append(f'{typevar.__name__}: {typevar.__constraints__}') + elif hasattr(type_param, "__bounds__"): + # Is ParamSpec + param_spec = cast(ParamSpec, type_param) + generic_arg_list.append(f'**{param_spec.__name__}') + else: + # Is TypeVarTuple + typevar_tuple = cast(TypeVarTuple, type_param) + generic_arg_list.append(f'*{typevar_tuple.__name__}') + + flat_internals = ",".join(generic_arg_list) + return f"[{flat_internals}]" diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index e65a16c8f395..47fb56f22260 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -8,7 +8,7 @@ import tempfile import unittest from types import ModuleType -from typing import Any +from typing import Any, ParamSpec, TypeVar, TypeVarTuple import pytest @@ -39,6 +39,7 @@ from mypy.stubutil import ( ClassInfo, common_dir_prefix, + generate_inline_generic, infer_method_ret_type, remove_misplaced_type_comments, walk_packages, @@ -824,6 +825,19 @@ 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_inline_generic(self) -> None: + T = TypeVar('T') + assert generate_inline_generic((T, )) == "[T]" + TBound = TypeVar('TBound', bound=int) + assert generate_inline_generic((TBound, )) == "[TBound: int]" + TBoundTuple = TypeVar('TBoundTuple', int, str) + assert generate_inline_generic((TBoundTuple, )) == "[TBoundTuple: (int, str)]" + P = ParamSpec('P') + assert generate_inline_generic((P, )) == "[**P]" + U = TypeVarTuple('U') + assert generate_inline_generic((U, )) == "[*U]" + assert generate_inline_generic((T, TBound, TBoundTuple, P, U)) =="[T, TBound: int, TBoundTuple: (int, str), **P, *U]" + def test_generate_class_stub_no_crash_for_object(self) -> None: output: list[str] = [] mod = ModuleType("module", "") # any module is fine From 1ed6c91184f6fb389dc0049e0cef59f8fb07365d Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 22 Jul 2024 11:07:42 -0400 Subject: [PATCH 02/23] works for classes --- mypy/stubgenc.py | 1 + mypy/stubutil.py | 53 +++++++++++++++++++++------------------- mypy/test/teststubgen.py | 46 +++++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 37 deletions(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index e5f7546f3ffd..68b81a25aa22 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -856,6 +856,7 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> continue elif attr == "__type_params__": inline_generic = generate_inline_generic(value) + continue prop_type_name = self.strip_or_import(self.get_type_annotation(value)) classvar = self.add_name("typing.ClassVar") diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 665d46cdaa7c..c24960b87328 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -8,8 +8,8 @@ from abc import abstractmethod from collections import defaultdict from contextlib import contextmanager -from typing import Final, Iterable, Iterator, Mapping, ParamSpec, TypeVar, TypeVarTuple, cast -from typing_extensions import overload +from typing import Final, Iterable, Iterator, Mapping, TypeVar, cast +from typing_extensions import ParamSpec, TypeVarTuple, overload from mypy_extensions import mypyc_attr @@ -845,28 +845,31 @@ def should_reexport(self, name: str, full_module: str, name_is_alias: bool) -> b def generate_inline_generic(type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]) -> str: - """Generate stub for inline generic from __type_params__""" - - generic_arg_list: list[str] = [] - - for type_param in type_params: - # Not done with isinstance checks so compiled code doesn't necessarily need to use - # typing.TypeVar, just something with similar duck typing. - if hasattr(type_param, "__constraints__"): - # Is TypeVar - typevar = cast(TypeVar, type_param) - if typevar.__bound__: - generic_arg_list.append(f'{typevar.__name__}: {typevar.__bound__}') - else: - generic_arg_list.append(f'{typevar.__name__}: {typevar.__constraints__}') - elif hasattr(type_param, "__bounds__"): - # Is ParamSpec - param_spec = cast(ParamSpec, type_param) - generic_arg_list.append(f'**{param_spec.__name__}') + """Generate stub for inline generic from __type_params__""" + + generic_arg_list: list[str] = [] + + for type_param in type_params: + # Not done with isinstance checks so compiled code doesn't necessarily need to use + # typing.TypeVar, just something with similar duck typing. + if hasattr(type_param, "__constraints__"): + # Is TypeVar + typevar = cast(TypeVar, type_param) + if typevar.__bound__: + generic_arg_list.append(f"{typevar.__name__}: {typevar.__bound__.__name__}") + elif typevar.__constraints__ != (): + generic_arg_list.append( + f"{typevar.__name__}: ({', '.join([constraint.__name__ for constraint in typevar.__constraints__])})" + ) else: - # Is TypeVarTuple - typevar_tuple = cast(TypeVarTuple, type_param) - generic_arg_list.append(f'*{typevar_tuple.__name__}') + generic_arg_list.append(f"{typevar.__name__}") + elif hasattr(type_param, "__bound__"): + # Is ParamSpec + param_spec = cast(ParamSpec, type_param) + generic_arg_list.append(f"**{param_spec.__name__}") + else: + # Is TypeVarTuple + generic_arg_list.append(f"*{type_param.__name__}") - flat_internals = ",".join(generic_arg_list) - return f"[{flat_internals}]" + flat_internals = ", ".join(generic_arg_list) + return f"[{flat_internals}]" diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 47fb56f22260..8aa2edbb6b98 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -8,7 +8,8 @@ import tempfile import unittest from types import ModuleType -from typing import Any, ParamSpec, TypeVar, TypeVarTuple +from typing import Any, TypeVar, cast +from typing_extensions import ParamSpec, TypeVarTuple import pytest @@ -826,17 +827,26 @@ def test_infer_cast_sig(self) -> None: assert_equal(infer_method_ret_type(f"__{op}__"), op) def test_generate_inline_generic(self) -> None: - T = TypeVar('T') - assert generate_inline_generic((T, )) == "[T]" - TBound = TypeVar('TBound', bound=int) - assert generate_inline_generic((TBound, )) == "[TBound: int]" - TBoundTuple = TypeVar('TBoundTuple', int, str) - assert generate_inline_generic((TBoundTuple, )) == "[TBoundTuple: (int, str)]" - P = ParamSpec('P') - assert generate_inline_generic((P, )) == "[**P]" - U = TypeVarTuple('U') - assert generate_inline_generic((U, )) == "[*U]" - assert generate_inline_generic((T, TBound, TBoundTuple, P, U)) =="[T, TBound: int, TBoundTuple: (int, str), **P, *U]" + T = TypeVar("T") + assert generate_inline_generic((T,)) == "[T]" + TBound = TypeVar("TBound", bound=int) + assert generate_inline_generic((TBound,)) == "[TBound: int]" + TBoundTuple = TypeVar("TBoundTuple", int, str) + assert generate_inline_generic((TBoundTuple,)) == "[TBoundTuple: (int, str)]" + P = ParamSpec("P") + p_tuple = cast(tuple[ParamSpec], (P,)) + assert generate_inline_generic(p_tuple) == "[**P]" + U = TypeVarTuple("U") + u_tuple = cast(tuple[TypeVarTuple], (U,)) + assert generate_inline_generic(u_tuple) == "[*U]" + all_tuple = cast( + tuple[TypeVar, TypeVar, TypeVar, ParamSpec, TypeVarTuple], + (T, TBound, TBoundTuple, P, U), + ) + assert ( + generate_inline_generic(all_tuple) + == "[T, TBound: int, TBoundTuple: (int, str), **P, *U]" + ) def test_generate_class_stub_no_crash_for_object(self) -> None: output: list[str] = [] @@ -920,6 +930,18 @@ class TestClass(argparse.Action): assert_equal(output, ["class C(argparse.Action): ..."]) assert_equal(gen.get_imports().splitlines(), ["import argparse"]) + def test_inline_generic_class(self) -> None: + T = TypeVar("T") + + class TestClass: + __type_params__ = (T,) + + output: list[str] = [] + mod = ModuleType("module", "") + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_class_stub("C", TestClass, output) + assert_equal(output, ["class C[T]: ..."]) + def test_generate_c_type_inheritance_builtin_type(self) -> None: class TestClass(type): pass From 1a197f7e8206dc963ac5ce11b06506a32869ae5c Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 22 Jul 2024 11:34:09 -0400 Subject: [PATCH 03/23] added support for inline functions --- mypy/stubdoc.py | 5 +-- mypy/stubgenc.py | 8 ++++- mypy/stubutil.py | 5 +++ mypy/test/teststubgen.py | 67 +++++++++++++++++++++++++++------------- 4 files changed, 60 insertions(+), 25 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 928d024514f3..f305715c4c25 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -108,6 +108,7 @@ def format_sig( is_async: bool = False, any_val: str | None = None, docstring: str | None = None, + generic: str = "" ) -> str: args: list[str] = [] for arg in self.args: @@ -141,8 +142,8 @@ 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 = "{indent}{prefix}def {name}{generic}({args}){ret}:".format( + indent=indent, prefix=prefix, name=self.name, args=", ".join(args), ret=retfield, generic=generic ) if docstring: suffix = f"\n{indent} {mypy.util.quote_docstring(docstring)}" diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 68b81a25aa22..09f709b8057c 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -647,7 +647,13 @@ def generate_function_stub( if docstring: docstring = self._indent_docstring(docstring) - output.extend(self.format_func_def(inferred, decorators=decorators, docstring=docstring)) + + if hasattr(obj, "__type_params__"): + generic = generate_inline_generic(obj.__type_params__) + else: + generic = "" + + output.extend(self.format_func_def(inferred, decorators=decorators, docstring=docstring, generic=generic)) self._fix_iter(ctx, inferred, output) def _indent_docstring(self, docstring: str) -> str: diff --git a/mypy/stubutil.py b/mypy/stubutil.py index c24960b87328..fe63acbe2421 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -766,6 +766,7 @@ def format_func_def( is_coroutine: bool = False, decorators: list[str] | None = None, docstring: str | None = None, + generic: str = "", ) -> list[str]: lines: list[str] = [] if decorators is None: @@ -781,6 +782,7 @@ def format_func_def( indent=self._indent, is_async=is_coroutine, docstring=docstring if self._include_docstrings else None, + generic=generic ) ) return lines @@ -847,6 +849,9 @@ def should_reexport(self, name: str, full_module: str, name_is_alias: bool) -> b def generate_inline_generic(type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]) -> str: """Generate stub for inline generic from __type_params__""" + if len(type_params) == 0: + return "" + generic_arg_list: list[str] = [] for type_param in type_params: diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 8aa2edbb6b98..75af33d8d213 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -614,6 +614,29 @@ 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_generate_inline_generic(self) -> None: + assert generate_inline_generic(()) == "" + T = TypeVar("T") + assert generate_inline_generic((T,)) == "[T]" + TBound = TypeVar("TBound", bound=int) + assert generate_inline_generic((TBound,)) == "[TBound: int]" + TBoundTuple = TypeVar("TBoundTuple", int, str) + assert generate_inline_generic((TBoundTuple,)) == "[TBoundTuple: (int, str)]" + P = ParamSpec("P") + p_tuple = cast(tuple[ParamSpec], (P,)) + assert generate_inline_generic(p_tuple) == "[**P]" + U = TypeVarTuple("U") + u_tuple = cast(tuple[TypeVarTuple], (U,)) + assert generate_inline_generic(u_tuple) == "[*U]" + all_tuple = cast( + tuple[TypeVar, TypeVar, TypeVar, ParamSpec, TypeVarTuple], + (T, TBound, TBoundTuple, P, U), + ) + assert ( + generate_inline_generic(all_tuple) + == "[T, TBound: int, TBoundTuple: (int, str), **P, *U]" + ) + class StubgenHelpersSuite(unittest.TestCase): def test_is_blacklisted_path(self) -> None: @@ -826,28 +849,6 @@ 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_inline_generic(self) -> None: - T = TypeVar("T") - assert generate_inline_generic((T,)) == "[T]" - TBound = TypeVar("TBound", bound=int) - assert generate_inline_generic((TBound,)) == "[TBound: int]" - TBoundTuple = TypeVar("TBoundTuple", int, str) - assert generate_inline_generic((TBoundTuple,)) == "[TBoundTuple: (int, str)]" - P = ParamSpec("P") - p_tuple = cast(tuple[ParamSpec], (P,)) - assert generate_inline_generic(p_tuple) == "[**P]" - U = TypeVarTuple("U") - u_tuple = cast(tuple[TypeVarTuple], (U,)) - assert generate_inline_generic(u_tuple) == "[*U]" - all_tuple = cast( - tuple[TypeVar, TypeVar, TypeVar, ParamSpec, TypeVarTuple], - (T, TBound, TBoundTuple, P, U), - ) - assert ( - generate_inline_generic(all_tuple) - == "[T, TBound: int, TBoundTuple: (int, str), **P, *U]" - ) - def test_generate_class_stub_no_crash_for_object(self) -> None: output: list[str] = [] mod = ModuleType("module", "") # any module is fine @@ -942,6 +943,28 @@ class TestClass: gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C[T]: ..."]) + def test_inline_generic_function(self) -> None: + T = TypeVar("T", bound=int) + class TestClass: + def test(self, arg0: T) -> T: + """ + test(self, arg0: T) -> T + """ + return arg0 + + test.__type_params__ = (T, ) + + output: list[str] = [] + mod = ModuleType(TestClass.__module__, "") + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_function_stub( + "test", + TestClass.test, + output=output, + class_info=ClassInfo(self_var="self", cls=TestClass, name="TestClass"), + ) + assert_equal(output, ["def test[T: int](self, arg0: T) -> T: ..."]) + def test_generate_c_type_inheritance_builtin_type(self) -> None: class TestClass(type): pass From 5b631cc210ff7af9d087820dadbb59c605262bca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:38:36 +0000 Subject: [PATCH 04/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/stubdoc.py | 9 +++++++-- mypy/stubgenc.py | 6 +++++- mypy/stubutil.py | 2 +- mypy/test/teststubgen.py | 3 ++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index f305715c4c25..561ab7d650d7 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -108,7 +108,7 @@ def format_sig( is_async: bool = False, any_val: str | None = None, docstring: str | None = None, - generic: str = "" + generic: str = "", ) -> str: args: list[str] = [] for arg in self.args: @@ -143,7 +143,12 @@ def format_sig( prefix = "async " if is_async else "" sig = "{indent}{prefix}def {name}{generic}({args}){ret}:".format( - indent=indent, prefix=prefix, name=self.name, args=", ".join(args), ret=retfield, generic=generic + indent=indent, + prefix=prefix, + name=self.name, + args=", ".join(args), + ret=retfield, + generic=generic, ) if docstring: suffix = f"\n{indent} {mypy.util.quote_docstring(docstring)}" diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 09f709b8057c..fb1612dbd447 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -653,7 +653,11 @@ def generate_function_stub( else: generic = "" - output.extend(self.format_func_def(inferred, decorators=decorators, docstring=docstring, generic=generic)) + output.extend( + self.format_func_def( + inferred, decorators=decorators, docstring=docstring, generic=generic + ) + ) self._fix_iter(ctx, inferred, output) def _indent_docstring(self, docstring: str) -> str: diff --git a/mypy/stubutil.py b/mypy/stubutil.py index fe63acbe2421..193c82c5c0c4 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -782,7 +782,7 @@ def format_func_def( indent=self._indent, is_async=is_coroutine, docstring=docstring if self._include_docstrings else None, - generic=generic + generic=generic, ) ) return lines diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 75af33d8d213..ccc0ec414f6f 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -945,6 +945,7 @@ class TestClass: def test_inline_generic_function(self) -> None: T = TypeVar("T", bound=int) + class TestClass: def test(self, arg0: T) -> T: """ @@ -952,7 +953,7 @@ def test(self, arg0: T) -> T: """ return arg0 - test.__type_params__ = (T, ) + test.__type_params__ = (T,) output: list[str] = [] mod = ModuleType(TestClass.__module__, "") From 7e07a995d58778812f0dc7f63e923f9645ef6fc7 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 22 Jul 2024 11:43:13 -0400 Subject: [PATCH 05/23] add version guard --- mypy/test/teststubgen.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index ccc0ec414f6f..4a622f57fb7f 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -931,6 +931,9 @@ class TestClass(argparse.Action): assert_equal(output, ["class C(argparse.Action): ..."]) assert_equal(gen.get_imports().splitlines(), ["import argparse"]) + @unittest.skipIf( + sys.version < (3, 12), "Inline Generics not supported before Python3.12" + ) def test_inline_generic_class(self) -> None: T = TypeVar("T") @@ -943,6 +946,9 @@ class TestClass: gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C[T]: ..."]) + @unittest.skipIf( + sys.version < (3, 12), "Inline Generics not supported before Python3.12" + ) def test_inline_generic_function(self) -> None: T = TypeVar("T", bound=int) From a2d2328e40530a8a6d103ef2736c12339794e11d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:43:41 +0000 Subject: [PATCH 06/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/test/teststubgen.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 4a622f57fb7f..f78c2dadf76d 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -931,9 +931,7 @@ class TestClass(argparse.Action): assert_equal(output, ["class C(argparse.Action): ..."]) assert_equal(gen.get_imports().splitlines(), ["import argparse"]) - @unittest.skipIf( - sys.version < (3, 12), "Inline Generics not supported before Python3.12" - ) + @unittest.skipIf(sys.version < (3, 12), "Inline Generics not supported before Python3.12") def test_inline_generic_class(self) -> None: T = TypeVar("T") @@ -946,9 +944,7 @@ class TestClass: gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C[T]: ..."]) - @unittest.skipIf( - sys.version < (3, 12), "Inline Generics not supported before Python3.12" - ) + @unittest.skipIf(sys.version < (3, 12), "Inline Generics not supported before Python3.12") def test_inline_generic_function(self) -> None: T = TypeVar("T", bound=int) From f351cdb81f121219fa8034797e2cc6af61080de3 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 22 Jul 2024 11:44:42 -0400 Subject: [PATCH 07/23] use version_info over version --- mypy/test/teststubgen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index f78c2dadf76d..8697085edb35 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -931,7 +931,7 @@ class TestClass(argparse.Action): assert_equal(output, ["class C(argparse.Action): ..."]) assert_equal(gen.get_imports().splitlines(), ["import argparse"]) - @unittest.skipIf(sys.version < (3, 12), "Inline Generics not supported before Python3.12") + @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_inline_generic_class(self) -> None: T = TypeVar("T") @@ -944,7 +944,7 @@ class TestClass: gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C[T]: ..."]) - @unittest.skipIf(sys.version < (3, 12), "Inline Generics not supported before Python3.12") + @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_inline_generic_function(self) -> None: T = TypeVar("T", bound=int) From 82ff3c27f9fff22f4e8045582d4a95ca708a6597 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 22 Jul 2024 12:21:10 -0400 Subject: [PATCH 08/23] if versino check inside function --- mypy/test/teststubgen.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 8697085edb35..e119e3fcf055 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -946,6 +946,13 @@ class TestClass: @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_inline_generic_function(self) -> None: + + if sys.version_info < ( + 3, + 12, + ): # Done to prevent mypy [attr-defined] error on __type_params__ in older versions of python + return + T = TypeVar("T", bound=int) class TestClass: From e2d5aa8b57117b7a3a5a0325492c9a85bb335975 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 22 Jul 2024 12:43:11 -0400 Subject: [PATCH 09/23] use python3.8 Tuple --- mypy/test/teststubgen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index e119e3fcf055..245191cf45f8 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -8,7 +8,7 @@ import tempfile import unittest from types import ModuleType -from typing import Any, TypeVar, cast +from typing import Any, Tuple, TypeVar, cast from typing_extensions import ParamSpec, TypeVarTuple import pytest @@ -623,13 +623,13 @@ def test_generate_inline_generic(self) -> None: TBoundTuple = TypeVar("TBoundTuple", int, str) assert generate_inline_generic((TBoundTuple,)) == "[TBoundTuple: (int, str)]" P = ParamSpec("P") - p_tuple = cast(tuple[ParamSpec], (P,)) + p_tuple = cast(Tuple[ParamSpec], (P,)) assert generate_inline_generic(p_tuple) == "[**P]" U = TypeVarTuple("U") - u_tuple = cast(tuple[TypeVarTuple], (U,)) + u_tuple = cast(Tuple[TypeVarTuple], (U,)) assert generate_inline_generic(u_tuple) == "[*U]" all_tuple = cast( - tuple[TypeVar, TypeVar, TypeVar, ParamSpec, TypeVarTuple], + Tuple[TypeVar, TypeVar, TypeVar, ParamSpec, TypeVarTuple], (T, TBound, TBoundTuple, P, U), ) assert ( From 1521c90bd84cdd0e215878317cbe3e17602c447c Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 22 Jul 2024 18:13:20 -0400 Subject: [PATCH 10/23] Add syntax validation --- mypy/test/teststubgen.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 245191cf45f8..4dc490926957 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -944,6 +944,10 @@ class TestClass: gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C[T]: ..."]) + @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") + def test_generic_class(self) -> None: + exec("class Test[A]: ...") + @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_inline_generic_function(self) -> None: From d0b6f2e015ec3fa1ff2f49f70dd66ad584231a36 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 22 Jul 2024 18:40:55 -0400 Subject: [PATCH 11/23] Add generic for class with properties --- mypy/stubgenc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index fb1612dbd447..6cd5135839b4 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -880,7 +880,7 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> 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}:") + output.append(f"{self._indent}class {class_name}{inline_generic}{bases_str}:") for line in types: if ( output From b4e40b5edce95d71875add23c99ddfd3e03b69af Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 09:24:34 -0400 Subject: [PATCH 12/23] Add rest of test case --- mypy/stubgenc.py | 7 +++++++ mypy/test/teststubgen.py | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 6cd5135839b4..d0df9309e495 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -11,6 +11,7 @@ import inspect import keyword import os.path +from contextlib import suppress from types import FunctionType, ModuleType from typing import Any, Callable, Mapping @@ -497,6 +498,8 @@ def is_skipped_attribute(self, attr: str) -> bool: "__firstlineno__", "__static_attributes__", "__annotate__", + "__orig_bases__", + "__parameters__", ) or attr in self.IGNORED_DUNDERS or is_pybind_skipped_attribute(attr) # For pickling @@ -875,6 +878,10 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> self.dedent() bases = self.get_base_types(cls) + if inline_generic != "": + # Removes typing.Generic form bases if it exists for python3.12 inline generics + with suppress(ValueError): + bases.remove("typing.Generic") if bases: bases_str = "(%s)" % ", ".join(bases) else: diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 4dc490926957..325c57d8ce21 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -948,6 +948,14 @@ class TestClass: def test_generic_class(self) -> None: exec("class Test[A]: ...") + class TestClass[A]: ... + + output: list[str] = [] + mod = ModuleType("module", "") + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_class_stub("C", TestClass, output) + assert_equal(output, ["class C[A]: ..."]) + @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_inline_generic_function(self) -> None: From e5ef50af53a5d4b7cc6431cebdaccbace3b524d7 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 09:30:58 -0400 Subject: [PATCH 13/23] add mypy guard --- mypy/test/teststubgen.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 325c57d8ce21..89cd6e2f2cb4 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -946,6 +946,12 @@ class TestClass: @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_generic_class(self) -> None: + if sys.version_info < ( + 3, + 12, + ): # Done to prevent mypy [syntax] error on inline Generics in older versions of python + return + exec("class Test[A]: ...") class TestClass[A]: ... From 7004be508b0e14eaa3a07994630ab0dc7fd28772 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 09:39:16 -0400 Subject: [PATCH 14/23] use type ignore --- mypy/test/teststubgen.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 89cd6e2f2cb4..afc41e0f69d6 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -946,15 +946,10 @@ class TestClass: @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_generic_class(self) -> None: - if sys.version_info < ( - 3, - 12, - ): # Done to prevent mypy [syntax] error on inline Generics in older versions of python - return - exec("class Test[A]: ...") - class TestClass[A]: ... + # type: ignore used for older versions of python type checking + class TestClass[A]: ... # type: ignore[invalid-syntax] output: list[str] = [] mod = ModuleType("module", "") From c4467bc3017abe5d1503da7ecd456824d786847a Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 09:40:51 -0400 Subject: [PATCH 15/23] fix type: ignore --- mypy/test/teststubgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index afc41e0f69d6..89684faa8ee2 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -949,7 +949,7 @@ def test_generic_class(self) -> None: exec("class Test[A]: ...") # type: ignore used for older versions of python type checking - class TestClass[A]: ... # type: ignore[invalid-syntax] + class TestClass[A]: ... # type: ignore[syntax] output: list[str] = [] mod = ModuleType("module", "") From 893cd540abe279a8f5ea087ea291bfb478912c9e Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 09:45:15 -0400 Subject: [PATCH 16/23] more general? --- mypy/test/teststubgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 89684faa8ee2..1cd463f41160 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -949,7 +949,7 @@ def test_generic_class(self) -> None: exec("class Test[A]: ...") # type: ignore used for older versions of python type checking - class TestClass[A]: ... # type: ignore[syntax] + class TestClass[A]: ... # type: ignore output: list[str] = [] mod = ModuleType("module", "") From 0bd3c1ebaac61efda4bcecacca73325d8d52aaf4 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 09:49:41 -0400 Subject: [PATCH 17/23] no_type_check --- mypy/test/teststubgen.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 1cd463f41160..b175f0b9fed6 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -8,7 +8,7 @@ import tempfile import unittest from types import ModuleType -from typing import Any, Tuple, TypeVar, cast +from typing import Any, Tuple, TypeVar, cast, no_type_check from typing_extensions import ParamSpec, TypeVarTuple import pytest @@ -944,12 +944,13 @@ class TestClass: gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C[T]: ..."]) + # type: ignore used for older versions of python type checking inline generics. + @no_type_check @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_generic_class(self) -> None: exec("class Test[A]: ...") - # type: ignore used for older versions of python type checking - class TestClass[A]: ... # type: ignore + class TestClass[A]: ... output: list[str] = [] mod = ModuleType("module", "") From 23c1a30c8ebef76c211529ae722c7d91f2898cfe Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 10:10:40 -0400 Subject: [PATCH 18/23] add enable --- mypy/test/teststubgen.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index b175f0b9fed6..84811a8b43ad 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -8,7 +8,7 @@ import tempfile import unittest from types import ModuleType -from typing import Any, Tuple, TypeVar, cast, no_type_check +from typing import Any, Tuple, TypeVar, cast from typing_extensions import ParamSpec, TypeVarTuple import pytest @@ -944,10 +944,9 @@ class TestClass: gen.generate_class_stub("C", TestClass, output) assert_equal(output, ["class C[T]: ..."]) - # type: ignore used for older versions of python type checking inline generics. - @no_type_check @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_generic_class(self) -> None: + # mypy: enable-incomplete-feature=NewGenericSyntax exec("class Test[A]: ...") class TestClass[A]: ... From 74027b714f716139c0ad54649013caf0e38a0765 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 10:39:17 -0400 Subject: [PATCH 19/23] use exec --- mypy/test/teststubgen.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 84811a8b43ad..41f93b7db45e 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -946,16 +946,17 @@ class TestClass: @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_generic_class(self) -> None: - # mypy: enable-incomplete-feature=NewGenericSyntax - exec("class Test[A]: ...") - - class TestClass[A]: ... - - output: list[str] = [] - mod = ModuleType("module", "") - gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) - gen.generate_class_stub("C", TestClass, output) - assert_equal(output, ["class C[A]: ..."]) + # This test lives in the exec block to avoid syntax version on python versions < 3.12 + code = """ +class Test[A]: ... + +output: list[str] = [] +mod = ModuleType("module", "") +gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) +gen.generate_class_stub("C", Test, output) +assert_equal(output, ["class C[A]: ..."]) + """ + exec(code) @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_inline_generic_function(self) -> None: From fb0e698ff6760d74f86add03a6ca9b55cc8d41b1 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 10:39:49 -0400 Subject: [PATCH 20/23] fix grammar --- mypy/test/teststubgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 41f93b7db45e..31f4e2d699a8 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -946,7 +946,7 @@ class TestClass: @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_generic_class(self) -> None: - # This test lives in the exec block to avoid syntax version on python versions < 3.12 + # This test lives in the exec block to avoid syntax errors on python versions < 3.12 code = """ class Test[A]: ... From b88917088f6047cdb7eed5ec3be2985fb33c82b7 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 11:12:11 -0400 Subject: [PATCH 21/23] simpler exec --- mypy/test/teststubgen.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 31f4e2d699a8..0e270eebfdf9 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -946,17 +946,15 @@ class TestClass: @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_generic_class(self) -> None: - # This test lives in the exec block to avoid syntax errors on python versions < 3.12 - code = """ -class Test[A]: ... - -output: list[str] = [] -mod = ModuleType("module", "") -gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) -gen.generate_class_stub("C", Test, output) -assert_equal(output, ["class C[A]: ..."]) - """ - exec(code) + # This class declaration lives in exec to avoid syntax version on python versions < 3.12 + local: dict[str, Any] = {} + exec("class Test[A]: ...", globals(), local) + + output: list[str] = [] + mod = ModuleType("module", "") + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.generate_class_stub("C", local['Test'], output) + assert_equal(output, ["class C[A]: ..."]) @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") def test_inline_generic_function(self) -> None: From 3c943dc22ee50b9a6f217546447670feb5a162b8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:12:44 +0000 Subject: [PATCH 22/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/test/teststubgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 0e270eebfdf9..086ac84fc558 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -953,7 +953,7 @@ def test_generic_class(self) -> None: output: list[str] = [] mod = ModuleType("module", "") gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) - gen.generate_class_stub("C", local['Test'], output) + gen.generate_class_stub("C", local["Test"], output) assert_equal(output, ["class C[A]: ..."]) @unittest.skipIf(sys.version_info < (3, 12), "Inline Generics not supported before Python3.12") From 53a59796e733bba81935cffc212bdbf266900309 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 23 Jul 2024 11:14:24 -0400 Subject: [PATCH 23/23] pass in None --- mypy/test/teststubgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 086ac84fc558..09e821fadb18 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -948,7 +948,7 @@ class TestClass: def test_generic_class(self) -> None: # This class declaration lives in exec to avoid syntax version on python versions < 3.12 local: dict[str, Any] = {} - exec("class Test[A]: ...", globals(), local) + exec("class Test[A]: ...", None, local) output: list[str] = [] mod = ModuleType("module", "")