KotlinIsland / KotlinIsland/basedtyping

proper type for the properties on `ReifiedGeneric`

Open
#39 0 comments 0 reactions 0 assignees View on GitHub
reified generics todo typetime
Dominant language
Python
Stars
12
Forks
2
PR merge metrics
No merged PRs in 30d

Description

ReifiedGenericCopy._can_do_instance_and_subclass_checks_without_generics = ( # pylint:disable=protected-access

https://github.com/KotlinIsland/basedtyping/blob/3945f0c5680f7e35feb1fb1587c0f9a9c7daeb9d/basedtyping/__init__.py#L295

```python

is tracked [here](https://github.com/KotlinIsland/basedmypy/issues/5)
"""

__reified_generics__: tuple[type, ...]
"""Should be a generic but cant due to https://github.com/KotlinIsland/basedmypy/issues/142"""
__type_vars__: tuple[TypeVar, ...]
"""``TypeVar``\\s that have not yet been reified. so this tuple should always be empty by the time the ``ReifiedGeneric`` is instantiated"""

@_tp_cache # type: ignore[name-defined,misc]
def __class_getitem__( # type: ignore[misc]
cls, item: GenericItems
) -> type[ReifiedGeneric[T]]:
# when defining the generic (ie. `class Foo(ReifiedGeneric[T]):`) we want the normal behavior
if cls is ReifiedGeneric:
# https://github.com/KotlinIsland/basedtypeshed/issues/7
return super().__class_getitem__(item) # type: ignore[misc,no-any-return]

items = item if isinstance(item, tuple) else (item,)

# if we're subtyping a class that already has reified generics:
superclass_reified_generics = tuple(
generic
for generic in (
cls.__reified_generics__ if hasattr(cls, "__reified_generics__") else ()
)
if not isinstance(generic, TypeVar) # type: ignore[misc]
)

# normal generics use __parameters__, we use __type_vars__ because the Generic base class deletes properties
# named __parameters__ when copying to a new class
orig_type_vars = (
cls.__type_vars__
if hasattr(cls, "__type_vars__")
else cast(
tuple[TypeVar, ...], cls.__parameters__ # type: ignore[attr-defined]
)
)

# add any reified generics from the superclass if there is one
items = superclass_reified_generics + items
expected_length = len(orig_type_vars)
actual_length = len(items) - len(superclass_reified_generics)
if expected_length != len(items) - len(superclass_reified_generics):
raise NotEnoughTypeParametersError(
"Incorrect number of type parameters specified. expected length:"
f" {expected_length}, actual length {actual_length}"
)
ReifiedGenericCopy: type[ReifiedGeneric[T]] = type(
cls.__name__,
(
cls, # make the copied class extend the original so normal instance checks work
),
# TODO: proper type
dict[str, object](
__reified_generics__=tuple(
_type_convert(t) for t in items # type: ignore[name-defined,misc]
),
_orig_type_vars=orig_type_vars,
__type_vars__=_collect_type_vars( # type: ignore[name-defined,misc]
items, cast(type, TypeVar)
),
),
)
# can't set it in the dict above otherwise __init_subclass__ overwrites it
ReifiedGenericCopy._can_do_instance_and_subclass_checks_without_generics = ( # pylint:disable=protected-access
False
)
return ReifiedGenericCopy

def __init_subclass__(cls) -> None: # pylint:disable=arguments-differ
cls._can_do_instance_and_subclass_checks_without_generics = True
super().__init_subclass__()

# TODO: make this work with any "form", not just unions

```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in basedtyping/__init__.py around the linked line, where ReifiedGeneric.__class_getitem__ constructs ReifiedGenericCopy with type(). Inspect the class namespace passed to type() and determine the proper type for the currently marked TODO. Done means the construction is accurately typed without that TODO, with the surrounding type-checking expectations still satisfied.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Refactor
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.