Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9e6097e

Browse files
committed
Trunk merge.
2 parents 87b9637 + 5398e1a commit 9e6097e

7 files changed

Lines changed: 119 additions & 22 deletions

File tree

Doc/library/dis.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ The bytecode analysis API allows pieces of Python code to be wrapped in a
4444
:class:`Bytecode` object that provides easy access to details of the
4545
compiled code.
4646

47-
.. class:: Bytecode(x, *, first_line=None)
47+
.. class:: Bytecode(x, *, first_line=None, current_offset=None)
4848

4949
Analyse the bytecode corresponding to a function, method, string of
5050
source code, or a code object (as returned by :func:`compile`).
@@ -59,6 +59,16 @@ compiled code.
5959
Otherwise, the source line information (if any) is taken directly from
6060
the disassembled code object.
6161

62+
If *current_offset* is not None, it refers to an instruction offset
63+
in the disassembled code. Setting this means :meth:`dis` will display
64+
a "current instruction" marker against the specified opcode.
65+
66+
.. classmethod:: from_traceback(tb)
67+
68+
Construct a :class:`Bytecode` instance from the given traceback,
69+
setting *current_offset* to the instruction responsible for the
70+
exception.
71+
6272
.. data:: codeobj
6373

6474
The compiled code object.

Doc/whatsnew/3.4.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ The new :class:`dis.Bytecode` class provides an object-oriented API for
385385
inspecting bytecode, both in human-readable form and for iterating over
386386
instructions.
387387

388-
(Contributed by Nick Coghlan, Ryan Kelly and Thomas Kluyver in :issue:`11816`)
388+
(Contributed by Nick Coghlan, Ryan Kelly and Thomas Kluyver in :issue:`11816`
389+
and Claudiu Popa in :issue:`17916`)
389390

390391

391392
doctest

Lib/dis.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ class Bytecode:
406406
407407
Iterating over this yields the bytecode operations as Instruction instances.
408408
"""
409-
def __init__(self, x, *, first_line=None):
409+
def __init__(self, x, *, first_line=None, current_offset=None):
410410
self.codeobj = co = _get_code_object(x)
411411
if first_line is None:
412412
self.first_line = co.co_firstlineno
@@ -417,6 +417,7 @@ def __init__(self, x, *, first_line=None):
417417
self._cell_names = co.co_cellvars + co.co_freevars
418418
self._linestarts = dict(findlinestarts(co))
419419
self._original_object = x
420+
self.current_offset = current_offset
420421

421422
def __iter__(self):
422423
co = self.codeobj
@@ -429,20 +430,32 @@ def __repr__(self):
429430
return "{}({!r})".format(self.__class__.__name__,
430431
self._original_object)
431432

433+
@classmethod
434+
def from_traceback(cls, tb):
435+
""" Construct a Bytecode from the given traceback """
436+
while tb.tb_next:
437+
tb = tb.tb_next
438+
return cls(tb.tb_frame.f_code, current_offset=tb.tb_lasti)
439+
432440
def info(self):
433441
"""Return formatted information about the code object."""
434442
return _format_code_info(self.codeobj)
435443

436444
def dis(self):
437445
"""Return a formatted view of the bytecode operations."""
438446
co = self.codeobj
447+
if self.current_offset is not None:
448+
offset = self.current_offset
449+
else:
450+
offset = -1
439451
with io.StringIO() as output:
440452
_disassemble_bytes(co.co_code, varnames=co.co_varnames,
441453
names=co.co_names, constants=co.co_consts,
442454
cells=self._cell_names,
443455
linestarts=self._linestarts,
444456
line_offset=self._line_offset,
445-
file=output)
457+
file=output,
458+
lasti=offset)
446459
return output.getvalue()
447460

448461

Lib/test/test_dis.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@
1010
import types
1111
import contextlib
1212

13+
def get_tb():
14+
def _error():
15+
try:
16+
1 / 0
17+
except Exception as e:
18+
tb = e.__traceback__
19+
return tb
20+
21+
tb = _error()
22+
while tb.tb_next:
23+
tb = tb.tb_next
24+
return tb
25+
26+
TRACEBACK_CODE = get_tb().tb_frame.f_code
27+
1328
class _C:
1429
def __init__(self, x):
1530
self.x = x == 1
@@ -174,6 +189,46 @@ def bug1333982(x=[]):
174189
25 RETURN_VALUE
175190
"""
176191

