forked from paulross/PythonExtensionPatterns
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcPyRefs.c
More file actions
158 lines (141 loc) · 4.86 KB
/
cPyRefs.c
File metadata and controls
158 lines (141 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//
// PyReferences.c
// PythonExtensionPatterns
//
// Created by Paul Ross on 07/05/2014.
// Copyright (c) 2014 Paul Ross. All rights reserved.
//
#include "Python.h"
//#include <stdio.h>
/*
* 'New', 'stolen' and 'borrowed' references.
* These terms are used throughout the Python documentation, they refer to
* who is the real owner of the reference i.e. whose job it is to finally
* decref it (free it).
*
* This is all about programming by contract and each of reference types
* has a different contract.
*/
/* New reference.
* This is object creation and it is your job to dispose of it.
*
* The analogy with 'C' is the reference has been malloc'd and must be free'd
* by you.
*/
static PyObject *subtract_long(long a, long b) {
PyObject *pA, *pB, *r;
pA = PyLong_FromLong(a); /* pA: New reference. */
pB = PyLong_FromLong(b); /* pB: New reference. */
r = PyNumber_Subtract(pA, pB); /* r: New reference. */
Py_DECREF(pA); /* My responsibility to decref. */
Py_DECREF(pB); /* My responsibility to decref. */
return r; /* Callers responsibility to decref. */
}
static PyObject *subtract_two_longs(PyObject *pModule) {
return subtract_long(421, 17);
}
/* Stolen reference.
* This is object creation but where another object takes responsibility
* for decref'ing (freeing) the object.
* These are quite rare; typical examples are object insertion into tuples
* lists, dicts etc.
*
* The analogy with C would be malloc'ing some memory, populating it and
* inserting that pointer into a linked list where the linked list promises
* to free the memory when that item in the list is removed.
*/
static PyObject *make_tuple(PyObject *pModule) {
PyObject *r;
PyObject *v;
r = PyTuple_New(3); /* New reference. */
fprintf(stdout, "Ref count new: %zd\n", r->ob_refcnt);
v = PyLong_FromLong(1L); /* New reference. */
/* PyTuple_SetItem steals the new reference v. */
PyTuple_SetItem(r, 0, v);
/* This is fine. */
v = PyLong_FromLong(2L);
PyTuple_SetItem(r, 1, v);
/* More common pattern. */
PyTuple_SetItem(r, 2, PyUnicode_FromString("three"));
return r; /* Callers responsibility to decref. */
}
void handle_list(PyObject *pList) {
while (PyList_Size(pList) > 0) {
PySequence_DelItem(pList, PyList_Size(pList) - 1);
}
}
/* 'Borrowed' reference this is when reading from an object, you get back a
* reference to something that the object still owns _and_ the container
* can dispose of at _any_ time.
* The problem is that you might want that reference for longer.
*/
static PyObject *pop_and_print_BAD(PyObject *pModule, PyObject *pList) {
PyObject *pLast;
pLast = PyList_GetItem(pList, PyList_Size(pList) - 1);
fprintf(stdout, "Ref count was: %zd\n", pLast->ob_refcnt);
/* ... stuff here ... */
handle_list(pList);
/* ... more stuff here ... */
fprintf(stdout, "Ref count now: %zd\n", pLast->ob_refcnt);
PyObject_Print(pLast, stdout, 0); /* Boom. */
fprintf(stdout, "\n");
Py_RETURN_NONE;
}
static PyObject *pop_and_print_OK(PyObject *pModule, PyObject *pList) {
PyObject *pLast;
pLast = PyList_GetItem(pList, PyList_Size(pList) - 1);
fprintf(stdout, "Ref count was: %zd\n", pLast->ob_refcnt);
Py_INCREF(pLast);
fprintf(stdout, "Ref count now: %zd\n", pLast->ob_refcnt);
/* ... stuff here ... */
handle_list(pList);
/* ... more stuff here ... */
PyObject_Print(pLast, stdout, 0);
fprintf(stdout, "\n");
Py_DECREF(pLast);
fprintf(stdout, "Ref count fin: %zd\n", pLast->ob_refcnt);
Py_RETURN_NONE;
}
static PyObject *bad_incref(PyObject *pModule, PyObject *pObj) {
fprintf(stdout, "Ref count was: %zd\n", pObj->ob_refcnt);
Py_INCREF(pObj);
fprintf(stdout, "Ref count now: %zd\n", pObj->ob_refcnt);
if (1) {
Py_RETURN_NONE;
}
Py_RETURN_NONE;
}
static PyMethodDef cPyRefs_methods[] = {
{"newRef", (PyCFunction)subtract_two_longs, METH_NOARGS,
"Returns a new long by subtracting two longs in Python."
},
{"stealRef", (PyCFunction)make_tuple, METH_NOARGS,
"Creates a tuple by stealing new references."
},
{"popBAD", (PyCFunction)pop_and_print_BAD, METH_O,
"Borrowed refs, might segfault."
},
{"popOK", (PyCFunction)pop_and_print_OK, METH_O,
"Borrowed refs, should not segfault."
},
{"incref", (PyCFunction)bad_incref, METH_O,
"Naughty incref."
},
{NULL, NULL, 0, NULL} /* Sentinel */
};
static PyModuleDef cPyRefs_module = {
PyModuleDef_HEAD_INIT,
"cPyRefs",
"Examples of reference types in a 'C' extension.",
-1,
cPyRefs_methods,
NULL, /* inquiry m_reload */
NULL, /* traverseproc m_traverse */
NULL, /* inquiry m_clear */
NULL, /* freefunc m_free */
};
PyMODINIT_FUNC
PyInit_cPyRefs(void)
{
return PyModule_Create(&cPyRefs_module);
}