Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1637c04
[WIP] PEP 814: Add built-in frozendict type
vstinner Nov 2, 2025
49aca48
Update Tools/build/generate_token.py for token.py
vstinner Nov 13, 2025
3299299
Try to fix build on Ubuntu and macOS
vstinner Nov 13, 2025
18ce520
Fix make check-c-globals
vstinner Nov 13, 2025
1a72eec
Add PyFrozenDict_Type to static_types
vstinner Nov 13, 2025
9d6ca58
Update Doc/library/stdtypes.rst
vstinner Nov 13, 2025
439c98c
Replace PyDict_Check() with _PyAnyDict_Check()
vstinner Nov 13, 2025
4a1a504
copy: support frozendict
vstinner Nov 13, 2025
302767b
Fix frozendict.__reduce_ex__()
vstinner Nov 13, 2025
07b3510
exec(): accept frozendict for globals
vstinner Nov 13, 2025
04f66ad
Make PyAnyDict_Check() public
vstinner Nov 13, 2025
6fca6d1
Optimize frozendict.copy()
vstinner Nov 14, 2025
6727967
Fix frozendict merge ("|=" operator); add tests
vstinner Nov 14, 2025
d231cdb
copy: use _copy_atomic_types instead; add tests
vstinner Nov 14, 2025
3a07c46
frozendict
vstinner Nov 14, 2025
bc25bb2
pprint supports frozendict
vstinner Nov 14, 2025
0acff5e
change json.tool._group_to_theme_color formatting
vstinner Nov 14, 2025
e90156e
json: use PyAnyDict_Check()
vstinner Nov 14, 2025
fe7e7f5
_pickle: fix refleak
vstinner Nov 14, 2025
4c5c8d2
frozendict_hash: use atomic load/store
vstinner Nov 14, 2025
0a57448
Update Lib/pydoc_data/topics.py
vstinner Nov 14, 2025
fc66056
Fix test_copy: remove duplicated test
vstinner Nov 17, 2025
c1f5055
frozendict | {} returns the same frozendict
vstinner Dec 12, 2025
6ff9c67
Fix frozendict_or()
vstinner Dec 12, 2025
9a64439
Moar frozendict|frozendict optimizations
vstinner Dec 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Replace PyDict_Check() with _PyAnyDict_Check()
  • Loading branch information
