From 24613f5c156390ae205c3a0a740abbc8e370b6a5 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 17 Aug 2023 14:25:09 -0500 Subject: [PATCH 1/7] Add test that succeeds for the C version but fails for the pure Python version. --- Lib/test/test_ordered_dict.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index decbcc2419c9fc..be3a5874d281a9 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -122,6 +122,16 @@ def items(self): self.OrderedDict(Spam()) self.assertEqual(calls, ['keys']) + def test_overridden_init(self): + # Sync-up pure Python OD class with C class where + # a consistent internal state is created in __new__ + # rather than __init__. + class ODNI: + def __init__(*args, **kwargs): + pass + od = ODNI() + od['a'] = 1 # This used to fail because __init__ was bypassed + def test_fromkeys(self): OrderedDict = self.OrderedDict od = OrderedDict.fromkeys('abc') From 00ef45b399de436e97a00e9b6fb9e80c9e946a65 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 17 Aug 2023 14:32:28 -0500 Subject: [PATCH 2/7] Fix the test to inherit from OrderedDict --- Lib/test/test_ordered_dict.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index be3a5874d281a9..4571b23dfe7c1a 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -126,7 +126,8 @@ def test_overridden_init(self): # Sync-up pure Python OD class with C class where # a consistent internal state is created in __new__ # rather than __init__. - class ODNI: + OrderedDict = self.OrderedDict + class ODNI(OrderedDict): def __init__(*args, **kwargs): pass od = ODNI() From cfe1af2bd8a5579ca2fb895e513f4548e3274593 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 17 Aug 2023 14:33:43 -0500 Subject: [PATCH 3/7] Sync pure python version with C version --- Lib/collections/__init__.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 03ca2d7e18f6f0..b23468e7147c41 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -95,10 +95,9 @@ class OrderedDict(dict): # Individual links are kept alive by the hard reference in self.__map. # Those hard references disappear when a key is deleted from an OrderedDict. - def __init__(self, other=(), /, **kwds): - '''Initialize an ordered dictionary. The signature is the same as - regular dictionaries. Keyword argument order is preserved. - ''' + def __new__(cls, /, *args, **kwds): + "Create the ordered dict object and set up the underlying structures." + self = dict.__new__(cls) try: self.__root except AttributeError: @@ -106,6 +105,12 @@ def __init__(self, other=(), /, **kwds): self.__root = root = _proxy(self.__hardroot) root.prev = root.next = root self.__map = {} + return self + + def __init__(self, other=(), /, **kwds): + '''Initialize an ordered dictionary. The signature is the same as + regular dictionaries. Keyword argument order is preserved. + ''' self.__update(other, **kwds) def __setitem__(self, key, value, From f20aa9dcb80dfb121cd439829169e4fba4dd0dfc Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 17 Aug 2023 14:45:37 -0500 Subject: [PATCH 4/7] Add blurb --- .../Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst diff --git a/Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst b/Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst new file mode 100644 index 00000000000000..f051317d141ff5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-17-14-45-25.gh-issue-105736.NJsH7r.rst @@ -0,0 +1,3 @@ +Harmonized the pure Python version of OrderedDict with the C version. Now, +both versions set up their internal state in `__new__`. Formerly, the pure +Python version did the set up in `__init__`. From 014428b38395ec25bef8b8331bc7beb28ee3d09b Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 17 Aug 2023 14:50:35 -0500 Subject: [PATCH 5/7] Since __new__ can only be called once, the try/except is not needed. --- Lib/collections/__init__.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index b23468e7147c41..8652dc8a4ec450 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -98,13 +98,10 @@ class OrderedDict(dict): def __new__(cls, /, *args, **kwds): "Create the ordered dict object and set up the underlying structures." self = dict.__new__(cls) - try: - self.__root - except AttributeError: - self.__hardroot = _Link() - self.__root = root = _proxy(self.__hardroot) - root.prev = root.next = root - self.__map = {} + self.__hardroot = _Link() + self.__root = root = _proxy(self.__hardroot) + root.prev = root.next = root + self.__map = {} return self def __init__(self, other=(), /, **kwds): From 4758750918b8c1aafba0bd68e95765c4fb3223db Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 17 Aug 2023 15:02:28 -0500 Subject: [PATCH 6/7] Remove unused import. --- Lib/collections/__init__.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 8652dc8a4ec450..5ad29e85cfade5 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -45,11 +45,6 @@ else: _collections_abc.MutableSequence.register(deque) -try: - from _collections import _deque_iterator -except ImportError: - pass - try: from _collections import defaultdict except ImportError: From d3179bc2970297e804e2e22788b0ac4846085c63 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 17 Aug 2023 15:31:04 -0500 Subject: [PATCH 7/7] Revert "Remove unused import." This reverts commit 4758750918b8c1aafba0bd68e95765c4fb3223db. --- Lib/collections/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 5ad29e85cfade5..8652dc8a4ec450 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -45,6 +45,11 @@ else: _collections_abc.MutableSequence.register(deque) +try: + from _collections import _deque_iterator +except ImportError: + pass + try: from _collections import defaultdict except ImportError: