Skip to content

Instantly share code, notes, and snippets.

@TheArcherST
Last active December 20, 2022 21:37
Show Gist options
  • Save TheArcherST/99e6a792b27ca0b00594d3a34212bc9a to your computer and use it in GitHub Desktop.
Save TheArcherST/99e6a792b27ca0b00594d3a34212bc9a to your computer and use it in GitHub Desktop.
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