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

Skip to content

Commit eae2da1

Browse files
committed
Issue 9147: Add dis.code_info()
1 parent 9887683 commit eae2da1

4 files changed

Lines changed: 216 additions & 30 deletions

File tree

Doc/library/dis.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ the following command can be used to get the disassembly of :func:`myfunc`::
3636
The :mod:`dis` module defines the following functions and constants:
3737

3838

39+
.. function:: code_info(x=None)
40+
41+
Return a formatted multi-line string with detailed code object
42+
information for the supplied function, method, source code string
43+
or code object.
44+
45+
Note that the exact contents of code info strings are highly
46+
implementation dependent and they may change arbitrarily across
47+
Python VMs or Python releases.
48+
49+
.. versionadded:: 3.2
50+
3951
.. function:: dis(x=None)
4052

4153
Disassemble the *x* object. *x* can denote either a module, a

Lib/dis.py

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ def _try_compile(source, name):
1919
Utility function to accept strings in functions that otherwise
2020
expect code objects
2121
"""
22-
# ncoghlan: currently only used by dis(), but plan to add an
23-
# equivalent for show_code() as well (but one that returns a
24-
# string rather than printing directly to the console)
2522
try:
2623
c = compile(source, name, 'eval')
2724
except SyntaxError:
@@ -37,11 +34,11 @@ def dis(x=None):
3734
if x is None:
3835
distb()
3936
return
40-
if hasattr(x, '__func__'):
37+
if hasattr(x, '__func__'): # Method
4138
x = x.__func__
42-
if hasattr(x, '__code__'):
39+
if hasattr(x, '__code__'): # Function
4340
x = x.__code__
44-
if hasattr(x, '__dict__'):
41+
if hasattr(x, '__dict__'): # Class or module
4542
items = sorted(x.__dict__.items())
4643
for name, x1 in items:
4744
if isinstance(x1, _have_code):
@@ -51,11 +48,11 @@ def dis(x=None):
5148
except TypeError as msg:
5249
print("Sorry:", msg)
5350
print()
54-
elif hasattr(x, 'co_code'):
51+
elif hasattr(x, 'co_code'): # Code object
5552
disassemble(x)
56-
elif isinstance(x, (bytes, bytearray)):
53+
elif isinstance(x, (bytes, bytearray)): # Raw bytecode
5754
_disassemble_bytes(x)
58-
elif isinstance(x, str):
55+
elif isinstance(x, str): # Source code
5956
_disassemble_str(x)
6057
else:
6158
raise TypeError("don't know how to disassemble %s objects" %
@@ -97,35 +94,54 @@ def pretty_flags(flags):
9794
names.append(hex(flags))
9895
return ", ".join(names)
9996

100-
def show_code(co):
101-
"""Show details about a code object."""
102-
print("Name: ", co.co_name)
103-
print("Filename: ", co.co_filename)
104-
print("Argument count: ", co.co_argcount)
105-
print("Kw-only arguments:", co.co_kwonlyargcount)
106-
print("Number of locals: ", co.co_nlocals)
107-
print("Stack size: ", co.co_stacksize)
108-
print("Flags: ", pretty_flags(co.co_flags))
97+
def code_info(x):
98+
"""Formatted details of methods, functions, or code."""
99+
if hasattr(x, '__func__'): # Method
100+
x = x.__func__
101+
if hasattr(x, '__code__'): # Function
102+
x = x.__code__
103+
if isinstance(x, str): # Source code
104+
x = _try_compile(x, "<code_info>")
105+
if hasattr(x, 'co_code'): # Code object
106+
return _format_code_info(x)
107+
else:
108+
raise TypeError("don't know how to disassemble %s objects" %
109+
type(x).__name__)
110+
111+
def _format_code_info(co):
112+
lines = []
113+
lines.append("Name: %s" % co.co_name)
114+
lines.append("Filename: %s" % co.co_filename)
115+
lines.append("Argument count: %s" % co.co_argcount)
116+
lines.append("Kw-only arguments: %s" % co.co_kwonlyargcount)
117+
lines.append("Number of locals: %s" % co.co_nlocals)
118+
lines.append("Stack size: %s" % co.co_stacksize)
119+
lines.append("Flags: %s" % pretty_flags(co.co_flags))
109120
if co.co_consts:
110-
print("Constants:")
121+
lines.append("Constants:")
111122
for i_c in enumerate(co.co_consts):
112-
print("%4d: %r" % i_c)
123+
lines.append("%4d: %r" % i_c)
113124
if co.co_names:
114-
print("Names:")
125+
lines.append("Names:")
115126
for i_n in enumerate(co.co_names):
116-
print("%4d: %s" % i_n)
127+
lines.append("%4d: %s" % i_n)
117128
if co.co_varnames:
118-
print("Variable names:")
129+
lines.append("Variable names:")
119130
for i_n in enumerate(co.co_varnames):
120-
print("%4d: %s" % i_n)
131+
lines.append("%4d: %s" % i_n)
121132
if co.co_freevars:
122-
print("Free variables:")
133+
lines.append("Free variables:")
123134
for i_n in enumerate(co.co_freevars):
124-
print("%4d: %s" % i_n)
135+
lines.append("%4d: %s" % i_n)
125136
if co.co_cellvars:
126-
print("Cell variables:")
137+
lines.append("Cell variables:")
127138
for i_n in enumerate(co.co_cellvars):
128-
print("%4d: %s" % i_n)
139+
lines.append("%4d: %s" % i_n)
140+
return "\n".join(lines)
141+
142+
def show_code(co):
143+
"""Show details about a code object."""
144+
print(code_info(co))
129145

130146
def disassemble(co, lasti=-1):
131147
"""Disassemble a code object."""

Lib/test/test_dis.py

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Minimal tests for dis module
22

3-
from test.support import run_unittest
3+
from test.support import run_unittest, captured_stdout
44
import unittest
55
import sys
66
import dis
@@ -211,8 +211,162 @@ def test_disassemble_str(self):
211211
self.do_disassembly_test(simple_stmt_str, dis_simple_stmt_str)
212212
self.do_disassembly_test(compound_stmt_str, dis_compound_stmt_str)
213213

214+
code_info_code_info = """\
215+
Name: code_info
216+
Filename: {0}
217+
Argument count: 1
218+
Kw-only arguments: 0
219+
Number of locals: 1
220+
Stack size: 4
221+
Flags: OPTIMIZED, NEWLOCALS, NOFREE
222+
Constants:
223+
0: 'Formatted details of methods, functions, or code.'
224+
1: '__func__'
225+
2: '__code__'
226+
3: '<code_info>'
227+
4: 'co_code'
228+
5: "don't know how to disassemble %s objects"
229+
6: None
230+
Names:
231+
0: hasattr
232+
1: __func__
233+
2: __code__
234+
3: isinstance
235+
4: str
236+
5: _try_compile
237+
6: _format_code_info
238+
7: TypeError
239+
8: type
240+
9: __name__
241+
Variable names:
242+
0: x""".format(dis.__file__)
243+
244+
@staticmethod
245+
def tricky(x, y, z=True, *args, c, d, e=[], **kwds):
246+
def f(c=c):
247+
print(x, y, z, c, d, e, f)
248+
yield x, y, z, c, d, e, f
249+
250+
co_tricky_nested_f = tricky.__func__.__code__.co_consts[1]
251+
252+
code_info_tricky = """\
253+
Name: tricky
254+
Filename: {0}
255+
Argument count: 3
256+
Kw-only arguments: 3
257+
Number of locals: 8
258+
Stack size: 7
259+
Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR
260+
Constants:
261+
0: None
262+
1: <code object f at {1}, file "{0}", line {2}>
263+
Variable names:
264+
0: x
265+
1: y
266+
2: z
267+
3: c
268+
4: d
269+
5: e
270+
6: args
271+
7: kwds
272+
Cell variables:
273+
0: e
274+
1: d
275+
2: f
276+
3: y
277+
4: x
278+
5: z""".format(__file__,
279+
hex(id(co_tricky_nested_f)),
280+
co_tricky_nested_f.co_firstlineno)
281+
282+
code_info_tricky_nested_f = """\
283+
Name: f
284+
Filename: {0}
285+
Argument count: 1
286+
Kw-only arguments: 0
287+
Number of locals: 1
288+
Stack size: 8
289+
Flags: OPTIMIZED, NEWLOCALS, NESTED
290+
Constants:
291+
0: None
292+
Names:
293+
0: print
294+
Variable names:
295+
0: c
296+
Free variables:
297+
0: e
298+
1: d
299+
2: f
300+
3: y
301+
4: x
302+
5: z""".format(__file__)
303+
304+
code_info_expr_str = """\
305+
Name: <module>
306+
Filename: <code_info>
307+
Argument count: 0
308+
Kw-only arguments: 0
309+
Number of locals: 0
310+
Stack size: 2
311+
Flags: NOFREE
312+
Constants:
313+
0: 1
314+
Names:
315+
0: x"""
316+
317+
code_info_simple_stmt_str = """\
318+
Name: <module>
319+
Filename: <code_info>
320+
Argument count: 0
321+
Kw-only arguments: 0
322+
Number of locals: 0
323+
Stack size: 2
324+
Flags: NOFREE
325+
Constants:
326+
0: 1
327+
1: None
328+
Names:
329+
0: x"""
330+
331+
code_info_compound_stmt_str = """\
332+
Name: <module>
333+
Filename: <code_info>
334+
Argument count: 0
335+
Kw-only arguments: 0
336+
Number of locals: 0
337+
Stack size: 2
338+
Flags: NOFREE
339+
Constants:
340+
0: 0
341+
1: 1
342+
2: None
343+
Names:
344+
0: x"""
345+
346+
class CodeInfoTests(unittest.TestCase):
347+
test_pairs = [
348+
(dis.code_info, code_info_code_info),
349+
(tricky, code_info_tricky),
350+
(co_tricky_nested_f, code_info_tricky_nested_f),
351+
(expr_str, code_info_expr_str),
352+
(simple_stmt_str, code_info_simple_stmt_str),
353+
(compound_stmt_str, code_info_compound_stmt_str),
354+
]
355+
356+
def test_code_info(self):
357+
self.maxDiff = 1000
358+
for x, expected in self.test_pairs:
359+
self.assertEqual(dis.code_info(x), expected)
360+
361+
def test_show_code(self):
362+
self.maxDiff = 1000
363+
for x, expected in self.test_pairs:
364+
with captured_stdout() as output:
365+
dis.show_code(x)
366+
self.assertEqual(output.getvalue(), expected+"\n")
367+
214368
def test_main():
215-
run_unittest(DisTests)
369+
run_unittest(DisTests, CodeInfoTests)
216370

217371
if __name__ == "__main__":
218372
test_main()

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ Extensions
9090
Library
9191
-------
9292

93+
- Issue #9147: Added dis.code_info() which is similar to show_code()
94+
but returns formatted code information in a string rather than
95+
displaying on screen.
96+
9397
- Issue #9567: functools.update_wrapper now adds a __wrapped__ attribute
9498
pointing to the original callable
9599

0 commit comments

Comments
 (0)