Skip to content

Commit

Permalink
Implement typing_extensions.Any (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gobot1234 authored Aug 30, 2022
1 parent 9683c1a commit 7d1aeea
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Release 4.4.0 ()
- Add `typing_extensions.Any` a backport of python 3.11's Any class which is
subclassable at runtime. (backport from python/cpython#31841, by Shantanu
and Jelle Zijlstra). Patch by James Hilton-Balfe (@Gobot1234).

# Release 4.3.0 (July 1, 2022)

- Add `typing_extensions.NamedTuple`, allowing for generic `NamedTuple`s on
Expand Down
48 changes: 45 additions & 3 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
from unittest.mock import patch
from test import ann_module, ann_module2, ann_module3
import typing
from typing import TypeVar, Optional, Union, Any, AnyStr
from typing import TypeVar, Optional, Union, AnyStr
from typing import T, KT, VT # Not in __all__.
from typing import Tuple, List, Dict, Iterable, Iterator, Callable
from typing import Generic
from typing import no_type_check
import typing_extensions
from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict, Self
from typing_extensions import NoReturn, Any, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict, Self
from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard
from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager, Required, NotRequired
from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, final, is_typeddict
Expand Down Expand Up @@ -160,6 +160,48 @@ def test_exception(self):
assert_never(None)


class AnyTests(BaseTestCase):
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)

class SubclassesAny(Any):
...

def test_repr(self):
if sys.version_info >= (3, 11):
mod_name = 'typing'
else:
mod_name = 'typing_extensions'
self.assertEqual(repr(Any), f"{mod_name}.Any")
if sys.version_info < (3, 11): # skip for now on 3.11+ see python/cpython#95987
self.assertEqual(repr(self.SubclassesAny), "<class 'test_typing_extensions.AnyTests.SubclassesAny'>")

def test_instantiation(self):
with self.assertRaises(TypeError):
Any()

self.SubclassesAny()

def test_isinstance(self):
with self.assertRaises(TypeError):
isinstance(object(), Any)

isinstance(object(), self.SubclassesAny)


class ClassVarTests(BaseTestCase):

def test_basics(self):
Expand Down Expand Up @@ -3018,7 +3060,7 @@ def test_typing_extensions_defers_when_possible(self):
if sys.version_info < (3, 10):
exclude |= {'get_args', 'get_origin'}
if sys.version_info < (3, 11):
exclude |= {'final', 'NamedTuple'}
exclude |= {'final', 'NamedTuple', 'Any'}
for item in typing_extensions.__all__:
if item not in exclude and hasattr(typing, item):
self.assertIs(
Expand Down
32 changes: 32 additions & 0 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# Please keep __all__ alphabetized within each category.
__all__ = [
# Super-special typing primitives.
'Any',
'ClassVar',
'Concatenate',
'Final',
Expand Down Expand Up @@ -149,6 +150,37 @@ def _collect_type_vars(types, typevar_types=None):
T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers.
T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant.


if sys.version_info >= (3, 11):
from typing import Any
else:

class _AnyMeta(type):
def __instancecheck__(self, obj):
if self is Any:
raise TypeError("typing_extensions.Any cannot be used with isinstance()")
return super().__instancecheck__(obj)

def __repr__(self):
if self is Any:
return "typing_extensions.Any"
return super().__repr__()

class Any(metaclass=_AnyMeta):
"""Special type indicating an unconstrained type.
- Any is compatible with every type.
- Any assumed to have all methods.
- All values assumed to be instances of Any.
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
checks.
"""
def __new__(cls, *args, **kwargs):
if cls is Any:
raise TypeError("Any cannot be instantiated")
return super().__new__(cls, *args, **kwargs)


ClassVar = typing.ClassVar

# On older versions of typing there is an internal class named "Final".
Expand Down

0 comments on commit 7d1aeea

Please sign in to comment.