Concern about PEP 585 removals

PEP 585 currently mentions the following:

The deprecated functionality will be removed from the typing module in the first Python version released 5 years after the release of Python 3.9.0.

where “deprecated functionality” refers to typing.List, typing.Dict, etc.

Removal will break pretty much all typed code and all typing documentation / resources written to support Python 3.8 and earlier. I’m not sure the one year between 3.8 going EOL and the removal date mentioned in PEP 585 is enough to expect all libraries with type hints to update.

Given how breaking this is, I think there’s a lack of appetite to go through with the removal on the proposed timeline. For example, see gh-91873: Summarise deprecations in typing at the top level by Fidget-Spinner · Pull Request #91864 · python/cpython · GitHub, in which the CPython documentation describes the removal date as “undecided”. The maintenance cost of these objects isn’t terrible and we could choose to simply alias List = list in typing.py if that were a concern.

I propose we remove the phrasing of a definite timeline from PEP 585. The objects themselves remain deprecated and we’ll work to minimise their use. If there is concern about kicking the can down the road forever, here are some concrete actions we can take in that space to ensure an eventual path to removal:

  • Evangelising use of tools that can automatically switch to use of builtins, like pyupgrade
  • Minimising reference to deprecated objects in documentation
  • I’m not aware of type checkers that have PEP 585’s proposed warning for usage of deprecated typing objects enabled by default. We can help ensure this happens, but likely only feasible following 3.8’s EOL.

I’ll also make note of an alternative history that could have existed at the time of PEP 585’s writing. There is a world in which type hints are only used by static type checkers and from __future__ import annotations is extremely widely used, and the default in Python 3.10. In this alternate history, once Python 3.6 went EOL, most code starts using builtin generics in annotations. Unfortunately, this doesn’t appear to be the history our universe has chosen.

4 Likes

I’m in favour of delaying the removal of these objects from typing; I agree that they’re very widely used at the moment, and that the current timescale probably isn’t long enough for libraries to update their code.

I would like for us to have a plan, though, instead of just saying “the removal date is undecided”. The maintenance cost of these objects is very low, but I’d really like the typing module to have a smaller API — I think the cognitive load for newcomers to Python’s typing system is too big at the moment.

I propose, instead, that we introduce DeprecationWarnings for these objects in 3.14, with a view to removing these objects in 3.16.

1 Like

I don’t really want to ever remove the deprecated aliases, because doing so will break compatibility for a lot of code written for older Python versions.

To reduce the cognitive load in the docs, we can relegate the aliases to a very concise section that goes “List – deprecated alias for :class:list; Generator – deprecated alias for :class:collections.abc.Generator”. Similarly, at runtime we can simply define List = list in typing.py.

How sure are we that setting List = list doesn’t break anything?

I suppose it will break some code that introspects annotations (and hasn’t been updated to deal with types.GenericAlias). But I’m more willing to break that category of code than to break everyone who writes x: List[int].

3 Likes

A somewhat comparable removal is asyncio.coroutine, since it affects all users of async Python analogously to how this affects all users of typed Python. async def syntax was added in 3.5, in 2015. asyncio.coroutine was removed in 3.11, in 2022. So that would suggest no sooner than 3.16 (2027).

Note, however that:

  • asyncio.coroutine was provisional in all Python versions that didn’t support async def, so there was less of a stability promise
  • PEP 3156 to async def was three years, PEP 484 to list[int] was six years.
  • typing is much more widely used than asyncio

A much more compelling datapoint to me is moving collections ABCs to collections.abc. That deprecation period was 9 years, which would suggest 3.18 (2029), with warnings starting in 3.15 (2026).

And of course, if you think that a better analogy is turning print to a function, that looks more on the lines of keeping things around forever.

My personal opinions:

Like Jelle, I think we can get the costs low enough that an indefinite deprecation would be fine.

If we want removal, I think collections.abc is reasonable prior art. In particular, that timeline would leave a nice window of 2024 to 2026, where people can slowly upgrade to versions of type checkers that statically warn about this, without incurring runtime warnings (which were annoying for a while with collections.abc).

We do not need to decide upon a fixed date today. The date in the PEP is the minimum, we won’t do it sooner than that.

When it comes time to both (a) prominently surface warnings (not intended in the PEP 585 case) and (b) do removals based on an earlier scheduled assumption declared by a PEP we should always take the state of the global Python ecosystem at the time into account. We’ve delayed deprecation removals in the past due to disruption.

In the PEP 585 situation it appears to be merely adopting a conditional import to code still needing to support a very old Python version. Probably not too onerous. But we can wait until after 3.13 is released to start doing research on typed codebases to find out.

1 Like

I was not aware about any term so I did not do anything yet. I think that it should be more gradual, in more steps:

  1. Make typing._GenericAlias (or typing._BaseGenericAlias) a subclass of types.GenericAlias). You will only need to check for a public class if you use isinstance().
  2. Deprecate typing._GenericAlias API which is not in types.GenericAlias. Maybe move something to types.GenericAlias.
  3. Replace the use of typing._GenericAlias in typing with types.GenericAlias (at least for List, etc).
  4. Make List an alias of list.
  5. Make List a deprecated alias of list.
  6. Remove List.
3 Likes

I like Serhiy’s steps. I also want to add that I do lookout for feature parity between List[] and list[] and often consider different behavior (other than the relaxation of type checks) a bug. So hopefully if we alias List to list in the future, the runtime checkers shouldn’t break. (I bet years from now I’ll look back and realise I’m making a fool of myself here by saying this :slight_smile: )

2 Likes