PROJET AUTOBLOG


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

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

⇐ retour index

Installer des packages depuis le shell Python 6

mercredi 27 janvier 2016 à 22:30

J’améliore tout le temps mon fichier de démarrage de Python.

Aujourd’hui je lui ai rajouté une petite fonction pour installer des packages avec pip depuis le shell. En effet, souvent il m’arrive de le lancer, puis d’importer un truc qui n’est pas là. Alors il faut sortir, installer, revenir.

Chiant.

Du coup :

import pip
def pip_install(*packages):
    """ Install packages diretly in the shell """
    for name in packages:
        cmd = ['install', name]
        if not hasattr(sys, 'real_prefix'):
            # Si pas dans un virtual env, install juste
            # dans le dossier utilisateur
            cmd.append('--user')
        pip.main(cmd)

Et dans le shell :

>>> pip_install('arrow', 'requests', 'minibelt')
>>> import arrow

Comment mocker une coroutine ? 6   Recently updated !

lundi 25 janvier 2016 à 11:34

Dans le guide sur les tests en python (que je dois toujours terminer, je sais…), je vous parle des objets mocks.

Si vous avez eu le plaisir de jouer avec asyncio, vous avez du noter que unittest.mock n’a aucun outil pour gérer gérer les coroutines.

En attendant que ce soit intégré à la stdlib, voici une petite recette :

import asyncio
from unittest.mock import Mock
 
# on utilise toute la machinerie du Mock original
class AMock(Mock):
 
 
    def __call__(self, *args, **kwargs):
        # la référence du parent doit se récupérer hors
        # hors de la closure
        parent = super(AMock, self) 
        # sauf qu'à l'appel on créé une fonction coroutine
        @asyncio.coroutine
        def coro():
            # Qui fait le vrai Mock.__call__ (et donc popule l'historique 
            # des appels), mais seulement après l'évent loop l'ait éxécuté
            return parent.__call__(*args, **kwargs)
 
        # On appelle la fonction coroutine pour générer une coroutine
        # (les coroutines marchent comme les générateurs)
        return coro()

Je propose qu’en l’honneur de ce bidouillage, on l’appelle… mockoroutine !

Ca va s’utiliser comme ça:

mockorourine = AMock()
yield from mockorourine()

Après le yield from, mockorourine.call_count == 1, et mockorourine.assert_called_once_with()passe.

Si vous êtes en 3.5+, on peut même faire:

class AMock(Mock):
 
    def __call__(self, *args, **kwargs):
        parent = super(AMock, self)
        async def coro():
            return parent.__call__(*args, **kwargs)
        return coro()
 
    def __await__(self):
        # on delegue le await à la couroutine créée par __call__
        return self().__await__()

Puis:

await AMock()

Enfin un moyen d’afficher une vidéo avec des cases à cocher (PyQt4 et OpenCV inside) 20

dimanche 24 janvier 2016 à 15:11

Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Bonsjours les amis,

J’espère que vous allez bien et que vous n’avez pas trop pensé que j’étais mort rapport au fait que ça faisait longtemps que je n’avais pas posté d’article.

 

Où je mentionne plusieurs fois le mot JavaScript.

Le fait est que j’étais trop occupé à faire du JavaScript.

Bon, ce n’est pas exactement vrai, mais, je voulais vérifier une théorie selon laquelle dès qu’on parle de JavaScript sur Sam&Max, le nombre des commentaires explosent (je n’ai pas de théorie sur le fait qu’il faille l’écrire en gras, mais je trouve ça joli (sauf quand il y en a partout (parce qu’après ça fait trop))).

Mais, dire que j’étais trop occupé à faire du JavaScript n’est pas précisément faux non plus dans la mesure où je me suis mis à p5.js, qui est au JavaScript ce que Processing est au Java, à savoir un environnement de développement simplifié pour les pucelles et autres puceaux de la programmation.

p5.js qui m’a permis d’assouvir un de mes fantasmes : afficher un cheval qui court avec des cases à cocher dans une page web..

Un cheval qui court en cases à cocher d'après les célèbres photographies d'Easweard Muybridge, désormais dans le domaine publique.

Un cheval qui court en cases à cocher d’après les photos d’E.Muybridge, désormais dans le domaine publique.

Je vous ai mis un GIF animé, pour faire de l’animation, mais vous pouvez le voir en vrai ici avec vos cases à cocher à vous.

(Sauf pour celles et ceux pour qui ça ne marchera pas, bien évidemment.)

(Voilà pour la partie JavaScript. Je ne vais plus en parler. Les personnes concernées peuvent donc se rendre dès à présent à la section des commentaires. Merci.)

 

Où je m’attelle à une tâche bien plus sérieuse.

J’étais content, mais, passé ces enfantillages, il me fallait désormais m’atteler à une tâche bien plus sérieuse : afficher un cheval qui court avec des cases à cocher en Python.

Pour cela, j’ai choisi d’utiliser PyQt4 parce que c’est la meilleure librairie pour créer des interfaces graphiques en Python.

Ah Ah Ah…
Nan, mais, comme si j’en savais quelque chose…
C’est juste qu’un ami avait déjà réussi à afficher un bouton avec…
Donc, je me suis dit que c’était pour moi…
Bon, en fait, ce n’est pas pour moi…
Mais, que les choses soient claires, c’est l’informatique en général et la programmation en particulier qui ne sont pas pour moi…
Je veux dire, quand je vais lire Stack Overflow, j’ai de la peine pour moi…
C’est à dire que je me fais vraiment pitié tant je comprends que dalle…
Il m’est déjà arrivé de mettre une dizaine de secondes pour savoir si je lisais bien du Python (et s’en était)…
Nan, c’est moche…
Mais bon, je ne sais faire que de l’informatique (on me souffle à l’oreille que je sais aussi faire du Chirashi et c’est vrai)
Donc, je fais de l’informatique… (et du Chirashi)
Et j’en chie…
Mais à la fin je gagne…
Et je me paye même le luxe d’en faire un article sur S&M…
Pourquoi je vous raconte ça ?
Aucune idée…
Toujours est-il que j’ai choisi PyQt4
Que ça n’est pas pour moi…
Mais que [SPOILER ALERT] ça a fini par marcher…

 

Où j’affiche une fenêtre. UNE FE-NÊ-TRE. Oui messieurs, dames.

D’abord, il faut installer PyQt4

sudo apt-get install python-qt4

…et on ne peut pas dire que ce soit trop compliqué.

Et, voici le code minimal pour afficher une fenêtre :

#-*- coding: utf-8 -*-
from PyQt4 import QtGui
 
# On crée une appli Qt
application = QtGui.QApplication([])
# On fout quelque chose dedans
fond = QtGui.QWidget()
# On l'affiche
fond.show()
# On lance l'appli
application.exec_()

Les connaisseurs apprécieront mon désormais célèbre style procédural de champion du monde.
Mais bon. Ça marche où ça marche pas ?

Une fenêtre

Une fenêtre

Ça marche.

 

Où j’affiche un bouton dans la fenêtre.

Toujours en procédural, voici comment on ajoute un bouton à notre fenêtre (que l’on customize un peu au passage).
(J’insiste pour le procédural car, comme PyQt4 est conçu pour être utilisé en Programmation Orientée Objet, vous ne verrez pas ça tous les jours)

#-*- coding: utf-8 -*-
from PyQt4 import QtGui
 
app = QtGui.QApplication([])
 
# On crée un fond et lui donne une taille, une position sur l'écran et un titre
fond = QtGui.QWidget()  
fond.resize(300, 200)
fond.move(100, 100) 
fond.setWindowTitle("C'est formidable") 
 
# On crée un bouton sur notre fond et on lui donne une position sur celui-ci
bouton = QtGui.QPushButton('Mon petit bouton',fond)
bouton.move(80, 80)
 
fond.show()
app.exec_()

Et hop…

Un petit bouton dans une fenêtre

Un petit bouton dans une fenêtre

La première fois que ça a marché, j’ai cliqué 25 fois sur le bouton et j’en garde un très bon souvenir.

 

Où j’affiche plein de cases à cocher dans la fenêtre.

Bon, on peut afficher tout un tas de bordel dans une fenêtre, vous vous en doutez. Des images, du texte, des champs texte, des sliders, des menus déroulants, des labels, des séparations…
Mais, ça n’est pas le propos ici.
Nous ce qu’on veut, ce sont des cases à cocher. Plein de cases à cocher.

J’ai commencé par les placer dans une grille, mais, je me suis rendu compte qu’en leur donnant des positions absolues en pixels, il était possible de les faire se toucher, voire se chevaucher. Ce qui, quand on veut afficher un cheval, apporte un petit plus indéniable.
(chevaucher/cheval/humour)

Allons y donc pour nos cases à cocher.
(Je vous recolle tout à chaque fois parce que je sais qu’il y a des goulus parmi vous.)

#-*- coding: utf-8 -*-
from PyQt4 import QtGui
 
# On définie les dimensions de notre bazar
largeur = 40
hauteur = 30
tailleCase =14
 
app = QtGui.QApplication([])
 
fond = QtGui.QWidget()  
fond.resize(largeur*tailleCase+6, hauteur*tailleCase+6) # le +6 c'est pour tenir compte des marges
fond.setWindowTitle(u"Plein de cases à cocher") 
 
# On parcourt les ordonnées
for j in range(hauteur):
 
    # On parcourt les abscisses
    for i in range(largeur):
 
        # On crée une case à cocher sur notre fond
        check_box = QtGui.QCheckBox(fond)
        # Et on la positionne
        check_box.move(i*tailleCase, j*tailleCase)
 
fond.show()
app.exec_()
Le mot

Le mot “Joie” écrit à la main avec des cases à cocher.

 

Où j’affiche plein de cases à cocher dans la fenêtre mais en utilisant cette fois la Programmation Orientée Objet.

On arrive au bout. Mais, ce qu’il faut comprendre avec pyQt c’est que quand le programme arrive à l’instruction app.exec_() il démarre une boucle. On est alors un peu coincé pour parler avec l’interface.
Il est possible de cliquer sur les cases à la main, soit, mais, nous ce qu’on veut, c’est que ce soit un bout de code qui le fasse à notre place (car ayant bien mis 2 minutes pour composer une seule image affichant le mot “Joie”, j’ai des doutes pour le 25 fps).

Dans tous les cas, il n’est plus possible de rester en procédural. Voici donc la version POO du précédent script :

#-*- coding: utf-8 -*-
from PyQt4 import QtGui
import sys
 
largeur = 40
hauteur = 30
tailleCase = 14
 
class CheckBoxVideo(QtGui.QWidget):
 
    # On initialise la class, j'imagine...
    def __init__(self):
        super(CheckBoxVideo, self).__init__()
        self.interface()
 
    # On initialise l'interface    
    def interface(self):
 
        self.resize(largeur*tailleCase+6, hauteur*tailleCase+6)
        self.setWindowTitle(u"Plein de cases à cocher, mais en POO.")
 
        for j in range(hauteur):
 
            for i in range(largeur):
 
                check_box = QtGui.QCheckBox(self)
                check_box.move(i*tailleCase, j*tailleCase)     
 
        self.show()
 
 
if __name__ == "__main__":
      appli = QtGui.QApplication(sys.argv)
      ckbx = CheckBoxVideo()
      sys.exit(appli.exec_())
Le mot "Bonheur" écrit en cases à cocher mais en utilisant cette fois -ci la programmation orientée objet.

Le mot “Bonheur” écrit en cases à cocher mais en utilisant cette fois la programmation orientée objet.

J’ai ajouté un petit if __name__ == "__main__" histoire de laisser croire que je comprends ce que je fais.
Et puis j’ai intégré la librairie sys pour être conforme avec tous les exemples de tous les tutos que j’ai trouvé. Mais c’est vraiment par respect pour les devs qui se sont donné du mal à les écrire. Parce que, vous pouvez essayer, ça marche très bien sans.
Bref.

 

Où, en faisant ce qu’il ne faut pas faire, je constate qu’il ne faut pas le faire.

Je vous épargnerai la liste des trucs ridicules que j’ai essayé pour entrer dans cette boucle et modifier l’état des cases à cocher et n’évoquerai que les solutions que j’ai trouvé pour faire marcher mon bazar… pendant 20 secondes.

En effet, à force de chercher, je suis tombé sur ce fil de Stack Overflow où la réponse la mieux notée donne trois moyens différents de créer des threads avec Qt.

J’ai méticuleusement tout recopié à l’arrache et testé chacune des solutions.
Toutes m’ont permis de voir ma silhouette en cases à cocher avant de me retourner un délicat segfault au bout de 20 secondes.

Vraisemblablement, il y a quelque chose que je faisais mal.
Chose qui a été confirmée par un article intitulé : “You’re doing it wrong…”

C’est à ce moment que j’ai décidé de me coucher, vu que le Soleil se levait.

 

Où j’ai la confirmation que dormir peut s’avérer parfois utile pour se reposer.

Si j’étais mort dans mon sommeil ce jour là, mes derniers mots auraient été : “Putain de boucle de merde”.
Mais je me suis réveillé et je me suis dis que, tout de même, il devait y avoir un moyen simple de faire exécuter du code dans cette boucle principale.

Et là, avec un cerveau frais, je me suis souvenu de cet exemple expliquant comment animer une barre de progression.
Passer des valeurs à un objet pour en changer son état, c’est un peu ce que je cherchais à faire, non ?
Non.
C’était exactement ce que je cherchais à faire.

Le principe est de créer un timer et d’écouter ses tours d’horloge pour déclencher des événements.
À noter la nécessité d’importer QtCore pour cela.

#-*- coding: utf-8 -*-
from PyQt4 import QtGui, QtCore
import sys
 
largeurOut = 40
hauteurOut = 30
tailleCase = 14
 
 
class CheckBoxVideo(QtGui.QWidget):
 
    def __init__(self):
        super(CheckBoxVideo, self).__init__()
        self.interface()
 
    def interface(self):
 
        self.resize(largeurOut*tailleCase+6, hauteurOut*tailleCase+6)
        self.setWindowTitle("OMG ! Mais que se passe-t-il dans la console ?")
 
        for j in range(hauteurOut):
 
            for i in range(largeurOut):
 
                check_box = QtGui.QCheckBox(self)
                check_box.move(i*tailleCase, j*tailleCase)
 
        # On crée le timer.
        self.timer = QtCore.QBasicTimer()
        # Et on le lance en renseignant un délais pour chaque tour d'horloge.
        # Ici, 40 millisecondes, pour tenter (naïvement) d'obtenir du 25 images par seconde.
        self.timer.start(40, self)
 
        self.show()
 
    # Et voici la méthode qui écoute le timer.
    def timerEvent(self, e):
 
        print("Ce message va s'écrire en console toutes les 40 ms.")
 
 
if __name__ == "__main__":
      appli = QtGui.QApplication(sys.argv)
      ckbx = CheckBoxVideo()
      sys.exit(appli.exec_())
Des cases à cocher dans une fenêtre et des messages dans une console.

Des cases à cocher dans une fenêtre et des messages dans une console.

 

Où j’évoque ma jeunesse.

Il s’agissait maintenant de trouver un moyen de traiter du flux vidéo.
Mais comme je suis un ouf et que non seulement j’avais déjà réussi à afficher de la vidéo 3-bit dans mon terminal , mais qu’en plus je vous en avais fait part ici-même, et bien je suis allé copier/coller mon pâté. Tout simplement.

Pour celles et ceux qui n’ont pas eu la force psychologique de cliquer sur le lien, sachez que la solution que j’avais retenue à l’époque pour lire un fichier vidéo afin d’en traiter les données était OpenCV, et que c’est tout aussi pertinent maintenant.
(Enfin, ça marche. Ce qui, à mon niveau, est la définition même de la pertinence.)

 

Où il est de nouveau affiché un cheval qui court en cases à cocher, mais en Python cette fois.

Et voilà…
C’est terminé…

Voici le script après l’intégration du code exploitant OpenCV :

#-*- coding: utf-8 -*-
from PyQt4 import QtGui, QtCore
import sys
import cv2
 
largeur = 50
hauteur = 34
tailleCase = 14
 
# On crée une liste qui va stocker les QCheckBox
listeCB = []
 
# On définit un seuil pour pour cocher ou non les cases
seuil = 150
 
# On définit le fichier à lire.
video = cv2.VideoCapture('cheval_qui_court.mjpg')
 
class CheckBoxVideo(QtGui.QWidget):
 
    def __init__(self):
        super(CheckBoxVideo, self).__init__()
        self.interface()
 
    def interface(self):
 
        self.resize(largeur*tailleCase+6, hauteur*tailleCase+6)
        self.setWindowTitle(u"Un cheval qui court en cases à cocher")
 
        for j in range(hauteur): 
            for i in range(largeur): 
                check_box = QtGui.QCheckBox(self)
                check_box.move(i*tailleCase, j*tailleCase)
                # On ajoute chaque QCheckBox dans notre liste
                listeCB.append(check_box)   
 
        self.timer = QtCore.QBasicTimer()
        self.timer.start(40, self)
        self.show()
 
    def timerEvent(self, e):
 
        # On lit une frame de la vidéo
        ret,img = video.read()
        # On la passe en niveau de gris
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # On la retaille pour que le nombre de pixel corresponde au nombre de cases à cocher
        gray = cv2.resize(gray, (largeur, hauteur)) 
 
        # On parcourt notre liste
        for index, checkBox in enumerate(listeCB):
 
            # On transforme les index en coordonnées            
            x = index%largeur
            y = index/largeur
            # On récupère le niveau de gris du pixel concerné
            valeur = gray.item(y, x)
 
            # En fonction de la valeur on coche ou non la case à cocher
            if (valeur > seuil):
              checkBox.setCheckState(0)
            else:
              checkBox.setCheckState(2)
 
            # Les plus attentifs auront remarqué que l'état de la case à cocher ne dépend pas d'un booléen.
            # En effet, il existe un état intermédiaire, le (1), que j'ai appelé l'état de Schrödinger, où 
            # la case à cocher est partiellement cochée. Un peu comme si elle était à la fois cochée et non cochée.
            # (Et j'interdis à quiconque d'évoquer, qu'en fait, elle n'est ni l'une, ni l'autre.)
 
 
if __name__ == "__main__":
      appli = QtGui.QApplication(sys.argv)
      ckbx = CheckBoxVideo()
      sys.exit(appli.exec_())

Et voici le cheval qui court en cases à cocher :

Je concède qu’arrivé là, il n’aurait pas été totalement farfelu d’ajouter un slider pour gérer le niveau du seuil.
Mais j’avais un peu peur de rendre le truc utile, vous voyez.
Donc j’ai laissé en l’état.

 

Où je conclue.

Je pense qu’il est assez clair désormais qu’il est possible de sauver l’Humanité en programmant en Python.
Je vous encourage donc à le faire.

À bientôt ou tard.

 

Où j’ajoute tout de même un bonus.

Et voici un cheval qui court avec des cases à cocher qui se chevauchent.
Ne me remerciez pas, ça me fait plaisir.

cheval

Les enums en Python 9

dimanche 17 janvier 2016 à 20:01

Je n’ai jamais eu besoin d’un type exactement comme les d’Enum en Python de ma vie. Jamais eu le cas d’utilisation. Alors quand la feature est arrivé en python 3.4, cela a été un non-évènement pour moi.

Pourquoi faire ?

Je comprends parfaitement que des langages comme le C ou le Java en aient besoin. Leur syntaxe amène à ce genre de chose. Leur typage aussi. Mais Python ? Il y a tellement de manières de représenter les données, de les manipuler, de les passer.

Les enums c’étaient vraiment pour la déco à mes yeux.

Mais la fonctionnalité a fait débat. Si, si, on peut devenir tout rouge à propos d’une séquence de constantes.

