Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from typing import Callable, Any, TypeVar, Union, Tuple, Dict, Set, cast, Container, List
- from inspect import signature
- class TypedAny:
- """
- Basic class for checking type of objects. Accept any given object.
- """
- def __instancecheck__(self, instance: Any) -> Any:
- return isinstance(instance, object)
- def __repr__(self) -> Any:
- return 'TypedAny'
- AcceptedTypes = Union[type, TypedAny]
- class TypedUnion(TypedAny):
- """
- Accepts types from fixed list.
- """
- def __init__(self, types: List[AcceptedTypes]):
- self.types_ = types
- def __repr__(self) -> Any:
- return 'TypedUnion'
- def __instancecheck__(self, instance: Any) -> Any:
- return isinstance(instance, cast(type, self.types_))
- class TypedContainer(TypedAny):
- """
- Accepts containers with elements of given type.
- """
- def __init__(self, my_container: AcceptedTypes, my_types: AcceptedTypes) -> None:
- self.container_ = my_container
- self.types_ = my_types
- def __repr__(self) -> Any:
- return 'TypedContainer' + str(self.types_)
- def __instancecheck__(self, instance: Any) -> Any:
- if not isinstance(instance, cast(type, self.container_)):
- return False
- return isinstance(instance, Container)
- class TypedList(TypedContainer):
- """
- Accepts lists with elements of given type.
- """
- def __init__(self, t: TypedAny) -> None:
- super().__init__(List, t)
- self.types_ = t
- def __repr__(self) -> Any:
- return 'TypedList' + str(self.types_)
- def __instancecheck__(self, instance: Any) -> Any:
- if not super().__instancecheck__(instance):
- return False
- for item in instance:
- if not isinstance(item, cast(type, self.types_)):
- return False
- return True
- class TypedTuple(TypedContainer):
- """
- Accepts tuples with elements of given type.
- """
- def __init__(self, t: TypedAny) -> None:
- super().__init__(cast(type, Tuple), t)
- self.types_ = t
- def __repr__(self) -> Any:
- return 'TypedTuple' + str(self.types_)
- def __instancecheck__(self, instance: Any) -> Any:
- if not super().__instancecheck__(instance):
- return False
- for item in instance:
- if not isinstance(item, cast(type, self.types_)):
- return False
- return True
- class TypedDict(TypedContainer):
- """
- Accepts dicts with keys and values of given types.
- """
- def __init__(self, k: TypedAny, v: TypedAny) -> None:
- super().__init__(Dict, k)
- self.type_key = k
- self.type_item = v
- def __repr__(self) -> Any:
- return 'TypedDict' + str(self.type_key) + str(self.type_item)
- def __instancecheck__(self, instance: Any) -> Any:
- if not super().__instancecheck__(instance):
- return False
- for item in instance:
- if not isinstance(item, cast(type, self.type_key)) or \
- not isinstance(instance[item], cast(type, self.type_item)):
- return False
- return True
- class TypedSet(TypedContainer):
- """
- Accepts sets with elements of given type.
- """
- def __init__(self, t: AcceptedTypes) -> None:
- super().__init__(Set, t)
- self.type_ = t
- def __repr__(self) -> Any:
- return 'TypedSet' + str(self.type_)
- def __instancecheck__(self, instance: Any) -> Any:
- if not super().__instancecheck__(instance):
- return False
- for item in instance:
- if not isinstance(item, cast(type, self.types_)):
- return False
- return True
- class TypedCallable(TypedAny):
- """
- Accepts callable arguments.
- """
- def __init__(self, t: TypedAny) -> None:
- self.funcs = t
- def __repr__(self) -> Any:
- return 'TypedCallable' + str(self.funcs)
- def __instancecheck__(self, instance: Any) -> Any:
- return isinstance(instance, cast(type, Callable))
- class TypedAnnotatedCallable(TypedCallable):
- """
- Accepts callable arguments with annotated arguments.
- """
- __types__: List[AcceptedTypes]
- def __init__(self, types: List[AcceptedTypes]) -> None:
- self.__types__ = types
- def __repr__(self) -> Any:
- return 'TypedAnnotatedCallable' + str(self.__types__)
- def __instancecheck__(self, instance: Any) -> Any:
- list_1 = [str(item) for item in instance.__types__]
- list_2 = [str(item) for item in self.__types__]
- return super().__instancecheck__(instance) and list_1 == list_2
- Function = TypeVar('Function', bound=Callable[..., Any])
- def annotated_func(arg: List[AcceptedTypes]) -> Callable[[Function], Function]:
- """
- Decorator for annotating function arguments
- :param args: list of types to annotate functions.
- If any element of args is not subclass of type or TypedAny TypeError must be thrown.
- :return: decorator that will set __types__ attribute of passed function to args.
- Returned decorator must throw TypeError for functions with quantity of arguments not equal to len(args).
- """
- def simple_decotator(func: Function) -> Function:
- for type_ in arg:
- if not isinstance(type_, cast(type, TypedAny())):
- raise TypeError
- setattr(cast(TypedAnnotatedCallable, func), '__types__', arg)
- if len(signature(func).parameters) != len(arg):
- raise TypeError
- return func
- return simple_decotator
- def type_check(func: Function) -> Any:
- """
- Check types of arguments for annotated func in runtime.
- :param func: callable which must be annotated by annotated_func decorator
- :return: wrapped function which will check types of arguments passed to func.
- Must throw TypeError for any inconcistency.
- """
- list_args = getattr(func, '__types__')
- def decorator(*args: List[TypedAny]) -> Any:
- for type_, args_ in zip(args, list_args):
- if not isinstance(type_, args_):
- raise TypeError
- return func(*args)
- return decorator
- @type_check
- @annotated_func([float, float, TypedSet(int)])
- def hello_func(x, y, z):
- print("Hello! Arguments are {0}, {1}, {2}.".format(x, y, z))
- print("useless")
- print(hello_func(1.1, 2.1, {7}))
- print("+++")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement