fork(1) download
  1. import functools
  2.  
  3. ##
  4.  
  5. def typecheck(f):
  6. def do_typecheck(name, arg):
  7. trait = f.__annotations__.get(name, None)
  8. if trait and not trait.is_satisfied(arg):
  9. raise RuntimeError("{} is {} which does not satisfy trait {}".format(name, type(arg).__name__, trait.name))
  10.  
  11. @functools.wraps(f)
  12. def wrapper(*args, **kwargs):
  13. for i, arg in enumerate(args[:f.__code__.co_nlocals]):
  14. do_typecheck(f.__code__.co_varnames[i], arg)
  15. for name, arg in kwargs.items():
  16. do_typecheck(name, arg)
  17.  
  18. result = f(*args, **kwargs)
  19.  
  20. do_typecheck('return', result)
  21. return result
  22.  
  23. return wrapper
  24.  
  25. ##
  26.  
  27. class Trait:
  28. def __init__(self, name, methods=None):
  29. self.name = name
  30. self.methods = [] if methods is None else methods
  31.  
  32. def is_satisfied(self, value):
  33. return all(hasattr(value, method) for method in self.methods)
  34.  
  35. num = Trait("num", ["__add__", "__sub__", "__mul__", "__truediv__"])
  36.  
  37. ##
  38.  
  39. @typecheck
  40. def gcd(a: num, b: num) -> num:
  41. #Obviously, not the GCD
  42. return a / b
  43.  
  44. try:
  45. gcd(5, 6)
  46. gcd(5, "hello")
  47.  
  48. except RuntimeError as e:
  49. print(str(e))
Success #stdin #stdout 0.03s 9072KB
stdin
Standard input is empty
stdout
b is str which does not satisfy trait num