| 1889 | |
| 1890 | @requires_debug_ranges() |
| 1891 | class TestSourcePositions(unittest.TestCase): |
| 1892 | # Ensure that compiled code snippets have correct line and column numbers |
| 1893 | # in `co_positions()`. |
| 1894 | |
| 1895 | def check_positions_against_ast(self, snippet): |
| 1896 | # Basic check that makes sure each line and column is at least present |
| 1897 | # in one of the AST nodes of the source code. |
| 1898 | code = compile(snippet, 'test_compile.py', 'exec') |
| 1899 | ast_tree = compile(snippet, 'test_compile.py', 'exec', _ast.PyCF_ONLY_AST) |
| 1900 | self.assertTrue(type(ast_tree) == _ast.Module) |
| 1901 | |
| 1902 | # Use an AST visitor that notes all the offsets. |
| 1903 | lines, end_lines, columns, end_columns = set(), set(), set(), set() |
| 1904 | class SourceOffsetVisitor(ast.NodeVisitor): |
| 1905 | def generic_visit(self, node): |
| 1906 | super().generic_visit(node) |
| 1907 | if not isinstance(node, (ast.expr, ast.stmt, ast.pattern)): |
| 1908 | return |
| 1909 | lines.add(node.lineno) |
| 1910 | end_lines.add(node.end_lineno) |
| 1911 | columns.add(node.col_offset) |
| 1912 | end_columns.add(node.end_col_offset) |
| 1913 | |
| 1914 | SourceOffsetVisitor().visit(ast_tree) |
| 1915 | |
| 1916 | # Check against the positions in the code object. |
| 1917 | for (line, end_line, col, end_col) in code.co_positions(): |
| 1918 | if line == 0: |
| 1919 | continue # This is an artificial module-start line |
| 1920 | # If the offset is not None (indicating missing data), ensure that |
| 1921 | # it was part of one of the AST nodes. |
| 1922 | if line is not None: |
| 1923 | self.assertIn(line, lines) |
| 1924 | if end_line is not None: |
| 1925 | self.assertIn(end_line, end_lines) |
| 1926 | if col is not None: |
| 1927 | self.assertIn(col, columns) |
| 1928 | if end_col is not None: |
| 1929 | self.assertIn(end_col, end_columns) |
| 1930 | |
| 1931 | return code, ast_tree |
| 1932 | |
| 1933 | def assertOpcodeSourcePositionIs(self, code, opcode, |
| 1934 | line, end_line, column, end_column, occurrence=1): |
| 1935 | |
| 1936 | for instr, position in instructions_with_positions( |
| 1937 | dis.Bytecode(code), code.co_positions() |
| 1938 | ): |
| 1939 | if instr.opname == opcode: |
| 1940 | occurrence -= 1 |
| 1941 | if not occurrence: |
| 1942 | self.assertEqual(position[0], line) |
| 1943 | self.assertEqual(position[1], end_line) |
| 1944 | self.assertEqual(position[2], column) |
| 1945 | self.assertEqual(position[3], end_column) |
| 1946 | return |
| 1947 | |
| 1948 | self.fail(f"Opcode {opcode} not found in code") |
nothing calls this directly
no test coverage detected
searching dependent graphs…