Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why PyList_Append is called each time a list is evaluated?

I’m working with CPython3.11.0a3+. I added a break point at PyList_Append and modified the function to stop when the newitem is a dict. The original function:

int
PyList_Append(PyObject *op, PyObject *newitem)
{
    if (PyList_Check(op) && (newitem != NULL))
        return app1((PyListObject *)op, newitem);
    PyErr_BadInternalCall();
    return -1;
}

Modified version:

int
PyList_Append(PyObject *op, PyObject *newitem)
{
    if (PyDict_CheckExact(newitem)) {
        fprintf(stdout, "hello\n");
    }
    if (PyList_Check(op) && (newitem != NULL))
        return app1((PyListObject *)op, newitem);
    PyErr_BadInternalCall();
    return -1;
}

I placed a break point on line fprintf(stdout, "hello\n");. This is because in python startup process, there are infinite calls of PyList_Append but none of them are with newitem of type dict. So in this way I can escape all of those calls and reach the repl to workaround with my own variables.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

(gdb) break Objects/listobject.c:328
Breakpoint 1 at 0x44fb00: file Objects/listobject.c, line 328.
(gdb) run
Starting program: /home/amirreza/Desktop/edu/python_internals/1/cpython/python 
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.31-2.fc32.x86_64
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Python 3.11.0a3+ (heads/main-dirty:1cbb887, Dec  1 2022, 12:22:29) [GCC 10.3.1 20210422 (Red Hat 10.3.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = []
>>> a.append(3)
>>> a
[3]
>>> a.append({})
>>> 
>>> a

Breakpoint 1, PyList_Append (op=op@entry=0x7fffea8e9780, newitem=newitem@entry=0x7fffeaa22bc0) at Objects/listobject.c:328
328         fprintf(stdout, "hello\n");
Missing separate debuginfos, use: dnf debuginfo-install ncurses-libs-6.1-15.20191109.fc32.x86_64 readline-8.0-4.fc32.x86_64
(gdb) c
Continuing.
hello
[3, {}]
>>> 
>>> a

Breakpoint 1, PyList_Append (op=op@entry=0x7fffea8e9780, newitem=newitem@entry=0x7fffeaa22bc0) at Objects/listobject.c:328
328         fprintf(stdout, "hello\n");
(gdb) c
Continuing.
hello
[3, {}]
>>> 
>>> a

Breakpoint 1, PyList_Append (op=op@entry=0x7fffea8e9780, newitem=newitem@entry=0x7fffeaa22bc0) at Objects/listobject.c:328
328         fprintf(stdout, "hello\n");
(gdb) c
Continuing.
hello
[3, {}]
>>> 

My question is why each time I only try to evaluate the a list, the PyList_Append is invoked? For the first time we can ignore it due to lazy-evaluation. But for the second and third time, why it’s called every time?

>Solution :

dict.__repr__ uses the CPython-internal Py_ReprEnter and Py_ReprLeave functions to stop infinite recursion for recursively nested data structures, and Py_ReprEnter appends the dict to a list of objects currently having their repr evaluated in the running thread.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading