Sam & Max: Python, Django, Git et du cul http://sametmax.com Deux développeurs en vadrouille qui se sortent les doigts du code Sun, 15 Dec 2013 13:51:38 +0000 en hourly 1 http://wordpress.org/?v=3.3.1 Les mensonges des DSL http://sametmax.com/les-mensonges-des-dsl/ http://sametmax.com/les-mensonges-des-dsl/#comments Sun, 15 Dec 2013 01:36:55 +0000 Sam http://sametmax.com/?p=8327 Un DSL, ou Domaine Specific Language, est un langage qui est dédié à un usage très pointu, et pour lequel il est donc particulièrement efficace.

Par exemple, le langage de Matlab est un DSL, dédié à l’expression mathématique. SQL est un DSL, orienté requête. PHP a commencé comme un DSL, optimisé pour le Web.

En théorie, un DSL doit vous rendre plus productif. En théorie. En pratique, une fois qu’un DSL sort de son domaine de prédilection, il est extrêmement inéficace. C’est le prix de la spécialisation.

Or, dernièrement, on a fait beaucoup l’apanage des DSL dans le cadre d’autres langages. Car oui, certains langages permettent de créer des DSL. Les macros du C et les capacités de meta programmations de Lisp permettent par exemple de créer des langages complets, avec des dialectes spécialisés.

Vient alors le premier problème : on créé un nouveau langage. Récent. Supporté et donc débuggé et (mal) documenté par l’auteur. Ensuite, on se rajoute un niveau d’indirection. Car du coup ça nous fait une abstraction supplémentaire, et il faut savoir ce que ça fait sous le capot. En prime, on freine l’entrée de nouveaux venus dans le projet, puisqu’il faut qu’ils apprenent à faire avec le DSL en plus, là où une simple lib aurait pu faire l’affaire.

Et on touche ici à une seconde problématique, les faux DSL : des libs ordinnaires qui se déguisent en DSL. Typiquement, je pense à Ruby, ici.

Les rubistes prétendent partout qu’ils peuvent créer des DSL avec leur langage. Encore un mensonge, puisque tout ce qu’ils font c’est utiliser le chaînage de méthode, le namespacing, la surcharge des opérateurs et les parenthèses/virgules facultatives pour donner l’impression qu’un nouveau langage est créé.

Tout comme on donne l’illusion de retourner deux paramètres dans une fonction en Python en retournant un tuple et en faisant de l’unpacking. C’est du sucre syntaxique, mais on est très loin de ce que ça prétend être.

Pourquoi c’est important ? Parce que cela laisse à croire qu’il y a quelque chose de spéciale là dedans, alors qu’il s’agit ni plus ni moins que d’une bête lib avec une API fluide. Ce qu’on peut faire dans tout autre langage (excepté l’absence de parenthèses, sur lequel il faudra que j’écrive un article tellement c’est une FBI).

Donc plutôt que de faire du bruit et du hype autour de cela, et amener les gens à se concentrer sur l’aspect “comment obtenir une syntaxe exotique”, il serait plus intéressant de dire tout simplement : voilà comment on peut faire une belle API, voici les bonnes pratiques, appliquez les.

Et aussi écrire une doc…

J’ai horreur en informatique quand on donne 40 noms différents à la même chose. Comme par exemple pour les promises, les futures, les deferred, etc. Merde, non seulement ça n’aide personne, mais en plus ça rend la comprehension de principes plus difficile. Déjà que c’est rarement bien expliqué…

Au final, un DSL est rarement une bonne idée, que ce soit un vrai ou un faux. SQL nous aura bien servi, il faut le reconnaitre, même si on aurait pu faire mieux. Mais la plupart du temps, ce sont quelques heures de gagnées en redaction de code, et des jours de formation et maintenance perdus, ou alors juste une masquarade cachant simplement derrière le hype des principes sains de programmation.

Languages are more than just languages, they are a form of culture, and by being culture they tend to enforce (indirecty or directly) a certain way of doing things, i.e. standards or conventions. This means that if you know the language and its culture, there are less surprises and a longer learning or adaptation curve

(Extrait de Is Lisp Too Powerful ?)

flattr this!

]]>
http://sametmax.com/les-mensonges-des-dsl/feed/ 5
Remplacer sed, awk, cut et Perl par Python (= orgasme pour sysadmin) http://sametmax.com/remplacer-sed-awk-cut-et-perl-par-python-orgasme-pour-sysadmin/ http://sametmax.com/remplacer-sed-awk-cut-et-perl-par-python-orgasme-pour-sysadmin/#comments Sat, 14 Dec 2013 08:28:41 +0000 Sam http://sametmax.com/?p=8261 La force de Perl c’est qu’il permettait de piper des données directement via la ligne de commande pour faire des manipulations rapides.

C’est pour cela que c’était devenu les choix des sysadmins. Parce que jusqu’ici, le choix c’était soit de faire un truc simple en connaissant par coeur la tool box GNU, soit ouvrir un fichier et faire un script.

Python ne permet pas de piper des données directement dans la commande, mais des projets ont vu le jour pour le faire.

Il y a le projet pyp, que l’on doit à Sony Pictures Imageworks qui avait besoin de se simplifier l’automatisation des tâches de build pour ses films.

Et il y a pyped, dont j’avais brièvement parlé ici (article qui mérite d’être mis à jour vu que j’ai remplace dateutils par arrow).

Les deux étaient sympas, mais avait des syntaxes alambiquées. Cependant, pyped est récemment passé en v1.0, donc stable, et a une toute nouvelle approche de syntaxe qui rend la bestiole super agréable à utiliser.

Présentation.

Stdin, ligne à ligne

L’installation est bateau, c’est du pip :

pip install --user pyped

Et derrirère, on obtient la commande py. Elle s’utilise essentiellement à la suite d’une autre commande. Typiquement :

cat /etc/fsta | py "un truc"

L’astuce, c’est que “un truc” peut être n’importe quelle expression Python. Généralement une expression qui print() quelque chose.

Or, Pyped met automatiquement à disposition de cette expression deux variables :

  • La ligne en cours, dans la variable x.
  • Le numéro de la ligne en cours, dans la variable i.

L’expression Python est appelée une fois pour chaque ligne.

Par exemple, supposons que j’ai un fichier “fortune.txt” contenant :

bitcoin (btc) : 5
euros () : 100
dollars ($) : 80

Si je veut tout mettre en majuscule, je fais :

$ cat fortune.txt | py "print(x.upper())"
BITCOIN (BTC) : 5
EUROS () : 100
DOLLARS ($) : 80

On peut mettre plusieurs expressions d’affilé. Ainsi, si je veux récupérer la somme et le symbole uniquement :

$ cat fortune.txt | py "devise, sign, _, value = x.split()" "sign = sign.strip('()')" "print('%s%s' % (value, sign))"
5btc
10080$

Ok, c’est plus long que perl, mais vachement plus facile à écrire et à relire. Et j’utilise un langage que je connais déjà. Et pas besoin de faire un mix incompréhensible de sed, awk et autre cut.

Si j’ai vraiment besoin de lisibilité, je peux même le mettre sur plusieurs lignes :

$ cat fortune.txt | py "                                                                                                 
devise, sign, _, value = x.split() 
sign = sign.strip('()') 
print('%s%s' % (value, sign))  
"
5btc
10080$

Vous aurez noté que j’utilise print() et que je semble ne pas me soucier de l’unicode. C’est parceque pyped fait ça au début du script :

from __future__ import print_function, unicode_literals, division, absolute_imports

Du coup, on est bien en Python 2.7, mais on bénéficie de la division améliorée, de la fonction pour printer, des imports absolus et surtout, de l’unicode partout. D’ailleurs pyped vous transforme x pour que ce soit un objet unicode.

Tout traiter d’un coup

Parfois, on a besoin d’avoir accès à toutes les lignes, pas juste les lignes une à une. pyped permet cela avec l’option -i. Les variables x et i disparaissent au profit de la variable l, qui contient un itérable sur toutes les lignes.

Par exemple, envie de trier tout ça ?

cat fortune.txt | py -i "
lignes = (x.split() for x in l)
lignes = sorted((v, s.strip('()')) for d, s, _, v in lignes)
for ligne in lignes: print('%s%s' % ligne)
"
100€
5btc
80$

Moar options

Lisez la doc, car il y a d’autres options du genre éviter que pyped vous strip automatiquement le ligne break, forcer l’encoding, etc.

Parmi les trucs les plus utiles, il y a l’option -b qui permet de lancer un code avant la boucle. Pratique pour importer des trucs genre le module tarfile pour extraire une archive avant d’utiliser son contenu.

Néanmoins la plupart du temps on a rien besoin d’importer car pyped importe déjà automatiquement les modules les plus utiles : maths, datetime, re, json, hashlib, uuid, etc.

flattr this!

]]>
http://sametmax.com/remplacer-sed-awk-cut-et-perl-par-python-orgasme-pour-sysadmin/feed/ 9
Pourquoi j’ai horreur d’acheter http://sametmax.com/pourquoi-jai-horreur-dacheter/ http://sametmax.com/pourquoi-jai-horreur-dacheter/#comments Fri, 13 Dec 2013 08:20:53 +0000 Sam http://sametmax.com/?p=8312 J’achète rarement des trucs neufs. Déjà, il faut que ça soit utile, que ça prenne pas trop de place, et que ça se déplace facilement vu que je bouge tout le temps.

Mais en plus, le problème d’un achat, c’est que ça bouffe énormément de temps, surtout si on l’achète pas en ligne.

Exemple, je vais à la fnac pour acheter un bidule à 150 euros. Je dois prendre la voiture (j’ai horreur de conduire) pour aller en centre ville, ce qui prend une bonne demi-heure. Il faut se garer, puis se taper la foule de mongoliens dans le magasin, en arpentant les étages pour trouver le bon rayon.

Là, je prends le produit dont j’ai fait le choix préalablement sur le net (encore du temps… pour un putain d’objet !) car les vendeurs n’y connaissent que dalle. Il faut se farcir la queue, payer en caisse, retourner chez soit. Une bonne heure et demi de perdue que j’aurais pu passer à faire des choses plus importantes, comme jouer à Don’t Starve, et encore, si on sait exactement ce qu’on fait.

Maintenant ça s’arrête là si tout va bien, mais évidement, l’histoire ne mériterait pas un article si c’était le cas.

Il se trouve qu’arrivé chez moi, le produit ne me satisfait pas. Pour 150 euros, je vais donc faire l’effort de le rapporter. Je le remballe, et on reprend la caisse. Ai-je précisé que je déteste conduire ?

Je demande à un vigile à l’entrée où est l’accueil.

Je vais à l’accueil pour demander à ce qu’on me le change. Je fais donc la queue.

L’accueil me renvoie vers un autre accueil un étage au dessus, qui s’en occupe. Je fais donc la queue.

L’autre accueil me dit que pour l’électronique, ce sont les vendeurs qui s’en occupent. Je cherche un vendeur, et tombe sur un mec qui est en fait vendeur Microsoft, pas fnac. Donc je vais en trouver un autre, qui est entouré de personnes qui lui posent des questions essentielles comme la couleur des barrettes de RAM, si l’anti-virus le protège contre le terrorisme et où sont les toilettes. Je fais donc la queue.

Le vendeur me signale qu’il me faut un bon de circulation pour cela, qu’il faut demander au vigile. Je retourne voir mon vigile à l’entrée, qui fouille le sac d’un mec alors que bien entendu seule la police est autorisée à faire ça. J’attends, mon tour. Je fais donc la queue.

Je chope le bon, retourne voir le vendeur, qui entre temps a changé de place alors qu’il avait dit qu’il m’attendrait. Je le retrouve, traitant un autre client. Je fais donc la queue.

Il me fais mon retour produit et m’annonce la couleur : ce sera un avoir. Donc uniquement valable dans les magasins fnac, et à utiliser dans les 3 mois. La partie fun maintenant : on ne peut pas le diviser, il va falloir que je fasse un achat de 150 euros. Joie.

Je note mentalement que j’achèterai avec 150 euros de carte cadeau, utilisable un an et divisible. Quand on fait une coloscopie, on choisit son hôpital.

Mais l’avoir n’est pas valable tant qu’il n’est pas validé en caisse, donc j’y vais pour, vous l’avez deviné, faire la queue.

A ce stade, la magasin ferme. Si, si. Je suis parti de chez moi en fin début d’après midi, et je sors du magasin à la fermeture. Pour me faire faire fouiller mon sac par le vigile.

Je hais les magasins. Je hais acheter des trucs.

Parce que même quand tout se passe bien (en supposant que ça ne tombe pas en panne, parce que là, c’est reparti pour l’Iliade version longue avec bonus DVD et sous-titrage en russe), l’histoire ne s’arrête pas là. L’objet prend de la place. Il faut le ranger. Occasionnellement le nettoyer ou l’entretenir. Et le transporter quand on déménage. Puis en disposer quand il arrive en fin de vie, ce qui, si on est sensible à l’écologie, suppose l’amener au bon point de recyclage. En l’occurrence, la fnac.

Je ne comprends pas comment “faire du shopping” peut être considéré comme un passe temps.

flattr this!

]]>
http://sametmax.com/pourquoi-jai-horreur-dacheter/feed/ 25
Du Darwinisme pythonien http://sametmax.com/du-darwinisme-pythonien/ http://sametmax.com/du-darwinisme-pythonien/#comments Thu, 12 Dec 2013 10:13:22 +0000 golgotha http://sametmax.com/?p=8171 moutons clara morgane et de s'adonner à des expérimentations scientifiques de haut niveau ?]]> Ceci est un post invité de golgotha posté sous licence creative common 3.0 unported.

Qui n’a jamais rêvé de cloner des moutons clara morgane et de s’adonner à des expérimentations scientifiques de haut niveau ?

Bon ici, nous n’avons pas de clara morgane sous la main pour notre expérience mais, avec le python et les théories scientifiques de Darwin, on peut faire quelques trucs sympas : On va essayer de déterminer le plus court chemin à prendre pour relier plusieurs points entre eux, le truc cool c’est que pour solutionner le problème on va utiliser un algorithme génétique.

De la génétique dans du python ?!

En fait c’est assez simple (encore des termes barbares pour épater les copains..), écoutez bien, je vous explique le concept : on va faire des individus, chaque individu va faire un parcours en fonction des points du tracé en paramètre. A la fin, on note chaque individu avec un score, le score étant la longueur du parcours de l’individu. Vous suivez toujours ? Bien. On prend les meilleurs (Normal) et on les accouple avec des moins bons (faut les pousser un peu, au début ils sont timides mais, après ça va tout seul, c’est même l’orgie parfois…) ce qui donne une nouvelle population, normalement un poil meilleure que l’ancienne qu’on va vite mettre à la poubelle. On recommence le processus n fois et à la fin, on devrait arriver à des super individus, genre blonds aux yeux bleus qui parlent 14 langues : ça c’est notre solution.

Passons aux travaux pratiques !

Je commence par déclarer deux variables globales, la population et la liste de points.

population = []
a_map = []

Ensuite on créer une classe Point standard :

class Point(object):
 
    COUNT = 0
 
    def __init__(self, x, y):
        self.X = x
        self.Y = y
 
    def __str__(self):
        return "Point(%s,%s)"%(self.X, self.Y) 
 
    def distance(self, other):
        dx = self.X - other.X
        dy = self.Y - other.Y
        return sqrt(dx**2 + dy**2)

Rien de particulier ici, l’objet nous sera utile plus tard.

class Individu(object):
 
    # le constructeur de l'objet.
    # on met le score à zéro.
    # on peut aussi lui passer la liste de points
    # pour qu'il initialise une route au hasard.
    def __init__(self, init = False, map_point = []):
        self.score = 0
        self.route = []
        if init :
            self.set_route(map_point)
 
    # ici on créé une route avec un mélange des points
    # on utilise shuffle pour mélanger les points.
    # ensuite on calcul le score, c'est à dire la longueur du trajet.
    def set_route(self, map_point) :
        shuffle(map_point)
        self.route = map_point
        for p in range(len(map_point) - 1) :
            self.score += map_point[p].distance(map_point[p+1])
 
    # ici on donne à l'objet la capacité de faire un enfant
    # ça prend comme paramètre l'objet (lui même), et un autre individu.
    # on prend la moitié du trajet de l'objet et on complète avec
    # les points de l'autre individu.
    # on retourne un enfant, qui est un individu.
    def croisement(self, other):
        child = Individu()
        # je prends la moitier de moi-même.
        wdth = len(self.route)/2
        first_segment = self.route[:wdth/2]
        last_segment  = []
        # je complète avec l'autre
        for i in range(len(self.route)) :
            if other.route[i] not in first_segment :
                last_segment.append(other.route[i])
        child.set_route(first_segment + last_segment)
        return child
 
    # ici on défini une fonction pour que l'objet puisse se dessiner.
    # pour cela on utilisera Turtle de python.
    def show_me(self):
        turtle.clearscreen()
        pen = turtle.Turtle()
        pen.speed(0)
        pen.up()
        pen.setpos(self.route[0].X, self.route[0].Y)
        for point in self.route :
            pen.goto(point.X, point.Y)
            pen.down()
            pen.dot()
 
        pen.goto(self.route[0].X, self.route[0].Y)

Voilà pour l’objet individu (pas très inspiré sur le nom j’avoue..) qui est donc capable maintenant de faire pas mal de choses qui sera utile: se montrer, faire un petit (capacité que beaucoup d’objet lui envie déjà) et choisir une route parmi une liste de points.

La suite, j’ai écris ça dans des fonctions, il y a surement plus propre mais bon, le but est de vous montrer comment fonctionne un algo génétique, je laisserai le soin aux pro du python d’améliorer le code en lui-même (je ne vais pas faire tout le boulot non plus !)

# initialisation des points de la carte.
# prend en paramètre un nombre de points.
def init_map(nb):
    global a_map
    del a_map[:]
    for i in range(nb):
        p = Point(randint(1, 300), randint(1, 300))
        a_map.append(p)
# initialisation de la population.
# prend en paramètre le nombre d'individus à créer.
def init_pop(nb, map_point):
    global population
    del population[:]
    for i in range(nb):
        i = Individu(True, map_point)
        population.append(i)
# fonction qui sert à trier les individus suivant leur score.
# utile pour trouver les meilleurs.
def selection(pop):
    pop.sort(key=lambda x: x.score, reverse=True)
# dans cette fonction, on sélectionne les 15 meilleurs individus de la population
# que l'on croise avec les autres individus.
# la nouvelle population est constituée des 15 meilleurs plus les enfants.
def croisement(pop):
    new_pop = []
    best_pop = population[85:]
    for i in range(len(pop)-15) :
        new_pop.append(choice(best_pop).croisement(choice(population[20:85])))
    return new_pop + best_pop
# la fonction principal.
# on passe en paramètre le nombre de générations que l'on souhaite faire
# et le nombre de points. 
# Ensuite, on itère selon un algorithme précis :
# Création d'une population initiale.
# Sélection puis croisement de la population
# à chaque génération on regarde si on a un meilleur score
# si oui, on l'affiche.
def play(nb_gene, nb_point) :
    init_map(nb_point)
    init_pop(100, a_map)
    best_score = 1000000
    for i in range(nb_gene) :
        global population
        population = croisement(population)
        selection(population)
        if best_score > population[99].score :
            best_score = population[99].score
            print 'meilleur score : ' + str(population[99].score)
            population[99].show_me()

Voilà le morceau, je pense que j’ai laissé assez de commentaires dans le code pour bien comprendre comment ça fonctionne et au niveau du python en lui-même il n’y a vraiment rien de spécial, ici ce qui compte c’est que vous voyez rapidement comment fonctionne l’algorithme.

Alors, maintenant : Pourquoi s’emmerder à accoupler des objets à 2,78 Ghz ?

Le problème ci-dessus, un problème dit np-complet, c’est-à-dire que c’est la merde pour trouver une solution dans un temps raisonnable si on l’a fait de façon traditionnelle : pour trouver le meilleur trajet sur 10 points, on sera obligé dans un premier temps de trouver tous les trajets possibles, avec N villes on a (N-1)!/2 trajet possible, le nombre de trajets explose littéralement si N augmente. Avec 100 points il est déjà pratiquement impossible de calculer tous les trajets possibles en un temps raisonnable.

C’est là que l’algorithme génétique est très fort, on arrive très vite à une solution approchée, il est tout de même à noter que le résultat obtenu par l’algorithme génétique n’est pas LA solution exact au problème, il donne une solution approchée.

Dernier point sur le code ci-dessus, ce n’est que les bases de l’algorithme génétique, avec ce code vous ne pourrez pas venir à bout d’un parcours de plus de 20 ou 30 villes, pour cela il faut améliorer l’algorithme, par exemple le croisement entre deux individus peut être fait de plusieurs façons différentes, dans mon exemple je prends la moitié du “code génétique” d’un individu que je colle à une autre moitié, on peut aussi faire du crossover : c’est-à-dire qu’on prend des bouts du code génétique des deux individus alternativement. Ensuite, il y a aussi des mutations génétiques à introduire dans le croisement, à un certain taux, par exemple 1% des croisements entre individus produira une mutation génétique, concrètement : on fait le croisement puis on change aléatoirement des données du code génétique, dans notre exemple on échangera deux points sur le parcours. Cela a pour effet de produire des individus potentiellement meilleurs que les autres, en terme mathématique ça permet aussi de ne pas s’enfermer dans une solution locale, ce qui est souvent le cas.

J’espère ne pas vous avoir complètement perdu avec mes explications et vous avoir donné envie de regarder de plus près cet algorithme que je trouve très élégant.

flattr this!

]]>
http://sametmax.com/du-darwinisme-pythonien/feed/ 33
Qu’est-de que MVC et à quoi ça sert ? http://sametmax.com/quest-de-que-mvc-et-a-quoi-ca-sert/ http://sametmax.com/quest-de-que-mvc-et-a-quoi-ca-sert/#comments Tue, 10 Dec 2013 08:39:53 +0000 Sam http://sametmax.com/?p=7440 et PHP, car c'est une question qui hante les codeurs de ce langage. En effet on leur rabâche qu'il faut utiliser MVC, que tel framework est MVC, que leur code à eux ne l'est pas, etc. Sans que nul part, évidement, on ne donne une explication correcte de la notion.]]> MVC, pour “Modèle, Vue, Contrôleur”, est le nom donné à une manière d’organiser son code. C’est une façon d’appliquer le principe de séparation des responsabilités, en l’occurrence celles du traitement de l’information et de sa mise en forme.

Une fois n’est pas coutume je vais donner un exemple en Python et PHP, car c’est une question qui hante les codeurs de ce langage. En effet on leur rabâche qu’il faut utiliser MVC, que tel framework est MVC, que leur code à eux ne l’est pas, etc. Sans que nulle part, évidement, on ne donne une explication correcte de la notion.

Long article, petite musique.

(piqué à What the cut :-))

Principe de base

Il n’y a pas de Tables De La Loi qui disent ce qu’est le MVC, il y a donc autant de manières de le faire que de programmes. En fait, c’est un simple principe d’organisation de code, et il y en a d’autres. Mais généralement, c’est basé sur la répartition suivante :

  • Une part du code gère l’affichage. C’est la partie “Vue”.
  • Une part du code gère la manipulation des données. C’est la partie “Modèle”.
  • Tout le reste. L’espèce de merdier qu’on va mettre en place pour faire marcher le programme, c’est le contrôleur. Souvent, c’est le code qui réagit à l’action de l’utilisateur, mais pas seulement.

MVC est typiquement quelque chose d’abstrait qu’on ne peut pas comprendre avec une explication seule. Passons donc rapidement à un exemple.

Imaginons que l’on ait des tas de fichiers CSV ainsi faits :

"Jeu";"Nombre de joueurs Max";"Support"
"Secret of Mana";"3";"Super Nintendo"
"Bomberman";"8";"Super Nintendo"
"Mario Kart";"4";"Nintendo 64"
"Age of Empire 2";"8";"PC"

Et que nous voulions un programme qui fasse un rapport sur le CSV, affichant :

Nombre de jeux analysés : 10

Détails
--------

Support: Super Nintendo
Nombre de jeux : 2
Nombre de joueurs max : 8

Support: Nintendo 64
Nombre de jeux : 1
Nombre de joueurs max : 4

etc

Il y a de nombreuses manières de coder ce programme. Si on le fait en suivant le principe du modèle MVC, on va faire 3 fichiers : un pour le modèle, un pour la vue, et un pour le contrôleur. On peut avoir plus ou moins de 3 fichiers, j’ai choisi 3 fichiers pour bien illustrer le principe de séparation des responsabilités.

Le modèle

Le modèle manipule la donnée. Dans un site Web, le modèle est souvent le code qui permet de faire de requêtes à la base de données. Dans notre cas, c’est le code qui va manipuler le CSV. Encore une fois, il n’y a pas de définition divine de ce qu’est un modèle, ceci n’est qu’un exemple de ce que cela PEUT être. C’est le choix du dev.

modele.py

 
from __future__ import unicode_literals, absolute_import
 
from csv import DictReader
from collections import OrderedDict
 
class Modele(object):
 
    def __init__(self, csv):
        self.total_jeux = 0
        self.supports = OrderedDict()
        with open(csv) as f:
            # on parse le csv
            for data in DictReader(f, delimiter=b';', quotechar=b'"'):
                # on calcule les stats pour que ligne du csv
                support = self.supports.setdefault(data['Support'], {})
                support['nombre_de_jeux'] = support.get('nombre_de_jeux', 0) + 1
                self.total_jeux += 1
                if support.get('joueurs_max', 0) < data['Nombre de joueurs Max']:
                    support['joueurs_max'] = data['Nombre de joueurs Max']
 
    def __iter__(self):
        # goodies pour pouvoir itérer sur le modèle
        return self.supports.iteritems()

Ca s’utilise comme ça :

>>> modele = Modele("Bureau/jeux.csv")
>>> modele.total_jeux
4
>>> for support, data in modele:
    print support
    print data
...
Super Nintendo
{u'nombre_de_jeux': 2, u'joueurs_max': '8'}
Nintendo 64
{u'nombre_de_jeux': 1, u'joueurs_max': '4'}
PC
{u'nombre_de_jeux': 1, u'joueurs_max': '8'}

On voit ici le principe : le modèle ne fait que manipuler la donnée, et rien d’autre. Il extrait, regroupe, calcule, raffine, et donne une belle interface propre pour que le reste du programme puisse utiliser le résultat sans avoir à connaitre les détails du traitement.

La vue

La vue, c’est de la présentation. C’est comment on veut que la donnée soit présentée à l’utilisateur. Ça peut être le code qui pond du HTML ou produit un CSV, ou fait configurer de jolis boutons dans une UI.

Dans notre cas, c’est le code qui va formater le texte pour la console.

On veut un truc comme ça :

Nombre de jeux analysés : 10

Détails
--------

Support: Super Nintendo
Nombre de jeux : 2
Nombre de joueurs max : 8

Support: Nintendo 64
Nombre de jeux : 1
Nombre de joueurs max : 4

Normalement, on voudrait un template. Mais on a pas de langage de template qui accepte les boucles dans la lib standard, alors on va faire comme la norme WSGI et retourner un générateur de strings.

vue.py

from __future__ import unicode_literals, absolute_import
 
def rapport(modele):
    # affichage de l'en-tête
    yield ("Nombre de jeux analysés : {total_jeux}\n\n"
           "Détails\n--------\n").format(total_jeux=modele.total_jeux)
 
    # affichage des stats pour chaque console
    for support, data in modele:
        yield ("Support: {support}\n"
               "Nombre de jeux : {nombre_de_jeux}\n"
               "Nombre de joueurs max : {joueurs_max}\n").format(
               support=support, **data)

Et ça s’utilise comme ça :

>>> m = Modele("Bureau/jeux.csv")
>>> list(rapport(m))
[u'Nombre de jeux analys\xe9s : 4\n\nD\xe9tails\n--------\n', u'Support: Super Nintendo\nNombre de jeux : 2\nNombre de joueurs max : 8\n', u'Support: Nintendo 64\nNombre de jeux : 1\nNombre de joueurs max : 4\n', u'Support: PC\nNombre de jeux : 1\nNombre de joueurs max : 8\n']

Encore une fois, ceci n’est pas LA manière de faire une vue. Ceci est UNE manière de faire une vue. Le but de la vue est de contenir le code qui se charge de formater la donnée pour l’utilisateur.

Il est plus courant d’utiliser un template pour cela, c’est à dire une sorte lib de texte à trou à remplir plus tard avec le modèle. C’est plus facile et flexible qu’une fonction. Il y a des tas de libs de templates en Python. Je ferai sans doute un article dessus un jour. Si vous voulez un truc simple et rapide, utilisez templite : rien besoin d’installer, ça tient dans un fichier. Si vous voulez le truc le plus standard possible, utiliser jinja2, c’est plus ou moins la lib la plus connue actuellement.

Le contrôleur

Le contrôleur, c’est tout le reste. Essayer de définir le contrôleur est généralement voué à l’échec, tant sa nature change d’une application à l’autre. Certains disent que c’est le code glue qui permet de lier le modèle et la vue. D’autres qu’il contient la logique de flux du programme. Personnellement, je vous invite à vous fier à la définition “c’est tout le reste”. Avec l’expérience, vous en viendrez à faire des modèles et des vues de plus en plus adaptées, et la partie contrôleur découlera d’elle-même.

De toute façon, aucun MVC n’est parfait, et un peu de vue dégouline parfois sur le contrôleur, un peu de contrôleur coule dans le modèle, ou inversement. Il ne sert à rien d’être un nazi du MVC, c’est une bonne pratique, pas un dogme religieux.

Dans notre cas le programme a besoin d’un code qui :

  • Importe notre vue et notre modèle.
  • Prend en paramètre le fichier CSV via la ligne de commande.
  • Mélange tout ça pour afficher le résultat dans la console.

Le contrôleur est par ailleurs le point d’entrée d’un programme. Et ce sera essentiellement ça, le contrôleur de notre programme : un point d’entrée.

controlleur.py

from __future__ import unicode_literals, absolute_import
 
import os
import sys
 
from vue import rapport
from modele import Modele
 
# on prend le csv en paramètre du script
try:
    f = sys.argv[1]
except IndexError:
    sys.exit("Veuillez passer le chemin d'un fichier CSV en paramètre.")
 
# on vérifie que le csv existe
if not os.path.isfile(f):
    sys.exit("Le fichier '%s' n'existe pas" % f)
 
# on analyse le CSV et on affiche le rapport
for texte in rapport(Modele(f)):
    print texte

Résultat final

$ python controlleur.py jeux.csv
Nombre de jeux analysés : 4
 
Détails
--------
 
Support: Super Nintendo
Nombre de jeux : 2
Nombre de joueurs max : 8
 
Support: Nintendo 64
Nombre de jeux : 1
Nombre de joueurs max : 4
 
Support: PC
Nombre de jeux : 1
Nombre de joueurs max : 8

Vous pouvez télécharger le code Python de cet article.

Exemple en PHP

Le PHP a eu beaucoup de succès du fait de la facilité avec laquelle on pouvait coder un site Web, en mélangeant code et HTML. Malheureusement cela a donné lieu à des codes très sales, où on trouvait les requêtes SQL à côté de l’affichage d’un tableau, l’analyse des paramètres $_GET à deux pas de la vérification du mot de passe.

MVC a été une réponse à cela.

Un modèle MVC propre sera généralement très riche et complexe, mais il est possible de bricoler un site en MVC basique à la main sans trop de problème. Je ne vous recommande pas d’utiliser ce code en prod, mais c’est un bon début pour comprendre comment ça marche. Une fois que vous serez à l’aise avec l’idée, n’hésitez pas à coder le votre sur un petit projet, puis à tester un framework. Symfony, par exemple, est une valeur sûre en PHP.

Admettons que notre site ait deux pages : accueil et liste des utilisateurs.

L’accueil dit juste bonjour, la liste affiche tous les utilisateurs du site Web. Passionnant.

Le modèle

L’idée est de mettre toutes les requêtes SQL au même endroit.

Les vieux routards du PHP m’excuseront, mais je n’ai plus codé dans ce langage depuis des années, donc mon style va dater un peu :-) Et honnêtement tous ces points-virgules, ces dollars et ces brackets dans tous les sens, sans compter la flèche comme caractère de look up, ça me perturbe grandement.

modele.php

<?php
 
$con = mysqli_connect("127.0.0.1", 'root', 'admin123', 'ma_db');
 
class User {
 
    public $name;
    public $age;
 
    function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }
 
    static function liste() {
 
        $users = array();
 
        $query =  mysqli_query($con, 'SELECT * FROM `user`');
 
        while ($row = mysql_fetch_assoc($query))
        {
            $users[] = User($row[0], $row[1]);
        }
 
        return users;
    }
 
}

Et ça s’utilise comme ça :

$users = User->liste();
foreach ($users as $user) {
    echo $user.name . '(' . $users.age. 'ans)';
}

Ce qui affiche tous les noms et les ages des utilisateurs.

Bien, on a isolé l’accès aux données, maintenant on va isoler la mise en forme.

La vue

Ou plutôt, les vues, puisqu’on a deux pages, et donc deux vues.

Vous ne le savez peut être pas, mais PHP vient avec une syntaxe alternative spécialement conçue pour être utilisée dans le HTML. Elle est similaire à la syntaxe originale, mais les blocs sont ouverts avec : au lieux de { et fermés par endinstruction. Les variables sont affichées avec <?=$nom_de_variable?>.

Par exemple:

<?php if $truc: ?>
    <p>
        <?=$machin?>
    </p>
<?php endif; ?>

Cette syntaxe permet de bien séparer le texte du code PHP, et donc sera utilisée pour la vue.

accueil.php

<html><body><h1>Bonjour</h1></body></html>

liste_utilisateurs.php

<html>
    <body>
        <h1>Utilisateurs</h1>
 
        <ul>http://www.php.net/manual/fr/control-structures.alternative-syntax.php
            <?php foreach $users as $user: ?>
                <li><?=$user->name?> (<?=$user->age?> ans)</li>
            <?php endforeach; ?>
        </ul>
 
    </body>
</html>

Et voilà, on a deux pages, et la deuxième affiche notre liste d'utilisateur. Vous remarquerez qu'il n'y a pas de requête ou de logique de choix de page, pas d'accès à mysql_* ou à $_GET dans ce code. Que de l'affichage.

Le contrôleur

Puisque le contrôleur, c'est le reste, ce sera à la fois notre point d'entrée, notre code glue et notre routing.

 
<?php
 
if (isset($_GET['page']) && $_GET['page'] == 'liste') {
    require 'modele.php'
    require 'liste_utilisateurs.php'
} else {
    require 'accueil.php'
}

Et c'est tout.

Si l'utilisateur va sur l'adresse monsite.com/, il va arriver sur l'accueil, si il va sur monsite.com/?page=liste, il va atterrir sur la liste des utilisateurs.

Si on veut changer le look de la page, on modifie la vue. Si on veut changer de base de source de données (et lire par exemple depuis un fichier), on change le modèle. Si on veut rajouter des pages, on change le contrôleur. L'avantage de la séparation des responsabilités, c'est la facilité de lecture et donc de maintenance et d'évolution.

J'insiste sur le fait que c'est un exemple pédagogique, et pas quelque chose à utiliser en prod (par exemple à cause des URLs très moches). Mais il va vous permettre de coder votre premier site en MVC, et plus tard, aller vers des versions plus sérieuses.

L'important, c'est la séparation donnée / formatage / reste du code.

MVC dans la vraie vie vivante

Créer un modèle MVC à la main propre et efficace, c'est énormément de taff. C'est pour cela qu'on utilise des outils tout fait comme des frameworks Web ou des libs graphiques (Qt, wxWidget et Gtk ont toutes des outils MVC, ex : Qt possède QML, un dialecte type CSS pour manipuler des vues).

Un modèle MVC simple, mais propre, est celui du micro-framework Web Python bottle, dont Max vous avait parlé ici. Lisez l'article, et revenez à ce paragraphe, et vous comprendrez que :

  • La vue, c'est le template.
  • Le modèle n'est inclus dans bottle, il faut le faire à la main ou utiliser quelque chose comme un ORM (peewee est très bien adapté).
  • Le contrôleur, ce sont les fonctions qu'on trouve sous les décorateurs @url.

Comme je l'ai dit précédemment, il y a de nombreuses manières de séparer le contenu de sa présentation.

Django par exemple n'utilise pas un modèle MVC au sens traditionnel, mais plutôt du MVT (Modèle - Vue - Template). Ce qu'il appelle les vues sont en fait ce qu'on appelle le contrôleur dans bottle : les fonctions qui mélangent les données avec le template. Django propose par contre un ORM, qui est bel est bien un système de modèle très élaboré.

C'est une question de sémantique, et au final, qu'importe le flacon, pourvu qu'on ait l'ivresse.

flattr this!

]]>
http://sametmax.com/quest-de-que-mvc-et-a-quoi-ca-sert/feed/ 23
Ignorer certains caractères spéciaux dans un template django http://sametmax.com/ignorer-certains-caracteres-speciaux-dans-un-template-django/ http://sametmax.com/ignorer-certains-caracteres-speciaux-dans-un-template-django/#comments Mon, 09 Dec 2013 07:04:51 +0000 Sam http://sametmax.com/?p=8268 Hier Max me demandait comment mettre un template Javascript dans un template Django s’ils utilisent la même syntaxe.

La réponse : utiliser le tag “verbatim” :

{% verbatim %}
  Mettre ici le code que django doit afficher tel quel, sans interpréter.
{% endverbatim %}

flattr this!

]]>
http://sametmax.com/ignorer-certains-caracteres-speciaux-dans-un-template-django/feed/ 6
Anecdotes sexuelles à travers le monde http://sametmax.com/anecdotes-sexuelles-a-travers-le-monde/ http://sametmax.com/anecdotes-sexuelles-a-travers-le-monde/#comments Sun, 08 Dec 2013 11:26:17 +0000 Sam http://sametmax.com/?p=8269 Franchement c’était mal parti. Je voulais écrire un article de cul ce matin, mais malgré un dépassement de nos 129 drafts (oui, ça continue à augmenter…), j’avais envie de rien.

J’ai demandé à Max si il avait fini son retour d’expérience sur l’épilation permanente des poils des couilles, mais il a laissé la machine en France sans avoir pu terminer ses séances. Du coup, c’est pas concluant. Un indice toute de même : ça fait mal.

Et puis je suis tombé sur ça :

Photo d'une manif "we want porn in iraq"

La cruauté de l'homme n'a-t-elle donc pas de limite ?

Et ça a fait tilt

Je péroquette, mais Max et moi on a pas mal voyagé. Et j’ai pu voir pas mal de manières différentes d’aborder la sexualité. Par exemple il est très difficile de baiser dans certains pays.

Que quelqu’un m’éclaire, impossible de coucher avec une indienne (je ne parle pas de prostitution, bien entendu). Je suis arrivé jusqu’à boire un verre. J’ai eu tous les signes d’intérêts possibles, mais impossible d’aller plus loin. Je dois être complètement à côté de la plaque sur le marché indien.

Il y a aussi bien évidement, le Japon. On vous avait déjà parlé de l’espèce de dichotomie qu’il y a entre une part de la population japonaise super inhibée (mon frère en revient, et j’ai des amis qui y ont vécu qui en témoignent) et la représentation pornographique qui peut en être faite.

Apparemment, il y a un pan de la population, appelé “herbivore”, qui est complètement asexuée. Pourtant les témoignages que j’en ai, c’est qu’il y aussi un monde de la nuit particulièrement décontracté sur le sujet.

Mais je ne parle pas ici d’expérience, puisque je n’y suis pas encore allé en personne.

Par contre, ce qui m’a bluffé, c’est l’Afrique

Par exemple, en Algérie, le sexe est super taboo. Du coup, quand j’entrais dans les cyber café, je voyais que des mecs sur des sites pornos.

Et uniquement sur ça :

Photo d'une jeune femme blonde utilisant une godmichet

Avec le voile, ça rend moins bien il faut avouer

Des blanches, blondes.

Rien d’autre. Pas l’actu. Pas de jeux flash. Même pas facebook.

Du cul, partout, sur tous les postes.

Au mali, j’ai donné un cours de Python avec un de mes exercices habituels : un téléchargeur d’image pornos. Je sais, je suis un super prof.

Cet exercice passe super bien en Europe, mais là, choc culturel, les mecs ont rougi. Si si, rougi. Un noir qui rougi, c’est trop choupinet.

On parle du même pays qui a pour coutume d’enfermer les jeunes mariés pendant une semaine dans leur chambre. Enfermer. Je le promet. Ils sont nourris par un membre de la famille, et ne sortent pas avant la fin de la lune de miel.

Mon collègue marié sur place m’a confié qu’il avait pris ses précautions avec wifi + DVD…

En Uganda, un soir à mon hôtel, je commande, tard, un coca. Apparemment, ça doit être un mot de passe pour “envoyez moi une pute svp”, parce que ma serveuse d’étage était habillée sexy, bien maquillée, et après avoir servi mon soda a attendu là, sans rien dire, en me regardant. Généralement le service de chambre fait mine de demander un pourboire et se casse le plus vite possible.

Devant mon air intrigué, elle me demande si je n’ai VRAIMENT pas besoin d’autre chose… En sortant de ma chambre, je croise un vieux accompagné de deux paires de jambes de 3 mètres 12, et je comprends le principe.

Chez nous, c’est pas mal aussi

Bon, l’Europe de l’Est, c’est hyper sexualisé dans les grandes villes, mais ça toute personne qui y va s’en rend compte. On vous a déjà aussi parlé des FKK en Allemagne.

Non, ce qui est étonnant, c’est de voir de ses propres yeux le bordel de la sexualité aux USA. Vous avez d’un côté le monde de la pub qui montre des éphèbes, et de l’autre la population réelle qui est remplie de gens en mauvaise santé, souvent gros, rarement beaux, et mal dans leur peau.

Vous avez des bars où les nanas se bourrent la gueule, et on peut venir les ramasser limite sans rien dire, comme en Angleterre après 23h. Et vous avez les meufs hyper prudes qui ne couchent pas avant le mariage. C’est vrai, ce n’est pas un sitecom, c’est la réalité de tous les jours.

Max a tendance à me dire que la France est un pays de frustrés sexuels, avec des meufs hyper-éxigentes par rapport à ce qu’elle ont à offrir et qui ont peur de tout, et des gars sans couilles qui ne respectent pas leurs envies et en deviennent des boulets avec une vie de merde.

Analyse agressive, mais j’ai du mal à le contredire certains jours. Et je pense que c’est une caricature qui peut s’appliquer à la plupart des pays occidentaux industrialisés. C’est triste.

Dans le domaine du cul, je n’ai pas encore croisé un pays où la société ait une approche globalement saine. C’est à dire des gens qui ne tombent pas systématiquement dans un excès. Après tout, attendre le mariage, pourquoi pas. Mettre une meuf à poil pour vendre une voiture, pourquoi pas. Regarder du porno hardcore dans un cyber, pourquoi pas. Mais quand on est dans une systématisation d’un comportement qui est en plus éloigné de la réalité, ça craint. Qu’on ne viennent pas me dire que ce sont des causes, ce sont des symptômes.

flattr this!

]]>
http://sametmax.com/anecdotes-sexuelles-a-travers-le-monde/feed/ 21
Petite astuce d’unpacking en Python http://sametmax.com/petite-astuce-dunpacking-en-python/ http://sametmax.com/petite-astuce-dunpacking-en-python/#comments Sat, 07 Dec 2013 08:46:44 +0000 Sam http://sametmax.com/?p=8245 L’unpacking, fonction géniale de Python s’il en est, peut se faire sur un seul element :

>>> a = [1]
>>> b, = a
>>> b
1

Pour cet exemple, pas super utile. Par contre dans une boucle :

>>> l = ([1], [1], [1])
>>> for i, in l:
...     print(i)
...     
1
1
1

flattr this!

]]>
http://sametmax.com/petite-astuce-dunpacking-en-python/feed/ 1
Introduction à Ansible: l’outil du sysadmin paresseux mais pragmatique http://sametmax.com/introduction-a-ansible-loutil-du-sysadmin-paresseux-mais-pragmatique/ http://sametmax.com/introduction-a-ansible-loutil-du-sysadmin-paresseux-mais-pragmatique/#comments Fri, 06 Dec 2013 10:43:19 +0000 VonTenia http://sametmax.com/?p=8177 Ceci est un post invité de VonTenia posté sous licence creative common 3.0 unported.

Je profite du fait que Sam & Max me donnent la parole pour vous parler d’Ansible, un programme très puissant et relativement simple dont je me sers depuis récemment (beaucoup trop tardivement à mon goût), mais qui a radicalement changé ma façon de gérer mes déploiements d’appli sur serveur.

Avant-propos : Ce guide s’adresse avant tout à ceux et celles ayant le minimum d’aisance avec les systèmes linux. Je pense qu’il est nécessaire de savoir marcher avant d’apprendre à courir, l’automatisation de configuration est une bonne chose (vous allez voir que vous ne pourrez plus vous en passer), mais si vous n’avez aucune idée de comment éditer un fichier de configuration, ou comment redémarrer un service, vous risqueriez bien d’être pris au dépourvu… Mieux vaut alors pour vous commencer par apprendre les bases de l’administration système puis revenir une fois à l’aise avec le concept.

Pourquoi utiliser un “Configuration Management Tool”

Vous vous dites : mon boulot c’est de coder, l’administration système c’est sympa 5 minutes mais ça me gonfle… Et pourtant, au final votre application sera accédée via vos serveurs, et selon leur fragilité, la satisfaction de vos clients pourrait en pâtir (ce malgré votre excellent code parfaitement testé).

En tant que dev, il serait risible pour vous de ne pas versionner votre code ou ne pas le tester. Pourtant c’est ce que vous faites avec vos systèmes en n’utilisant pas de CfM. Et personne n’est à l’abri des aléas de la vie, par exemple:

  • vous êtes hébergé dans le cloud et votre voisin d’hyperviseur s’avère maintenir un site Tor de trafic d’organes et de prostitution animalière (tout ça pour blanchir des bitcoins)… Vous apprécierez moyennement le downtime lorsque le FBI saisira le serveur que vous partagiez avec cet indélicat voisin.
  • Votre sysadmin ultra compètent pourrait se trouver dans l’incapacité d’exercer à la suite d’une banale auto-asphyxie érotique qui aurait mal tournée. Et bien évidemment il n’a rien documenté avant, le saligaud…

Bref, le genre de risque qu’on apprend à identifier quand on passe sa certif ITIL…

Les alternatives aux CfM

Je vous vois venir, me disant que vous ne m’avez pas attendu pour envisager les situations précédentes. Vous avez déjà un plan de secours, à savoir :

  • Des scripts : Si vous êtes déjà un peu plus malin que la moyenne, vous vous êtes aperçu que pour déployer une nouvelle machine, vous tapez toujours les mêmes commandes : installer vos packages, les configurer, démarrer les services. Vous aurez donc votre collection de scripts shell ou fabric pour vous aider à la tâche.

    Inconvénient : il faut être très organisé, gérer les différents fichiers de config peut prendre du temps lorsqu’il faut les modifier pour chaque serveur. Il est aussi parfois dangereux de relancer le script plusieurs fois sur la même machine, cela peut avoir des conséquences pour le moins hasardeuses.

  • Une image disque : Une fois votre serveur configuré et parfaitement fonctionnel, vous avez pris soin d’en prendre une image disque. Le saint Graal de la prod, contenant la vérité absolue. En cas de crash vous serez opérationnel en un rien de temps.

    Inconvénient : Les besoins de votre application vont évoluer avec le temps, les fichiers de configuration auront probablement changé aussi. A chaque changement vous devez refaire votre image… Ça devient assez lourd à la longue, et c’est facile d’oublier de le faire jusqu’au jour où “the shit hit the fan”.

  • Rien (ou presque) : Si vous êtes comme moi, d’un naturel optimiste, vous n’avez quasiment pas de solution de secours (à part des backups). Le jour ou votre serveur crash, vous essayez de fixer le problème, et au pire vous en re-configurez un nouveau, ce qui vous prends entre 2 heures et 2 jours, en fonction de la dernière fois où vous avez eu à le faire (je n’ai jamais prétendu être un sysadmin très compètent…). Sans aller jusqu’au crash irrécupérable, le simple fait de vouloir changer d’hébergeur peut vous faire perdre un temps précieux. Vous perdez donc en flexibilité.

    Inutile de vous dire que si vous faites parti de cette catégorie, je vous invite d’autant plus à continuer de lire.