Regardons un peu plus près de quoi il est question.

L’ancien statuquo

D’abord, en Python, on définit généralement les constantes ainsi :

FOO = 1
BAR = 2 
DOH = 3
OYO = 4

Ce ne sont pas vraiment des constantes. Il n’y a rien qui ne puisse être réassigné en Python, même si parfois ça demande un peu de bidouillage, mais c’est une convention que les gens respectent.

Comme les modules sont des singleton, on peut facilement faire:

import module print(module.FOO)

Parfois on a besoin d’un peu plus. On veut plusieurs groupes de constantes. Alors on peut utiliser une classe.

class Stuff:     
    FOO = 1     
    BAR = 2     
    DOH = 3
    OYO = 4

Et on peut faire:

from module import Stuff 
print(Stuff.FOO)

Après, il y a toujours les variantes des gens qui veulent itérer dessus.

from module import Stuff 
for attr, value in Stuff.__dict__.items():     
    print(attr, value)

Et puis il y a les gens qui veulent itérer dessus de manière ordonnée. On leur conseille généralement d’utiliser namedtuple:

from collections import namedtuple
Stuff = namedtuple('Stuff', 'FOO BAR DOH OYO')(*range(1, 5)) 
for val in Stuff:     
    print(val)

Mais certains vont vouloir les noms avec les valeurs. Ils se tourneront peut être vers OrderedDict:

from collections import OrderedDict
Stuff = OrderedDict((('FOO', 1), ('BAR', 2), ('DOH', 3), ('OYO', 4))) 
for attr, value in Stuff.items():     
    print(attr, value)

Mais pas d’accès direct par attribut, et il faut faire gaffe à la saisie ou quand on change les valeurs, etc.

Bref, les quelques personnes qui voulaient les enums n’étaient parfaitement satisfaites de ces solutions.

Ce que font les enums en Python

En gros, les Enums sont des classes bridées qui représentent des itérables de constantes. Chaque constante est un couple clé/valeur.

from enum import Enum
 
class Stuff(Enum):     
    FOO = 1     
    BAR = 2     
    DOH = 3     
    OYO = 4
 
for member in Stuff:      
    print(member.name, member.value)

Les enums peuvent être héritées (mais de manière limitée) et implémenter un peu de logique puisque ce sont finalement des classes.

Il existe un type IntEnum pour que chaque membre soit comparable à un int directement plutôt que de voir chercher .value, qui par ailleurs peut être n’importe quel type avec Enum.

Et on a une syntaxe raccourcie:

Stuff = Enum('Stuff', 'FOO BAR DOH OYO')

Bref, la feature ne fait pas grand-chose, en tout cas rien de ce qu’on ne pouvait faire avant. Mais elle a quelques avantages :

Mais tout ce temps après avoir vu les enums introduites, je n’en ai toujours pas l’usage. Jamais assez de constantes, ou alors quand j’en ai, je peux en faire des tuples ou des dicos sans avoir besoin d’un import.

Je suppose que je fais pas assez d’extensions C.

Quoiqu’il en soit généralement quand je vois des gens demander des enums, ce sont souvent pour de mauvaises raisons, pour reproduire ce qu’ils ont l’habitude d’utiliser dans un autre langage. Mais il y a vraiment peu de use cases typiquement Python pour les enums.

Allez, si vous avez quelques flags à mettre quelque part ou une API à wrapper, mettre une enum ne fera pas de mal.

Passer son compte linux en superuser postgres 4

vendredi 15 janvier 2016 à 16:27

Dans la config par défaut, les roles postgres matchent les utilisateurs système sous Linux. Au départ, le seul super utilisateur est “postgres”, et à chaque fois qu’on veut lancer psql pour faire de l’admin il faut se connecter avec ce user.

Pour se simplifier la vie en dev:

# on se log come "postgres"
sudo -i -u postgres # sous les debian likes. sinon "su" marche aussi
# on créé un utilisateur
createuser --interactive # et on dit "oui" quand il demande de créer un super user
# on créé une db à son nom
createdb ton_username

Et voilà, on peut utiliser psql depuis son compte.