]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-100117: Make `co_lines` more efficient (GH-100447)
authorBrandt Bucher <brandtbucher@microsoft.com>
Tue, 10 Jan 2023 10:56:53 +0000 (02:56 -0800)
committerGitHub <noreply@github.com>
Tue, 10 Jan 2023 10:56:53 +0000 (10:56 +0000)
Lib/test/test_code.py
Lib/test/test_compile.py
Misc/NEWS.d/next/Core and Builtins/2022-12-17-19-44-57.gh-issue-100117.yRWQ1y.rst [new file with mode: 0644]
Objects/codeobject.c

index b13d5770abe8d28e7765d5c5bd0b97b78ed725dc..67ed1694205cd64c11c720b0f497db9f63653124 100644 (file)
@@ -702,7 +702,8 @@ class CodeLocationTest(unittest.TestCase):
 
     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)
index 998ce57927f1a9a31c292ac8b8952f0d92171415..f74d2ed2c420b89a0890c2f6bb10adab392ce8eb 100644 (file)
@@ -161,9 +161,8 @@ if 1:
         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 @@ if 1:
         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 (file)
index 0000000..0ba6ee6
--- /dev/null
@@ -0,0 +1,2 @@
+Improve the output of ``co_lines`` by emitting only one entry for each line
+range.
index 6facfef4c9b4736e1ae8129a8c5bde7259c49701..ab31b6582cdaae1cfcd34fa52386d0de33d075ce 100644 (file)
@@ -1183,6 +1183,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)
 {
@@ -1190,31 +1198,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 = {
@@ -1290,14 +1284,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)
 {