With the help of Ethan Smith (and some code review by Serhiy) I have started a prototype of PEP585. You can see my code at https://github.com/python/cpython/pull/18239 (a PR to CPython) or directly in my branch (where I take PRs too): https://github.com/gvanrossum/cpython/tree/pep585. I am cautiously optimistic that we can get this integrated into Python 3.9.
It is an excellent work. It is much simpler than I expected.
Based on this work in progress I have a bunch of questions and suggestions for PEP 585.
1. The PEP says that e.g. list[str][str] should be forbidden. But there's at least one example where we currently allow chained subscript operations. This currently works (in mypy as well as at runtime):
T = typing.TypeVar("T")D = typing.Dict[str, T]# Now D is a generic typex: D[int]
# Now x has type dict[str, int]
But if, in my branch, we replace Dict with dict, we get an error on the last line:
T = typing.TypeVar("T")D = dict[str, T]x: D[int] # E: TypeError: 'GenericAlias' object is not subscriptable
The last line raises a TypeError.
We can add `__getitem__` which replaces type vars with actual parameters.
I tried to write the implementation here, but it turned out to be quite complicated,
taking to account that the same type variable can be used multiple
times and that they can be nested. It is not an easy problem, but
neither very hard.
2. The PEP says that isinstance([1, 2, 3], list[int]) should be True. I think this is wrong -- with typing.List[int] it currently raises a TypeError, and I think we should keep forbidding this. Similar with issubclass.
3. The PEP says that list == list[str]. This currently returns false in my branch (apparently this doesn't use __getattribute__ to get the __eq__ operation), and I think I like that better. It also matches the behavior of typing.List.
Do not have opinion.
4. The PEP doesn't provide a name for the proxy type. I propose to name it GenericAlias and to add it to the types.py module, so it can be imported as types.GenericAlias (note: types; not typing).
Great! It can be used for implementing `__class_getitem__` for
many Python classes (which already have trivial
`__class_getitem__`) without importing the typing module.
5. I think the following classes should also be made generic: re.Pattern, re.Match, io.IO.
But it would be nice to not make generic the BaseIO subclasses BufferedIOBase, RawIOBase and TextIOBase (if it is not too hard).
6. How far should we go with replacing everything in typing.py that shadows something that has become generic due to the PEP's changes? (collections, collections.abc, contextlib, re, io). I expect this to be somewhat controversial (Ivan may have some ideas) and difficult to keep compatible with 3.8.
Is the goal of the PEP to make typing.List an alias to list? Sorry, it's hard for me to read, so I could miss it.
7. I haven't figured out yet how to support type[x] (which should have the same meaning as typing.Type[x]) without it also enabling int[x], which we would like to prevent. (We don't want to just add object.__class_getitem__!)
We can add type.__class_getitem__ and check that "cls is type". There is a precedence in type.__call__: type('1') returns a type, but int('1') returns an int. Using the same type.__call__.
Interesting problem.8. I have a tricky bug remaining where inheriting from Tuple[something] causes problems. Take this example:
class C(Tuple[int]): pass
If we now examine C.__mro__, we see that it is (C, tuple, typing.Generic, object). IOW, inheriting from typing.Tuple causes builtins.tuple to be inserted into the MRO. The problem is that in my branch, tuple.__class_getitem__ overrides Generic.__class_getitem__, whereas the latter is needed to satisfy some tests for typing.py (and presumably to enable certain special behavior). I'm mostly looking for suggestions on how to make those failing tests work (see https://github.com/python/cpython/commit/35cd5f477c9da985880130d72297335e010450e4/checks?check_suite_id=427090531).