Rick
Rick

Reputation: 45251

Passing arguments to a decorator using the decorator module

I want to validate the input value of a class member against a range. I am trying out the decorator module because of all the benefits it provides (such as preserving class meta data).

Here is my validation decorator:

from decorator import decorator

@decorator
def valid_range(f,*args,**kwargs):
    value = args[1]
    inrange = True
    try:
        inrange = (inrange and (value <= range['max_inclusive']))
    except KeyError: pass 
    try:
        inrange = (inrange and (value < range['max_exclusive']))
    except KeyError: pass 
    try:
        inrange = (inrange and (value >= range['min_inclusive']))
    except KeyError: pass
    try:
        inrange = (inrange and (value > range['min_exclusive']))
    except KeyError: pass
    if inrange:
        return f(*args,**kwargs)
    else:
        raise Exception

Here is the application of the decorator (assume value_setter is in the body of some class):

@valid_range({'max_inclusive':1,'min_inclusive':0})
def value_setter(self,value):
    self.value = value

Is there a way to pass the max/min dict as an argument to my decorator (which is used as range in the decorator definition itself)? I know how to do this using regular decorators, but as I said, I'd like to use the decorator module if at all possible.

If it isn't possible, how else can I do this and preserve all the identifying data? Any alternative modules I should consider using instead?

EDIT: Note that passing the range dict argument as part of *args and **kwargs isn't an option. I don't want to include it as part of my class definition.

Upvotes: 0

Views: 184

Answers (2)

Rick
Rick

Reputation: 45251

Came up with the following solution. Basically, I am wrapping my decorator in another decorator.

This does allow separating the entry of max and min from the rest of the code, but it's definitely not a nice solution and misses the point of the decorator module (which is to simplify the application of decorators). For this reason I prefer acushner's answer to my own.

from decorator import decorator

@decorator
def _valid_range(f,*args,**kwargs):
    return f(*args,range = {'max_inclusive':1,'min_inclusive':0},**kwargs)

@decorator
@_valid_range
def valid_range(f,*args,**kwargs):
    range = kwargs.pop('range',None)
    value = args[0]
    inrange = True
    try:
        inrange = (inrange and (value <= range['max_inclusive']))
    except KeyError: pass 
    try:
        inrange = (inrange and (value < range['max_exclusive']))
    except KeyError: pass 
    try:
        inrange = (inrange and (value >= range['min_inclusive']))
    except KeyError: pass
    try:
        inrange = (inrange and (value > range['min_exclusive']))
    except KeyError: pass
    if inrange:
        return f(*args,**kwargs)
    else:
        raise Exception

@valid_range
def value_setter(value):
    print(value)

Upvotes: 0

acushner
acushner

Reputation: 9946

you can just write your own (shortened example):

from functools import wraps

def valid_range(min_val=0, max_val=0):
    def deco(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            val = args[0]
            if min_val <= val <= max_val:
                return f(*args, **kwargs):
            else:
                raise RangeError
        return wrapper
    return deco

usage:

@valid_range(10, 20)
def f(x):
    pass

Upvotes: 4

Related Questions