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.

8 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.

3 Likes

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].

4 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.

3 Likes

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.
5 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: )

3 Likes

This came up today again:

Given a) I don’t think anyone wants to pursue the removal on the original timeline, b) there doesn’t seem much appetite for coming up with a concrete removal timeline, we could alter the PEP 585 wording to say:

The deprecated functionality will eventually be removed from the typing module. Removal will occur no sooner than the first Python version released 5 years after the release of Python 3.9.0.

Realistically, I don’t think there’s any way we remove these in Python 3.14 — this would be unprecedentedly fast — so I’d like to propose the following change:

The deprecated functionality will eventually be removed from the typing module. Removal will occur no sooner than the first Python version released 7 years after the release of Python 3.9.0.

An extra two years is significantly better in that it means most people will stop worrying about whether or not they have to write “bridging code” for Python 3.9, like in today’s case:

It’s a little unclear to me what the process here is. To try and close the loop, if this post gets likes from core devs and no explicit opposition, I’ll open a PR to the PEPs repo, but please let me know if that’s overstepping :slight_smile:

3 Likes

I would rather tie removal to the end-of-life of Python 3.8, i.e. the last version where you couldn’t use built-in generics.

The deprecated functionality will eventually be removed from the typing module. Removal will occur no sooner than one year after the final release of Python 3.8.

The deprecated functionality will eventually be removed from the typing module.

Regardless of how the timing is decided, this wording should be changed.

functionality is a vague term. Does it means symbols? Or definitions like Tuple = _TupleType(…?

I think that it would be better to explicit whether symbols will not disappear from the namespace even if the definition changes, or whether they will disappear from the namespace.

Per PEP 1:

In general, PEPs are no longer substantially modified after they have reached the Accepted, Final, Rejected or Superseded state. Once resolution is reached, a PEP is considered a historical document rather than a living specification. Formal documentation of the expected behavior should be maintained elsewhere, such as the Language Reference for core features, the Library Reference for standard library modules or the PyPA Specifications for packaging.

However, here we don’t explicitly address PEP language that talks about planned future events that may change. This particular case is a close parallel to python/peps#2096 in that respect, which also sought to update a specified criterion for a future removal. In that case, we went ahead and merged it, and I believe the same reasoning would apply here, given the cases are nearly identical and the main justification in both was reducing repeated user (and potentially implementer) confusion, which there was clear evidence for.

The main prerequisite is ensuring there is consensus among the PEP author ( @ambv ) and the typing community that the change is both worthwhile and does not compromise the intent of the approved PEP. I suggest making a Typing-SIG thread and if there’s general consensus there and here, and folks like @ambv and @Jelle are :+1: on it, then I’d see no objections to merging it following appropriate review (at least speaking for myself here, not the other PEP editors).

When PEP 585 was being written, I was asked to explicitly state when deprecated names would get removed. Regular deprecations are removed two releases after they get deprecated per PEP 387. I felt like the regular deprecation schedule for this would be too aggressive so I opted to extend this period to five years after 3.9.0, which essentially means “after Python 3.9 goes entirely EOL”. That’s October 2025. @srittau is suggesting tying it to “one year after 3.8 EOL” which is also October 2025.

It’s not clear to me how changing the wording from 5 years to 7 improves the situation. I’m pretty sure we’ll get more people to complain when that deadline is reached. Why not 6? Why not 8?

3 Likes

Maybe the only change we require is changing the wording from “will be removed” to “will not be removed before”, i.e. change the requirement to remove the items to a promise that the items won’t be removed for a certain time. Then we can analyze usage and/or introduce more or less aggressive warnings after that date, but are not “forced” to remove the items if usage is still too high.

5 Likes

FWIW, that seems reasonable to me, and mirrors similar changes we’ve merged to other PEPs to make them less prescriptive about events well into the future.

So be it: PEP 585: clarify removal as "no sooner than" by hauntsaninja · Pull Request #2778 · python/peps · GitHub