Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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

Copy link
Copy link
Open
@Vipul-Cariappa

Description

@Vipul-Cariappa
Issue body actions

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)(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.