gh-142495: make defaultdict keep existed value when racing with __missing__#142668
gh-142495: make defaultdict keep existed value when racing with __missing__#142668serhiy-storchaka merged 15 commits intopython:mainfrom
defaultdict keep existed value when racing with __missing__#142668Conversation
b57dd36 to
57064dc
Compare
serhiy-storchaka
left a comment
There was a problem hiding this comment.
Does it need threads to reproduce? Would not the following code be enough to reproduce the issue?
count = 0
def default_factory():
nonlocal count
count += 1
if count == 1:
test_dict[key]
return count
Hi @serhiy-storchaka , Thanks very much for your suggestion! ❤ Yes, this case could be designed simply without threads. I have updated the test case as suggested. Best Regards, |
serhiy-storchaka
left a comment
There was a problem hiding this comment.
Great! But similar tests usually use a local function with a nonlocal variable.
Hi @serhiy-storchaka , Thanks very much for your suggestion! 😊 Best Regards, |
| } | ||
| return value; | ||
| PyObject *result = NULL; | ||
| (void)PyDict_SetDefaultRef(op, key, value, &result); |
There was a problem hiding this comment.
If we don't need the return value, could we use PyDict_SetDefault?
There was a problem hiding this comment.
PyDict_SetDefault returns a borrowed reference and I find this easier to read (at least the result is know to be a strong reference and we can directly return it).
serhiy-storchaka
left a comment
There was a problem hiding this comment.
Does the new test reproduce the issue? It seems that __missing__() is not called recursively in your current code, unlike to the code in my suggestion.
Hi @serhiy-storchaka , Thanks very much for your correction!❤ Sorry for misunderstanding the test case comment. Please correct me if I misunderstand. Best Regards, |
Lib/test/test_defaultdict.py
Outdated
| def test_factory_conflict_with_set_value(self): | ||
| key = "conflict_test" | ||
| count = 0 | ||
| test_dict = None |
There was a problem hiding this comment.
Is this needed? test_dict is defined at line 203.
There was a problem hiding this comment.
Hi @serhiy-storchaka ,
Thanks for your correction! 😊
I have removed the redundant declaration of test_dict.
Best Regards,
Edward
Lib/test/test_defaultdict.py
Outdated
|
|
||
| def default_factory(): | ||
| nonlocal count | ||
| nonlocal test_dict |
There was a problem hiding this comment.
This is not needed, as the variable is not modified in the function (the variable is bound to the same object, even if its value is changed).
There was a problem hiding this comment.
serhiy-storchaka
left a comment
There was a problem hiding this comment.
LGTM. 👍
Thank you for your contribution. In the future, try to use chatbots less (could you use a simple online translator instead?). They create an unpleasant, creepy impression.
|
Thanks @LindaSummer for the PR, and @serhiy-storchaka for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13. |
|
Thanks @LindaSummer for the PR, and @serhiy-storchaka for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14. |
|
Sorry, @LindaSummer and @serhiy-storchaka, I could not cleanly backport this to |
…th `__missing__` (pythonGH-142668) (cherry picked from commit a043407) Co-authored-by: Edward Xu <xuxiangad@gmail.com>
|
GH-142832 is a backport of this pull request to the 3.14 branch. |
Got it. Thanks for your review and help! |
|
…cing with `__missing__` (pythonGH-142668) (cherry picked from commit a043407) Co-authored-by: Edward Xu <xuxiangad@gmail.com>
|
GH-142858 is a backport of this pull request to the 3.13 branch. |
Issue
#142495
Proposed Changes
PyDict_SetDefaultRefto guard the object update and keep existed value.Comment
The
PyDict_SetDefaultRefwould increase the reference count of the setted item, so we must decrement the refrence count on__missing__created object to avoid memory leak.