Skip to content

Commit 0cbef89

Browse files
committed
gh-141510: Always return a dict in PyDict_Copy()
* PyDict_Copy() now also returns a dict if the argument is a frozendict. * Remove _PyDict_CopyAsDict() function. * Fix frozendict.items() ^ frozendict.items(). Add non-regression test.
1 parent 0a51113 commit 0cbef89

File tree

7 files changed

+60
-43
lines changed

7 files changed

+60
-43
lines changed

Doc/c-api/dict.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,6 @@ Dictionary objects
8282
8383
Return a new dictionary that contains the same key-value pairs as *p*.
8484
85-
.. versionchanged:: next
86-
If *p* is a subclass of :class:`frozendict`, the result will be a
87-
:class:`frozendict` instance instead of a :class:`dict` instance.
88-
8985
.. c:function:: int PyDict_SetItem(PyObject *p, PyObject *key, PyObject *val)
9086
9187
Insert *val* into the dictionary *p* with a key of *key*. *key* must be

Include/internal/pycore_dict.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,6 @@ 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-
165163
#define DKIX_EMPTY (-1)
166164
#define DKIX_DUMMY (-2) /* Used internally */
167165
#define DKIX_ERROR (-3)

Lib/test/test_capi/test_dict.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,8 @@ def test_dict_copy(self):
100100
for dict_type in ANYDICT_TYPES:
101101
dct = dict_type({1: 2})
102102
dct_copy = 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)
103+
self.assertIs(type(dct_copy), dict)
104+
self.assertEqual(dct_copy, dct)
113105

114106
for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES:
115107
self.assertRaises(SystemError, copy, test_type())

Lib/test/test_dict.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,11 @@ def test_update(self):
18631863
self.assertEqual(d, frozendict({'x': 1, 'y': 2}))
18641864
self.assertEqual(copy, frozendict({'x': 1}))
18651865

1866+
def test_items_xor(self):
1867+
# test "a ^ b" operator on items views
1868+
res = frozendict(a=1, b=2).items() ^ frozendict(b=2, c=3).items()
1869+
self.assertEqual(res, {('a', 1), ('c', 3)})
1870+
18661871
def test_repr(self):
18671872
d = frozendict()
18681873
self.assertEqual(repr(d), "frozendict()")

Objects/clinic/dictobject.c.h

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Objects/dictobject.c

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,9 @@ static int dict_merge_from_seq2(PyObject *d, PyObject *seq2, int override);
146146

147147
/*[clinic input]
148148
class dict "PyDictObject *" "&PyDict_Type"
149+
class frozendict "PyFrozenDictObject *" "&PyFrozenDict_Type"
149150
[clinic start generated code]*/
150-
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/
151+
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5dfa93bac68e7c54]*/
151152

152153

153154
/*
@@ -4384,19 +4385,6 @@ copy_lock_held(PyObject *o, int as_frozendict)
43844385
return NULL;
43854386
}
43864387

4387-
// Similar to PyDict_Copy(), but copy also frozendict.
4388-
static PyObject *
4389-
_PyDict_Copy(PyObject *o)
4390-
{
4391-
assert(PyAnyDict_Check(o));
4392-
4393-
PyObject *res;
4394-
Py_BEGIN_CRITICAL_SECTION(o);
4395-
res = copy_lock_held(o, PyFrozenDict_Check(o));
4396-
Py_END_CRITICAL_SECTION();
4397-
return res;
4398-
}
4399-
44004388
PyObject *
44014389
PyDict_Copy(PyObject *o)
44024390
{
@@ -4405,22 +4393,23 @@ PyDict_Copy(PyObject *o)
44054393
return NULL;
44064394
}
44074395

4408-
if (PyFrozenDict_CheckExact(o)) {
4409-
return Py_NewRef(o);
4410-
}
4411-
4412-
return _PyDict_Copy(o);
4396+
PyObject *res;
4397+
Py_BEGIN_CRITICAL_SECTION(o);
4398+
res = copy_lock_held(o, 0);
4399+
Py_END_CRITICAL_SECTION();
4400+
return res;
44134401
}
44144402

4415-
// Similar to PyDict_Copy(), but return a dict if the argument is a frozendict.
4416-
PyObject*
4417-
_PyDict_CopyAsDict(PyObject *o)
4403+
// Similar to PyDict_Copy(), but return a frozendict if the argument
4404+
// is a frozendict.
4405+
static PyObject *
4406+
_PyDict_Copy(PyObject *o)
44184407
{
44194408
assert(PyAnyDict_Check(o));
44204409

44214410
PyObject *res;
44224411
Py_BEGIN_CRITICAL_SECTION(o);
4423-
res = copy_lock_held(o, 0);
4412+
res = copy_lock_held(o, PyFrozenDict_Check(o));
44244413
Py_END_CRITICAL_SECTION();
44254414
return res;
44264415
}
@@ -6523,7 +6512,7 @@ dictitems_xor_lock_held(PyObject *d1, PyObject *d2)
65236512
ASSERT_DICT_LOCKED(d1);
65246513
ASSERT_DICT_LOCKED(d2);
65256514

6526-
PyObject *temp_dict = copy_lock_held(d1, PyFrozenDict_Check(d1));
6515+
PyObject *temp_dict = copy_lock_held(d1, 0);
65276516
if (temp_dict == NULL) {
65286517
return NULL;
65296518
}
@@ -8057,7 +8046,7 @@ static PyMethodDef frozendict_methods[] = {
80578046
DICT_ITEMS_METHODDEF
80588047
DICT_VALUES_METHODDEF
80598048
DICT_FROMKEYS_METHODDEF
8060-
DICT_COPY_METHODDEF
8049+
FROZENDICT_COPY_METHODDEF
80618050
DICT___REVERSED___METHODDEF
80628051
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
80638052
{"__getnewargs__", frozendict_getnewargs, METH_NOARGS},
@@ -8182,6 +8171,25 @@ PyFrozenDict_New(PyObject *iterable)
81828171
}
81838172
}
81848173

8174+
/*[clinic input]
8175+
frozendict.copy
8176+
8177+
Return a shallow copy of the frozendict.
8178+
[clinic start generated code]*/
8179+
8180+
static PyObject *
8181+
frozendict_copy_impl(PyFrozenDictObject *self)
8182+
/*[clinic end generated code: output=e580fd91d9fc2cf7 input=19650637a441d51e]*/
8183+
{
8184+
assert(PyFrozenDict_Check(self));
8185+
8186+
if (PyFrozenDict_CheckExact(self)) {
8187+
return Py_NewRef(self);
8188+
}
8189+
8190+
return _PyDict_Copy((PyObject*)self);
8191+
}
8192+
81858193

81868194
PyTypeObject PyFrozenDict_Type = {
81878195
PyVarObject_HEAD_INIT(&PyType_Type, 0)

Objects/typeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4850,7 +4850,7 @@ 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 = _PyDict_CopyAsDict(ctx->orig_dict);
4853+
PyObject *dict = PyDict_Copy(ctx->orig_dict);
48544854
if (dict == NULL) {
48554855
goto error;
48564856
}

0 commit comments

Comments
 (0)