Skip to content

Commit 95f56b1

Browse files
authored
gh-141510: Return frozendict unmodified in PyDict_Copy() (#145505)
Add also the internal _PyDict_CopyAsDict() function.
1 parent 8a7eb8b commit 95f56b1

File tree

4 files changed

+50
-36
lines changed

4 files changed

+50
-36
lines changed

Include/internal/pycore_dict.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ extern void _PyDict_Clear_LockHeld(PyObject *op);
160160
PyAPI_FUNC(void) _PyDict_EnsureSharedOnRead(PyDictObject *mp);
161161
#endif
162162

163+
extern PyObject* _PyDict_CopyAsDict(PyObject *op);
164+
163165
#define DKIX_EMPTY (-1)
164166
#define DKIX_DUMMY (-2) /* Used internally */
165167
#define DKIX_ERROR (-3)

Lib/test/test_capi/test_dict.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,18 @@ def test_dict_copy(self):
9898
# Test PyDict_Copy()
9999
copy = _testlimitedcapi.dict_copy
100100
for dict_type in ANYDICT_TYPES:
101-
if issubclass(dict_type, frozendict):
102-
expected_type = frozendict
103-
else:
104-
expected_type = dict
105101
dct = dict_type({1: 2})
106102
dct_copy = copy(dct)
107-
self.assertIs(type(dct_copy), expected_type)
108-
self.assertEqual(dct_copy, dct)
103+
if dict_type == frozendict:
104+
expected_type = frozendict
105+
self.assertIs(dct_copy, dct)
106+
else:
107+
if issubclass(dict_type, frozendict):
108+
expected_type = frozendict
109+
else:
110+
expected_type = dict
111+
self.assertIs(type(dct_copy), expected_type)
112+
self.assertEqual(dct_copy, dct)
109113

110114
for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES:
111115
self.assertRaises(SystemError, copy, test_type())

Objects/dictobject.c

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4235,9 +4235,6 @@ static PyObject *
42354235
dict_copy_impl(PyDictObject *self)
42364236
/*[clinic end generated code: output=ffb782cf970a5c39 input=73935f042b639de4]*/
42374237
{
4238-
if (PyFrozenDict_CheckExact(self)) {
4239-
return Py_NewRef(self);
4240-
}
42414238
return PyDict_Copy((PyObject *)self);
42424239
}
42434240

@@ -4263,18 +4260,17 @@ copy_values(PyDictValues *values)
42634260
}
42644261

