Skip to content

Commit 96bd4f2

Browse files
committed
Add PyFrozenSetWriter_AddSteal() function
1 parent f1992f1 commit 96bd4f2

9 files changed

Lines changed: 58 additions & 10 deletions

File tree

‎Doc/c-api/set.rst‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ See also the :c:func:`PyFrozenSet_New` function.
278278
On success, return ``0``.
279279
On error, set an exception and return ``-1``.
280280
281+
.. c:function:: int PyFrozenSetWriter_AddSteal(PyFrozenSetWriter *writer, PyObject *item)
282+
283+
Similar to :c:func:`PyFrozenSetWriter_Add`, but :term:`steal` the *item*
284+
reference.
285+
281286
.. c:function:: int PyFrozenSetWriter_Update(PyFrozenSetWriter *writer, PyObject *iterable)
282287
283288
Update the frozenset writer, adding elements from *iterable*.

‎Doc/whatsnew/3.16.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,7 @@ New features
13021302
* :c:func:`PyFrozenSetWriter_Finish`
13031303
* :c:func:`PyFrozenSetWriter_Discard`
13041304
* :c:func:`PyFrozenSetWriter_Add`
1305+
* :c:func:`PyFrozenSetWriter_AddSteal`
13051306
* :c:func:`PyFrozenSetWriter_Update`
13061307

13071308
(Contributed by Victor Stinner in :gh:`160147`.)

‎Include/cpython/setobject.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ PyAPI_FUNC(void) PyFrozenSetWriter_Discard(PyFrozenSetWriter *writer);
8181
PyAPI_FUNC(int) PyFrozenSetWriter_Add(
8282
PyFrozenSetWriter *writer,
8383
PyObject *item);
84+
PyAPI_FUNC(int) PyFrozenSetWriter_AddSteal(
85+
PyFrozenSetWriter *writer,
86+
PyObject *item);
8487
PyAPI_FUNC(int) PyFrozenSetWriter_Update(
8588
PyFrozenSetWriter *writer,
8689
PyObject *iterable);

‎Lib/test/test_capi/test_set.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ def create_items(self, nitem):
313313
# Use mortal (and unique) objects to be able to detect reference leaks
314314
return tuple(object() for _ in range(nitem))
315315

316-
def test_append(self):
316+
def test_add(self):
317317
# Test PyFrozenSetWriter_Append()
318318
items = self.create_items(self.PySet_MINSIZE * 2 - 1)
319319
table_size = self.compute_table_size(len(items))
@@ -324,6 +324,13 @@ def test_append(self):
324324
writer.add(item)
325325
self.frozenset_equal(writer.finish(), items)
326326

327+
# Test error (unhashable type)
328+
writer = self.create_writer(0)
329+
writer.update('abc')
330+
with self.assertRaisesRegex(TypeError, "unhashable type: 'list'"):
331+
writer.add([])
332+
self.frozenset_equal(writer.finish(), frozenset('abc'))
333+
327334
def test_update(self):
328335
# Test PyFrozenSetWriter_Update()
329336
items1 = self.create_items(self.PySet_MINSIZE - 1)

‎Misc/NEWS.d/next/C_API/2026-10-21-18-20-57.gh-issue-160147.7uo4pF.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Add a new :ref:`PyFrozenSetWriter <pyfrozensetwriter>` C API to create a
55
* :c:func:`PyFrozenSetWriter_Finish`
66
* :c:func:`PyFrozenSetWriter_Discard`
77
* :c:func:`PyFrozenSetWriter_Add`
8+
* :c:func:`PyFrozenSetWriter_AddSteal`
89
* :c:func:`PyFrozenSetWriter_Update`
910

1011
Patch by Victor Stinner.

‎Modules/_hashopenssl.c‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,11 +2505,11 @@ _openssl_hash_name_mapper(const EVP_MD *md, const char *from,
25052505
py_name = name == NULL ? NULL : PyUnicode_FromString(name);
25062506
if (py_name == NULL) {
25072507
state->error = 1;
2508-
} else {
2509-
if (PyFrozenSetWriter_Add(state->set, py_name) != 0) {
2510-
state->error = 1;
2511-
}
2512-
Py_DECREF(py_name);
2508+
return;
2509+
}
2510+
2511+
if (PyFrozenSetWriter_AddSteal(state->set, py_name) != 0) {
2512+
state->error = 1;
25132513
}
25142514
}
25152515

‎Modules/_ssl.c‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6541,8 +6541,7 @@ parseKeyUsage(PCCERT_CONTEXT pCertCtx, DWORD flags)
65416541
if (oid == NULL) {
65426542
goto error;
65436543
}
6544-
err = PyFrozenSetWriter_Add(retval, oid);
6545-
Py_DECREF(oid);
6544+
err = PyFrozenSetWriter_AddSteal(retval, oid);
65466545
if (err == -1) {
65476546
goto error;
65486547
}

‎Modules/_testcapi/set.c‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,22 @@ writer_add(PyObject *self_raw, PyObject *item)
118118
}
119119

120120

121+
// Test PyFrozenSetWriter_AddSteal()
122+
static PyObject*
123+
writer_add_steal(PyObject *self_raw, PyObject *item)
124+
{
125+
WriterObject *self = (WriterObject *)self_raw;
126+
if (writer_check(self) < 0) {
127+
return NULL;
128+
}
129+
130+
if (PyFrozenSetWriter_AddSteal(self->writer, Py_NewRef(item)) < 0) {
131+
return NULL;
132+
}
133+
Py_RETURN_NONE;
134+
}
135+
136+
121137
// Test PyFrozenSetWriter_Update()
122138
static PyObject*
123139
writer_update(PyObject *self_raw, PyObject *iterable)
@@ -180,6 +196,7 @@ writer_discard(PyObject *self_raw, PyObject *Py_UNUSED(args))
180196

181197
static PyMethodDef writer_methods[] = {
182198
{"add", _PyCFunction_CAST(writer_add), METH_O},
199+
{"add_steal", _PyCFunction_CAST(writer_add_steal), METH_O},
183200
{"update", _PyCFunction_CAST(writer_update), METH_O},
184201
{"get_table_size", _PyCFunction_CAST(writer_get_table_size), METH_NOARGS},
185202
{"finish", _PyCFunction_CAST(writer_finish), METH_NOARGS},

‎Objects/setobject.c‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3304,11 +3304,26 @@ PyFrozenSetWriter_Finish(PyFrozenSetWriter *writer)
33043304

33053305

33063306
int
3307-
PyFrozenSetWriter_Add(PyFrozenSetWriter *writer, PyObject *item)
3307+
PyFrozenSetWriter_AddSteal(PyFrozenSetWriter *writer, PyObject *item)
33083308
{
33093309
assert(frozsetwriter_check_consistency(writer));
3310+
3311+
Py_hash_t hash = PyObject_Hash(item);
3312+
if (hash == -1) {
3313+
set_unhashable_type(item);
3314+
Py_DECREF(item);
3315+
return -1;
3316+
}
3317+
33103318
PySetObject *frozenset = _PySet_CAST(writer);
3311-
return set_add_key(frozenset, item);
3319+
return set_add_entry_takeref(frozenset, item, hash);
3320+
}
3321+
3322+
3323+
int
3324+
PyFrozenSetWriter_Add(PyFrozenSetWriter *writer, PyObject *item)
3325+
{
3326+
return PyFrozenSetWriter_AddSteal(writer, Py_NewRef(item));
33123327
}
33133328

33143329

0 commit comments

Comments
 (0)