diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 02ab8fbcdb0700..e57bca45accf42 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -676,7 +676,8 @@ def test_positions(self): def check_lines(self, func): co = func.__code__ - lines1 = list(dedup(l for (_, _, l) in co.co_lines())) + lines1 = [line for _, _, line in co.co_lines()] + self.assertEqual(lines1, list(dedup(lines1))) lines2 = list(lines_from_postions(positions_from_location_table(co))) for l1, l2 in zip(lines1, lines2): self.assertEqual(l1, l2) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 998ce57927f1a9..f74d2ed2c420b8 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -161,9 +161,8 @@ def test_leading_newlines(self): s256 = "".join(["\n"] * 256 + ["spam"]) co = compile(s256, 'fn', 'exec') self.assertEqual(co.co_firstlineno, 1) - lines = list(co.co_lines()) - self.assertEqual(lines[0][2], 0) - self.assertEqual(lines[1][2], 257) + lines = [line for _, _, line in co.co_lines()] + self.assertEqual(lines, [0, 257]) def test_literals_with_leading_zeroes(self): for arg in ["077787", "0xj", "0x.", "0e", "090000000000000", @@ -955,9 +954,9 @@ def no_code2(): for func in (no_code1, no_code2): with self.subTest(func=func): code = func.__code__ - lines = list(code.co_lines()) - start, end, line = lines[0] + [(start, end, line)] = code.co_lines() self.assertEqual(start, 0) + self.assertEqual(end, len(code.co_code)) self.assertEqual(line, code.co_firstlineno) def get_code_lines(self, code): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst new file mode 100644 index 00000000000000..0ba6ee6f96e318 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst @@ -0,0 +1,2 @@ +Improve the output of ``co_lines`` by emitting only one entry for each line +range. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 1e5a92270be84e..de4d7c11bc3cbb 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1189,6 +1189,14 @@ lineiter_dealloc(lineiterator *li) Py_TYPE(li)->tp_free(li); } +static PyObject * +_source_offset_converter(int *value) { + if (*value == -1) { + Py_RETURN_NONE; + } + return PyLong_FromLong(*value); +} + static PyObject * lineiter_next(lineiterator *li) { @@ -1196,31 +1204,17 @@ lineiter_next(lineiterator *li) if (!_PyLineTable_NextAddressRange(bounds)) { return NULL; } - PyObject *start = NULL; - PyObject *end = NULL; - PyObject *line = NULL; - PyObject *result = PyTuple_New(3); - start = PyLong_FromLong(bounds->ar_start); - end = PyLong_FromLong(bounds->ar_end); - if (bounds->ar_line < 0) { - line = Py_NewRef(Py_None); - } - else { - line = PyLong_FromLong(bounds->ar_line); - } - if (result == NULL || start == NULL || end == NULL || line == NULL) { - goto error; + int start = bounds->ar_start; + int line = bounds->ar_line; + // Merge overlapping entries: + while (_PyLineTable_NextAddressRange(bounds)) { + if (bounds->ar_line != line) { + _PyLineTable_PreviousAddressRange(bounds); + break; + } } - PyTuple_SET_ITEM(result, 0, start); - PyTuple_SET_ITEM(result, 1, end); - PyTuple_SET_ITEM(result, 2, line); - return result; -error: - Py_XDECREF(start); - Py_XDECREF(end); - Py_XDECREF(line); - Py_XDECREF(result); - return result; + return Py_BuildValue("iiO&", start, bounds->ar_end, + _source_offset_converter, &line); } PyTypeObject _PyLineIterator = { @@ -1296,14 +1290,6 @@ positionsiter_dealloc(positionsiterator* pi) Py_TYPE(pi)->tp_free(pi); } -static PyObject* -_source_offset_converter(int* value) { - if (*value == -1) { - Py_RETURN_NONE; - } - return PyLong_FromLong(*value); -} - static PyObject* positionsiter_next(positionsiterator* pi) {