Skip to content

Commit eb1ec91

Browse files
committed
actual fix for ReifiedGeneric with pyright
1 parent d76500b commit eb1ec91

File tree

2 files changed

+23
-25
lines changed

2 files changed

+23
-25
lines changed

basedtyping/__init__.py

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
cast,
1919
)
2020

21-
from typing_extensions import Final, Self, TypeAlias, TypeGuard
21+
from typing_extensions import Final, TypeAlias, TypeGuard
2222

2323
from basedtyping.runtime_only import OldUnionType
2424

@@ -109,7 +109,7 @@ class NotEnoughTypeParametersError(ReifiedGenericError):
109109
"""
110110

111111

112-
class _ReifiedGenericMetaclass(type, Generic[T]):
112+
class _ReifiedGenericMetaclass(type):
113113
# these should really only be on the class not the metaclass, but since it needs to be accessible from both instances and the class itself, its duplicated here
114114

115115
__reified_generics__: Tuple[type, ...]
@@ -125,7 +125,7 @@ class _ReifiedGenericMetaclass(type, Generic[T]):
125125
_can_do_instance_and_subclass_checks_without_generics: bool
126126
"""Used internally for ``isinstance`` and ``issubclass`` checks, ``True`` when the class can currenty be used in said checks without generics in them"""
127127

128-
def _orig_class(cls) -> _ReifiedGenericMetaclass[T]:
128+
def _orig_class(cls) -> _ReifiedGenericMetaclass:
129129
"""Gets the original class that ``ReifiedGeneric.__class_getitem__`` copied from
130130
"""
131131
result = cls.__bases__[0]
@@ -175,7 +175,7 @@ def _check_generics_reified(cls) -> None:
175175
if not cls._generics_are_reified() or cls._has_non_reified_type_vars():
176176
cls._raise_generics_not_reified()
177177

178-
def _is_subclass(cls, subclass: object) -> TypeGuard[_ReifiedGenericMetaclass[T]]:
178+
def _is_subclass(cls, subclass: object) -> TypeGuard[_ReifiedGenericMetaclass]:
179179
"""For ``__instancecheck__`` and ``__subclasscheck__``. checks whether the
180180
"origin" type (ie. without the generics) is a subclass of this reified generic
181181
"""
@@ -188,7 +188,7 @@ def _is_subclass(cls, subclass: object) -> TypeGuard[_ReifiedGenericMetaclass[T]
188188
cls._orig_class(),
189189
# https://github.com/python/mypy/issues/11671
190190
cast( # pylint:disable=protected-access
191-
_ReifiedGenericMetaclass[T], subclass
191+
_ReifiedGenericMetaclass, subclass
192192
)._orig_class(),
193193
)
194194

@@ -220,40 +220,35 @@ def __instancecheck__(cls, instance: object) -> bool:
220220
cast(ReifiedGeneric[object], instance).__reified_generics__
221221
)
222222

223-
def __call__(cls, *args: object, **kwargs: object) -> T:
223+
# need the generic here for pyright. see https://github.com/microsoft/pyright/issues/5488
224+
def __call__(cls: type[T], *args: object, **kwargs: object) -> T:
224225
"""A placeholder ``__call__`` method that gets called when the class is
225226
instantiated directly, instead of first supplying the type parameters.
226227
"""
228+
cls_narrowed = cast(type[ReifiedGeneric[object]], cls)
227229
if (
228230
# instantiating a ReifiedGeneric without specifying any TypeVars
229-
not hasattr(cls, "_orig_type_vars")
231+
not hasattr(cls_narrowed, "_orig_type_vars")
230232
# instantiating a subtype of a ReifiedGeneric without specifying any TypeVars
231-
or cls._orig_type_vars == cls.__type_vars__
233+
or cls_narrowed._orig_type_vars == cls_narrowed.__type_vars__
232234
):
233235
raise NotReifiedError(
234-
f"Cannot instantiate ReifiedGeneric {cls.__name__!r} because its type"
235-
" parameters were not supplied. The type parameters must be explicitly"
236-
" specified in the instantiation so that the type data can be made"
237-
" available at runtime.\n\n"
238-
"For example:\n\n"
239-
"foo: Foo[int] = Foo() #wrong\n"
240-
"foo = Foo[T]() #wrong\n"
241-
"foo = Foo[int]() # correct"
236+
f"Cannot instantiate ReifiedGeneric {cls_narrowed.__name__!r} because"
237+
" its type parameters were not supplied. The type parameters must be"
238+
" explicitly specified in the instantiation so that the type data can"
239+
" be made available at runtime.\n\nFor example:\n\nfoo: Foo[int] ="
240+
" Foo() #wrong\nfoo = Foo[T]() #wrong\nfoo = Foo[int]() # correct"
242241
)
243-
cls._check_generics_reified()
244-
return cast(T, super().__call__(*args, **kwargs))
242+
cls_narrowed._check_generics_reified()
243+
# see comment about cls above
244+
return cast(T, super().__call__(*args, **kwargs)) # type:ignore[misc]
245245

246246

247247
GenericItems: TypeAlias = Union[type, TypeVar, Tuple[Union[type, TypeVar], ...]]
248248
"""The ``items`` argument passed to ``__class_getitem__`` when creating or using a ``Generic``"""
249249

250250

251-
class ReifiedGeneric(
252-
Generic[T],
253-
# mypy doesn't support metaclasses with generics but for pyright we need to correctly type the `__call__`
254-
# return type, otherwise all instances of `ReifiedGeneric` will have the wrong type
255-
metaclass=_ReifiedGenericMetaclass[Self], # type:ignore[misc]
256-
):
251+
class ReifiedGeneric(Generic[T], metaclass=_ReifiedGenericMetaclass):
257252
"""A ``Generic`` where the type parameters are available at runtime and is
258253
usable in ``isinstance`` and ``issubclass`` checks.
259254
@@ -316,7 +311,9 @@ def __class_getitem__( # type: ignore[no-any-decorated]
316311
orig_type_vars = (
317312
cls.__type_vars__
318313
if hasattr(cls, "__type_vars__")
319-
else cast(Tuple[TypeVar, ...], cls.__parameters__)
314+
else cast(
315+
Tuple[TypeVar, ...], cls.__parameters__ # type:ignore[attr-defined]
316+
)
320317
)
321318

322319
# add any reified generics from the superclass if there is one

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ combine_as_imports = true
3434
[tool.black]
3535
target-version = ["py37"]
3636
preview = true
37+
skip-magic-trailing-comma = true
3738

3839
[tool.pylint.MASTER]
3940
fail-on = "I"

0 commit comments

Comments
 (0)