Given source code `segment` corresponding to a FrameSummary, determine: - for binary ops, the location of the binary op - for indexing and function calls, the location of the brackets. `segment` is expected to be a valid Python expression.
(segment)
| 826 | ) |
| 827 | |
| 828 | def _extract_caret_anchors_from_line_segment(segment): |
| 829 | """ |
| 830 | Given source code `segment` corresponding to a FrameSummary, determine: |
| 831 | - for binary ops, the location of the binary op |
| 832 | - for indexing and function calls, the location of the brackets. |
| 833 | `segment` is expected to be a valid Python expression. |
| 834 | """ |
| 835 | import ast |
| 836 | |
| 837 | try: |
| 838 | # Without parentheses, `segment` is parsed as a statement. |
| 839 | # Binary ops, subscripts, and calls are expressions, so |
| 840 | # we can wrap them with parentheses to parse them as |
| 841 | # (possibly multi-line) expressions. |
| 842 | # e.g. if we try to highlight the addition in |
| 843 | # x = ( |
| 844 | # a + |
| 845 | # b |
| 846 | # ) |
| 847 | # then we would ast.parse |
| 848 | # a + |
| 849 | # b |
| 850 | # which is not a valid statement because of the newline. |
| 851 | # Adding brackets makes it a valid expression. |
| 852 | # ( |
| 853 | # a + |
| 854 | # b |
| 855 | # ) |
| 856 | # Line locations will be different than the original, |
| 857 | # which is taken into account later on. |
| 858 | tree = ast.parse(f"(\n{segment}\n)") |
| 859 | except SyntaxError: |
| 860 | return None |
| 861 | |
| 862 | if len(tree.body) != 1: |
| 863 | return None |
| 864 | |
| 865 | lines = segment.splitlines() |
| 866 | |
| 867 | def normalize(lineno, offset): |
| 868 | """Get character index given byte offset""" |
| 869 | return _byte_offset_to_character_offset(lines[lineno], offset) |
| 870 | |
| 871 | def next_valid_char(lineno, col): |
| 872 | """Gets the next valid character index in `lines`, if |
| 873 | the current location is not valid. Handles empty lines. |
| 874 | """ |
| 875 | while lineno < len(lines) and col >= len(lines[lineno]): |
| 876 | col = 0 |
| 877 | lineno += 1 |
| 878 | assert lineno < len(lines) and col < len(lines[lineno]) |
| 879 | return lineno, col |
| 880 | |
| 881 | def increment(lineno, col): |
| 882 | """Get the next valid character index in `lines`.""" |
| 883 | col += 1 |
| 884 | lineno, col = next_valid_char(lineno, col) |
| 885 | return lineno, col |
no test coverage detected
searching dependent graphs…