Les Greenlets c’est bon, c’est bio, mangez-en !


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

Coroutines et Machines à états.

Quel est le rapport entre les coroutines et les machines à état me demanderez-vous (ou pas) ?
Et bieeeeen aucun, si ce n’est que vous pouvez déjà consulter deux articles sur ce site qui vous permettent d’en bricoler en python.
Mais bon, les tournevis Playskool et la colle UHU, pour du vrai bricolage de grand, c’est rigolo mais c’est pas forcement très adapté. Je vais donc vous présenter le Black & Decker de l’exécution concurrente, le Rubson de la State Machine : le module greenlet.
Les greenlets c’est quoi ? C’est simplement un fork de l’interpréteur Stackless sous forme de module pour CPython. Ca permet de profiter de la quasi-totalité de Stackless sans mettre en péril l’environnement d’exécution et/ou le code existant.

Mais comment ça s’utilise alors ?

Tout d’abord :

from greenlet import greenlet

Le principe de base c’est :

def fun1():
    print 42
    g2.switch()
    print 44
 
def fun2():
    print 43
    g2.switch()
    print 45
 
g1 = greenlet(fun1)
g2 = greenlet(fun2)
g1.switch()

Sortie écran :

42
43
44

Ca à l’air simple comme ça, mais on constate une petite chose : on n’affiche jamais 45. Pourquoi ? Et bien parce que quand une greenlet se termine, on ne retourne pas à l’appelant (puisque ce n’est pas réellement un appel) on retourne à la greenlet parente, qui est ici notre main.
En fait, les greenlets sont organisées en arbre, chaque greenlet possède un unique parent qui est lui est définit au moment de sa création. Les appels à « switch » permettent de sauter d’une greenlet à l’autre mais ils n’affectent pas l’organisation de l’arbre.
Heureusement, cet arbe n’est pas en lecture seule et on peut réorganiser les parents comme on veut (du moment qu’on ne crée pas de cycles) :

g1 = greenlet(fun1)
g2 = greenlet(fun2)
g1.parent = g2
g1.switch()

Sortie écran :

42
43
44
45

On est passé de ça :
cas1
à ça :
cas2

Et dans la vraie vie ?

« Oui parce que là, t’es mignon, mais ton exemple bidon ça me sert pas à grand chose. » Du coup on peut essayer de faire un truc utile et de mettre tout ça en forme pour une faire une super Machine à État Orientée Objet en Wolframite (la fameuse MeooW). Au passage on va utiliser un peu d’héritage sur les greenlets et montrer que la fonction exécutable qu’on leur passe à la création dans le premier exemple est simplement affectée à la méthode run() de la greenlet.

max_iter = 5
 
class Etat1(greenlet):
 
    def run(self, iteration):
        while True:
            print "Etat 1, Iteration : %s"%iteration
            iteration = etat2.switch(iteration + 1)
            if iteration > max_iter:
                return iteration
 
class Etat2(greenlet):
 
    def run(self, iteration):
        while True:
            print "Etat 2, Iteration : %s"%iteration
            iteration = etat1.switch(iteration + 1)
            if iteration > max_iter:
                return iteration
 
class EtatFinal(greenlet):
 
    def run(self, iteration):
        print "Monde de merde ! Iteration : %s"%iteration
 
etat1 = Etat1()
etat2 = Etat2()
etat_final = EtatFinal()
etat1.parent = etat_final
etat2.parent = etat_final
etat1.switch(2)

Sortie écran :

Etat 1, Iteration : 2
Etat 2, Iteration : 3
Etat 1, Iteration : 4
Etat 2, Iteration : 5
Monde de merde ! Iteration : 6

On peut voir ici que la méthode switch permet de faire passe-plat pour des variables (de la même manière que yield retourne l’argument qu’on send() à un itérateur).

Plus loin

Les greenlets permettent d’aller beaucoup plus loin que ça, notamment en travaillant sur le dynamisme de l’arbre. C’est très pratique pour faire de la gestion d’évènements. Attention tout de même car ça peut vite devenir un enfer à débugger (pas de stack, pas de chocolat).

5 thoughts on “Les Greenlets c’est bon, c’est bio, mangez-en !

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.