Advertisement
Guest User

Untitled

a guest
Oct 20th, 2019
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.46 KB | None | 0 0
  1. from typing import Callable, Any, TypeVar, Union, Tuple, Dict, Set, cast, Container, List
  2. from inspect import signature
  3.  
  4.  
  5. class TypedAny:
  6.     """
  7.    Basic class for checking type of objects. Accept any given object.
  8.    """
  9.  
  10.     def __instancecheck__(self, instance: Any) -> Any:
  11.         return isinstance(instance, object)
  12.  
  13.     def __repr__(self) -> Any:
  14.         return 'TypedAny'
  15.  
  16.  
  17. AcceptedTypes = Union[type, TypedAny]
  18.  
  19.  
  20. class TypedUnion(TypedAny):
  21.     """
  22.    Accepts types from fixed list.
  23.    """
  24.  
  25.     def __init__(self, types: List[AcceptedTypes]):
  26.         self.types_ = types
  27.  
  28.     def __repr__(self) -> Any:
  29.         return 'TypedUnion'
  30.  
  31.     def __instancecheck__(self, instance: Any) -> Any:
  32.         return isinstance(instance, cast(type, self.types_))
  33.  
  34.  
  35. class TypedContainer(TypedAny):
  36.     """
  37.    Accepts containers with elements of given type.
  38.    """
  39.  
  40.     def __init__(self, my_container: AcceptedTypes, my_types: AcceptedTypes) -> None:
  41.         self.container_ = my_container
  42.         self.types_ = my_types
  43.  
  44.     def __repr__(self) -> Any:
  45.         return 'TypedContainer' + str(self.types_)
  46.  
  47.     def __instancecheck__(self, instance: Any) -> Any:
  48.         if not isinstance(instance, cast(type, self.container_)):
  49.             return False
  50.         return isinstance(instance, Container)
  51.  
  52.  
  53. class TypedList(TypedContainer):
  54.     """
  55.    Accepts lists with elements of given type.
  56.    """
  57.  
  58.     def __init__(self, t: TypedAny) -> None:
  59.         super().__init__(List, t)
  60.         self.types_ = t
  61.  
  62.     def __repr__(self) -> Any:
  63.         return 'TypedList' + str(self.types_)
  64.  
  65.     def __instancecheck__(self, instance: Any) -> Any:
  66.         if not super().__instancecheck__(instance):
  67.             return False
  68.  
  69.         for item in instance:
  70.             if not isinstance(item, cast(type, self.types_)):
  71.                 return False
  72.         return True
  73.  
  74.  
  75. class TypedTuple(TypedContainer):
  76.     """
  77.    Accepts tuples with elements of given type.
  78.    """
  79.  
  80.     def __init__(self, t: TypedAny) -> None:
  81.         super().__init__(cast(type, Tuple), t)
  82.         self.types_ = t
  83.  
  84.     def __repr__(self) -> Any:
  85.         return 'TypedTuple' + str(self.types_)
  86.  
  87.     def __instancecheck__(self, instance: Any) -> Any:
  88.         if not super().__instancecheck__(instance):
  89.             return False
  90.  
  91.         for item in instance:
  92.             if not isinstance(item, cast(type, self.types_)):
  93.                 return False
  94.         return True
  95.  
  96.  
  97. class TypedDict(TypedContainer):
  98.     """
  99.    Accepts dicts with keys and values of given types.
  100.    """
  101.  
  102.     def __init__(self, k: TypedAny, v: TypedAny) -> None:
  103.         super().__init__(Dict, k)
  104.         self.type_key = k
  105.         self.type_item = v
  106.  
  107.     def __repr__(self) -> Any:
  108.         return 'TypedDict' + str(self.type_key) + str(self.type_item)
  109.  
  110.     def __instancecheck__(self, instance: Any) -> Any:
  111.         if not super().__instancecheck__(instance):
  112.             return False
  113.         for item in instance:
  114.             if not isinstance(item, cast(type, self.type_key)) or \
  115.                     not isinstance(instance[item], cast(type, self.type_item)):
  116.                 return False
  117.         return True
  118.  
  119.  
  120. class TypedSet(TypedContainer):
  121.     """
  122.    Accepts sets with elements of given type.
  123.    """
  124.  
  125.     def __init__(self, t: AcceptedTypes) -> None:
  126.         super().__init__(Set, t)
  127.         self.type_ = t
  128.  
  129.     def __repr__(self) -> Any:
  130.         return 'TypedSet' + str(self.type_)
  131.  
  132.     def __instancecheck__(self, instance: Any) -> Any:
  133.         if not super().__instancecheck__(instance):
  134.             return False
  135.  
  136.         for item in instance:
  137.             if not isinstance(item, cast(type, self.types_)):
  138.                 return False
  139.         return True
  140.  
  141.  
  142. class TypedCallable(TypedAny):
  143.     """
  144.    Accepts callable arguments.
  145.    """
  146.     def __init__(self, t: TypedAny) -> None:
  147.         self.funcs = t
  148.  
  149.     def __repr__(self) -> Any:
  150.         return 'TypedCallable' + str(self.funcs)
  151.  
  152.     def __instancecheck__(self, instance: Any) -> Any:
  153.         return isinstance(instance, cast(type, Callable))
  154.  
  155.  
  156. class TypedAnnotatedCallable(TypedCallable):
  157.     """
  158.    Accepts callable arguments with annotated arguments.
  159.    """
  160.     __types__: List[AcceptedTypes]
  161.  
  162.     def __init__(self, types: List[AcceptedTypes]) -> None:
  163.         self.__types__ = types
  164.  
  165.     def __repr__(self) -> Any:
  166.         return 'TypedAnnotatedCallable' + str(self.__types__)
  167.  
  168.     def __instancecheck__(self, instance: Any) -> Any:
  169.         list_1 = [str(item) for item in instance.__types__]
  170.         list_2 = [str(item) for item in self.__types__]
  171.         return super().__instancecheck__(instance) and list_1 == list_2
  172.  
  173.  
  174. Function = TypeVar('Function', bound=Callable[..., Any])
  175.  
  176.  
  177. def annotated_func(arg: List[AcceptedTypes]) -> Callable[[Function], Function]:
  178.     """
  179.    Decorator for annotating function arguments
  180.    :param args: list of types to annotate functions.
  181.                 If any element of args is not subclass of type or TypedAny TypeError must be thrown.
  182.    :return: decorator that will set __types__ attribute of passed function to args.
  183.             Returned decorator must throw TypeError for functions with quantity of arguments not equal to len(args).
  184.    """
  185.  
  186.     def simple_decotator(func: Function) -> Function:
  187.         for type_ in arg:
  188.             if not isinstance(type_, cast(type, TypedAny())):
  189.                 raise TypeError
  190.  
  191.         setattr(cast(TypedAnnotatedCallable, func), '__types__', arg)
  192.         if len(signature(func).parameters) != len(arg):
  193.             raise TypeError
  194.         return func
  195.  
  196.     return simple_decotator
  197.  
  198.  
  199. def type_check(func: Function) -> Any:
  200.     """
  201.    Check types of arguments for annotated func in runtime.
  202.    :param func: callable which must be annotated by annotated_func decorator
  203.    :return: wrapped function which will check types of arguments passed to func.
  204.            Must throw TypeError for any inconcistency.
  205.    """
  206.     list_args = getattr(func, '__types__')
  207.  
  208.     def decorator(*args: List[TypedAny]) -> Any:
  209.         for type_, args_ in zip(args, list_args):
  210.             if not isinstance(type_, args_):
  211.                 raise TypeError
  212.  
  213.         return func(*args)
  214.  
  215.     return decorator
  216.  
  217.  
  218. @type_check
  219. @annotated_func([float, float, TypedSet(int)])
  220. def hello_func(x, y, z):
  221.     print("Hello! Arguments are {0}, {1}, {2}.".format(x, y, z))
  222.     print("useless")
  223.  
  224. print(hello_func(1.1, 2.1, {7}))
  225. print("+++")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement