@@ -124,6 +124,15 @@ The Python documentation for the `Tuple API <https://docs.python.org/3/c-api/tup
124
124
``PyTuple_SetItem() ``
125
125
---------------------
126
126
127
+ `PyTuple_SetItem() `_ inserts an object into a tuple with error checking.
128
+ It checks for these errors returning non-zero in that case:
129
+
130
+ * The container is a tuple.
131
+ * The index is in range. Negative indexes are not allowed.
132
+
133
+ As seen below, the failure of `PyTuple_SetItem() `_ has serious consequences for the value that is intended to be
134
+ inserted.
135
+
127
136
Basic Usage
128
137
^^^^^^^^^^^
129
138
@@ -159,21 +168,192 @@ What happens when you use `PyTuple_SetItem()`_ to replace an existing element in
159
168
PyTuple_SetItem(container, 0, value_a); /* Ref count of value_a will be 1. */
160
169
PyObject *value_b = new_unique_string(__FUNCTION__, NULL); /* Ref count will be 1. */
161
170
PyTuple_SetItem(container, 0, value_b);
162
- /* Ref count of value_b will be 1, value_a ref count will be decremented. */
171
+ /* Ref count of value_b will be 1, value_a ref count will be decremented (possibly free'd) . */
163
172
164
173
For code tests see:
165
174
166
175
* ``dbg_PyTuple_SetItem_steals_replace `` in ``src/cpy/Containers/DebugContainers.c ``.
167
176
* ``test_PyTuple_SetItem_steals_replace `` in ``src/cpy/RefCount/cRefCount.c ``.
168
- * ``tests.unit.test_c_ref_count.test_test_PyTuple_SetItem_steals_replece ``.
177
+ * ``tests.unit.test_c_ref_count.test_test_PyTuple_SetItem_steals_replace ``.
178
+
179
+ ``PyTuple_SetItem() `` Failures
180
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
181
+
182
+ `PyTuple_SetItem() `_ can fail for these reasons:
183
+
184
+ * The given container is not a tuple.
185
+ * The index is out of range; index < 0 or index >= tuple length. So negative indexes are not allowed.
186
+
187
+ A consequence of failure is that the value being inserted will be decref'd.
188
+ For example this code will segfault:
189
+
190
+ .. code-block :: c
191
+
192
+ PyObject *container = PyTuple_New(1); /* Reference count will be 1. */
193
+ PyObject *value = new_unique_string(__FUNCTION__, NULL); /* Ref count will be 1. */
194
+ assert(Py_REFCNT(value) == 1);
195
+ /* Lets increment the reference count so it can be examined after
196
+ * PyTuple_SetItem() failure. */
197
+ Py_INCREF(value);
198
+ assert(Py_REFCNT(value) == 2);
199
+ assert(!PyErr_Occurred());
200
+ int result = PyTuple_SetItem(container, 1, value); /* Index out of range. */
201
+ /* Failure... */
202
+ assert(result == -1);
203
+ assert(PyErr_Occurred());
204
+ /* Yes, has been decremented on failure.
205
+ * If we hadn't done Py_INCREF(value); above then value would have gone
206
+ * out of scope and lost to us. */
207
+ assert(Py_REFCNT(value) == 1);
208
+ Py_DECREF(container); /* OK. */
209
+ /* This would segfault if we hadn't done Py_INCREF(value); above. */
210
+ Py_DECREF(value);
211
+
212
+ Or to be clear:
213
+
214
+ .. code-block :: c
215
+
216
+ PyObject *container = PyTuple_New(1); /* Reference count will be 1. */
217
+ PyObject *value = new_unique_string(__FUNCTION__, NULL); /* Ref count will be 1. */
218
+ PyTuple_SetItem(container, 1, value); /* Index out of range. */
219
+ Py_DECREF(value); /* SIGSEGV */
169
220
221
+ For code tests see (not a tuple):
222
+
223
+ * ``dbg_PyTuple_SetItem_fails_not_a_tuple `` in ``src/cpy/Containers/DebugContainers.c ``.
224
+ * ``test_PyTuple_SetItem_fails_not_a_tuple `` in ``src/cpy/RefCount/cRefCount.c ``.
225
+ * ``tests.unit.test_c_ref_count.test_test_PyTuple_SetItem_fails_not_a_tuple ``.
226
+
227
+ And (index out of range):
228
+
229
+ * ``dbg_PyTuple_SetItem_fails_out_of_range `` in ``src/cpy/Containers/DebugContainers.c ``.
230
+ * ``test_PyTuple_SetItem_fails_out_of_range `` in ``src/cpy/RefCount/cRefCount.c ``.
231
+ * ``tests.unit.test_c_ref_count.test_test_PyTuple_SetItem_fails_out_of_range ``.
170
232
171
233
172
234
``PyTuple_SET_ITEM() ``
173
235
----------------------
174
236
175
- `PyTuple_SET_ITEM() `_
237
+ `PyTuple_SET_ITEM() `_ inserts an object into a tuple without any error checking.
238
+ Because of that is slightly faster than `PyTuple_SetItem() `_.
239
+ If invoked with these errors the results are likely to be tragic, mostly undefined behaviour and/or memory corruption:
240
+
241
+ * The container is a not a tuple.
242
+ * The index is out of range.
243
+
244
+ Importantly `PyTuple_SET_ITEM() `_ behaves **differently ** to `PyTuple_SetItem() `_ when replacing another object.
245
+
246
+ Basic Usage
247
+ ^^^^^^^^^^^
248
+
249
+ `PyTuple_SET_ITEM() `_ *steals * a reference.
250
+
251
+ .. code-block :: c
252
+
253
+ PyObject *container = PyTuple_New(1); /* Reference count will be 1. */
254
+ PyObject *value = new_unique_string(__FUNCTION__, NULL); /* Ref count will be 1. */
255
+ PyTuple_SET_ITEM(container, 0, value); /* Ref count of value will be 1. */
256
+ /* get_item == value and Ref count will be 1. */
257
+ PyObject *get_item = PyTuple_GET_ITEM(container, 0);
258
+ Py_DECREF(container); /* The contents of the container, value, will be decref'd */
259
+ /* Do not do this as the container deals with this. */
260
+ /* Py_DECREF(value); */
261
+
262
+ For code tests see:
263
+
264
+ * ``dbg_PyTuple_PyTuple_SET_ITEM_steals `` in ``src/cpy/Containers/DebugContainers.c ``.
265
+ * ``test_PyTuple_PyTuple_SET_ITEM_steals `` in ``src/cpy/RefCount/cRefCount.c ``.
266
+ * ``tests.unit.test_c_ref_count.test_test_PyTuple_PyTuple_SET_ITEM_steals ``.
267
+
268
+ Replacement
269
+ ^^^^^^^^^^^
270
+
271
+ `PyTuple_SET_ITEM() `_ differs from `PyTuple_SetItem() `_ when replacing an existing element in a tuple.
272
+ The original reference will be leaked:
273
+
274
+ .. code-block :: c
275
+
276
+ PyObject *container = PyTuple_New(1); /* Reference count will be 1. */
277
+ PyObject *value_a = new_unique_string(__FUNCTION__, NULL); /* Ref count will be 1. */
278
+ PyTuple_SET_ITEM(container, 0, value_a); /* Ref count of value_a will be 1. */
279
+ PyObject *value_b = new_unique_string(__FUNCTION__, NULL); /* Ref count will be 1. */
280
+ PyTuple_SET_ITEM(container, 0, value_b);
281
+ assert(Py_REFCNT(value_a) == 1);
282
+ /* Ref count of value_b will be 1, value_a ref count will still be at 1. value_a will be leaked. */
283
+
284
+ For code tests see:
285
+
286
+ * ``dbg_PyTuple_SET_ITEM_steals_replace `` in ``src/cpy/Containers/DebugContainers.c ``.
287
+ * ``test_PyTuple_SetItem_steals_replace `` in ``src/cpy/RefCount/cRefCount.c ``.
288
+ * ``tests.unit.test_c_ref_count.test_test_PyTuple_SetItem_steals_replace ``.
289
+
290
+
291
+ ``PyTuple_SetItem() `` Failures
292
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
293
+
294
+ `PyTuple_SetItem() `_ can fail for these reasons:
295
+
296
+
297
+ Setting and Replacing ``NULL ``
298
+ ------------------------------
299
+
300
+ Both `PyTuple_SetItem() `_ and `PyTuple_SET_ITEM() `_ behave the same way.
301
+
302
+ Setting a ``NULL `` will not cause an error:
303
+
304
+ .. code-block :: c
305
+
306
+ assert(!PyErr_Occurred());
307
+ PyTuple_SetItem(container, 0, NULL);
308
+ assert(!PyErr_Occurred());
309
+
310
+ For code tests see:
311
+
312
+ * ``dbg_PyTuple_SetIem_NULL `` in ``src/cpy/Containers/DebugContainers.c ``.
313
+ * ``test_PyTuple_SetItem_NULL `` in ``src/cpy/RefCount/cRefCount.c ``.
314
+ * ``tests.unit.test_c_ref_count.test_test_PyTuple_SetItem_NULL ``.
315
+
316
+ And:
317
+
318
+ .. code-block :: c
319
+
320
+ assert(!PyErr_Occurred());
321
+ PyTuple_SET_ITEM(container, 0, NULL);
322
+ assert(!PyErr_Occurred());
323
+
324
+ For code tests see:
325
+
326
+ * ``dbg_PyTuple_SET_ITEM_NULL `` in ``src/cpy/Containers/DebugContainers.c ``.
327
+ * ``test_PyTuple_SET_ITEM_NULL `` in ``src/cpy/RefCount/cRefCount.c ``.
328
+ * ``tests.unit.test_c_ref_count.test_test_PyTuple_SET_ITEM_NULL ``.
329
+
330
+ Replacing a ``NULL `` will not cause an error, the replaced value reference is *stolen *:
331
+
332
+ .. code-block :: c
333
+
334
+ PyTuple_SetItem(container, 0, NULL);
335
+ PyObject *value = new_unique_string(__FUNCTION__, NULL); /* Ref Count of value is 1. */
336
+ PyTuple_SetItem(container, 0, value); /* Ref Count of value is still 1. */
337
+
338
+ For code tests see:
339
+
340
+ * ``dbg_PyTuple_SetIem_NULL_SetIem `` in ``src/cpy/Containers/DebugContainers.c ``.
341
+ * ``test_PyTuple_SetItem_NULL_SetIem `` in ``src/cpy/RefCount/cRefCount.c ``.
342
+ * ``tests.unit.test_c_ref_count.test_test_PyTuple_SetItem_NULL_SetIem ``.
343
+
344
+ And:
345
+
346
+ .. code-block :: c
347
+
348
+ PyTuple_SET_ITEM(container, 0, NULL);
349
+ PyObject *value = new_unique_string(__FUNCTION__, NULL); /* Ref Count of value is 1. */
350
+ PyTuple_SET_ITEM(container, 0, value); /* Ref Count of value is still 1. */
351
+
352
+ For code tests see:
176
353
354
+ * ``dbg_PyTuple_SET_ITEM_NULL_SET_ITEM `` in ``src/cpy/Containers/DebugContainers.c ``.
355
+ * ``test_PyTuple_SET_ITEM_NULL_SET_ITEM `` in ``src/cpy/RefCount/cRefCount.c ``.
356
+ * ``tests.unit.test_c_ref_count.test_test_PyTuple_SET_ITEM_NULL_SET_ITEM ``.
177
357
178
358
``Py_BuildValue() ``
179
359
-------------------
0 commit comments