Des transactions avec yield ?

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour !

Pour le contexte, je travaille avec WAMP, Crossbar.io et AutobahnPython. Ce n'est pas très important, mis à part que ça implique que tout ce qui suit est majoritairement asynchrone (basé sur twisted, notamment avec le décorateur inlineCallbacks). Et je souhaiterais faire ça. J'en déduis alors le code suivant :

data.py et locator.py

Seulement, c'est pas optimal : j'ai deux fonctions (is_ et set_) pour chaque attribut de ma classe Data et des if de partout dans mon Locator. Et encore, je n'ai pas beaucoup de conditions ici.

Du coup, je voudrais fusionner les is_ et les set_. Sauf qu'avec mon histoire de transaction, il faut que je teste si tous les arguments de chaque fonction sont corrects avant d'exécuter ces dernières (faire tous les is_ puis tous les set_). Du coup, je me suis dis que j'allais passer par un générateur, pour modéliser cette coupure entre vérification et affectation :

data.py v2 et locator.py v2 avec tools.py

Sauf que pickle ne veut pas me sérialiser mon générateur. J'en viens à me demander s'il s'agit de la bonne manière de faire.

Merci !

+0 -0
Auteur du sujet

Bon, j'ai encore voulu utiliser une usine à gaz pour faire des cookies… Rassembler la vérification et l'affectation dans une même fonction complique les choses pour rien. Restons donc avec deux fonctions check et set. Par contre, vu comme la première version, avec les if, est sale, utilisons plutôt les exceptions (v3) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from twisted.internet.defer import inlineCallbacks

from autobahn import wamp
from autobahn.twisted.wamp import ApplicationSession
from autobahn.wamp.exception import ApplicationError
from autobahn.twisted.wamp import ApplicationRunner



class Locator(ApplicationSession):

    @inlineCallbacks
    def onJoin(self, details):
        yield self.register(self)

    @wamp.register(u'locator.move')
    @inlineCallbacks
    def move(self):
        """Try to mark the current position and to go to the next one. Fail if 
        the current position is not markable or if the next position does not 
        exist.
        """

        print('Move!')

        pos = yield self.call('data.get_position')
        direction = yield self.call('data.get_direction')
        next_pos = pos + direction

        try:
            yield self.call('data.check_markable', pos)
            yield self.call('data.check_position', next_pos)
        except ApplicationError as e:
            raise e
        else:
            self.call('data.mark_position', pos)
            self.call('data.set_position', next_pos)
        finally:
            try:
                yield self.call('data.check_sth')
            except ApplicationError as e:
                raise e
            else:
                self.call('data.do_sth')


if __name__ == "__main__":
    print('Starting Locator component...')

    ApplicationRunner(url='ws://localhost:8080/ws', realm='realm1').run(Locator)

A présent, il faut chercher à offrir une interface propre à la place de ce code spaghetti.

+0 -0

Bonjour,

Je ne sais pas si ça peut te convenir, mais tu peux toujours écrire ça :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@wamp.register(u'data.isAnd')
def isAnd(self, markpos, pos):
    try:
        self.markables[markpos]
    except IndexError:
        return 0
    else:
        try:
            self.positions[pos]
        except IndexError:
            return 1
        else:
            self.positions[markpos] = 'marked'
            self.position = pos
            print('Positions: {}'.format(self.positions))
            print('Positions: {}'.format(self.position))
            return 2

@wamp.register(u'locator.move')
@inlineCallbacks
def move(self):
    print('Move !')
    pos = yield self.call('data.get_position')
    direction = yield self.call('data.get_direction')
    next_pos = pos + direction

    isAnd = yield self.call('data.isAnd', pos, next_pos)
    if isAnd == 0:
        self.publish(
            'error',
            'The pos {} is not markable.'.format(pos)
        )
    elif isAnd == 1:
        self.publish(
            'error',
            'The pos {} does not exist.'.format(next_pos)
        )
    self.call('data.sth')

Après je n'ai pas pu le tester, il faudrait que je télécharge les modules. :)

Édité par Yarflam

Tant de choses, tant de vies, tant de possibilités.

+0 -0
Auteur du sujet

Tout d'abord, merci pour l'investissement. ^^ Néanmoins, il y a quelques soucis :

  • Le code dans la fonction est toujours aussi crade.
  • Le code dans la fonction appartient au module Locator, pas au module Data. En fait, le second offre une API au premier. Je fournis le module Data et le programmeur l'utilise pour écrire son propre module Locator (plus ici).
  • La valeur de retour est superflue : autant lever directement l'exception, avec le message défini dans le if. Mais si tu ne connais pas WAMP, il est normal que tu ne saches pas que l'exception (son message) est transmise à celui qui fait l'appel via RPC (donc, ici, le client Web).

Thanks!

+0 -0

Le code dans la fonction appartient au module Locator, pas au module Data.

Oui, c'est pour ça que j'ai bien marqué les entêtes Wamp. J'ai clipsé le contenu des deux fichiers dans un seul bloc de code. :)

Effectivement le code reste crade. Mais en même temps je ne vois pas vraiment comment tu peux faire autrement. Sans compter que tu n'as pas expliqué l'objectif final de ton projet ? A quoi ça va servir ?

Édité par Yarflam

Tant de choses, tant de vies, tant de possibilités.

+0 -0
Auteur du sujet

Non, ce que je veux dire c'est que la fonction isAnd doit se situer dans le module Locator. Celui Data ne fournit que des getters, des setters et des checkers.

Par rapport au projet dans sa globalité, tu as plus d'informations ici.

Dans le cas présent, je travaille sur le module de localisation. Le module Data contient la carte et fournit des méthodes pour la manipuler (les deux communiquent via RPC). Le module de localisation permet de mettre à jour la position du tracteur sur la carte. En fonction des informations qu'il reçoit de la carte électronique, il détermine la zone courante (en utilisant des informations sur les zones fournies par le module Data : leurs coordonnées GPS, leur distance au début du rang… tout dépend de comment l'agriculteur décide de se repérer) et demande au module Data de mettre à jour la position sur la carte.

Pour faire ça plus proprement, il y aurait peut-être la classe DeferredList de twisted.

Édité par Vayel

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte