PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Ceci n’est pas une pipe

jeudi 16 octobre 2014 à 11:26

Ni un forum. Ni un chan IRC. C’est un blog.

Pour cette raison, il est inutile de demander de l’aide en commentaire. Faites-le dans un endroit qui est adapté pour cela.

Exemple, pour Python :

Un commentaire se doit d’être en lien avec l’article posé, ou un autre commentaire. La seule exception est pour les anciens du blog, qui ont carte blanche car ils ont participé à la création de la communauté, et c’est un peu chez eux.

Abordons maintenant le point délicat du formulaire de contact. Il peut servir à poser des questions qui ne sont pas liées à un article, pourvu que :

Si vous ne le faites pas et que je suis de mauvais poil, la réponse risque de ne pas être celle attendue. Pour vous donner un exemple, voici cet article tel que je l’aurais écrit un jour grincheux :

Ceci n’est pas une pipe

Ni un forum. Ni un chan IRC. C’est un blog.

Si vous avez une question posez la plutôt sur un forum d’entre-aide, vous aurez une chance d’avoir une réponse, et vous nous éviterez de vous ajouter à la liste des milliers de personnes qui nous ont fait perdre du temps avant vous.

Car oui vous êtes le 9878eme, non, on ne vous doit rien, et enfin, ce blog demande des centaines d’heures de travail par mois. Bref, on a pas que ça à foutre de vous modérer parce que vous n’avez pas eu la politesse de vous sortir les doigts du cul pour faire une recherche sur google et trouver un endroit qui est FAIT pour poser des questions.

Exemple, pour Python :

http://www.afpy.org/forums/forum_python”>Forum de l’afpy
http://www.developpez.net/forums/f96/autres-langages/python-zope/”>Forum de devloppez.com
http://fr.openclassrooms.com/forum/categorie/langage-python”>Forum d’openclassroom
http://forum.hardware.fr/hfr/Programmation/Python/liste_sujet-1.htm”>Forum de hardware.fr

Oua, ça m’a pris 10 minutes pour trouver ces liens, et ce n’est QUE la partie fr.

Si vous n’êtes pas foutu de bouger votre fion pour trouver ces endroits, sérieusement, pourquoi voudriez-vous que je frémisse le petit doigt pour votre tronche de feignasse ?

Et si vous vous demandez pourquoi le ton de l’article est agressif, c’est parce que la masse de connards passifs me casse les couilles, et que si j’ai posté le lien, vous en faites partie.

Vous comprenez donc qu’il est préférable de garder cet espace joyeux, libre de haine et plein d’arc-en-ciels dans le cœur.

Sauvez un chaton, écrivez vos demandes d’aide sur un forum.

Appliquer un traitement à tous les fichiers d’un dossier en Python

mercredi 15 octobre 2014 à 10:35

Opération courante en informatique et on a tous eu besoin de chercher comment faire une fois.

Soit l’arborescence :

test
├── dossier
│   ├── fichier.py
│   ├── fichier.txt
│   └── pas_un_dossier.txt
├── Dossier
│   ├── dOssier
│   │   └── faichier
│   └── fichiiiiiiiiier
├── .fichier
├── fichier
├── fIchier
└── Fichier

Lister le contenu d’un dossier

>>> import os
>>> os.listdir('.')
['dossier', 'Dossier', 'fichier', 'fIchier', '.fichier', 'Fichier']

On récupère les noms des dossiers et les fichiers, y compris cachés, mais pas les dossiers spéciaux types .. et ..

Le type des noms retournés est str que ce soit en Python 2 ou 3. Ça a l’air cool et homogène comme ça, jusqu’à ce qu’on se souvienne que str sont des bits en Python 2 et de l’unicode en Python 3. Donc gaffe quand vous portez votre code d’une version à l’autre. Les noms de fichiers contiennent des caractères non ASCII dans la vraie vie vivante.

Si vous voulez récupérer uniquement les dossiers ou les fichiers, il va falloir filtrer :

>>> for element in os.listdir('/tmp/test'):
...     if os.path.isdir(element):
...         print("'%s' un dossier" % element)
...     else:
...         print("'%s' est un fichier" % element)
'dossier' un dossier
'Dossier' un dossier
'fichier' est un fichier
'fIchier' est un fichier
'.fichier' est un fichier
'Fichier' est un fichier

Pareil si on veut filtrer par extensions :

>>> for element in os.listdir('/tmp/test/dossier'):
...     if element.endswith('.txt'):
...         print("'%s' est un fichier texte" % element)
...     else:
...         print("'%s' n'est pas un fichier texte" % element)
'fichier.txt' est un fichier texte
'pas_un_dossier.txt' est un fichier texte
'fichier.py' n'est pas un fichier texte

Néanmoins, Python vient avec le module glob qui permet de demander le listing du contenu d’un dossier en appliquant des filtres Unix :

>>> import glob
>>> glob.glob('/tmp/test/dossier/*.txt')
['/tmp/test/dossier/fichier.txt', '/tmp/test/dossier/pas_un_dossier.txt']
>>> glob.glob('./dossier/*.txt')
['./dossier/fichier.txt', './dossier/pas_un_dossier.txt']

Mais comme vous pouvez le voir, le comportement n’est pas le même que listdir : les éléments de la liste sont des chemins relatifs à celui passé en paramètre si il est lui-même relatif, ou absolus si celui passé en paramètre est absolu. N’oubliez donc pas de normaliser l’entrée ou la sortie avec os.path.realpath qui retournera un chemin canonique.

Même problème Python 2/3 : le type est str dans les 2 cas.

Parcours récursif

La fonction os.walk permet de lister récursivement tous les fichiers et les dossiers à partir d’un point dans l’arborescence. C’est un générateur, et sa valeur de retour est un peu particulière et se récupère via unpacking:

for dossier, sous_dossiers, fichiers in os.walk('/tmp/test'):
    print('##### %s #####' % dossier)
    print("Sous dossiers : %s" % sous_dossiers)
    print("Fichiers : %s" % fichiers)
##### /tmp/test #####
Sous dossiers : ['dossier', 'Dossier']
Fichiers : ['fichier', 'fIchier', '.fichier', 'Fichier']
##### /tmp/test/dossier #####
Sous dossiers : []
Fichiers : ['fichier.txt', 'pas_un_dossier.txt', 'fichier.py']
##### /tmp/test/Dossier #####
Sous dossiers : ['dOssier']
Fichiers : ['fichiiiiiiiiier']
##### /tmp/test/Dossier/dOssier #####
Sous dossiers : []
Fichiers : ['faichier']

Du coup, si vous voulez avoir la liste des chemin des fichiers absolus, il faut reconstituer à la main :

for dossier, sous_dossiers, fichiers in os.walk('/tmp/test'):
    for fichier in fichiers:
        print(os.path.join(dossier, fichier))
/tmp/test/fichier
/tmp/test/fIchier
/tmp/test/.fichier
/tmp/test/Fichier
/tmp/test/dossier/fichier.txt
/tmp/test/dossier/pas_un_dossier.txt
/tmp/test/dossier/fichier.py
/tmp/test/Dossier/fichiiiiiiiiier
/tmp/test/Dossier/dOssier/faichier

Encore une fois, c’est du str partout, alors attention. L’article sur l’encoding en Python reste un des plus consultés de la categ prog.

Avec une lib qui va bien

Je n’ai jamais caché mon amour immodéré pour path.py (qui enterre tous ses concurrents, dont unipath), qui encapsule toutes les opérations des fichiers de manière simple et élégante et qui marche sous Python 2 et 3.

pip install path.py

Et c’est quand même plus facile pour le parcours récursif :

>>> from path import path
>>> for f in path('.').walkfiles():
    print f
   ....:     
./dossier/fichier.txt
./dossier/pas_un_dossier.txt
./dossier/fichier.py
./Dossier/fichiiiiiiiiier
./Dossier/dOssier/faichier
./fichier
./fIchier
./.fichier
./Fichier

En plus, f est de type path sous Python 2 et 3. C’est homogène, et ça permet de faire toutes les opérations magiques que ce type permet.

Néanmoins, si vous êtes sous Python 3.4 et que vous ne voulez pas ajouter une dépendance externe, vous pouvez utiliser le module pathlib :

>>> from pathlib import Path
>>> for p in  Path('.').glob('./**/*'):
...    if p.is_file():
...        print(p)
fichier
fIchier
.fichier
Fichier
dossier/fichier.txt
dossier/pas_un_dossier.txt
dossier/fichier.py
Dossier/fichiiiiiiiiier
Dossier/dOssier/faichier

Redis : pourquoi et comment ?

mardi 14 octobre 2014 à 13:31

Redis fait partie de ces technologies tellement utiles et simples à mettre en oeuvre qu’il est facile d’oublier toutes les personnes qui ne savent toujours pas ce que c’est. D’autant plus qu’on l’associe avec beaucoup d’étiquettes : cache, queues, pub/sub, base de données, nosql… Et qu’est-ce que Redis comparé à Memcache, MongoDB, MySQL, RabbitMQ, des technos qui n’ont rien à voir et auxquelles on le compare ?

Bref, c’est pas clair tout ça.

Hello redis

D’abord, installer le bouzin.

Si vous êtes sous Linux, Redis est dans votre gestionnaire de paquets. Par exemple, sous Ubuntu :

sudo apt-get install redis-server

Pour Mac, via macport :

sudo port install redis
sudo port load redis

Je crois que brew install redis marche aussi pour les amateurs de homebrew.

Pour Windows, il y a un exe à télécharger ici.

Mais le plus fun avec redis, c’est que même quand il y a pas de binaire, c’est le truc le plus facile du monde à compiler. Et Dieu sait que je hais la compilation, donc quand je vous dis que c’est simple, c’est que c’est mega, ultra, simple.

Ensuite lui faire dire bonjour.

Utiliser Redis se fait à base de commandes, dont la liste est sur le site officiel. On peut envoyer ces commandes depuis n’importe quel langage, mais Redis fournit un shell qui permet de rentrer ces commandes directement:

$ redis-cli # lancer le shell redis
127.0.0.1:6379>  ECHO "Hello"
"Hello"

Redis comme base de données clés/valeurs expirables

L’usage de base de Redis, c’est de stocker des valeurs associées à des clés. Le serveur Redis est comme un gigantesque Hash Map, dictionnaire Python, object Javascript, Array associatif en PHP… En premier lieu, donc, on utilise Redis pour stocker des choses ainsi :

"cle1" => valeur1
"cle2" => valeur2
etc

La clé doit être une chaîne de caractères, de préférence ASCII. La valeur peut être n’importe quoi, vraiment, mais généralement c’est un gros bloc de texte, un entier ou un blob binaire.

Ca s’utilise avec les commandes SET et GET, qui peuvent, comme toutes les commandes, être tapées dans le shell de Redis :

127.0.0.1:6379> GET une_cle
(nil)
127.0.0.1:6379> SET une_cle "Une valeur"
OK
127.0.0.1:6379> GET une_cle
"Une valeur"
127.0.0.1:6379> SET une_cle 780708
OK
127.0.0.1:6379> GET une_cle
"780708"

Ce genre d’usage est surtout sollicité pour stocker des paramètres de configurations ou des compteurs.

En effet, la plupart des opérations sur les clés sont atomiques, rendant ce genre d’usage idéal. On peut même incrémenter ou décrémenter une valeur avec INCR et DECR atomiquement :

127.0.0.1:6379> INCR une_cle
(integer) 780709
127.0.0.1:6379> INCR une_cle
(integer) 780710
127.0.0.1:6379> DECR une_cle
(integer) 780709
127.0.0.1:6379>

Vous allez me dire, mais quel intérêt ? Je peux déjà faire ça avec une variable. Certes, mais Redis est accessible depuis n’importe quel programme de votre serveur. Vous pouvez donc facilement partager des valeurs entre vos processus. Par exemple, avec Django, on a souvent plusieurs workers WSGI, et Redis permet donc de partager des informations entre ces workers. Pour cette raison, on peut utiliser Redis pour créer un lock partagé.

Ainsi, un paramètre dynamique peut être changé et lu depuis tous les process de votre projet en utilisant Redis, et la valeur sera garantie d’être à jour. On peut bien entendu faire ça avec une base de données ordinaire, mais Redis a un plus : la performance.

En effet, Redis est par défaut configuré pour garder toutes les données de sa base en mémoire vive. Pour cette raison, il est très, très rapide, supportant 100000 lectures/écritures par seconde. Si vous comptez le nombre de visiteurs en ligne pour l’afficher sur chaque page, votre base de données vous dira merci de plutôt demander à Redis.

Plus encore, les clés peuvent expirer :

127.0.0.1:6379> SET une_autre_cle "Tu ne le sais pas mais tu es deja mort"
OK
127.0.0.1:6379> EXPIRE une_autre_cle 5 # cette clé expire dans 5 secondes
(integer) 1
127.0.0.1:6379> GET une_autre_cle
"Tu ne le sais pas mais tu es deja mort"
127.0.0.1:6379> GET une_autre_cle
"Tu ne le sais pas mais tu es deja mort"
127.0.0.1:6379> GET une_autre_cle
"Tu ne le sais pas mais tu es deja mort"
127.0.0.1:6379> GET une_autre_cle
(nil)
127.0.0.1:6379> GET une_autre_cle
(nil)

La limite en taille de ce qu’on peut stocker par couple est assez large.

On peut également utiliser EXPIREAT et un timestamp unique si on a une date en tête.

Cette caractéristique le rend similaire à Memcache, qui est basé sur des clés/valeurs en mémoire qui peuvent expirer. Et comme Memcache, cela fait de Redis un outil idéal pour gérer du cache : faites une opération longue, stockez-là avec une clé, mettez lui une date d’expiration et pouf, vous avez une valeur cachée globale à votre app.

Redis a néanmoins 3 différences majeures qui le sépare de Memcache :

Redis comme base NoSQL zarbie

Redis n’a pas besoin d’un schéma particulier à définir pour pouvoir sauvegarder ses données, mais n’est pas pour autant limité à une forme d’organisation basique. En fait, des types très proches de ceux qu’on trouve dans le langage Python sont disponibles pour stocker internalement les données, et ils sont décrits dans son excellente doc.

Liste

Une liste est juste une collection ordonnée d’éléments. On utilise toujours la logique de clé, mais au lieu d’une valeur, on a une séquence d’éléments.

Cela permet d’avoir :

cle => [
	valeur1,
	valeur2,
	...
]

Dans le shell :

127.0.0.1:6379> lpush batman na
(integer) 1
127.0.0.1:6379> lpush batman na
(integer) 2
127.0.0.1:6379> lpush batman na
(integer) 3
127.0.0.1:6379> lpush batman na
(integer) 4
127.0.0.1:6379> lpush batman na
(integer) 5
127.0.0.1:6379> lpush batman na
(integer) 6
127.0.0.1:6379> llen batman
(integer) 6
127.0.0.1:6379> LINDEX batman 3
"na"
127.0.0.1:6379> LINDEX batman 10
(nil)
127.0.0.1:6379> LRANGE batman 0 -1 # du début au dernier élément
1) "na"
2) "na"
3) "na"
4) "na"
5) "na"
6) "na"
127.0.0.1:6379> LRANGE batman 2 3
1) "na"
2) "na"
127.0.0.1:6379>

On peut faire toutes les opérations qu’on a l’habitude de faire sur des listes : récupérer un élément, en ajouter un, en retirer un, faire du LIFO, du FIFO, du pipo, etc.

Utile pour créer des files d’attente, des journaux d’évènements (pensez jeux vidéos) ou plus simplement une valeur de config plus complexe qu’une entrée.

Hash

Le Hash se comporte comme un Hash Map, dictionnaire Python, object Javascript, Array associatif en PHP… Bref, comme Redis lui-même en fait, mais sans expiration de clé. Cela permet d’avoir :

cle => {
	cle : valeur,
	cle : valeur,
}

Dans le shell :

127.0.0.1:6379> HSET scores titi 1
(integer) 1
127.0.0.1:6379> HSET scores grosminet 0
(integer) 1
127.0.0.1:6379> HSET scores tom 0
(integer) 1
127.0.0.1:6379> HSET scores jerry 1
(integer) 1
127.0.0.1:6379> HGET scores jerry
"1"
127.0.0.1:6379> HGETALL scores
1) "titi"
2) "1"
3) "grosminet"
4) "0"
5) "tom"
6) "0"
7) "jerry"
8) "1"
127.0.0.1:6379>

Notez encore une fois qu’il n’est pas utile de vérifier que la clé existe avant de rajouter une valeur dans le hash, bien qu’il y ait une commande pour le faire. Si on essaye de récupérer une clé qui n’existe pas, Redis retourne nil.

Le hash est fort pratique pour toute forme de compteurs groupés, ou juste pour mettre en cache des relations. Redis n’ayant pas de JOIN, on utilise parfois un hash pour faire le lien entre deux listes par exemple.

Set

Comme les sets en Python, le set est une collection NON ordonnée d’éléments uniques. Il ne peut pas y avoir de doublons dans un set, et vérifier si un élément fait partie d’un set est une opération très rapide. Ils permettent aussi de faire des opérations ensemblistes de manière performante, par exemple vérifier quels éléments d’un set sont ou non dans un autre set.

cle => { valeur1, valeur2, ...}

Dans le shell :

127.0.0.1:6379> SADD ip 192.168.1.1
(integer) 1
127.0.0.1:6379> SADD ip 192.168.1.1 # ajouter 2x le même ne fait rien
(integer) 0
127.0.0.1:6379> SADD ip 192.168.1.1
(integer) 0
127.0.0.1:6379> SADD ip 192.168.1.2
(integer) 1
127.0.0.1:6379> SADD ip 192.168.1.3
(integer) 1
127.0.0.1:6379> SCARD ip
(integer) 3
127.0.0.1:6379> SMEMBERS ip
1) "192.168.1.2"
2) "192.168.1.1"
3) "192.168.1.3"
127.0.0.1:6379> SADD ip:banned 192.168.1.3 # le ":" est une séparateur courant pour les clés
(integer) 1
127.0.0.1:6379> SADD ip:banned 192.168.1.10 # ip:banned est un AUTRE set
(integer) 1
127.0.0.1:6379> SINTER ip ip:banned # trouver les IP qui sont dans les 2 sets
1) "192.168.1.3"

On va utiliser les sets pour éviter les doublons (tirage au sort par exemple) ou pour vérifier facilement une appartenance (notion de groupes, de permissions, etc.).

Le set possède une variante, le set ordonné, qui associe à chaque élément du set un score. On peut changer ce score, l’incrémenter ou le décrémenter atomiquement, avoir deux scores égaux… Et au final, récupérer le set dans l’ordre ascendant ou descendant des scores.

cle => { valeur1 (1), valeur2 (3), ...}

Dans le shell :

127.0.0.1:6379> zadd participants 1 staline
(integer) 1
127.0.0.1:6379> zadd participants 1 hitler
(integer) 1
127.0.0.1:6379> zadd participants 2 "pol pot"
(integer) 1
127.0.0.1:6379> zadd participants 1999 "rainbow dash"
(integer) 1
127.0.0.1:6379> zrange participants 0 - 1
(error) ERR value is not an integer or out of range
127.0.0.1:6379> zrange participants 0 -1 
1) "hitler"
2) "staline"
3) "pol pot"
4) "rainbow dash"
127.0.0.1:6379> zrange participants 0 -1 withscores
1) "hitler"
2) "1"
3) "staline"
4) "1"
5) "pol pot"
6) "2"
7) "rainbow dash"
8) "1999"

HyperLogLog

J’ai déjà parlé de l’HyperLogLog ici et celui de Redis fonctionne pareil. Cette structure de données permettra de faire des compteurs d’éléments uniques, comme par exemple un compteur de visiteurs ou de gens connectés, qui soit approximatif (+ ou – 1%) mais prenne une taille fixe en mémoire.

Dans le shell :

127.0.0.1:6379> PFADD connectes toto
(integer) 1
127.0.0.1:6379> PFADD connectes toto
(integer) 0
127.0.0.1:6379> PFADD connectes toto
(integer) 0
127.0.0.1:6379> PFADD connectes tata
(integer) 1
127.0.0.1:6379> PFADD connectes titi
(integer) 1
127.0.0.1:6379> PFADD connectes tutu
(integer) 1
127.0.0.1:6379> PFADD connectes tutu
(integer) 0
127.0.0.1:6379> PFADD connectes tutu
(integer) 0
127.0.0.1:6379> PFADD connectes tete
(integer) 1
127.0.0.1:6379> PFADD connectes bob
(integer) 1
127.0.0.1:6379> PFcount connectes
(integer) 6
127.0.0.1:6379>

Redis comme super pote de Python

Vu que Redis est simple à installer et à configurer, c’est un outil qu’on dégaine facilement, même pour un petit script, pas forcément pour une grosse app Web. Par exemple, je fais une analyse de mon serveur, plutôt que de stocker le résultat dans un fichier, je peux mettre tout ça dans Redis, c’est tellement pratique.

D’abord, la lib pour communiquer avec Redis est à un pip du clavier :

pip install redis

Ensuite, si vous utilisez le client StrictRedis, l’API est exactement la même que les commandes du shell :

>>> import redis
>>> r = redis.StrictRedis()
>>> r.hgetall('scores')
{b'titi': b'1', b'tom': b'0', b'jerry': b'1', b'grosminet': b'0'}

Il existe aussi des drivers asynchrones pour tornado, twisted et asyncio.

Redis comme message broker

J’aime le pub/sub, je pense qu’après mon enthousiasme pour WAMP.ws, c’est clair.

Redis met à disposition des primitives pour créer des files d’attente de messages et les lire, comme une version simplifiée de RabbitMQ ou Crossbar.io. Pour que ce soit intéressant, il nous faut deux process. D’abord un shell Python qui écoute les messages arrivant sur la file “sametmax” :

>>> import redis
>>> r = redis.StrictRedis()
>>> listener = r.pubsub()
>>> listener.subscribe(['sametmax'])
>>> for item in listener.listen():
...     print(item)
...

Le code va bloquer, et affichera quelque chose à chaque fois qu’il reçoit un message.

Du coup si je fais ceci dans le shell Redis :

127.0.0.1:6379> publish sametmax yolo
(integer) 1
127.0.0.1:6379> publish sametmax carpediem
(integer) 1
127.0.0.1:6379> publish sametmax wololo
(integer) 1
127.0.0.1:6379> publish sametmax2 oyooyo
(integer) 0
127.0.0.1:6379>

Mon shell python va afficher :

{'type': 'subscribe', 'pattern': None, 'channel': b'sametmax', 'data': 1}
{'type': 'message', 'pattern': None, 'channel': b'sametmax', 'data': b'yolo'}
{'type': 'message', 'pattern': None, 'channel': b'sametmax', 'data': b'carpediem'}
{'type': 'message', 'pattern': None, 'channel': b'sametmax', 'data': b'wololo'}

Il n’a pas affiché oyooyo puisque c’est sur une autre channel.

C’est une méthode assez simple de faire du pub/sub. C’est bas niveau, il n’y a pas de RPC, il faut boucler à la main et créer soit-même la mécanique pour arrêter d’écouter ou faire du multitask, mais c’est facile pour débuter. Pour cette raison, plein de gens utilisent une solution bricolée là-dessus pour faire du pub/sub.

Et donc

Non seulement Redis est facile à installer, simple à utiliser et performant, mais en plus il est accessible depuis de nombreux langages. Ajoutez à cela ses très nombreuses fonctionnalités, et vous avez là un système qui est installé par défaut sur la plupart de mes serveurs. Par ailleurs, suivez le blog de l’auteur, ce mec est un génie. Il écrit pas souvent, mais quand il écrit, c’est passionant, et humble. C’est beau.

Django pleure ‘MySQL server has gone away’

lundi 13 octobre 2014 à 10:52

Dans certaines circonstances, par exemple une transaction ouverte pendant trop longtemps, MySQL ferme la connexion avec son client.

Cela arrive par exemple quand on l’utilise comme broker pour celery. On a des tâches qui plantent, et quand on met du debug, on lit un (2006, 'MySQL server has gone away') bien cryptique.

Généralement je recommande de changer de backend ici. Passer à redis pour cet usage par exemple.

Mais parfois on ne peut pas. La solution est alors de forcer Django à réinitialiser la connexion en la fermant. Il faut le faire au niveau où on a remarqué que la requête échouait. Dans notre cas, au début de chaque tâche celery :

from django.db import connection 
 
@task
def do_stuff():
    connection.close()
    # le reste du code

Voyant la connexion fermée, Django va en ouvrir une nouvelle à la prochaine requête automatiquement.

Cela a, évidement, un impact sur les performances, donc choisissez bien entre mettre une rustine et changer la roue.

Est-ce la fin de jQuery ?

dimanche 12 octobre 2014 à 09:42

Avec un report massif des usagers sur des mobiles, la plupart sous un navigateur Webkit powered, on peut tabler sur un bien meilleur support de Javascript et ses APIs. Cela signifie qu’on a moins besoin d’utiliser des libs pour combler les trous, mais aussi la possibilité d’utiliser des frameworks comme AngularJS, MeteorJS, etc.

Est-ce pour autant la fin de notre bien aimé jQuery, qui nous a sorti de l’age sombre, et nous a tenu la main pendant cette dernière décénnie ?

Non.

Bien entendu, on utilise moins jQuery. On utilise parfois des choses plus légères. Parfois des choses plus lourdes. Parfois du JS pur.

Et il faut avouer que les projets parallèles à jQuery comme jQuery Mobile et jQueryUI n’ont jamais décollé. Merci Bootstrap et le responsive design.

Néanmoins, il reste des tas de choses pour lesquelles jQuery est utile.

Certes, il y a les versions light qui font “presque la même chose mais avec beaucoup moins de ko”. Mais c’est le “presque” qui finit par vous faire ajouter plein d’autres dépendances, pour finalement vous retrouver avec un truc qui fait “presque” la même taille que la lib que vous vouliez éviter, et qui ne sera probablement pas en cache. De plus, la documentation disponible n’est pas du tout comparable.

Et certes, il y a les outils bazooka, qui font tout, même le café. Mais les solutions comme Angular ne permettent pas une dégradation gracieuse du site, pèchent en référencement, et massacrent l’accessibilité. Les apps sont des bons candidats pour une “one page navigation”, mais pas les sites orientés contenus, quant aux hybrides…

Pensez aussi que même si vous codez pour des browsers modernes (donc pas IE8 qui limite ses sélecteurs à css 2.1), les APIs haut niveau comme querySelector et querySelectorAll sont très loin d’être aussi pratiques et puissantes que le bon vieux $(). Je ne parle même pas de la gestion des événements et d’Ajax qui sont toujours à chier en 2014.

Donc jQuery permet de rajouter cette petite touche de dynamisme à votre site statique, ou de faire un outil moderne sans sacrifier les aveugles et le PR. C’est un compromis entre puissance et simplicité. Et quand vous sortirez du cas standard, contrairement aux versions au bifidus actif allégé, la lib vous permettra de sauter le pas avec ses centaines de features, ou ses milliers de plugins.

Sans compter l’immense base de projets existants utilisant le bouzin, et qu’il va bien falloir maintenir. Bonne chance à ceux qui veulent porter un site avec jQuery vers un autre truc. N’oubliez pas non plus l’inertie technique. Et le temps de formation de la population de dev aux nouvelles technos. Surtout qu’en ce moment, avec l’avalanche de nouveaux trucs à tester/apprendre/valider, il va falloir gérer ses priorités. Ajoutez à ça la baisse drastique du niveau des devs sur le marché du travail :)

Bref, bien que j’apprécie et utilise les nombreuses alternatives à jQuery, il ne va pas disparaitre cette année, et probablement pas l’année prochaine.