Skip to content

Instantly share code, notes, and snippets.

@chadrik
Last active August 8, 2017 22:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chadrik/54d7b3d051839f90983676a47368526b to your computer and use it in GitHub Desktop.
Save chadrik/54d7b3d051839f90983676a47368526b to your computer and use it in GitHub Desktop.
from __future__ import absolute_import, print_function
from typing import Generic
# MRO code adapted from https://gist.github.com/pendulm/2271366
def head(l):
return l[0]
def tail(l):
return l[1:]
def is_not_in_tail(h, l):
if h in tail(l):
return False
return True
def remove_head(h, l):
if h is head(l):
l.remove(h)
return l
def merge(list_of_linearization):
for l in list_of_linearization:
h = head(l)
if all(is_not_in_tail(h, l) for l in list_of_linearization):
list_of_stripped = [remove_head(h, l) for l in list_of_linearization]
list_of_stripped = [i for i in list_of_stripped if len(i) != 0]
if len(list_of_stripped) == 0:
return [h]
return [h] + merge(list_of_stripped)
raise TypeError("Cannot create a consostent method resolution")
def mro(C):
if C is object:
return [object]
try:
bases = list(C.__orig_bases__)
except AttributeError:
bases = list(C.__bases__)
return [C] + merge([mro(b) for b in bases] + [bases])
def get_generic_type(klass, typevar):
"""
Given a Generic class, find the value for the given TypeVar.
Parameters
----------
klass : type
typevar : TypeVar
Returns
-------
Any
"""
for sub in mro(klass):
# we have an origin every time a generic class is indexed. e.g.:
# the origin of Foo[T, V] is Foo
origin = getattr(sub, '__origin__', None)
if origin is not None:
if origin is Generic:
# for some reason Generic does not record its args
args = sub.__parameters__
params = args
else:
# the args are the index arguments
args = sub.__args__
params = origin.__parameters__
try:
index = params.index(typevar)
except ValueError:
continue
else:
return args[index]
from __future__ import absolute_import, print_function
from typing import Generic, TypeVar
from runtime_typing import mro, get_generic_type
T = TypeVar('T')
U = TypeVar('U', bound=str)
V = TypeVar('V')
def print_generic_type_info(klass):
for sub in mro(klass):
print(sub)
print(" args: ", getattr(sub, '__args__', None))
print(" params:", getattr(sub, '__parameters__', None))
print(" origin:", getattr(sub, '__origin__', None))
class A(Generic[T]):
pass
IntA = A[int]
class SubIntA(IntA):
pass
class B(Generic[T, U]):
pass
class SubB(B[T, U]): # Note: `class SubB(B):` is not allowed
pass
class Mixin(object):
pass
class IntB(SubB[int, U], Mixin):
pass
class SubIntB(IntB):
pass
class SubIntStrB(IntB[str], Generic[V]):
pass
def test_types():
print_generic_type_info(IntA)
print()
print_generic_type_info(SubIntStrB)
# FIXME: typing was changed so that this no longer works:
# assert issubclass(str, T)
# assert issubclass(str, U)
# assert not issubclass(int, U)
assert get_generic_type(IntA, T) is int
assert get_generic_type(SubIntA, T) is int
assert get_generic_type(SubIntStrB, T) is int
assert get_generic_type(SubIntStrB, U) is str
assert get_generic_type(SubIntStrB, V) is V
assert get_generic_type(SubIntB, T) is int
assert get_generic_type(SubIntB, U) is U
assert get_generic_type(SubB, T) is T
assert get_generic_type(SubB, U) is U
assert get_generic_type(B, T) is T
assert get_generic_type(B, U) is U
# FIXME: not totally sure what the behavior should be here
assert get_generic_type(SubIntStrB, TypeVar('T')) is None
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment