Skip to content

PyDict_Merge and PyDict_Update have conflicting docs: both require .keys to exist #160669

Open

Description

@sobolevn

While working on msgspec we noticed that PyDict_Merge does not follow the documented way of merging objects: msgspec/msgspec#1309 (comment)

The docs (https://docs.python.org/3/c-api/dict.html#c.PyDict_Update) of PyDict_Update say:

This is the same as PyDict_Merge(a, b, 1) in C, and is similar to a.update(b) in Python except that PyDict_Update() doesn’t fall back to the iterating over a sequence of key value pairs if the second argument has no “keys” attribute.

So, this confuses me:

  • PyDict_Merge says:

Iterate over mapping object b adding key-value pairs to dictionary a. b may be a dictionary, or any object supporting PyMapping_Keys() and PyObject_GetItem().

  • PyDict_Update says:

PyDict_Update() doesn’t fall back to the iterating over a sequence of key value pairs if the second argument has no “keys” attribute.

So, I have 2 questions:

  • Is this a PyDict_Update doc issue?
  • Should PyDict_Merge require .keys attribute? Or can it work with an iterable of key-value pairs?

Repro

I can re-create this problem in CAPI tests:

diff --git Lib/test/test_capi/test_dict.py Lib/test/test_capi/test_dict.py
index 4cf404e9f56..6323a3ea28f 100644
--- Lib/test/test_capi/test_dict.py
+++ Lib/test/test_capi/test_dict.py
@@ -460,6 +460,11 @@ def test_dict_update(self):
     def test_dict_merge(self):
         # Test PyDict_Merge()
         merge = _testlimitedcapi.dict_merge
+
+        dct = {'a': 1, 'b': 2}
+        merge(dct, [('b', 3), ('c', 4)], 0)
+        self.assertEqual(dct, {'a': 1, 'b': 2, 'c': 4})
+
         for cls1 in DICT_TYPES:
             for cls2 in ANYDICT_TYPES + MAPPING_TYPES:
                 dct = cls1({'a': 1, 'b': 2})   
» ./python.exe -m test test_capi.test_dict
Using random seed: 1975933612
0:00:00 load avg: 1.91 mem:   27.6 MiB Run 1 test sequentially in a single process
0:00:00 load avg: 1.91 mem:   27.6 MiB [1/1] test_capi.test_dict
test test_capi.test_dict failed -- Traceback (most recent call last):
  File "/Users/sobolev/Desktop/cpython/Lib/test/test_capi/test_dict.py", line 465, in test_dict_merge
    merge(dct, [('b', 3), ('c', 4)], 0)
    ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'keys'

Linked PRs

Activity

  1. added
    docsDocumentation in the Doc dir
    on Oct 25, 2026
  2. commit-msuyog commented on Oct 26, 2026

    @commit-msuyog

    Hi @sobolevn! Is this issue still free? I'd like to work on it.

    It looks like a docs clarification in Doc/c-api/dict.rst. PyDict_Merge needs a keys() method and does not accept a sequence of pairs, so the docs should point to PyDict_MergeFromSeq2 for that case.

    Could you confirm that direction? Thanks!

  3. sobolevn commented on Oct 26, 2026

    @sobolevn
    MemberAuthor

    @commit-msuyog nope, please, wait for the consensus.

  4. encukou commented on Nov 4, 2026

    @encukou
    Member

    As the docs say, PyDict_Update “is the same as PyDict_Merge(a, b, 1) in C”. IMO, the sentence (or ideally the paragraph) should end there.

    Compared to a.update(b) in Python, neither PyDict_Merge nor PyDict_Update falls back to iterating key/value pairs.

  5. self-assigned this
    on Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions