Pythons Präprozessor
(pydong.org)Pythons Präprozessor
- Die Behauptung, Python habe keinen Präprozessor, stimmt nicht
- Python verfügt über einen sehr mächtigen Präprozessor
Kodierung von Python-Quellcode
- Dank PEP-0263 kann die Quellcode-Kodierung definiert werden
- Die Kodierung kann durch einen Magic Comment in den ersten beiden Zeilen festgelegt werden
- Beispiele:
# coding=utf8,# -*- coding: utf8 -*-,# vim: set fileencoding=utf8 :
Path Configuration Files (.pth)
- Wenn der Python-Interpreter ohne die Option
-Sgestartet wird, lädt er automatisch das Paketsite - Durch Hinzufügen einer
.pth-Datei im Ordnersite-packageskann der Modulsuchpfad erweitert werden - Zeilen in einer
.pth-Datei, die mitimportbeginnen, werden ausgeführt - Dadurch lässt sich beim Initialisieren des Python-Interpreters beliebiger Code ausführen
Benutzerdefinierte Codecs definieren
- Der Python-Interpreter verlangt zwei Dinge:
- eine Funktion
decode(data: bytes) -> tuple[str, int] - eine Klasse für inkrementelles Decoding
- eine Funktion
- Mit
codecs.utf_8_decodewird das eigentliche Decoding durchgeführt und der resultierende String an den Präprozessor übergeben - Es ist sinnvoll, Ausnahmen abzufangen, auszugeben und anschließend erneut auszulösen
Einen inkrementellen Decoder bereitstellen
- Durch Vererbung von
codecs.BufferedIncrementalDecoderkann ein inkrementeller Decoder implementiert werden - Dabei werden Daten im Puffer gesammelt und die gesamte Datei beim abschließenden Decode-Aufruf vorverarbeitet
Python erweitern
- Es ist vergleichsweise einfach, Python mit der Standardbibliothek zu erweitern
- Mit dem Modul
tokenizekann der Token-Stream einer Datei verändert werden, oder mit dem Modulastder abstrakte Syntaxbaum
Unäres Inkrement und Dekrement
- Python hat keine unären Inkrement- und Dekrement-Operatoren
x++,x--sind ungültig++x,--xsind gültig, haben aber eine andere Bedeutung- Ausdrücke mit unärem Inkrement und Dekrement können in Python-Ausdrücke umgewandelt werden
Beispiel
- Eingabedatei
incdec.py:# coding: magic.incdec i = 6 assert i-- == 6 assert i == 5 assert ++i == 6 assert --i == 5 assert i++ == 5 assert i == 6 assert (++i, 'i++') == (7, 'i++') print("PASSED") - Umgewandelte Datei:
i = 6 assert ((i, i := i - 1)[0]) == 6 assert i == 5 assert ((i, i := i + 1)[1]) == 6 assert ((i, i := i - 1)[1]) == 5 assert ((i, i := i + 1)[0]) == 5 assert i == 6 assert (((i, i := i + 1)[1]), 'i++') == (7, 'i++') print("PASSED")
Python mit geschweiften Klammern (Bython)
- Statt Einrückung kann Python geschweifte Klammern zur Bereichsdefinition verwenden
- Mit
tokenize.generate_tokenskann der Token-Stream verändert werden
Beispiel
- Eingabedatei
test.by:# coding: magic.braces def print_message(num_of_times) { for i in range(num_of_times) { print("braces ftw") } print({'x': 3}) } x = { 'foo': 42, 'bar': 5 } if __name__ == "__main__" { print_message(2) print({k: v for k, v in x.items()}) } - Umgewandelte Datei:
def print_message(num_of_times): for i in range(num_of_times): print("braces ftw") print({'x': 3}) x = { 'foo': 42, 'bar': 5 } if __name__ == "__main__": print_message(2) print({k: v for k, v in x.items()})
Andere Sprachen interpretieren
- Der Python-Interpreter kann so eingerichtet werden, dass er andere Sprachen interpretiert
- Beispiele: C, C++, TOML
Beispiel
- C++-Datei
test.cpp:#define CODEC "coding:magic.cpp" #include <cstdio> int main() { puts("Hello World"); } - Umgewandelte Datei:
import cppyy cppyy.cppdef(r""" #define CODEC "coding:magic.cpp" #include <cstdio> int main() { puts("Hello World"); } """) from cppyy.gbl import main if __name__ == "__main__": main()
Datenvalidierung
- Daten im TOML-Format können mit JSON Schema validiert werden
- Die eigentliche Validierung erfolgt mit
jsonschema
Beispiel
- Schema-Datei
schema.json:{ "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "number"}, "scores": { "type": "array", "items": {"type": "number"} }, "address": {"$ref": "#/$defs/address"} }, "required": ["name"], "$defs": { "address": { "type": "object", "properties": { "street": {"type": "string"}, "postcode": {"type": "number"} }, "required": ["street"] } } } - Gültige Datendatei
data_valid.toml:# coding: magic.toml name = "John Doe" age = 42 scores = [40, 20, 80, 90] [address] street = "Grove St. 4" postcode = 19201 - Ungültige Datendatei
data_invalid.toml:# coding: magic.toml name = "John Doe" age = 42 scores = [40, "20", 80, 90] [address] street = "Grove St. 4" postcode = 19201
Fazit
- Mit benutzerdefinierten Codecs und Path Configuration Files lässt sich das Verhalten des Python-Interpreters stark verändern
- Beispiele sind
pythonql,future-typing,future-fstrings,future-annotations - Mit
magic_codeckann man leicht mit eigenen Präprozessoren experimentieren
Zusammenfassung von GN⁺
- Mithilfe von Pythons Präprozessor lassen sich verschiedene Spracherweiterungen und Datenvalidierungen umsetzen
- Es wird erklärt, wie sich das Verhalten des Python-Interpreters über benutzerdefinierte Codecs verändern lässt
- Der Artikel bietet Python-Entwicklern nützliche Werkzeuge und Techniken
- Projekte mit ähnlicher Funktionalität sind unter anderem
pythonqlundfuture-typing
1 Kommentare
Hacker-News-Kommentare
Die Syntaxfehlermeldung für
from __future__ import bracesist seit 2001 fest in cpython einkodiertIch habe über kreative Methoden zur Kündigung mithilfe von import-hooks nachgedacht, war aber enttäuscht, dass der codec-regex Dinge wie
μtf8nicht zulässtsys.settraceverwenden, um jede Funktion per Monkey-Patching durch die zuvor aufgerufene Funktion zu ersetzen und alle 17 MinutenstdoutundstderrauszutauschenEs gibt einen Grund dafür, dass Python keine Preprocessor-Hooks offenlegt, und ich denke, vernünftige Erwachsene sollten sie meiden
Ein Preprocessor ist praktischer und nützlicher
ast-Modul umzuschreiben, ihn mitexecauszuführen und danachexit()einzufügenIch liebe die Flexibilität von Python
mmapmissbraucht, damit sich das Skript selbst verändertDer beste Anwendungsfall mit pyxl ist von jsx inspiriert
# coding: pyxlkann man HTML-Code schreibenIch frage mich, ob der Übergang von Python 2 zu 3 besser hätte gehandhabt werden können
# coding: six.python2könnte Python-2-Code in Python 3 gültig machen, und# coding: six.python3könnte Python-3-Code so anpassen, dass er unter Python 2 lauffähig istIch freue mich, dass euch diese Idee gefällt, bald kommt mehr dazu
Es ist das erste Mal seit Langem, dass ich von einer völlig neuen Idee überrascht bin
Wenn man in Python Inline-Codegenerierung möchte, kann man cogs von Ned Batchelder verwenden
Ich frage mich, ob Abhängigkeiten, die durch diese coding-hook-Strategie eingeführt werden, von
pip freezeoder uv erkannt werden