General decorator to wrap try except in python? - python

I'd interacting with a lot of deeply nested json I didn't write, and would like to make my python script more 'forgiving' to invalid input. I find myself writing involved try-except blocks, and would rather just wrap the dubious function up.
I understand it's a bad policy to swallow exceptions, but I'd rather prefer they to be printed and analysed later, than to actually stop execution. It's more valuable, in my use-case to continue executing over the loop than to get all keys.
Here's what I'm doing now:
try:
item['a'] = myobject.get('key').METHOD_THAT_DOESNT_EXIST()
except:
item['a'] = ''
try:
item['b'] = OBJECT_THAT_DOESNT_EXIST.get('key2')
except:
item['b'] = ''
try:
item['c'] = func1(ARGUMENT_THAT_DOESNT_EXIST)
except:
item['c'] = ''
...
try:
item['z'] = FUNCTION_THAT_DOESNT_EXIST(myobject.method())
except:
item['z'] = ''
Here's what I'd like, (1):
item['a'] = f(myobject.get('key').get('subkey'))
item['b'] = f(myobject.get('key2'))
item['c'] = f(func1(myobject)
...
or (2):
#f
def get_stuff():
item={}
item['a'] = myobject.get('key').get('subkey')
item['b'] = myobject.get('key2')
item['c'] = func1(myobject)
...
return(item)
...where I can wrap either the single data item (1), or a master function (2), in some function that turns execution-halting exceptions into empty fields, printed to stdout. The former would be sort of an item-wise skip - where that key isn't available, it logs blank and moves on - the latter is a row-skip, where if any of the fields don't work, the entire record is skipped.
My understanding is that some kind of wrapper should be able to fix this. Here's what I tried, with a wrapper:
def f(func):
def silenceit():
try:
func(*args,**kwargs)
except:
print('Error')
return(silenceit)
Here's why it doesn't work. Call a function that doesn't exist, it doesn't try-catch it away:
>>> f(meow())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'meow' is not defined
Before I even add a blank return value, I'd like to get it to try-catch correctly. If the function had worked, this would have printed "Error", right?
Is a wrapper function the correct approach here?
UPDATE
I've had a lot of really useful, helpful answers below, and thank you for them---but I've edited the examples I used above to illustrate that I'm trying to catch more than nested key errors, that I'm looking specifically for a function that wraps a try-catch for...
When a method doesn't exist.
When an object doesn't exist, and is getting a method called on it.
When an object that does not exist is being called as an argument to a function.
Any combination of any of these things.
Bonus, when a function doesn't exist.

There are lots of good answers here, but I didn't see any that address the question of whether you can accomplish this via decorators.
The short answer is "no," at least not without structural changes to your code. Decorators operate at the function level, not on individual statements. Therefore, in order to use decorators, you would need to move each of the statements to be decorated into its own function.
But note that you can't just put the assignment itself inside the decorated function. You need to return the rhs expression (the value to be assigned) from the decorated function, then do the assignment outside.
To put this in terms of your example code, one might write code with the following pattern:
#return_on_failure('')
def computeA():
item['a'] = myobject.get('key').METHOD_THAT_DOESNT_EXIST()
item["a"] = computeA()
return_on_failure could be something like:
def return_on_failure(value):
def decorate(f):
def applicator(*args, **kwargs):
try:
return f(*args,**kwargs)
except:
print('Error')
return value
return applicator
return decorate

You could use a defaultdict and the context manager approach as outlined in Raymond Hettinger's PyCon 2013 presentation
from collections import defaultdict
from contextlib import contextmanager
#contextmanager
def ignored(*exceptions):
try:
yield
except exceptions:
pass
item = defaultdict(str)
obj = dict()
with ignored(Exception):
item['a'] = obj.get(2).get(3)
print item['a']
obj[2] = dict()
obj[2][3] = 4
with ignored(Exception):
item['a'] = obj.get(2).get(3)
print item['a']

It's very easy to achieve using configurable decorator.
def get_decorator(errors=(Exception, ), default_value=''):
def decorator(func):
def new_func(*args, **kwargs):
try:
return func(*args, **kwargs)
except errors, e:
print "Got error! ", repr(e)
return default_value
return new_func
return decorator
f = get_decorator((KeyError, NameError), default_value='default')
a = {}
#f
def example1(a):
return a['b']
#f
def example2(a):
return doesnt_exist()
print example1(a)
print example2(a)
Just pass to get_decorator tuples with error types which you want to silence and default value to return.
Output will be
Got error! KeyError('b',)
default
Got error! NameError("global name 'doesnt_exist' is not defined",)
default
Edit: Thanks to martineau i changed default value of errors to tuples with basic Exception to prevents errors.

It depends on what exceptions you expect.
If your only use case is get(), you could do
item['b'] = myobject.get('key2', '')
For the other cases, your decorator approach might be useful, but not in the way you do it.
I'll try to show you:
def f(func):
def silenceit(*args, **kwargs): # takes all kinds of arguments
try:
return func(*args, **kwargs) # returns func's result
except Exeption, e:
print('Error:', e)
return e # not the best way, maybe we'd better return None
# or a wrapper object containing e.
return silenceit # on the correct level
Nevertheless, f(some_undefined_function())won't work, because
a) f() isn't yet active at the execution time and
b) it is used wrong. The right way would be to wrap the function and then call it: f(function_to_wrap)().
A "layer of lambda" would help here:
wrapped_f = f(lambda: my_function())
wraps a lambda function which in turn calls a non-existing function. Calling wrapped_f() leads to calling the wrapper which calls the lambda which tries to call my_function(). If this doesn't exist, the lambda raises an exception which is caught by the wrapper.
This works because the name my_function is not executed at the time the lambda is defined, but when it is executed. And this execution is protected and wrapped by the function f() then. So the exception occurs inside the lambda and is propagated to the wrapping function provided by the decorator, which handles it gracefully.
This move towards inside the lambda function doesn't work if you try to replace the lambda function with a wrapper like
g = lambda function: lambda *a, **k: function(*a, **k)
followed by a
f(g(my_function))(arguments)
because here the name resolution is "back at the surface": my_function cannot be resolved and this happens before g() or even f() are called. So it doesn't work.
And if you try to do something like
g(print)(x.get('fail'))
it cannot work as well if you have no x, because g() protects print, not x.
If you want to protect x here, you'll have to do
value = f(lambda: x.get('fail'))
because the wrapper provided by f() calls that lambda function which raises an exception which is then silenced.

