PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

Rechercher dans l’historique de Git

mercredi 17 octobre 2012 à 15:47

Si vous voulez trouver tous les commits dont les messages contiennent une chaîne de caractères:

git log --grep="le truc a chercher"

Comme grep, on peut y adjoindre -i pour le rendre insensible à la casse, et -E pour utiliser des regex PCRE.

Si vous voulez trouver tous les commits dans lequel une chaîne a été retirée ou introduite dans un des fichiers:

git log -S"le truc a chercher"

Et dans ce cas vous voudrez probablement passer aussi -p --color=auto qui va vous permettre d’avoir un diff coloré et mis en contexte pour savoir ce qui a été modifié.

On peut limiter sa recherche dans le temps en utilisant --before et --after. Par exemple, pour chercher dans tous les commits créés après le 1er juillet et jusqu’à il y a 2 semaines:

git log --before="2 weeks ago" --after="2012-07-01"

Ou alors choisir deux commits et chercher entre ces deux commits:

git log 710f0f..8a5cbc

Ce qui va limiter la recherche à la branche en cours. En parlant de branche, on peut voir les commits qui sont sur une branche et pas l’autre:

git log master..develop

Ici on aura tout ce qui est sur develop qui n’est pas encore mergé dans master. L’inverse est bien sur possible.

Parfois on a besoin de savoir qui est le connard qui a fait ça, et on peut lister les commits d’un utilisateur:

git log --author sam

Ou

git log --author lesametlemax@notremail.com

En fait ça marche même avec une partie du nom.

Les critères ci-dessus peuvent être utilisés en même temps, et les résultats obtenus seront ceux correspondant à au moins un des critères (OU). Mais si vous voulez les commits qui correspondent à tous ces critères (ET), utilisez --all-match.

Tout ça cherche dans l’historique entier, mais si vous êtes plutôt intéressé par certains fichiers en particulier, vous pouvez préciser des paths après --.

git log --author max --before="1 day ago" -- project/settings.py project/settings_local.py

Et oui, ça marche récursivement sur les dossiers.

La plupart du temps les commits juste après les merges ne vous intéresse pas. On peut les ignorer avec –-no-merges.

Enfin si vous avez merdé et que vous pensez avoir perdu quelque chose dans Git, vous pouvez chercher dans le reflog avec --walk-reflogs. Cela vous donnera accès à tous les commits qui ne sont pas sur des branches ou plus accessibles facilement pour une raison ou une autre.

Générateur de nom d’heroic fantasy en Python

mardi 16 octobre 2012 à 18:21

Les générateurs de noms d’heroic fantasy sont légions sur internet. Mais autant j’ai trouvé des générateurs de noms normaux en Python (dont je me sers d’ailleurs pour créer des comptes bidons automatiquement pour les démos), autant je n’ai rien trouvé pour pythonement printer l’en-tête d’une feuille de personnage de DDA.

Et comme c’est fun, voilà le nonos du Sam du jour:

from random import randint, choice
 
class FantasyNameGenerator(object):
    """
        Random fantasy name generator
    """
 
    # on déclare les lettres, leurs positions voulues dans le nom
    # et un poids de fréquence d'apparition
 
    # voyelles
    VOWELS = (
        (('start', 'middle', 'end'), (
                (5, ("a", "e", "i", "o", "u")),
                (1, ("ae", "ai", "ao", "au", "aa", "ea", "eo", "eu", "ee", "ia",
                     "io", "iu", "ii", "oa", "oe", "oi", "ou", "oo", "eau", "y"))
        )),
        (('middle'), (
            (1, ("'",)),
        ))
    )
 
    # consonnes
    CONSONANTS = (
        (('start', 'middle', 'end'), (
                (3, ("b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p",
                     "r", "s", "t", "v", "w")),
                (1, ("x", "y", "z", "sc", "ch", "gh", "ph", "sh", "th", "sk", "wk", "st"))
        )),
        (('middle', 'end'), (
                (1, ("ck", "nk", "rk", "ss")),
        )),
        (('start', 'middle'), (
                (2, ("br", "dr", "fr", "gr", "kr", )),
                (1, ("cr", "pr", "sr", "tr", "qu", "wh", "cl", "fl", "gl", "kl",
                     "ll", "pl", "sl", "str"))
        )),
    )
 
    # on génère deux listes de type ((lettre, position), (lettre, position)...)
    # pour pouvoir piocher au hasard dedans
    SYLLABLES_POOL = [[], []]
 
    for i, group in enumerate((VOWELS, CONSONANTS)):
        pool = SYLLABLES_POOL[i]
        for place, pack in group:
            for frequency, letters in pack:
                for letter in letters:
                    pool.extend(((letter, set(place)),) * frequency)
 
 
    def __init__(self, min_syllable=2, max_syllable=None):
        self.min_syllable = min_syllable
        self.max_syllable = max_syllable or (min_syllable + 2)
 
    def __iter__(self):
        # juste pour le plaisir d'avoir le générateur itérable
        while True:
            yield self.get_new_name()
 
    # générer un nom avec les réglages de l'instance
    def get_new_name(self):
        return self.generate_name(self.min_syllable, self.max_syllable)
 
    @classmethod
    def generate_name(cls, min_syllable, max_syllable, base=""):
 
        # méthode pour générer un nom à indépendament de l'instance
 
        # on génère aléatoirement la taille du mot et le son de départ
        length, pool = randint(min_syllable, max_syllable), randint(0, 1)
 
        # pour chaque syllabe, on en choppe une au hasard et on change de pool
        # si la syllable n'est pas à la bonne place, on tire à nouveau
        for i in xrange(1, length + 1):
 
            while True:
 
                letter, place = choice(cls.SYLLABLES_POOL[pool])
 
                if i == 1:
                    if 'start' not in place:
                        continue
                elif i == length:
                    if 'end' not in place:
                        continue
                else:
                    if 'middle' not in place:
                        continue
 
                base += letter
                pool = abs(pool - 1)
                break
 
        return base.title()

