How can I avoid using a global variable as a parameter to a python decorator

De openkb
Aller à : Navigation, rechercher

Sommaire

Questions

I am writing a python script where I am using a decorator(retry is the one I am using) that takes a parameter (tries). I want the parameter to be configurable from a command line argument. The only way I can figure out how to set the parameter for the decorator is by reading my arguments into a global variable. I hate this from a design perspective. It makes writing unit tests and anything else that wants to import any functions from my script reliant on the command line arguments being all the same.

Here is a dumbed down example of the problem I am having:

import argparse
from functools import wraps

def get_args():
    parser = argparse.ArgumentParser()
    parser.add_argument( -t ,  --test_value , dest= test_value , required=True, default="sample value")
    args = parser.parse_args()
    return args
args = get_args()

def decorator_example(test_value):
    def deco_example(f):
        @wraps(f)
        def f_example(*args, **kwargs):
            print "The value I need is", test_value
            return f(*args, **kwargs) 
        return f_example 
    return deco_example

@decorator_example(args.test_value)
def test():
    print "running test"

if __name__ ==  __main__ :
    test()

If anyone can think of a better way to do this without having args be a global, please share! I have exhausted the internet searching for a better way... I want to call getargs() in main and pass the arguments around as needed....

Thanks, Robin

Answers

I don t think a decorator is appropriate here. A class seems more suitable precisely because of the problem you re facing. Something like this:

class Test(object):
    def __init__(self, test_value):
        self.test_value = test_value

    def test(self):
        print "The value I need is", self.test_value
        self._inner_test()

    def _inner_test():
        print "running test"


if __name__ ==  __main__ :
    args = get_args()
    t = TestClass(args.test_value)
    t.test()

How exactly to structure the class is not clear from the example you have given and would depend on what you re actually doing, but I think something in this vein will provide you with a more robust solution than trying to shoehorn this into decorators.

Classes are designed to maintain state and provide modified behavior based on that state. That is what you re doing. Your application has state that modifies its behavior. Decorators are designed to wrap extra, static functionality around an existing method.

However, if that is unsuitable, another alternative is to simply allow your arguments to be global. Something along the lines of this:

config.py

import argparse

test_value = None

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument( -t ,  --test_value , dest= test_value , required=True, default="sample value")
    args = parser.parse_args()
    return args

def configure():
    global test_value
    args = parse_args()
    test_value = args.test_value

main.py

from functools import wraps
import config

def decorator_example(f):
    @wraps(f)
    def f_example(*args, **kwargs):
        print "The value I need is", config.test_value
        return f(*args, **kwargs) 
    return f_example

@decorator_example
def test():
    print "running test"


if __name__ ==  __main__ :
    config.configure()
    test()

One nice side of this solution is that it gives you an obvious way to also supplement your arguments with a configuration file. Note that this should actually work since config.test_value is not actually read until test is called.

Source

License : cc by-sa 3.0

http://stackoverflow.com/questions/21151807/how-can-i-avoid-using-a-global-variable-as-a-parameter-to-a-python-decorator

Related

Outils personnels
Espaces de noms

Variantes
Actions
Navigation
Outils