1

I'm curently learn decorators and understand, that decorators may created in 2 ways: as function and as class. As a class I guided from this answer Reading this examples from pep318 I decide rewrite example 4 that check atributes and return values types of function. So here's some code below:

from inspect import signature


def accepts(*types):
    def check_types(f):
        f_args = list(signature(f).parameters)
        assert len(types) == len(f_args)
        def inner_f(*args, **kwargs):
            for (a, t) in zip(args, types):
                assert isinstance(a, t), f"arg {a} doesn't match {t}"
            return f(*args, **kwargs)

        inner_f.__name__ = f.__name__
        return inner_f
    return check_types


def returns(rtype):
    def check_returns(f):
        def new_f(*args, **kwargs):
            result = f(*args, **kwargs)
            assert isinstance(result, rtype), f"return value {result} doesn't match {rtype}"
        new_f.__name__ = f.__name__
        return new_f
    return check_returns


class CheckTypes(object):
    def __init__(self, func, *types):
        self._func = func
        self._types = types
        f_args = list(signature(self._func).parameters)
        assert len(types) == len(f_args)

    def __call__(self, *args, **kwargs):
        for number, (a, t) in enumerate(zip(args, self._types)):
            assert isinstance(a, t), f"{number} arg {a} with type {type(a)} doesn't match {t}"        


class ExternalWrapperCheckTypes(object):
    def __init__(self, *types):
        self._types = types

    def __call__(self, func):
        return CheckTypes(func, *self._types)

class CheckReturns(object):
    def __init__(self, func, *types):
        self._func = func
        self._types = types

    def __call__(self, *args, **kwargs):
        result = self._func(*args, **kwargs)
        assert isinstance(result, self._types), f"return value {result} doesn't match {self._types}"


class ExternalWrapperCheckReturns(object):

    def __init__(self, *types):
        self._types = types

    def __call__(self, func):
        return CheckReturns(func, *self._types)


@accepts(int, (int, float))
@returns((int,))
def decorated_by_functions(arg1, arg2):
    return "Incorrect output"


@ExternalWrapperCheckTypes(int, (int, float))
@ExternalWrapperCheckReturns((int,))
def decorated_by_classes(arg1, arg2):
    return "Incorrect output"


def main():
    res1 = decorated_by_functions (42, 42.42) # AssertionError: return value s doesn't match (<class 'int'>,)
    res2 = decorated_by_classes(42, 42.42) # Ignore assertion

So, in what problem? decorated_by_functions will cause to assertion error as expected, but decorated_by_classes ignore assertion. On my mind- the problem in overloaded function __call__ in both methods, where I might to return instance of class or something else, but when I return it- behavior doesn't changed.

Antares
  • 11
  • 3
  • You wrote e.g. `@returns((int))`, where apparently you wanted `((int,))` for a tuple, or maybe `([int])` for a list. – J_H Jul 07 '19 at 19:00
  • @J_H make a sense, fixed and thank you – Antares Jul 07 '19 at 19:10
  • Your `CheckTypes` wrapper doesn’t actually call anything. (Neither do your return-type checkers return anything, but that’s not the cause of this issue.) – Davis Herring Jul 07 '19 at 19:23

1 Answers1

0

Update

Actually, my intuition was right- I needed in return right object, thanks to pythontips it was our function, that called with params: self._func(*args, **kwargs). Thanks to all for attention and your time!

Final solution has this view:

from inspect import signature


class CheckTypes(object):
    def __init__(self, func, *types):
        self._func = func
        self._types = types
        f_args = list(signature(self._func).parameters)
        assert len(types) == len(f_args)

    def __call__(self, *args, **kwargs):
        for number, (a, t) in enumerate(zip(args, self._types)):
            assert isinstance(a, t), f"{number} arg {a} with type {type(a)} doesn't match {t}"

        return self._func(*args, **kwargs)

class ExternalWrapperCheckTypes(object):
    def __init__(self, *types):
        self._types = types

    def __call__(self, func, *args, **kwargs):
        return CheckTypes(func, *self._types)

class CheckReturns(object):
    def __init__(self, func, *types):
        self._func = func
        self._types = types

    def __call__(self, *args, **kwargs):
        result = self._func(*args, **kwargs)
        assert isinstance(result, self._types), f"return value {result} doesn't match {self._types}"

        return self._func(*args, **kwargs)

class ExternalWrapperCheckReturns(object):
    def __init__(self, *types):
        self._types = types

    def __call__(self, func, *args, **kwargs):
        return CheckReturns(func, *self._types)


@ExternalWrapperCheckTypes(int, (int, float))
@ExternalWrapperCheckReturns((int, ))
def decorated_by_classes(arg1, arg2):
    return "Incorrect output"


def main():
    ans = decorated_by_classes(42, 42.42) # AssertionError: return value s doesn't match (<class 'int'>,)
    print(ans)
Antares
  • 11
  • 3