From 2b622f2c57f765c3f105bd7a56ca7d0612482b6e Mon Sep 17 00:00:00 2001 From: hongweipeng <961365124@qq.com> Date: Sat, 31 Aug 2019 12:59:45 +0800 Subject: [PATCH 1/4] Improve ForwardRef equality checks --- Lib/typing.py | 6 ++++-- .../next/Library/2019-08-31-12-57-17.bpo-37953.Fz3V7f.rst | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-08-31-12-57-17.bpo-37953.Fz3V7f.rst diff --git a/Lib/typing.py b/Lib/typing.py index 5f1a0ad3d637430..13972f522bbde7f 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -524,8 +524,10 @@ def _evaluate(self, globalns, localns): def __eq__(self, other): if not isinstance(other, ForwardRef): return NotImplemented - return (self.__forward_arg__ == other.__forward_arg__ and - self.__forward_value__ == other.__forward_value__) + if self.__forward_evaluated__ and other.__forward_evaluated__: + return (self.__forward_arg__ == other.__forward_arg__ and + self.__forward_value__ == other.__forward_value__) + return self.__forward_arg__ == other.__forward_arg__ def __hash__(self): return hash((self.__forward_arg__, self.__forward_value__)) diff --git a/Misc/NEWS.d/next/Library/2019-08-31-12-57-17.bpo-37953.Fz3V7f.rst b/Misc/NEWS.d/next/Library/2019-08-31-12-57-17.bpo-37953.Fz3V7f.rst new file mode 100644 index 000000000000000..ae355b620c49b30 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-08-31-12-57-17.bpo-37953.Fz3V7f.rst @@ -0,0 +1 @@ +Improve ForwardRef equality checks. Patch by hongweipeng. From 45c3eb842393dff4c043d8f17f6536716ffa6bf4 Mon Sep 17 00:00:00 2001 From: hongweipeng <961365124@qq.com> Date: Sat, 31 Aug 2019 14:08:11 +0800 Subject: [PATCH 2/4] replace hash with equal in _remove_dups_flatten --- Lib/typing.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index 13972f522bbde7f..7a3cee8d381d50b 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -230,15 +230,11 @@ def _remove_dups_flatten(parameters): else: params.append(p) # Weed out strict duplicates, preserving the first of each occurrence. - all_params = set(params) - if len(all_params) < len(params): - new_params = [] - for t in params: - if t in all_params: - new_params.append(t) - all_params.remove(t) - params = new_params - assert not all_params, all_params + new_params = [] + for p in params: + if p not in new_params: + new_params.append(p) + params = new_params return tuple(params) From ca1b8e35a035dbe6e3f8b2affd20c53cabdb3560 Mon Sep 17 00:00:00 2001 From: HongWeipeng Date: Sun, 1 Sep 2019 02:35:00 +0800 Subject: [PATCH 3/4] add some tests --- Lib/test/test_typing.py | 56 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index ba0800fae90e301..d415559ce7c8197 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2353,6 +2353,62 @@ def test_forward_equality(self): self.assertEqual(fr, typing.ForwardRef('int')) self.assertNotEqual(List['int'], List[int]) + def test_forward_equality_gth(self): + c1 = typing.ForwardRef('C') + c1_gth = typing.ForwardRef('C') + c2 = typing.ForwardRef('C') + c2_gth = typing.ForwardRef('C') + + class C: + pass + def foo(a: c1_gth, b: c2_gth): + pass + + self.assertEqual(get_type_hints(foo, globals(), locals()), {'a': C, 'b': C}) + self.assertEqual(c1, c2) + self.assertEqual(c1, c1_gth) + self.assertEqual(c1_gth, c2_gth) + self.assertEqual(Union[c1], Union[c1_gth]) + self.assertEqual(Union[c1, c1_gth, c2, c2_gth], Union[c1]) + + def test_forward_equality_hash(self): + c1 = typing.ForwardRef('int') + c1_gth = typing.ForwardRef('int') + c2 = typing.ForwardRef('int') + c2_gth = typing.ForwardRef('int') + + def foo(a: c1_gth, b: c2_gth): + pass + get_type_hints(foo, globals(), locals()) + + self.assertEqual(hash(c1), hash(c2)) + self.assertEqual(hash(c1_gth), hash(c2_gth)) + self.assertNotEqual(hash(c1), hash(c1_gth)) + + def test_forward_equality_namespace(self): + class A: + pass + def namespace1(): + a = typing.ForwardRef('A') + def fun(x: a): + pass + get_type_hints(fun, globals(), locals()) + return a + + def namespace2(): + a = typing.ForwardRef('A') + + class A: + pass + def fun(x: a): + pass + + get_type_hints(fun, globals(), locals()) + return a + + self.assertEqual(namespace1(), namespace1()) + self.assertNotEqual(namespace1(), namespace2()) + def test_forward_repr(self): self.assertEqual(repr(List['int']), "typing.List[ForwardRef('int')]") From 3676a50e7cfc364d5b8b77a85901696ceb393e14 Mon Sep 17 00:00:00 2001 From: HongWeipeng Date: Sun, 1 Sep 2019 10:05:49 +0800 Subject: [PATCH 4/4] code review --- Lib/test/test_typing.py | 5 +++-- Lib/typing.py | 7 +------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index d415559ce7c8197..22a668f5b7a2b95 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2368,8 +2368,9 @@ def foo(a: c1_gth, b: c2_gth): self.assertEqual(c1, c2) self.assertEqual(c1, c1_gth) self.assertEqual(c1_gth, c2_gth) - self.assertEqual(Union[c1], Union[c1_gth]) - self.assertEqual(Union[c1, c1_gth, c2, c2_gth], Union[c1]) + self.assertEqual(List[c1], List[c1_gth]) + self.assertNotEqual(List[c1], List[C]) + self.assertNotEqual(List[c1_gth], List[C]) def test_forward_equality_hash(self): c1 = typing.ForwardRef('int') diff --git a/Lib/typing.py b/Lib/typing.py index 7a3cee8d381d50b..686b386875ec368 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -230,12 +230,7 @@ def _remove_dups_flatten(parameters): else: params.append(p) # Weed out strict duplicates, preserving the first of each occurrence. - new_params = [] - for p in params: - if p not in new_params: - new_params.append(p) - params = new_params - return tuple(params) + return tuple(dict.fromkeys(params)) _cleanups = []