8000 Support PEP 612 in typing_extensions (Python 3) (#774) · python/typing@c23141f · GitHub
[go: up one dir, main page]

Skip to content

Commit c23141f

Browse files
Support PEP 612 in typing_extensions (Python 3) (#774)
1 parent dacb6b0 commit c23141f

File tree

3 files changed

+362
-1
lines changed

3 files changed

+362
-1
lines changed

typing_extensions/README.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ All Python versions:
5050
- ``Text``
5151
- ``Type``
5252
- ``TypedDict``
53+
- ``TypeAlias``
5354
- ``TYPE_CHECKING``
5455

5556
Python 3.4+ only:
@@ -82,6 +83,12 @@ subclass ``typing.Reversible`` as of Python 3.5.3.
8283
These changes are _not_ backported to prevent subtle compatibility
8384
issues when mixing the differing implementations of modified classes.
8485

86+
Certain types have incorrect runtime behavior due to limitations of older
87+
versions of the typing module. For example, ``ParamSpec`` and ``Concatenate``
88+
will not work with ``get_args``, ``get_origin`` or user-defined ``Generic``\ s
89+
because they need to be lists to work with older versions of ``Callable``.
90+
These types are only guaranteed to work for static type checking.
91+
8592
Running tests
8693
=============
8794

typing_extensions/src_py3/test_typing_extensions.py

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
from typing import Generic
1414
from typing import no_type_check
1515
from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict
16-
from typing_extensions import TypeAlias
16+
from typing_extensions import TypeAlias, ParamSpec, Concatenate
17+
1718
try:
1819
from typing_extensions import Protocol, runtime, runtime_checkable
1920
except ImportError:
@@ -1898,6 +1899,118 @@ def test_cannot_subscript(self):
18981899
with self.assertRaises(TypeError):
18991900
TypeAlias[int]
19001901

1902+
class ParamSpecTests(BaseTestCase):
1903+
1904+
def test_basic_plain(self):
1905+
P = ParamSpec('P')
1906+
self.assertEqual(P, P)
1907+
self.assertIsInstance(P, ParamSpec)
1908+
# Should be hashable
1909+
hash(P)
1910+
1911+
def test_repr(self):
1912+
P = ParamSpec('P')
1913+
P_co = ParamSpec('P_co', covariant=True)
1914+
P_contra = ParamSpec('P_contra', contravariant=True)
1915+
P_2 = ParamSpec('P_2')
1916+
self.assertEqual(repr(P), '~P')
1917+
self.assertEqual(repr(P_2), '~P_2')
1918+
1919+
# Note: PEP 612 doesn't require these to be repr-ed correctly, but
1920+
# just follow CPython.
1921+
self.assertEqual(repr(P_co), '+P_co')
1922+
self.assertEqual(repr(P_contra), '-P_contra')
1923+
1924+
def test_valid_uses(self):
1925+
P = ParamSpec('P')
1926+
T = TypeVar('T')
1927+
C1 = typing.Callable[P, int]
1928+
C2 = typing.Callable[P, T]
1929+
1930+
# Note: no tests for Callable.__args__ and Callable.__parameters__ here
1931+
# because pre-3.10 Callable sees ParamSpec as a plain list, not a
1932+
# TypeVar.
1933+
1934+
# Test collections.abc.Callable too.
1935+
if sys.version_info[:2] >= (3, 9):
1936+
C3 = collections.abc.Callable[P, int]
1937+
C4 = collections.abc.Callable[P, T]
1938+
1939+
# ParamSpec instances should also have args and kwargs attributes.
1940+
self.assertIn('args', dir(P))
1941+
self.assertIn('kwargs', dir(P))
1942+
P.args
1943+
P.kwargs
1944+
1945+
# Note: ParamSpec doesn't work for pre-3.10 user-defined Generics due
1946+
# to type checks inside Generic.
1947+
1948+
def test_pickle(self):
1949+
global P, P_co, P_contra
1950+
P = ParamSpec('P')
1951+
P_co = ParamSpec('P_co', covariant=True)
1952+
P_contra = ParamSpec('P_contra', contravariant=True)
1953+
for proto in range(pickle.HIGHEST_PROTOCOL):
1954+
with self.subTest('Pickle protocol {proto}'.format(proto=proto)):
1955+
for paramspec in (P, P_co, P_contra):
1956+
z = pickle.loads(pickle.dumps(paramspec, proto))
1957+
self.assertEqual(z.__name__, paramspec.__name__)
1958+
self.assertEqual(z.__covariant__, paramspec.__covariant__)
1959+
self.assertEqual(z.__contravariant__, paramspec.__contravariant__)
1960+
self.assertEqual(z.__bound__, paramspec.__bound__)
1961+
1962+
def test_eq(self):
1963+
P = ParamSpec('P')
1964+
self.assertEqual(P, P)
1965+
self.assertEqual(hash(P), hash(P))
1966+
# ParamSpec should compare by id similar to TypeVar in CPython
1967+
self.assertNotEqual(ParamSpec('P'), P)
1968+
self.assertIsNot(ParamSpec('P'), P)
1969+
# Note: normally you don't test this as it breaks when there's
1970+
# a hash collision. However, ParamSpec *must* guarantee that
1971+
# as long as two objects don't have the same ID, their hashes
1972+
# won't be the same.
1973+
self.assertNotEqual(hash(ParamSpec('P')), hash(P))
1974+
1975+
1976+
class ConcatenateTests(BaseTestCase):
1977+
def test_basics(self):
1978+
P = ParamSpec('P')
1979+
1980+
class MyClass: ...
1981+
1982+
c = Concatenate[MyClass, P]
1983+
self.assertNotEqual(c, Concatenate)
1984+
1985+
def test_valid_uses(self):
1986+
P = ParamSpec('P')
1987+
T = TypeVar('T')
1988+
C1 = typing.Callable[Concatenate[int, P], int]
1989+
C2 = typing.Callable[Concatenate[int, T, P], T]
1990+
1991+
# Test collections.abc.Callable too.
1992+
if sys.version_info[:2] >= (3, 9):
1993+
C3 = collections.abc.Callable[Concatenate[int, P], int]
1994+
C4 = collections.abc.Callable[Concatenate[int, T, P], T]
1995+
1996+
def test_basic_introspection(self):
1997+
P = ParamSpec('P')
1998+
C1 = Concatenate[int, P]
1999+
C2 = Concatenate[int, T, P]
2000+
self.assertEqual(C1.__origin__, Concatenate)
2001+
self.assertEqual(C1.__args__, (int, P))
2002+
self.assertEqual(C2.__origin__, Concatenate)
2003+
self.assertEqual(C2.__args__, (int, T, P))
2004+
2005+
def test_eq(self):
2006+
P = ParamSpec('P')
2007+
C1 = Concatenate[int, P]
2008+
C2 = Concatenate[int, P]
2009+
C3 = Concatenate[int, T, P]
2010+
self.assertEqual(C1, C2)
2011+
self.assertEqual(hash(C1), hash(C2))
2012+
self.assertNotEqual(C1, C3)
2013+
19012014

19022015
class AllTests(BaseTestCase):
19032016

@@ -1914,6 +2027,10 @@ def test_typing_extensions_includes_standard(self):
19142027
self.assertIn('overload', a)
19152028
self.assertIn('Text', a)
19162029
self.assertIn('TYPE_CHECKING', a)
2030+
self.assertIn('TypeAlias', a)
2031+
self.assertIn('ParamSpec', a)
2032+
self.assertIn("Concatenate", a)
2033+
19172034
if TYPING_3_5_3:
19182035
self.assertIn('Annotated', a)
19192036
if PEP_560:

0 commit comments

Comments
 (0)
0