Mypy-Erweiterung mit Plugins

Guten Tag, Freunde. Wir erhöhen weiterhin die Intensität des Starts neuer Kurse und freuen uns, Ihnen mitteilen zu können, dass die Kurse des Kurses "Webentwickler in Python" Ende April beginnen werden. In dieser Hinsicht teilen wir traditionell die Übersetzung von nützlichem Material. Fangen wir an.

Python ist als dynamische Schreibsprache bekannt. Es ist sehr einfach, DSL-ähnliche Frameworks zu schreiben, die mit statischen Typprüfungstools nur schwer zu analysieren sind. Trotzdem können wir mit den neuesten funktionalen Innovationen von mypy wie Protokollen und Literaltypen sowie der grundlegenden Unterstützung für Metaklassen und Deskriptorunterstützung häufig genaue Typen erhalten, aber es ist immer noch schwierig, falsch positive und andere negative Faktoren zu vermeiden. Um dieses Problem zu lösen und zu vermeiden, dass das Typsystem für jedes Framework angepasst werden muss, unterstützt mypy ein Plug-In- System. Plugins sind Module in Python, die Plugin-Hooks bereitstellen, die mypy aufruft , wenn die Arten von Klassen und Funktionen überprüft werden, die mit einer Bibliothek oder einem Framework interagieren. Somit ist es möglich, den Typ der zurückgegebenen Funktion, der ansonsten äußerst schwierig auszudrücken ist, genauer zu unterscheiden oder automatisch einige Klassenmethoden zu generieren, um die Auswirkungen des Dekorateurs widerzuspiegeln. Weitere Informationen zur Architektur des Plug-In-Systems und die vollständige Liste der Funktionen finden Sie in der Dokumentation .



Verwandte Plugins für die Standardbibliothek

Mypy enthält Standard-Plugins zum Implementieren grundlegender Funktionen und Klassen sowie dataclasses für ctypes , dataclasses und dataclasses . Es enthält auch Plugins für attrs (es war in der Vergangenheit das erste Plugin eines Drittanbieters, das für mypy geschrieben wurde ). Mit diesen Plugins kann mypy mithilfe dieser Bibliotheksfunktionen Typen genauer bestimmen und den Code korrekt auf Typ überprüfen. Um dies anhand eines Beispiels zu zeigen, sehen Sie sich ein Code-Snippet an:

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

Oben wird get_class_decorator_hook() aufgerufen, wenn die Klasse definiert wird. Dadurch werden dem Funktionskörper automatisch generierte Methoden __init__() , einschließlich __init__() . Mypy verwendet einen solchen Konstruktor, um TaggedVector[int] als position korrekt zu berechnen. Wie Sie dem Beispiel entnehmen können, funktionieren Plugins auch mit generischen Klassen.

Hier ist ein weiterer Code:

 from contextlib import contextmanager @contextmanager def timer(title: str) -> Iterator[float]: ... with timer(9000) as tm: ... 

Hier gibt get_function_hook() den genauen Rückgabetyp für den contextmanager Dekorator an, sodass Aufrufe der dekorierten Funktion auf Übereinstimmung mit einem bestimmten Typ überprüft werden können. Jetzt kann mypy den Fehler erkennen: Das Argument für timer() sollte eine Zeichenfolge sein.

Eine Kombination aus Plugins und Stubs

Neben der Verwendung dynamischer Python-Funktionen stoßen Frameworks häufig auf das Problem großer APIs. Mypy benötigt Stub- Dateien für Bibliotheken, um den Code zu testen, der diese Bibliotheken verwendet (nur wenn die Bibliothek keine integrierten Anmerkungen enthält, was nicht so häufig vorkommt). Das Verteilen von Stubs für große Frameworks mit typisierten Daten ist keine gängige Praxis:

  • Typeshed hat einen relativ langsamen Release-Zyklus (im Lieferumfang von mypy enthalten ).
  • Unvollständige Stubs können zu falschen Anrufen führen, die äußerst schwer zu vermeiden sind.
  • Mischen Sie nicht nur Stubs aus verschiedenen typisierten Versionen.

In PEP 561 eingeführte Stub-Pakete führen Folgendes aus:

  • Entwickler können Stub-Pakete so oft veröffentlichen, wie sie möchten.
  • Benutzer, die sich nicht für die Verwendung des Pakets entschieden haben, sehen keine Fehlalarme.
  • Sie können beliebige Versionen mehrerer verschiedener Stub-Pakete sicher installieren.

Darüber hinaus können Sie mit pip verschiedene Stubs für Bibliotheken und die entsprechenden mypy- Plugins in einer Distribution kombinieren. Stubs für das mypy- Framework oder das entsprechende Plugin können einfach entwickelt und zu einer Distribution zusammengefasst werden. Dies ist äußerst nützlich, da Plugins fehlende oder ungenaue Definitionen in Stubs ausfüllen.

Das neueste Beispiel für ein solches Paket sind SQLAlchemy-Stubs und -Plugins mit der ersten öffentlichen Version von Version 0.1, die vor einiger Zeit auf PyPI veröffentlicht wurde. Trotz der Tatsache, dass dieses Projekt in der frühen Alpha-Version vorliegt, können wir es sicher in DropBox verwenden, um die Typprüfung zu verbessern. Das Plugin versteht die grundlegenden ORM-Deklarationen:

 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) 

Im obigen Code-Snippet verwendet das Plugin get_dynamic_class_hook() , um mypy mitzuteilen, dass Base eine gültige Basisklasse ist, auch wenn es nicht so aussieht. Anschließend wird get_base_class_hook() aufgerufen, um den Benutzer zu definieren, und es werden mehrere automatisch generierte Attribute get_base_class_hook() . Als Nächstes erstellen wir eine Instanz des Modells:

user = User(id=42, name=42)

get_function_hook() aufgerufen, daher kann mypy einen Fehler anzeigen: Anstelle des Benutzernamens wird ein integer Wert empfangen.

Stubs definieren Column als generischen Deskriptor, damit die Modellattribute die richtigen Typen erhalten:

 id_col = User.id # Inferred type is "Column[int]" name = user.name # Inferred type is "Optional[str]" 

Wir begrüßen PRs, die Stubs präzisere Typen hinzufügen (der Fortschritt für Kernmodule wird hier verfolgt).

Hier sind einige Fallstricke, die wir bei der Arbeit an Steckern entdeckt haben:

  • Verwenden Sie __getattr__() , um Fehlalarme in den frühen Phasen zu vermeiden, wenn Stubs nicht abgeschlossen sind (dies verhindert Mypy- Fehler, wenn Modulattribute fehlen). Sie können dies auch in __init__.py Dateien verwenden, wenn Submodule fehlen.
  • Deskriptoren helfen häufig bei genaueren Typdefinitionen für den Zugriff auf benutzerdefinierte Attribute (wie im oben beschriebenen Spaltenbeispiel). Die Verwendung von Deskriptoren ist auch dann in Ordnung, wenn für die tatsächliche Implementierung der Laufzeit ein komplexerer Mechanismus verwendet wird, beispielsweise eine Metaklasse.
  • Deklarieren Sie die Framework-Klassen ohne zu zögern als verallgemeinert. Trotz der Tatsache, dass sie zur Laufzeit nicht so sind, können Sie mit dieser Technik den Typ einiger Elemente des Frameworks genauer bestimmen, während Laufzeitfehler leicht umgangen werden können. (Wir hoffen, dass Frameworks nach und nach integrierte Unterstützung für generische Typen hinzufügen und die entsprechenden Klassen explizit von typing.Generic .)

Kürzlich veröffentlichte mypy Plugins

Für die beliebten Python-Frameworks sind bereits mehrere Plugins verfügbar. Neben dem oben erwähnten SQLAlchemy- Plugin enthalten andere bemerkenswerte Beispielpakete mit Stubs und das integrierte mypy- Plugin Stubs für die Django- und Zope- Schnittstellen. An diesen Projekten wird aktiv gearbeitet.

Installieren und Verbinden von Stub- und Plugin-Paketen

Verwenden Sie pip, um das Plugin-Paket für mypy und / oder stub in einer virtuellen Umgebung zu installieren, in der mypy bereits installiert ist :

  $ pip install sqlalchemy-stubs 

Mypy erkennt installierte Stubs automatisch. Um installierte Plugins zu verbinden, fügen Sie sie direkt in mypy.ini (oder in die Benutzerkonfigurationsdatei) ein:

 [mypy] plugins = sqlmypy, mypy_django_plugin.main 

Mypy- Plugins entwickeln und Stubs schreiben

Wenn Sie ein Paket von Stubs und Plugins für das von Ihnen verwendete Framework entwickeln möchten, können wir das Repository sqlalchemy-stubs als Vorlage verwenden. Es enthält eine setup.py , Infrastrukturtests mit datengesteuerten Tests und eine Beispiel-Plug-In-Klasse mit einer Reihe von Hooks für das Plug-In (Plugin-Hooks). Wir empfehlen die Verwendung von stubgen , um die mit mypy gelieferten Stubs automatisch zu generieren und sie zu verwenden. Stubgen hat sich in mypy 0.670 Stubgen mypy 0.670 verbessert.

Lesen Sie die Dokumentation, wenn Sie mehr über das mypy- Plugin- System erfahren möchten . Sie können auch im Internet nach den Quellcodes der im Artikel beschriebenen Plugins suchen. Wenn Sie Fragen haben, können Sie diese hier stellen .

Der 15. April wird ein kostenloses offenes Webinar über den Kurs sein, das von einem der Organisatoren der Moskauer Python-Community - Vladimir Filonov - abgehalten wird. Melden Sie sich an, es wird interessant sein. Und jetzt warten wir auf Ihre Kommentare zum übersetzten Material.

Source: https://habr.com/ru/post/de447556/


All Articles