diff options
author | Barry Warsaw <barry@python.org> | 2013-11-22 16:08:25 (GMT) |
---|---|---|
committer | Barry Warsaw <barry@python.org> | 2013-11-22 16:08:25 (GMT) |
commit | 9e6097ebe7bb99a4a22b949ef4b1563b21ad7166 (patch) | |
tree | e51d885f796058f800d6b1a5179e782a9f2617e7 | |
parent | 87b9637a86876f86302a659da3b2e52a5cae5314 (diff) | |
parent | 5398e1a56e4483a16e4620758aad0d91e986653f (diff) | |
download | cpython-9e6097ebe7bb99a4a22b949ef4b1563b21ad7166.zip cpython-9e6097ebe7bb99a4a22b949ef4b1563b21ad7166.tar.gz cpython-9e6097ebe7bb99a4a22b949ef4b1563b21ad7166.tar.bz2 |
Trunk merge.
-rw-r--r-- | Doc/library/dis.rst | 12 | ||||
-rw-r--r-- | Doc/whatsnew/3.4.rst | 3 | ||||
-rw-r--r-- | Lib/dis.py | 17 | ||||
-rw-r--r-- | Lib/test/test_dis.py | 66 | ||||
-rw-r--r-- | Lib/test/test_ssl.py | 33 | ||||
-rw-r--r-- | Misc/NEWS | 4 | ||||
-rw-r--r-- | Modules/_ssl.c | 6 |
7 files changed, 119 insertions, 22 deletions
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 78ddf7e..2365f0a 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -44,7 +44,7 @@ The bytecode analysis API allows pieces of Python code to be wrapped in a :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, + setting *current_offset* to the instruction responsible for the + exception. + .. data:: codeobj The compiled code object. diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index 22d168e..6db3d63 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -385,7 +385,8 @@ The new :class:`dis.Bytecode` class provides an object-oriented API for 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 @@ -406,7 +406,7 @@ class Bytecode: Iterating over this yields the bytecode operations as Instruction instances. """ - def __init__(self, x, *, first_line=None): + 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 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 """ + while tb.tb_next: + tb = tb.tb_next + return cls(tb.tb_frame.f_code, current_offset=tb.tb_lasti) + 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: + offset = self.current_offset + else: + offset = -1 with io.StringIO() as output: _disassemble_bytes(co.co_code, varnames=co.co_varnames, names=co.co_names, constants=co.co_consts, cells=self._cell_names, linestarts=self._linestarts, line_offset=self._line_offset, - file=output) + file=output, + lasti=offset) return output.getvalue() diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 2b54678..94d6be7 100644 --- 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: + 1 / 0 + except Exception as e: + tb = e.__traceback__ + return tb + + tb = _error() + while tb.tb_next: + tb = tb.tb_next + return tb + +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 = """\ 25 RETURN_VALUE """ +dis_traceback = """\ + %-4d 0 SETUP_EXCEPT 12 (to 15) + + %-4d 3 LOAD_CONST 1 (1) + 6 LOAD_CONST 2 (0) + --> 9 BINARY_TRUE_DIVIDE + 10 POP_TOP + 11 POP_BLOCK + 12 JUMP_FORWARD 46 (to 61) + + %-4d >> 15 DUP_TOP + 16 LOAD_GLOBAL 0 (Exception) + 19 COMPARE_OP 10 (exception match) + 22 POP_JUMP_IF_FALSE 60 + 25 POP_TOP + 26 STORE_FAST 0 (e) + 29 POP_TOP + 30 SETUP_FINALLY 14 (to 47) + + %-4d 33 LOAD_FAST 0 (e) + 36 LOAD_ATTR 1 (__traceback__) + 39 STORE_FAST 1 (tb) + 42 POP_BLOCK + 43 POP_EXCEPT + 44 LOAD_CONST 0 (None) + >> 47 LOAD_CONST 0 (None) + 50 STORE_FAST 0 (e) + 53 DELETE_FAST 0 (e) + 56 END_FINALLY + 57 JUMP_FORWARD 1 (to 61) + >> 60 END_FINALLY + + %-4d >> 61 LOAD_FAST 1 (tb) + 64 RETURN_VALUE +""" % (TRACEBACK_CODE.co_firstlineno + 1, + TRACEBACK_CODE.co_firstlineno + 2, + TRACEBACK_CODE.co_firstlineno + 3, + TRACEBACK_CODE.co_firstlineno + 4, + TRACEBACK_CODE.co_firstlineno + 5) + 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() + b = dis.Bytecode.from_traceback(tb) + while tb.tb_next: tb = tb.tb_next + + self.assertEqual(b.current_offset, tb.tb_lasti) + + def test_from_traceback_dis(self): + tb = get_tb() + b = dis.Bytecode.from_traceback(tb) + self.assertEqual(b.dis(), dis_traceback) def test_main(): run_unittest(DisTests, DisWithFileTests, CodeInfoTests, diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 4ebc4b0..7374327 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -536,21 +536,22 @@ class BasicSocketTests(unittest.TestCase): self.assertRaises(TypeError, ssl.enum_certificates) self.assertRaises(WindowsError, ssl.enum_certificates, "") - names = set() - ca = ssl.enum_certificates("CA") - self.assertIsInstance(ca, list) - for element in ca: - self.assertIsInstance(element, tuple) - self.assertEqual(len(element), 3) - cert, enc, trust = element - self.assertIsInstance(cert, bytes) - self.assertIn(enc, {"x509_asn", "pkcs_7_asn"}) - self.assertIsInstance(trust, (set, bool)) - if isinstance(trust, set): - names.update(trust) + trust_oids = set() + for storename in ("CA", "ROOT"): + store = ssl.enum_certificates(storename) + self.assertIsInstance(store, list) + for element in store: + self.assertIsInstance(element, tuple) + self.assertEqual(len(element), 3) + cert, enc, trust = element + self.assertIsInstance(cert, bytes) + self.assertIn(enc, {"x509_asn", "pkcs_7_asn"}) + self.assertIsInstance(trust, (set, bool)) + if isinstance(trust, set): + trust_oids.update(trust) serverAuth = "1.3.6.1.5.5.7.3.1" - self.assertIn(serverAuth, names) + self.assertIn(serverAuth, trust_oids) @unittest.skipUnless(sys.platform == "win32", "Windows specific") def test_enum_crls(self): @@ -584,7 +585,8 @@ class BasicSocketTests(unittest.TestCase): self.assertEqual(val, expected) self.assertIsInstance(val, ssl._ASN1Object) self.assertRaises(ValueError, ssl._ASN1Object.fromnid, -1) - self.assertRaises(ValueError, ssl._ASN1Object.fromnid, 100000) + with self.assertRaisesRegex(ValueError, "unknown NID 100000"): + ssl._ASN1Object.fromnid(100000) for i in range(1000): try: obj = ssl._ASN1Object.fromnid(i) @@ -602,7 +604,8 @@ class BasicSocketTests(unittest.TestCase): self.assertEqual(ssl._ASN1Object.fromname('serverAuth'), expected) self.assertEqual(ssl._ASN1Object.fromname('1.3.6.1.5.5.7.3.1'), expected) - self.assertRaises(ValueError, ssl._ASN1Object.fromname, 'serverauth') + with self.assertRaisesRegex(ValueError, "unknown object 'serverauth'"): + ssl._ASN1Object.fromname('serverauth') class ContextTests(unittest.TestCase): @@ -65,6 +65,10 @@ Core and Builtins Library ------- +- Issue #17916: Added dis.Bytecode.from_traceback() and + dis.Bytecode.current_offset to easily display "current instruction" + markers in the new disassembly API (Patch by Claudiu Popa). + - Issue #19552: venv now supports bootstrapping pip into virtual environments - Issue #17134: Finalize interface to Windows' certificate store. Cert and diff --git a/Modules/_ssl.c b/Modules/_ssl.c index f32d180..180355b 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -3387,7 +3387,7 @@ PySSL_txt2obj(PyObject *self, PyObject *args, PyObject *kwds) } obj = OBJ_txt2obj(txt, name ? 0 : 1); if (obj == NULL) { - PyErr_Format(PyExc_ValueError, "Unknown object"); + PyErr_Format(PyExc_ValueError, "unknown object '%.100s'", txt); return NULL; } result = asn1obj2py(obj); @@ -3411,12 +3411,12 @@ PySSL_nid2obj(PyObject *self, PyObject *args) return NULL; } if (nid < NID_undef) { - PyErr_Format(PyExc_ValueError, "NID must be positive."); + PyErr_SetString(PyExc_ValueError, "NID must be positive."); return NULL; } obj = OBJ_nid2obj(nid); if (obj == NULL) { - PyErr_Format(PyExc_ValueError, "Unknown NID"); + PyErr_Format(PyExc_ValueError, "unknown NID %i", nid); return NULL; } result = asn1obj2py(obj); |