From efd5df9e5267fd337cde95b0dec319ea59187bf4 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Fri, 25 Jul 2014 23:02:56 +1000 Subject: Issue #21947: handle generator-iterator objects in dis Patch by Clement Rouault. --- Doc/library/dis.rst | 16 ++++++++-------- Lib/dis.py | 8 ++++++-- Lib/test/test_dis.py | 8 ++++++++ Misc/ACKS | 1 + Misc/NEWS | 3 +++ 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index fbabe35772..8653da765b 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -48,8 +48,8 @@ compiled code. .. class:: Bytecode(x, *, first_line=None, current_offset=None) - Analyse the bytecode corresponding to a function, method, string of - source code, or a code object (as returned by :func:`compile`). + Analyse the bytecode corresponding to a function, generator, method, + 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, most notably :func:`get_instructions`, as iterating over a @@ -112,7 +112,7 @@ object isn't useful: .. function:: code_info(x) 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 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) 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 - of raw bytecode. For a module, it disassembles all functions. For a class, - it disassembles all methods. For a code object 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 + method, a function, a generator, a code object, a string of source code or + a byte sequence of raw bytecode. For a module, it disassembles all functions. + For a class, it disassembles all methods. For a code object 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. diff --git a/Lib/dis.py b/Lib/dis.py index 81cbe7f4f9..d215bc59e4 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -29,7 +29,7 @@ def _try_compile(source, name): return c 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. @@ -41,6 +41,8 @@ def dis(x=None, *, file=None): x = x.__func__ if hasattr(x, '__code__'): # Function x = x.__code__ + if hasattr(x, 'gi_code'): # Generator + x = x.gi_code if hasattr(x, '__dict__'): # Class or module items = sorted(x.__dict__.items()) for name, x1 in items: @@ -99,11 +101,13 @@ def pretty_flags(flags): return ", ".join(names) 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 x = x.__func__ if hasattr(x, '__code__'): # Function x = x.__code__ + if hasattr(x, 'gi_code'): # Generator + x = x.gi_code if isinstance(x, str): # Source code x = _try_compile(x, "") if hasattr(x, 'co_code'): # Code object diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index d1229fba6d..f04f12ca82 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -229,6 +229,9 @@ dis_traceback = """\ TRACEBACK_CODE.co_firstlineno + 4, TRACEBACK_CODE.co_firstlineno + 5) +def _g(x): + yield x + class DisTests(unittest.TestCase): 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 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): try: del sys.last_traceback diff --git a/Misc/ACKS b/Misc/ACKS index d9fed8a2a7..4a68015cdd 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1145,6 +1145,7 @@ Guido van Rossum Just van Rossum Hugo van Rossum Saskia van Rossum +Clement Rouault Donald Wallace Rouse II Liam Routt Todd Rovito diff --git a/Misc/NEWS b/Misc/NEWS index 515978aed6..3f49d9c9ac 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -108,6 +108,9 @@ Core and Builtins 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 BlockingIOError exceptions. -- cgit v1.2.1