Le préprocesseur de Python
(pydong.org)Le préprocesseur de Python
- Affirmer que Python n’a pas de préprocesseur est faux
- Python dispose d’un préprocesseur très puissant
Encodage du code source Python
- Grâce à PEP-0263, il est possible de définir l’encodage du code source
- On peut définir l’encodage en ajoutant un commentaire magique dans les deux premières lignes
- Exemples :
# coding=utf8,# -*- coding: utf8 -*-,# vim: set fileencoding=utf8 :
Fichiers de configuration de chemin (.pth)
- Si l’interpréteur Python démarre sans l’option
-S, il charge automatiquement le packagesite - On peut étendre le chemin de recherche des modules en ajoutant un fichier
.pthdans le dossiersite-packages - Les lignes d’un fichier
.pthqui commencent parimportsont exécutées - Cela permet d’exécuter du code arbitraire lors de l’initialisation de l’interpréteur Python
Définir des codecs personnalisés
- L’interpréteur Python attend deux éléments :
- une fonction
decode(data: bytes) -> tuple[str, int] - une classe de décodeur incrémental
- une fonction
- Utiliser
codecs.utf_8_decodepour effectuer le décodage réel, puis transmettre la chaîne résultante au préprocesseur - Il est recommandé d’attraper les exceptions, de les afficher, puis de les relancer
Fournir un décodeur incrémental
- Implémenter un décodeur incrémental en héritant de
codecs.BufferedIncrementalDecoder - Accumuler les données dans un tampon, puis prétraiter l’ensemble du fichier lors de l’appel final de décodage
Étendre Python
- Étendre Python à l’aide de sa bibliothèque standard est relativement simple
- On peut modifier le flux de tokens d’un fichier avec le module
tokenize, ou modifier l’arbre syntaxique abstrait avec le moduleast
Incrémentation et décrémentation unaires
- Python n’a pas d’opérateurs d’incrémentation et de décrémentation unaires
x++,x--ne sont pas valides++x,--xsont valides mais ont un autre sens- On peut convertir des expressions d’incrémentation et de décrémentation unaires en expressions Python
Exemple
- Fichier d’entrée
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") - Fichier transformé :
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 avec des accolades (Bython)
- Il est possible d’utiliser des accolades au lieu de l’indentation pour délimiter les blocs en Python
- Utilisation de
tokenize.generate_tokenspour modifier le flux de tokens
Exemple
- Fichier d’entrée
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()}) } - Fichier transformé :
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()})
Interpréter d’autres langages
- On peut faire en sorte que l’interpréteur Python interprète d’autres langages
- Exemples : C, C++, TOML
Exemple
- Fichier C++
test.cpp:#define CODEC "coding:magic.cpp" #include <cstdio> int main() { puts("Hello World"); } - Fichier transformé :
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()
Validation de données
- Des données au format TOML peuvent être validées à l’aide d’un schéma JSON
jsonschemaest utilisé pour effectuer la validation réelle
Exemple
- Fichier de schéma
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"] } } } - Fichier de données valide
data_valid.toml:# coding: magic.toml name = "John Doe" age = 42 scores = [40, 20, 80, 90] [address] street = "Grove St. 4" postcode = 19201 - Fichier de données invalide
data_invalid.toml:# coding: magic.toml name = "John Doe" age = 42 scores = [40, "20", 80, 90] [address] street = "Grove St. 4" postcode = 19201
Conclusion
- En utilisant des codecs personnalisés et des fichiers de configuration de chemin, on peut modifier en profondeur le comportement de l’interpréteur Python
- Parmi les exemples, on trouve
pythonql,future-typing,future-fstrings,future-annotations magic_codecpermet d’expérimenter facilement son propre préprocesseur
Résumé de GN⁺
- Le préprocesseur de Python permet de réaliser diverses extensions du langage et de la validation de données
- L’article explique comment modifier le comportement de l’interpréteur Python via des codecs personnalisés
- Cet article fournit aux développeurs Python des outils et des techniques utiles
- Des projets offrant des fonctionnalités similaires incluent
pythonql,future-typing, etc.
1 commentaires
Commentaires Hacker News
Le message d’erreur de syntaxe pour
from __future__ import bracesest codé en dur dans cpython depuis 2001J’ai pensé à une méthode créative de licenciement en utilisant des import-hooks, mais c’est dommage que la regex des codecs empêche d’utiliser quelque chose comme "μtf8"
sys.settracepour monkey-patcher toutes les fonctions vers la fonction appelée précédemment, et remplacer stdout et stderr toutes les 17 minutesIl y a une raison pour laquelle Python n’expose pas de hook de préprocesseur, et je pense qu’un adulte raisonnable devrait éviter ça
Le préprocesseur est plus pratique et plus utile
exit()J’adore la flexibilité de Python
Le meilleur cas d’usage de pyxl s’inspire de jsx
# coding: pyxl, on peut écrire du code HTMLJe me demande s’il aurait été possible de mieux gérer la transition de Python 2 vers 3
# coding: six.python2pourrait rendre du code Python2 valide en Python3, et# coding: six.python3pourrait adapter du code Python3 pour qu’il s’exécute en Python2Je suis heureux que cette idée vous plaise, il y en aura bientôt davantage
Cela faisait longtemps que je n’avais pas été surpris par une idée totalement nouvelle
Si vous voulez de la génération de code inline en Python, vous pouvez utiliser cog de Ned Batchelder
Je me demande si les dépendances introduites par cette stratégie de hook de codage sont détectées par
pip freezeou uv