Extending #iruvar answer - starting with Python 3.4 there is an existing context manager for this in Python standard lib: https://docs.python.org/3/library/contextlib.html#contextlib.suppress
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('somefile.tmp')
with suppress(FileNotFoundError):
os.remove('someotherfile.tmp')

in your case you first evaluate the value of the meow call (which doesn't exist) and then wrap it in the decorator. this doesn't work that way.
first the exception is raised before it was wrapped, then the wrapper is wrongly indented (silenceit should not return itself). You might want to do something like:
def hardfail():
return meow() # meow doesn't exist
def f(func):
def wrapper():
try:
func()
except:
print 'error'
return wrapper
softfail =f(hardfail)
output:
>>> softfail()
error
>>> hardfail()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in hardfail
NameError: global name 'meow' is not defined
anyway in your case I don't understand why you don't use a simple method such as
def get_subkey(obj, key, subkey):
try:
return obj.get(key).get(subkey, '')
except AttributeError:
return ''
and in the code:
item['a'] = get_subkey(myobject, 'key', 'subkey')
Edited:
In case you want something that will work at any depth. You can do something like:
def get_from_object(obj, *keys):
try:
value = obj
for k in keys:
value = value.get(k)
return value
except AttributeError:
return ''
That you'd call:
>>> d = {1:{2:{3:{4:5}}}}
>>> get_from_object(d, 1, 2, 3, 4)
5
>>> get_from_object(d, 1, 2, 7)
''
>>> get_from_object(d, 1, 2, 3, 4, 5, 6, 7)
''
>>> get_from_object(d, 1, 2, 3)
{4: 5}
And using your code
item['a'] = get_from_object(obj, 2, 3)
By the way, on a personal point of view I also like #cravoori solution using contextmanager. But this would mean having three lines of code each time:
item['a'] = ''
with ignored(AttributeError):
item['a'] = obj.get(2).get(3)

Why not just use cycle?
for dst_key, src_key in (('a', 'key'), ('b', 'key2')):
try:
item[dst_key] = myobject.get(src_key).get('subkey')
except Exception: # or KeyError?
item[dst_key] = ''
Or if you wish write a little helper:
def get_value(obj, key):
try:
return obj.get(key).get('subkey')
except Exception:
return ''
Also you can combine both solutions if you have a few places where you need to get value and helper function would be more reasonable.
Not sure that you actually need a decorator for your problem.

Since you're dealing with lots of broken code, it may be excusable to use eval in this case.
def my_eval(code):
try:
return eval(code)
except: # Can catch more specific exceptions here.
return ''
Then wrap all your potentially broken statements:
item['a'] = my_eval("""myobject.get('key').get('subkey')""")
item['b'] = my_eval("""myobject.get('key2')""")
item['c'] = my_eval("""func1(myobject)""")

How about something like this:
def exception_handler(func):
def inner_function(*args, **kwargs):
try:
func(*args, **kwargs)
except TypeError:
print(f"{func.__name__} error")
return inner_function
then
#exception_handler
def doSomethingExceptional():
a=2/0
all credits go to:https://medium.com/swlh/handling-exceptions-in-python-a-cleaner-way-using-decorators-fae22aa0abec

Try Except Decorator for sync and async functions
Note: logger.error can be replaced with print
Latest version can be found here.

Related

Conditional mocking: Call original function if condition matches

A very similar question was asked, however it doesn't answer my doubt.
#mock.patch('myget.myvalue.myfrom.myAPI')
def test_thatdoesntwork(myAPI_mock):
orig_function = myget.myvalue.myfrom.myAPI
nth_value = get_valuefrom_myDB(n=111)
def mock_function_for_nth_value_only(myvalue):
if myvalue == nth_value:
return get_valuefrom_myDB(n+1)
else:
return orig_function(n)
nplus1th_value = get_valuefrom_myDB(n=112)
myvalue = myget.myvalue.myfrom.myAPI(nplus1th_value)
# getting "CRITICAL: maximum recursion level exceeded"
am assuming when I call orig_function , I am actually calling the mocked function, and that causes the CRITICAL recursion error....
but what am I doing wrong?
Thanks!!
The #mock.patch decorator replaces the function before your code starts, so orig_function = myget.myvalue.myfrom.myAPI already points to the mocked version.
Instead, you can use the context manager to ensure that you only replace the function in the scope you want.
This test version should work better for you:
def test_that_works():
orig_function = myget.myvalue.myfrom.myAPI
nth_value = get_valuefrom_myDB(n=111)
def mock_function_for_nth_value_only(myvalue):
if myvalue == nth_value:
return get_valuefrom_myDB(myvalue + 1)
else:
return orig_function(myvalue)
with mock.patch('myget.myvalue.myfrom.myAPI',
side_effect=mock_function_for_nth_value_only):
nplus1th_value = get_valuefrom_myDB(n=112)
myvalue = myget.myvalue.myfrom.myAPI(nplus1th_value)

How to assign a returned value from the defer method in python/twisted

I have a class, which is annotated as #defer.inlineCallbacks
(I want to return the machine list from this)
#defer.inlineCallbacks
def getMachines(self):
serverip = 'xx'
basedn = 'xx'
binddn = 'xx'
bindpw = 'xx'
query = '(&(cn=xx*)(objectClass=computer))'
c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient)
overrides = {basedn: (serverip, 389)}
client = yield c.connect(basedn, overrides=overrides)
yield client.bind(binddn, bindpw)
o = ldapsyntax.LDAPEntry(client, basedn)
results = yield o.search(filterText=query)
for entry in results:
for i in entry.get('name'):
self.machineList.append(i)
yield self.machineList
return
I have another class defined in another python file, where i wnat to call above method and read the machineList.
returned = LdapClass().getMachines()
print returned
The print says <Deferred at 0x10f982908>. How can I read the list ?
inlineCallbacks is just an alternate API for working with Deferred.
You've mostly successfully used inlineCallbacks to avoid having to write callback functions. You forgot to use returnValue though. Replace:
yield self.machineList
with
defer.returnValue(self.machineList)
This does not fix the problem you're asking about, though. inlineCallbacks gives you a different API inside the function it decorates - but not outside. As you've noticed, if you call a function decorated with it, you get a Deferred.
Add a callback (and an errback, eventually) to the Deferred:
returned = LdapClass().getMachines()
def report_result(result):
print "The result was", result
returned.addCallback(report_result)
returned.addErrback(log.err)
Or use inlineCallbacks some more:
#inlineCallbacks
def foo():
try:
returned = yield LdapClass().getMachines()
print "The result was", returned
except:
log.err()

