summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBarry Warsaw <barry@python.org>2013-11-22 16:08:25 (GMT)
committerBarry Warsaw <barry@python.org>2013-11-22 16:08:25 (GMT)
commit9e6097ebe7bb99a4a22b949ef4b1563b21ad7166 (patch)
treee51d885f796058f800d6b1a5179e782a9f2617e7
parent87b9637a86876f86302a659da3b2e52a5cae5314 (diff)
parent5398e1a56e4483a16e4620758aad0d91e986653f (diff)
downloadcpython-9e6097ebe7bb99a4a22b949ef4b1563b21ad7166.zip
cpython-9e6097ebe7bb99a4a22b949ef4b1563b21ad7166.tar.gz
cpython-9e6097ebe7bb99a4a22b949ef4b1563b21ad7166.tar.bz2
Trunk merge.
-rw-r--r--Doc/library/dis.rst12
-rw-r--r--Doc/whatsnew/3.4.rst3
-rw-r--r--Lib/dis.py17
-rw-r--r--Lib/test/test_dis.py66
-rw-r--r--Lib/test/test_ssl.py33
-rw-r--r--Misc/NEWS4
-rw-r--r--Modules/_ssl.c6
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
diff --git a/Lib/dis.py b/Lib/dis.py
index 1fafcc5..81cbe7f 100644
--- 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):
+ 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):
diff --git a/Misc/NEWS b/Misc/NEWS
index 24ca366..35bca00 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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);