Journal Publication de Moustache, votre nouvel ami dans la transformation de texte

Posté par  . Licence CC By‑SA.
Étiquettes :
12
10
juin
2024

Cher journal,

Dans le cadre de mes activités pro ou perso, j'ai l'occasion de (beaucoup) rédigé. Pour le formatage j'ai l'habitude d'utiliser Jinja, particulièrement efficace et qui répond parfaitement à mes besoins.
A un défaut près : c'est généralement "lourd" car nécessite un interpréteur Python (qui n'est pas la langage le plus léger… coucou Vlang ou Lua !) ainsi que quelques dépendances. Pas grand chose, mais on atteint vite plus de la centaine de Mo et c'est moyen-bof en air-gap.

Ne peut-on pas imaginer un binaire unique, à la mode Golang (cible OS multiples et facilement distribuable), qui puisse répondre à 80% des besoins ? En somme qui fasse une forme de préprocessing (transformation initiale) de texte simple mais efficace ? Le plus "léger" possible ?

Pour y arriver, j'ai listé 4 points fondamentaux qui me sont indispensable à l'exercice : variabilité, conditionnalité, inclusion et répétition.

… mais sans être non plus turing-complete !

Le résultat c'est Moustache, qui s'inspire grandement de Jinja avec des expressions, des déclarations, des imbriquements. Et une notion plus large de renvoi de la sortie sur l'entrée, pour pouvoir faire de belles macros adaptées à mes fantaisies les plus diverses ! :)

Le code est publié sans garantie sous licence GNU General Public License, est écrit en Rust à 100%, sans aucune dépendance extérieure. Un coup de Cargo et c'est parti ! Avec UPX j'ai un binaire qui couvre plus de 80% de mes besoins (possiblement les vôtres ?) pour moins de 70 ko. Parfait dans des chaînes de CI/CD pour produire de la belle documentation…

Dans les fonctionnalités diverses, citons : l'inclusion de fichiers, la déclaration de blocs et leurs appels possiblement multiples après, la recherche de fichiers/dossiers selon un gabarit, des options diverses dans les déclarations, la possibilité de produire des extensions compilées (Rust) ou de l'indirection de variables (car oui, j'aime me faire du mal).

Pour une présentation plus exhaustive, un README 100% français est disponible : github.com/JGarderon/moustache/blob/main/README_fr.md

Note :
- La version actuelle à l'heure d'écriture de ces lignes est la 1.1.
- Le dépôt Github sert de relai pour mon propre dépôt Gitlab (s'y référer pour disposer d'une version plus à jour).

Bonne journée et bon code !

  • # mustache.js ?

    Posté par  . Évalué à 8 (+6/-0). Dernière modification le 10 juin 2024 à 10:56.

    Bonjour, est-ce volontaire d'avoir choisi un nom aussi proche de Mustache dont le but est sensiblement le même mais visiblement incompatible ?

    Aussi, une explication de la syntaxe de template serait la bienvenue. Aller voir les différents fichiers de tests (qui mériteraient d'être nommés spécifiquement avec ce qu'ils testent selon moi) est un peu fastidieux :-)

    • [^] # Re: mustache.js ?

      Posté par  . Évalué à 3 (+2/-0). Dernière modification le 10 juin 2024 à 11:04.

      En effet c'est entièrement incompatible (comme indiqué, je suis davantage dans une présentation proche de Jinja). Pour le nom je comprends que c'est perturbant (une erreur peut-être…).

      Pour le fonctionnement interne, je n'ai pas encore documenté (*). Par contre le README contient le nécessaire pour démarrer : https://github.com/JGarderon/moustache/blob/main/README_fr.md#les-3-d%C3%A9limiteurs-possibles

      J'avoue que j'ai ciblé davantage le code que l'écosystème…

      (*) en résumé grossier : je parcours le fichier en tentant de trouver des délimiteurs particuliers. Ces délimiteurs permettent de noter des débuts et fins de parties (qui peuvent être des expressions, commentaires ou déclarations). Puis j'applique une logique propre à chacun (s'ils sont "bordés" ou non : c'est-à-dire avec une balise de fin), et enfin j'analyse chacun pour arriver à un résultat, qui est sous le format d'un texte généré ou d'un début / d'une fin de portion du texte initial. Que je renvoie en entrée en boucle jusqu'à ne plus avoir de modifications. Cela me permet de garder une empreinte mémoire très faible car je ne travaille qu'avec des délimiteurs d'une chaîne d'origine la plupart du temps.

      • [^] # Re: mustache.js ?

        Posté par  . Évalué à 3 (+1/-0).

        Par contre le README contient le nécessaire pour démarrer

        Le README_fr.md En effet, je m'étais arrêté sur le README.md en version anglophone sur lequel les explications sont sensiblement plus concises.

    • [^] # Re: mustache.js ?

      Posté par  (site web personnel, Mastodon) . Évalué à 2 (+0/-0). Dernière modification le 15 juin 2024 à 04:58.

      Je pensais que c’était ce Mustache que j’aime bien.

      “It is seldom that liberty of any kind is lost all at once.” ― David Hume

  • # Arrivé trop tard ?

    Posté par  (site web personnel, Mastodon) . Évalué à 6 (+5/-0).

    Il existe déjà Minijinja, un clone de Jinja en Rust par le même auteur (Armin Ronacher). Avec la CLI kivabien.

    • [^] # Re: Arrivé trop tard ?

      Posté par  . Évalué à 5 (+4/-0).

      Je n'avais pas, merci !

      A la lecture des commentaires, je me dis que j'ai un peu trop insisté sur une parenté avec Jinja - qui n'est là que pour l'esprit des délimiteurs. Je ne cherche pas à en faire un clone.

      Dans son fonctionnement, il y a une philosophie héritée de M4 pour la résolution : "GNU M4 is a macro processor in the sense that it copies its input to the output expanding macros as it goes. Macros are either builtin or user-defined and can take any number of arguments."

      Pour l'anecdote, j'ai voulu ne pas refaire la roue au démarrage mais j'ai trouvé M4 un peu trop "brutal" pour simplement l'encapsuler dans un script qui va bien. Je voulais quelque chose de plus conviviale et proche de mes habitudes, mais en gardant un fonctionnement distinct. Les extensions dans Moustache viennent pour ajouter prochainement le support des commandes Shell, des valeurs numériques, etc..

      A l'arrivée, je pense que le produit est différent de Minijinja (pour le meilleur ou le pire !) et que mon article ici a raté sa cible. Désolé !

  • # Go est mignon

    Posté par  (site web personnel) . Évalué à 4 (+1/-0).

    Il y a déjà un moteur de template dans Go, pourquoi ne pas l'utiliser directement ?

    https://pkg.go.dev/text/template

    Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

    • [^] # Re: Go est mignon

      Posté par  . Évalué à 2 (+1/-0).

      J'ai goûté au Go (article + dépôt) et j'en ai apprécié 80% de l'usage. Par contre je trouve le binaire rendu assez lourd (là j'arrive à moins de 70 ko). C'est un premier point, mineur mais pas anodin.

      Comme je l'indiquais dans un autre commentaire ici, ce n'est pas juste "faire du formatage" mais aller dans une activité un peu plus poussé, en alliant une présentation à la Jinja que j'apprécie et la philosophie proche de M4.

      Le moteur de gabarit de Golang aurait indéniablement un atout mais je pense que j'aurais dû rapidement "hacké" son système pour y insérer réellement les principes que je souhaitais y faire tenir. Je me sentais plus à l'aise pour le faire dans Rust car justement, il n'a aucune opinion sur le sujet.

    • [^] # Re: Go est mignon

      Posté par  . Évalué à 3 (+1/-0).

      Je ne sais pas si les derniers versions se sont améliorées, mais quand j'ai eu a m'en servir je l'ai trouvé très mauvais pour ce qui est de la remonté d'erreur. Il était incapable d'indiquer ne serais-ce que la ligne qui a causé l'erreur (en soit il donne une ligne, mais elle n'a du sens que dans une représentation interne). C'est très désagréable à l'usage.

      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

      • [^] # Re: Go est mignon

        Posté par  . Évalué à 2 (+1/-0). Dernière modification le 11 juin 2024 à 22:30.

        J'avoue que redonner le contexte lorsque tu es au fin fond d'une fonction de contrôle, de la partie exacte que tu traites, éventuellement depuis d'un texte depuis un autre endroit… Finalement faire un truc "stack d'erreur Java de 100 lignes je-sais-pas-ce-qui-se-passe-mais-voilà-petit-poucet" trouve vite un intérêt marqué…

  • # standalone jinja

    Posté par  . Évalué à 3 (+1/-0).

    Je comprends à la lecture des commentaires que ce n'est pas l'intention initiale de l'auteur mais pour ceux que cela pourrait intéresser, il est relativement aisé d'avoir jinja en ligne de commande et dans un exécutable autoporteur.

    En combinant jinja2-cli et pyinstaller, et en appliquant une recette inspirée de celle-ci, on obtient un exe qui peut être installé n'importe où.

    • [^] # Re: standalone jinja

      Posté par  (Mastodon) . Évalué à 6 (+3/-0).

      Pour ceux qui veulent tester:

      python3 -m venv .venv && . .venv/bin/activate && pip install -U pip && pip install jinja2-cli pyinstaller \
      > && pyinstaller -F -n jinja2cli -s --collect-submodules jinja2cli .venv/bin/jinja2

      J'obtiens quand même un binaire de 9.1MB:

      $ du -hs dist/jinja2cli 
      9.1M    dist/jinja2cli
      $ dist/jinja2cli --version
      jinja2-cli v0.8.2
       - Jinja2 v3.1.4
      • [^] # Re: standalone jinja

        Posté par  . Évalué à 4 (+2/-0). Dernière modification le 10 juin 2024 à 20:16.

        Yes!

        En ajoutant le support de yaml, toml et xml, j'arrive à 20MB. Je trouve pas ça rédhibitoire.

        • [^] # Re: standalone jinja

          Posté par  (site web personnel) . Évalué à 3 (+3/-0).

          Ouais pareil, on a vite fait de remplir son disque de 1Go !

          • [^] # Re: standalone jinja

            Posté par  . Évalué à 2 (+1/-0). Dernière modification le 11 juin 2024 à 22:22.

            Je passe sur le côté troll, c'est de bonne guerre.

            Sur le fond par contre, peut-être que je suis devenu vieux (certainement), mais oui, ça me gêne. J'ai commencé avec un PC Windows 3.11 (avec un PC qui avait mon âge, dans les années 95… 1995). J'ai gardé une certaine hantise d'avoir un disque rempli de trucs qui ne servent pas du point de vue applicatif (avec une position rigoureusement inverse pour ce qui est du fichier texte, images, etc.).

            La question du "poids" d'un binaire est liée à l'usage. 9 Mo ou 20 Mo, pour un besoin de préprocessing de texte basique (inclusion, etc.), avec une syntaxe un peu moderne, c'est probablement 80 ou 90% de code inutile (sinon plus). C'est "packagé" pour être autonome, mais ce n'est que la façade d'un assemblage. J'ai connu au boulot des débats sur Snap par exemple : ça apporte de la sûreté, de l'isolation, mais ça a un prix et si tu multiples ce prix (ce poids) par 10 ou 15 k VM pour un SI classique de taille moyenne, puis par applicatif embarqué, c'est loin d'être négligeable.
            On a la même chose avec Docker aujourd'hui : c'est tellement cool et simple qu'on a ne fait pas gaffe au poids, à la distribution (c'est magique) ; et on lance sur des systèmes toujours plus nombreux en unités, au prix d'un gouffre de ressources. Et ce n'est pas être anti-conteneurs, car on peut en faire des légers et efficaces : j'interroge là leur construction trop souvent simpliste.

            Avoir un interpréteur Python complet avec sa logique, son intérêt certains mais aussi sa capacité à être détourné d'un usage par un exploitant mal intentionné (car peut-être que le cas c'est aussi de servir du contenus peu contrôlés, venant d'un service Web ?), juste pour faire du préprocessing de texte… bof.
            Dans mon cas je suis relativement sûr que mes effets de bord seront limités, surtout si je jail bien la partie FS et seccomp de manière large.

            Car après tout, si j'ai un interpréteur Python, je peux remplacer les commandes courantes (ls, grep, sed, etc.). Voir tout un OS, pourquoi pas.
            Tu me diras "c'est tiré par les cheveux car tu mélanges commandes 'bas niveau' et 'haut niveau'" - en gros ce qui est indispensable ou non. Je te répondrais par : "c'est l'utilisateur qui fixe ce qui lui est indispensable."

            Là j'ai une solution légère, facile à distribuer (cf. un format binaire), qui fait des choses bêtes (= ce qu'on [je] lui demande). Au-delà de l'aspect cool d'en avoir fait la conception et le développement, et de le proposer pour qui le souhaite.

            Oui, même si mon vieux PC portable fait plus de 1000 fois 1 Go de stockage (!), le poids d'un applicatif qui ne serait pas maîtrisé, par ce qu'il draine ou d'une logique sous-tendue, me gêne.

            PS :

            $ ls -lh /usr/bin/m4
            -rwxr-xr-x 1 root root 239K févr.  3  2023 /usr/bin/m4
            • [^] # Re: standalone jinja

              Posté par  . Évalué à 2 (+0/-0).

              Si les ressources te sont si précieuses :

              $ ls -lh /usr/local/bin/minijinja-cli
              -rwxr-xr-x 1 root root 6.5M Jun 12 12:33 /usr/local/bin/minijinja-cli

              à découvrir ici

  • # Exemple en cours de rédaction

    Posté par  . Évalué à 6 (+5/-0).

    Vous avez quelques-uns à envoyer des projets ressemblant et je vous en remercie.
    Le projet est probablement très mal présenté et vous avez raison de noter de pertinentes alternatives :)

    Je planche sur une documentation plus complète avec un exemple complet dans le cadre d'un autre article LinuxFr, pour illustrer ce qui ressemble (ou non) à vos sources, et ce qui est possible de faire à date.

    • [^] # Re: Exemple en cours de rédaction

      Posté par  . Évalué à 4 (+2/-0).

      Si je peux donner un humble conseil. Tu as toujours une foule de concurrents (que ce soit ton projet ou un autre), ce qui est bien c'est de rapidement mettre l'emphase sur ce qui te distingue d'eux. C'est pas une question de différenciateur dans une logique marketing, mais une forme de "voici le besoin que j'ai voulu combler".

      De mon point de vu de lecteur, quand je lis un article sur un nouveau projet c'est ma question : qu'est-ce que ce projet change par rapport à ce que je connais déjà ?

      Ça donne aussi une idée de quelle direction veut prendre le projet (basiquement un projet qui veut multiplier les fonctionnalités et un qui veut atteindre la plus grande vitesse seront probablement très différent à l'usage - même si ça n'est pas toujours le cas-).

      Après tu aura toujours des gens qui vont te montrer qu'ils peuvent faire pareil avec ce qu'ils connaissent et des fois c'est vrai. Il ne faut pas trop s'en embêter.

      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

  • # Mo en Bash

    Posté par  (site web personnel) . Évalué à 3 (+2/-0).

  • # Je suis désolé…

    Posté par  (site web personnel) . Évalué à 4 (+2/-0).

    …mais je n’arrive pas bien à comprendre ce que fait moustache, je lis la doc, je crois comprendre que ça permet de faire de la mise en forme, mais je ne comprends pas de quel type de texte il s’agit, et pour aboutir à quoi…

    • [^] # Re: Je suis désolé…

      Posté par  . Évalué à 3 (+1/-0).

      Si c'est bien un concurrent de Jinja et Mustache, c'est un Moteur_de_template. Ça permet de créer des pages de texte dynamiques, qui peuvent inclure des variables, des boucles, … Et quand tu compiles ton template, les variables sont remplacées par leur valeur au moment de l'exécution. Par exemple :

      <!DOCTYPE html>
      <html>
        <head>
          <title>{{ variable|escape }}</title>
        </head>
        <body>
        {%- for item in item_list %}
          {{ item }}{% if not loop.last %},{% endif %}
        {%- endfor %}
        </body>
      </html>
      
  • # Projet très/trop similaire

    Posté par  (site web personnel) . Évalué à 1 (+0/-0).

    Juste une petit message pour signaler un projet similaire même si lui ambitionne d'être proche de jinja.

    Écrie lui aussi en Rust pour le projet de site static Zola.

    Tera

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.