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'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions