Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
sync docs
  • Loading branch information
picnixz committed Mar 24, 2025
commit 811b8eb9b5e7e2b2051e7d3dc872a783f2dfbff2
61 changes: 35 additions & 26 deletions Doc/extending/newtypes_tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -250,16 +250,17 @@ Because we now have data to manage, we have to be more careful about object
allocation and deallocation. At a minimum, we need a deallocation method::

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

which is assigned to the :c:member:`~PyTypeObject.tp_dealloc` member::

.tp_dealloc = (destructor) Custom_dealloc,
.tp_dealloc = Custom_dealloc,

This method first clears the reference counts of the two Python attributes.
:c:func:`Py_XDECREF` correctly handles the case where its argument is
Expand All @@ -270,11 +271,10 @@ the object's type might not be :class:`!CustomType`, because the object may
be an instance of a subclass.

.. note::
The explicit cast to ``destructor`` above is needed because we defined
``Custom_dealloc`` to take a ``CustomObject *`` argument, but the ``tp_dealloc``
function pointer expects to receive a ``PyObject *`` argument. Otherwise,
the compiler will emit a warning. This is object-oriented polymorphism,
in C!
The explicit cast to ``CustomObject *`` above is needed because we defined
Comment thread
picnixz marked this conversation as resolved.
``Custom_dealloc`` to take a ``PyObject *`` argument, as the ``tp_dealloc``
function pointer expects to receive a ``PyObject *`` argument. Otherwise,
this would result in an undefined behaviour at runtime!
Comment thread
picnixz marked this conversation as resolved.
Outdated

We want to make sure that the first and last names are initialized to empty
strings, so we provide a ``tp_new`` implementation::
Expand Down Expand Up @@ -352,8 +352,9 @@ We also define an initialization function which accepts arguments to provide
initial values for our instance::

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

Expand All @@ -379,7 +380,7 @@ initial values for our instance::

by filling the :c:member:`~PyTypeObject.tp_init` slot. ::

.tp_init = (initproc) Custom_init,
.tp_init = Custom_init,

The :c:member:`~PyTypeObject.tp_init` slot is exposed in Python as the
:meth:`~object.__init__` method. It is used to initialize an object after it's
Expand Down Expand Up @@ -451,8 +452,9 @@ We define a single method, :meth:`!Custom.name`, that outputs the objects name a
concatenation of the first and last names. ::

