PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

Vous pouvez mettre du code dans __init__.py

mercredi 26 juin 2013 à 13:20

Le fichier __init__.py ne sert pas qu’à déclarer un dossier comme un package importable. C’est aussi le code exécuté automatiquement, une seule fois, quand on importe le module.

Du coup vous pouvez mettre dedans tout code Python que vous souhaitez lancer à l’import. On y voit souvent :

Je vous déconseille de mettre trop de code dans le fichier __init__.py, notamment du code métier. C’est une mauvaise habitude que l’on peut voir dans le code de Django par exemple. Car ça veut dire que l’import du package déclenche ce code, qui lui-même importe d’autres modules, qui déclenche d’autres codes, etc. Cela donne des effets de bord à l’import, alors que l’import d’un simple package est quelque chose que l’on veut généralement ne pas avoir beaucoup d’effets.

Dans Django par exemple, c’est ce qui fait que beaucoup de modules lèvent une exception à l’import :

django.core.exceptions.ImproperlyConfigured: Requested setting X but settings are not configured.

Alors qu’ils n’ont pas du tout besoin des settings pour fonctionner.

Si vous avez besoin que votre classe Bidule soit directement importable dans votre package machin, faites dans machin/__init__.py :

from .module_qui_contient_bidule import Bidule

Du coup vous pourrez faire n’importe où ailleurs:

from machin import Bidule

C’est bien plus propre que de mettre tout le code de Bidule dans le __init__.py.

flattr this!

Github comme hébergeur gratuit de fichiers

mardi 25 juin 2013 à 22:35

J’inaugure une nouvelle technique de feignasse : copier / coller les mails que je réponds à un lecteur, tel quel.

Ici on me demandais (je crois ^^) comment faire pour donner un lien de téléchargement d’un code sous Gitub. Voici l’info, si ça vous intéresse.

Sur github, tu peux sans problème donner un zip à télécharger, tant que
ton repository est publique.

Par exemple, notre lib batbelt est à l’URL github :

https://github.com/sametmax/Bat-belt/

Il suffit de rajouter :

archive/master.zip

Et c’est téléchargeable :

https://github.com/sametmax/Bat-belt/archive/master.zip

En plus ce sera toujours la dernière version. Ca marche pour toutes les
branches et tous les tags, mais aussi n’importe quel commit :

https://github.com/sametmax/Bat-belt/archive/96ca096bb9eead0675186b36c576251935e96cf9.zip

Pour avoir un fichier en particulier, il faut prendre l’URL du fichier,
et la préfixer de “raw” et retirer “blob”. Par exemple, le fichier :

https://github.com/sametmax/Bat-belt/blob/master/batbelt/structs.py

Est téléchargeable à :

https://raw.github.com/sametmax/Bat-belt/master/batbelt/structs.py

Là le fichier est accessible directement, et téléchargeable avec Ctrl + S.

@+

Alala, qu’est-ce que ne ferais pas un blogger pour ajouter du contenu à son blog en en branlant le moins possible ?

flattr this!

Exécuter du code à tout lancement de Python

lundi 24 juin 2013 à 12:54

Je vous avais montré comment lancer du code au démarrage du shell iPython, et on peut faire quelque chose de similaire pour le shell Python ordinnaire avec la variable d’environnement PYTHONSTARTUP:

export PYTHONSTARTUP=/chemin/vers/fichier/python/a/executer/au/demarrage.py

Mais comment lancer du code au démarrage pour Python quand on est PAS dans un shell ?

Comme pour l’exécution du code à la fermeture de la VM, il existe bien entendu un mécanisme pour cela.

Ouvez un shell, et tapez:

>>> import site
>>> site.getusersitepackages()
'/home/sam/.local/lib/python2.7/site-packages'

Vous obtenez ainsi le dossier des sites packages locaux, c’est à dire un dossier qui est dans le PYTHON PATH, mais uniquement pour l’utilisateur courant. Donc vous pouvez mettre dedans toute bibliothèque Python que vous voulez importable de partout, mais uniquement pour vous.

Dans ce dossier, créez un fichier nommé usercustomize.py, et voilà ! Tout ce qui est dans ce fichier est automatiquement exécuté au démarrage de Python.

Attention cependant, il est exécuté très tôt, et donc certaines choses ne sont pas encore chargées, telle que sys.argv ou __builtin__.

Il faut donc ruser un peut. Si vous êtes une loque qui n’aime pas taper import ipdb; ipdb.set_trace(), vous pouvez par exemple mettre dedans :

import __builtin__
 
from pdb import set_trace
 
__builtin__.set_trace = set_trace

Et maintenant, dans votre code, vous pourrez appeler set_trace() de pdb directement, sans import, sans préfixe.

Attention tout de même à ce que vous allez mettre là. Si vous commencez à trop trafiquer votre installation, vos programmes pourraient marcher dessus et pas ailleurs. Testez toujours vos logiciels sur des installations de Python standards.

Bonus point: le code de startup d’un codeur Python scientifique.

flattr this!

Refactoriser ses vérifications en Python

dimanche 23 juin 2013 à 23:56

On nous a interpelé sur Twitter pour nous demander comment faire un code comme ceci (en tout cas similaire) plus propre:

# -*- coding: utf-8 -*
 
from __future__ import unicode_literals
 
import random
 
def verification1(val):
    return bool(random.randint(0, 1))
 
verification2 = verification1
verification3 = verification1
 
def function(valeur):
 
    if not verification1(valeur):
        print("Erreur, la vérification 1 n'est pas passée")
 
    if not verification2(valeur):
        print("Erreur, la vérification 2 n'est pas passée")
 
    if not verification3(valeur):
        print("Erreur, la vérification 3 n'est pas passée")
 
    return valeur

Ceci est évidement un exemple tout bidon, mais la problématique, c’est qu’on a plein de vérifications à faire, et un code à lancer si jamais ces vérifications échouent. Et chaîner les if, c’est vrai que c’est pas super mignon (même si c’est tout à fait valide, faut pas non plus se prendre trop la tête avec ça).

En Python, on a plusieurs manières de s’occuper de ça. La première, j’en ai déjà parlé, c’est l’injection de dépendances. On va passer ces checks en paramètres.

Car je le rappelle, on peut passer des fonctions en paramètres, on peut mettre des fonctions dans des listes, et on peut même passer des listes de fonctions en paramètres. Ce qui ici va nous permettre de :

 
 
VERIF = (
    (verification1, "Erreur, la vérification 1 n'est pas passée"),
    (verification2, "Erreur, la vérification 2 n'est pas passée"),
    (verification3, "Erreur, la vérification 3 n'est pas passée"),
    ((lambda v: not bool(random.randint(0, 1))),
    "Erreur, la vérification 4 n'est pas passée")
)
 
def faire_verifications(verifications):
    for verif, msg in verifications:
        if not verif(valeur):
            print msg
 
def function(valeur, verifications=VERIF):
    faire_verifications(verifications)
    return valeur

Cette manière de faire est plus verbeuse si votre liste de if est courte, ou si vous n’avez à le faire que pour une fonction. Mais elle devient vite plus courte et clair dans le cas où votre code grandi.

Elle aussi l’avantage de pouvoir insérer ou retirer une modification très simplement (même avec une simple lambda). Enfin, le code de vérification est découplé du code de la fonction ce qui ajoute les bénéfices suivant :

On peut évidement imaginer autre chose qu’un print: lever une exception, faire un log, ou même carrément aussi passer une fonction en lieu et place du message d’erreur qui fait office de callback quand la vérification échoue (et qui par défaut print, le meilleur des deux mondes).

Si vos vérifications deviennent très courantes, alors, l’utilisation de décorateurs prend tout son sens. Je ne vais pas rentrer sur comment écrire son décorateur, il y a un autre article pour ça, mais on peut obtenir un résultat du genre :

def verif(verification, message):
    def decorateur(func):
        def wrapper(*args, **kwargs):
            if not verification(*args, **kwargs):
                print message
            return func(*args, **kwargs)
        return wrapper
    return decorateur
 
# si on utilise souvent une vérif, on peut l'avoir 
# tout le temps tout la main
verif3 = verif(verification3, "Erreur, la vérification 3 n'est pas passée")
 
# et ensuite il suffit d'appliquer autant de décorateur qu'on le souhaite
@verif(lambda v: not bool(random.randint(0, 1)), "Erreur, la vérification 4 n'est pas passée")
@verif3
@verif(verification2, "Erreur, la vérification 2 n'est pas passée")
@verif(verification1, "Erreur, la vérification 1 n'est pas passée")
def function(valeur):
    return valeur

Les avantages ici sont :

En résumé : si vous avez un petit code, ne vous prenez pas la tête. Une série de if est tout à fait valide, ça marche bien, c’est clair et lisible. Il faut savoir aller au plus simple. Mais si votre code devient touffu, utiliser une liste de fonction peut aider à faire le ménage. Si vous commencer carrément à avoir un gros projet ou que vous codez une lib réutilisable, les décorateurs seront de précieux alliés : ils permettent à celui qui passe derrière vous de se foutre complètement de savoir comment ça marche et de juste assembler son code comme un légo.


Télécharger le code de l’article

flattr this!

Django, une app à la fois : routing basique

samedi 22 juin 2013 à 21:21

J’ai rajouté une app dans le repo des snippets Django qui montre comment utiliser le système de routing de Django pour faire correspondre des URLs à des vues, et extraires des données de ces URLs.

J’en ai profité pour faire quelques modifs sur les anciennes apps:

J’ai aussi ajouté des tests unitaires (pas dans la partie snippet, qui doit rester simple) pour m’assurer que je ne casse pas tout à chaque ajout.

Je pensais qu’ajouter une nouvelle app, quand l’app est aussi simple au moins, serait très rapide. Je m’assurais ainsi quelques articles simples et rapides à pondre.

Mouahahah, jeune incrédule.

Pour faire ces modifs ça m’a pris près de 3 heures : il faut bien réfléchir pour rendre les notions à la fois simples et utiles, mettre les bons comments, traduire tout ça de l’anglais vers le français, s’assurer que les deux branches soient bien synchro, ajouter les tests…

En me lançant dans le truc, je m’étais dit “pourquoi personne ne l’a fait avant ? C’est tellement évident que ça manque à la doc”. Maintenant je sais pourquoi. C’est long. Très très long.

flattr this!