[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