static PyObject *
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
CustomObject *self = (CustomObject *) op;
if (self->first == NULL) {
PyErr_SetString(PyExc_AttributeError, "first");
return NULL;
Expand Down Expand Up @@ -486,7 +488,7 @@ Now that we've defined the method, we need to create an array of method
definitions::

static PyMethodDef Custom_methods[] = {
{"name", (PyCFunction) Custom_name, METH_NOARGS,
{"name", Custom_name, METH_NOARGS,
"Return the name, combining the first and last name"
},
{NULL} /* Sentinel */
Expand Down Expand Up @@ -543,15 +545,17 @@ we'll use custom getter and setter functions. Here are the functions for
getting and setting the :attr:`!first` attribute::

static PyObject *
Custom_getfirst(CustomObject *self, void *closure)
Custom_getfirst(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *) op;
Py_INCREF(self->first);
return self->first;
}

static int
Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
Custom_setfirst(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *) op;
PyObject *tmp;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
Expand Down Expand Up @@ -583,9 +587,9 @@ new value is not a string.
We create an array of :c:type:`PyGetSetDef` structures::

static PyGetSetDef Custom_getsetters[] = {
{"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
{"first", Custom_getfirst, Custom_setfirst,
"first name", NULL},
{"last", (getter) Custom_getlast, (setter) Custom_setlast,
{"last", Custom_getlast, Custom_setlast,
"last name", NULL},
{NULL} /* Sentinel */
};
Expand All @@ -609,8 +613,9 @@ We also need to update the :c:member:`~PyTypeObject.tp_init` handler to only
allow strings [#]_ to be passed::

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

Expand Down Expand Up @@ -689,8 +694,9 @@ First, the traversal method lets the cyclic GC know about subobjects that could
participate in cycles::

static int
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
Custom_traverse(PyObject *op, visitproc visit, void *arg)
{
CustomObject *self = (CustomObject *) op;
int vret;
if (self->first) {
vret = visit(self->first, arg);
Expand All @@ -716,8 +722,9 @@ functions. With :c:func:`Py_VISIT`, we can minimize the amount of boilerplate
in ``Custom_traverse``::

static int
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
Custom_traverse(PyObject *op, visitproc visit, void *arg)
{
CustomObject *self = (CustomObject *) op;
Py_VISIT(self->first);
Py_VISIT(self->last);
return 0;
Expand All @@ -731,8 +738,9 @@ Second, we need to provide a method for clearing any subobjects that can
participate in cycles::

static int
Custom_clear(CustomObject *self)
Custom_clear(PyObject *op)
{
CustomObject *self = (CustomObject *) op;
Py_CLEAR(self->first);
Py_CLEAR(self->last);
return 0;
Expand Down Expand Up @@ -765,11 +773,11 @@ Here is our reimplemented deallocator using :c:func:`PyObject_GC_UnTrack`
and ``Custom_clear``::

static void
Custom_dealloc(CustomObject *self)
Custom_dealloc(PyObject *op)
{
PyObject_GC_UnTrack(self);
Custom_clear(self);
Py_TYPE(self)->tp_free((PyObject *) self);
PyObject_GC_UnTrack(op);
(void)Custom_clear(op);
Py_TYPE(op)->tp_free(op);
}

Finally, we add the :c:macro:`Py_TPFLAGS_HAVE_GC` flag to the class flags::
Expand Down Expand Up @@ -825,9 +833,10 @@ When a Python object is a :class:`!SubList` instance, its ``PyObject *`` pointer
can be safely cast to both ``PyListObject *`` and ``SubListObject *``::

static int
SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
SubList_init(PyObject *op, PyObject *args, PyObject *kwds)
{
if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
SubListObject *self = (SubListObject *) op;
if (PyList_Type.tp_init(op, args, kwds) < 0)
return -1;
self->state = 0;
return 0;
Expand Down
6 changes: 3 additions & 3 deletions Doc/includes/newtypes/custom2.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ typedef struct {
static void
Custom_dealloc(PyObject *op)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
Py_XDECREF(self->first);
Py_XDECREF(self->last);
Py_TYPE(self)->tp_free(self);
Expand Down Expand Up @@ -42,7 +42,7 @@ Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static int
Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL;

Expand Down Expand Up @@ -73,7 +73,7 @@ static PyMemberDef Custom_members[] = {
static PyObject *
Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
if (self->first == NULL) {
PyErr_SetString(PyExc_AttributeError, "first");
return NULL;
Expand Down
14 changes: 7 additions & 7 deletions Doc/includes/newtypes/custom3.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ typedef struct {
static void
Custom_dealloc(PyObject *op)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
Py_XDECREF(self->first);
Py_XDECREF(self->last);
Py_TYPE(self)->tp_free(self);
Expand Down Expand Up @@ -42,7 +42,7 @@ Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static int
Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL;

Expand All @@ -69,14 +69,14 @@ static PyMemberDef Custom_members[] = {
static PyObject *
Custom_getfirst(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->first);
}

static int
Custom_setfirst(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
return -1;
Expand All @@ -93,14 +93,14 @@ Custom_setfirst(PyObject *op, PyObject *value, void *closure)
static PyObject *
Custom_getlast(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->last);
}

static int
Custom_setlast(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
return -1;
Expand All @@ -125,7 +125,7 @@ static PyGetSetDef Custom_getsetters[] = {
static PyObject *
Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
return PyUnicode_FromFormat("%S %S", self->first, self->last);
}

Expand Down
23 changes: 11 additions & 12 deletions Doc/includes/newtypes/custom4.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ typedef struct {
static int
Custom_traverse(PyObject *op, visitproc visit, void *arg)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
Py_VISIT(self->first);
Py_VISIT(self->last);
return 0;
Expand All @@ -21,7 +21,7 @@ Custom_traverse(PyObject *op, visitproc visit, void *arg)
static int
Custom_clear(PyObject *op)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
Py_CLEAR(self->first);
Py_CLEAR(self->last);
return 0;
Expand All @@ -30,10 +30,9 @@ Custom_clear(PyObject *op)
static void
Custom_dealloc(PyObject *op)
{
CustomObject *self = (CustomObject *)op;
PyObject_GC_UnTrack(self);
Custom_clear(self);
Py_TYPE(self)->tp_free(self);
PyObject_GC_UnTrack(op);
(void)Custom_clear(op);
Py_TYPE(op)->tp_free(op);
}

static PyObject *
Expand All @@ -60,7 +59,7 @@ Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static int
Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL;

Expand All @@ -87,14 +86,14 @@ static PyMemberDef Custom_members[] = {
static PyObject *
Custom_getfirst(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->first);
}

static int
Custom_setfirst(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
return -1;
Expand All @@ -111,14 +110,14 @@ Custom_setfirst(PyObject *op, PyObject *value, void *closure)
static PyObject *
Custom_getlast(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->last);
}

static int
Custom_setlast(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
return -1;
Expand All @@ -143,7 +142,7 @@ static PyGetSetDef Custom_getsetters[] = {
static PyObject *
Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
CustomObject *self = (CustomObject *)op;
CustomObject *self = (CustomObject *) op;
return PyUnicode_FromFormat("%S %S", self->first, self->last);
}

Expand Down
6 changes: 3 additions & 3 deletions Doc/includes/newtypes/sublist.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ typedef struct {
static PyObject *
SubList_increment(PyObject *op, PyObject *Py_UNUSED(dummy))
{
SubListObject *self = (SubListObject *)op;
SubListObject *self = (SubListObject *) op;
self->state++;
return PyLong_FromLong(self->state);
}
Expand All @@ -23,8 +23,8 @@ static PyMethodDef SubList_methods[] = {
static int
SubList_init(PyObject *op, PyObject *args, PyObject *kwds)
{
SubListObject *self = (SubListObject *)op;
if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
SubListObject *self = (SubListObject *) op;
if (PyList_Type.tp_init(op, args, kwds) < 0)
return -1;
self->state = 0;
return 0;
Expand Down
Loading