
Les tests Jinja2 sont des prédicats booléens écrits avec is qui vérifient le type ou l’état d’une variable. Ils sont différents des filtres : un filtre transforme une valeur ({{ var | upper }}), un test retourne un booléen ({% if var is defined %}). Cette page liste les tests les plus utilisés en Ansible : is defined, is undefined, is none, is mapping, is sequence, is iterable, is string, is number, is regex.
Connaître ces tests permet d’écrire des templates et des conditions résilients aux variables manquantes ou de type inattendu.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- La syntaxe
var is <test>(avecis, pas==) - Les tests d’existence :
is defined,is undefined - Les tests de type :
is mapping,is sequence,is string,is number - Les tests d’état :
is none,is iterable,is regex - La différence filtre vs test (transformer vs vérifier)
Filtre vs test — la différence
Section intitulée « Filtre vs test — la différence »Une confusion classique chez les débutants :
| Forme | Type | Retour | Exemple |
|---|---|---|---|
{{ var | default('x') }} | filtre | valeur transformée | {{ undef | default('x') }} → 'x' |
{% if var is defined %} | test | booléen | {% if undef is defined %} → false |
default est un filtre : il prend une valeur et en retourne une autre. defined est un test : il retourne true/false.
Tests d’existence
Section intitulée « Tests d’existence »{% if user is defined %}nom : {{ user.name }}{% endif %}
{% if password is undefined %}{# pas de password posé, on génère un random #}{% set password = lookup('password', '/dev/null length=24') %}{% endif %}Cas typique : variable optionnelle qu’on ne veut interpoler que si elle existe.
Tests de type — is mapping, is sequence, etc.
Section intitulée « Tests de type — is mapping, is sequence, etc. »{# Vérifier qu'on a bien un dict #}{% if config is mapping %}{% for key, value in config.items() %}{{ key }} = {{ value }}{% endfor %}{% endif %}
{# Vérifier qu'on a bien une liste #}{% if hosts is sequence and hosts is not string %}{% for h in hosts %}- {{ h }}{% endfor %}{% endif %}
{# Note : une string est aussi 'sequence' en Python — d'où le 'is not string' #}| Test | Type Python correspondant |
|---|---|
is mapping | dict, OrderedDict |
is sequence | list, tuple, string (!) |
is iterable | tout itérable (incluant generators) |
is string | str |
is number | int, float |
is integer | int |
is float | float |
Piège classique : is sequence matche aussi les strings (qui sont des séquences de caractères en Python). Combinez avec is not string si vous voulez uniquement liste/tuple.
Tests d’état — is none, is regex
Section intitulée « Tests d’état — is none, is regex »{% if value is none %}{# value vaut None / null #}{% else %}valeur : {{ value }}{% endif %}
{# is regex : True si la string match une regex #}{% if user.email is regex('^[a-z]+@[a-z]+\\.[a-z]+$') %}email valide{% endif %}is regex est très utilisé pour valider des inputs avant de les utiliser.
Tests numériques
Section intitulée « Tests numériques »{% if score is number and score >= 80 %}note : excellent{% elif score is divisibleby(2) %}note : pair{% endif %}| Test | Vrai si… |
|---|---|
is divisibleby(N) | var % N == 0 |
is even | nombre pair |
is odd | nombre impair |
Combiner tests et opérateurs
Section intitulée « Combiner tests et opérateurs »{% if user is defined and user.email is defined and user.email is regex('^[^@]+@[^@]+$') %}{# user existe, a un email, et l'email est valide #}{% endif %}
{% if hosts is defined and hosts is sequence and hosts | length > 0 %}{# hosts existe, est une liste, et n'est pas vide #}{% endif %}L’ordre des tests compte : user is defined doit venir avant user.email is defined, sinon l’accès à user.email plante quand user est absent.
Cas pratique — fichier conditionnel (lab ecrire-code/tests-jinja)
Section intitulée « Cas pratique — fichier conditionnel (lab ecrire-code/tests-jinja) »Voici l’exemple validé sur le lab 28-ecrire-code-tests-jinja :
vars: user: { name: alice, age: 30 } config: app: nginx port: 80 ports: [80, 443, 8080] # optional_var n'est PAS définie
tasks: - name: Poser un fichier conditionnel ansible.builtin.copy: dest: /tmp/tests-jinja.txt content: | {% if user is defined %} user_defined=yes {% endif %} {% if config is mapping %} config_mapping=yes {% endif %} {% if ports is sequence %} ports_sequence=yes {% endif %} {% if optional_var is undefined %} optional_undefined=yes {% endif %}Sortie sur db1.lab :
user_defined=yesconfig_mapping=yesports_sequence=yesoptional_undefined=yesLes 4 tests sont vrais (chacun pour la bonne raison) et chaque ligne apparaît dans le fichier.
when: accepte les tests
Section intitulée « when: accepte les tests »Les tests fonctionnent aussi dans when: au niveau task :
- name: Tâche conditionnelle sur un test ansible.builtin.copy: dest: /tmp/foo content: "{{ user.name }}\n" when: - user is defined - user.email is regex('^[^@]+@[^@]+$')Lecture : “exécuter cette tâche si user existe ET son email match la regex”.
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
'undefined' object has no attribute | Accès à un attribut sans is defined parent | Tester chaque niveau : var is defined and var.foo is defined |
is sequence retourne true sur une string | Une string est une séquence de caractères en Python | Combiner : is sequence and is not string |
var == None ne marche pas | Mauvais opérateur | Utiliser var is none (avec is, pas ==) |
is regex plante avec une regex invalide | Erreur de syntaxe regex | Tester la regex avec python3 -c 'import re; re.compile("...")' |
Confusion default (filtre) et defined (test) | Les deux gèrent l’absence de variable, mais différemment | Filtre transforme, test retourne booléen — choisir selon le besoin |
À retenir
Section intitulée « À retenir »- Tests Jinja2 = prédicats booléens écrits avec
is(is defined,is mapping…). - Différents des filtres : filtre transforme une valeur, test retourne un booléen.
is defined+ accès chaîné : tester chaque niveau pour éviter lesNoneType has no attribute.is sequencematche aussi les strings — ajouteris not stringsi vous voulez uniquement liste/tuple.is regexvalide une string contre une regex Python.- Les tests fonctionnent dans templates (
.j2) ET danswhen:au niveau task.
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »Cette page a un lab d’accompagnement : labs/ecrire-code/tests-jinja/ dans
stephrobert/ansible-training. Le challenge
combine 4 tests Jinja (is defined, is mapping, is sequence, is undefined)
dans un fichier conditionnel.
cd ~/Projets/ansible-training/labs/ecrire-code/tests-jinja/cat README.mdpytest -v challenge/tests/