Python 3.10 est disponible
-
Python 3.10 (rc1) est sorti le 2 août 2021, après quinze mois de développement (dont cinq à cheval sur les bêta et les rc de Python 3.9). Il reste deux mois avec des candidats (RC) avant la sortie définitive, prévue le 4 octobre 2021.
Voyons ce que cette version apporte comme nouveautés…
- lien nᵒ 1 : Notes de versions
Sommaire
- PEP 623 − Préparation de la suppression de
wstr
dansPyUnicodeObject
- PEP 604 − Écrire les unions de types plus facilement
- PEP 612 − Amélioration du typage pour les décorateurs
- PEP 626 − Numéro de ligne précis pour les débugueurs et autres outils
- PEP 618 − Ajout d’une vérification optionnelle des longueurs dans
zip
- Bug 12782 − Groupement des gestionnaires de contexte avec des parenthèses
- PEP 632 − Dépréciation du module
distutils
- PEP 613 − Alias de types explicites
- PEP 634/635/636 − Ajout du mot-clé
match
pour faire du filtrage par motif. - PEP 644 − OpenSSL 1.1.1 ou plus récent est maintenant requis
- PEP 624 − Retrait des API d’encodage
Py_UNICODE
- PEP 597 − Ajout d’un
EncodingWarning
optionnel - Bug 38605 − Évaluation retardée des annotations (non-inclus dans Python 3.10)
- Pour tester
- Compatibilité
Cette version comporte pas moins de onze nouveautés et une correction par rapport à la version 3.9 sortie le 5 octobre 2020.
Une correction (changement de défaut) n’a finalement pas été incluse dans cette version.PEP 623 − Préparation de la suppression de
wstr
dansPyUnicodeObject
PEP 623 : il n’y a plus aucune raison de garder la compatibilité avec les chaînes à larges caractères (wide string) dans l’implémentation Unicode de Python.
En effet,
wstr
etwstr_length
avaient été introduits pour une meilleure compatibilité des extensions enC
pour Python 2 et 3. Vu que Python 2 est maintenant déclaré mort, il n’y a plus besoin de garder ces API qui seront marquées comme dépréciées à partir de Python 3.10 et supprimées en version 3.12.Le gain en mémoire est de 8 octets par chaîne de caractères, ce qui n’est pas rien.
PEP 604 − Écrire les unions de types plus facilement
PEP 604 : avant, pour écrire l’union de type en Python, il fallait écrire
Union[X, Y]
.Il fallait donc importer
Union
detyping
pour pouvoir faire une union, et la lecture n’était pas limpide.Cette proposition permet d’écrire
X | Y
, ce qui apporte à la fois les avantages d’une lecture simplifiée, et d’un import de moins.Cela parait simple au niveau grammaire, mais pour que ça fonctionne au niveau du code, il a fallu ajouter la méthode magique
__or__
à la super-classe/l’objettype
.Du coup on gagne aussi dans d’autres domaines, comme les écritures suivantes :
isinstance(5, int | str) isinstance(None, int | None)
PEP 612 − Amélioration du typage pour les décorateurs
PEP 612 : la PEP 484 et la PEP 544 permettent de décrire des fonctions de retour d’appel (callback).
Sur un décorateur, en définissant un
TypeVar
, on peut définir une valeur de retour pour le décorateur qui correspond au type de la fonction décorée. Il n’était toutefois pas possible de faire la même chose avec les arguments.La PEP 612 comble ce manque en introduisant
ParamSpec
qui englobe la définition d’arguments.ParamSpec
contient notamment les attributsargs
etkwargs
qui correspondent respectivement aux types des arguments positionnels et nommés.Exemple :
from typing import Awaitable, Callable, ParamSpec, TypeVar P = ParamSpec("P") R = TypeVar("R") def add_logging(f: Callable[P, R]) -> Callable[P, Awaitable[R]]: async def inner(*args: P.args, **kwargs: P.kwargs) -> R: await log_to_database() return f(*args, **kwargs) return inner @add_logging def takes_int_str(x: int, y: str) -> int: return x + 7 await takes_int_str(1, "A") # accepté await takes_int_str("B", 2) # rejeté par le vérifieur de type
PEP 626 − Numéro de ligne précis pour les débugueurs et autres outils
PEP 626 : les attributs
f_lineno
etco_lnotab
d’une pile (frame) d’exécution ne donnent pas toujours le bon numéro de ligne du code source dans certains cas.La correction consiste à marquer des bytecodes comme virtuels et n’ayant pas de ligne dans le code source.
Afin de garantir la compatibilité avec les outils existants, rien n’est modifié sur
co_lnotab
mais une nouvelle méthodeco_lines()
est disponible.co_lnotab
sera donc toujours disponible mais généré à la demande.PEP 618 − Ajout d’une vérification optionnelle des longueurs dans
zip
PEP 618 : sécurisation optionnelle de l’utilisation de la fonction
zip
.La fonction
zip
permet d’itérer sur plusieurs séquences ou générateurs à la fois et de retourner un tuple de valeurs des différents itérateurs.Si un itérateur n’a plus d’élément à itérer,
zip
s’arrête sans erreur, même si un autre itérateur a encore des éléments à itérer. Cette PEP rajoute une option pour déclencher une erreur dans une telle situation.Exemple :
>>> list(zip(range(3), range(2, 5))) [(0, 2), (1, 3), (2, 4)] >>> list(zip(range(3), range(2, 4))) [(0, 2), (1, 3)] >>> list(zip(range(3), range(2, 4), strict=True)) ValueError: zip() argument 2 is shorter than argument 1
Bug 12782 − Groupement des gestionnaires de contexte avec des parenthèses
BPO 12782 : grouper des gestionnaires de contexte avec des parenthèses est désormais officiellement autorisé.
Contrairement aux autres mots-clés, il n’était pas possible d’utiliser les parenthèses avec
with
:with (open("a_really_long_foo") as foo, open("a_really_long_bar") as bar): pass Traceback (most recent call last): File "<input>", line 1, in <module> File "demo.py", line 19 with (open("a_really_long_foo") as foo, ^ SyntaxError: invalid syntax
Il fallait s’en remettre aux
\
pour sauter des lignes:with open("a_really_long_foo") as foo, \ open("a_really_long_bar") as bar: pass
Désormais, la première syntaxe sera valide, comme on peut légitimement s’y attendre, renforçant la cohérence du langage. Cette fonctionnalité a pu être introduite facilement grâce au nouveau PEG parser ajouté dans Python 3.9.
PEP 632 − Dépréciation du module
distutils
PEP 632 : le module
distutils
est déprécié car une version est maintenant intégrée danssetuptools
qui ne dépend plus de la librairie standard de Python.distutils
n’était pas très documenté ni maintenu dans la librairie standard de toutes façons.Ça laisse également le champ libre pour la PEP 517 qui veut construire un système de construction indépendant.
PEP 613 − Alias de types explicites
PEP 613 : ajout d’un type « Alias de type » pour permettre de définir les alias de type de manière explicite.
Jusqu’à présent la différence entre une affectation d’une valeur à une variable et d’une définition d’alias de type était assez ténue et ambiguë.
Exemple d’ambiguïté :
MonType = "ClassName" def foo() -> MonType: pass
Ici, il est difficile de savoir si
MonType
est une variable dont la valeur est une stringClassName
ou si c’est un alias de type sur la classeClassName
qui sera définie plus tard. Les vérifieurs de types se trompant souvent et rapportant de fausses erreurs, il a fallu se rendre à l’évidence : cette notation n’est pas assez explicite.Cette PEP, propose :
MonType: TypeAlias = "ClassName" def foo() -> MonType: pass
Là, plus d’ambiguïté.
PEP 634/635/636 − Ajout du mot-clé
match
pour faire du filtrage par motif.PEP 634, PEP 635 et PEP 636: filtrage par motif grâce au mot-clé
match
.Cette PEP a été divisée en trois parties : définition, justification, tutoriel. La lecture du tutoriel est un bon point de départ pour lire ces PEP.
L’amélioration vient du constat suivant : il est souvent nécessaire de faire une suite de
if … elif … elif … else
qui concerne un même élément. Ces tests peuvent porter sur du duck-typing (est-ce que cet objet couac comme un canard), sur de l’héritage, sur une liste de valeurs (comparaison sur des strings ou des numériques par exemple), sur une taille de séquence (liste, tuple) et d’autres tests.Cela fonctionne très bien avec des
if
mais c’est pas toujours super lisible parfois, et comme ce schéma se répète souvent dans les projets Python, il a été décidé d’aider les développeurs (ceux qui écrivent mais surtout ceux qui lisent) avec l’aide des mots-clésmatch
etcase
(et copier ce qu’on peut trouver sur d’autres langages)Les mots-clés
match
etcase
ne sont pas des termes réservés, on peut donc toujours les utiliser dans des noms de variables, le parseur faisant la différence de construction. Le code pré-python-3.10 reste donc compatible. Python parle de soft keywords.Le concept de filtrage par motifs s’explique par le fait que la variable (ou l’expression) à tester est comparée à des motifs et permet de sélectionner exclusivement un bloc de code.
Exemple de code gérant un jeu de rôle par entrée textuelle :
command = input("Que voulez-vous faire ? ") match command.split(): case ["quitter"]: print("Au revoir !") quit_game() case ["regarder"] | ["scruter"]: current_room.describe() case ["aller", "côté", ("Nord" | "Sud" | "Est" | "Ouest") as direction] | [("Nord" | "Sud" | "Est" | "Ouest") as direction] if direction in current_room.exits: current_room = current_room.neighbor(direction) case [("Nord" | "Sud" | "Est" | "Ouest")] | [_]: print("Désolé, il n’y a pas de sortie dans cette direction") case ["prendre", obj] | ["ramasser", obj] | ["mettre", obj, "dans", "le", "sac"]: character.get(obj, current_room) case ["lâcher", *objects]: for obj in objects: character.drop(obj, current_room) case _: print(f"Désolé, je ne comprends pas ce que veut dire {command!r}")
On peut comprendre le code en un clin d’œil, ce qui ne serait pas forcément le cas avec des
if
. De plus on peut voir que la syntaxe de déconstruction permet de valider le format attendu, mais également de récupérer des variables liées (bind en anglais). Si des alternatives sont présentes dans le motif, alors chaque alternative doit lier les mêmes variables. L’utilisation de l’expressionif condition
après le motif permet de garder le bloc sous une condition spéciale, supplémentaire au motif, et évaluée après la correspondance au motif et après les bind.Enfin l’utilisation de la variable
_
permet de définir des cas par défaut. En effet il est de convention en Python d’utiliser_
pour définir une variable que l’on ne va pas utiliser et qui sert de variable fictive (placeholder).On peut également utiliser la notation avec une ou deux étoiles pour récupérer une liste d’éléments ou un dictionnaire. Notations
*lst
ou**d
comme on peut le faire dans la syntaxe de déconstruction classique.Les comparaisons de motifs peuvent se faire en profondeur. L’égalité (
==
) est utilisée pour comparer les objets et littéraux, sauf pourTrue
,False
etNone
ou c’est l’identité (is
).Il est également possible de comparer avec des classes en utilisant cette syntaxe :
match event.get(): case Click(position=(x, y)): handle_click_at(x, y) case KeyPress(key_name="Q") | Quit(): game.quit() case KeyPress(key_name="up arrow"): game.go_north() ... case KeyPress(): pass # Ignore other keystrokes case other_event: raise ValueError(f"Unrecognized event: {other_event}")
Ici on va vérifier le type de retour de
event.get()
avec les types d’objets indiqués dans les différentscase
. Attention, même si la syntaxe donne l’impression que des objets sont créés (Click(position=(x, y))
,Quit()
) il n’en est rien.Ce qui est donné entre parenthèses restreint le motif à une classe qui hérite de la classe indiquée (exemple
Click
) et qui contient un attributposition
dont le motif correspond à un tuple de deux éléments qui seront liés aux variablesx
ety
.On peut bien évidemment mixer tous ces types de motifs. L’expressivité créée par cette PEP est énorme et des tests menés par Python montre une meilleure lisibilité sur une grosse partie du code source des modules Python et une réduction du nombre de lignes.
Bref, après des années de demande pour avoir un
switch/case
en Python, en voilà un sous stéroïdes !PEP 644 − OpenSSL 1.1.1 ou plus récent est maintenant requis
PEP 644 :
OpenSSL
(version1.1.1
ou supérieure) sera nécessaire pour Python 3.10.Il existe trop de différences entre les versions d’
openssl
(1.0.2
,1.1.0
et1.1.1
) pour que les deux experts qui gèrent ça chez Python puissent fournir un code compatible avec toutes. C’est la même raison pourlibressl
qui ne sera plus pris en charge à partir de Python 3.10.Pour résumer, seule l’API d’
openssl 1.1.1
est prise en charge.Ça ne devrait pas trop impacter les distributions selon la PEP 644 car
openssl 1.1.1
est la version par défaut sur la plupart des plateformes et distributions (Linux et BSD). Les rares distributions qui utilisentLibreSSL
devraient probablement avoir migré surOpenSSL
d’ici à ce que Python 3.10 sorte.PEP 624 − Retrait des API d’encodage
Py_UNICODE
PEP 624 : planification du retrait des API inutilisée
PyUnicode_*
pour Python 3.11.Cette PEP sort donc pour Python 3.10 mais n’aura d’effet que pour la version suivante.
PEP 597 − Ajout d’un
EncodingWarning
optionnelPEP 597 : ajout d’un
EncodingWarning
optionnel lors de l’ouverture d’un fichier en mode texte.En effet sous Linux, BSD et MacOS, l’encodage par défaut des fichiers est très généralement
UTF-8
. Mais ce n’est pas toujours le cas. Quand on ouvre un fichier en mode texte, c’est l’encodage par défaut du système qui est utilisé (qui peut être surchargé avec la variable d’environnementLANG
par exemple).On peut préciser/forcer l’encodage à utiliser pour les caractères/octets en le spécifiant dans un paramètre
encoding
. Celui-ci existe depuis fort longtemps, mais force est de constater que la plupart des bibliothèques et codes en python ne le précisent pas.Cette PEP vise à ajouter un avertissement
EncodingWarning
quand le paramètreencoding
n’a pas été précisé pour un fichier ouvert en mode texte.Afin d’activer cet avertissement, il faut invoquer Python avec les paramètres
-X warn_default_encoding
ou bien définir la variable d’environnementPYTHONWARNDEFAULTENCODING
à1
.Illustration de code correct (ne générant pas le warning) :
with open(`my_file.txt`, encoding='utf-8') as f: content = f.read()
–
Bug 38605 − Évaluation retardée des annotations (non-inclus dans Python 3.10)
BPO 38605 : l’évaluation retardée des annotations, qui est disponible depuis Python 3.7 en utilisant une ligne
from __future__ import annotations
est maintenant disponible par défaut.Pour rappel, la PEP 563 permet d’évaluer les annotations au lancement et pas simplement lors de la définition/compilation.
Les annotations sont présentes dans
__annotations__
. On peut les lire avecget_type_hints
detyping
. Les annotations sont toujours présentées comme du code python valide.On peut par exemple les lire à l’exécution comme suit :
>>> import typing >>> def x(a: 'int'): pass >>> typing.get_type_hints(x) {'a': ForwardRef('int')}
** /!\ Cette modification a été repoussée à Python 3.11 en raison de certaines incompatibilités **
Plus d’infoPour tester
Pas envie d’attendre que ce soit disponible dans votre distribution ? Deux options : utiliser un conteneur ou compiler depuis les sources.
Utilisation d’un conteneur
$ docker run --rm -it python:3.10.0rc1-slim
Compilation depuis les sources
En fait c’est pas si compliqué du tout et ça se compile assez vite, donc c’est une option viable, même pour tester.
Dépendances nécessaires :
expat
,bzip2
,gdbm
,openssl
,libffi
,zlib
,tk
,sqlite
,bluez-libs
,mpdecimal
Sous Debian/Ubuntu c’est :expat libbz2-dev liblzma-dev libgdbm-dev libdb5.3-dev libdb5.3++-dev libssl-dev libffi-dev zlib1g-dev tk-dev libsqlite3-dev libbluetooth-dev libncurses-dev libreadline-dev
Ensuite il suffit de récupérer le tarball et de lancer la compilation et l’installation (c’est tiré du paquet AUR sur ArchLinux) :
$ wget 'https://www.python.org/ftp/python/3.10.0/Python-3.10.0rc1.tar.xz' $ tar xf Python-3.10.0rc1.tar.xz $ cd Python-3.10.0rc1 $ rm -rf Modules/{expat,zlib} Modules/_ctypes/{darwin,libffi}* $ # Vous pouvez vous passer de enable-optimizations pour compiler plus vite si vous voulez $ ./configure --prefix=/usr \ --enable-shared \ --with-computed-gotos \ --enable-ipv6 \ --with-system-expat \ --enable-loadable-sqlite-extensions \ --without-ensurepip \ --enable-optimizations $ make $ mkdir -p pkg $ sudo make DESTDIR="$PWD/pkg" altinstall maninstall $ sudo rm -f "$PWD/pkg/usr/lib/libpython3.so" "$PWD/pkg/usr/share/man/man1/python3.1" $ sudo ln -sf ../../libpython3.10m.so \ "$PWD/pkg/usr/lib/python3.10/config-3.10-$(uname -m)-linux-gnu/libpython3.10m.so" $ sudo ln -sf python3.10m-config "$PWD/pkg/usr/bin/python3.10-config" $ sudo install -dm755 "$PWD/pkg/usr/lib/python3.10/Tools"/{i18n,scripts} $ sudo install -m755 Tools/i18n/{msgfmt,pygettext}.py "$PWD/pkg/usr/lib/python3.10/Tools/i18n/" $ sudo install -m755 Tools/scripts/{README,*py} "$PWD/pkg/usr/lib/python3.10/Tools/scripts/" $ sudo cp -a pkg/* / $ python3.10
Compatibilité
Python 3.10 est grandement compatible avec les versions précédentes. Ça devrait donc être très facile de passer à cette version pour votre projet.
Il y a toutefois quelques limitations temporaires :- Certains paquets Python compilés ne fournissent par encore de
wheel
(paquets binaires) pour Python 3.10 et donc l’installation d’un tel paquet va demander une compilation.
Le paquetnumpy
, ainsi quepandas
qui en dépend, fait partie de cette catégorie. La compilation peut s’avérer compliquée si vous êtes sur une distribution qui fait du morcelage de paquets (-dev
,-doc
, …). Il vous faudra alors installer une myriade de paquets systèmes (en plus du compilateur) pour pouvoir installer votre paquet python compilé.
Donc changer uniquement votre environnement en Python 3.10 n’est pas forcément suffisant. Ceci est bien sûr temporaire et les auteurs de ces bibliothèques fourniront pour sûr deswheels
prochainement. - Certains paquets Python n’ont pas pensé que la version mineure de Python pouvait tenir sur deux chiffres au lieu d’un.
Je pense notamment àuwsgi
. Mais c’est corrigé sur la branche principale et une version va voir le jour.
Ce ne sont que de petites contraintes qui vont se corriger d’ici que la finale sorte début octobre, mais en attendant, si vous voulez tester/anticiper, vous savez à quoi vous attendre.
Télécharger ce contenu au format EPUB
Commentaires : voir le flux Atom ouvrir dans le navigateur
Sauf mention contraire, le site est placé sous double licence Creative Commons BY-SA et GNU Free Documentation License propulsé par NodeBB