PEP 585 + PEP 604 backports.
Project description
modern_types
__modern_types__
aims to provide PEP 585 + PEP 604 backward compatibility for Python <=3.10 deferred type evaluation.
Hence, the targeted Python versions are 3.8 and 3.9.
What does it do?
Technically speaking, __modern_types__
traverses ASTs of type hint expressions to transform copies
of the namespaces passed to the evaluation routine ForwardRef._evaluate
.
The transformation prevents type errors in Python 3.8 and 3.9 when evaluating these type hints using future-version PEP 585 and PEP 604 syntaxes.
This might be very useful for writing pydantic models in Python <3.10 in a modern fashion, without having to import typing
.
As a result, in Python 3.8 and Python 3.9, the following code
from __future__ import annotations
import collections.abc
from collections import defaultdict
from pprint import pprint
from typing import get_type_hints
import __modern_types__ # without this line it won't work!
class Foo:
a: dict[str, int]
b: list[int]
c: set[int]
d: tuple[int, ...] | None
e: frozenset[int]
f: defaultdict[str, int]
g: str | None
h: str | int
i: str | int | None
j: str
k: collections.abc.Mapping[str, int]
l: collections.abc.Mapping[str, int] | None
m: collections.abc.Mapping[str, int | None] | float | None
pprint(get_type_hints(Foo, globals(), locals()))
gives:
{'a': typing.Dict[str, int],
'b': typing.List[int],
'c': typing.Set[int],
'd': typing.Union[typing.Tuple[int, ...], NoneType],
'e': typing.FrozenSet[int],
'f': typing.DefaultDict[str, int],
'g': typing.Union[str, NoneType],
'h': typing.Union[str, int],
'i': typing.Union[str, int, NoneType],
'j': <class 'str'>,
'k': typing.Mapping[str, int],
'l': typing.Union[typing.Mapping[str, int], NoneType],
'm': typing.Union[typing.Mapping[str, typing.Union[int, NoneType]], float, NoneType]}
instead of raising an error that type
object isn't subscriptable (Python 3.8)
or that GenericAlias
doesn't support the |
operator (Python 3.9).
Use case
Keep your codebase up-to-date by speeding up migration to modern types, even if you support Python versions >=3.8.
Stop using deprecated typing.Dict
, typing.List
, typing.Set
, typing.Tuple
, typing.FrozenSet
, typing.DefaultDict
and other typing
type proxies explicitly!
Importing __modern_types__
will make all typing.ForwardRef
-dependent parts of your application, including pydantic models, work flawlessly with PEP 585 and PEP 604.
Is __modern_types__
safe to use in production?
Yes. It doesn't break any existing codebase. It only uses AST and overwrites typing.ForwardRef._evaluate
.
__modern_types__
does not interact with the caller's namespaces, does not mutate built-in classes and does not do any other dubious things
that could potentially produce weird, unexpected side effects.
How to use?
[!Warning] Remember that the library does not change the built-in scope at runtime!
So
dict[str, int]
won't render at runtime, buttyping.Dict[str, int]
will.
__modern_types__
makes it possible to evaluatedict[str, int]
only through thetyping.get_type_hints
function.You should remember putting
from __future__ import annotations
at the top of your modules everywhere you want to leverage__modern_types__
.
Simply import __modern_types__
in your code, and it will make typing.ForwardRef
instances go through the
type hint expression AST to try to tweak the copy of the passed global/local namespace
to use typing._GenericAlias
instances that support []
and |
operators at runtime.
Example replacements taking place in the built-in scope:
Old type | New type | Without __modern_types__ , works on Python version... |
With __modern_types__ , works on Python version... |
Backports PEP |
---|---|---|---|---|
dict[KT, VT] |
typing.Dict[KT, VT] |
>=3.9 | >=3.8 | PEP 585 |
list[T] |
typing.List[T] |
>=3.9 | >=3.8 | PEP 585 |
set[T] |
typing.Set[T] |
>=3.9 | >=3.8 | PEP 585 |
tuple[T, ...] |
typing.Tuple[T, ...] |
>=3.9 | >=3.8 | PEP 585 |
frozenset[T] |
typing.FrozenSet[T] |
>=3.9 | >=3.8 | PEP 585 |
X | Y |
typing.Union[X, Y] |
>=3.10 | >=3.8 | PEP 604 |
Additionally, __modern_types__
also allows you to use collections.abc
and contextlib
generic classes.
[!Note] Some optional replacements will automatically also be registered if possible, according to those listed in the
__modern_types__._typeshed
source code.
Installation
You might simply install it with pip:
pip install modern-types
If you use Poetry, then run:
poetry add modern-types
For contributors
[!Note] If you use Windows, it is highly recommended to complete the installation in the way presented below through WSL2.
-
Fork the modern_types repository on GitHub.
-
Install Poetry.
Poetry is an amazing tool for managing dependencies & virtual environments, building packages and publishing them. You might use pipx to install it globally (recommended):pipx install poetry
If you encounter any problems, refer to the official documentation for the most up-to-date installation instructions.
Be sure to have Python 3.8 installed—if you use pyenv, simply run:
pyenv install 3.8
-
Clone your fork locally and install dependencies.
git clone https://github.com/your-username/modern_types path/to/modern_types cd path/to/modern_types poetry env use $(cat .python-version) poetry install
Next up, simply activate the virtual environment and install pre-commit hooks:
poetry shell pre-commit install --hook-type pre-commit --hook-type pre-push
For more information on how to contribute, check out CONTRIBUTING.md.
Always happy to accept contributions! ❤️
Legal info
© Copyright by Bartosz Sławecki (@bswck).
This software is licensed under the terms of MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for modern_types-2.0.8-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | aa5583fb18ce505941882fcc4871270e247fb27afb83aa092ba88618797e96cb |
|
MD5 | 4b35c1ffdb5b7dded747a3fbbfcca06a |
|
BLAKE2b-256 | a4e41ded53da92f4d6479957cd7dae3ad542277f4c266801535ab4e9b10a7ef0 |