Sync typing.py with upstream.

(Upstream is https://github.com/python/typing)

- Add TYPE_CHECKING (false at runtime, true in type checkers) (upstream #230).
- Avoid error on Union[xml.etree.cElementTree.Element, str] (upstream #229).
- Repr of Tuple[()] should be 'Tuple[()]' (upstream #231).
- Add NewType() (upstream #189).
This commit is contained in:
Guido van Rossum 2016-06-08 11:19:11 -07:00
parent 07a9fcdc86
commit 91185fe028
2 changed files with 76 additions and 4 deletions

View File

@ -3,7 +3,7 @@ import collections
import pickle import pickle
import re import re
import sys import sys
from unittest import TestCase, main, skipUnless from unittest import TestCase, main, skipUnless, SkipTest
from typing import Any from typing import Any
from typing import TypeVar, AnyStr from typing import TypeVar, AnyStr
@ -16,6 +16,7 @@ from typing import cast
from typing import get_type_hints from typing import get_type_hints
from typing import no_type_check, no_type_check_decorator from typing import no_type_check, no_type_check_decorator
from typing import Type from typing import Type
from typing import NewType
from typing import NamedTuple from typing import NamedTuple
from typing import IO, TextIO, BinaryIO from typing import IO, TextIO, BinaryIO
from typing import Pattern, Match from typing import Pattern, Match
@ -339,6 +340,20 @@ class UnionTests(BaseTestCase):
A = Union[str, Pattern] A = Union[str, Pattern]
A A
def test_etree(self):
# See https://github.com/python/typing/issues/229
# (Only relevant for Python 2.)
try:
from xml.etree.cElementTree import Element
except ImportError:
raise SkipTest("cElementTree not found")
Union[Element, str] # Shouldn't crash
def Elem(*args):
return Element(*args)
Union[Elem, str] # Nor should this
class TypeVarUnionTests(BaseTestCase): class TypeVarUnionTests(BaseTestCase):
@ -410,7 +425,7 @@ class TupleTests(BaseTestCase):
def test_repr(self): def test_repr(self):
self.assertEqual(repr(Tuple), 'typing.Tuple') self.assertEqual(repr(Tuple), 'typing.Tuple')
self.assertEqual(repr(Tuple[()]), 'typing.Tuple[]') self.assertEqual(repr(Tuple[()]), 'typing.Tuple[()]')
self.assertEqual(repr(Tuple[int, float]), 'typing.Tuple[int, float]') self.assertEqual(repr(Tuple[int, float]), 'typing.Tuple[int, float]')
self.assertEqual(repr(Tuple[int, ...]), 'typing.Tuple[int, ...]') self.assertEqual(repr(Tuple[int, ...]), 'typing.Tuple[int, ...]')
@ -1401,6 +1416,25 @@ class TypeTests(BaseTestCase):
joe = new_user(BasicUser) joe = new_user(BasicUser)
class NewTypeTests(BaseTestCase):
def test_basic(self):
UserId = NewType('UserId', int)
UserName = NewType('UserName', str)
self.assertIsInstance(UserId(5), int)
self.assertIsInstance(UserName('Joe'), str)
self.assertEqual(UserId(5) + 1, 6)
def test_errors(self):
UserId = NewType('UserId', int)
UserName = NewType('UserName', str)
with self.assertRaises(TypeError):
issubclass(UserId, int)
with self.assertRaises(TypeError):
class D(UserName):
pass
class NamedTupleTests(BaseTestCase): class NamedTupleTests(BaseTestCase):
def test_basics(self): def test_basics(self):

View File

@ -64,10 +64,12 @@ __all__ = [
'AnyStr', 'AnyStr',
'cast', 'cast',
'get_type_hints', 'get_type_hints',
'NewType',
'no_type_check', 'no_type_check',
'no_type_check_decorator', 'no_type_check_decorator',
'overload', 'overload',
'Text', 'Text',
'TYPE_CHECKING',
] ]
# The pseudo-submodules 're' and 'io' are part of the public # The pseudo-submodules 're' and 'io' are part of the public
@ -306,7 +308,7 @@ def _type_check(arg, msg):
return type(None) return type(None)
if isinstance(arg, str): if isinstance(arg, str):
arg = _ForwardRef(arg) arg = _ForwardRef(arg)
if not isinstance(arg, (type, _TypeAlias)): if not isinstance(arg, (type, _TypeAlias)) and not callable(arg):
raise TypeError(msg + " Got %.100r." % (arg,)) raise TypeError(msg + " Got %.100r." % (arg,))
return arg return arg
@ -503,7 +505,10 @@ class UnionMeta(TypingMeta):
if isinstance(t1, _TypeAlias): if isinstance(t1, _TypeAlias):
# _TypeAlias is not a real class. # _TypeAlias is not a real class.
continue continue
if any(issubclass(t1, t2) if not isinstance(t1, type):
assert callable(t1) # A callable might sneak through.
continue
if any(isinstance(t2, type) and issubclass(t1, t2)
for t2 in all_params - {t1} if not isinstance(t2, TypeVar)): for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
all_params.remove(t1) all_params.remove(t1)
# It's not a union if there's only one type left. # It's not a union if there's only one type left.
@ -684,6 +689,8 @@ class TupleMeta(TypingMeta):
params = [_type_repr(p) for p in self.__tuple_params__] params = [_type_repr(p) for p in self.__tuple_params__]
if self.__tuple_use_ellipsis__: if self.__tuple_use_ellipsis__:
params.append('...') params.append('...')
if not params:
params.append('()')
r += '[%s]' % ( r += '[%s]' % (
', '.join(params)) ', '.join(params))
return r return r
@ -1632,10 +1639,41 @@ def NamedTuple(typename, fields):
return cls return cls
def NewType(name, tp):
"""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 function 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
"""
def new_type(x):
return x
new_type.__name__ = name
new_type.__supertype__ = tp
return new_type
# Python-version-specific alias (Python 2: unicode; Python 3: str) # Python-version-specific alias (Python 2: unicode; Python 3: str)
Text = str Text = str
# Constant that's True when type checking, but False here.
TYPE_CHECKING = False
class IO(Generic[AnyStr]): class IO(Generic[AnyStr]):
"""Generic base class for TextIO and BinaryIO. """Generic base class for TextIO and BinaryIO.