Skip to content

Commit cca17eb

Browse files
Bring over TypeVarTests from CPython (#165)
I started out trying to backport python/cpython#104571, but realized that it makes sense to backport CPython's whole TypeVarTests class since we now have our own implementation of TypeVar. I dropped test_var_substitution and test_bad_var_substitution since they rely on the internal __typing_subst__ method, and the type substitution logic is generally very hard to get precisely the same across versions.
1 parent 8b6582e commit cca17eb

File tree

2 files changed

+155
-3
lines changed

2 files changed

+155
-3
lines changed

src/test_typing_extensions.py

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from unittest import TestCase, main, skipUnless, skipIf
2121
from unittest.mock import patch
2222
import typing
23-
from typing import TypeVar, Optional, Union, AnyStr
23+
from typing import Optional, Union, AnyStr
2424
from typing import T, KT, VT # Not in __all__.
2525
from typing import Tuple, List, Set, Dict, Iterable, Iterator, Callable
2626
from typing import Generic
@@ -36,7 +36,7 @@
3636
from typing_extensions import assert_type, get_type_hints, get_origin, get_args, get_original_bases
3737
from typing_extensions import clear_overloads, get_overloads, overload
3838
from typing_extensions import NamedTuple
39-
from typing_extensions import override, deprecated, Buffer, TypeAliasType
39+
from typing_extensions import override, deprecated, Buffer, TypeAliasType, TypeVar
4040
from _typed_dict_test_helper import Foo, FooGeneric
4141

4242
# Flags used to mark tests that only apply after a specific
@@ -3306,6 +3306,7 @@ def test_basic_plain(self):
33063306
P = ParamSpec('P')
33073307
self.assertEqual(P, P)
33083308
self.assertIsInstance(P, ParamSpec)
3309+
self.assertEqual(P.__name__, 'P')
33093310
# Should be hashable
33103311
hash(P)
33113312

@@ -4375,10 +4376,153 @@ class GenericNamedTuple(NamedTuple, Generic[T]):
43754376
self.assertEqual(CallNamedTuple.__orig_bases__, (NamedTuple,))
43764377

43774378

4379+
class TypeVarTests(BaseTestCase):
4380+
def test_basic_plain(self):
4381+
T = TypeVar('T')
4382+
# T equals itself.
4383+
self.assertEqual(T, T)
4384+
# T is an instance of TypeVar
4385+
self.assertIsInstance(T, TypeVar)
4386+
self.assertEqual(T.__name__, 'T')
4387+
self.assertEqual(T.__constraints__, ())
4388+
self.assertIs(T.__bound__, None)
4389+
self.assertIs(T.__covariant__, False)
4390+
self.assertIs(T.__contravariant__, False)
4391+
self.assertIs(T.__infer_variance__, False)
4392+
4393+
def test_attributes(self):
4394+
T_bound = TypeVar('T_bound', bound=int)
4395+
self.assertEqual(T_bound.__name__, 'T_bound')
4396+
self.assertEqual(T_bound.__constraints__, ())
4397+
self.assertIs(T_bound.__bound__, int)
4398+
4399+
T_constraints = TypeVar('T_constraints', int, str)
4400+
self.assertEqual(T_constraints.__name__, 'T_constraints')
4401+
self.assertEqual(T_constraints.__constraints__, (int, str))
4402+
self.assertIs(T_constraints.__bound__, None)
4403+
4404+
T_co = TypeVar('T_co', covariant=True)
4405+
self.assertEqual(T_co.__name__, 'T_co')
4406+
self.assertIs(T_co.__covariant__, True)
4407+
self.assertIs(T_co.__contravariant__, False)
4408+
self.assertIs(T_co.__infer_variance__, False)
4409+
4410+
T_contra = TypeVar('T_contra', contravariant=True)
4411+
self.assertEqual(T_contra.__name__, 'T_contra')
4412+
self.assertIs(T_contra.__covariant__, False)
4413+
self.assertIs(T_contra.__contravariant__, True)
4414+
self.assertIs(T_contra.__infer_variance__, False)
4415+
4416+
T_infer = TypeVar('T_infer', infer_variance=True)
4417+
self.assertEqual(T_infer.__name__, 'T_infer')
4418+
self.assertIs(T_infer.__covariant__, False)
4419+
self.assertIs(T_infer.__contravariant__, False)
4420+
self.assertIs(T_infer.__infer_variance__, True)
4421+
4422+
def test_typevar_instance_type_error(self):
4423+
T = TypeVar('T')
4424+
with self.assertRaises(TypeError):
4425+
isinstance(42, T)
4426+
4427+
def test_typevar_subclass_type_error(self):
4428+
T = TypeVar('T')
4429+
with self.assertRaises(TypeError):
4430+
issubclass(int, T)
4431+
with self.assertRaises(TypeError):
4432+
issubclass(T, int)
4433+
4434+
def test_constrained_error(self):
4435+
with self.assertRaises(TypeError):
4436+
X = TypeVar('X', int)
4437+
X
4438+
4439+
def test_union_unique(self):
4440+
X = TypeVar('X')
4441+
Y = TypeVar('Y')
4442+
self.assertNotEqual(X, Y)
4443+
self.assertEqual(Union[X], X)
4444+
self.assertNotEqual(Union[X], Union[X, Y])
4445+
self.assertEqual(Union[X, X], X)
4446+
self.assertNotEqual(Union[X, int], Union[X])
4447+
self.assertNotEqual(Union[X, int], Union[int])
4448+
self.assertEqual(Union[X, int].__args__, (X, int))
4449+
self.assertEqual(Union[X, int].__parameters__, (X,))
4450+
self.assertIs(Union[X, int].__origin__, Union)
4451+
4452+
if hasattr(types, "UnionType"):
4453+
def test_or(self):
4454+
X = TypeVar('X')
4455+
# use a string because str doesn't implement
4456+
# __or__/__ror__ itself
4457+
self.assertEqual(X | "x", Union[X, "x"])
4458+
self.assertEqual("x" | X, Union["x", X])
4459+
# make sure the order is correct
4460+
self.assertEqual(get_args(X | "x"), (X, typing.ForwardRef("x")))
4461+
self.assertEqual(get_args("x" | X), (typing.ForwardRef("x"), X))
4462+
4463+
def test_union_constrained(self):
4464+
A = TypeVar('A', str, bytes)
4465+
self.assertNotEqual(Union[A, str], Union[A])
4466+
4467+
def test_repr(self):
4468+
self.assertEqual(repr(T), '~T')
4469+
self.assertEqual(repr(KT), '~KT')
4470+
self.assertEqual(repr(VT), '~VT')
4471+
self.assertEqual(repr(AnyStr), '~AnyStr')
4472+
T_co = TypeVar('T_co', covariant=True)
4473+
self.assertEqual(repr(T_co), '+T_co')
4474+
T_contra = TypeVar('T_contra', contravariant=True)
4475+
self.assertEqual(repr(T_contra), '-T_contra')
4476+
4477+
def test_no_redefinition(self):
4478+
self.assertNotEqual(TypeVar('T'), TypeVar('T'))
4479+
self.assertNotEqual(TypeVar('T', int, str), TypeVar('T', int, str))
4480+
4481+
def test_cannot_subclass(self):
4482+
with self.assertRaises(TypeError):
4483+
class V(TypeVar): pass
4484+
T = TypeVar("T")
4485+
with self.assertRaises(TypeError):
4486+
class V(T): pass
4487+
4488+
def test_cannot_instantiate_vars(self):
4489+
with self.assertRaises(TypeError):
4490+
TypeVar('A')()
4491+
4492+
def test_bound_errors(self):
4493+
with self.assertRaises(TypeError):
4494+
TypeVar('X', bound=Union)
4495+
with self.assertRaises(TypeError):
4496+
TypeVar('X', str, float, bound=Employee)
4497+
with self.assertRaisesRegex(TypeError,
4498+
r"Bound must be a type\. Got \(1, 2\)\."):
4499+
TypeVar('X', bound=(1, 2))
4500+
4501+
# Technically we could run it on later versions of 3.7 and 3.8,
4502+
# but that's not worth the effort.
4503+
@skipUnless(TYPING_3_9_0, "Fix was not backported")
4504+
def test_missing__name__(self):
4505+
# See bpo-39942
4506+
code = ("import typing\n"
4507+
"T = typing.TypeVar('T')\n"
4508+
)
4509+
exec(code, {})
4510+
4511+
def test_no_bivariant(self):
4512+
with self.assertRaises(ValueError):
4513+
TypeVar('T', covariant=True, contravariant=True)
4514+
4515+
def test_cannot_combine_explicit_and_infer(self):
4516+
with self.assertRaises(ValueError):
4517+
TypeVar('T', covariant=True, infer_variance=True)
4518+
with self.assertRaises(ValueError):
4519+
TypeVar('T', contravariant=True, infer_variance=True)
4520+
4521+
43784522
class TypeVarLikeDefaultsTests(BaseTestCase):
43794523
def test_typevar(self):
43804524
T = typing_extensions.TypeVar('T', default=int)
4381-
typing_T = TypeVar('T')
4525+
typing_T = typing.TypeVar('T')
43824526
self.assertEqual(T.__default__, int)
43834527
self.assertIsInstance(T, typing_extensions.TypeVar)
43844528
self.assertIsInstance(T, typing.TypeVar)

src/typing_extensions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,8 @@ def __call__(self, name, *constraints, bound=None,
13741374
else:
13751375
typevar = typing.TypeVar(name, *constraints, bound=bound,
13761376
covariant=covariant, contravariant=contravariant)
1377+
if infer_variance and (covariant or contravariant):
1378+
raise ValueError("Variance cannot be specified with infer_variance.")
13771379
typevar.__infer_variance__ = infer_variance
13781380
_set_default(typevar, default)
13791381

@@ -1392,6 +1394,9 @@ class TypeVar(metaclass=_TypeVarMeta):
13921394

13931395
__module__ = 'typing'
13941396

1397+
def __init_subclass__(cls) -> None:
1398+
raise TypeError(f"type '{__name__}.TypeVar' is not an acceptable base type")
1399+
13951400

13961401
# Python 3.10+ has PEP 612
13971402
if hasattr(typing, 'ParamSpecArgs'):
@@ -1481,6 +1486,9 @@ class ParamSpec(metaclass=_ParamSpecMeta):
14811486

14821487
__module__ = 'typing'
14831488

1489+
def __init_subclass__(cls) -> None:
1490+
raise TypeError(f"type '{__name__}.ParamSpec' is not an acceptable base type")
1491+
14841492
# 3.7-3.9
14851493
else:
14861494

0 commit comments

Comments
 (0)