Issue #21947: handle generator-iterator objects in dis

Patch by Clement Rouault.
This commit is contained in:
Nick Coghlan 2014-07-25 23:02:56 +10:00
parent d0d64cfb59
commit efd5df9e52
5 changed files with 26 additions and 10 deletions

View File

@ -48,8 +48,8 @@ compiled code.
.. class:: Bytecode(x, *, first_line=None, current_offset=None) .. class:: Bytecode(x, *, first_line=None, current_offset=None)
Analyse the bytecode corresponding to a function, method, string of Analyse the bytecode corresponding to a function, generator, method,
source code, or a code object (as returned by :func:`compile`). string of source code, or a code object (as returned by :func:`compile`).
This is a convenience wrapper around many of the functions listed below, This is a convenience wrapper around many of the functions listed below,
most notably :func:`get_instructions`, as iterating over a most notably :func:`get_instructions`, as iterating over a
@ -112,7 +112,7 @@ object isn't useful:
.. function:: code_info(x) .. function:: code_info(x)
Return a formatted multi-line string with detailed code object information Return a formatted multi-line string with detailed code object information
for the supplied function, method, source code string or code object. for the supplied function, generator, method, source code string or code object.
Note that the exact contents of code info strings are highly implementation Note that the exact contents of code info strings are highly implementation
dependent and they may change arbitrarily across Python VMs or Python dependent and they may change arbitrarily across Python VMs or Python
@ -139,11 +139,11 @@ object isn't useful:
.. function:: dis(x=None, *, file=None) .. function:: dis(x=None, *, file=None)
Disassemble the *x* object. *x* can denote either a module, a class, a Disassemble the *x* object. *x* can denote either a module, a class, a
method, a function, a code object, a string of source code or a byte sequence method, a function, a generator, a code object, a string of source code or
of raw bytecode. For a module, it disassembles all functions. For a class, a byte sequence of raw bytecode. For a module, it disassembles all functions.
it disassembles all methods. For a code object or sequence of raw bytecode, For a class, it disassembles all methods. For a code object or sequence of
it prints one line per bytecode instruction. Strings are first compiled to raw bytecode, it prints one line per bytecode instruction. Strings are first
code objects with the :func:`compile` built-in function before being compiled to code objects with the :func:`compile` built-in function before being
disassembled. If no object is provided, this function disassembles the last disassembled. If no object is provided, this function disassembles the last
traceback. traceback.

View File

@ -29,7 +29,7 @@ def _try_compile(source, name):
return c return c
def dis(x=None, *, file=None): def dis(x=None, *, file=None):
"""Disassemble classes, methods, functions, or code. """Disassemble classes, methods, functions, generators, or code.
With no argument, disassemble the last traceback. With no argument, disassemble the last traceback.
@ -41,6 +41,8 @@ def dis(x=None, *, file=None):
x = x.__func__ x = x.__func__
if hasattr(x, '__code__'): # Function if hasattr(x, '__code__'): # Function
x = x.__code__ x = x.__code__
if hasattr(x, 'gi_code'): # Generator
x = x.gi_code
if hasattr(x, '__dict__'): # Class or module if hasattr(x, '__dict__'): # Class or module
items = sorted(x.__dict__.items()) items = sorted(x.__dict__.items())
for name, x1 in items: for name, x1 in items:
@ -99,11 +101,13 @@ def pretty_flags(flags):
return ", ".join(names) return ", ".join(names)
def _get_code_object(x): def _get_code_object(x):
"""Helper to handle methods, functions, strings and raw code objects""" """Helper to handle methods, functions, generators, strings and raw code objects"""
if hasattr(x, '__func__'): # Method if hasattr(x, '__func__'): # Method
x = x.__func__ x = x.__func__
if hasattr(x, '__code__'): # Function if hasattr(x, '__code__'): # Function
x = x.__code__ x = x.__code__
if hasattr(x, 'gi_code'): # Generator
x = x.gi_code
if isinstance(x, str): # Source code if isinstance(x, str): # Source code
x = _try_compile(x, "<disassembly>") x = _try_compile(x, "<disassembly>")
if hasattr(x, 'co_code'): # Code object if hasattr(x, 'co_code'): # Code object

View File

@ -229,6 +229,9 @@ dis_traceback = """\
TRACEBACK_CODE.co_firstlineno + 4, TRACEBACK_CODE.co_firstlineno + 4,
TRACEBACK_CODE.co_firstlineno + 5) TRACEBACK_CODE.co_firstlineno + 5)
def _g(x):
yield x
class DisTests(unittest.TestCase): class DisTests(unittest.TestCase):
def get_disassembly(self, func, lasti=-1, wrapper=True): def get_disassembly(self, func, lasti=-1, wrapper=True):
@ -314,6 +317,11 @@ class DisTests(unittest.TestCase):
method_bytecode = _C(1).__init__.__code__.co_code method_bytecode = _C(1).__init__.__code__.co_code
self.do_disassembly_test(method_bytecode, dis_c_instance_method_bytes) self.do_disassembly_test(method_bytecode, dis_c_instance_method_bytes)
def test_disassemble_generator(self):
gen_func_disas = self.get_disassembly(_g) # Disassemble generator function
gen_disas = self.get_disassembly(_g(1)) # Disassemble generator itself
self.assertEqual(gen_disas, gen_func_disas)
def test_dis_none(self): def test_dis_none(self):
try: try:
del sys.last_traceback del sys.last_traceback

View File

@ -1145,6 +1145,7 @@ Guido van Rossum
Just van Rossum Just van Rossum
Hugo van Rossum Hugo van Rossum
Saskia van Rossum Saskia van Rossum
Clement Rouault
Donald Wallace Rouse II Donald Wallace Rouse II
Liam Routt Liam Routt
Todd Rovito Todd Rovito

View File

@ -108,6 +108,9 @@ Core and Builtins
Library Library
------- -------
- Issue #21947: The dis module can now disassemble generator-iterator
objects based on their gi_code attribute. Patch by Clement Rouault.
- Issue #16133: The asynchat.async_chat.handle_read() method now ignores - Issue #16133: The asynchat.async_chat.handle_read() method now ignores
BlockingIOError exceptions. BlockingIOError exceptions.