Trunk merge.
This commit is contained in:
commit
9e6097ebe7
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
17
Lib/dis.py
17
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()
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue