19

Is there any way to get the output of dis.dis() without redirecting sys.stdout? I have tried:

out=str(dis.dis())

and

out=""""""
out+=str(dis.dis())

However I soon found out that it returns None. Is there any way to fix this?

Dan Getz
  • 8,774
  • 6
  • 30
  • 64
IT Ninja
  • 6,174
  • 10
  • 42
  • 65
  • Why don't you want to redirect stdout (temporarily), as in [this question](http://stackoverflow.com/questions/1218933/can-i-redirect-the-stdout-in-python-into-some-sort-of-string-buffer)? – David Robinson Aug 24 '12 at 14:55
  • For Python 3.4 or later, there are other solutions. See [this question](http://stackoverflow.com/q/31347044/3004881). – Dan Getz Jul 10 '15 at 18:02

2 Answers2

27

Unfortunately, in Python versions before 3.4 the dis module uses print statements to stdout, so it won't return anything directly useful. Either you have to re-implement the dis, disassemble and disassemble_string functions, or you temporarily replace sys.stdout with an alternative to capture the output:

import sys
from cStringIO import StringIO

out = StringIO()
stdout = sys.stdout
sys.stdout = out
try:
    dis.dis()
finally:
    sys.stdout = stdout
out = out.getvalue()

This is actually best done using a context manager:

import sys
from contextlib import contextmanager
from cStringIO import StringIO

@contextmanager
def captureStdOut(output):
    stdout = sys.stdout
    sys.stdout = output
    try:
        yield
    finally:
        sys.stdout = stdout

out = StringIO()
with captureStdOut(out):
    dis.dis()
print out.getvalue()

That way you are guaranteed to have stdout restored even if something goes wrong with dis. A little demonstration:

>>> out = StringIO()
>>> with captureStdOut(out):
...     dis.dis(captureStdOut)
... 
>>> print out.getvalue()
 83           0 LOAD_GLOBAL              0 (GeneratorContextManager)
              3 LOAD_DEREF               0 (func)
              6 LOAD_FAST                0 (args)
              9 LOAD_FAST                1 (kwds)
             12 CALL_FUNCTION_VAR_KW     0
             15 CALL_FUNCTION            1
             18 RETURN_VALUE        

In Python 3.4 and up, the relevant functions take a file parameter to redirect output to:

from io import StringIO

with StringIO() as out:
    dis.dis(file=out)
    print(out.getvalue())
oz123
  • 27,559
  • 27
  • 125
  • 187
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • (+1) : Using the context manager here is genius. Beautiful. – mgilson Aug 24 '12 at 15:08
  • I don't think you need to save `sys.stdout` in most cases, you can just restore it using `sys._stdout`. – agf May 15 '13 at 17:16
  • @agf: that presumes that `sys.stdout` was not already replaced.. `sys.__stdout__` is the *default* value for `sys.stdout` but that does not mean that `sys.stdout` is *currently* set to that value. – Martijn Pieters May 15 '13 at 17:16
  • @MartijnPieters I know, that's why I said "in most cases." – agf May 15 '13 at 17:17
  • 4
    @agf: When writing generic code you need to account for those corner cases too.. Code from answers on SO is copied all over, so I try to write stuff that can be *used* all over. :-) – Martijn Pieters May 15 '13 at 17:20
  • @MartijnPieters I wasn't saying your code was wrong, I was pointing out that line wasn't necessary in most cases. – agf May 15 '13 at 20:54
  • @agf: I never took it as you saying I was wrong. :-) I'm just motivating my insistence to qualify why the code does what it does. – Martijn Pieters May 15 '13 at 21:00
  • What if an exception is thrown inside the `captureStdOut` context but caught outside it (i.e. the entire context is inside a `try` block)? Wouldn't `stdout` not be restored? – buzjwa Nov 22 '17 at 10:55
0

If using Colab / Jupyter

In one cell you can run dis redirecting the output to a variable

%%capture dis_output
# dis : Disassembler for Python bytecode
from dis import dis
dis(function_to_check)

And then you can use it from Bash. Here an example

! echo  "{dis_output.stdout}" | grep -i global

14           4 LOAD_GLOBAL              0 (AudioLibrary)
             36 LOAD_GLOBAL              2 (userLanguageAudio)
             50 LOAD_GLOBAL              2 (userLanguageAudio)
             62 LOAD_GLOBAL              3 (LESSON_FILES_DIR)
 27          76 LOAD_GLOBAL              4 (get_ipython)
 30          96 LOAD_GLOBAL              6 (pread)
 31         104 LOAD_GLOBAL              7 (print)
 34     >>  124 LOAD_GLOBAL              7 (print)

Or from Python

print(dis_output.stdout)
Rub
  • 2,071
  • 21
  • 37