44 lines
1.5 KiB
Python
44 lines
1.5 KiB
Python
|
from __future__ import annotations
|
||
|
|
||
|
from typing import TypeVar
|
||
|
|
||
|
from .._compat import get_args, get_origin, is_generic
|
||
|
|
||
|
|
||
|
def generate_mapping(cl: type, old_mapping: dict[str, type] = {}) -> dict[str, type]:
|
||
|
mapping = {}
|
||
|
|
||
|
origin = get_origin(cl)
|
||
|
|
||
|
if origin is not None:
|
||
|
# To handle the cases where classes in the typing module are using
|
||
|
# the GenericAlias structure but aren't a Generic and hence
|
||
|
# end up in this function but do not have an `__parameters__`
|
||
|
# attribute. These classes are interface types, for example
|
||
|
# `typing.Hashable`.
|
||
|
parameters = getattr(get_origin(cl), "__parameters__", None)
|
||
|
if parameters is None:
|
||
|
return dict(old_mapping)
|
||
|
|
||
|
for p, t in zip(parameters, get_args(cl)):
|
||
|
if isinstance(t, TypeVar):
|
||
|
continue
|
||
|
mapping[p.__name__] = t
|
||
|
|
||
|
if not mapping:
|
||
|
return dict(old_mapping)
|
||
|
elif is_generic(cl):
|
||
|
# Origin is None, so this may be a subclass of a generic class.
|
||
|
orig_bases = cl.__orig_bases__
|
||
|
for base in orig_bases:
|
||
|
if not hasattr(base, "__args__"):
|
||
|
continue
|
||
|
base_args = base.__args__
|
||
|
if not hasattr(base.__origin__, "__parameters__"):
|
||
|
continue
|
||
|
base_params = base.__origin__.__parameters__
|
||
|
for param, arg in zip(base_params, base_args):
|
||
|
mapping[param.__name__] = arg
|
||
|
|
||
|
return mapping
|