Ansible à votre secours

Ansible est un outil open-source de gestion de configuration écrit en python (aussi dispo en version commerciale avec une interface graphique et un service de déploiement). La configuration se fait via des fichiers appelés “Playbooks”. Citons parmi les avantages :

  • Un système déclaratif : syntaxe YAML facilement lisible, ce qui rend l’apprentissage très rapide (plus qu’avec Chef à mon goût, où l’on s’empêtre très vite dans des problèmes de dépendance, et en plus je ne suis pas très fluent en ruby).
  • Templating des fichiers de configuration : qui permet d’avoir des fichiers dynamiquement générés en fonction de ce que vous voulez, tel que le rôle du serveur, ou bien dépendant d’un autre serveur. En plus le langage de template par défaut est Jinja2, ça plaira aux amateurs de Django.
  • Quasiment rien à installer. A part Ansible sur votre machine hôte, tout ce dont vous avez besoin c’est d’un accès root via SSH sur vos serveurs cibles.

Ansible ne sert pas qu’à déployer votre infrastructure, il peut aussi servir à tester et s’assurer que tous les services qui sont censés fonctionner soient bien tous actifs, et que tous les fichiers de configurations sont bien à jour. Autant vous dire que plus vous avez de machines, plus ça devient intéressant.

Je sens que j’écris beaucoup et que j’ai déjà perdu la moitié des lecteurs. Aussi je vous invite à suivre ce petit tutoriel que j’ai préparé rien que pour vous parce que vous êtes quand même sympa.

Tutoriel : Déployer une app django

Nous allons essayer de déployer l’application Django–an-app-at-a-time sur un système Debian wheezy en utilisant Ansible.

1. Préparer la machine cible

Pour les besoins du test, créer un serveur tout neuf sous Debian Wheezy :

  • Soit en utilisant Virtualbox (dans ce cas utilisez Debian netinstall). N’installez que le système de base et le serveur SSH. Seul impératif: un accès ssh via root sur la machine.
  • Si vous avez les moyens, vous pouvez aussi vous créer temporairement une machine cloud sur OVH ou digital ocean, ça pourrait être plus rapide.

2. Installer Ansible

Sur votre machine hôte (que j’assume être sous Ubuntu pour simplifier):

Installez Ansible, via pip (de façon globale sans passer par virtualenv) :
$ sudo pip install ansible

Générez clefs privée/publique si vous n’en avez pas déjà :
$ ssh-keygen

Copiez la clef publique sur le serveur cible (qui sera désigné par 192.168.1.1 dans ce tutoriel, mais bien entendu remplacez par l’adresse de votre serveur cible).
$ ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.1.1

Créez le fichier /etc/ansible/hosts qui contiendra la liste des serveurs à gérer, et placez-y l’adresse de votre serveur:
$ sudo vim /etc/ansible/hosts
192.168.1.1

Testez que le serveur soit bien accessible:
$ ansible all -m ping -u root
devrait retourner:
192.168.1.1 | success >> {
"changed": false,
"ping": "pong"
}

Bravo, Ansible est installé et peut communiquer avec votre serveur cible. En avant pour la magie !


3. Récupérer le Playbook de démo et l’exécuter

Clonez mon repo github concocté avec amour et exécutez le playbook:

$ git clone https://github.com/Remiz/playbook-demo.git
$ cd playbook-demo/
$ ansible-playbook site.yml

Maintenant, plus qu’à attendre…

3. Admirer le résultat

Visitez le site hébergé à l’adresse de votre serveur (dans mon exemple http://192.168.1.1)

Votre réaction la plus normale devrait être la suivante :

Je vous invite maintenant à ouvrir le playbook site.yml et essayer de comprendre. Durant ce court laps de temps, nous avons:

  • Créé un utilisateur “myproject”
  • Ajouté cet utilisateur aux sudoers
  • Ajouté votre clef privée locale
  • Mis a jour la date du serveur
  • Installé/activé le serveur NTP
  • Sécurisé le serveur en installant fail2ban et en configurant le firewall iptables (laissant ouvert les ports 22, 80, 443 et 4949 pour le monitoring sous munin)
  • Installé quelques outils systèmes bien utiles tel que git ou htop
  • Installé/configuré Nginx, Supervisor, Pip, virtualenv
  • Cloné le repo Django–an-app-at-a-time
  • Créé un virtualenv avec django/gunicorn
  • Configuré gunicorn pour être lancé via supervisor
  • et finalement deployé les fichiers statiques…

Pas mal en 5 minutes, non ? Maintenant si vous ne me croyez pas, je vous invite à vous connecter sur votre serveur

$ ssh myproject@192.168.1.1

et tester les commandes suivantes :

$ date
$ sudo iptables -L
$ ps -ef | grep fail2ban
$ ps -ef | grep gunicorn

Notez que je n’ai pas utilisé runserver de Django, tout est proprement déployé sur une stack gunicorn/supervisor/virtualenv, bref je me suis pas foutu de votre gueule. Le Playbook est à vous, c’est cadeau. J’espère qu’il vous servira comme base pour vos futurs déploiements, et si jamais vous vous rendez compte que vous gagnez un temps fou à l’utiliser, n’hésitez pas à me payer une pinte si vous êtes de passage au Canada.

Une autre expérience intéressante consiste à relancer l’exécution du playbook :

$ ansible-playbook site.yml

Tout devrait aller beaucoup plus vite, et à la place de “changed” après chaque instruction, vous devriez lire “ok”. Ce qui veut dire qu’un playbook est plus intelligent qu’un bête script, et ne se contente pas d’exécuter des instructions, Ansible va garantir quel tel service soit bien actif et qu’il utilise bien le dernier fichier de conf. Ce qui en fait l’outil parfait pour tester vos systèmes automatiquement.

La syntaxe Playbook

Le but de ce tutoriel n’est que de vous présenter Ansible, aussi je ne rentrerai pas trop dans les détails et je vous inviterai à vous rendre sur le site officiel pour une documentation plus complète.

Un playbook est avant tout composé de tâches :

- name: Texte qui décris votre tâche
module: option=value

Une tâche va donc appeler un module Ansible, dont la fonction peut être de copier un fichier, démarrer un service, clôner un repository… Il y en a vraiment beaucoup. Chaque module reçoit des paramètres tels que : un fichier de configuration source (sur votre machine hôte), un path de destination, un package apt à installer… Référez vous à la doc pour savoir quels paramètres sont acceptés.

Exemples :

- name: Démarrer fail2ban
service: name=fail2ban state=started enabled=true

va s’assurer que le service appelé fail2ban soit bien démarré (le démarrer si ce n’est pas le cas), mais aussi s’assurer qu’il soit bien présent au démarrage du système. Quand je vous disait que la syntaxe est très simple (même plus simple qu’avec des scripts shell).

Autre exemple:

- name: Configurer Nginx
template: src=templates/nginx.conf.j2
dest=/etc/nginx/sites-enabled/{{ user }}.conf
notify: restart nginx

se traduit par : récupérer le template de conf dans le répertoire local templates/ (le parser avec les bonnes valeurs), et placer le résultat dans le répertoire de conf de Nginx (en utilisant le nom d’utilisateur comme nom du fichier). Enfin redémarrer nginx via un handler (uniquement si le contenu du fichier de conf a changé).

Conclusion (et aller plus loin)

Je vous conseille de lire la documentation officielle, elle est plutôt bien faite, dites-vous que je ne connaissais pas du tout cet outil il y a deux semaines et je m’en sert désormais régulièrement (et je suis du genre slow-learner). Renseignez vous particulièrement sur les rôles que vous pouvez donner à vos serveurs, ce qui vous permet de diviser vos playbooks (frontend, cluster DB, worker celery…), et ce qui encourage aussi la réutilisation (par exemple j’ai toujours un rôle “common” qui inclus tout ce qui est nécessaire à l’ensemble de mes serveurs : utilisateur admin, sécurité, timezone…). Comme on n’apprend jamais aussi bien que par l’exemple, n’hésitez pas à vous inspirer des exemples issus de la doc (l’outil évolue vite et certains ne sont plus entièrement valides, mais c’est toujours bon à prendre).

Si vous voulez pousser l’automatisation jusqu’à l’extrême, il est aussi possible de configurer Ansible sur vos serveurs pour se connecter à un repo git, récupérer les playbooks et fichiers de conf appropriés et s’auto-configurer…

Voila, mon rôle s’arrête ici et libre à vous d’en apprendre plus. Au final j’espère avoir tenu ma promesse d’éclairer vos esprit sur les miracles de l’automatisation.

flattr this!

]]>
http://sametmax.com/introduction-a-ansible-loutil-du-sysadmin-paresseux-mais-pragmatique/feed/ 28
En Python 3, le type bytes est un array d’entiers http://sametmax.com/en-python-3-le-type-bytes-est-un-array-dentiers/ http://sametmax.com/en-python-3-le-type-bytes-est-un-array-dentiers/#comments Thu, 05 Dec 2013 16:00:32 +0000 Sam http://sametmax.com/?p=8160 Le plus gros changement quand on passe de Python 2 à Python 3, c’est la gestion des chaînes de caractères.

Pour rappel :

  • En 2.7, les chaînes sont par défaut des arrays d’octets, et il faut les décoder pour obtenir de l’unicode.
  • En 3, les chaînes sont par défaut de type ‘unicode’, et il faut les encoder pour obtenir de un array d’octets.

Si vous avez besoin d’une mise à jour sur l’encoding en Python, on a un article pour ça.

Comme toute entrée ou sortie est forcément un flux d’octets, mais pas forcément dans le même encodage, Python 2.7 pouvait poser problème pour le débutant qui essayait de comprendre pourquoi son programme plantait, bordel de merde.

La version 3 prend plusieurs mesures pour éviter les bugs vicieux liés à l’encodage de caractères:

  • L’encodage par défaut du code est UTF8.
  • L’encodage par défaut de lecture et d’écriture est UTF8.
  • On ne peut plus mélanger ‘bytes’ et ‘unicode’.
  • Les messages d’erreur expliquent clairement et tôt tout problème.

La plupart du temps, quand on va manipuler du texte, on va donc toujours manipuler de l’unicode, en Python 3. Ce dernier va nous forcer à faire le décodage / encodage au bon moment.

Mais il restera quelques fois le besoin de manipuler du bytes, et ce type a subi un lifting…

La base

Créer un array d’octets (le type bytes‘, en Python 3) demande de préfixer une chaîne avec ‘b’ :

>>> s = b'I am evil, stop laughing!'
>>> type(s)
<class 'bytes'>
>>> print(s)
b'I am evil, stop laughing!'

Première remarque, on ne peut plus utiliser ce type pour afficher quoi que ce soit, puisque l’affichage est une représentation du type (appel à __repr__), et pas du texte mis en forme. Déjà Python vous indique la couleur : si vous voulez manipulez du texte, n’utilisez pas ce type.

Comparez avec le type unicode :

>>> u = s.decode('utf8')
>>> type(u)
<class 'str'>
>>> print(u)
I am evil, stop laughing!

L’affichage marche comme on s’y attend. Bref, vous êtes forcé de toujours rester sur de l’unicode (le type str en Python 3, ce qui porte à confusion) si vous manipulez du texte. Heureusement, c’est quasiment toujours ce que vous aurez.

Par exemple, si vous ouvrez un fichier en Python 3 :

>>> content = open('/etc/fstab').read()
>>> type(content)
<class 'str'>

C’est du texte. A moins de demander qu’il soit ouvert en mode binaire :

>>>> content = open('/etc/fstab', 'rb').read()
>>> type(content)
<class 'bytes'>

Une autre différence MAJEURE, c’est que, si dans Python 2.7, les arrays d’octets pouvaient être manipulés comme un array de lettres :

>>> s = 'I put the goal in golem...' 
>>> s[0] # en Python 2.7
>>> 'I'

En Python 3, les array d’octets sont au mieux manipulables comme un array d’entiers :

>>> s = b'I put the goal in golem...'
>>> s[0] # en Python 3
73

La représentation sous forme de lettre est gardée pour l’initialisation pour des raisons pratiques, mais sous le capot, il se passe ça:

>>> bytes([73, 32, 112, 117, 116, 32, 116, 104, 101, 32, 103, 111, 97, 108, 32, 105, 110, 32, 103, 111, 108, 101, 109, 46, 46, 46])
b'I put the goal in golem...'

D’ailleurs, on ne peut même plus faire d’opérations de formatage avec des octets comme en Python 2.7 :

>>> b"Welcome to the league of %s" % input('')
Draven
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for %: 'bytes' and 'str'

format() ne marche pas non plus. On est assez proche du tableau d’octets en C, sauf qu’en plus, on ne peut pas le modifier :

>>> s = b"My right arm is a lot stronger than my left arm."
>>> s[0] = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'bytes' object does not support item assignment

Les arrays d’octets sont donc maintenant essentiellement des outils de communication avec le monde extérieur.

Bytearray

Il existe encore des raisons de manipuler des arrays d’octets : les applications scientifiques. Typiquement, les algos de crypto opérent sur des arrays d’octets.

Pour cette raison, Python 3 vient également avec un nouveau type de base : bytearray, un array d’octets modifiable.

>>> s = bytearray(b"this tasted purple !")
>>> s[2:4] = b'at'
>>> print(s)
bytearray(b'that tasted purple !')

Et on a toutes les opérations de liste dessus, comme append, pop(), etc :

>>> for x in b' ,puY':
...     s.insert(0, x)
... 
>>> print(s)
bytearray(b'Yup, that tasted purple !')

Attention par contre, ces opérations attendent un entier en paramètres et NON un array d’octets.

Et un dernier détail :

>>> isinstance(bytes, bytearray)
False
>>> isinstance(bytearray, bytes)
False

Différence entre string et array d’octets

Il est facile de confondre tout ce merdier.

En Python 2.7, le type str était un array d’octets, et on le manipulait comme une chaîne, d’où la difficulté de transition.

En Python 3, bien qu’on puisse créer un array d’octets avec une syntaxe utilisant des lettres, ils ne sont plus du tout utilisés pour la manipulation de texte. Si vous voulez manipuler du texte qui vient de l’extérieur de votre programme, il faudra toujours le décoder pour obtenir un type str (qui est l’ancien type unicode de Python 2.7).

Le décodage sera fait automatiquement dans la plupart des cas, et plantera si on tombe sur un cas où vous devez le faire à la main et que vous ne le faites pas. Du coup, plus de difficulté à trouver d’où vient ce bug d’encoding, car on a toujours l’erreur à la source.

En ce sens, Python 3 est beaucoup plus clair : les octets d’un côté, le texte de l’autre. Bon, tout ça c’est de la surcouche, au final, tout est octet. Mais on a rarement envie de manipuler un octet directement, sinon on coderait encore en assembleur.

Avec ce système, Python 3 est le langage le plus sain que j’ai pu rencontrer dans sa gestion de l’encodage : il ne cache rien, oblige l’utilisateur à coder avec de bonnes habitudes, facilite le débugage et met sur le devant de la scène la problématique de l’encoding, qui est le plus souvent cachée vite fait sous le tapis.

L’alternative intelligente la plus proche étant celle de node.js, qui interdit tout simplement la plupart des encodings dans son API.

La bonne nouvelle ? 99% du temps, vous n’aurez même pas à vous en soucier, car ASCII est inclus dans UTF8, et ce sont les encodings les plus utilisés. Avec Python 3 forçant UTF8 par défaut partout et des chaînes en unicode dès le départ, il n’y a presque rien à faire. Je doute que la plupart des gens aient même à manipuler le type bytes.

flattr this!

]]>
http://sametmax.com/en-python-3-le-type-bytes-est-un-array-dentiers/feed/ 16