[Python-checkins] bpo-28556: Don't simplify unions at runtime (GH-6841)
Ivan Levkivskyi
webhook-mailer at python.org
Fri May 18 19:00:49 EDT 2018
https://github.com/python/cpython/commit/f65e31fee3b55dfb6ed5398179d5c5d6b502dee5
commit: f65e31fee3b55dfb6ed5398179d5c5d6b502dee5
branch: master
author: Ivan Levkivskyi <levkivskyi at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-05-18T16:00:38-07:00
summary:
bpo-28556: Don't simplify unions at runtime (GH-6841)
files:
A Misc/NEWS.d/next/Library/2018-05-17-22-53-08.bpo-28556.C6Hnd1.rst
M Doc/library/typing.rst
M Lib/test/test_typing.py
M Lib/typing.py
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index 142e169b89c4..be6636eea08f 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -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.
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index be768f12fb4c..904cd93691a1 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -253,10 +253,11 @@ def test_union_any(self):
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 @@ def test_single_class_disappears(self):
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 @@ def test_cannot_instantiate(self):
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 @@ def test_extended_generic_rules_eq(self):
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]
diff --git a/Lib/typing.py b/Lib/typing.py
index b10615c07fbd..3e82c6b1bb21 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -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 @@ class Starship:
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
index 000000000000..35e13bde1897
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-05-17-22-53-08.bpo-28556.C6Hnd1.rst
@@ -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.
More information about the Python-checkins
mailing list