cpython: d71251d9fbbe (original) (raw)
Mercurial > cpython
changeset 87342:d71251d9fbbe
Close #17916: dis.Bytecode based replacement for distb - Bytecode.from_traceback() alternate constructor - current_offset parameter and attribute Patch by Claudiu Popa [#17916]
Nick Coghlan ncoghlan@gmail.com | |
---|---|
date | Sat, 23 Nov 2013 00:57:00 +1000 |
parents | 57fbab22ab4e |
children | de65df13ed50 |
files | Doc/library/dis.rst Doc/whatsnew/3.4.rst Lib/dis.py Lib/test/test_dis.py Misc/NEWS |
diffstat | 5 files changed, 98 insertions(+), 4 deletions(-)[+] [-] Doc/library/dis.rst 12 Doc/whatsnew/3.4.rst 3 Lib/dis.py 17 Lib/test/test_dis.py 66 Misc/NEWS 4 |
line wrap: on
line diff
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -44,7 +44,7 @@ The bytecode analysis API allows pieces
:class:Bytecode
object that provides easy access to details of the
compiled code.
-.. class:: Bytecode(x, *, first_line=None)
+.. 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
).
@@ -59,6 +59,16 @@ compiled code.
Otherwise, the source line information (if any) is taken directly from
the disassembled code object.
- If current_offset is not None, it refers to an instruction offset
- in the disassembled code. Setting this means :meth:
dis
will display - a "current instruction" marker against the specified opcode. +
- .. classmethod:: from_traceback(tb) +
Construct a :class:`Bytecode` instance from the given traceback,[](#l1.22)
setting *current_offset* to the instruction responsible for the[](#l1.23)
exception.[](#l1.24)
+ .. data:: codeobj The compiled code object.
--- a/Doc/whatsnew/3.4.rst
+++ b/Doc/whatsnew/3.4.rst
@@ -385,7 +385,8 @@ The new :class:dis.Bytecode
class prov
inspecting bytecode, both in human-readable form and for iterating over
instructions.
-(Contributed by Nick Coghlan, Ryan Kelly and Thomas Kluyver in :issue:11816
)
+(Contributed by Nick Coghlan, Ryan Kelly and Thomas Kluyver in :issue:11816
+and Claudiu Popa in :issue:17916
)
doctest
--- a/Lib/dis.py +++ b/Lib/dis.py @@ -406,7 +406,7 @@ class Bytecode: Iterating over this yields the bytecode operations as Instruction instances. """
- def init(self, x, *, first_line=None, current_offset=None): self.codeobj = co = _get_code_object(x) if first_line is None: self.first_line = co.co_firstlineno
@@ -417,6 +417,7 @@ class Bytecode: self._cell_names = co.co_cellvars + co.co_freevars self._linestarts = dict(findlinestarts(co)) self._original_object = x
self.current_offset = current_offset[](#l3.16)
def iter(self): co = self.codeobj @@ -429,6 +430,13 @@ class Bytecode: return "{}({!r})".format(self.class.name, self._original_object)
- @classmethod
- def from_traceback(cls, tb):
""" Construct a Bytecode from the given traceback """[](#l3.26)
while tb.tb_next:[](#l3.27)
tb = tb.tb_next[](#l3.28)
return cls(tb.tb_frame.f_code, current_offset=tb.tb_lasti)[](#l3.29)
+ def info(self): """Return formatted information about the code object.""" return _format_code_info(self.codeobj) @@ -436,13 +444,18 @@ class Bytecode: def dis(self): """Return a formatted view of the bytecode operations.""" co = self.codeobj
if self.current_offset is not None:[](#l3.38)
offset = self.current_offset[](#l3.39)
else:[](#l3.40)
offset = -1[](#l3.41) with io.StringIO() as output:[](#l3.42) _disassemble_bytes(co.co_code, varnames=co.co_varnames,[](#l3.43) names=co.co_names, constants=co.co_consts,[](#l3.44) cells=self._cell_names,[](#l3.45) linestarts=self._linestarts,[](#l3.46) line_offset=self._line_offset,[](#l3.47)
file=output)[](#l3.48)
file=output,[](#l3.49)
lasti=offset)[](#l3.50) return output.getvalue()[](#l3.51)
--- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -10,6 +10,21 @@ import io import types import contextlib +def get_tb():
- def _error():
try:[](#l4.9)
1 / 0[](#l4.10)
except Exception as e:[](#l4.11)
tb = e.__traceback__[](#l4.12)
return tb[](#l4.13)
+ +TRACEBACK_CODE = get_tb().tb_frame.f_code + class _C: def init(self, x): self.x = x == 1 @@ -174,6 +189,46 @@ dis_compound_stmt_str = """[](#l4.25) 25 RETURN_VALUE """ +dis_traceback = """[](#l4.29)
- %-4d 0 SETUP_EXCEPT 12 (to 15) +
- %-4d 3 LOAD_CONST 1 (1)
6 LOAD_CONST 2 (0)[](#l4.33)
- --> 9 BINARY_TRUE_DIVIDE
10 POP_TOP[](#l4.35)
11 POP_BLOCK[](#l4.36)
12 JUMP_FORWARD 46 (to 61)[](#l4.37)
- %-4d >> 15 DUP_TOP
16 LOAD_GLOBAL 0 (Exception)[](#l4.40)
19 COMPARE_OP 10 (exception match)[](#l4.41)
22 POP_JUMP_IF_FALSE 60[](#l4.42)
25 POP_TOP[](#l4.43)
26 STORE_FAST 0 (e)[](#l4.44)
29 POP_TOP[](#l4.45)
30 SETUP_FINALLY 14 (to 47)[](#l4.46)
- %-4d 33 LOAD_FAST 0 (e)
36 LOAD_ATTR 1 (__traceback__)[](#l4.49)
39 STORE_FAST 1 (tb)[](#l4.50)
42 POP_BLOCK[](#l4.51)
43 POP_EXCEPT[](#l4.52)
44 LOAD_CONST 0 (None)[](#l4.53)
>> 47 LOAD_CONST 0 (None)[](#l4.54)
50 STORE_FAST 0 (e)[](#l4.55)
53 DELETE_FAST 0 (e)[](#l4.56)
56 END_FINALLY[](#l4.57)
57 JUMP_FORWARD 1 (to 61)[](#l4.58)
>> 60 END_FINALLY[](#l4.59)
+""" % (TRACEBACK_CODE.co_firstlineno + 1,
TRACEBACK_CODE.co_firstlineno + 2,[](#l4.64)
TRACEBACK_CODE.co_firstlineno + 3,[](#l4.65)
TRACEBACK_CODE.co_firstlineno + 4,[](#l4.66)
TRACEBACK_CODE.co_firstlineno + 5)[](#l4.67)
+ class DisTests(unittest.TestCase): def get_disassembly(self, func, lasti=-1, wrapper=True): @@ -758,6 +813,17 @@ class BytecodeTests(unittest.TestCase): actual = dis.Bytecode(_f).dis() self.assertEqual(actual, dis_f)
- def test_from_traceback(self):
tb = get_tb()[](#l4.77)
b = dis.Bytecode.from_traceback(tb)[](#l4.78)
while tb.tb_next: tb = tb.tb_next[](#l4.79)
self.assertEqual(b.current_offset, tb.tb_lasti)[](#l4.81)
- def test_from_traceback_dis(self):
tb = get_tb()[](#l4.84)
b = dis.Bytecode.from_traceback(tb)[](#l4.85)
self.assertEqual(b.dis(), dis_traceback)[](#l4.86)
def test_main(): run_unittest(DisTests, DisWithFileTests, CodeInfoTests,
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -65,6 +65,10 @@ Core and Builtins Library ------- +- Issue #17916: Added dis.Bytecode.from_traceback() and