
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
Section intitulée « Cas pratique, fichier conditionnel »Voici l'exemple validé sur le lab 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" mode: "0644" 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.
Mettre en pratique
Section intitulée « Mettre en pratique »Un test Jinja2 renvoie un booléen, et c'est ce booléen qui décide de la suite. Le lab vous fait vérifier la définition puis le type d'une variable avec is defined, is mapping et is sequence, matcher une chaîne avec is match et is search, et réutiliser ces tests dans un when: comme dans un template. Le fichier conditionnel produit change de contenu selon les tests satisfaits.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- lineinfile vs template : l'arbitrage final du module Templates, une fois la syntaxe Jinja2 maîtrisée.
- Quiz Écrire du code : le diagnostic de fin de section, tiré d'une banque alignée sur le RHCE EX294.