0

My objective is to raise SystemExit and log the error when my program encounter an unexpected behavior.

I was doing something like:

logger.error('Unexpected behaviour')
raise SystemExit

In order to avoid the repetition in my code i tried to write a decorator to raise SystemExit at each logger.error call:

error = logger.error
def error_from_logger(msg) :
    ''' Decorator for logger.error to kill the program at the call '''

    error(msg)
    raise SystemExit

logger.error = error_from_logger
del(error_from_logger)

So my question is: Is my decorator pythonic? And if not what is the best pythonic way to write it? (I saw people use @something but I don't understand it's usage).

Thanks!

Liad
  • 340
  • 4
  • 15
  • This looks more like [monkey patching](https://en.wikipedia.org/wiki/Monkey_patch) – bracco23 Jul 26 '19 at 10:11
  • @bracco23 Indeed... So it's not a decorator because it modify the behaviour of the function? – Liad Jul 26 '19 at 12:15
  • 1
    It's not a decorator because you're actually changing the `error` function of the `logger` module with a custom one you wrote, which actually adds behavior to the original one. It's a tricky move to have, it might not behave like you want, especially since you are having it throw an exception. I would go for a Facade pattern, using a custom object that in turns uses a logger instead. – bracco23 Jul 26 '19 at 12:34
  • @bracco23 Okay, you're right, I will do a separate object instead of modifying the logger one. Thanks for the explanation! – Liad Jul 29 '19 at 09:19

1 Answers1

1

As has been mentioned in the comments, what you have done isn't quite decorating. This would be decorating:

def call_then_exit(func):
    def called_and_exited(*args, **kwargs):
        func(*args, **kwargs)
        raise SystemExit
    return called_and_exited

logger = logging.getLogger()
logger.error = call_then_exit(logger.error)  # this is the decoration

logger.error("some error has happened")  # prints the message and exists

@decorator is just syntactic sugar which you use when declaring a function. This isn't much use to you if you are using a function/method declared elsewhere.

@call_then_exit  # this is the decoration
def say_hi():
    print('hello')

say_hi()  # prints 'hi' and exits
print('did we exit?')  # we never reach this

Is my decorator pythonic?

Arguably it is not because patching is ugly and it adds unexpected behaviour. To be more explicit, you could make a log_error_and_exit() function or register your own logging class with logging.setLoggerClass(OurLogger) and maybe add a .fatal_error() method. However, I think your solution is OK as-is.

FiddleStix
  • 3,016
  • 20
  • 21