Getting a hold of locals() from surrounding frame - python

I'm trying to replace my Template(s).substitute("$a,$b", locals()) with something short like
sub("$a,$b")
However, I don't have access to locals of surrounding scope inside sub(), any idea how to get them?
One possible workaround I found is to throw an exception, catch it, and step along the frames to find the previous frame, but perhaps there's an easier way?
import traceback, sys, code
try:
2/0
except Exception as e:
type, value, tb = sys.exc_info()
traceback.print_exc()
last_frame = lambda tb=tb: last_frame(tb.tb_next) if tb.tb_next else tb
frame = last_frame().tb_frame
ns = dict(frame.f_globals)

Try using sys._current_frames() instead of raising exception.
Possible alternatives: sys._getframe(), inspect.currentframe(), inspect.stack()
I cant think of better solution, than analysing frames.

You can access it directly viasys._getframe(), although it's only guaranteed to work with CPython.
from string import Template
import sys
def sub(template):
namespace = sys._getframe(1).f_locals # caller's locals
return Template(template).substitute(namespace)
a, b = 1, 42
print sub("$a,$b") # -> 1,42

Related

Raising custom exceptions and output

I have the following code from a tutorial:
class Ex(Exception):
def __init__(self,msg):
Exception.__init__(self,msg+msg)
self.args=(msg,)
try:
raise Ex('ex')
except Ex as e:
print(e)
The question is what the output will be? I thought it would be exex. However, the output is ex. Now I am not understanding the construction. As far as I can see I can ignore the self.args=(msg,). This does not matter. What I am not understanding is the line Exception.__init__(self,msg+msg). I am not that used to exceptions.
when calling Exception.__init__(self,msg+msg) the msg+msg (in this case exex is stored in the args attribute. by doing self.args=(msg,) afterwards, you are overwriting the previous assignment of the args attribute. if you want to print exex instead of just ex, simply remove self.args=(msg,)

Should I use finally after try/except?

I have a bunch of functions similar to this structure:
def df():
try:
foo = #do some computation
except Exception:
foo = #do other computation
return foo
I was wondering what would be the difference with this other implementation:
def df():
try:
foo = #do some computation
except Exception:
foo = #do other computation
finally:
return foo
What should I use in this case? I see it a little bit redundant and also I'm concerned of the time execution, because I have many more functions with this same architecture and I don't know if adding finally would increase the execution time too much or not.
If you are catching a generic exception like that and not throwing it back to the calling method then both are functionally the same. The finally keyword is guaranteed to run after the try/catch has processed so in those examples, it makes no real difference. Typically, the finally keyword is used to ensure thread state or connection closures after execution of the try/catch block. If those are truly representative of your code then I wouldn’t use finally.
"finally" is executed even if the exception is raised. In your specific case it wouldn't be required.

How to target an Exception more accurately?

Consider the (compressed for the sake of example) code below:
import ics
import arrow
import requests
a = min(list(ics.Calendar(requests(get('http://asitewithcalendar.org').text).timeline.on(arrow.now())))
Quite a lot of things are happening here, I am fine with issues (connection, problems with the URL, ...) crashing the code but not with the following error:
ValueError: min() arg is an empty sequence
I would like to catch that specific error: the fact that what is provided to min() is an empty sequence (and pass on it). Even more specifically, I want the other exceptions to crash, including ValueError ones that are not related to the empty sequence fed to min().
A straightforward try catching ValueError would be fine for everything except the last constraint.
Is there a way to say "except ValueError when the error is min() arg is an empty sequence"?
Note: I know that the code in my example is ugly - I wrote it to showcase my question so if the only answer is "impossible - you have to rewrite it to pinpoint the line you want to try" then fine, otherwise I am looking for general solutions
You can do something like:
try:
# Put your code to try here
a = min(list(ics.Calendar(requests(get('http://asitewithcalendar.org').text).timeline.on(arrow.now())))
except ValueError as e:
if str(e) == 'min() arg is an empty sequence':
pass
else:
raise e
This is a case where I would simply check the value before calling min rather than wait for an exception. There is no expression-level way to handle exceptions.
foo = list(ics.Calendar(requests(get('http://asitewithcalendar.org').text).timeline.on(arrow.now()))
if foo:
a = min(foo)
It remains to decide what a should be if foo is empty, but you would have the same problem with a try statement:
foo = list(ics.Calendar(requests(get('http://asitewithcalendar.org').text).timeline.on(arrow.now()))
try:
a = min(foo)
except ValueError:
???
I also wouldn't worry too much about only dealing with empty-sequence errors. Even if it is a different ValueError, a is just as undefined.
how about this.
import numpy
a = min(list(ics.Calendar(requests(get('http://asitewithcalendar.org').text).timeline.on(arrow.now())) + [-np.inf])
when -inf has returned. list has nothing inside it.

Using try exception/catch in the method definition or in the calling method?

Which of the following snippet codes is common?
#1:
def foo():
try:
pass # Some process
except Exception as e:
print(e)
foo()
#2:
def foo():
pass # Some process
try:
foo()
except Exception as e:
print(e)
It depends on what foo does, and the type of Exception, i'd say.
Should the caller handle it or should the method?
For instance, consider the following example:
def try_get_value(registry, key):
try:
return registry[key]
except KeyError:
return None
This function will attempt to fetch a value from a dictionary using its key. If the value is not there, it should return None.
The method should handle KeyError, because it needs to return None when this happens, so as to comply with its expected behavior. (It's the method's responsability to catch this error)
But think of other exception types, such as TypeError (e.g., if the registry is not a dict).
Why should our method handle that? That's the caller mess-up. He should handle that, and he should worry about that.
Besides, what can our method do if we get such Exception? There's no way we can handle that from this scope.
try_get_value has one simple task: to get a value from the registry (a default one if there is none). It's not responsible for the caller breaking the rules.
So we don't catch TypeError because it's not our responsability.
Hence, the caller's code may look like something like this:
try:
value = try_get_value(reg, 'some_key')
# Handle value
except TypeError:
# reg is not a dict, do something about it...
P.S.: There may be times when our foo method needs to do some cleanup if there is an unexpected exit (e.g. it has allocated some resources which would leak if not closed).
In this case, foo should catch the exceptions, just so it can fix its state appropriately, but should then raise them back again to the caller.
I think the first part is cleaner and more elegant. Also more logical because as an implementer of the function, you want to handle all exceptions that it might throw rather than leave it to the client or caller. Even if you'll be the only one using the method, you still want to handle exceptions inside the function as in the future you may not remember what exception it is throwing.

Why can't I pickle an error's Traceback in Python?

I've since found a work around, but still want to know the answer.
The traceback holds references to the stack frames of each function/method that was called on the current thread, from the topmost-frame on down to the point where the error was raised. Each stack frame also holds references to the local and global variables in effect at the time each function in the stack was called.
Since there is no way for pickle to know what to serialize and what to ignore, if you were able to pickle a traceback you'd end up pickling a moving snapshot of the entire application state: as pickle runs, other threads may be modifying the values of shared variables.
One solution is to create a picklable object to walk the traceback and extract only the information you need to save.
You can use tblib
try:
1 / 0
except Exception as e:
raise Exception("foo") from e
except Exception as e:
s = pickle.dumps(e)
raise pickle.loads(s)
I guess you are interested in saving the complete call context (traceback + globals + locals of each frame).
That would be very useful to determine a difference of behavior of the same function in two different call contexts, or to build your own advanced tools to process, show or compare those tracebacks.
The problem is that pickl doesn't know how to serialize all type of objects that could be in the locals or globals.
I guess you can build your own object and save it, filtering out all those objects that are not picklabe. This code can serve as basis:
import sys, traceback
def print_exc_plus():
"""
Print the usual traceback information, followed by a listing of all the
local variables in each frame.
"""
tb = sys.exc_info()[2]
while 1:
if not tb.tb_next:
break
tb = tb.tb_next
stack = []
f = tb.tb_frame
while f:
stack.append(f)
f = f.f_back
stack.reverse()
traceback.print_exc()
print "Locals by frame, innermost last"
for frame in stack:
print
print "Frame %s in %s at line %s" % (frame.f_code.co_name,
frame.f_code.co_filename,
frame.f_lineno)
for key, value in frame.f_locals.items():
print "\t%20s = " % key,
#We have to be careful not to cause a new error in our error
#printer! Calling str() on an unknown object could cause an
#error we don't want.
try:
print value
except:
print "<ERROR WHILE PRINTING VALUE>"
but instead of printing the objects you can add them to a list with your own pickable representation ( a json or yml format might be better).
Maybe you want to load all this call context in order to reproduce the same situation for your function without run the complicated workflow that generate it. I don't know if this can be done (because of memory references), but in that case you would need to de-serialize it from your format.

Categories