diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 14f49b01aa9f01..d6e4ed02e9c45f 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -270,6 +270,12 @@ class UnionTests(BaseTestCase): def test_basics(self): u = Union[int, float] self.assertNotEqual(u, Union) + with self.assertRaises(TypeError): + Union[int, 42] + with self.assertRaises(TypeError): + Union[int, chr] + with self.assertRaises(TypeError): + Union[int, ...] def test_subclass_error(self): with self.assertRaises(TypeError): @@ -395,10 +401,6 @@ def test_no_eval_union(self): def f(x: u): ... self.assertIs(get_type_hints(f)['x'], u) - def test_function_repr_union(self): - def fun() -> int: ... - self.assertEqual(repr(Union[fun, int]), 'typing.Union[fun, int]') - def test_union_str_pattern(self): # Shouldn't crash; see http://bugs.python.org/issue25390 A = Union[str, Pattern] @@ -411,11 +413,6 @@ def test_etree(self): Union[Element, str] # Shouldn't crash - def Elem(*args): - return Element(*args) - - Union[Elem, str] # Nor should this - class TupleTests(BaseTestCase): @@ -2511,6 +2508,8 @@ class ClassVarTests(BaseTestCase): def test_basics(self): with self.assertRaises(TypeError): ClassVar[1] + with self.assertRaises(TypeError): + ClassVar[chr] with self.assertRaises(TypeError): ClassVar[int, str] with self.assertRaises(TypeError): @@ -2551,6 +2550,8 @@ def test_basics(self): Final[int] # OK with self.assertRaises(TypeError): Final[1] + with self.assertRaises(TypeError): + Final[chr] with self.assertRaises(TypeError): Final[int, str] with self.assertRaises(TypeError): diff --git a/Lib/typing.py b/Lib/typing.py index e4e32b5b320d04..1a65f854fb22b6 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -178,11 +178,10 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms= return arg if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol): raise TypeError(f"Plain {arg} is not valid as type argument") - if isinstance(arg, (type, TypeVar, ForwardRef, types.UnionType, ParamSpec)): + if isinstance(arg, (type, TypeVar, ForwardRef, types.UnionType, ParamSpec, + _GenericAlias, _SpecialGenericAlias, NewType)): return arg - if not callable(arg): - raise TypeError(f"{msg} Got {arg!r:.100}.") - return arg + raise TypeError(f"{msg} Got {arg!r:.100}.") def _is_param_expr(arg): @@ -2065,6 +2064,50 @@ class Other(Leaf): # Error reported by type checker return f +class NewType: + """NewType creates simple unique types with almost zero + runtime overhead. NewType(name, tp) is considered a subtype of tp + by static type checkers. At runtime, NewType(name, tp) returns + a dummy callable that simply returns its argument. Usage:: + + UserId = NewType('UserId', int) + + def name_by_id(user_id: UserId) -> str: + ... + + UserId('user') # Fails type check + + name_by_id(42) # Fails type check + name_by_id(UserId(42)) # OK + + num = UserId(5) + 1 # type: int + """ + + __call__ = _idfunc + + def __init__(self, name, tp): + self.__qualname__ = name + if '.' in name: + name = name.rpartition('.')[-1] + self.__name__ = name + self.__supertype__ = tp + def_mod = _caller() + if def_mod != 'typing': + self.__module__ = def_mod + + def __repr__(self): + return f'{self.__module__}.{self.__qualname__}' + + def __reduce__(self): + return self.__qualname__ + + def __or__(self, other): + return Union[self, other] + + def __ror__(self, other): + return Union[other, self] + + # Some unconstrained type variables. These are used by the container types. # (These are not for export.) T = TypeVar('T') # Any type. @@ -2438,50 +2481,6 @@ class body be required. TypedDict.__mro_entries__ = lambda bases: (_TypedDict,) -class NewType: - """NewType creates simple unique types with almost zero - runtime overhead. NewType(name, tp) is considered a subtype of tp - by static type checkers. At runtime, NewType(name, tp) returns - a dummy callable that simply returns its argument. Usage:: - - UserId = NewType('UserId', int) - - def name_by_id(user_id: UserId) -> str: - ... - - UserId('user') # Fails type check - - name_by_id(42) # Fails type check - name_by_id(UserId(42)) # OK - - num = UserId(5) + 1 # type: int - """ - - __call__ = _idfunc - - def __init__(self, name, tp): - self.__qualname__ = name - if '.' in name: - name = name.rpartition('.')[-1] - self.__name__ = name - self.__supertype__ = tp - def_mod = _caller() - if def_mod != 'typing': - self.__module__ = def_mod - - def __repr__(self): - return f'{self.__module__}.{self.__qualname__}' - - def __reduce__(self): - return self.__qualname__ - - def __or__(self, other): - return Union[self, other] - - def __ror__(self, other): - return Union[other, self] - - # Python-version-specific alias (Python 2: unicode; Python 3: str) Text = str diff --git a/Misc/NEWS.d/next/Library/2022-02-06-13-56-58.bpo-46644.rWSd0I.rst b/Misc/NEWS.d/next/Library/2022-02-06-13-56-58.bpo-46644.rWSd0I.rst new file mode 100644 index 00000000000000..4943e0c94a0a6b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-02-06-13-56-58.bpo-46644.rWSd0I.rst @@ -0,0 +1,2 @@ +No longer accept arbitrary callables as type arguments in generics. E.g. +``List[chr]`` is now an error.