192+
dis_traceback = """\
193+
%-4d 0 SETUP_EXCEPT 12 (to 15)
194+
195+
%-4d 3 LOAD_CONST 1 (1)
196+
6 LOAD_CONST 2 (0)
197+
--> 9 BINARY_TRUE_DIVIDE
198+
10 POP_TOP
199+
11 POP_BLOCK
200+
12 JUMP_FORWARD 46 (to 61)
201+
202+
%-4d >> 15 DUP_TOP
203+
16 LOAD_GLOBAL 0 (Exception)
204+
19 COMPARE_OP 10 (exception match)
205+
22 POP_JUMP_IF_FALSE 60
206+
25 POP_TOP
207+
26 STORE_FAST 0 (e)
208+
29 POP_TOP
209+
30 SETUP_FINALLY 14 (to 47)
210+
211+
%-4d 33 LOAD_FAST 0 (e)
212+
36 LOAD_ATTR 1 (__traceback__)
213+
39 STORE_FAST 1 (tb)
214+
42 POP_BLOCK
215+
43 POP_EXCEPT
216+
44 LOAD_CONST 0 (None)
217+
>> 47 LOAD_CONST 0 (None)
218+
50 STORE_FAST 0 (e)
219+
53 DELETE_FAST 0 (e)
220+
56 END_FINALLY
221+
57 JUMP_FORWARD 1 (to 61)
222+
>> 60 END_FINALLY
223+
224+
%-4d >> 61 LOAD_FAST 1 (tb)
225+
64 RETURN_VALUE
226+
""" % (TRACEBACK_CODE.co_firstlineno + 1,
227+
TRACEBACK_CODE.co_firstlineno + 2,
228+
TRACEBACK_CODE.co_firstlineno + 3,
229+
TRACEBACK_CODE.co_firstlineno + 4,
230+
TRACEBACK_CODE.co_firstlineno + 5)
231+
177232
class DisTests(unittest.TestCase):
178233

179234
def get_disassembly(self, func, lasti=-1, wrapper=True):
@@ -758,6 +813,17 @@ def test_disassembled(self):
758813
actual = dis.Bytecode(_f).dis()
759814
self.assertEqual(actual, dis_f)
760815

816+
def test_from_traceback(self):
817+
tb = get_tb()
818+
b = dis.Bytecode.from_traceback(tb)
819+
while tb.tb_next: tb = tb.tb_next
820+
821+
self.assertEqual(b.current_offset, tb.tb_lasti)
822+
823+
def test_from_traceback_dis(self):
824+
tb = get_tb()
825+
b = dis.Bytecode.from_traceback(tb)
826+
self.assertEqual(b.dis(), dis_traceback)
761827

