Sam & Max » jinja http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Templates de projet avec cookiecutter 10 http://sametmax.com/templates-de-projet-avec-cookiecutter/ http://sametmax.com/templates-de-projet-avec-cookiecutter/#comments Thu, 11 Jun 2015 21:49:11 +0000 http://sametmax.com/?p=16371 cookiecutter.]]> Beaucoup de frameworks viennent avec des templates de projets maintenant. Django vient avec django-admin startproject --template, tandis que crossbar vient avec crossbar init --template. L’idée d’avoir de templates pour ses projets n’a rien de nouveau, d’ailleurs la plupart des OS modernes ont au moins ça intégré pour des fichiers. Ainsi Ubuntu à un dossière “Modèles”, et tout ce qu’il y a dedans peut être dupliqué ailleurs via un clic droit dans Nautilus.

Néanmoins la meilleure solution à ce jour vient de Audrey Roy, qui est avant tout connue pour avoir co-écrit le livre Two scoops of Django, et qui a aussi pondu cookiecutter.

L’outil est très simple. D’abord un pip pour l’installer :

pip install cookiecutter

Notez que bien que le projet soit en Python, les templates peuvent être en n’importe quel langage, cookiecutter s’en branle. Lui, il va juste copier le contenu du template dans un dossier, et remplacer les variables notées {{}} dedans.

Pour commencer, on créé un dossier nommé {{cookiecutter.repo_name}} qui va contenir le template de son projet. Oui, le nom du dossier est {{cookiecutter.repo_name}}. En effet les noms de dossiers et fichiers sont aussi templatisables, et passés à la moulinette de jinja2.

Dans ce dossier, on peut mettre tous les fichiers qu’on veut, avec des noms normaux, ou avec des variables. Le contenu des fichiers peut, bien entendu, contenir aussi des variables. Les variables sont toujours de la forme {{cookiecutter.nom_de_variable}}.

Ensuite, à côté de notre dossier nommé {{cookiecutter.repo_name}} (le dossier du template), on peut créer un fichier cookiecutter.json qui va contenir les valeurs par défaut de nos variables :

{
    "repo_name": "nouveau_projet",
    "version": "0.1.0",
    "autre_variable": "Bidule"
}

Enfin pour récolter les fruits de son labeur, on lance cookiecutter /chemin/vers/template et l’outil va vous poser tout un tas de question pour remplir les variables, puis va générer le nouveau projet dans le dossier courant.

Cookiecutter accepte aussi des urls de repo git et mercurial comme source de template, et l’auteur de l’outil fournit un template de projet Python très complet par ce biais.

On va l’utiliser comme exemple.

Voici son contenu :

├── cookiecutter.json <= valeur par défaut des variables
├── {{cookiecutter.repo_name}} <= template du projet
│   ├── AUTHORS.rst
│   ├── CONTRIBUTING.rst
│   ├── {{cookiecutter.repo_name}}
│   │   ├── {{cookiecutter.repo_name}}.py
│   │   └── __init__.py
│   ├── docs
│   │   ├── authors.rst
│   │   ├── conf.py
│   │   ├── contributing.rst
│   │   ├── history.rst
│   │   ├── index.rst
│   │   ├── installation.rst
│   │   ├── make.bat
│   │   ├── Makefile
│   │   ├── readme.rst
│   │   └── usage.rst
│   ├── HISTORY.rst
│   ├── LICENSE
│   ├── Makefile
│   ├── MANIFEST.in
│   ├── README.rst
│   ├── requirements.txt
│   ├── setup.cfg
│   ├── setup.py
│   ├── tests
│   │   ├── __init__.py
│   │   └── test_{{cookiecutter.repo_name}}.py
│   └── tox.ini
└── README.rst

Certains usages de variables sont assez évident, comme par exemple le fichier __init__:

# -*- coding: utf-8 -*-
 
__author__ = '{{ cookiecutter.full_name }}'
__email__ = '{{ cookiecutter.email }}'
__version__ = '{{ cookiecutter.version }}'

D'autres sont assez malines et inattendues comme le fichier de test :

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
"""
test_{{ cookiecutter.repo_name }}
----------------------------------
Tests for `{{ cookiecutter.repo_name }}` module.
"""
 
import unittest
 
from {{ cookiecutter.repo_name }} import {{ cookiecutter.repo_name }}
 
 
class Test{{ cookiecutter.repo_name|capitalize }}(unittest.TestCase):
 
    def setUp(self):
        pass
 
    def test_something(self):
        pass
 
    def tearDown(self):
        pass
 
if __name__ == '__main__':
    unittest.main()

On note l'usage du filtre jinja capitalize. Tous les outils de jinja sont à disposition, ce serait con de s'en priver.

Pour utiliser le template on fait $ cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git, ce qui nous donne :

Clonage dans 'cookiecutter-pypackage'...
remote: Counting objects: 505, done.
remote: Total 505 (delta 0), reused 0 (delta 0), pack-reused 505
Réception d'objets: 100% (505/505), 77.89 KiB | 0 bytes/s, done.
Résolution des deltas: 100% (265/265), done.
Vérification de la connectivité... fait.
full_name (default is "Audrey Roy")? Sam
email (default is "audreyr@gmail.com")? lesametlemax@gmail.com
github_username (default is "audreyr")? sam
project_name (default is "Python Boilerplate")? essai
repo_name (default is "boilerplate")? essai
project_short_description (default is "Python Boilerplate contains all the boilerplate you need to create a Python package.")? C'est un essai j'ai dis
release_date (default is "2015-01-11")? 
year (default is "2015")? 
version (default is "0.1.0")? 

Notez les valeurs par défaut du fichier JSON, qui sont celles de l'auteur.

Le résultat généré :

├── essai
│   ├── AUTHORS.rst
│   ├── CONTRIBUTING.rst
│   ├── docs
│   │   ├── authors.rst
│   │   ├── conf.py
│   │   ├── contributing.rst
│   │   ├── history.rst
│   │   ├── index.rst
│   │   ├── installation.rst
│   │   ├── make.bat
│   │   ├── Makefile
│   │   ├── readme.rst
│   │   └── usage.rst
│   ├── essai
│   │   ├── essai.py
│   │   └── __init__.py
│   ├── HISTORY.rst
│   ├── LICENSE
│   ├── Makefile
│   ├── MANIFEST.in
│   ├── README.rst
│   ├── requirements.txt
│   ├── setup.cfg
│   ├── setup.py
│   ├── tests
│   │   ├── __init__.py
│   │   └── test_essai.py
│   └── tox.ini
└── README.rst

Le fichier __init__ est devenu :

# -*- coding: utf-8 -*-
 
__author__ = 'Sam'
__email__ = 'lesametlemax@gmail.com'
__version__ = '0.1.0'

Et le fichier de test :

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
"""
test_essai
----------------------------------
 
Tests for `essai` module.
"""
 
import unittest
 
from essai import essai
 
 
class TestEssai(unittest.TestCase):
 
    def setUp(self):
        pass
 
    def test_something(self):
        pass
 
    def tearDown(self):
        pass
 
if __name__ == '__main__':
    unittest.main()

L'outil accepte un beau degré de customisation, avec un fichier de configuration général au niveau de l'utilisateur (et donc des valeurs par défaut par utilisateur), une API programmable en Python, des hooks pre et post generation (qui permettent d'injecter des variables dynamiquement comme par exemple un uuid ou un timestamp).

Donc si vous recréez souvent les mêmes layout de projet encore et encore, pensez à cookiecutter.

Mais surtout, surtout, si vous codez votre propre framework, ne faites pas comme Django, utilisez l'API de cookiecutter au lieu de réinventer la roue.

]]>
http://sametmax.com/templates-de-projet-avec-cookiecutter/feed/ 10