From 4ed7ad399217da16ba04ac5092b74950a8317b90 Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Thu, 15 Feb 2024 09:48:13 +0100 Subject: [PATCH] Add support to use the pipe (`|`) syntax for `typing.Union`. (#285) Fixes #280. --- CHANGES.rst | 4 +++ src/zope/interface/interface.py | 9 ++++++ src/zope/interface/tests/test_interface.py | 34 ++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 90e1309a..2e2772b7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,10 @@ - Add preliminary support for Python 3.13 as of 3.13a3. +- Add support to use the pipe (``|``) syntax for ``typing.Union``. + (`#280 `_) + + 6.1 (2023-10-05) ================ diff --git a/src/zope/interface/interface.py b/src/zope/interface/interface.py index 1bd6f9e8..02a512e3 100644 --- a/src/zope/interface/interface.py +++ b/src/zope/interface/interface.py @@ -18,6 +18,7 @@ from types import MethodType from types import FunctionType import weakref +from typing import Union from zope.interface._compat import _use_c_impl from zope.interface.exceptions import Invalid @@ -941,6 +942,14 @@ def _call_conform(self, conform): def __reduce__(self): return self.__name__ + def __or__(self, other): + """Allow type hinting syntax: Interface | None.""" + return Union[self, other] + + def __ror__(self, other): + """Allow type hinting syntax: None | Interface.""" + return Union[other, self] + Interface = InterfaceClass("Interface", __module__='zope.interface') # Interface is the only member of its own SRO. Interface._calculate_sro = lambda: (Interface,) diff --git a/src/zope/interface/tests/test_interface.py b/src/zope/interface/tests/test_interface.py index b3a440e6..096bdcf4 100644 --- a/src/zope/interface/tests/test_interface.py +++ b/src/zope/interface/tests/test_interface.py @@ -2636,3 +2636,37 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): for key, value in self.to_restore.items(): setattr(self.module, key, value) + +class TestTypeAnnotations(unittest.TestCase): + """Test using Interfaces in type annotations.""" + + def test___or__(self): + from zope.interface import Interface + from typing import Optional, Union + class I1(Interface): + pass + class I2(Interface): + pass + + class B: + a: I1|None + b: I1|I2 + + self.assertEqual( + B.__annotations__, {'a': Optional[I1], 'b': Union[I1, I2]}) + + def test___ror__(self): + from zope.interface import Interface + from typing import Optional, Union + class I1(Interface): + pass + + class A: + pass + + class B: + a: None|I1 + b: A|I1 + + self.assertEqual( + B.__annotations__, {'a': Optional[I1], 'b': Union[A, I1]})