In Python, how can I print the current call stack from within a method (for debugging purposes).
Here's an example of getting the stack via the traceback module, and printing it:
import traceback
def f():
g()
def g():
for line in traceback.format_stack():
print(line.strip())
f()
# Prints:
# File "so-stack.py", line 10, in <module>
# f()
# File "so-stack.py", line 4, in f
# g()
# File "so-stack.py", line 7, in g
# for line in traceback.format_stack():
If you really only want to print the stack to stderr, you can use:
traceback.print_stack()
Or to print to stdout (useful if want to keep redirected output together), use:
traceback.print_stack(file=sys.stdout)
But getting it via traceback.format_stack() lets you do whatever you like with it.
import traceback
traceback.print_stack()
for those who need to print the call stack while using pdb, just do
(Pdb) where
inspect.stack() returns the current stack rather than the exception traceback:
import inspect
print inspect.stack()
See https://gist.github.com/FredLoney/5454553 for a log_stack utility function.
If you use python debugger, not only interactive probing of variables but you can get the call stack with the "where" command or "w".
So at the top of your program
import pdb
Then in the code where you want to see what is happening
pdb.set_trace()
and you get dropped into a prompt
Here's a variation of #RichieHindle's excellent answer which implements a decorator that can be selectively applied to functions as desired. Works with Python 2.7.14 and 3.6.4.
from __future__ import print_function
import functools
import traceback
import sys
INDENT = 4*' '
def stacktrace(func):
#functools.wraps(func)
def wrapped(*args, **kwds):
# Get all but last line returned by traceback.format_stack()
# which is the line below.
callstack = '\n'.join([INDENT+line.strip() for line in traceback.format_stack()][:-1])
print('{}() called:'.format(func.__name__))
print(callstack)
return func(*args, **kwds)
return wrapped
#stacktrace
def test_func():
return 42
print(test_func())
Output from sample:
test_func() called:
File "stacktrace_decorator.py", line 28, in <module>
print(test_func())
42
Install Inspect-it
pip3 install inspect-it --user
Code
import inspect;print(*['{:40}| {}:{}\n'.format(x.function, x.filename, x.lineno) for x in inspect.stack()])
you can Make a snippet of this line
it will show you a list of the function call stack with a filename and line number
list from start to where you put this line
Related
I have developed a python framework that is being used by others. In order to print any data to the output, the developer should use a Log class (Log.print(...)) and should not use the print() method directly. Is there any ways to force this rule throughout the code? For example, by throwing an error when a developer uses the print method directly like this:
Error: print method cannot be called directly. Please use Log.print().
Suppressing print (as discussed here) is not a good idea as the developer might get confused.
Actullay, below two line code are the same:
sys.stdout.write('hello'+'\n')
print('hello')
so, you can redirect sys.stdout to a class which raise a exception at calling print.
import sys
class BlockPrint():
call_print_exception = Exception('Error: print method cannot be called directly. Please use Log.print().')
def write(self, str):
raise self.call_print_exception
bp = BlockPrint()
sys.stdout=bp
print('aaa')
Output:
Traceback (most recent call last):
File "p.py", line 12, in <module>
print('aaa')
File "p.py", line 7, in write
raise self.call_print_exception
Exception: Error: print method cannot be called directly. Please use Log.print().
I'm debugging a large Python codebase. Somewhere, a piece of code is printing {} to console, presumably this is some old debugging code that's been left in by accident.
As this is the only console output that doesn't go through logger, is there any way I can find the culprit? Perhaps by redefining what the print statement does, so I can cause an exception?
Try to redirect sys.stdout to custom stream handler (see Redirect stdout to a file in Python?), where you can override write() method.
Try something like this:
import io
import sys
import traceback
class TestableIO(io.BytesIO):
def __init__(self, old_stream, initial_bytes=None):
super(TestableIO, self).__init__(initial_bytes)
self.old_stream = old_stream
def write(self, bytes):
if 'bb' in bytes:
traceback.print_stack(file=self.old_stream)
self.old_stream.write(bytes)
sys.stdout = TestableIO(sys.stdout)
sys.stderr = TestableIO(sys.stderr)
print('aa')
print('bb')
print('cc')
Then you will get nice traceback:
λ python test.py
aa
File "test.py", line 22, in <module>
print('bb')
File "test.py", line 14, in write
traceback.print_stack(file=self.old_stream)
bb
cc
I have code for cassandra python driver.
from cassandra.cqlengine.management import sync_table
def sync_my_tables():
print sync_table
print "*" * 80
sync_table(my_models.student)
When I try to write UT for this, i mocked sync_table using #patch.
from unittest import TestCase
from mock import patch
class TestCassandraSetup(TestCase):
#patch('cassandra.cqlengine.management.sync_table', return_value=True)
def test_sync_my_tables(self, _):
from cassandra.cqlengine.management import sync_table
print "*"*80
print sync_table
print "*"*80
cass_setup.sync_my_tables()
After patch, it call actual function and give error.
Traceback (most recent call last):
File "/venv/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
return func(*args, **keywargs)
File "/venv/tests/test_cassandra_setup.py", line 26, in test_sync_database_tables
cassandra_client.sync_database_tables()
File "/venv/code/cass_setup.py", line 18, in sync_my_tables
sync_table(my_tables.student)
File "/venv/lib/python2.7/site-packages/cassandra/cqlengine/management.py", line 200, in sync_table
cluster = get_cluster()
File "/venv/lib/python2.7/site-packages/cassandra/cqlengine/connection.py", line 182, in get_cluster
raise CQLEngineException("%s.cluster is not configured. Call one of the setup or default functions first." % __name__)
CQLEngineException: cassandra.cqlengine.connection.cluster is not configured. Call one of the setup or default functions first.
-------------------- >> begin captured stdout << ---------------------
********************************************************************************
<MagicMock name='sync_table' id='4490003152'>
********************************************************************************
<function sync_table at 0x10b8075f0>
********************************************************************************
In print statement, it print MagicMock first time, but when print same in actual code, it print actual function not mocked object.
Whey it change in between ?
When using patch you need to patch the object where it is used. So if sync_my_tables is located in a file with a path of foo/bar/baz.py you will need to call patch like this:
#patch('foo.bar.baz.sync_table')
def test_sync_table(self, _):
# test code
Python cannot mock (replace) things that are already in the module scope. Once imported, you cannot change it from the outside. To make the code testable, you need to import the higher level module and use the method from there as then it becomes just a property that can be changed.
from cassandra.cqlengine import management
def sync_my_tables():
print management.sync_table
print "*" * 80
management.sync_table(my_models.student)
This way mock is capable of replacing function at runtime (just does management.sync_table = MagicMock()).
In your test function, you are doing the import after the function has been replaced so it works as expected.
A package that I'm using in my python program is throwing a warning that I'd like to understand the exact cause of. I've set logging.captureWarning(True) and am capturing the warning in my logging, but still have no idea where it is coming from. How do I also log the stack trace so I can see where in my code the warning is coming from? Do I use traceback?
I've ended up going with the below:
import warnings
import traceback
_formatwarning = warnings.formatwarning
def formatwarning_tb(*args, **kwargs):
s = _formatwarning(*args, **kwargs)
tb = traceback.format_stack()
s += ''.join(tb[:-1])
return s
warnings.formatwarning = formatwarning_tb
logging.captureWarnings(True)
It's a little hackish, but you can monkeypatch the warnings.warn method to this:
import traceback
import warnings
def g():
warnings.warn("foo", Warning)
def f():
g()
warnings.warn("bar", Warning)
_old_warn = warnings.warn
def warn(*args, **kwargs):
tb = traceback.extract_stack()
_old_warn(*args, **kwargs)
print("".join(traceback.format_list(tb)[:-1]))
warnings.warn = warn
f()
print("DONE")
This is the output:
/tmp/test.py:14: Warning: foo
_old_warn(*args, **kwargs)
File "/tmp/test.py", line 17, in <module>
f()
File "/tmp/test.py", line 8, in f
g()
File "/tmp/test.py", line 5, in g
warnings.warn("foo", Warning)
/tmp/test.py:14: Warning: bar
_old_warn(*args, **kwargs)
File "/tmp/test.py", line 17, in <module>
f()
File "/tmp/test.py", line 9, in f
warnings.warn("bar", Warning)
DONE
See that calling the original warnings.warn function does not report the line you'd want, bu the stack trace is indeed correct (you could print the warning message yourself).
If you do not know what data/instruction is causing the warning throw, you can use tools like the standard Python Debugger.
The documentation is really good and detailed, but some quickly examples that may help should be:
Without modifying source code: invoking the debbugger as script:
$ python -m pdb myscript.py
Modifying source code: you can make use of calls to pdb.set_trace(), that work like breakpoints; For example, consider I have the following example code:
x = 2
x = x * 10 * 100
y = x + 3 + y
return y
And I would like to know what value does x and y have before the return, or what does the stack contains, I would add the following line between those statements:
pdb.set_trace()
And I will be promted to the (Pdb) prompt, that will allow you to go through the code line by line. Useful commands for the (Pdb) prompt are:
n: executes the next statement.
q: quits the whole program.
c: quits the (Pdb) prompt and stops debugging.
p varname: prints the value of varname
As you do not provide more information, I do not know if that should be enough, but I think that at least, it may be a good start.
BONUS EDIT
Based on this answer, I have found there is a nice and friendly GUI debugging tool, that you can simply install by:
$ pip install pudb
And run the debugger with your script with:
$ python -m pudb.run myscript.py
EDIT: Adding the postmortem debugging
If we do not even know if the code is going to crash or not, we can enter in postmortem debugging if there has been a crash. From the Pbd documentation:
The typical usage to inspect a crashed program is:
>>> import pdb
>>> import mymodule
>>> mymodule.test()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "./mymodule.py", line 4, in test
test2()
File "./mymodule.py", line 3, in test2
print spam
NameError: spam
>>> pdb.pm()
> ./mymodule.py(3)test2()
-> print spam
(Pdb)
As postmortem looks at sys.last_traceback, to enter only if there is a traceback (and so on, a warning or crash):
if sys.last_traceback:
pdb.pm()
You can turn warnings into exceptions, which means you will get a stack trace automatically:
warnings.filterwarnings("error")
See https://docs.python.org/3.4/library/warnings.html#the-warnings-filter
If it was me, I'd go with #Lluís Vilanova's quick & dirty hack, just to find something. But if that's not an option...
If you really want a "logging" solution, you could try something like this (fully working source).
Basic steps are:
Create a custom logging.Formatter subclass that includes the current stack where the logging record is formatted
Use that formatter on the class of the warning
The meat of the code is the custom formatter:
class Formatter(logging.Formatter):
def format(self, record):
record.stack_info = ''.join(traceback.format_stack())
return super().format(record)
Per the docs:
New in version 3.2: The stack_info parameter was added.
For python 3.2 and above, using the optional stack_info keyword argument is the easiest way to get stack trace info along with the log message.
In the example below, "Server.py" is using "lib2.py", which is in turn using "lib.py".
On enabling the stack_info argument the complete trace back is logged along with every logging.log() call. This works the same with logging.info() and other convenience methods as well.
Usage :-
logging.log(DEBUG, "RWL [{}] : acquire_read()".format(self._ownerName), stack_info=True)
Output :-
2018-10-06 10:59:55,726|DEBUG|MainThread|lib.py|acquire_read|RWL [Cache] : acquire_read()
Stack (most recent call last):
File "./Server.py", line 41, in <module>
logging.info("Found {} requests for simulation".format(simdata.count()))
File "<Path>\lib2.py", line 199, in count
with basics.ReadRWLock(self.cacheLock):
File "<Path>\lib.py", line 89, in __enter__
self.rwLock.acquire_read()
File "<Path>\lib.py", line 34, in acquire_read
logging.log(DEBUG, "RWL [{}] : acquire_read()".format(self._ownerName), stack_info=True)
I'm running PyCharm Community Edition 4.0.4
Does anyone know why the error messages don't display after the console output?
Thanks
C:\Python27\python.exe "F:/Google Drive/code/python_scripts/leetcode/lc_127_word_ladder.py"
Traceback (most recent call last):
START
File "F:/Google Drive/code/python_scripts/leetcode/lc_127_word_ladder.py", line 68, in <module>
print sol.ladderLength('talk', 'tail', set)
Graph:
File "F:/Google Drive/code/python_scripts/leetcode/lc_127_word_ladder.py", line 54, in ladderLength
hall ['fall']
for item in graph[node[0]]:
fall ['hall']
KeyError: 'talk'
End Graph:
Visited = {'talk': 0}
Node = ['talk', 0]
Queue Before = deque([])
Process finished with exit code 1
If you'll notice, print statements such as START, Graph:, hall ['fall'], up to Queue Before = deque([]) all happen within the functioning part of my code. The Error messages should appear after all this.
This is caused by PyCharm mixing print statements from stdout and stderr. There's a fix if you add the following line to your idea.properties file:
output.reader.blocking.mode=true
Get to idea.properties via Help | Edit Custom Properties.
might just be an issue with the stdout.
a workaround would be to use sys.flush.stdout() after your print statements.
import sys
do_something()
print("Your print statement")
sys.stdout.flush()
I'm new to pycharm, so not sure if there's a clean way to do this. But as a workaround, you could replace your print function with a custom one that sleeps quickly after printing, then your traceback should appear after your outputs.
import time
print = (lambda p: lambda *args,**kwargs: [p(*args,**kwargs), time.sleep(.01)])(print)
'''
# the above is just a one liner equivalent to this decorator
def add_sleep(p):
def new_p(*args, **kwargs):
p(*args,**kwargs)
time.sleep(.01)
return new_p
print = add_sleep(print)
'''