Bonjour mes amis. Et nous continuons d'augmenter l'intensité du lancement de nouveaux cours et nous sommes heureux d'annoncer que les cours du cours
"Web-développeur en Python" commenceront fin avril. À cet égard, nous partageons traditionnellement la traduction de documents utiles. Commençons.
Python est connu pour être un langage de frappe dynamique. Il est très facile d'écrire des frameworks de type DSL qui sont difficiles à analyser avec des outils de vérification de type statique. Malgré cela, avec
les dernières innovations fonctionnelles de
mypy , telles que les
protocoles et
les types littéraux , ainsi que la prise en charge de base des métaclasses et la prise en charge des descripteurs, nous pouvons souvent obtenir des types exacts, mais il est toujours difficile d'éviter les faux positifs et autres facteurs négatifs. Pour résoudre ce problème et éviter d'avoir à personnaliser le système de type pour chaque framework,
mypy prend en charge un système de
plug-in . Les plugins sont des modules en Python qui fournissent des hooks de plugins que
mypy appellera lors de la vérification des types de classes et de fonctions qui interagissent avec une bibliothèque ou un framework. Ainsi, il est possible de distinguer plus précisément le type de la fonction retournée, qui est autrement extrêmement difficile à exprimer, ou de générer automatiquement certaines méthodes de classe pour refléter les effets du décorateur. Pour en savoir plus sur l'architecture du système de plug-in et voir la liste complète des fonctionnalités, consultez la
documentation .
Plugins associés pour la bibliothèque standardMypy est livré avec des plugins par défaut pour implémenter des fonctions et classes de base, ainsi que des
ctypes
,
contextlib
et
dataclasses
. Il comprend également des plugins pour
attrs
(il a toujours été le premier plugin tiers écrit pour
mypy ). Ces plugins permettent à
mypy de déterminer plus précisément les types et de vérifier correctement le code pour le type à l'aide de ces fonctions de bibliothèque. Pour le montrer avec un exemple, jetez un œil à un extrait de code:
from dataclasses import dataclass from typing import Generic, TypeVar @dataclass class TaggedVector(Generic[T]): data: List[T] tag: str position = TaggedVector([0, 0, 0], 'origin')
Ci-dessus,
get_class_decorator_hook()
est appelé lorsque la classe est définie. Cela ajoute des méthodes générées automatiquement, y compris
__init__()
, au corps de la fonction.
Mypy utilise un tel constructeur pour calculer correctement
TaggedVector[int]
comme type de
position
. Comme vous pouvez le voir dans l'exemple, les plugins fonctionnent même avec des classes génériques.
Voici un autre morceau de code:
from contextlib import contextmanager @contextmanager def timer(title: str) -> Iterator[float]: ... with timer(9000) as tm: ...
Ici,
get_function_hook()
fournit le type de retour exact pour le décorateur
contextmanager
, donc les appels à la fonction décorée peuvent être vérifiés pour la conformité avec un type spécifique. Maintenant,
mypy peut reconnaître l'erreur: l'argument de
timer()
doit être une chaîne.
Une combinaison de plugins et de stubsEn plus d'utiliser des fonctions Python dynamiques, les frameworks rencontrent souvent le problème d'avoir de grandes API.
Mypy a besoin de fichiers de
raccord pour les bibliothèques afin de vérifier le code qui utilise ces bibliothèques (uniquement si la bibliothèque ne contient pas d'annotations intégrées, ce qui n'est pas si courant). La distribution de stubs pour les grands frameworks avec
typage n'est pas une pratique courante:
- Typeshed a un cycle de libération relativement lent (livré avec mypy ).
- Les talons incomplets peuvent entraîner de faux appels, ce qui sera extrêmement difficile à éviter.
- Ne vous contentez pas de mélanger des talons de différentes versions de typeshed .
Les packages de stub introduits dans
PEP 561 procèdent comme suit:
- Les développeurs peuvent publier des packages de stub aussi souvent qu'ils le souhaitent.
- Les utilisateurs qui n'ont pas choisi d'utiliser le package ne verront pas de faux positifs.
- Vous pouvez installer en toute sécurité des versions arbitraires de plusieurs packages de talon différents.
De plus,
pip
vous permet de combiner différents stubs pour les bibliothèques et les plugins
mypy correspondants en une seule distribution. Les stubs pour le framework
mypy ou le plugin correspondant peuvent être facilement développés et regroupés en une seule distribution, ce qui est extrêmement utile car les plugins remplissent des définitions manquantes ou inexactes dans les stubs.
Le dernier exemple d'un tel package est les
plugins et plugins SQLAlchemy , avec la première version publique de la version 0.1, qui a été publiée il y a quelque temps sur PyPI. Malgré le fait que ce projet soit dans la première version Alpha, nous pouvons l'utiliser en toute sécurité dans DropBox pour améliorer la vérification de type. Le plugin comprend les déclarations ORM de base:
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String)
Dans l'extrait de code ci-dessus, le plugin utilise
get_dynamic_class_hook()
pour dire à
mypy que Base est une classe de base valide, même si elle ne lui ressemble pas. Ensuite,
get_base_class_hook()
est appelé pour définir l'utilisateur et ajoute plusieurs attributs générés automatiquement. Ensuite, nous créons une instance du modèle:
user = User(id=42, name=42)
get_function_hook()
appelé, donc
mypy peut indiquer une erreur: une valeur
integer
est reçue à la place du nom d'utilisateur.
Les stubs définissent la
Column
comme un descripteur
générique , de sorte que les attributs du modèle obtiennent les types corrects:
id_col = User.id
Nous accueillons les RP qui ajoutent des types plus précis aux stubs (la progression des modules de base est suivie
ici ).
Voici quelques pièges que nous avons découverts en travaillant sur des plugs:
- Utilisez
__getattr__()
pour éviter les faux positifs dans les premiers stades lorsque les stubs ne sont pas terminés (cela empêche les erreurs Mypy si les attributs du module sont manquants). Vous pouvez également l'utiliser dans les fichiers __init__.py
si des sous-modules manquent. - Les descripteurs aident souvent à des définitions de type plus précises pour l'accès aux attributs personnalisés (comme dans l'exemple de colonne que nous avons examiné ci-dessus). L'utilisation de descripteurs est correcte même si l'implémentation réelle du runtime utilise un mécanisme plus complexe, notamment une métaclasse, par exemple.
- Sans hésitation, déclarez les classes du framework comme généralisées. Malgré le fait qu'ils ne le sont pas au moment de l'exécution, cette technique vous permet de déterminer plus précisément le type de certains éléments du framework, tandis que les erreurs d'exécution peuvent être facilement contournées . (Nous espérons que les frameworks ajouteront progressivement un support intégré pour les types génériques, héritant explicitement des classes correspondantes de
typing.Generic
.)
Plugins mypy récemment sortisIl existe déjà plusieurs plugins disponibles pour les frameworks Python populaires. Outre le plugin
SQLAlchemy mentionné ci-dessus, d'autres exemples de packages avec des stubs et le plugin
mypy intégré incluent des
stubs pour les interfaces
Django et
Zope . Un travail actif est en cours sur ces projets.
Installation et connexion des packages de stub et de pluginUtilisez pip pour installer le module d'
extension pour
mypy et / ou stub dans un environnement virtuel où
mypy est déjà
installé :
$ pip install sqlalchemy-stubs
Mypy détectera automatiquement les talons installés. Pour connecter les plugins installés, incluez-les directement dans mypy.ini (ou dans le fichier de configuration utilisateur):
[mypy] plugins = sqlmypy, mypy_django_plugin.main
Développement de plugins
mypy et écriture de stubs
Si vous souhaitez développer un package de stubs et de plugins pour le framework que vous utilisez, nous pouvons utiliser
le référentiel sqlalchemy-stubs comme modèle. Il comprend le
setup.py
, le test de l'infrastructure à l'aide de tests pilotés par les données et un exemple de classe de plug-in avec un ensemble de hooks pour le plug-in (plug-in hooks). Nous vous recommandons d'utiliser
stubgen pour générer automatiquement les
stubs fournis avec
mypy pour commencer à les utiliser.
Stubgen
s'est un
mypy 0.670
amélioré dans
mypy 0.670
.
Consultez la
documentation si vous souhaitez en savoir plus sur le
système de plugin
mypy . Vous pouvez également rechercher sur Internet les codes sources des plugins abordés dans l'article. Si vous avez des questions, vous pouvez les poser
ici .
Le 15 avril sera un
webinaire ouvert gratuit sur le cours, qui sera organisé par l'un des organisateurs de la communauté Python de Moscou -
Vladimir Filonov , inscrivez-vous, ce sera intéressant. Et maintenant, nous attendons vos commentaires sur le matériel traduit.