Author: Philip Jenvey <pjen...@underboss.org> Branch: py3.3 Changeset: r72702:8ba1b63dfa1b Date: 2014-08-05 17:33 -0700 http://bitbucket.org/pypy/pypy/changeset/8ba1b63dfa1b/
Log: Merged in kiilerix/pypy/py3.3 (pull request #257) py3k: implement PyCode.dump() properly (issue 1827) 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", _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit