Macros personnalisées
Le plugin pyodide_macros
hérite de la classe MacrosPlugin
du plugin mkdocs-macros-plugin
, et les différentes façons de déclarer les macros pour celui-ci restent donc toutes applicables avec le thème.
Il est donc toujours possible d'ajouter vos propres macros en plus de celles définies par le thème :
- Soit via un fichier
main.py
à la racine du dépôt, comme celui à la racine de la version 1.5.0 de pyodide-mkdocs-theme. - Soit avec un paquet python déclaré à la racine du projet, comme le dossier
pmt_macros
à la racine de la version actuelle du thème.
Contraintes sur les noms des macros personnalisées
Les macros personnalisées ne doivent pas avoir le même nom que l'une de celles définies par le thème (voir ici).
Fichier main.py
#
C'est la méthode la plus simple :
- Ajouter un fichier
main.py
Ă la racine de votre propjet (PAS dans ledocs_dir
!) - Y déclarer une fonction
define_env(env:PyodideMacrosPlugin)
, dans laquelle déclarer vos macros :
from pyodide_mkdocs_theme.pyodide_macros import PyodideMacrosPlugin
def define_env(env:PyodideMacrosPlugin):
@env.macro
def macro1(...) -> str :
return ...
Module de macros#
Si vous avez beaucoup de code/macros personnalisées, il peut être intéressant d'utiliser un package python plutôt qu'un simple fichier main.py
, pour obtenir une meilleure organisation du projet :
-
Créer un dossier à la racine du projet.
-
Dans le ficher
mkdocs.yml
, ajouter le nom du dossier de macros personnalisées dans la configuration du plugin :Utiliser un package pour les macros personnaliséesplugins: - pyodide_macros: module_name: package_name
-
Ajouter un fichier
__init__.py
dans ce dossier (ceci transforme le dossier en package python). -
Ajouter une fonction
define_env(env:PyodideMacrosPlugin)
Ă ce fichier__init__.py
.
Toutes les macros doivent être définies depuis l'intérieur de cette fonction, comme pour le fichiermain.py
.
Les fonctions définissant les macros peuvent aussi être déclarées dans d'autres fichiers de la bibliothèque, puis importées pour être enregistrées en tant que macros.
Il y a de nombreuses approches possibles pour réaliser ceci. En voici une ci-dessous :
- Import des fonctions depuis le fichier
__init__.py
- Enregistrées en tant que macros en executant
env.macro(function_importée)
depuis l'intérieur de la fonctiondefine_env
.
from pyodide_mkdocs_theme.pyodide_macros import PyodideMacrosPlugin
from . import my_file1, my_file2
def define_env(env:PyodideMacrosPlugin):
env.macro(my_file1.macro1) # my_file1 contient une fonction "macro1"
env.macro(my_file1.macro2)
...
# Ou créer les macros directement ici (mais le package n'a alors plus d'intérêt...)
@env.macro
def macroX(...):
...
Si une de ces macros nécessite d'accéder à la variable env
, on peut ruser de différentes façons :
La façon de procéder la plus naturelle, en combinant des fichiers contenant une fonction define_env(env)
, comme un fichier main.py
isolé le ferait :
└── `macros_perso`
├── __init__.py
├── module1.py
└── module2.py
module1.py
def define_env(env):
@env.macro
def my_macro():
...
return ...
__init__.py
from . import module1, module2
def define_env(env):
for module in (module1, module2):
module.define_env(env)
Une alternative, moins naturelle, utilisant des "functions factories". L'intérêt pourrait être de voir les noms de toutes les macros personnalisées depuis le fichier __init__.py
.
La structure de fichiers est la mĂŞme:
└── `macros_perso`
├── __init__.py
└── module1.py
module.py
def macro1(env):
def macro1():
# impérativement le même nom !
return ... # avec env
return macro1 # la fonction interne
__init__.py
from .module import macro1
def define_env(env):
env.macro( macro1(env) )
Exemples#
Comme dit précédemment, il y a de nombreuses stratégies utilisables pour intégrer ses propres macros à un projet PMT.
L'exemple qui vient naturellement à l'esprit est le module de macros personnalisées du thème lui-même, pmt_macros
, mais il a atteint un niveau de complexité telle, que je déconseille de commencer par là .
Voici l'évolution des codes déclarant des macros personnalisées dans la documentation de PMT ou sur CodEx, illustrant différentes stratégies.
Projet | Stratégie/intérêt |
---|---|
PMT 1.5.0 |
Fichier main.py unique.Simple d'utilisation. |
PMT 2.0.0 |
Module pmt_macros .Correspond à la "méthode 2" décrite ci-dessus. |
CodEx (ancien) |
Module codex_macros .Correspond à la "méthode 2" décrite ci-dessus. |
CodEx (actuel) |
Module codex_macros .Utilise les deux techniques décrites ci-dessus pour enregistrer les macros, avec le fichier __init__.py appelant la fonction define_env d'un sous-module (méthode 1). Cette version est sans doute plus naturelle, venant d'un fichier main.py unique. |
pmt_macros |
Module pmt_macros .Utilise une logistique entièrement différente, afin de déclarer certaines macros globalement pour une "session" mkdocs serve entière (ceci permet de mettre en cache certains résultats). Pour programmeurs avertis seulement, car il est très facile de se retrouver avec des problèmes de données invalides en cache. |
Configuration#
Toutes les options du plugin original des macros sont disponibles directement sur le plugin pyodide_macros
.
Pour plus de détails, voir :
- La page de configuration du plugin
pyodide_macros
du thème. - La documentation du plugin original de
mkdocs-macros-plugin
.
Indentation et macros multilignes #
Pour les macros qui doivent insérer du contenu multilignes, il est important de pouvoir savoir à quel niveau d'indentation l'appel de la macro en cours a été fait.
Par exemple, pour une macro admo(lst:List[str])
qui générerait une liste de puces dans une admonition, on souhaite obtenir le résultat suivant :
Rendu markdown souhaité
!!! tip ""
* chat
* chien
* chaud
??? tip "indenté"
!!! tip ""
* chat
* chien
* chaud
Fichier markdown source
{{ admo(["chat", "chien", "chaud"]) }}
??? tip "indenté"
{{ admo(["chat", "chien", "chaud"]) }}
 
 
 
 
 
Pour réaliser cela, il faut que la macro sache combien d'espaces elle doit ajouter au début de chaque ligne.
Pyodide-mkdocs-theme
propose une fonctionnalité permettant d'indenter automatiquement le contenu généré par votre macro avec le bon niveau d'indentation : env.indent_macro(contenu)
.
Cette fonctionnalité peut-être étendue aux macros personnalisées, en procédant de la façon suivante :
-
Ajouter le nom de la macro dans la configuration du plugin du thème, dans
build.macros_with_indents
:plugins: - pyodide_macros: build: macros_with_indents: - admo
-
Implanter la macro dans votre fichier
main.py
ou votre module de macros personnalisées (voir en haut de cette page), en omettant la logique d'indentation dans un premier temps :from typing import List from pyodide_mkdocs_theme.pyodide_macros import PyodideMacrosPlugin def define_env(env:PyodideMacrosPlugin): @env.macro def admo(lst:List[str]): list_items = '!!! tip ""' + ''.join( f'\n * { item }' for item in lst ) return list_items
-
Intégrer au code de la macro l'appel à la méthode
env.indent_macro(content:str)
, qui prend en argument le contenu markdown non indenté, et renvoie le contenu indenté de manière appropriée. :@env.macro def admo(lst:List[str]): list_items = '!!! tip ""' + ''.join( f'\n * { item }' for item in lst ) indented = env.indent_macro(list_items) return indented
Les appels de macro dans l'exemple ci-dessus générerait en l'occurrence les chaînes suivantes :
-
Appel 1 :
'!!! tip ""\n * chat\n * chien\n * chaud'
-
Appel 2 :
'!!! tip ""\n * chat\n * chien\n * chaud'
Notez que dans la sortie indentée, la première ligne ne doit contenir aucune indentation, puisque l'appel de macro lui-même est déjà indenté dans le fichier markdown source.
Utilisation des macros référencées dans build.macros_with_indents
-
Il ne doit pas y avoir d'autres caractères que des espaces à gauche de ces appels de macros, dans les fichiers markdown.
-
PyodideMacrosTabulationError
est levée si des caractères de tabulation sont trouvés dans les indentations à gauche de ces appels de macros.Il est possible de contourner cette erreur en utilisant l'option
build.tab_to_spaces
du plugin, pour remplacer automatiquement les caractères de tabulation.
Garder en tête que la validité des indentations obtenues n'est alors plus garantie.
ContrĂ´le "plus fin" (?) de l'ajout des indentations
S'il s'avérait nécessaire d'exercer un contrôle plus poussé sur l'insertion des indentations, ou de stocker l'information pour une utilisation ultérieure lors du processus de construction du site, la méthode env.get_macro_indent()
renvoie la chaîne de caractères avec le bon nombre d'espaces à utiliser au début de chaque ligne, sauf la première, pour indenter les contenus multilignes.
La macro ci-dessus pourrait alors être écrite de la façon suivante :
def admo(lst:List[str]):
list_items = '!!! tip ""' + ''.join( f'\n * { item }' for item in lst )
indent = env.get_macro_indent()
return list_items.replace('\n', '\n'+indent)