762828
def test_main():
763829
run_unittest(DisTests, DisWithFileTests, CodeInfoTests,

Lib/test/test_ssl.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -536,21 +536,22 @@ def test_enum_certificates(self):
536536
self.assertRaises(TypeError, ssl.enum_certificates)
537537
self.assertRaises(WindowsError, ssl.enum_certificates, "")
538538

539-
names = set()
540-
ca = ssl.enum_certificates("CA")
541-
self.assertIsInstance(ca, list)
542-
for element in ca:
543-
self.assertIsInstance(element, tuple)
544-
self.assertEqual(len(element), 3)
545-
cert, enc, trust = element
546-
self.assertIsInstance(cert, bytes)
547-
self.assertIn(enc, {"x509_asn", "pkcs_7_asn"})
548-
self.assertIsInstance(trust, (set, bool))
549-
if isinstance(trust, set):
550-
names.update(trust)
539+
trust_oids = set()
540+
for storename in ("CA", "ROOT"):
541+
store = ssl.enum_certificates(storename)
542+
self.assertIsInstance(store, list)
543+
for element in store:
544+
self.assertIsInstance(element, tuple)
545+
self.assertEqual(len(element), 3)
546+
cert, enc, trust = element
547+
self.assertIsInstance(cert, bytes)
548+
self.assertIn(enc, {"x509_asn", "pkcs_7_asn"})
549+
self.assertIsInstance(trust, (set, bool))
550+
if isinstance(trust, set):
551+
trust_oids.update(trust)
551552

552553
serverAuth = "1.3.6.1.5.5.7.3.1"
553-
self.assertIn(serverAuth, names)
554+
self.assertIn(serverAuth, trust_oids)
554555

555556
@unittest.skipUnless(sys.platform == "win32", "Windows specific")
556557
def test_enum_crls(self):
@@ -584,7 +585,8 @@ def test_asn1object(self):
584585
self.assertEqual(val, expected)
585586
self.assertIsInstance(val, ssl._ASN1Object)
586587
self.assertRaises(ValueError, ssl._ASN1Object.fromnid, -1)
587-
self.assertRaises(ValueError, ssl._ASN1Object.fromnid, 100000)
588+
with self.assertRaisesRegex(ValueError, "unknown NID 100000"):
589+
ssl._ASN1Object.fromnid(100000)
588590
for i in range(1000):
589591
try:
590592
obj = ssl._ASN1Object.fromnid(i)
@@ -602,7 +604,8 @@ def test_asn1object(self):
602604
self.assertEqual(ssl._ASN1Object.fromname('serverAuth'), expected)
603605
self.assertEqual(ssl._ASN1Object.fromname('1.3.6.1.5.5.7.3.1'),
604606
expected)
605-
self.assertRaises(ValueError, ssl._ASN1Object.fromname, 'serverauth')
607+
with self.assertRaisesRegex(ValueError, "unknown object 'serverauth'"):
608+
ssl._ASN1Object.fromname('serverauth')
606609

607610

608611
class ContextTests(unittest.TestCase):

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ Core and Builtins
6565
Library
6666
-------
6767

68+
- Issue #17916: Added dis.Bytecode.from_traceback() and
69+
dis.Bytecode.current_offset to easily display "current instruction"
70+
markers in the new disassembly API (Patch by Claudiu Popa).
71+
6872
- Issue #19552: venv now supports bootstrapping pip into virtual environments
6973

7074
- Issue #17134: Finalize interface to Windows' certificate store. Cert and

Modules/_ssl.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3387,7 +3387,7 @@ PySSL_txt2obj(PyObject *self, PyObject *args, PyObject *kwds)
33873387
}
33883388
obj = OBJ_txt2obj(txt, name ? 0 : 1);
33893389
if (obj == NULL) {
3390-
PyErr_Format(PyExc_ValueError, "Unknown object");
3390+
PyErr_Format(PyExc_ValueError, "unknown object '%.100s'", txt);
33913391
return NULL;
33923392
}
33933393
result = asn1obj2py(obj);
@@ -3411,12 +3411,12 @@ PySSL_nid2obj(PyObject *self, PyObject *args)
34113411
return NULL;
34123412
}
34133413
if (nid < NID_undef) {
3414-
PyErr_Format(PyExc_ValueError, "NID must be positive.");
3414+
PyErr_SetString(PyExc_ValueError, "NID must be positive.");
34153415
return NULL;
34163416
}
34173417
obj = OBJ_nid2obj(nid);
34183418
if (obj == NULL) {
3419-
PyErr_Format(PyExc_ValueError, "Unknown NID");
3419+
PyErr_Format(PyExc_ValueError, "unknown NID %i", nid);
34203420
return NULL;
34213421
}
34223422
result = asn1obj2py(obj);

0 commit comments

Comments
 (0)