Commit 5c8b54eb authored by Nick Coghlan's avatar Nick Coghlan

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

parent 9bf2b3ae
...@@ -33,10 +33,13 @@ The :mod:`dis` module defines the following functions and constants: ...@@ -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)
......
...@@ -12,6 +12,22 @@ del _opcodes_all ...@@ -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): ...@@ -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): ...@@ -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, ...@@ -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):
......
...@@ -96,6 +96,46 @@ Disassembly of g: ...@@ -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): ...@@ -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)
......
...@@ -797,6 +797,7 @@ Doobee R. Tzeck ...@@ -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
......
...@@ -468,6 +468,8 @@ C-API ...@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment