Issue 6507: accept source strings directly in dis.dis(). Original patch by Daniel Urban

This commit is contained in:
Nick Coghlan 2010-07-03 07:36:51 +00:00
parent 9bf2b3ae6a
commit 5c8b54eb04
5 changed files with 79 additions and 6 deletions

View File

@ -33,10 +33,13 @@ The :mod:`dis` module defines the following functions and constants:
.. function:: dis(x=None) .. function:: dis(x=None)
Disassemble the *x* object. *x* can denote either a module, a Disassemble the *x* object. *x* can denote either a module, a
class, a method, a function, or a code object. For a module, it disassembles class, a method, a function, a code object, a string of source code or a
all functions. For a class, it disassembles all methods. For a single code byte sequence of raw bytecode. For a module, it disassembles all
sequence, it prints one line per bytecode instruction. If no object is functions. For a class, it disassembles all methods. For a code object
provided, it disassembles the last traceback. or sequence of raw bytecode, it prints one line per bytecode instruction.
Strings are first compiled to code objects with the :func:`compile`
built-in function before being disassembled. If no object is provided,
this function disassembles the last traceback.
.. function:: distb(tb=None) .. function:: distb(tb=None)

View File

@ -12,6 +12,22 @@ del _opcodes_all
_have_code = (types.MethodType, types.FunctionType, types.CodeType, type) _have_code = (types.MethodType, types.FunctionType, types.CodeType, type)
def _try_compile(source, name):
"""Attempts to compile the given source, first as an expression and
then as a statement if the first approach fails.
Utility function to accept strings in functions that otherwise
expect code objects
"""
# ncoghlan: currently only used by dis(), but plan to add an
# equivalent for show_code() as well (but one that returns a
# string rather than printing directly to the console)
try:
c = compile(source, name, 'eval')
except SyntaxError:
c = compile(source, name, 'exec')
return c
def dis(x=None): def dis(x=None):
"""Disassemble classes, methods, functions, or code. """Disassemble classes, methods, functions, or code.
@ -38,7 +54,9 @@ def dis(x=None):
elif hasattr(x, 'co_code'): elif hasattr(x, 'co_code'):
disassemble(x) disassemble(x)
elif isinstance(x, (bytes, bytearray)): elif isinstance(x, (bytes, bytearray)):
disassemble_string(x) _disassemble_bytes(x)
elif isinstance(x, str):
_disassemble_str(x)
else: else:
raise TypeError("don't know how to disassemble %s objects" % raise TypeError("don't know how to disassemble %s objects" %
type(x).__name__) type(x).__name__)
@ -157,7 +175,7 @@ def disassemble(co, lasti=-1):
print('(' + free[oparg] + ')', end=' ') print('(' + free[oparg] + ')', end=' ')
print() print()
def disassemble_string(code, lasti=-1, varnames=None, names=None, def _disassemble_bytes(code, lasti=-1, varnames=None, names=None,
constants=None): constants=None):
labels = findlabels(code) labels = findlabels(code)
n = len(code) n = len(code)
@ -196,6 +214,10 @@ def disassemble_string(code, lasti=-1, varnames=None, names=None,
print('(' + cmp_op[oparg] + ')', end=' ') print('(' + cmp_op[oparg] + ')', end=' ')
print() print()
def _disassemble_str(source):
"""Compile the source string, then disassemble the code object."""
disassemble(_try_compile(source, '<dis>'))
disco = disassemble # XXX For backwards compatibility disco = disassemble # XXX For backwards compatibility
def findlabels(code): def findlabels(code):

View File

@ -96,6 +96,46 @@ Disassembly of g:
""" """
expr_str = "x + 1"
dis_expr_str = """\
1 0 LOAD_NAME 0 (x)
3 LOAD_CONST 0 (1)
6 BINARY_ADD
7 RETURN_VALUE
"""
simple_stmt_str = "x = x + 1"
dis_simple_stmt_str = """\
1 0 LOAD_NAME 0 (x)
3 LOAD_CONST 0 (1)
6 BINARY_ADD
7 STORE_NAME 0 (x)
10 LOAD_CONST 1 (None)
13 RETURN_VALUE
"""
compound_stmt_str = """\
x = 0
while 1:
x += 1"""
# Trailing newline has been deliberately omitted
dis_compound_stmt_str = """\
1 0 LOAD_CONST 0 (0)
3 STORE_NAME 0 (x)
2 6 SETUP_LOOP 13 (to 22)
3 >> 9 LOAD_NAME 0 (x)
12 LOAD_CONST 1 (1)
15 INPLACE_ADD
16 STORE_NAME 0 (x)
19 JUMP_ABSOLUTE 9
>> 22 LOAD_CONST 2 (None)
25 RETURN_VALUE
"""
class DisTests(unittest.TestCase): class DisTests(unittest.TestCase):
def do_disassembly_test(self, func, expected): def do_disassembly_test(self, func, expected):
@ -166,6 +206,11 @@ class DisTests(unittest.TestCase):
from test import dis_module from test import dis_module
self.do_disassembly_test(dis_module, dis_module_expected_results) self.do_disassembly_test(dis_module, dis_module_expected_results)
def test_disassemble_str(self):
self.do_disassembly_test(expr_str, dis_expr_str)
self.do_disassembly_test(simple_stmt_str, dis_simple_stmt_str)
self.do_disassembly_test(compound_stmt_str, dis_compound_stmt_str)
def test_main(): def test_main():
run_unittest(DisTests) run_unittest(DisTests)

View File

@ -797,6 +797,7 @@ Doobee R. Tzeck
Eren Türkay Eren Türkay
Lionel Ulmer Lionel Ulmer
Roger Upole Roger Upole
Daniel Urban
Michael Urman Michael Urman
Hector Urtubia Hector Urtubia
Andi Vajda Andi Vajda

View File

@ -468,6 +468,8 @@ C-API
Library Library
------- -------
- Issue #6507: Accept source strings in dis.dis()
- Issue #7829: Clearly document that the dis module is exposing an - Issue #7829: Clearly document that the dis module is exposing an
implementation detail that is not stable between Python VMs or releases. implementation detail that is not stable between Python VMs or releases.