Navigation

    La Cabane Libre

    • Register
    • Login
    • Search
    • Catégories
    • Recent
    • Mots-clés
    • Ciné Libre

    Python 3.10 est disponible

    Articles de Logiciels
    1
    1
    109
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • LinuxFR
      LinuxFR last edited by

      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

      • lien nᵒ 1 : Notes de versions

      Sommaire

      • PEP 623 − Préparation de la suppression de wstr dans PyUnicodeObject
      • 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
        • Utilisation d’un conteneur
        • Compilation depuis les sources
      • 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 dans PyUnicodeObject

      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 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 : 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 :

      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 attributs args et kwargs 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 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 : 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 dans setuptools 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 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 :

      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é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 :

      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 :

      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 : 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 : 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 : 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) :

      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 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 :

      >>> 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

      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

      $ 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 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.

      Télécharger ce contenu au format EPUB

      Commentaires : voir le flux Atom ouvrir dans le navigateur

      https://linuxfr.org/news/python-3-10-est-disponible

      1 Reply Last reply Reply Quote 0
      • First post
        Last post

      Sauf mention contraire, le site est placé sous double licence Creative Commons BY-SA et GNU Free Documentation License propulsé par NodeBB