Skip to content

Commit

Permalink
testing for classes
Browse files Browse the repository at this point in the history
  • Loading branch information
InvincibleRMC committed Jul 22, 2024
1 parent 0b09116 commit 95b5ed8
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
8 changes: 7 additions & 1 deletion mypy/stubgenc.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
ClassInfo,
FunctionContext,
SignatureGenerator,
generate_inline_generic,
infer_method_arg_types,
infer_method_ret_type,
)
Expand Down Expand Up @@ -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}] = ...")
Expand Down Expand Up @@ -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.
Expand Down
30 changes: 29 additions & 1 deletion mypy/stubutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}]"
16 changes: 15 additions & 1 deletion mypy/test/teststubgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 95b5ed8

Please sign in to comment.