[pypy-commit] pypy py3.3: Make pycode.dump work for py3 opcodes on py2 (issue 1827)

kiilerix noreply at buildbot.pypy.org
Wed Aug 6 02:34:11 CEST 2014


Author: Mads Kiilerich <mads at kiilerich.com>
Branch: py3.3
Changeset: r72701:803c1daaac74
Date: 2014-07-27 17:30 +0200
http://bitbucket.org/pypy/pypy/changeset/803c1daaac74/

Log:	Make pycode.dump work for py3 opcodes on py2 (issue 1827)

	Python 3 dis.py and opcode.py are "backported" as more or less
	trivial copies. This code duplication seems like the least intrusive
	solution.

diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -4,7 +4,7 @@
 The bytecode interpreter itself is implemented by the PyFrame class.
 """
 
-import dis, imp, struct, types, new, sys
+import imp, struct, types, new, sys
 
 from pypy.interpreter import eval
 from pypy.interpreter.signature import Signature
@@ -13,6 +13,7 @@
 from pypy.interpreter.astcompiler.consts import (
     CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS, CO_NESTED,
     CO_GENERATOR, CO_KILL_DOCSTRING, CO_YIELD_INSIDE_TRY)
+from pypy.tool import dis3
 from pypy.tool.stdlib_opcode import opcodedesc, HAVE_ARGUMENT
 from rpython.rlib.rarithmetic import intmask
 from rpython.rlib.objectmodel import compute_hash, we_are_translated
@@ -245,33 +246,6 @@
             if isinstance(w_co, PyCode):
                 w_co.remove_docstrings(space)
 
-    def _to_code(self):
-        """For debugging only."""
-        consts = [None] * len(self.co_consts_w)
-        num = 0
-        for w in self.co_consts_w:
-            if isinstance(w, PyCode):
-                consts[num] = w._to_code()
-            else:
-                consts[num] = self.space.unwrap(w)
-            num += 1
-        assert self.co_kwonlyargcount == 0, 'kwonlyargcount is py3k only, cannot turn this code object into a Python2 one'
-        return new.code(self.co_argcount,
-                        #self.co_kwonlyargcount, # this does not exists in python2
-                        self.co_nlocals,
-                        self.co_stacksize,
-                        self.co_flags,
-                        self.co_code,
-                        tuple(consts),
-                        tuple(self.co_names),
-                        tuple(self.co_varnames),
-                        self.co_filename,
-                        self.co_name,
-                        self.co_firstlineno,
-                        self.co_lnotab,
-                        tuple(self.co_freevars),
-                        tuple(self.co_cellvars))
-
     def exec_host_bytecode(self, w_globals, w_locals):
         if sys.version_info < (2, 7):
             raise Exception("PyPy no longer supports Python 2.6 or lower")
@@ -280,11 +254,11 @@
         return frame.run()
 
     def dump(self):
-        """A dis.dis() dump of the code object."""
-        print 'WARNING: dumping a py3k bytecode using python2 opmap, the result might be inaccurate or wrong'
-        print
-        co = self._to_code()
-        dis.dis(co)
+        """NOT_RPYTHON: A dis.dis() dump of the code object."""
+        if not hasattr(self, 'co_consts'):
+            self.co_consts = [w if isinstance(w, PyCode) else self.space.unwrap(w)
+                              for w in self.co_consts_w]
+        dis3.dis(self)
 
     def fget_co_consts(self, space):
         return space.newtuple(self.co_consts_w)
diff --git a/pypy/interpreter/test/test_pycode.py b/pypy/interpreter/test/test_pycode.py
new file mode 100644
--- /dev/null
+++ b/pypy/interpreter/test/test_pycode.py
@@ -0,0 +1,19 @@
+import sys, StringIO
+
+def test_dump(space):
+    """test that pycode.dump kind of works with py3 opcodes"""
+    compiler = space.createcompiler()
+    code = compiler.compile('lambda *, y=7: None', 'filename', 'exec', 0)
+    output = None
+    stdout = sys.stdout
+    try:
+        sys.stdout = StringIO.StringIO()
+        code.dump()
+        output = sys.stdout.getvalue()
+        sys.stdout.close()
+    finally:
+        sys.stdout = stdout
+    print '>>>\n' + output + '\n<<<'
+    assert ' 1 (7)' in output
+    assert ' 3 (None)' in output
+    assert ' 16 RETURN_VALUE ' in output
diff --git a/lib-python/3/dis.py b/pypy/tool/dis3.py
copy from lib-python/3/dis.py
copy to pypy/tool/dis3.py
--- a/lib-python/3/dis.py
+++ b/pypy/tool/dis3.py
@@ -1,30 +1,18 @@
-"""Disassembler of Python byte code into mnemonics."""
+"""Disassembler of Python byte code into mnemonics.
+Python 3 dis.py partly backported to Python 2"""
 
 import sys
 import types
 
-from opcode import *
-from opcode import __all__ as _opcodes_all
+from opcode3 import *
+from opcode3 import __all__ as _opcodes_all
 
-__all__ = ["code_info", "dis", "disassemble", "distb", "disco",
-           "findlinestarts", "findlabels", "show_code"] + _opcodes_all
+__all__ = ["dis", "disassemble", "distb", "disco",
+           "findlinestarts", "findlabels"] + _opcodes_all
 del _opcodes_all
 
 _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
-    """
-    try:
-        c = compile(source, name, 'eval')
-    except SyntaxError:
-        c = compile(source, name, 'exec')
-    return c
-
 def dis(x=None):
     """Disassemble classes, methods, functions, or code.
 
@@ -34,29 +22,31 @@
     if x is None:
         distb()
         return
-    if hasattr(x, '__func__'):  # Method
-        x = x.__func__
-    if hasattr(x, '__code__'):  # Function
-        x = x.__code__
-    if hasattr(x, '__dict__'):  # Class or module
-        items = sorted(x.__dict__.items())
+    if isinstance(x, types.InstanceType):
+        x = x.__class__
+    if hasattr(x, 'im_func'):
+        x = x.im_func
+    if hasattr(x, 'func_code'):
+        x = x.func_code
+    if hasattr(x, 'co_code'): # PyCode needs co_code before __dict__
+        disassemble(x)
+    elif hasattr(x, '__dict__'):
+        items = x.__dict__.items()
+        items.sort()
         for name, x1 in items:
             if isinstance(x1, _have_code):
-                print("Disassembly of %s:" % name)
+                print "Disassembly of %s:" % name
                 try:
                     dis(x1)
-                except TypeError as msg:
-                    print("Sorry:", msg)
-                print()
-    elif hasattr(x, 'co_code'): # Code object
-        disassemble(x)
-    elif isinstance(x, (bytes, bytearray)): # Raw bytecode
-        _disassemble_bytes(x)
-    elif isinstance(x, str):    # Source code
-        _disassemble_str(x)
+                except TypeError, msg:
+                    print "Sorry:", msg
+                print
+    elif isinstance(x, str):
+        disassemble_string(x)
     else:
-        raise TypeError("don't know how to disassemble %s objects" %
-                        type(x).__name__)
+        raise TypeError, \
+              "don't know how to disassemble %s objects" % \
+              type(x).__name__
 
 def distb(tb=None):
     """Disassemble a traceback (default: last traceback)."""
@@ -64,86 +54,10 @@
         try:
             tb = sys.last_traceback
         except AttributeError:
-            raise RuntimeError("no last traceback to disassemble")
+            raise RuntimeError, "no last traceback to disassemble"
         while tb.tb_next: tb = tb.tb_next
     disassemble(tb.tb_frame.f_code, tb.tb_lasti)
 
-# The inspect module interrogates this dictionary to build its
-# list of CO_* constants. It is also used by pretty_flags to
-# turn the co_flags field into a human readable list.
-COMPILER_FLAG_NAMES = {
-     1: "OPTIMIZED",
-     2: "NEWLOCALS",
-     4: "VARARGS",
-     8: "VARKEYWORDS",
-    16: "NESTED",
-    32: "GENERATOR",
-    64: "NOFREE",
-}
-
-def pretty_flags(flags):
-    """Return pretty representation of code flags."""
-    names = []
-    for i in range(32):
-        flag = 1<<i
-        if flags & flag:
-            names.append(COMPILER_FLAG_NAMES.get(flag, hex(flag)))
-            flags ^= flag
-            if not flags:
-                break
-    else:
-        names.append(hex(flags))
-    return ", ".join(names)
-
-def code_info(x):
-    """Formatted details of methods, functions, or code."""
-    if hasattr(x, '__func__'): # Method
-        x = x.__func__
-    if hasattr(x, '__code__'): # Function
-        x = x.__code__
-    if isinstance(x, str):     # Source code
-        x = _try_compile(x, "<code_info>")
-    if hasattr(x, 'co_code'):  # Code object
-        return _format_code_info(x)
-    else:
-        raise TypeError("don't know how to disassemble %s objects" %
-                        type(x).__name__)
-
-def _format_code_info(co):
-    lines = []
-    lines.append("Name:              %s" % co.co_name)
-    lines.append("Filename:          %s" % co.co_filename)
-    lines.append("Argument count:    %s" % co.co_argcount)
-    lines.append("Kw-only arguments: %s" % co.co_kwonlyargcount)
-    lines.append("Number of locals:  %s" % co.co_nlocals)
-    lines.append("Stack size:        %s" % co.co_stacksize)
-    lines.append("Flags:             %s" % pretty_flags(co.co_flags))
-    if co.co_consts:
-        lines.append("Constants:")
-        for i_c in enumerate(co.co_consts):
-            lines.append("%4d: %r" % i_c)
-    if co.co_names:
-        lines.append("Names:")
-        for i_n in enumerate(co.co_names):
-            lines.append("%4d: %s" % i_n)
-    if co.co_varnames:
-        lines.append("Variable names:")
-        for i_n in enumerate(co.co_varnames):
-            lines.append("%4d: %s" % i_n)
-    if co.co_freevars:
-        lines.append("Free variables:")
-        for i_n in enumerate(co.co_freevars):
-            lines.append("%4d: %s" % i_n)
-    if co.co_cellvars:
-        lines.append("Cell variables:")
-        for i_n in enumerate(co.co_cellvars):
-            lines.append("%4d: %s" % i_n)
-    return "\n".join(lines)
-
-def show_code(co):
-    """Print details of methods, functions, or code to stdout."""
-    print(code_info(co))
-
 def disassemble(co, lasti=-1):
     """Disassemble a code object."""
     code = co.co_code
@@ -154,92 +68,90 @@
     extended_arg = 0
     free = None
     while i < n:
-        op = code[i]
+        c = code[i]
+        op = ord(c)
         if i in linestarts:
             if i > 0:
-                print()
-            print("%3d" % linestarts[i], end=' ')
+                print
+            print "%3d" % linestarts[i],
         else:
-            print('   ', end=' ')
+            print '   ',
 
-        if i == lasti: print('-->', end=' ')
-        else: print('   ', end=' ')
-        if i in labels: print('>>', end=' ')
-        else: print('  ', end=' ')
-        print(repr(i).rjust(4), end=' ')
-        print(opname[op].ljust(20), end=' ')
+        if i == lasti: print '-->',
+        else: print '   ',
+        if i in labels: print '>>',
+        else: print '  ',
+        print repr(i).rjust(4),
+        print opname[op].ljust(20),
         i = i+1
         if op >= HAVE_ARGUMENT:
-            oparg = code[i] + code[i+1]*256 + extended_arg
+            oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg
             extended_arg = 0
             i = i+2
             if op == EXTENDED_ARG:
-                extended_arg = oparg*65536
-            print(repr(oparg).rjust(5), end=' ')
+                extended_arg = oparg*65536L
+            print repr(oparg).rjust(5),
             if op in hasconst:
-                print('(' + repr(co.co_consts[oparg]) + ')', end=' ')
+                print '(' + repr(co.co_consts[oparg]) + ')',
             elif op in hasname:
-                print('(' + co.co_names[oparg] + ')', end=' ')
+                print '(' + co.co_names[oparg] + ')',
             elif op in hasjrel:
-                print('(to ' + repr(i + oparg) + ')', end=' ')
+                print '(to ' + repr(i + oparg) + ')',
             elif op in haslocal:
-                print('(' + co.co_varnames[oparg] + ')', end=' ')
+                print '(' + co.co_varnames[oparg] + ')',
             elif op in hascompare:
-                print('(' + cmp_op[oparg] + ')', end=' ')
+                print '(' + cmp_op[oparg] + ')',
             elif op in hasfree:
                 if free is None:
                     free = co.co_cellvars + co.co_freevars
-                print('(' + free[oparg] + ')', end=' ')
+                print '(' + free[oparg] + ')',
             elif op in hasnargs:
-                print('(%d positional, %d keyword pair)'
-                      % (code[i-2], code[i-1]), end=' ')
-        print()
+                print '(%d positional, %d keyword pair)' % \
+                      (ord(code[i-2]), ord(code[i-1])),
+        print
 
-def _disassemble_bytes(code, lasti=-1, varnames=None, names=None,
+def disassemble_string(code, lasti=-1, varnames=None, names=None,
                        constants=None):
     labels = findlabels(code)
     n = len(code)
     i = 0
     while i < n:
-        op = code[i]
-        if i == lasti: print('-->', end=' ')
-        else: print('   ', end=' ')
-        if i in labels: print('>>', end=' ')
-        else: print('  ', end=' ')
-        print(repr(i).rjust(4), end=' ')
-        print(opname[op].ljust(15), end=' ')
+        c = code[i]
+        op = ord(c)
+        if i == lasti: print '-->',
+        else: print '   ',
+        if i in labels: print '>>',
+        else: print '  ',
+        print repr(i).rjust(4),
+        print opname[op].ljust(15),
         i = i+1
         if op >= HAVE_ARGUMENT:
-            oparg = code[i] + code[i+1]*256
+            oparg = ord(code[i]) + ord(code[i+1])*256
             i = i+2
-            print(repr(oparg).rjust(5), end=' ')
+            print repr(oparg).rjust(5),
             if op in hasconst:
                 if constants:
-                    print('(' + repr(constants[oparg]) + ')', end=' ')
+                    print '(' + repr(constants[oparg]) + ')',
                 else:
-                    print('(%d)'%oparg, end=' ')
+                    print '(%d)'%oparg,
             elif op in hasname:
                 if names is not None:
-                    print('(' + names[oparg] + ')', end=' ')
+                    print '(' + names[oparg] + ')',
                 else:
-                    print('(%d)'%oparg, end=' ')
+                    print '(%d)'%oparg,
             elif op in hasjrel:
-                print('(to ' + repr(i + oparg) + ')', end=' ')
+                print '(to ' + repr(i + oparg) + ')',
             elif op in haslocal:
                 if varnames:
-                    print('(' + varnames[oparg] + ')', end=' ')
+                    print '(' + varnames[oparg] + ')',
                 else:
-                    print('(%d)' % oparg, end=' ')
+                    print '(%d)' % oparg,
             elif op in hascompare:
-                print('(' + cmp_op[oparg] + ')', end=' ')
+                print '(' + cmp_op[oparg] + ')',
             elif op in hasnargs:
-                print('(%d positional, %d keyword pair)'
-                      % (code[i-2], code[i-1]), end=' ')
-        print()
-
-def _disassemble_str(source):
-    """Compile the source string, then disassemble the code object."""
-    disassemble(_try_compile(source, '<dis>'))
+                print '(%d positional, %d keyword pair)' % \
+                      (ord(code[i-2]), ord(code[i-1])),
+        print
 
 disco = disassemble                     # XXX For backwards compatibility
 
@@ -253,10 +165,11 @@
     n = len(code)
     i = 0
     while i < n:
-        op = code[i]
+        c = code[i]
+        op = ord(c)
         i = i+1
         if op >= HAVE_ARGUMENT:
-            oparg = code[i] + code[i+1]*256
+            oparg = ord(code[i]) + ord(code[i+1])*256
             i = i+2
             label = -1
             if op in hasjrel:
@@ -274,8 +187,8 @@
     Generate pairs (offset, lineno) as described in Python/compile.c.
 
     """
-    byte_increments = list(code.co_lnotab[0::2])
-    line_increments = list(code.co_lnotab[1::2])
+    byte_increments = [ord(c) for c in code.co_lnotab[0::2]]
+    line_increments = [ord(c) for c in code.co_lnotab[1::2]]
 
     lastlineno = None
     lineno = code.co_firstlineno
diff --git a/lib-python/3/opcode.py b/pypy/tool/opcode3.py
copy from lib-python/3/opcode.py
copy to pypy/tool/opcode3.py
--- a/lib-python/3/opcode.py
+++ b/pypy/tool/opcode3.py
@@ -1,7 +1,7 @@
-
 """
 opcode module - potentially shared between dis and other modules which
 operate on bytecodes (e.g. peephole optimizers).
+"Backported" from Python 3 to Python 2 land - an excact copy of lib-python/3/opcode.py
 """
 
 __all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs",


More information about the pypy-commit mailing list