import functools
##
def typecheck(f):
def do_typecheck(name, arg):
trait = f.__annotations__.get(name, None)
if trait and not trait.is_satisfied(arg):
raise RuntimeError("{} is {} which does not satisfy trait {}".format(name, type(arg).__name__, trait.name))
@functools.wraps(f)
def wrapper(*args, **kwargs):
for i, arg in enumerate(args[:f.__code__.co_nlocals]):
do_typecheck(f.__code__.co_varnames[i], arg)
for name, arg in kwargs.items():
do_typecheck(name, arg)
result = f(*args, **kwargs)
do_typecheck('return', result)
return result
return wrapper
##
class Trait:
def __init__(self, name, methods=None):
self.name = name
self.methods = [] if methods is None else methods
def is_satisfied(self, value):
return all(hasattr(value, method) for method in self.methods)
num = Trait("num", ["__add__", "__sub__", "__mul__", "__truediv__"])
##
@typecheck
def gcd(a: num, b: num) -> num:
#Obviously, not the GCD
return a / b
try:
gcd(5, 6)
gcd(5, "hello")
except RuntimeError as e:
print(str(e))
aW1wb3J0IGZ1bmN0b29scwoKIyMKCmRlZiB0eXBlY2hlY2soZik6CiAgICBkZWYgZG9fdHlwZWNoZWNrKG5hbWUsIGFyZyk6CiAgICAgICAgdHJhaXQgPSBmLl9fYW5ub3RhdGlvbnNfXy5nZXQobmFtZSwgTm9uZSkKICAgICAgICBpZiB0cmFpdCBhbmQgbm90IHRyYWl0LmlzX3NhdGlzZmllZChhcmcpOgogICAgICAgICAgICByYWlzZSBSdW50aW1lRXJyb3IoInt9IGlzIHt9IHdoaWNoIGRvZXMgbm90IHNhdGlzZnkgdHJhaXQge30iLmZvcm1hdChuYW1lLCB0eXBlKGFyZykuX19uYW1lX18sIHRyYWl0Lm5hbWUpKQogICAgICAgIAogICAgQGZ1bmN0b29scy53cmFwcyhmKQogICAgZGVmIHdyYXBwZXIoKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICBmb3IgaSwgYXJnIGluIGVudW1lcmF0ZShhcmdzWzpmLl9fY29kZV9fLmNvX25sb2NhbHNdKToKICAgICAgICAgICAgZG9fdHlwZWNoZWNrKGYuX19jb2RlX18uY29fdmFybmFtZXNbaV0sIGFyZykKICAgICAgICBmb3IgbmFtZSwgYXJnIGluIGt3YXJncy5pdGVtcygpOgogICAgICAgICAgICBkb190eXBlY2hlY2sobmFtZSwgYXJnKQoKICAgICAgICByZXN1bHQgPSBmKCphcmdzLCAqKmt3YXJncykKCiAgICAgICAgZG9fdHlwZWNoZWNrKCdyZXR1cm4nLCByZXN1bHQpCiAgICAgICAgcmV0dXJuIHJlc3VsdAogICAgICAgIAogICAgcmV0dXJuIHdyYXBwZXIKCiMjCgpjbGFzcyBUcmFpdDoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBuYW1lLCBtZXRob2RzPU5vbmUpOgogICAgICAgIHNlbGYubmFtZSA9IG5hbWUKICAgICAgICBzZWxmLm1ldGhvZHMgPSBbXSBpZiBtZXRob2RzIGlzIE5vbmUgZWxzZSBtZXRob2RzCgogICAgZGVmIGlzX3NhdGlzZmllZChzZWxmLCB2YWx1ZSk6CiAgICAgICAgcmV0dXJuIGFsbChoYXNhdHRyKHZhbHVlLCBtZXRob2QpIGZvciBtZXRob2QgaW4gc2VsZi5tZXRob2RzKQoKbnVtID0gVHJhaXQoIm51bSIsIFsiX19hZGRfXyIsICJfX3N1Yl9fIiwgIl9fbXVsX18iLCAiX190cnVlZGl2X18iXSkKCiMjCgpAdHlwZWNoZWNrCmRlZiBnY2QoYTogbnVtLCBiOiBudW0pIC0+IG51bToKICAgICNPYnZpb3VzbHksIG5vdCB0aGUgR0NECiAgICByZXR1cm4gYSAvIGIKCnRyeToKICAgIGdjZCg1LCA2KQogICAgZ2NkKDUsICJoZWxsbyIpCiAgICAKZXhjZXB0IFJ1bnRpbWVFcnJvciBhcyBlOgogICAgcHJpbnQoc3RyKGUpKQ==