vstinner committed Nov 13, 2025
commit 439c98c307e0ce3d62b0eabf0afe065a0bbe4c07
2 changes: 1 addition & 1 deletion Modules/_collectionsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2403,7 +2403,7 @@ defdict_or(PyObject* left, PyObject* right)
self = right;
other = left;
}
if (!PyDict_Check(other)) {
if (!_PyAnyDict_Check(other)) {
Py_RETURN_NOTIMPLEMENTED;
}
// Like copy(), this calls the object's class.
Expand Down
10 changes: 5 additions & 5 deletions Objects/abstract.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ PyObject_GetItem(PyObject *o, PyObject *key)
int
PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result)
{
if (PyDict_CheckExact(obj)) {
if (_PyAnyDict_CheckExact(obj)) {
return PyDict_GetItemRef(obj, key, result);
}

Expand Down Expand Up @@ -1672,7 +1672,7 @@ PyNumber_ToBase(PyObject *n, int base)
int
PySequence_Check(PyObject *s)
{
if (PyDict_Check(s))
if (_PyAnyDict_Check(s))
return 0;
return Py_TYPE(s)->tp_as_sequence &&
Py_TYPE(s)->tp_as_sequence->sq_item != NULL;
Expand Down Expand Up @@ -2454,7 +2454,7 @@ PyMapping_Keys(PyObject *o)
if (o == NULL) {
return null_error();
}
if (PyDict_CheckExact(o)) {
if (_PyAnyDict_CheckExact(o)) {
return PyDict_Keys(o);
}
return method_output_as_list(o, &_Py_ID(keys));
Expand All @@ -2466,7 +2466,7 @@ PyMapping_Items(PyObject *o)
if (o == NULL) {
return null_error();
}
if (PyDict_CheckExact(o)) {
if (_PyAnyDict_CheckExact(o)) {
return PyDict_Items(o);
}
return method_output_as_list(o, &_Py_ID(items));
Expand All @@ -2478,7 +2478,7 @@ PyMapping_Values(PyObject *o)
if (o == NULL) {
return null_error();
}
if (PyDict_CheckExact(o)) {
if (_PyAnyDict_CheckExact(o)) {
return PyDict_Values(o);
}
return method_output_as_list(o, &_Py_ID(values));
Expand Down
2 changes: 1 addition & 1 deletion Objects/descrobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,7 @@ static int
mappingproxy_contains(PyObject *self, PyObject *key)
{
mappingproxyobject *pp = (mappingproxyobject *)self;
if (PyDict_CheckExact(pp->mapping))
if (_PyAnyDict_CheckExact(pp->mapping))
return PyDict_Contains(pp->mapping, key);
else
return PySequence_Contains(pp->mapping, key);
Expand Down
32 changes: 16 additions & 16 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,7 @@ new_dict_with_shared_keys(PyDictKeysObject *keys)
static PyDictKeysObject *
clone_combined_dict_keys(PyDictObject *orig)
{
assert(PyDict_Check(orig));
assert(_PyAnyDict_Check(orig));
assert(Py_TYPE(orig)->tp_iter == dict_iter);
assert(orig->ma_values == NULL);
assert(orig->ma_keys != Py_EMPTY_KEYS);
Expand Down Expand Up @@ -2374,7 +2374,7 @@ _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
PyDictObject *mp = (PyDictObject *)op;
PyObject *value;

if (!PyDict_Check(op)) {
if (!_PyAnyDict_Check(op)) {
PyErr_BadInternalCall();
return NULL;
}
Expand Down Expand Up @@ -3280,8 +3280,8 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
return NULL;


if (PyDict_CheckExact(d)) {
if (PyDict_CheckExact(iterable)) {
if (_PyAnyDict_CheckExact(d)) {
if (_PyAnyDict_CheckExact(iterable)) {
PyDictObject *mp = (PyDictObject *)d;

Py_BEGIN_CRITICAL_SECTION2(d, iterable);
Expand Down Expand Up @@ -3496,7 +3496,7 @@ dict_subscript(PyObject *self, PyObject *key)
if (ix == DKIX_ERROR)
return NULL;
if (ix == DKIX_EMPTY || value == NULL) {
if (!PyDict_CheckExact(mp)) {
if (!_PyAnyDict_CheckExact(mp)) {
/* Look up __missing__ method if we're a subclass. */
PyObject *missing, *res;
missing = _PyObject_LookupSpecial(
Expand Down Expand Up @@ -3535,7 +3535,7 @@ keys_lock_held(PyObject *dict)
{
ASSERT_DICT_LOCKED(dict);

if (dict == NULL || !PyDict_Check(dict)) {
if (dict == NULL || !_PyAnyDict_Check(dict)) {
PyErr_BadInternalCall();
return NULL;
}
Expand Down Expand Up @@ -3584,7 +3584,7 @@ values_lock_held(PyObject *dict)
{
ASSERT_DICT_LOCKED(dict);

if (dict == NULL || !PyDict_Check(dict)) {
if (dict == NULL || !_PyAnyDict_Check(dict)) {
PyErr_BadInternalCall();
return NULL;
}
Expand Down Expand Up @@ -3632,7 +3632,7 @@ items_lock_held(PyObject *dict)
{
ASSERT_DICT_LOCKED(dict);

if (dict == NULL || !PyDict_Check(dict)) {
if (dict == NULL || !_PyAnyDict_Check(dict)) {
PyErr_BadInternalCall();
return NULL;
}
Expand Down Expand Up @@ -3712,7 +3712,7 @@ dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value)
static int
dict_update_arg(PyObject *self, PyObject *arg)
{
if (PyDict_CheckExact(arg)) {
if (_PyAnyDict_CheckExact(arg)) {
return PyDict_Merge(self, arg, 1);
}
int has_keys = PyObject_HasAttrWithError(arg, &_Py_ID(keys));
Expand Down Expand Up @@ -3982,7 +3982,7 @@ dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override)
}
mp = (PyDictObject*)a;
int res = 0;
if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == dict_iter)) {
if (_PyAnyDict_Check(b) && (Py_TYPE(b)->tp_iter == dict_iter)) {
other = (PyDictObject*)b;
int res;
Py_BEGIN_CRITICAL_SECTION2(a, b);
Expand Down Expand Up @@ -4243,7 +4243,7 @@ PyDict_Copy(PyObject *o)
Py_ssize_t
PyDict_Size(PyObject *mp)
{
if (mp == NULL || !PyDict_Check(mp)) {
if (mp == NULL || !_PyAnyDict_Check(mp)) {
PyErr_BadInternalCall();
return -1;
}
Expand Down Expand Up @@ -5789,7 +5789,7 @@ dictreviter_iter_lock_held(PyDictObject *d, PyObject *self)
{
dictiterobject *di = (dictiterobject *)self;

assert (PyDict_Check(d));
assert (_PyAnyDict_Check(d));
ASSERT_DICT_LOCKED(d);

if (di->di_used != d->ma_used) {
Expand Down Expand Up @@ -5919,7 +5919,7 @@ static PyObject *
dict___reversed___impl(PyDictObject *self)
/*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/
{
assert (PyDict_Check(self));
assert (_PyAnyDict_Check(self));
return dictiter_new(self, &PyDictRevIterKey_Type);
}

Expand Down Expand Up @@ -6194,7 +6194,7 @@ dictviews_to_set(PyObject *self)
if (PyDictKeys_Check(self)) {
// PySet_New() has fast path for the dict object.
PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict;
if (PyDict_CheckExact(dict)) {
if (_PyAnyDict_CheckExact(dict)) {
left = dict;
}
}
Expand Down Expand Up @@ -7714,7 +7714,7 @@ validate_watcher_id(PyInterpreterState *interp, int watcher_id)
int
PyDict_Watch(int watcher_id, PyObject* dict)
{
if (!PyDict_Check(dict)) {
if (!_PyAnyDict_Check(dict)) {
PyErr_SetString(PyExc_ValueError, "Cannot watch non-dictionary");
return -1;
}
Expand All @@ -7729,7 +7729,7 @@ PyDict_Watch(int watcher_id, PyObject* dict)
int
PyDict_Unwatch(int watcher_id, PyObject* dict)
{
if (!PyDict_Check(dict)) {
if (!_PyAnyDict_Check(dict)) {
PyErr_SetString(PyExc_ValueError, "Cannot watch non-dictionary");
return -1;
}
Expand Down
2 changes: 1 addition & 1 deletion Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1424,7 +1424,7 @@ _list_extend(PyListObject *self, PyObject *iterable)
res = list_extend_set(self, (PySetObject *)iterable);
Py_END_CRITICAL_SECTION2();
}
else if (PyDict_CheckExact(iterable)) {
else if (_PyAnyDict_CheckExact(iterable)) {
Py_BEGIN_CRITICAL_SECTION2(self, iterable);
res = list_extend_dict(self, (PyDictObject *)iterable, 0 /*keys*/);
Py_END_CRITICAL_SECTION2();
Expand Down
2 changes: 1 addition & 1 deletion Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ _PyObject_CheckConsistency(PyObject *op, int check_content)
if (PyUnicode_Check(op)) {
_PyUnicode_CheckConsistency(op, check_content);
}
else if (PyDict_Check(op)) {
else if (_PyAnyDict_Check(op)) {
_PyDict_CheckConsistency(op, check_content);
}
return 1;
Expand Down
2 changes: 1 addition & 1 deletion Objects/odictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2272,7 +2272,7 @@ static int
mutablemapping_update_arg(PyObject *self, PyObject *arg)
{
int res = 0;
if (PyDict_CheckExact(arg)) {
if (_PyAnyDict_CheckExact(arg)) {
PyObject *items = PyDict_Items(arg);
if (items == NULL) {
return -1;
Expand Down
14 changes: 7 additions & 7 deletions Objects/setobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ set_iter(PyObject *so)
static int
set_update_dict_lock_held(PySetObject *so, PyObject *other)
{
assert(PyDict_CheckExact(other));
assert(_PyAnyDict_CheckExact(other));

_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
Expand Down Expand Up @@ -1048,7 +1048,7 @@ set_update_lock_held(PySetObject *so, PyObject *other)
if (PyAnySet_Check(other)) {
return set_merge_lock_held(so, other);
}
else if (PyDict_CheckExact(other)) {
else if (_PyAnyDict_CheckExact(other)) {
return set_update_dict_lock_held(so, other);
}
return set_update_iterable_lock_held(so, other);
Expand All @@ -1066,7 +1066,7 @@ set_update_local(PySetObject *so, PyObject *other)
Py_END_CRITICAL_SECTION();
return rv;
}
else if (PyDict_CheckExact(other)) {
else if (_PyAnyDict_CheckExact(other)) {
int rv;
Py_BEGIN_CRITICAL_SECTION(other);
rv = set_update_dict_lock_held(so, other);
Expand All @@ -1089,7 +1089,7 @@ set_update_internal(PySetObject *so, PyObject *other)
Py_END_CRITICAL_SECTION2();
return rv;
}
else if (PyDict_CheckExact(other)) {
else if (_PyAnyDict_CheckExact(other)) {
int rv;
Py_BEGIN_CRITICAL_SECTION2(so, other);
rv = set_update_dict_lock_held(so, other);
Expand Down Expand Up @@ -1781,7 +1781,7 @@ set_difference(PySetObject *so, PyObject *other)
if (PyAnySet_Check(other)) {
other_size = PySet_GET_SIZE(other);
}
else if (PyDict_CheckExact(other)) {
else if (_PyAnyDict_CheckExact(other)) {
other_size = PyDict_GET_SIZE(other);
}
else {
Expand All @@ -1798,7 +1798,7 @@ set_difference(PySetObject *so, PyObject *other)
if (result == NULL)
return NULL;

if (PyDict_CheckExact(other)) {
if (_PyAnyDict_CheckExact(other)) {
while (set_next(so, &pos, &entry)) {
key = entry->key;
hash = entry->hash;
Expand Down Expand Up @@ -1989,7 +1989,7 @@ set_symmetric_difference_update_impl(PySetObject *so, PyObject *other)
}

int rv;
if (PyDict_CheckExact(other)) {
if (_PyAnyDict_CheckExact(other)) {
Py_BEGIN_CRITICAL_SECTION2(so, other);
rv = set_symmetric_difference_update_dict(so, other);
Py_END_CRITICAL_SECTION2();
Expand Down
2 changes: 1 addition & 1 deletion Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -13189,7 +13189,7 @@ unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z)
const void *data;

/* x must be a dict */
if (!PyDict_CheckExact(x)) {
if (!_PyAnyDict_CheckExact(x)) {
PyErr_SetString(PyExc_TypeError, "if you give only one argument "
"to maketrans it must be a dict");
goto err;
Expand Down