Thanks to visit codestin.com
Credit goes to github.com

Skip to content

sys.setprofile does not dipatch C Extension dunder methods #134243

Closed as not planned
@Vipul-Cariappa

Description

@Vipul-Cariappa

I am trying to log all the function calls that happen. Within Python, as well as any of the C Extensions. But I see that dunder/magic methods defined within the C extensions are not registered with sys.setprofile.
Is this the expected behaviour? If yes, are there any other means for me to log such events?
Or is this a bug in CPython?

Code to reproduce:

// c_extensions.c
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stddef.h> /* for offsetof() */

typedef struct {
    PyObject_HEAD PyObject *first; /* first name */
    PyObject *last;                /* last name */
    int number;
} CustomObject;

static void Custom_dealloc(CustomObject *self) {
    Py_XDECREF(self->first);
    Py_XDECREF(self->last);
    Py_TYPE(self)->tp_free((PyObject *)self);
}

static PyObject *Custom_new(PyTypeObject *type, PyObject *args,
                            PyObject *kwds) {
    CustomObject *self;
    self = (CustomObject *)type->tp_alloc(type, 0);
    if (self != NULL) {
        self->first = PyUnicode_FromString("");
        if (self->first == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->last = PyUnicode_FromString("");
        if (self->last == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->number = 0;
    }
    return (PyObject *)self;
}

static int Custom_init(CustomObject *self, PyObject *args, PyObject *kwds) {
    static char *kwlist[] = {"first", "last", "number", NULL};
    PyObject *first = NULL, *last = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, &first, &last,
                                     &self->number))
        return -1;

    if (first) {
        Py_XSETREF(self->first, Py_NewRef(first));
    }
    if (last) {
        Py_XSETREF(self->last, Py_NewRef(last));
    }
    return 0;
}

static PyMemberDef Custom_members[] = {
    {"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0, "first name"},
    {"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0, "last name"},
    {"number", Py_T_INT, offsetof(CustomObject, number), 0, "custom number"},
    {NULL} /* Sentinel */
};

static PyObject *Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored)) {
    if (self->first == NULL) {
        PyErr_SetString(PyExc_AttributeError, "first");
        return NULL;
    }
    if (self->last == NULL) {
        PyErr_SetString(PyExc_AttributeError, "last");
        return NULL;
    }
    return PyUnicode_FromFormat("%S %S", self->first, self->last);
}

static PyMethodDef Custom_methods[] = {
    {"name", (PyCFunction)Custom_name, METH_NOARGS,
     "Return the name, combining the first and last name"},
    {NULL} /* Sentinel */
};

static PyTypeObject CustomType = {
    .ob_base = PyVarObject_HEAD_INIT(NULL, 0).tp_name = "custom2.Custom",
    .tp_doc = PyDoc_STR("Custom objects"),
    .tp_basicsize = sizeof(CustomObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    .tp_new = Custom_new,
    .tp_init = (initproc)Custom_init,
    .tp_dealloc = (destructor)Custom_dealloc,
    .tp_members = Custom_members,
    .tp_methods = Custom_methods,
};

static PyObject *print_stderr(PyObject *self, PyObject *args) {
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = fprintf(stderr, "%s\n", command);
    return PyLong_FromLong(sts);
}

static PyMethodDef customMethods[] = {
    {"print_stderr", print_stderr, METH_VARARGS, "Prints."},
    {NULL, NULL, 0, NULL},
};

static PyModuleDef custommodule = {
    .m_base = PyModuleDef_HEAD_INIT,
    .m_name = "custom2",
    .m_doc = "Example module that creates an extension type.",
    .m_size = -1,
    .m_methods = customMethods,
};

PyMODINIT_FUNC PyInit_custom2(void) {
    PyObject *m;
    if (PyType_Ready(&CustomType) < 0)
        return NULL;

    m = PyModule_Create(&custommodule);
    if (m == NULL)
        return NULL;

    if (PyModule_AddObjectRef(m, "Custom", (PyObject *)&CustomType) < 0) {
        Py_DECREF(m);
        return NULL;
    }

    return m;
}
# main.py
import sys
import custom2  # C Extension


class MyClass:
    def __init__(self, *args, **kwargs):
        pass


def dispatch_handler(frame, event, arg):
    if event.startswith("c_"):
        print(f"{event = } name = {arg.__name__}")
    else:
        print(f"{event = } {frame = }")

    return dispatch_handler


def test_dummy():
    custom2.print_stderr("Hello World")

    c = custom2.Custom(19)  # XXX: Does not dispatch an event for custom2.Custom.__init__
    custom2.print_stderr(c.name())

    c = MyClass()  # XXX: Dispatches an event for MyClass.__init__


if __name__ == "__main__":
    sys.setprofile(dispatch_handler)
    test_dummy()
# setup.py to compile the C Extension
# ???: use pyproject.toml instead

from setuptools import setup, Extension

module = Extension("custom2", sources=["c_extension.c"])

setup(
    name="custom2",
    version="0.0.1",
    description="Python C Extension",
    ext_modules=[module],
)
❯ python ./main.py > output.txt
Hello World
19
event = 'call' frame = <frame at 0x712491d45b40, file '/home/vipul-cariappa/Documents/Workspace/python/debugger/pxc-dbg/tmp/example0/./main.py', line 19, code test_dummy>
event = 'c_call' name = print_stderr
event = 'c_return' name = print_stderr
event = 'c_call' name = name
event = 'c_return' name = name
event = 'c_call' name = print_stderr
event = 'c_return' name = print_stderr
event = 'call' frame = <frame at 0x712491d46740, file '/home/vipul-cariappa/Documents/Workspace/python/debugger/pxc-dbg/tmp/example0/./main.py', line 6, code __init__>
event = 'return' frame = <frame at 0x712491d46740, file '/home/vipul-cariappa/Documents/Workspace/python/debugger/pxc-dbg/tmp/example0/./main.py', line 7, code __init__>
event = 'return' frame = <frame at 0x712491d45b40, file '/home/vipul-cariappa/Documents/Workspace/python/debugger/pxc-dbg/tmp/example0/./main.py', line 25, code test_dummy>
event = 'return' frame = <frame at 0x712491d45b40, file '/home/vipul-cariappa/Documents/Workspace/python/debugger/pxc-dbg/tmp/example0/./main.py', line 30, code <module>>

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions