From 585c93daea510634ef0c99969b88bc320cdb61a1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 23 Apr 2016 09:23:52 +0300 Subject: [PATCH] Issue #26733: Disassembling a class now disassembles class and static methods. Patch by Xiang Zhang. --- Doc/library/dis.rst | 10 ++++----- Lib/dis.py | 3 ++- Lib/test/test_dis.py | 52 ++++++++++++++++++++++++++++++++++++++++++-- Misc/NEWS | 3 +++ 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 68c196ec864..d2d8ac7839b 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -139,11 +139,11 @@ operation is being performed, so the intermediate analysis object isn't useful: Disassemble the *x* object. *x* can denote either a module, a class, a method, a function, a generator, a code object, a string of source code or a byte sequence of raw bytecode. For a module, it disassembles all functions. - For a class, it disassembles all methods. For a code object or sequence of - raw bytecode, it prints one line per bytecode instruction. Strings are first - compiled to code objects with the :func:`compile` built-in function before being - disassembled. If no object is provided, this function disassembles the last - traceback. + For a class, it disassembles all methods (including class and static methods). + For a code object or sequence of raw bytecode, it prints one line per bytecode + instruction. Strings are first compiled to code objects with the :func:`compile` + built-in function before being disassembled. If no object is provided, this + function disassembles the last traceback. The disassembly is written as text to the supplied *file* argument if provided and to ``sys.stdout`` otherwise. diff --git a/Lib/dis.py b/Lib/dis.py index af37cdf0c67..841208ffa14 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -13,7 +13,8 @@ __all__ = ["code_info", "dis", "disassemble", "distb", "disco", "get_instructions", "Instruction", "Bytecode"] + _opcodes_all del _opcodes_all -_have_code = (types.MethodType, types.FunctionType, types.CodeType, type) +_have_code = (types.MethodType, types.FunctionType, types.CodeType, + classmethod, staticmethod, type) def _try_compile(source, name): """Attempts to compile the given source, first as an expression and diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 2cbf64ad58f..0fd13480276 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -30,6 +30,14 @@ class _C: def __init__(self, x): self.x = x == 1 + @staticmethod + def sm(x): + x = x == 1 + + @classmethod + def cm(cls, x): + cls.x = x == 1 + dis_c_instance_method = """\ %3d 0 LOAD_FAST 1 (x) 3 LOAD_CONST 1 (1) @@ -50,6 +58,37 @@ dis_c_instance_method_bytes = """\ 18 RETURN_VALUE """ +dis_c_class_method = """\ +%3d 0 LOAD_FAST 1 (x) + 3 LOAD_CONST 1 (1) + 6 COMPARE_OP 2 (==) + 9 LOAD_FAST 0 (cls) + 12 STORE_ATTR 0 (x) + 15 LOAD_CONST 0 (None) + 18 RETURN_VALUE +""" % (_C.cm.__code__.co_firstlineno + 2,) + +dis_c_static_method = """\ +%3d 0 LOAD_FAST 0 (x) + 3 LOAD_CONST 1 (1) + 6 COMPARE_OP 2 (==) + 9 STORE_FAST 0 (x) + 12 LOAD_CONST 0 (None) + 15 RETURN_VALUE +""" % (_C.sm.__code__.co_firstlineno + 2,) + +# Class disassembling info has an extra newline at end. +dis_c = """\ +Disassembly of %s: +%s +Disassembly of %s: +%s +Disassembly of %s: +%s +""" % (_C.__init__.__name__, dis_c_instance_method, + _C.cm.__name__, dis_c_class_method, + _C.sm.__name__, dis_c_static_method) + def _f(a): print(a) return 1 @@ -311,13 +350,22 @@ class DisTests(unittest.TestCase): def test_disassemble_bytes(self): self.do_disassembly_test(_f.__code__.co_code, dis_f_co_code) - def test_disassemble_method(self): + def test_disassemble_class(self): + self.do_disassembly_test(_C, dis_c) + + def test_disassemble_instance_method(self): self.do_disassembly_test(_C(1).__init__, dis_c_instance_method) - def test_disassemble_method_bytes(self): + def test_disassemble_instance_method_bytes(self): method_bytecode = _C(1).__init__.__code__.co_code self.do_disassembly_test(method_bytecode, dis_c_instance_method_bytes) + def test_disassemble_static_method(self): + self.do_disassembly_test(_C.sm, dis_c_static_method) + + def test_disassemble_class_method(self): + self.do_disassembly_test(_C.cm, dis_c_class_method) + def test_disassemble_generator(self): gen_func_disas = self.get_disassembly(_g) # Disassemble generator function gen_disas = self.get_disassembly(_g(1)) # Disassemble generator itself diff --git a/Misc/NEWS b/Misc/NEWS index 07b7fde759d..d9a5b1de82f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -107,6 +107,9 @@ Core and Builtins Library ------- +- Issue #26733: Disassembling a class now disassembles class and static methods. + Patch by Xiang Zhang. + - Issue #26801: Fix error handling in :func:`shutil.get_terminal_size`, catch :exc:`AttributeError` instead of :exc:`NameError`. Patch written by Emanuel Barry.