42654262
static PyObject *
4266-
copy_lock_held(PyObject *o)
4263+
copy_lock_held(PyObject *o, int as_frozendict)
42674264
{
42684265
PyObject *copy;
42694266
PyDictObject *mp;
4270-
int frozendict = PyFrozenDict_Check(o);
42714267

42724268
ASSERT_DICT_LOCKED(o);
42734269

42744270
mp = (PyDictObject *)o;
42754271
if (mp->ma_used == 0) {
42764272
/* The dict is empty; just return a new dict. */
4277-
if (frozendict) {
4273+
if (as_frozendict) {
42784274
return PyFrozenDict_New(NULL);
42794275
}
42804276
else {
@@ -4288,7 +4284,7 @@ copy_lock_held(PyObject *o)
42884284
if (newvalues == NULL) {
42894285
return PyErr_NoMemory();
42904286
}
4291-
if (frozendict) {
4287+
if (as_frozendict) {
42924288
split_copy = (PyDictObject *)PyObject_GC_New(PyFrozenDictObject,
42934289
&PyFrozenDict_Type);
42944290
}
@@ -4307,7 +4303,7 @@ copy_lock_held(PyObject *o)
43074303
split_copy->ma_used = mp->ma_used;
43084304
split_copy->_ma_watcher_tag = 0;
43094305
dictkeys_incref(mp->ma_keys);
4310-
if (frozendict) {
4306+
if (as_frozendict) {
43114307
PyFrozenDictObject *frozen = (PyFrozenDictObject *)split_copy;
43124308
frozen->ma_hash = -1;
43134309
}
@@ -4318,7 +4314,7 @@ copy_lock_held(PyObject *o)
43184314
if (Py_TYPE(mp)->tp_iter == dict_iter &&
43194315
mp->ma_values == NULL &&
43204316
(mp->ma_used >= (mp->ma_keys->dk_nentries * 2) / 3) &&
4321-
!frozendict)
4317+
!as_frozendict)
43224318
{
43234319
/* Use fast-copy if:
43244320
@@ -4350,7 +4346,7 @@ copy_lock_held(PyObject *o)
43504346
return (PyObject *)new;
43514347
}
43524348

4353-
if (frozendict) {
4349+
if (as_frozendict) {
43544350
copy = PyFrozenDict_New(NULL);
43554351
}
43564352
else {
@@ -4364,6 +4360,19 @@ copy_lock_held(PyObject *o)
43644360
return NULL;
43654361
}
43664362

4363+
// Similar to PyDict_Copy(), but copy also frozendict.
4364+
static PyObject *
4365+
_PyDict_Copy(PyObject *o)
4366+
{
4367+
assert(PyAnyDict_Check(o));
4368+
4369+
PyObject *res;
4370+
Py_BEGIN_CRITICAL_SECTION(o);
4371+
res = copy_lock_held(o, PyFrozenDict_Check(o));
4372+
Py_END_CRITICAL_SECTION();
4373+
return res;
4374+
}
4375+
43674376
PyObject *
43684377
PyDict_Copy(PyObject *o)
43694378
{
@@ -4372,11 +4381,22 @@ PyDict_Copy(PyObject *o)
43724381
return NULL;
43734382
}
43744383

4375-
PyObject *res;
4376-
Py_BEGIN_CRITICAL_SECTION(o);
4384+
if (PyFrozenDict_CheckExact(o)) {
4385+
return Py_NewRef(o);
4386+
}
4387+
4388+
return _PyDict_Copy(o);
4389+
}
43774390

4378-
res = copy_lock_held(o);
4391+
// Similar to PyDict_Copy(), but return a dict if the argument is a frozendict.
4392+
PyObject*
4393+
_PyDict_CopyAsDict(PyObject *o)
4394+
{
4395+
assert(PyAnyDict_Check(o));
43794396

4397+
PyObject *res;
4398+
Py_BEGIN_CRITICAL_SECTION(o);
4399+
res = copy_lock_held(o, 0);
43804400
Py_END_CRITICAL_SECTION();
43814401
return res;
43824402
}
@@ -4925,7 +4945,7 @@ dict_or(PyObject *self, PyObject *other)
49254945
if (!PyAnyDict_Check(self) || !PyAnyDict_Check(other)) {
49264946
Py_RETURN_NOTIMPLEMENTED;
49274947
}
4928-
PyObject *new = PyDict_Copy(self);
4948+
PyObject *new = _PyDict_Copy(self);
49294949
if (new == NULL) {
49304950
return NULL;
49314951
}
@@ -6479,7 +6499,7 @@ dictitems_xor_lock_held(PyObject *d1, PyObject *d2)
64796499
ASSERT_DICT_LOCKED(d1);
64806500
ASSERT_DICT_LOCKED(d2);
64816501

6482-
PyObject *temp_dict = copy_lock_held(d1);
6502+
PyObject *temp_dict = copy_lock_held(d1, PyFrozenDict_Check(d1));
64836503
if (temp_dict == NULL) {
64846504
return NULL;
64856505
}

Objects/typeobject.c

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4850,21 +4850,9 @@ type_new_get_slots(type_new_ctx *ctx, PyObject *dict)
48504850
static PyTypeObject*
48514851
type_new_init(type_new_ctx *ctx)
48524852
{
4853-
PyObject *dict;
4854-
if (PyFrozenDict_Check(ctx->orig_dict)) {
4855-
dict = PyDict_New();
4856-
if (dict == NULL) {
4857-
goto error;
4858-
}
4859-
if (PyDict_Merge(dict, ctx->orig_dict, 1) < 0) {
4860-
goto error;
4861-
}
4862-
}
4863-
else {
4864-
dict = PyDict_Copy(ctx->orig_dict);
4865-
if (dict == NULL) {
4866-
goto error;
4867-
}
4853+
PyObject *dict = _PyDict_CopyAsDict(ctx->orig_dict);
4854+
if (dict == NULL) {
4855+
goto error;
48684856
}
48694857

48704858
if (type_new_get_slots(ctx, dict) < 0) {

0 commit comments

Comments
 (0)