Fairly new to decorators, is this considered bad code? If it is, what would be a good substitute?
import functools
def error_handaler_decorator(func):
@functools.wraps(func)
def wrapper(error_message_for_wrapper = None, cont = True, *args, **kwargs):
try:
return func(*args, **kwargs)
except:
if error_message_for_wraper != None:
# Report error to user in application specific way
if cont == True:
return True
@error_handaler_decorator
def some_func(input_for_func):
# Do a thing.
@error_handaler_decorator
def some_func_in_a_class(self,input):
# Do another thing.
some_func(error_message_for_wrapper = something bad happened, input_for_func = some_input)
some_class.some_func_in_a_class(error_message_for_wrapper = something bad happened, cont = False, input_for_func = some_input)
This means that I have to pass the wrapper variables when I call the decorated function, and I don't think I can pass args
, only kwargs
, but it allows me to define the error message based on what I pass to the function, not when I define the function.
The code works, (at least as much as I have tested it), but my IDE (Visual Studio code) gets very angry, saying:
Unexpected keyword argument 'error_message_for_wrapper' in method call
I really want to clean up my code, and the alternatives I see are try: except:
or with:
. try: except:
makes my code messy, (at least subjectively).
With.
is better, but I would rather have my decorators as functions, it works better for the project.
I don't think I can have with
as a function.
Okay this is going to depend on which Python version you use I believe. In python 3 you can just do:
def error_handler_decorator(func):
@functools.wraps(func)
def wrapper(*args, error_message_for_wrapper = None, cont = True, **kwargs):
try:
return func(*args, **kwargs)
except:
if error_message_for_wrapper is not None:
# Report error to user in application specific way
if cont:
return True
return wrapper
In python 2 (but would also work in python 3) you can use:
def error_handler_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
error_message_for_wrapper = kwargs.pop('error_message_for_wrapper', None)
cont = kwargs.pop('cont', False)
try:
return func(*args, **kwargs)
except:
if error_message_for_wrapper is not None:
# Report error to user in application specific way
if cont:
return True
return wrapper
This is probably a case where you should use a context manager rather than a decorator.
from contextlib import contextmanager
@contextmanager
def handler(msg=None, cont=True):
try:
yield
except Exception:
if msg is not None:
print(msg)
if not cont:
reraise
with handler("Don't divide by zero!"):
3/0
print("OK")
will output
Don't divide by zero!
OK
If you set cont=False
when you call handler
, you'll see Don't divide by zero
, but then a traceback as the re-raised exception prevents OK
from being printed.
Coming full circle, contextlib
also provides a way to use a context manager as a decorator. You'll have to define the context manager without the help of contextmanager
, though.
from contextlib import ContextDecorator
class handler(ContextDecorator):
def __init__(self, msg=None, cont=True):
self.msg = msg
self.cont = cont
# ContextDecorator doesn't provide default definitions,
# so we have to provide something, even it doesn't really
# do anything.
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
if exc_value is not None and self.msg is not None:
print(self.msg)
# Returning true suppresses any exception
# that may have been raised in the context. Returning false
# means the exception is raised as usual.
return self.cont
# Scolds you, but returns None
@handler("Don't divide by zero")
def some_func(x):
return 3/x
# Scolds you *and* raises the exception
@handler("Don't divide by zero", cont=False)
def some_other_func(x):
return 3/x
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.