简体   繁体   中英

Python: decorator/wrapper for try/except statement

I have some blocks of code which need to be wrapped by function.

try:
    if config.DEVELOPMENT == True:
        # do_some_stuff
except:
    logger.info("Config is not set for development")

Then I'll do again:

try:
    if config.DEVELOPMENT == True:
        # do_some_another_stuff
except:
    logger.info("Config is not set for development")

So, how can I wrap this "do_some_stuff" and "do_some_another_stuff"?

I'm trying to write function with contextmanager:

@contextmanager
def try_dev_config(name):
    try:
        if name is not None:
            yield
    except Exception as e:
        print "not dev config"

with try_dev_config("config.DEVELOPMENT"):
    # do_some_stuff

And I got an error:

RuntimeError: generator didn't yield

You could pass in a function.

boolean = True

def pass_this_in():
    print("I just did some stuff")

def the_try_except_bit(function):
    try:
        if boolean:
            function()
    except:
        print("Excepted")


# Calling the above code
the_try_except_bit(pass_this_in)

If you want to reduce the "pass_this_in" definition bit, then you can use lambda function definitions :

pass_this_in = lambda : print("I just did some stuff")

I am not sure that a context manager is the good method to achieve what you want. The context manager goal is to provide a mecanism to open/instantiate a resource, give access to it (or not) and close/clean it automatically when you no more need it.

IMHO, what you need is a decorator . A decorator aims at executing code around a function call. It would force you to put each block of code in a function but I don't think it is so difficult. You can implement it like this:

class Config(object):
    """for demonstration purpose only: used to have a config.DEVELOPMENT value"""
    DEVELOPMENT = True

class Logger(object):
    """for demonstration purpose only: used to have a logger.info method"""
    @staticmethod
    def info(msg):
        print("Logged: {}".format(msg))

def check_dev_config(config, logger):
    def dev_config_checker(func):
        def wrapper(*args, **kwargs):
            try:
                if config.DEVELOPMENT:
                    func(*args, **kwargs)
            except Exception as err:
                logger.info(
                    "Config is not set for developpement: {}".format(err))
        return wrapper
    return dev_config_checker

@check_dev_config(Config, Logger)
def do_stuff_1():
    print("stuff 1 done")

@check_dev_config(Config, Logger)
def do_stuff_2():
    raise Exception("stuff 2 failed")

do_stuff_1()
do_stuff_2()

This code prints

stuff 1 done
Logged: Config is not set for developpement: stuff 2 failed

Explanations:

  • The check_dev_config function is actually a decorator generator which accepts the config and the logger as arguments.
  • It returns the dev_config_checker function which is an actual (and parameterised) decorator, and which accepts a function to decorate as argument.
  • This decorator returns a wrapper function which will actually run code around the decorated function call. In this function, the decorated function is called inside a try/except structure and only if the config.DEVELOPMENT is evaluated to True. In case of exception, the logger is used to log an information.
  • Each block of code to decorate is put into a function ( do_stuff_1 , do_stuff_2 and decorated with the check_dev_config decorator generator, giving it the config and the logger .
  • When decorated functions are called, they are called via their decorator and not directly. As you can see, the do_stuff_2 exception has been catched and the a message has been logged.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM