Last active
December 20, 2022 21:37
-
-
Save TheArcherST/99e6a792b27ca0b00594d3a34212bc9a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Any, Dict, Union | |
from types import FunctionType | |
from inspect import getargs, getfullargspec | |
import functools | |
import warnings | |
class TypesCompilationWarning(Warning): | |
pass | |
class StrongType: | |
"""Strong funcs typing | |
>>> @StrongType | |
>>> def main(number: int, optional_string: str = 'default') -> None: | |
... pass | |
Now, if you try give args no right types into main, it cause TypeError | |
""" | |
def __init__(self, func: FunctionType): | |
if not self._is_func_annotated(func): | |
warnings.warn( | |
f'\n\nFunction {func} not full-annotated' | |
f'\nNo found annotations will be sat to typing.Any (default)\n', | |
TypesCompilationWarning, stacklevel=2 | |
) | |
func = self._repair_annotations(func) | |
functools.update_wrapper(self, func) # overwrite gave function | |
self._func: FunctionType = func | |
def __call__(self, *args, **kwargs): | |
""" Func call with types check """ | |
self._check_args_valid(args, kwargs) | |
return self._func(*args, **kwargs) | |
@staticmethod | |
def _is_func_annotated(func: FunctionType) -> bool: | |
""" Is all args and return in function annotated """ | |
func_args = getargs(func.__code__).args | |
result = len(func.__annotations__) - 1 == len(func_args) | |
return result | |
@staticmethod | |
def _repair_annotations(func: FunctionType) -> FunctionType: | |
""" Set empty func annotations on default: types.Any """ | |
func_args = getargs(func.__code__).args | |
annotated_args = func.__annotations__.keys() | |
for arg in func_args: | |
if arg not in annotated_args: | |
func.__annotations__.update({arg: Any}) | |
if 'return' not in annotated_args: | |
func.__annotations__.update({'return': Any}) | |
return func | |
def _check_args_valid(self, args: Union[tuple, list, Dict[str, Any]], kwargs: Dict[str, Any]): | |
if isinstance(args, (list, tuple)): | |
requested_args = getfullargspec(self._func).args # with kwargs, but zip cut it before len(args) | |
args = {key: value | |
for key, value in zip(requested_args, args)} | |
args.update(kwargs) # args now is args and kwargs union | |
del kwargs | |
args_types = {arg: type(value) for arg, value in args.items()} | |
for arg, type_ in args_types.items(): | |
if self._func.__annotations__[arg] is Any: | |
continue | |
if not issubclass(self._func.__annotations__[arg], type_): | |
raise TypeError | |
@StrongType | |
def my_print(text: str) -> None: | |
return print(text) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment