This will work for Python
3.8 and above, though if you're using
3.8 specifically you will need to run the hook with
A complete type list is shown below.
See the complete list
I'm a visual learner
In a nutshell, this code
from typing import List, Tuple, Dict, Set, FrozenSet def do_thing(x: List[Tuple[str, ...]], y: Dict[str, Set[str]]) -> FrozenSet:
def do_thing(x: list[tuple[str, ...]], y: dict[str, set[str]]) -> frozenset:
or this, if you're running python < 3.9 or enable the futures option
from __future__ import annotations def do_thing(x: list[tuple[str, ...]], y: dict[str, set[str]]) -> frozenset:
- Performs in-line substitution for new types
- Adds new imports for upgrade types which need them
__futures__imports if the futures flag is enabled
- Removes no longer needed typing imports
Note: even though we remove and add imports reasonably well, I would recommend running this in tandem with hooks like
isort to aggregate and sort your imports, and flake8 to discover any unused imports neither were able to remove. Otherwise you risk needing to do some manual cleanup from time to time (though it should be pretty rare).
To use this with pre-commit, simply add this to your config file:
- repo: https://github.com/sondrelg/pep585-upgrade rev: '' # Use the sha / tag you want to point at hooks: - id: upgrade-type-hints
and while futures imports are added automatically if you're running Python older than
3.9, you can also enable them explicitly, by adding a
This is required, e.g., when maintaining code that needs to support older Python versions.
- repo: https://github.com/sondrelg/pep585-upgrade rev: '' # Use the sha / tag you want to point at hooks: - id: upgrade-type-hints args: [ '--futures=true' ]
For more information about available arguments, see the function definitions.
Running this once on my codebase
If you wish to run this once on your codebase, that's not easy to do without pre-commit, as it piggybacks on that process quite a bit.
However, installing pre-commit and configuring the hook to run will take you less than a minute. These are the steps:
pip install pre-commit
- Copy the configuration shown above into the file
pre-commit run --all-files
Running this once on a single file
To run the upgrade on a single file, you can clone the repo and run
python -m upgrade_type_hints <filename> from the src folder, or something equivalent.
We have a hard time removing common
import typingimports, since we don't have a full inventory of all possible places
typingcould be used. Seeing something like this, you might think this is easy to handle
import typing x: typing.List
but extending this example to a thousand-line file, the way we've structured the logic, there is no way to know whether there is a valid
typing.Optionalsomewhere in the file.
We might remove typing imports in a file where you needed them for more than just type annotations. An example of this is custom type declarations:
from typing import List x: List # this will be upgraded and the import will be removed y = List[str] # this will be left without its required import
The reason for this is that custom type declarations are not a part of the
astobjects we look at.
Both points are resolved by running flake8.