Et ça s’utilise comme ça:

import itertools
 
for name in itertools.islice(FantasyNameGenerator(max_syllable=5), 5):
    print name

Et ça affiche ça:

Oislaex
Feasroap
Kab
Uthi
Iin

Ok, ok, ça sert à rien. Mais c’est marrant !

Nos tweets sans Twitter

lundi 15 octobre 2012 à 19:21

On met pas mal de choses sur le flux Twitter, mais tout le monde n’a pas envie d’y aller (ni même de s’inscrire) pour les consulter.

Du coup on a mis directement sur le site le contenu de notre feed @sam_et_max avec en plus un lien vers le flux RSS des tweets. Comme ça, pas besoin de compte pour les suivre.

On poste la plupart de nos articles dessus, donc c’est un peu redondant avec le flux des articles. Mais parfois il y a des choses sur l’un et pas sur l’autre. Les liens en vrac sont sur Twitter par exemple, mais le flux des articles contient les articles en entier, donc pas besoin d’aller sur le site, on peut même les lire dans l’avion.

Au passage, on a reçu notre premier don flattr par Twitter. Merci @Chassegnouf, ça fait super plaisir.

MySQL: créer un utilisateur avec une base de données à son nom sur laquelle il a tous les droits

lundi 15 octobre 2012 à 17:07

Le truc classique, qu’on fait tout le temps, et qu’on oublie toujours comment on a fait la dernière fois.

Vu qu’on a pas toujours PhpMyAdmin installé…

CREATE DATABASE nom_db;
GRANT ALL PRIVILEGES ON nom_db.* TO "nom_utilisateur"@"localhost" IDENTIFIED BY 'mot_de_passe';
FLUSH PRIVILEGES;

Le pattern observer en utilisant des décorateurs

dimanche 14 octobre 2012 à 17:03

Nous avons vu précédemment que les décorateurs permettaient d’exécuter du code avant et après une fonction, sans modifier la fonction. La plupart du temps on retourne ainsi une nouvelle fonction avec un comportement différent.

Mais il existe d’autres usages pour les décorateurs, et notamment un qui est au cœur du fonctionnement de django-quicky: l’abonnement.

def evenement(nom):
 
    # on assure que la liste des events et callabcks est initialisae
    evenement.abonnements = getattr(evenement, 'abonnements', {})
 
    # on ajoute un moyen d'appeler tous les callbacks pour un event
    evenement.trigger = lambda e: [f(e) for f in evenement.abonnements[e]] 
 
    # définition du décorateur lui-même
    def decorateur(func):
 
        # on ajoute la fonction comme callback pour cet event
        evenement.abonnements.setdefault(nom, []).append(func)
 
        # et on retourne la fonction telle qu'elle, sans la modifier
        return func
 
    return decorateur

Ce morceaux de code s’utilise ainsi:

# a chaque fois qu'on met le decorateur
# la fonction est liae à un événement
@evenement('evenement1')
@evenement('evenement2')
def reagir_a_evenement(evenement):
    # la fonction doit acccepter l'evenement en paramètre
    print "Oh, evenement '%s' a eu lieu" % evenement
 
@evenement('evenement1')
def moi_aussi(evenement):
    print "Cool, moi aussi j'ai reagit a l'evenement '%s'" % evenement
 
# ici on déclenche l'événement sans raison
# mais dans du vrai code on le déclenche à la suite
# d'une action réelle
evenement.trigger('evenement1')
evenement.trigger('evenement2')

Ici c’est un exemple simplifié, mais le principe y est: chaque fois qu’on utilise un décorateur, on associe la fonction décorée à un nom (ici le nom de notre événement, mais dans django-quicky, c’est l’url). Et quand l’événement se produit, on appelle toutes les fonctions enregistrées, en leur passant l’objet événement (ici une simple string) en paramètres.

Avec ce design pattern appelé ‘observer’, on découple complètement le code qui déclenche un événement (la lecture d’un fichier, une erreur, un entrée utilisateur, etc) et le code qui réagit à cet événement (qui peut très bien être celui d’une lib complètement séparée).

Le décorateur Python est un moyen particulièrement pratique de déclarer un abonnement d’une fonction à un événement, et ne demande quasiment aucun effort de la part du développeur qui l’utilise à part d’avoir une fonction qui accepte les bons paramètres pour réagir à tous les événements que peuvent produire votre library.