URL: https://linuxfr.org/news/python-3-10-est-disponible Title: Python 3.10 est disponible Authors: Cyrille Pontvieux Snark, Anonyme, Ysabeau đŸ§¶, ariasuni, Yves Bourguignon, BenoĂźt Sibaud, bobble bubble, dourouc05, pamputt et palm123 Date: 2021ćčŽ04月30æ—„T13:19:22+02:00 License: CC By-SA Tags: python et sortie_version Score: 50 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... ![Logo Python](https://www.python.org/static/img/python-logo.png) ---- [Notes de version 3.10.0rc1](https://www.python.org/downloads/release/python-3100rc1/) [Notes de version 3.10.0](https://www.python.org/downloads/release/python-3100/) ---- 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` dans `PyUnicodeObject` [PEP 623](https://www.python.org/dev/peps/pep-0623/) : 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` et `wstr_length` avaient Ă©tĂ© introduits pour une meilleure compatibilitĂ© des extensions en `C` 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](https://www.python.org/dev/peps/pep-0604/) : avant, pour Ă©crire l’union de type en Python, il fallait Ă©crire `Union[X, Y]`. Il fallait donc importer `Union` de `typing` 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’objet `type`. Du coup on gagne aussi dans d’autres domaines, comme les Ă©critures suivantes : ```python isinstance(5, int | str) isinstance(None, int | None) ``` # PEP 612 − AmĂ©lioration du typage pour les dĂ©corateurs [PEP 612](https://www.python.org/dev/peps/pep-0612/) : la [PEP 484](https://www.python.org/dev/peps/pep-0484/) et la [PEP 544](https://www.python.org/dev/peps/pep-0544/) 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 attributs `args` et `kwargs` qui correspondent respectivement aux types des arguments positionnels et nommĂ©s. Exemple : ```python 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](https://www.python.org/dev/peps/pep-0626/) : les attributs `f_lineno` et `co_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Ă©thode `co_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](https://www.python.org/dev/peps/pep-0618/) : 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 : ```python>>> 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](https://bugs.python.org/issue12782) : 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`: ```python with (open("a_really_long_foo") as foo, open("a_really_long_bar") as bar): pass ``` ``` Traceback (most recent call last): File "", line 1, in 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: ```python 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](https://www.python.org/dev/peps/pep-0617/). # PEP 632 − DĂ©prĂ©ciation du module `distutils` [PEP 632](https://www.python.org/dev/peps/pep-0632/) : le module `distutils` est dĂ©prĂ©ciĂ© car une version est maintenant [intĂ©grĂ©e dans `setuptools`](https://github.com/pypa/setuptools/issues/417) 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](https://www.python.org/dev/peps/pep-0517/) qui veut construire un systĂšme de construction indĂ©pendant. # PEP 613 − Alias de types explicites [PEP 613](https://www.python.org/dev/peps/pep-0613/) : 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Ă© : ```python MonType = "ClassName" def foo() -> MonType: pass ``` Ici, il est difficile de savoir si `MonType` est une variable dont la valeur est une string `ClassName` ou si c’est un alias de type sur la classe `ClassName` 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 : ```python 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](https://www.python.org/dev/peps/pep-0634/), [PEP 635](https://www.python.org/dev/peps/pep-0635/) et [PEP 636](https://www.python.org/dev/peps/pep-0636/): [filtrage par motif](https://fr.wikipedia.org/wiki/Filtrage_par_motif) grĂące au mot-clĂ© `match`. Cette PEP a Ă©tĂ© divisĂ©e en trois parties : [dĂ©finition](https://www.python.org/dev/peps/pep-0634/), [justification](https://www.python.org/dev/peps/pep-0635/), [tutoriel](https://www.python.org/dev/peps/pep-0636/). 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Ă©s `match` et `case` (et copier ce qu’on peut trouver sur d’autres langages) Les mots-clĂ©s `match` et `case` 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 : ```python 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’expression `if 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 pour `True`, `False` et `None` ou c’est l’identitĂ© (`is`). Il est Ă©galement possible de comparer avec des classes en utilisant cette syntaxe : ```python 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Ă©rents `case`. 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 attribut `position` dont le motif correspond Ă  un tuple de deux Ă©lĂ©ments qui seront liĂ©s aux variables `x` et `y`. 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](https://www.python.org/dev/peps/pep-0644/) : `OpenSSL` (version `1.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` et `1.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 pour `libressl` 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 utilisent `LibreSSL` devraient probablement avoir migrĂ© sur `OpenSSL` d’ici Ă  ce que Python 3.10 sorte. # PEP 624 − Retrait des API d’encodage `Py_UNICODE` [PEP 624](https://www.python.org/dev/peps/pep-0624/) : 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` optionnel [PEP 597](https://www.python.org/dev/peps/pep-0597/) : 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’environnement `LANG` 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Ăštre `encoding` 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’environnement `PYTHONWARNDEFAULTENCODING` Ă  `1`. Illustration de code correct (ne gĂ©nĂ©rant pas le warning) : ```python 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](https://bugs.python.org/issue38605) : 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](https://www.python.org/dev/peps/pep-0563/) 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 avec `get_type_hints` de `typing`. Les annotations sont toujours prĂ©sentĂ©es comme du code python valide. On peut par exemple les lire Ă  l’exĂ©cution comme suit : ```python>>> 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’info](https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/) # Pour 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 ```sh $ 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) : ```sh $ 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 paquet `numpy`, ainsi que `pandas` 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 des `wheels` 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.

AltStyle ă«ă‚ˆăŁăŠć€‰æ›ă•ă‚ŒăŸăƒšăƒŒă‚ž (->ă‚ȘăƒȘă‚žăƒŠăƒ«) /