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.Optional[Tuple[int, ...]],
"e": typing.FrozenSet[int],
"f": typing.DefaultDict[str, int],
"g": typing.Optional[str],
"h": typing.Union[str, int],
"i": typing.Optional[typing.Union[str, int]],
"j": str,
"k": typing.Mapping[str, int],
"l": typing.Optional[typing.Mapping[str, int]],
"m": typing.Union[typing.Mapping[str, typing.Optional[int]], float, None]}
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
and typing.DefaultDict
!
Importing __modern_types__
will make all typing.ForwardRef
-dependent parts of your application, including pydantic models, work 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.
ProTip: How to subclass built-in generic classes in Python 3.8?
Supposing you are subclassing dict
, you could write
from __future__ import annotations
from functools import partial
from typing import TypeVar
from __modern_types__ import PEP604GenericAlias
KT = TypeVar("KT")
VT = TypeVar("VT")
@partial(PEP604GenericAlias, params=(KT, VT))
class YourDictSubclass(dict):
pass
so that YourDictSubclass[str, int]
, for instance, could be used as an evaluable type annotation.
If you need an API that simplifies this, please submit an issue so it has a reason to become a feature.
Installation
If you want to…
…use this tool in your project 💻
You might simply install it with pip:
pip install modern-types
If you use Poetry, then run:
poetry add modern-types
…contribute to modern_types 🚀
[!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.4-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | d47bd60b54430da006a9aee035e0cb491ce9113969cbc6eb4e73334f6ca9b51a |
|
MD5 | 717a434041c70522f210bf6f6868d5c1 |
|
BLAKE2b-256 | d109fcbf65f61e7bd6444309e927ca7a1ba2f55a573ae7b8f5b513133f955afd |