summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorShantanu <12621235+hauntsaninja@users.noreply.github.com>2022-04-05 02:35:29 (GMT)
committerGitHub <noreply@github.com>2022-04-05 02:35:29 (GMT)
commit5a4973e29f2f5c4ee8c086f40325786c62381540 (patch)
tree299614c1035acc20242c9e73156b70b40c3cc200 /Lib
parentbb86d1d9fbd1888524e04475383f4ea764277f67 (diff)
downloadcpython-5a4973e29f2f5c4ee8c086f40325786c62381540.zip
cpython-5a4973e29f2f5c4ee8c086f40325786c62381540.tar.gz
cpython-5a4973e29f2f5c4ee8c086f40325786c62381540.tar.bz2
bpo-46998: Allow subclassing Any at runtime (GH-31841)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_functools.py8
-rw-r--r--Lib/test/test_pydoc.py12
-rw-r--r--Lib/test/test_typing.py28
-rw-r--r--Lib/typing.py21
4 files changed, 38 insertions, 31 deletions
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index abbd50a..82e73f4 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -2802,8 +2802,6 @@ class TestSingleDispatch(unittest.TestCase):
f.register(list[int] | str, lambda arg: "types.UnionTypes(types.GenericAlias)")
with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
f.register(typing.List[float] | bytes, lambda arg: "typing.Union[typing.GenericAlias]")
- with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
- f.register(typing.Any, lambda arg: "typing.Any")
self.assertEqual(f([1]), "default")
self.assertEqual(f([1.0]), "default")
@@ -2823,8 +2821,6 @@ class TestSingleDispatch(unittest.TestCase):
f.register(list[int] | str)
with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
f.register(typing.List[int] | str)
- with self.assertRaisesRegex(TypeError, "Invalid first argument to "):
- f.register(typing.Any)
def test_register_genericalias_annotation(self):
@functools.singledispatch
@@ -2847,10 +2843,6 @@ class TestSingleDispatch(unittest.TestCase):
@f.register
def _(arg: typing.List[float] | bytes):
return "typing.Union[typing.GenericAlias]"
- with self.assertRaisesRegex(TypeError, "Invalid annotation for 'arg'"):
- @f.register
- def _(arg: typing.Any):
- return "typing.Any"
self.assertEqual(f([1]), "default")
self.assertEqual(f([1.0]), "default")
diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py
index 9c900c3..13c77b6 100644
--- a/Lib/test/test_pydoc.py
+++ b/Lib/test/test_pydoc.py
@@ -1066,14 +1066,14 @@ class TestDescriptions(unittest.TestCase):
self.assertIn(types.UnionType.__doc__.strip().splitlines()[0], doc)
def test_special_form(self):
- self.assertEqual(pydoc.describe(typing.Any), '_SpecialForm')
- doc = pydoc.render_doc(typing.Any, renderer=pydoc.plaintext)
+ self.assertEqual(pydoc.describe(typing.NoReturn), '_SpecialForm')
+ doc = pydoc.render_doc(typing.NoReturn, renderer=pydoc.plaintext)
self.assertIn('_SpecialForm in module typing', doc)
- if typing.Any.__doc__:
- self.assertIn('Any = typing.Any', doc)
- self.assertIn(typing.Any.__doc__.strip().splitlines()[0], doc)
+ if typing.NoReturn.__doc__:
+ self.assertIn('NoReturn = typing.NoReturn', doc)
+ self.assertIn(typing.NoReturn.__doc__.strip().splitlines()[0], doc)
else:
- self.assertIn('Any = class _SpecialForm(_Final)', doc)
+ self.assertIn('NoReturn = class _SpecialForm(_Final)', doc)
def test_typing_pydoc(self):
def foo(data: typing.List[typing.Any],
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 0e28655..041b6ad 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -89,12 +89,6 @@ class AnyTests(BaseTestCase):
with self.assertRaises(TypeError):
isinstance(42, Any)
- def test_any_subclass_type_error(self):
- with self.assertRaises(TypeError):
- issubclass(Employee, Any)
- with self.assertRaises(TypeError):
- issubclass(Any, Employee)
-
def test_repr(self):
self.assertEqual(repr(Any), 'typing.Any')
@@ -104,13 +98,21 @@ class AnyTests(BaseTestCase):
with self.assertRaises(TypeError):
Any[int] # Any is not a generic type.
- def test_cannot_subclass(self):
- with self.assertRaises(TypeError):
- class A(Any):
- pass
- with self.assertRaises(TypeError):
- class A(type(Any)):
- pass
+ def test_can_subclass(self):
+ class Mock(Any): pass
+ self.assertTrue(issubclass(Mock, Any))
+ self.assertIsInstance(Mock(), Mock)
+
+ class Something: pass
+ self.assertFalse(issubclass(Something, Any))
+ self.assertNotIsInstance(Something(), Mock)
+
+ class MockSomething(Something, Mock): pass
+ self.assertTrue(issubclass(MockSomething, Any))
+ ms = MockSomething()
+ self.assertIsInstance(ms, MockSomething)
+ self.assertIsInstance(ms, Something)
+ self.assertIsInstance(ms, Mock)
def test_cannot_instantiate(self):
with self.assertRaises(TypeError):
diff --git a/Lib/typing.py b/Lib/typing.py
index 36f9ece..4636798 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -429,8 +429,17 @@ class _LiteralSpecialForm(_SpecialForm, _root=True):
return self._getitem(self, *parameters)
-@_SpecialForm
-def Any(self, parameters):
+class _AnyMeta(type):
+ def __instancecheck__(self, obj):
+ if self is Any:
+ raise TypeError("typing.Any cannot be used with isinstance()")
+ return super().__instancecheck__(obj)
+
+ def __repr__(self):
+ return "typing.Any"
+
+
+class Any(metaclass=_AnyMeta):
"""Special type indicating an unconstrained type.
- Any is compatible with every type.
@@ -439,9 +448,13 @@ def Any(self, parameters):
Note that all the above statements are true from the point of view of
static type checkers. At runtime, Any should not be used with instance
- or class checks.
+ checks.
"""
- raise TypeError(f"{self} is not subscriptable")
+ def __new__(cls, *args, **kwargs):
+ if cls is Any:
+ raise TypeError("Any cannot be instantiated")
+ return super().__new__(cls, *args, **kwargs)
+
@_SpecialForm
def NoReturn(self, parameters):