• Nick Coghlan's avatar
    Close #19378: address flaws in the new dis module APIs · 90b8e7d2
    Nick Coghlan yazdı
    - confusing line_offset parameter -> first_line parameter
    - systematically test and fix new file parameter
    - remove redundant Bytecode.show_info() API
    - rename Bytecode.display_code() to Bytecode.dis() and have it
      return the multi-line string rather than printing it directly
    - eliminated some not-so-helpful helpers from the bytecode_helper
      test support module
    
    Also fixed a longstanding defect (worked around in the test suite)
    where lines emitted by the dis module could include trailing white
    space. That no longer happens, allowing the formatting tests to be
    simplified to use plain string comparisons.
    90b8e7d2
bytecode_helper.py 1.57 KB
"""bytecode_helper - support tools for testing correct bytecode generation"""

import unittest
import dis
import io

_UNSPECIFIED = object()

class BytecodeTestCase(unittest.TestCase):
    """Custom assertion methods for inspecting bytecode."""

    def get_disassembly_as_string(self, co):
        s = io.StringIO()
        dis.dis(co, file=s)
        return s.getvalue()

    def assertInBytecode(self, x, opname, argval=_UNSPECIFIED):
        """Returns instr if op is found, otherwise throws AssertionError"""
        for instr in dis.get_instructions(x):
            if instr.opname == opname:
                if argval is _UNSPECIFIED or instr.argval == argval:
                    return instr
        disassembly = self.get_disassembly_as_string(x)
        if argval is _UNSPECIFIED:
            msg = '%s not found in bytecode:\n%s' % (opname, disassembly)
        else:
            msg = '(%s,%r) not found in bytecode:\n%s'
            msg = msg % (opname, argval, disassembly)
        self.fail(msg)

    def assertNotInBytecode(self, x, opname, argval=_UNSPECIFIED):
        """Throws AssertionError if op is found"""
        for instr in dis.get_instructions(x):
            if instr.opname == opname:
                disassembly = self.get_disassembly_as_string(co)
                if opargval is _UNSPECIFIED:
                    msg = '%s occurs in bytecode:\n%s' % (opname, disassembly)
                elif instr.argval == argval:
                    msg = '(%s,%r) occurs in bytecode:\n%s'
                    msg = msg % (opname, argval, disassembly)
                self.fail(msg)