]> granicus.if.org Git - python/commitdiff
bpo-28556: Don't simplify unions at runtime (GH-6841) (GH-6979)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 18 May 2018 23:27:14 +0000 (16:27 -0700)
committerIvan Levkivskyi <levkivskyi@gmail.com>
Fri, 18 May 2018 23:27:14 +0000 (16:27 -0700)
(cherry picked from commit f65e31fee3b55dfb6ed5398179d5c5d6b502dee5)

Co-authored-by: Ivan Levkivskyi <levkivskyi@gmail.com>
Doc/library/typing.rst
Lib/test/test_typing.py
Lib/typing.py
Misc/NEWS.d/next/Library/2018-05-17-22-53-08.bpo-28556.C6Hnd1.rst [new file with mode: 0644]

index 142e169b89c415b02273c690c24b316df1760903..be6636eea08fbb8f13176eb3999920b6549d44d7 100644 (file)
@@ -961,16 +961,15 @@ The module defines the following classes, functions and decorators:
 
        Union[int, str] == Union[str, int]
 
-   * When a class and its subclass are present, the latter is skipped, e.g.::
-
-       Union[int, object] == object
-
    * You cannot subclass or instantiate a union.
 
    * You cannot write ``Union[X][Y]``.
 
    * You can use ``Optional[X]`` as a shorthand for ``Union[X, None]``.
 
+   .. versionchanged:: 3.7
+      Don't remove explicit subclasses from unions at runtime.
+
 .. data:: Optional
 
    Optional type.
index be768f12fb4c6be7bdfcb641ccb3d01f83180fbc..904cd93691a1bf951048910e674e045e6aa16736 100644 (file)
@@ -253,10 +253,11 @@ class UnionTests(BaseTestCase):
     def test_union_object(self):
         u = Union[object]
         self.assertEqual(u, object)
-        u = Union[int, object]
-        self.assertEqual(u, object)
-        u = Union[object, int]
-        self.assertEqual(u, object)
+        u1 = Union[int, object]
+        u2 = Union[object, int]
+        self.assertEqual(u1, u2)
+        self.assertNotEqual(u1, object)
+        self.assertNotEqual(u2, object)
 
     def test_unordered(self):
         u1 = Union[int, float]
@@ -267,13 +268,11 @@ class UnionTests(BaseTestCase):
         t = Union[Employee]
         self.assertIs(t, Employee)
 
-    def test_base_class_disappears(self):
-        u = Union[Employee, Manager, int]
-        self.assertEqual(u, Union[int, Employee])
-        u = Union[Manager, int, Employee]
-        self.assertEqual(u, Union[int, Employee])
+    def test_base_class_kept(self):
         u = Union[Employee, Manager]
-        self.assertIs(u, Employee)
+        self.assertNotEqual(u, Employee)
+        self.assertIn(Employee, u.__args__)
+        self.assertIn(Manager, u.__args__)
 
     def test_union_union(self):
         u = Union[int, float]
@@ -317,7 +316,8 @@ class UnionTests(BaseTestCase):
     def test_union_generalization(self):
         self.assertFalse(Union[str, typing.Iterable[int]] == str)
         self.assertFalse(Union[str, typing.Iterable[int]] == typing.Iterable[int])
-        self.assertTrue(Union[str, typing.Iterable] == typing.Iterable)
+        self.assertIn(str, Union[str, typing.Iterable[int]].__args__)
+        self.assertIn(typing.Iterable[int], Union[str, typing.Iterable[int]].__args__)
 
     def test_union_compare_other(self):
         self.assertNotEqual(Union, object)
@@ -917,7 +917,7 @@ class GenericTests(BaseTestCase):
         self.assertEqual(Union[T, U][int, Union[int, str]], Union[int, str])
         class Base: ...
         class Derived(Base): ...
-        self.assertEqual(Union[T, Base][Derived], Base)
+        self.assertEqual(Union[T, Base][Union[Base, Derived]], Union[Base, Derived])
         with self.assertRaises(TypeError):
             Union[T, int][1]
 
index b10615c07fbdf93bed3f59577ee2a25110899f5c..3e82c6b1bb210e3a8efa51330ff78a8fcf9f7db8 100644 (file)
@@ -206,8 +206,8 @@ def _check_generic(cls, parameters):
 
 
 def _remove_dups_flatten(parameters):
-    """An internal helper for Union creation and substitution: flatten Union's
-    among parameters, then remove duplicates and strict subclasses.
+    """An internal helper for Union creation and substitution: flatten Unions
+    among parameters, then remove duplicates.
     """
     # Flatten out Union[Union[...], ...].
     params = []
@@ -228,20 +228,7 @@ def _remove_dups_flatten(parameters):
                 all_params.remove(t)
         params = new_params
         assert not all_params, all_params
-    # Weed out subclasses.
-    # E.g. Union[int, Employee, Manager] == Union[int, Employee].
-    # If object is present it will be sole survivor among proper classes.
-    # Never discard type variables.
-    # (In particular, Union[str, AnyStr] != AnyStr.)
-    all_params = set(params)
-    for t1 in params:
-        if not isinstance(t1, type):
-            continue
-        if any((isinstance(t2, type) or
-                isinstance(t2, _GenericAlias) and t2._special) and issubclass(t1, t2)
-               for t2 in all_params - {t1}):
-            all_params.remove(t1)
-    return tuple(t for t in params if t in all_params)
+    return tuple(params)
 
 
 _cleanups = []
@@ -440,19 +427,6 @@ Union = _SpecialForm('Union', doc=
 
         Union[int, str] == Union[str, int]
 
-    - When two arguments have a subclass relationship, the least
-      derived argument is kept, e.g.::
-
-        class Employee: pass
-        class Manager(Employee): pass
-        Union[int, Employee, Manager] == Union[int, Employee]
-        Union[Manager, int, Employee] == Union[int, Employee]
-        Union[Employee, Manager] == Employee
-
-    - Similar for object::
-
-        Union[int, object] == object
-
     - You cannot subclass or instantiate a union.
     - You can use Optional[X] as a shorthand for Union[X, None].
     """)
diff --git a/Misc/NEWS.d/next/Library/2018-05-17-22-53-08.bpo-28556.C6Hnd1.rst b/Misc/NEWS.d/next/Library/2018-05-17-22-53-08.bpo-28556.C6Hnd1.rst
new file mode 100644 (file)
index 0000000..35e13bd
--- /dev/null
@@ -0,0 +1,3 @@
+Do not simplify arguments to `typing.Union`. Now `Union[Manager, Employee]`
+is not simplified to `Employee` at runtime. Such simplification previously
+caused several bugs and limited possibilities for introspection.