diff options
Diffstat (limited to 'Lib/dis.py')
-rw-r--r-- | Lib/dis.py | 108 |
1 files changed, 74 insertions, 34 deletions
diff --git a/Lib/dis.py b/Lib/dis.py index 2b400dc88f..f64bae66fb 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -6,10 +6,25 @@ import types from opcode import * from opcode import __all__ as _opcodes_all -__all__ = ["dis", "disassemble", "distb", "disco", - "findlinestarts", "findlabels"] + _opcodes_all +__all__ = ["code_info", "dis", "disassemble", "distb", "disco", + "findlinestarts", "findlabels", "show_code"] + _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. @@ -19,25 +34,26 @@ def dis(x=None): if x is None: distb() return - if hasattr(x, '__func__'): + if hasattr(x, '__func__'): # Method x = x.__func__ - if hasattr(x, '__code__'): + if hasattr(x, '__code__'): # Function x = x.__code__ - if hasattr(x, '__dict__'): + if hasattr(x, '__dict__'): # Class or module items = sorted(x.__dict__.items()) for name, x1 in items: - if isinstance(x1, (types.MethodType, types.FunctionType, - types.CodeType, type)): + if isinstance(x1, _have_code): print("Disassembly of %s:" % name) try: dis(x1) except TypeError as msg: print("Sorry:", msg) print() - elif hasattr(x, 'co_code'): + elif hasattr(x, 'co_code'): # Code object disassemble(x) - elif isinstance(x, (bytes, bytearray)): - disassemble_string(x) + elif isinstance(x, (bytes, bytearray)): # Raw bytecode + _disassemble_bytes(x) + elif isinstance(x, str): # Source code + _disassemble_str(x) else: raise TypeError("don't know how to disassemble %s objects" % type(x).__name__) @@ -52,9 +68,10 @@ def distb(tb=None): while tb.tb_next: tb = tb.tb_next disassemble(tb.tb_frame.f_code, tb.tb_lasti) -# XXX This duplicates information from code.h, also duplicated in inspect.py. -# XXX Maybe this ought to be put in a central location, like opcode.py? -flag2name = { +# 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", @@ -70,7 +87,7 @@ def pretty_flags(flags): for i in range(32): flag = 1<<i if flags & flag: - names.append(flag2name.get(flag, hex(flag))) + names.append(COMPILER_FLAG_NAMES.get(flag, hex(flag))) flags ^= flag if not flags: break @@ -78,35 +95,54 @@ def pretty_flags(flags): names.append(hex(flags)) return ", ".join(names) -def show_code(co): - """Show details about a code object.""" - print("Name: ", co.co_name) - print("Filename: ", co.co_filename) - print("Argument count: ", co.co_argcount) - print("Kw-only arguments:", co.co_kwonlyargcount) - print("Number of locals: ", co.co_nlocals) - print("Stack size: ", co.co_stacksize) - print("Flags: ", pretty_flags(co.co_flags)) +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: - print("Constants:") + lines.append("Constants:") for i_c in enumerate(co.co_consts): - print("%4d: %r" % i_c) + lines.append("%4d: %r" % i_c) if co.co_names: - print("Names:") + lines.append("Names:") for i_n in enumerate(co.co_names): - print("%4d: %s" % i_n) + lines.append("%4d: %s" % i_n) if co.co_varnames: - print("Variable names:") + lines.append("Variable names:") for i_n in enumerate(co.co_varnames): - print("%4d: %s" % i_n) + lines.append("%4d: %s" % i_n) if co.co_freevars: - print("Free variables:") + lines.append("Free variables:") for i_n in enumerate(co.co_freevars): - print("%4d: %s" % i_n) + lines.append("%4d: %s" % i_n) if co.co_cellvars: - print("Cell variables:") + lines.append("Cell variables:") for i_n in enumerate(co.co_cellvars): - print("%4d: %s" % i_n) + 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.""" @@ -156,7 +192,7 @@ def disassemble(co, lasti=-1): print('(' + free[oparg] + ')', end=' ') print() -def disassemble_string(code, lasti=-1, varnames=None, names=None, +def _disassemble_bytes(code, lasti=-1, varnames=None, names=None, constants=None): labels = findlabels(code) n = len(code) @@ -195,6 +231,10 @@ def disassemble_string(code, lasti=-1, varnames=None, names=None, print('(' + cmp_op[oparg] + ')', end=' ') print() +def _disassemble_str(source): + """Compile the source string, then disassemble the code object.""" + disassemble(_try_compile(source, '<dis>')) + disco = disassemble # XXX For backwards compatibility def findlabels(code): |