Mohammad Kiyan
Mohammad Kiyan

Reputation: 131

Decorator for cheking any function annotation type

i want to design a decorator to check any function annotation type and if it has similar type then run function. can python do such this thing?? if python can, please help me!!

def foo (a:int):
    if foo.__annotations__.get('a') == type(a):
        pass

def boo (b:str):
    if boo.__annotations__.get('b') == type(b):
        pass

and another thing is annotations is a dict type, i want such this :

from type import FunctionType
def check (f:FunctionType):
    result = True
    k = [k for k in f.__annotations__.keys()]
    v = [v for v in f.__annotations__.values()]
    for i in range(len(v)):
        if v[i] != type(k[i]): #but we don't access to the type of k[i] out of th f function
            result = False
    return result       

Upvotes: 2

Views: 964

Answers (3)

ichigo .b
ichigo .b

Reputation: 21

import functools
def annotations_checker(func):
    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        TrueFalseChecker=True
        keys=tuple(func.__annotations__.keys())
        for key_num in range(len(keys)):
            if func.__annotations__[keys[key_num]]!=type(args[key_num]):
                break
        else:
            value=func(*args,*kwargs)
            return value
        return

    return wrapper

and you can use @annotations_checker decorator for any python method / python function to check type annotations like:

@annotations_checker
def test(str_example:str,int_example:int,second_str_example:str):
    print("if you can see this, the args and annonations type are same!") 
test(1,"2",3) #the function will not work
test("1",1,"testtest") #function will work

Upvotes: 1

Andrey Kostrenko
Andrey Kostrenko

Reputation: 119

If I understand the idea correctly, perhaps this code will help you:

from types import FunctionType

def check(f: FunctionType):
    def wrapper(*args, **kwargs):
        result = True

        # check args
        keys = tuple(f.__annotations__.keys())
        for ar in enumerate(args):
            if not isinstance(ar[1], f.__annotations__.get(keys[ar[0]])):
                result = False
                break

        if result:
            # check kwargs
            for k, v in kwargs.items():
                if not isinstance(v, f.__annotations__.get(k)):
                    result = False
                    break

        if result:
            f(*args, **kwargs)

    return wrapper

Example usage:

@check
def foo(a: str, b: int = None):
    print(f"a  = {a}")
    print(f"b  = {b}")


# Example 1: a=324, b=32:
foo(234, b=32)
# result: function not executed

# Example 2: a="abc", b="zzz":
foo("abc", b="zzz")
# result: function not executed

# Example 3: a="qwe", b= not set:
foo("qwe")
# result: function executed, output:
# a  = qwe
# b  = None

# Example 4: a="abc", b=99:
foo("abc", 99)
# result: function executed, output:
# a  = abc
# b  = 99

The decorator checks the argument types, and if everything is in order, it executes the function, otherwise it does nothing.

Upvotes: 2

Kevin Chan
Kevin Chan

Reputation: 69

something like this.

import inspect
import functools


def check(func):
    msg = "Expected type {etype} for {para} got {got}"
    para = inspect.signature(func).parameters
    keys = tuple(para.keys())

    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        def do_check(anno,value,para):
            if not isinstance(value, anno):
                raise TypeError(msg.format(etype=anno,
                    para=para,
                    got=type(value)))

        for i,value in  enumerate(args):
            anno = para[keys[i]].annotation
            do_check(anno, value, keys[i])

        for arg_name,value in  kwargs.items():
            anno = para[arg_name].annotation
            do_check(anno, value, arg_name)

        ret = func(*args,**kwargs)
        if "return" in func.__annotations__:
            anno = func.__annotations__["return"]
            do_check(anno, ret, "return")
        return ret
    return wrapper

@check
def test(a:int,b:str) -> str:
    return 'aaa'

@check
def test2(a:int,b:str) -> str:
    return 123

Upvotes: 1

Related Questions