From ea9aa31cca243a6021c172767909a7c392c8d5ee Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 18 May 2016 16:06:01 -0700 Subject: [PATCH 1/4] Type[C] for Python 3 -- seems too easy? --- src/test_typing.py | 28 ++++++++++++++++++++++++++++ src/typing.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/test_typing.py b/src/test_typing.py index f9e54b27..d0555f76 100644 --- a/src/test_typing.py +++ b/src/test_typing.py @@ -15,6 +15,7 @@ from typing import cast from typing import get_type_hints from typing import no_type_check, no_type_check_decorator +from typing import Type from typing import NamedTuple from typing import IO, TextIO, BinaryIO from typing import Pattern, Match @@ -1373,6 +1374,33 @@ def manager(): self.assertNotIsInstance(42, typing.ContextManager) +class TypeTests(BaseTestCase): + + def test_type_basic(self): + + class User: pass + class BasicUser(User): pass + class ProUser(User): pass + + def new_user(user_class: Type[User]): + return user_class() + + joe = new_user(BasicUser) + + def test_type_typevar(self): + + class User: pass + class BasicUser(User): pass + class ProUser(User): pass + + U = TypeVar('U', bound=User) + + def new_user(user_class: Type[U]) -> U: + return user_class() + + joe = new_user(BasicUser) + + class NamedTupleTests(BaseTestCase): def test_basics(self): diff --git a/src/typing.py b/src/typing.py index 841e7786..5e7934c5 100644 --- a/src/typing.py +++ b/src/typing.py @@ -447,6 +447,7 @@ def __subclasscheck__(self, cls): # Some unconstrained type variables. These are used by the container types. +# (These are not for export.) T = TypeVar('T') # Any type. KT = TypeVar('KT') # Key type. VT = TypeVar('VT') # Value type. @@ -456,6 +457,7 @@ def __subclasscheck__(self, cls): T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. # A useful type variable with constraints. This represents string types. +# (This one *is* for export!) AnyStr = TypeVar('AnyStr', bytes, str) @@ -1572,6 +1574,35 @@ def __new__(cls, *args, **kwds): return super().__new__(cls, *args, **kwds) +# Internal type variable used for Type[]. +CT = TypeVar('CT', bound=type) + + +class Type(type, Generic[CT], extra=type): + """A generic type usable to annotate class objects. + + For example, suppose we have the following classes:: + + class User: ... # Abstract base for User classes + class BasicUser(User): ... + class ProUser(User): ... + class TeamUser(User): ... + + And a function that takes a class argument that's a subclass of + User and returns an instance of the corresponding class:: + + U = TypeVar('U', bound=User) + def new_user(user_class: Type[U]) -> U: + user = user_class() + # (Here we could write the user object to a database) + return user + + joe = new_user(BasicUser) + + At this point the type checker knows that joe has type BasicUser. + """ + + def NamedTuple(typename, fields): """Typed version of namedtuple. From 0cecb69506cea16f620ad8d25ced9d3d8cc66785 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 18 May 2016 16:11:34 -0700 Subject: [PATCH 2/4] Type[C] for Python 2. --- python2/test_typing.py | 31 +++++++++++++++++++++++++++++++ python2/typing.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/python2/test_typing.py b/python2/test_typing.py index c11ba221..8b027098 100644 --- a/python2/test_typing.py +++ b/python2/test_typing.py @@ -14,6 +14,7 @@ from typing import Callable from typing import Generic from typing import cast +from typing import Type from typing import NamedTuple from typing import IO, TextIO, BinaryIO from typing import Pattern, Match @@ -1110,6 +1111,36 @@ def __len__(self): self.assertIsSubclass(MMC, typing.Mapping) +class TypeTests(BaseTestCase): + + def test_type_basic(self): + + class User(object): pass + class BasicUser(User): pass + class ProUser(User): pass + + def new_user(user_class): + # type: (Type[User]) -> None + return user_class() + + joe = new_user(BasicUser) + + def test_type_typevar(self): + + class User(object): pass + class BasicUser(User): pass + class ProUser(User): pass + + global U + U = TypeVar('U', bound=User) + + def new_user(user_class): + # type: (Type[U]) -> U + return user_class() + + joe = new_user(BasicUser) + + class NamedTupleTests(BaseTestCase): def test_basics(self): diff --git a/python2/typing.py b/python2/typing.py index 44eb9f2e..7a7ba9da 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -454,6 +454,7 @@ def __subclasscheck__(self, cls): # Some unconstrained type variables. These are used by the container types. +# (These are not for export.) T = TypeVar('T') # Any type. KT = TypeVar('KT') # Key type. VT = TypeVar('VT') # Value type. @@ -463,6 +464,7 @@ def __subclasscheck__(self, cls): T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. # A useful type variable with constraints. This represents string types. +# (This one *is* for export!) AnyStr = TypeVar('AnyStr', bytes, unicode) @@ -1518,6 +1520,36 @@ def __new__(cls, *args, **kwds): return super(Generator, cls).__new__(cls, *args, **kwds) +# Internal type variable used for Type[]. +CT = TypeVar('CT', bound=type) + + +class Type(type, Generic[CT]): + """A generic type usable to annotate class objects. + + For example, suppose we have the following classes:: + + class User: ... # Abstract base for User classes + class BasicUser(User): ... + class ProUser(User): ... + class TeamUser(User): ... + + And a function that takes a class argument that's a subclass of + User and returns an instance of the corresponding class:: + + U = TypeVar('U', bound=User) + def new_user(user_class: Type[U]) -> U: + user = user_class() + # (Here we could write the user object to a database) + return user + + joe = new_user(BasicUser) + + At this point the type checker knows that joe has type BasicUser. + """ + __extra__ = type + + def NamedTuple(typename, fields): """Typed version of namedtuple. From f437c88b82cb5b3f36529cdc2d17833ab0e82fc6 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 19 May 2016 13:22:22 -0700 Subject: [PATCH 3/4] Make CT covariant, like the PEP says. --- python2/typing.py | 2 +- src/typing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python2/typing.py b/python2/typing.py index 7a7ba9da..ff6ba911 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -1521,7 +1521,7 @@ def __new__(cls, *args, **kwds): # Internal type variable used for Type[]. -CT = TypeVar('CT', bound=type) +CT = TypeVar('CT', covariant=True, bound=type) class Type(type, Generic[CT]): diff --git a/src/typing.py b/src/typing.py index 5e7934c5..8dc081a0 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1575,7 +1575,7 @@ def __new__(cls, *args, **kwds): # Internal type variable used for Type[]. -CT = TypeVar('CT', bound=type) +CT = TypeVar('CT', covariant=True, bound=type) class Type(type, Generic[CT], extra=type): From 002d88b72921f1041ec8b1de6fce43d836a55468 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 19 May 2016 14:46:55 -0700 Subject: [PATCH 4/4] Fix return type of new_user(). --- python2/test_typing.py | 2 +- src/test_typing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python2/test_typing.py b/python2/test_typing.py index 8b027098..b801383c 100644 --- a/python2/test_typing.py +++ b/python2/test_typing.py @@ -1120,7 +1120,7 @@ class BasicUser(User): pass class ProUser(User): pass def new_user(user_class): - # type: (Type[User]) -> None + # type: (Type[User]) -> User return user_class() joe = new_user(BasicUser) diff --git a/src/test_typing.py b/src/test_typing.py index d0555f76..ade8a358 100644 --- a/src/test_typing.py +++ b/src/test_typing.py @@ -1382,7 +1382,7 @@ class User: pass class BasicUser(User): pass class ProUser(User): pass - def new_user(user_class: Type[User]): + def new_user(user_class: Type[User]) -> User: return user_class() joe = new_user(BasicUser)