From f5af20e0461cca3eeeb04ff4262f01ebbe5f7acb Mon Sep 17 00:00:00 2001 From: Batuhan Taskaya Date: Sun, 6 Nov 2022 00:18:44 +0300 Subject: [PATCH 1/2] gh-99103: Normalize positions of specialized traceback anchors against the current line --- Lib/test/test_traceback.py | 34 +++++++++++++++++++ Lib/traceback.py | 7 ++-- ...2-11-06-00-17-58.gh-issue-99103.bFA9BX.rst | 2 ++ Python/traceback.c | 7 ++-- 4 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-11-06-00-17-58.gh-issue-99103.bFA9BX.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 430daf69d2955d..2914b2ce4ad87c 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -559,6 +559,23 @@ def f_with_binary_operator(): result_lines = self.get_exception(f_with_binary_operator) self.assertEqual(result_lines, expected_error.splitlines()) + def test_caret_for_binary_operators_with_unicode(self): + def f_with_binary_operator(): + áóí = 20 + return 10 + áóí / 0 + 30 + + lineno_f = f_with_binary_operator.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + f' File "{__file__}", line {lineno_f+2}, in f_with_binary_operator\n' + ' return 10 + áóí / 0 + 30\n' + ' ~~~~^~~\n' + ) + result_lines = self.get_exception(f_with_binary_operator) + self.assertEqual(result_lines, expected_error.splitlines()) + def test_caret_for_binary_operators_two_char(self): def f_with_binary_operator(): divisor = 20 @@ -593,6 +610,23 @@ def f_with_subscript(): result_lines = self.get_exception(f_with_subscript) self.assertEqual(result_lines, expected_error.splitlines()) + def test_caret_for_subscript_unicode(self): + def f_with_subscript(): + some_dict = {'ó': {'á': {'í': {'theta': 1}}}} + return some_dict['ó']['á']['í']['beta'] + + lineno_f = f_with_subscript.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + f' File "{__file__}", line {lineno_f+2}, in f_with_subscript\n' + " return some_dict['ó']['á']['í']['beta']\n" + ' ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^\n' + ) + result_lines = self.get_exception(f_with_subscript) + self.assertEqual(result_lines, expected_error.splitlines()) + def test_traceback_specialization_with_syntax_error(self): bytecode = compile("1 / 0 / 1 / 2\n", TESTFN, "exec") diff --git a/Lib/traceback.py b/Lib/traceback.py index 8d518728fa1b10..776c73dce3ef4b 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -586,12 +586,13 @@ def _extract_caret_anchors_from_line_segment(segment): if len(tree.body) != 1: return None + normalize = lambda offset: _byte_offset_to_character_offset(segment, offset) statement = tree.body[0] match statement: case ast.Expr(expr): match expr: case ast.BinOp(): - operator_str = segment[expr.left.end_col_offset:expr.right.col_offset] + operator_str = segment[normalize(expr.left.end_col_offset):normalize(expr.right.col_offset)] operator_offset = len(operator_str) - len(operator_str.lstrip()) left_anchor = expr.left.end_col_offset + operator_offset @@ -601,9 +602,9 @@ def _extract_caret_anchors_from_line_segment(segment): and not operator_str[operator_offset + 1].isspace() ): right_anchor += 1 - return _Anchors(left_anchor, right_anchor) + return _Anchors(normalize(left_anchor), normalize(right_anchor)) case ast.Subscript(): - return _Anchors(expr.value.end_col_offset, expr.slice.end_col_offset + 1) + return _Anchors(normalize(expr.value.end_col_offset), normalize(expr.slice.end_col_offset + 1)) return None diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-11-06-00-17-58.gh-issue-99103.bFA9BX.rst b/Misc/NEWS.d/next/Core and Builtins/2022-11-06-00-17-58.gh-issue-99103.bFA9BX.rst new file mode 100644 index 00000000000000..064d930bbba0be --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-11-06-00-17-58.gh-issue-99103.bFA9BX.rst @@ -0,0 +1,2 @@ +Fix the error reporting positions of specialized traceback anchors when the +source line contains unicode characters. diff --git a/Python/traceback.c b/Python/traceback.c index aacdb33d39b8a2..3c222a7a2ccb9f 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -700,8 +700,11 @@ extract_anchors_from_line(PyObject *filename, PyObject *line, done: if (res > 0) { - *left_anchor += start_offset; - *right_anchor += start_offset; + // Normalize the AST offsets to byte offsets and adjust it with the + // start of the actual line (instead of the source code segment). + assert(segment != NULL); + *left_anchor = _PyPegen_byte_offset_to_character_offset(segment, *left_anchor) + start_offset; + *right_anchor = _PyPegen_byte_offset_to_character_offset(segment, *right_anchor) + start_offset; } Py_XDECREF(segment); if (arena) { From f2220f7ee9bf1bb6558db89756aa7db516a527ae Mon Sep 17 00:00:00 2001 From: Batuhan Taskaya Date: Sun, 13 Nov 2022 01:51:31 +0300 Subject: [PATCH 2/2] Apply suggestions --- Lib/test/test_traceback.py | 2 +- Lib/traceback.py | 10 +++++++--- .../2022-11-06-00-17-58.gh-issue-99103.bFA9BX.rst | 2 +- Python/traceback.c | 4 +++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 2914b2ce4ad87c..c17bbb48b65b2d 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -3390,7 +3390,7 @@ def func(): actual = self.get_suggestion(func) self.assertNotIn("blech", actual) - + def test_name_error_with_instance(self): class A: def __init__(self): diff --git a/Lib/traceback.py b/Lib/traceback.py index 776c73dce3ef4b..c43c4720ae5a15 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -592,7 +592,9 @@ def _extract_caret_anchors_from_line_segment(segment): case ast.Expr(expr): match expr: case ast.BinOp(): - operator_str = segment[normalize(expr.left.end_col_offset):normalize(expr.right.col_offset)] + operator_start = normalize(expr.left.end_col_offset) + operator_end = normalize(expr.right.col_offset) + operator_str = segment[operator_start:operator_end] operator_offset = len(operator_str) - len(operator_str.lstrip()) left_anchor = expr.left.end_col_offset + operator_offset @@ -604,7 +606,9 @@ def _extract_caret_anchors_from_line_segment(segment): right_anchor += 1 return _Anchors(normalize(left_anchor), normalize(right_anchor)) case ast.Subscript(): - return _Anchors(normalize(expr.value.end_col_offset), normalize(expr.slice.end_col_offset + 1)) + subscript_start = normalize(expr.value.end_col_offset) + subscript_end = normalize(expr.slice.end_col_offset + 1) + return _Anchors(subscript_start, subscript_end) return None @@ -1045,7 +1049,7 @@ def _compute_suggestion_error(exc_value, tb, wrong_name): self = frame.f_locals['self'] if hasattr(self, wrong_name): return f"self.{wrong_name}" - + # Compute closest match if len(d) > _MAX_CANDIDATE_ITEMS: diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-11-06-00-17-58.gh-issue-99103.bFA9BX.rst b/Misc/NEWS.d/next/Core and Builtins/2022-11-06-00-17-58.gh-issue-99103.bFA9BX.rst index 064d930bbba0be..f5378eb837d1bd 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-11-06-00-17-58.gh-issue-99103.bFA9BX.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-11-06-00-17-58.gh-issue-99103.bFA9BX.rst @@ -1,2 +1,2 @@ Fix the error reporting positions of specialized traceback anchors when the -source line contains unicode characters. +source line contains Unicode characters. diff --git a/Python/traceback.c b/Python/traceback.c index 3c222a7a2ccb9f..356e64364832aa 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -700,9 +700,11 @@ extract_anchors_from_line(PyObject *filename, PyObject *line, done: if (res > 0) { - // Normalize the AST offsets to byte offsets and adjust it with the + // Normalize the AST offsets to byte offsets and adjust them with the // start of the actual line (instead of the source code segment). assert(segment != NULL); + assert(*left_anchor >= 0); + assert(*right_anchor >= 0); *left_anchor = _PyPegen_byte_offset_to_character_offset(segment, *left_anchor) + start_offset; *right_anchor = _PyPegen_byte_offset_to_character_offset(segment, *right_anchor) + start_offset; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy