PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

Les imports en Python

jeudi 16 mai 2013 à 11:13

Je suis fan de carmina burrana depuis l’age de 12 ans, alors pourquoi pas O Fortuna comme musique d’ambiance :

Les imports, c’était fastoche. Vous étiez dans votre petit programme, et pour importer un module de la lib standard, vous faisiez:

import module

Par exemple :

import os

Et pour importer une classe ou une fonction de cette lib, vous faisiez :

from module import fonction
from module import Classe

Par exemple :

from hashlib import md5
from xml.etree import Element

Parfois, c’était un peu plus compliqué, mais ça allait encore. Des fois il fallait importer un sous-module :

from package.sous_package import module

Par exemple :

from xml.sax import saxutils

Mais ça allait encore.

Et puis un jour vous avez du écrire votre propre module. Vous n’aviez pas vraiment réfléchi à la question. C’était juste une petite lib pour regrouper des fonctions. Ou juste une app Django. Un truc tout simple. Mais les imports ont soudainement cessé de devenir clairs. Ça ne marchait pas. Rien ne marchait. Vous aviez des sys.path.append partout juste au cas où et c’était encore pire.

Vous avez donc décidé de vous remettre à PHP, au moins le include utilise les chemins de fichiers, et ça, c’est facile.

Sous le capot

Quand vous utilisez import, sous le capot Python utilise le fonction __import__. Malgré ses __ dans le nom, c’est une fonction ordinaire, et vous pouvez d’ailleurs l’utiliser vous-même :

>>> os = __import__('os')
>>> os.path.join('s', 'ton', 'mon', 'g')
u's/ton/mon/g'

En fait, importer un module, c’est créer un objet module qui est assigné à une variable tout à fait normale :

>>> type(os)
<type 'module'>
>>> os = "on peut ecraser un module"
>>> os.path
Traceback (most recent call last):
  File "<ipython-input-12-e34748f24345>", line 1, in <module>
    os.path
AttributeError: 'unicode' object has no attribute 'path'
 
>>> import sys
>>> type(sys)
<type 'module'>
>>> sys = "je t'ecrase la tronche"
>>> type(sys)
<type 'unicode'>

Le mécanisme de module Python n’est donc pas un truc à part, c’est un objet comme le reste, qui contient des attributs. Les attributs sont les variables et les fonctions du module.

Pour charger un module, la fonction __import__ passe par les étapes suivantes :

  1. Chercher si le module os existe.
  2. Chercher si le module a déjà été importé. Si oui, s’arrêter ici et renvoyer le module existant.
  3. Si non, chercher si il a été déjà compilé en .pyc.
  4. Si ce n’est pas le cas, compiler le fichier .py en .pyc.
  5. Charger le bytecode du fichier pyc.
  6. Créer un objet module vide.
  7. Éxecuter le bytecode dans le contexte de l’objet module et remplir ce dernier avec le résultat.
  8. Ajouter l’objet module dans sys.modules, un dictionnaire qui contient tous les modules déjà chargés.
  9. Retourner le module pour pouvoir l’assigner à une variable, par défaut la variable porte son nom.

La fonction __import__ est donc très complexe, et d’ailleurs si vous voulez l’utiliser pour des trucs plus compliqués qu’un simple import de module, vous allez galérer car sa signature est vraiment zarb.

Mais pour vous, seule l’étape 1 est importante à comprendre. C’est l’étape à laquelle tout se joue.

Comment Python définit quel module importer ?

C’est la partie vraiment difficile, en effet si un import ne marche pas, c’est très souvent parce que Python ne trouve pas le module que vous voulez. Et la raison pour laquelle il ne le trouve pas, c’est que vous ne comprenez pas comment il cherche.

Python utilise ce qu’on appelle le PYTHON PATH pour chercher les modules importables. C’est une variable système qui contient une liste de dossiers. Par exemple, sur ma machine, elle contient ceci :

['',
 '/usr/bin',
 '/usr/local/lib/python2.7/dist-packages/grin-1.2.1-py2.7.egg',
 '/usr/lib/python2.7',
 '/usr/lib/python2.7/plat-linux2',
 '/usr/lib/python2.7/lib-tk',
 '/usr/lib/python2.7/lib-old',
 '/usr/lib/python2.7/lib-dynload',
 '/home/sam/.local/lib/python2.7/site-packages',
 '/usr/local/lib/python2.7/dist-packages',
 '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info',
 '/usr/lib/python2.7/dist-packages',
 '/usr/lib/python2.7/dist-packages/PIL',
 '/usr/lib/python2.7/dist-packages/gst-0.10',
 '/usr/lib/python2.7/dist-packages/gtk-2.0',
 '/usr/lib/pymodules/python2.7',
 '/usr/lib/python2.7/dist-packages/ubuntu-sso-client',
 '/usr/lib/python2.7/dist-packages/ubuntuone-client',
 '/usr/lib/python2.7/dist-packages/ubuntuone-control-panel',
 '/usr/lib/python2.7/dist-packages/ubuntuone-couch',
 '/usr/lib/python2.7/dist-packages/ubuntuone-installer',
 '/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol',
 '/usr/lib/python2.7/dist-packages/wx-2.6-gtk2-unicode',
 '/usr/lib/python2.7/dist-packages/IPython/extensions']

Donc, quand vous faites import os, Python va faire une boucle for là dessus et chercher dans chaque dossier si un package (un dossier avec un fichier __init__.py) ou un module (un fichier avec l’extension .py) nommé os existe.

Dès qu’il en trouve un, il s’arrête de chercher et l’importe. Si il n’en trouve pas, il va lever une ImportError.

Ce qui signifie que si votre module n’est PAS dans le PYTHON PATH, vous ne pouvez PAS l’importer. C’est impossible.

La grande majorité des problèmes d’import vient du fait que le module que vous essayez d’importer n’est pas dans le PYTHON PATH.

Maintenant, la grande question, c’est :

Qu’est-ce qui est dans le PYTHON PATH ?

Par défault, les dossiers sites-packages et dist-packages dans le dossier d’installation Python sont dans le PYTHON PATH. Quelques autres sont ajoutés selon les systèmes, mais vous pouvez toujours compter sur sites-packages et dist-packages pour être dans le PYTHON PATH. Quand vous installez une lib, par exemple avec pip, c’est là dedans que la lib va s’installer, pour être sûre de pouvoir être importée.

Quand vous êtes dans un virtualenv, les dossiers sites-packages et dist-packages de l’environnement virtuel sont ajoutés au PYTHON PATH.

Mais tout ça ne change pas grand chose pour vous. En effet, vous n’allez pas mettre VOTRE code dans les dossiers sites-packages et dist-packages.

C’est pour cela que Python possède un mécanisme supplémentaire : le dossier qui contient le module sur lequel vous lancez la commande python est automatiquement ajouté au PYTHON PATH.

Le PYTHON PATH, en pratique

Supposons que je sois dans le dossier /home/sam/Bureau et que j’aie dedans ce package. Voici à quoi ressemble mon arbo (téléchargez l’arbo vierge pour vos tests):

/home/sam/Bureau # <-- je suis ici
.
`-- test_imports
    |-- __init__.py
    |-- package_tout_en_haut
    |   |-- __init__.py
    |   |-- autre_sous_package
    |   |   |-- __init__.py
    |   |   `-- autre_module_en_bas.py
    |   |-- sous_module.py
    |   `-- sous_package
    |       |-- __init__.py
    |       |-- autre_module_en_bas.py
    |       |-- autre_sous_package
    |       |   |-- __init__.py
    |       |   `-- autre_module_en_bas.py
    |       `-- module_tout_en_bas.py
    `-- top_module.py

Si je lance un shell Python depuis ce dossier ou un script Python contenu dans ce dossier, je peux faire import test_imports, car /home/sam/Bureau est automatiquement ajouté au PYTHON PATH.

Je peux donc faire :

>>> import test_imports
>>> from test_imports import package_tout_en_haut
>>> from test_imports import top_module
test_imports.top_module
>>> from test_imports.package_tout_en_haut import sous_module
test_imports.package_tout_en_haut.sous_module

Mais si je me mets ici dans ./package_tout_en_haut/sous_package :

/home/sam/Bureau
.
`-- test_imports
    |-- __init__.py
    |-- package_tout_en_haut
    |   |-- __init__.py
    |   |-- autre_sous_package
    |   |   |-- __init__.py
    |   |   `-- autre_module_en_bas.py
    |   |-- sous_module.py
    |   `-- sous_package     # <-- je suis ici
    |       |-- __init__.py
    |       |-- autre_module_en_bas.py
    |       |-- autre_sous_package
    |       |   |-- __init__.py
    |       |   `-- autre_module_en_bas.py
    |       |-- module_tout_en_bas.py
    `-- top_module.py

Je ne peux PAS importer test_imports, ni dans un shell, ni depuis un module de ce dossier :

>>> import test_imports
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named test_imports

En effet, comme je lance la commande Python depuis

./package_tout_en_haut/sous_package

alors

./package_tout_en_haut/sous_package

EST ajouté au PYTHON PATH, mais

/home/sam/Bureau/

n’est PAS ajouté au PYTHON PATH.

Je ne peux donc PAS faire

from test_imports import top_module

depuis un fichier comme

.test_imports/package_tout_en_haut/sous_package/autre_module_en_bas.py

et exécuter directement

python autre_module_en_bas.py

ni même

python ./test_imports/package_tout_en_haut/sous_package/autre_module_en_bas.py

Je peux faire

from test_imports import top_module

depuis

autre_module_en_bas.py

uniquement si je lance un script Python tout en haut de mon arbo qui importe

autre_module_en_bas.py.

Mais alors, comment on fait ?

Il faut s’assurer que le dossier qui contient test_imports, notre module racine, soit TOUJOURS dans le PYTHON PATH.

Il y a plusieurs possibilités pour cela.

La première, c’est que notre lib va être utilisée une fois installée avec pip. Dans ce cas, on s’en branle, test_imports sera dans sites-packages automatiquement, et on pourra faire from test_imports import top_module de partout joyeusement.

Mais souvent, ce n’est pas le cas, votre code n’est pas fait pour être installé.

La seconde technique consiste à s’assurer que l’on appelle TOUJOURS la commande Python depuis le dossier qui est tout au dessus. C’est ce que fait django avec sa commande ./manage.py par exemple.

Vous avez votre projet :

./manage.py
projet

Et tout passe par python manage.py, qui est au dessus de projet, donc le dossier est bien ajouté au PYTHON PATH, et tout va bien.

Dans votre cas ça veut dire vous assurer qu’on lance toujours votre programme depuis un script d’entrée qui est tout en haut de votre arborescence.

Ca veut dire que vous devez avoir un point d’entrée UNIQUE pour votre package.

Mais parfois ça ne convient pas. Dans le cas des tests unitaires par exemple, il vous faut un point d’entrée spécialement pour les tests.

Pour ce genre de scénario, il faut donc avoir le dossier qui les contient à côté de votre package. Ainsi, si j’avais des tests unitaires, je devrais faire un dossier tests à côté du dossier test_imports. Par exemple, transformer mon arbo en un truc comme ça :

src
   |_ test_imports
   |_ tests

Afin que je lance les tests en faisant python tests depuis src. Et dans mes fichiers de tests, je pourrai faire des from test_imports import truc.

La manière dont vous organisez votre projet est donc très importante en Python, et si vous avez des problèmes d’import, la première chose à faire est de changer sa structure. Il n’y a pas de magie.

La dernière possibilité, quand tout a échoué, c’est de rajouter à la main le dossier dans le PYTHON PATH. sys.path est une simple liste, on peut donc faire un append() dessus.

Par exemple, si je veux absolument (mais je ne devrais pas :-)) pouvoir faire :

python .test_imports/package_tout_en_haut/sous_package/autre_module_en_bas.py et importer test_imports dans autre_module_en_bas.py, je peux faire un truc du genre :

import os
 
dossier = os.path.dirname(os.path.abspath(__file__))
 
while not dossier.endswith('test_imports'):
    dossier = os.path.dirname(dossier)
 
dossier = os.path.dirname(dossier)
 
if dossier not in sys.path:
    sys.path.append(dossier)

Ce code va remonter dans l’arbo jusqu’à tomber sur le chemin du dossier test_imports et ajouter son dossier parent au PYTHON PATH.

Ce n’est pas le truc le plus propre du monde, mais ça peut dépanner.

Imports absolus et relatifs

Si vous êtes dans ./package_tout_en_haut/sous_package :

/home/sam/Bureau
.
`-- test_imports
    |-- __init__.py
    |-- package_tout_en_haut
    |   |-- __init__.py
    |   |-- autre_sous_package
    |   |   |-- __init__.py
    |   |   `-- autre_module_en_bas.py
    |   |-- sous_module.py
    |   `-- sous_package     # <-- je suis ici
    |       |-- __init__.py
    |       |-- autre_module_en_bas.py
    |       |-- autre_sous_package
    |       |   |-- __init__.py
    |       |   `-- autre_module_en_bas.py
    |       |-- module_tout_en_bas.py
    |       `-- test_imports  # <-- autre package nommé test_imports
    |           `-- sous_module.py
    `-- top_module.py

Vous voyez que vous avez deux packages nommés test_imports.

Si vous écrivez import test_imports dans autre_module_en_bas.py, que va-t-il se passer ?

C'est le module tout en bas qui va être importé.

Ce n'est pas forcément ce que vous voulez. Python 3 corrige cela en permettant des imports relatifs, et Python 2.7 peut en bénéficier en important tout en haut du module :

from __future__ import absolute_import

En faisant cela, vous obtenez le comportement de Python 3 dans Python 2.7, et vous pourrez alors choisir entre faire :

import test_imports # importe le module tout en haut
import .test_imports # import le module dans le même dossier
from .test_imports import sous_module
from test_imports import top_module

Je vous recommande de toujours utiliser from __future__ import absolute_import. Ca ne coûte rien, et c'est plus cohérent. Par contre, vous ne pourrez pas tester from __future__ import absolute_import dans le shell, donc cet exemple ne marche pas dans ipython, mais il fonctionne parfaitement dans vos modules.

On peut aussi faire des imports relatifs du package contenant avec :

from .. import truc
from ..package import machin

N'oubliez pas que ceci ne marche que :

Sinon, ça ne sert A RIEN. Ce n'est pas comme un ../ dans un bash. Ça ne remonte pas d'un dossier. C'est juste une notation pour dire j'utilise celui la plutôt que l'autre, quand il y a ambiguité.

Pièges des imports

Package sans init

Si vous avez :

.
`-- test_imports
    |-- __init__.py
    |-- package_sans_init
    |   `-- nada.py

nada.py n'est pas importable, car package_sans_init ne contient pas de fichier __init__.py, même si test_imports est dans le PYTHON PATH. Ce comportement est corrigé en Python 3, et tout sous-dossier d'un package importable est automatiquement importable, qu'il contienne un __init__.py ou non.

Imports circulaires

J'en ai déjà parlé ici.

Vous avez :

.
`-- test_imports
    |-- __init__.py
    |-- package_tout_en_haut
    |   |-- __init__.py
    |   `-- sous_package
    |       |-- __init__.py
    |       |-- autre_module_en_bas.py
    |       `-- module_tout_en_bas.py

Et vous importez autre_module_en_bas dans module_tout_en_bas et inversement. Non seulement ça ne marchera pas, mais en plus l'erreur est déroutante :

ImportError: No module named module_tout_en_bas

Oui vous avez bien lu, il va vous dire que le module n'existe pas !

Il n'y a pas non plus de solution propre à ce problème : soit vous fusionnez vos deux fichiers, soit vous faites un 3eme module qui utilise ces deux modules (et ces deux modules n'importent pas ce 3eme module).

Sinon il y a la solution crade : mettre un des imports dans un appel de fonction ou de méthode comme ça:

def truc():
    import module_tout_en_bas
    module_tout_en_bas.bidule()

Parfois, ça dépanne :-) On ne tue pas des chatons non plus, donc si ça ne devient pas une habitude, ça peut passer.

flattr this!

Synchroniser son serveur avec ntp sous Linux

mercredi 15 mai 2013 à 09:32

NTP (Network Time Protocol) est un protocole utilisé pour synchroniser l’heure de votre système en utilisant un serveur en ligne. Cet article explique comment l’installer et le configurer sur un système d’exploitation Linux.

1. Installation

Pour installer le service ntp, ouvrez un terminal en root.

Sous Ubuntu:

sudo apt-get install ntp


2. Utilisation

La configuration de ntp se trouve généralement dans le fichier /etc/ntp.conf. On peut y ajouter de nouveaux serveurs de temps, en ajoutant une ligne similaire à celle-ci par exemple :

server 0.fedora.pool.ntp.org dynamic

Démarrez ensuite le service avec cette commande afin de synchroniser la date et l’heure (Attention, il faut plusieurs minutes avant que l’heure soit synchronisée) :

/etc/init.d/ntpd start

On pourra ensuite vérifier que l’heure est correcte avec la commande “date”:

[sm@web1 ~]# date
Wed May 15 07:21:15 UTC 2013
[sm@web1 ~]#

Note : en cas de problème, on pourra vérifier le bon fonctionnement avec la commande ntpstat qui donnera des informations sur le statut du service ntp :

[sm@web1 ~]# ntpstat
synchronised to NTP server (213.161.194.93) at stratum 4
time correct to within 562 ms
polling server every 64 s

A quoi ça sert ?

Dans notre cas nous avons des sites qui hébergent du contenu avec des urls qui expirent au bout d’un certainstemps.
La création de ces urls à durée de vie comptée est faite depuis le serveur web, le contenu se trouvant sur d’autres serveurs.
ntp dans ce cas nous assure que tous les serveurs seront calés sur la même heure, ce qui évite de générer des urls déjà expirées si un des serveurs de contenu n’était pas à l’heure.

flattr this!

Le choix d’un langage influence le fun de votre carrière

mardi 14 mai 2013 à 11:47

Les langages de programmation sont censés être des technologies neutres, mais comme toute chose utilisée dans le monde réel pour des usages concrets et nombreux, l’humain finit par leur donner une orientation, une préférence.

De fait, chaque langage finit par être plus utilisé dans certains types de métiers ou d’activités, pour certains types de projets, dans certains environnements. Plus important encore, un langage appelle d’autres outils, et un certain type de collègue, et même si, comme d’habitude, la généralisation est un piège à con, il y a bien des stéréotypes visibles qui se dégagent.

Ainsi, Java et PHP sont des langages pour lesquels il est très facile de trouver du travail. Il y a une telle base de code là dehors qu’il y a des annonces partout, et tout le temps. En revanche, les environnements Java sont généralement très très lourds à manipuler. Pas en terme de performance (Java est aujourd’hui très rapide), mais en terme de charge de travail : énormément de configuration, beaucoup d’abstractions et de design pattern imbriqués, des APIs et des formats très verbeux…

Travailler dans un monde Java, c’est généralement travailler à un rythme lent, plus souvent dans des grosses boîtes, pour des systèmes assez larges avec un grosse inertie. Ne vous attendez pas à des Java-party avec vos collègues après le boulot.

PHP, c’est la même chose, avec des projets et composants plus simples (dans des entreprises de toutes tailles), et souvent bien plus dégueulasses. Non pas qu’on ne puisse pas écrire du PHP propre, mais 15 ans de PHP codé à l’arrache ne s’effacent pas avec quelques années de Symfony, et autre cakePHP. Du coup on hérite souvent d’un projet moche comme ta mère.

Bref, Java et PHP vous garantissent un job, mais les projets sont rarement marrants, et l’ambiance au taff sera pas pumped up. Par contre il y a de la doc et des tutos, même si généralement ceux pour PHP sont de meilleure qualité que pour Java (qui peuvent être assez indigestes).

Il y a une alternative intermédiaire : le C#. Beau langage, beaux outils, c’est propre sans être trop lourd, et la base de code est pas à vomir. Mais on reste dans le corporate, et la plupart du temps, sur du 98% Microsoft ou affiliés.

Après vous avez des langages spécialisés comme Erlang, Scala, Lisp. Là, trouver du taff sera généralement un challenge. Ce n’est pas le genre de truc qu’on voit partout. Et le niveau sera difficile : un débutant peut arriver avec la bite et le couteau dans un projet PHP, mais si vous vous pointez avec 3 mois d’expérience sur une gateway jabber haute tolérance, le bidouillage ça va pas le faire.

Ce sont des langages qu’il faut choisir si on aime le taff de haut niveau, la débrouillardise et les solutions intelligentes. En général les missions sont super intéressantes, mais la reconversion est dure. Par contre, vous rencontrerez des collègues qui valent le détour.

Puis il y a les langages de bas niveau, type C/C++. Aujourd’hui, c’est massivement des missions scientifiques ou de l’embarqué : analyse d’image, cartes électroniques, petites machines, etc. Il y a encore des trucs pas cool genre Windev (j’ai un pote coincé là dedans, c’est triste, fuyez ces annonces), mais ce n’est plus la majorité. Ces langages, c’est une question de tempérament. Est-ce que vous aimez la minutie ? L’efficacité des algos ? Le travail sous contrainte technique ? Si oui, alors ce genre de taff peut être très sympa. Sinon, choisissez un langage plus dynamique.

Ensuite il y a les langages de niches : Cobol, Power Builder, etc. Là c’est généralement des projets de merde très bien payés. Plus personne ne veut coder avec ces trucs, mais ça coûte trop cher à changer. Les jeunes sont acceptés, les formations offertes, le salaire de début de carrière est bon, mais par contre, le code est un truc de mémé. C’est pas mal pour commencer une carrière et se faire un peu de thune, mais faut savoir en sortir, et ça ne donne pas de bonnes habitudes en prog.

Et pour finir il y des langages dit “modernes” (ce qui est un abus… de langage, car ils ne datent pas d’hier) comme Python, Ruby ou Javascript. Le côté “moderne” vient surtout du fait qu’ils sont maintenant très adaptés à des projets modernes, où la souplesse de développement a priorité sur le reste des caractéristiques. Choisissez ces langages si vous visez des missions, principalement Web, sur des produits récents. Ce sont les communautés les plus sympas et dynamiques, ça brasse pas mal dans le milieu et c’est assez jeune. Mais un grand nombre de start up, donc il faut pas chercher la sécurité de l’emploi.

Une petite parenthèse pour Python tout de même : il n’est pas autant limité au Web et on le voit aussi beaucoup dans le secteur scientifique et bancaire. C’est sa versatilité et la facilité de reconversion qu’il offre qui me fait dire que c’est un très bon choix comme premier langage. Mais. Car il y a un “mais”. Trouver une offre Python sera plus difficile qu’une offre Ruby, et BEAUCOUP plus difficile qu’une offre PHP ou Java. Après perso je n’ai jamais connu le chômage.

Warning ! Il faut faire gaffe à se lancer dans le dev Web. C’est très tentant, mais contrairement aux autres carrières, ça veut dire une courbe d’apprentissage beaucoup plus longue. Vous ne pouvez pas juste apprendre votre langage, ses libs et son env et vous vendre comme “expert”.

J’ai laissé pas mal de truc de côté, comme l’objective C, Haskell, Smalltalk, le Bash, Perl et quelques milliers d’autres langages. On ne peut pas tout faire, et je voudrais surtout peindre un tableau global du marché. En me relisant, je me dis que c’est bourré d’idées reçues, qu’il y a plein d’exceptions, etc. Mais je pense que les infos données peuvent permettre à des gens qui se posent la question de l’orientation de prendre une décision moins mauvaise que “je lance un D20 et on verra bien”.

flattr this!

C’est l’ascenseur émotionnel, tu baises à tous les coups

lundi 13 mai 2013 à 09:37

Vous connaissez ce genre de plan. Les amis des amis des amis vous invitent à un apéro d’amis d’amis. Vous vous pointez, et vous constatez que que :

  1. y a encore des gens qui louent des palaces pour 600 euros par mois.
  2. y a quand même des invités que vous connaissez d’un autre groupe qui n’a rien à voir.
  3. finalement vous allez bien rester pour le bar-beuk.

Vous discutez avec “la meuf trop bonne qui a un copain”, “le gay de service qui travaille dans la com”, “le gamin de 12 ans trop intelligent pour son age”, “la grosse qui parle très fort pour se faire plus remarquer que sa bonnasse de copine”… Vous vous connectez à internet en screud, parce que merde, on est bourré mais quand même.

Pétanque, eau de vie, tarot, les musicos se mettent en place, ça roule dans les coins, et finalement on va bien rester jusqu’au dîner.

Deux potes font la compet’ pour la métisse qui a un nom exotique et pas de conversation, le premier se barre avec pendant 1h et revient brocouille, le second tente le rencart en semaine, se prend un demi vent mais chope le numéro.

Et puis finalement on va bien se faire un after.

On termine chez les champions de la soirée, des Sam et Max du commerce. L’un est un rond et jovial commercial importé du moyen orient, le second est un petit et souriant négociant en immobilier en provenance du soleil levant. Le duo de choc, pas assortis, super sociables, Batman et Robin depuis pas mal de temps.

Whisky-coca. Marie-Juana.

Et on parle business, puis nana. Parce que le pognon et le cul, c’est assez fédérateur.

Et je suis surpris d’apprendre que les mecs sont blindés des deux. Ils ne paient pas de mine pourtant. Pas les prototypes du beau gosse, sympas et discrets, pas prétentieux, facilement approchables, habillés simplement.

Sauf que quand on les rejoint ils récupèrent la porsche, et on les suit dans un très bel appart’ avec terrasse et vue.

Ils sont amateurs de FKK, on est allé dans les mêmes endroits, et font du chiffre sur adopte un mec :

“Le ratio, c’est un rendez-vous avec baise pour 70 approches.”, qu’il me dit. “Je suis spécialiste en marketting sur ce site. Je fais ça avec une photo floue, et de loin.”

“Ouai enfin avec la décapotable, ça aide déjà pas mal non ?”

“Nan, jamais au premier rendez-vous. Hier on était à un bar, tu vois, et on leur a dit qu’on était postier, et plombier. Là, soit les meufs se barrent, soit on passe une bonne soirée. Après, le deuxième soir, on arrive avec la caisse. C’est la surprise, c’est l’ascenseur émotionnel, elles écartent les cuisses cash, tu baises à tous les coups.”

Je me suis dis que je ferai circuler l’info. Au cas où vous sachiez pas comment utiliser votre porsche.

flattr this!

Faire cohabiter plusieurs versions de jQuery

dimanche 12 mai 2013 à 15:08

jQuery est très bien foutu, non content de permettre d’utiliser des libs concurrentes utilisant la même API avec noConflict() (c’est beau l’open source quand même), elle permet également d’utiliser en même temps une version plus récente ou plus ancienne de son code, facilitant les migrations.

Pour utiliser noConflict à bon escient, il faut simplement faire très attention à l’ordre d’inclusion de ses scripts…

D’abord, loader les deux versions de jQuery :

<script type="text/javascript" src="version1-de-jquery.js"></script>
<script type="text/javascript" src="plugin-qui-utilise-cette-version.js"></script>

Ensuite utilise noConflict() pour sauvegarder une référence à cette version :

<script type="text/javascript">
var version1dejQuery = $.noConflict(true);
</script>

Et on charge la deuxième fournée :

<script type="text/javascript" src="autre-version-de-jquery.js"></script>
<script type="text/javascript" src="plugin-qui-utilise-cette-autre-version.js"></script>

On sauvegarde aussi cette version dans une variable :

<script type="text/javascript">
var autreVersionDejQuery = $.noConflict(true);
</script>

Et du coup on peut utiliser les deux en parallèle. Au lieu d’appeler $, les appelle par leur p’tit nom :

autreVersionDejQuery('a').click(truc);

Et comme d’hab, vous pouvez utiliser la technique de la fonction anonyme immédiatement appelée pour créer un alias local et réutiliser $.

(function($) {
    $('img').hover(bidule);
})(version1dejQuery);

D’ailleurs, je pense que ça ne marche que si les plugins inclus utilisent cette technique, ce qui est normalement une convention dans le monde de jQuery.

flattr this!