Get last function's arguments from traceback? - python

I read Get last function's call arguments from traceback? but it is not specific enough to answer my problem.
This is really bothering me since not having the call arguments is slowing me down and I am pretty sure that it is possible to get this kind of info from Python.
Here is an example to illustrate the problem:
# -*- coding: utf-8 -*-
import sys
import traceback
import inspect
import logging as log
def fl(x):
# exception is happening here
y = 5/x
return y
def fm(x):
return fl(x-3)
def fn(a, b, c=None):
return fm(c)
def main():
try:
print fn(1, 2, c=3)
except Exception as e:
log.error('Unexpected problem.')
log.error(e)
traceback.print_exc()
### what I need to see is are the call arguments of the last / deepest call: ###
### def fl(x) was called with arguments: [(x, 3)] ###
# this does not cut it:
tb = sys.exc_info()[2]
traceback.print_tb(tb)
# this is broken:
#frames = inspect.getinnerframes(tb)
#log.error('Argvalues: %s', inspect.getargvalues(frames))
# not sure:
frames = inspect.trace()
argvalues = inspect.getargvalues(frames[0][0])
log.error('Argvalues: %s', inspect.formatargvalues(*argvalues))
if __name__ == '__main__':
main()
so I get details, but the call arguments are not contained:
ERROR:root:Unexpected problem.
ERROR:root:integer division or modulo by zero
Traceback (most recent call last):
File "sample.py", line 24, in main
print fn(1, 2, c=3)
File "sample.py", line 18, in fn
return fm(c)
File "sample.py", line 14, in fm
return fl(x-3)
File "sample.py", line 9, in fl
y = 5/x
ZeroDivisionError: integer division or modulo by zero
File "sample.py", line 24, in main
print fn(1, 2, c=3)
File "sample.py", line 18, in fn
return fm(c)
File "sample.py", line 14, in fm
return fl(x-3)
File "sample.py", line 9, in fl
y = 5/x
ERROR:root:Argvalues: ()

frames[0][0] represents main function. main is called without arguments, thats why you get empty tuple. Change it to frames[-1][0] to get last frame.

Related

Python pass variable to multiprocessing pool

I been trying to figure out this for ages, but wondering if anyone might know how I can pass the s variable to the pool without making it into an argument?
import ctypes
import multiprocessing as mp
import os
def worker1(n):
k = n*3
print(k)
print(s)
# print(ctypes_int.value)
if __name__ == '__main__':
# global s
somelist = [1,2,3]
# ctypes_int = mp.Value(ctypes.c_wchar_p , "hi")
s = "TESTING"
# worker1(1)
p = mp.Pool(1)
p.map(worker1,somelist)
This is the error I am getting:
3
6
9
multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
File "C:\Program Files\Python\Python37\lib\multiprocessing\pool.py", line 121, in worker
result = (True, func(*args, **kwds))
File "C:\Program Files\Python\Python37\lib\multiprocessing\pool.py", line 44, in mapstar
return list(map(*args))
File "C:\Users\light\AppData\Local\Temp\tempCodeRunnerFile.python", line 10, in worker1
print(s)
NameError: name 's' is not defined
"""
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Users\light\AppData\Local\Temp\tempCodeRunnerFile.python", line 21, in <module>
p.map(worker1,somelist)
File "C:\Program Files\Python\Python37\lib\multiprocessing\pool.py", line 268, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "C:\Program Files\Python\Python37\lib\multiprocessing\pool.py", line 657, in get
raise self._value
NameError: name 's' is not defined
You can pass your variable along with each item in somelist:
import multiprocessing as mp
def worker1(p):
n,s = p
k = n*3
print(k)
print(s)
if __name__ == '__main__':
somelist = [1,2,3]
s = "TESTING"
p = mp.Pool(1)
p.map(worker1,[(n,s) for n in somelist])
The parameter (n,s) gets passed as p and I unpack it into n,s.

bbox works when inputting exact coordinates within module but fails with variable containing identical data

I'm having an issue trying to make a screen grab of an area defined by lines in a config file:
The following code:
def coordstore(): # check line 1 of config file (screencap name)
f=open('config.txt')
line=f.readlines()
coordstore.x1,coordstore.y1,coordstore.x2,coordstore.y2 = (line[4]).strip(),(line[5]).strip(),(line[6]).strip(),(line[7]).strip() # note: confusing. 0=line 1, 1=line2 etc.
coordstore.coords = coordstore.x1+',',coordstore.y1+',',coordstore.x2+',',coordstore.y2
coordstore.stripped = ' '.join((coordstore.coords))
print(coordstore.stripped)
return(coordstore.stripped)
coordstore()
def screenshot():
import pyscreenshot as ImageGrab2
# part of the screen
if __name__ == '__main__':
im = ImageGrab2.grab(bbox=(coordstore.stripped)) # X1,Y1,X2,Y2
im.save('tc.png')
screenshot()
prints exactly this value: 10, 20, 100, 300
it then fails with this Traceback:
Traceback (most recent call last):
File "file location", line 82, in <module>
screenshot()
File "file location", line 80, in screenshot
im = ImageGrab2.grab(bbox=(coordstore.stripped)) # X1,Y1,X2,Y2
File "PYCHARM\venv\lib\site-packages\pyscreenshot\__init__.py", line 67, in grab
to_file=False, childprocess=childprocess, backend=backend, bbox=bbox)
File "PYCHARM\venv\lib\site-packages\pyscreenshot\__init__.py", line 38, in _grab
x1, y1, x2, y2 = bbox
ValueError: too many values to unpack (expected 4)
If I manually replace (bbox=(coordstore.stripped)) with (bbox=(10, 20, 100, 300)) which is literally identical to what the variable prints, the code works perfectly but is not useful for my needs. What am I not seeing? Thanks for the help!
UPDATE:
I have tried another approach.
def coordstore(): # check line 1 of config file (screencap name)
f=open('config.txt')
line=f.readlines()
coordstore.x1 = (line[4]).strip()
coordstore.y1 = (line[5]).strip()
coordstore.x2 = (line[6]).strip()
coordstore.y2 = (line[7]).strip()
print(coordstore.x1+',', coordstore.y1+',', coordstore.x2+',', coordstore.y2)
return(coordstore.x1, coordstore.y1, coordstore.x2, coordstore.y2)
coordstore()
def screenshot():
import pyscreenshot as ImageGrab2
# part of the screen
if __name__ == '__main__':
im = ImageGrab2.grab(bbox=(coordstore.x1+',', coordstore.y1+',', coordstore.x2+',', coordstore.y2+',')) # X1,Y1,X2,Y2
im.save('tc.png')
screenshot()
This prints
10, 20, 100, 300
but returns the following Traceback errors:
Traceback (most recent call last):
File "module location", line 84, in <module>
screenshot()
File "module location", line 82, in screenshot
im = ImageGrab2.grab(bbox=(coordstore.x1+',', coordstore.y1+',', coordstore.x2+',', coordstore.y2+',')) # X1,Y1,X2,Y2
File "\PYCHARM\venv\lib\site-packages\pyscreenshot\__init__.py", line 67, in grab
to_file=False, childprocess=childprocess, backend=backend, bbox=bbox)
File "\PYCHARM\venv\lib\site-packages\pyscreenshot\__init__.py", line 42, in _grab
raise ValueError('bbox y2<=y1')
ValueError: bbox y2<=y1
As you have mentioned in comments the value of coordstore.stripped is string. And expected argument by ImageGrab2.grab is type of tuple so, first you have to convert it into tuple.
Update the method like this:
def screenshot():
import pyscreenshot as ImageGrab2
# part of the screen
if __name__ == '__main__':
im = ImageGrab2.grab(bbox=tuple(map(int, coordstore.stripped.split(', ')))) # X1,Y1,X2,Y2
im.save('tc.png')

how to use the post_mortem method of pdb?

I am trying to understand how to use the pdb.post_mortem() method.
for this given file
# expdb.py
import pdb
import trace
def hello():
a = 6 * 9
b = 7 ** 2
c = a * b
d = 4 / 0
print(c)
tracer = trace.Trace()
Command prompt
'''
# first Try
λ python -i expdb.py
>>> pdb.post_mortem()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Program Files\Anaconda3\lib\pdb.py", line 1590, in post_mortem
raise ValueError("A valid traceback must be passed if no "
ValueError: A valid traceback must be passed if no exception is being handled
'''
'''
# Second Try
λ python -i expdb.py
>>> pdb.post_mortem(traceback=tracer.run('hello()') )
--- modulename: trace, funcname: _unsettrace
trace.py(77): sys.settrace(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Program Files\Anaconda3\lib\trace.py", line 500, in run
self.runctx(cmd, dict, dict)
File "C:\Program Files\Anaconda3\lib\trace.py", line 508, in runctx
exec(cmd, globals, locals)
File "<string>", line 1, in <module>
File "expdb.py", line 8, in hello
d = 4 / 0
ZeroDivisionError: division by zero
>>>
The post_mortem method wants a traceback object, not a Trace object. Traceback objects can be acquired from sys.exc_info()[2] inside of an except block, or you can simply call pdb.post_mortem() with no arguments directly (in the except block).
But either way, you must catch the exception before you can debug it.

Handling exceptions in while loop - Python

Here is my code (almost full version for #cdhowie :)):
def getResult(method, argument=None):
result = None
while True:
print('### loop')
try:
print ('### try hard...')
if argument:
result = method(argument)
else:
result = method()
break
except Exception as e:
print('### GithubException')
if 403 == e.status:
print('Warning: ' + str(e.data))
print('I will try again after 10 minutes...')
else:
raise e
return result
def getUsernames(locations, gh):
usernames = set()
for location in locations:
print location
result = getResult(gh.legacy_search_users, location)
for user in result:
usernames.add(user.login)
print user.login,
return usernames
# "main.py"
gh = Github()
locations = ['Washington', 'Berlin']
# "main.py", line 12 is bellow
usernames = getUsernames(locations, gh)
The problem is, that exception is raised, but I can't handle it. Here is an output:
### loop
### try hard...
Traceback (most recent call last):
File "main.py", line 12, in <module>
usernames = getUsernames(locations, gh)
File "/home/ciembor/projekty/github-rank/functions.py", line 39, in getUsernames
for user in result:
File "/usr/lib/python2.7/site-packages/PyGithub-1.8.0-py2.7.egg/github/PaginatedList.py", line 33, in __iter__
newElements = self.__grow()
File "/usr/lib/python2.7/site-packages/PyGithub-1.8.0-py2.7.egg/github/PaginatedList.py", line 45, in __grow
newElements = self._fetchNextPage()
File "/usr/lib/python2.7/site-packages/PyGithub-1.8.0-py2.7.egg/github/Legacy.py", line 37, in _fetchNextPage
return self.get_page(page)
File "/usr/lib/python2.7/site-packages/PyGithub-1.8.0-py2.7.egg/github/Legacy.py", line 48, in get_page
None
File "/usr/lib/python2.7/site-packages/PyGithub-1.8.0-py2.7.egg/github/Requester.py", line 69, in requestAndCheck
raise GithubException.GithubException(status, output)
github.GithubException.GithubException: 403 {u'message': u'API Rate Limit Exceeded for 11.11.11.11'}
Why it doesn't print ### handling exception?
Take a close look at the stack trace in the exception:
Traceback (most recent call last):
File "main.py", line 12, in <module>
usernames = getUsernames(locations, gh)
File "/home/ciembor/projekty/github-rank/functions.py", line 39, in getUsernames
for user in result:
File "/usr/lib/python2.7/site-packages/PyGithub-1.8.0-py2.7.egg/github/PaginatedList.py", line 33, in __iter__
newElements = self.__grow()
...
The exception is being thrown from code being called by the line for user in result: after getResult finishes executing. This means that the API you're using is using lazy evaluation, so the actual API request doesn't quite happen when you expect it to.
In order to catch and handle this exception, you'll need to wrap the code inside the getUsernames function with a try/except handler.

How to get a complete exception stack trace in Python

The following snippet:
import traceback
def a():
b()
def b():
try:
c()
except:
traceback.print_exc()
def c():
assert False
a()
Produces this output:
Traceback (most recent call last):
File "test.py", line 8, in b
c()
File "test.py", line 13, in c
assert False
AssertionError
What should I use if I want the complete stack trace including the call to a?
If it matters I have Python 2.6.6
edit: What I'd like to get is the same information I'd get if I left the try except out and let the exception propagate to the top level. This snippet for example:
def a():
b()
def b():
c()
def c():
assert False
a()
Produces this output:
Traceback (most recent call last):
File "test.py", line 10, in <module>
a()
File "test.py", line 2, in a
b()
File "test.py", line 5, in b
c()
File "test.py", line 8, in c
assert False
AssertionError
Here's a function based on this answer. It will also work when no exception is present:
def full_stack():
import traceback, sys
exc = sys.exc_info()[0]
stack = traceback.extract_stack()[:-1] # last one would be full_stack()
if exc is not None: # i.e. an exception is present
del stack[-1] # remove call of full_stack, the printed exception
# will contain the caught exception caller instead
trc = 'Traceback (most recent call last):\n'
stackstr = trc + ''.join(traceback.format_list(stack))
if exc is not None:
stackstr += ' ' + traceback.format_exc().lstrip(trc)
return stackstr
print full_stack() will print the full stack trace up to the top, including e.g. IPython's interactiveshell.py calls, since there is (to my knowledge) no way of knowing who would catch exceptions. It's probably not worth figuring out anyway...
If print full_stack() is called from within an except block, full_stack will include the stack trace down to the raise. In the standard Python interpreter, this will be identical to the message you receive when not catching the exception (Which is why that del stack[-1] is there, you don't care about the except block but about the try block).
I don't know if there is a better way, but here's what I did:
import traceback
import sys
def format_exception(e):
exception_list = traceback.format_stack()
exception_list = exception_list[:-2]
exception_list.extend(traceback.format_tb(sys.exc_info()[2]))
exception_list.extend(traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1]))
exception_str = "Traceback (most recent call last):\n"
exception_str += "".join(exception_list)
# Removing the last \n
exception_str = exception_str[:-1]
return exception_str
def main1():
main2()
def main2():
try:
main3()
except Exception as e:
print "Printing only the traceback above the current stack frame"
print "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
print
print "Printing the full traceback as if we had not caught it here..."
print format_exception(e)
def main3():
raise Exception()
if __name__ == '__main__':
main1()
And here's the output I get:
Printing only the traceback above the current stack frame
Traceback (most recent call last):
File "exc.py", line 22, in main2
main3()
File "exc.py", line 31, in main3
raise Exception()
Exception
Printing the full traceback as if we had not caught it here...
Traceback (most recent call last):
File "exc.py", line 34, in <module>
main1()
File "exc.py", line 18, in main1
main2()
File "exc.py", line 22, in main2
main3()
File "exc.py", line 31, in main3
raise Exception()
Exception
Use
traceback.print_stack()
http://docs.python.org/library/traceback.html#traceback.print_stack
suxmac2 $ python out.py
File "out.py", line 16, in <module>
a()
File "out.py", line 5, in a
b()
File "out.py", line 11, in b
traceback.print_stack()
Here is a bit better variant of Tobias Kienzler answer. It works same, but can be called not right in except block, but somewhere deeper.
In other words, this variant will print same stacks, when called like
try:
...
except Exception:
print full_stack()
or
def print_full_stack():
print full_stack()
try:
...
except Exception:
print_full_stack()
Here is code:
def full_stack():
import traceback, sys
exc = sys.exc_info()[0]
if exc is not None:
f = sys.exc_info()[-1].tb_frame.f_back
stack = traceback.extract_stack(f)
else:
stack = traceback.extract_stack()[:-1] # last one would be full_stack()
trc = 'Traceback (most recent call last):\n'
stackstr = trc + ''.join(traceback.format_list(stack))
if exc is not None:
stackstr += ' ' + traceback.format_exc().lstrip(trc)
return stackstr

Categories

Resources