Prevent 'try' to catch an exception and pass to the next line in python

I have a python function that runs other functions.
def main():
func1(a,b)
func2(*args,*kwargs)
func3()
Now I want to apply exceptions on main function. If there was an exception in any of the functions inside main, the function should not stop but continue executing next line. In other words, I want the below functionality
def main():
try:
func1()
except:
pass
try:
func2()
except:
pass
try:
func3()
except:
pass
So is there any way to loop through each statement inside main function and apply exceptions on each line.
for line in main_function:
try:
line
except:
pass
I just don't want to write exceptions inside the main function.
Note : How to prevent try catching every possible line in python? this question comes close to solving this problem, but I can't figure out how to loop through lines in a function.
If you have any other way to do this other than looping, that would help too.
What you want is on option that exists in some languages where an exception handler can choose to proceed on next exception. This used to lead to poor code and AFAIK has never been implemented in Python. The rationale behind is that you must explicitely say how you want to process an exception and where you want to continue.
In your case, assuming that you have a function called main that only calls other function and is generated automatically, my advice would be to post process it between its generation and its execution. The inspect module can even allow to do it at run time:
def filter_exc(func):
src = inspect.getsource(func)
lines = src.split('\n')
out = lines[0] + "\n"
for line in lines[1:]:
m = re.match('(\s*)(.*)', line)
lead, text = m.groups()
# ignore comments and empty lines
if not (text.startswith('#') or text.strip() == ""):
out += lead + "try:\n"
out += lead + " " + text + "\n"
out += lead + "except:\n" + lead + " pass\n"
return out
You can then use the evil exec (the input in only the source from your function):
exec(filter_exc(main)) # replaces main with the filtered version
main() # will ignore exceptions
After your comment, you want a more robust solution that can cope with multi line statements and comments. In that case, you need to actually parse the source and modify the parsed tree. ast module to the rescue:
class ExceptFilter(ast.NodeTransformer):
def visit_Expr(self, node):
self.generic_visit(node)
if isinstance(node.value, ast.Call): # filter all function calls
# print(node.value.func.id)
# use a dummy try block
n = ast.parse("""try:
f()
except:
pass""").body[0]
n.body[0] = node # make the try call the real function
return n # and use it
return node # keep other nodes unchanged
With that example code:
def func1():
print('foo')
def func2():
raise Exception("Test")
def func3(x):
print("f3", x)
def main():
func1()
# this is a comment
a = 1
if a == 1: # this is a multi line statement
func2()
func3("bar")
we get:
>>> node = ast.parse(inspect.getsource(main))
>>> exec(compile(ExceptFilter().visit(node), "", mode="exec"))
>>> main()
foo
f3 bar
In that case, the unparsed node(*) write as:
def main():
try:
func1()
except:
pass
a = 1
if (a == 1):
try:
func2()
except:
pass
try:
func3('bar')
except:
pass
Alternatively it is also possible to wrap every top level expression:
>>> node = ast.parse(inspect.getsource(main))
>>> for i in range(len(node.body[0].body)): # process top level expressions
n = ast.parse("""try:
f()
except:
pass""").body[0]
n.body[0] = node.body[0].body[i]
node.body[0].body[i] = n
>>> exec(compile(node, "", mode="exec"))
>>> main()
foo
f3 bar
Here the unparsed tree writes:
def main():
try:
func1()
except:
pass
try:
a = 1
except:
pass
try:
if (a == 1):
func2()
except:
pass
try:
func3('bar')
except:
pass
BEWARE: there is an interesting corner case if you use exec(compile(... in a function. By default exec(code) is exec(code, globals(), locals()). At top level, local and global dictionary is the same dictionary, so the top level function is correctly replaced. But if you do the same in a function, you only create a local function with the same name that can only be called from the function (it will go out of scope when the function will return) as locals()['main'](). So you must either alter the global function by passing explicitely the global dictionary:
exec(compile(ExceptFilter().visit(node), "", mode="exec"), globals(), globals())
or return the modified function without altering the original one:
def myfun():
# print(main)
node = ast.parse(inspect.getsource(main))
exec(compile(ExceptFilter().visit(node), "", mode="exec"))
# print(main, locals()['main'], globals()['main'])
return locals()['main']
>>> m2 = myfun()
>>> m2()
foo
f3 bar
(*) Python 3.6 contains an unparser in Tools/parser, but a simpler to use version exists in pypi
You could use a callback, like this:
def main(list_of_funcs):
for func in list_of_funcs:
try:
func()
except Exception as e:
print(e)
if __name__ == "__main__":
main([func1, func2, func3])

Decorator over a nose test case that yields

I have the following decorator that is supposed to wrap the implementation of test case functions within a try/except block and print the log if an exception occurs.
def print_log(test_case):
#wraps(test_case)
def run_test(self):
try:
test_case(self)
except:
Test_Loop.failure_teardown(self)
raise
return run_test
This however does not seem to work on one of my test cases that calls a yield generator
Please bear with me as this is a basic example:
class Test_Loop:
# ton of implementation here (e.g. initialization, etc)
def runIt(self, name, ip, port):
# code here
#print_log
def test_log_looper(self):
for l in self.links:
# initialize variables seen below and other stuff
for n in names:
# do stuff
for i in ips:
# do stuff
for p in ports:
yield self.runIt, l, n, i, p
From debugging, when the decorator is applied, it seems that it does not even enter the first loop. What am I doing wrong?
You need to iterate over your generator. Modify your decorator like this:
def print_log(test_case):
#wraps(test_case)
def run_test(self):
try:
for _ in test_case(self): pass
except:
Test_Loop.failure_teardown(self)
raise
return run_test

Improvizing a drop-in replacement for the "with" statement for Python 2.4

Can you suggest a way to code a drop-in replacement for the "with" statement that will work in Python 2.4?
It would be a hack, but it would allow me to port my project to Python 2.4 more nicely.
EDIT:
Removed irrelevant metaclass sketch
Just use try-finally.
Really, this may be nice as a mental exercise, but if you actually do it in code you care about you will end up with ugly, hard to maintain code.
You could (ab)use decorators to do this, I think. The following works, eg:
def execute_with_context_manager(man):
def decorator(f):
target = man.__enter__()
exc = True
try:
try:
f(target)
except:
exc = False
if not man.__exit__(*sys.exc_info()):
raise
finally:
if exc:
man.__exit__(None, None, None)
return None
return decorator
#execute_with_context_manager(open("/etc/motd"))
def inside(motd_file):
for line in motd_file:
print line,
(Well, in Python 2.4 file objects don't have __enter__ and __exit__ methods, but otherwise it works)
The idea is you're replacing the with line in:
with bar() as foo:
do_something_with(foo)
do_something_else_with(foo)
# etc...
with the decorated function "declaration" in:
#execute_with_context_manager( bar() )
def dummyname( foo ):
do_something_with(foo)
do_something_else_with(foo)
# etc...
but getting the same behaviour (the do_something_... code executed). Note the decorator changes the function declaration into an immediate invocation which is more than a little evil.
Since you need to exit the context manager both during errors and not errors, I don't think it's possible to do a generic usecase with metaclasses, or in fact at all. You are going to need try/finally blocks for that.
But maybe it's possible to do something else in your case. That depends on what you use the context manager for.
Using __del__ can help in some cases, like deallocating resource, but since you can't be sure it gets called, it can only be used of you need to release resources that will be released when the program exits. That also won't work if you are handling exceptions in the __exit__ method.
I guess the cleanest method is to wrap the whole context management in a sort of context managing call, and extract the code block into a method. Something like this (untested code, but mostly stolen from PEP 343):
def call_as_context_manager(mgr, function):
exit = mgr.__exit__
value = mgr.__enter__()
exc = True
try:
try:
function(value)
except:
exc = False
if not exit(*sys.exc_info()):
raise
finally:
if exc:
exit(None, None, None)
How about this?
def improvize_context_manager(*args, **kwargs):
assert (len(args) + len(kwargs)) == 1
if args:
context_manager = args[0]
as_ = None
else: # It's in kwargs
(as_, context_manager) = kwargs.items()[0]
def decorator(f):
exit_ = context_manager.__exit__ # Not calling it yet
enter_ = context_manager.__enter__()
exc = True
try:
try:
if as_:
f(*{as_: enter_})
else:
f()
except:
exc = False
if not exit_(*sys.exc_info()):
raise
finally:
if exc:
exit_(None, None, None)
return None
return decorator
Usage:
#improvize_context_manager(lock)
def null():
do(stuff)
Which parallels the with keyword without as.
Or:
#improvize_context_manager(my_lock=lock)
def null(my_lock):
do(stuff_with, my_lock)
Which parallels the with keyword with the as.
If you are OK with using def just to get a block, and decorators that immediately execute, you could use the function signature to get something more natural for the named case.
import sys
def with(func):
def decorated(body = func):
contexts = body.func_defaults
try:
exc = None, None, None
try:
for context in contexts:
context.__enter__()
body()
except:
exc = sys.exc_info()
raise
finally:
for context in reversed(contexts):
context.__exit__(*exc)
decorated()
class Context(object):
def __enter__(self):
print "Enter %s" % self
def __exit__(self, *args):
print "Exit %s(%s)" % (self, args)
x = Context()
#with
def _(it = x):
print "Body %s" % it
#with
def _(it = x):
print "Body before %s" % it
raise "Nothing"
print "Body after %s" % it

Categories

Resources