Il existe une lib appelée crochet qui permet de faire marcher des API de twisted entre deux bouts de code bloquants. Certes, ça ne marche qu’en 2.7 et c’est pas hyper performant, mais on peut faire des trucs mignons du genre cette démo qui mélange flask et WAMP.
C’est du pur Python, pas de process externe à gérer, c’est presque simple.
Bref, si on veut utiliser WAMP avec une app synchrone comme flask, c’est un bon moyen de s’y mettre. On aura jamais des perfs fantastiques, mais on peut pusher vers le browser.
Du coup je me suis demandé si on pouvait faire ça avec Django.
Évidement, ça a été un peu plus compliqué car par défaut runserver
lance plusieurs workers et fait un peu de magie avec les threads. Mais après un peu de bidouillage, ça marche !
On peut utiliser WAMP, directement
dans Django.
Suivez le guide
D’abord, on installe tout le bouzin (python 2.7, souvenez-vous) :
pip install crossbar crochet django |
Il vous faudra un Django 1.7, le tout dernier, car il possède une fonctionnalité qui nous permet de lancer du code quand tout le framework est chargé.
Vous vous faites votre projet comme d’hab, et vous ouvrez le fichier de settings et au lieu de mettre votre app dans INSTALLED_APPS
, vous rajoutez ça :
INSTALLED_APPS = ( '...', 'votreapp.app.VotreAppConfig' ) |
Puis dans le module de votre app, vous créez un fichier app.py, qui va contenir ça:
# -*- coding: utf-8 -*- import crochet from django.apps import AppConfig # On charge l'objet contenant la session WAMP définie dans la vue from votreapp.views import wapp class VotreAppConfig(AppConfig): name = 'votreapp' def ready(self): # On dit a crochet de faire tourner notre app wamp dans sa popote qui # isole le reactor de Twisted @crochet.run_in_reactor def start_wamp(): # On démarre la session WAMP en se connectant au serveur # publique de test wapp.run("wws://demo.crossbar.io/ws", "realm1", start_reactor=False) start_wamp() |
On passe à urls.py dans lequel on se rajoute des vues de démo :
url(r'^ping/', 'votreapp.views.ping'), url(r'^$', 'votreapp.views.index') |
Puis dans notre fichier views.py, on met :
# -*- coding: utf-8 -*- import uuid from django.shortcuts import render import crochet # Crochet se démerde pour faire tourner le reactor twisted de # manière invisible. A lancer avant d'importer autobahn crochet.setup() from autobahn.twisted.wamp import Application # un objet qui contient une session WAMP wapp = Application() # On enrobe les primitives de WAMP pour les rendre synchrones @crochet.wait_for(timeout=1) def publish(topic, *args, **kwargs): return wapp.session.publish(topic, *args, **kwargs) @crochet.wait_for(timeout=1) def call(name, *args, **kwargs): return wapp.session.call(name, *args, **kwargs) def register(name, *args, **kwargs): @crochet.run_in_reactor def decorator(func): wapp.register(name, *args, **kwargs)(func) return decorator def subscribe(name, *args, **kwargs): @crochet.run_in_reactor def decorator(func): wapp.subscribe(name, *args, **kwargs)(func) return decorator # Et hop, on peut utiliser nos outils WAMP ! @register('uuid') def get_uuid(): return uuid.uuid4().hex @subscribe('ping') def onping(): with open('test', 'w') as f: f.write('ping') # Et à côté, quelques vues django normales def index(request): # pub et RPC en action côté Python publish('ping') print call('uuid') with open('test') as f: print(f.read()) return render(request, 'index.html') def ping(request): return render(request, 'ping.html') |
Après, un peu de templating pour que ça marche…
Index.html :
{% load staticfiles %} <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title> UUID </title> <script src="{% static 'autobahn.min.js' %}"></script> <script type="text/javascript"> var connection = new autobahn.Connection({ url: "ws://localhost:8080/ws", realm: "realm1" }); connection.onopen = function (session) { session.call("uuid").then(function (uuid) { var p = document.getElementById('uuid'); p.innerHTML = uuid; }); }; connection.open(); </script> </head> <body> <h2>UUID</h2> <p id="uuid"></p> </body> </html> |
ping.html :
{% load staticfiles %} <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title> Ping </title> <script src="{% static 'autobahn.min.js' %}"></script> <script type="text/javascript"> var connection = new autobahn.Connection({ url: "ws://localhost:8080/ws", realm: "realm1" }); connection.onopen = function (session) { session.subscribe("ping", function () { var ul = document.getElementById('ping'); var li = document.createElement('li'); li.innerHTML = 'Ping !' ul.appendChild(li); }); }; connection.open(); </script> </head> <body> <h2>Ping me !</h2> <ul id="ping"> </ul> </body> </html> |
On ouvre la console, on lance son routeur :
crossbar init crossbar start |
On lance dans une autre console son serveur Django :
./manage.py runserver |
Et si on navigue sur http://127.0.0.1:8000
, on récupère un UUID tout frais via RCP.
On peut aussi voir dans le shell que ça marche côté Python :
94cfccf0899d4c42950788fa655b65ed ping
D’ailleurs un fichier nommé “test” est créé à la racine du projet.
Et si on navigue sur http://127.0.0.1:8000/ping/
et qu’on refresh http://127.0.0.1:8000
plusieurs fois, on voit la page se mettre à jour.
Achievement unlock : use WAMP and Django code in the same file.
A partir de là
Il y a plein de choses à faire.
On pourrait faire une lib qui wrap tout ça pour pas à avoir à le mettre dans son fichier de vue et qui utilise settings.py pour la configuration.
Il faut tester ça avec des setups plus gros pour voir comment ça se comporte avec gunicorn, plusieurs workers, le logging de Django, etc. Je suis à peu près sûr que les callbacks vont être registrés plusieurs fois et ça devrait faire des erreurs dans les logs (rien de grave ceci dit).
On pourrait aussi adapter le RPC pour qu’il utilise les cookies d’authentification Django, et pouvoir les protéger avec @login_required.
Mais un monde d’opportunités s’offrent à vous à partir de là.
Moi, ça fait 6 h que je taffe dessus, je vais me pieuter.
Comme quoi, on peut tout faire avec WAMP ^^
J’ai un gros problème de lag sur vos pages à cause des codes sources. Peut-être le plugin de coloration syntaxique ? Pb présent sur firefox et chromium.
+1 pour le lag de la coloration syntaxique. Le scrolling lag énormément.
Sinon c’est quoi ce titre click-bait è_é ?
Maintenant que j’ai lu cet article, je crois bien que je peux mourrir en paix.
Ouai même problème mais va débugger un plugin wordpress…
Alors là ca va etre cool pour bibi ;)
Ca devrait pouvoir permettre de lancer des requêtes de traitements de triggers unitairement et en récupérer le résultat dans le browser directement. Bon c’est pas le plus urgent mais ca permettrait ça !
Merci pour ces 6 heures passées là dessus.
Comme d’hab’ une mine d’or :)
Pour la coloration syntaxique, y a u’ truc App prism.JS qui est bien cool, pas trop lourd et w3c compliant. Sais pas comment intégrer dans un wordpress par contre..
Trop fort ! Comme d’habitude.
Bonjour,
C’est la première fois que je commente même si ca fait maintenant un an que je vous suis. Je me suis mis au python en partie grâce à vous. Et c’est tous les jours un plaisir de venir vous lire pour voir les nouvelles choses que vous nous avez trouvé…la veille techno est un plaisir avec des sites comme le votre :)
Sinon en ce qui concerne Wamp j’ai commencé à jouer avec et c’est vrai que c’est une tuerie.
Petit commentaire inutile mais qui me permet de vous dire merci pour tout ce que vous faites!
:)
C’est sensé fonctionner avec python 3.3/3.4 aussi
Je suppose qu’il manque un point d’interrogation ?
Pas encore, car c’est basé sur twisted, et donc en cours de portage sur python 3.
Youhou ! La suite la suite ! Ya eu quoi de neuf depuis ? :)
Y a t il eu des changements avec asyncio et tout et tout ?
Merci pour tout ces éléments !
Hé bien un an après, y’a moi qui débarque avec une petite lib pour packager cette super astuce (à voir ici
https://github.com/Scille/autobahn-sync) ^^
C’est encore jeune (faut que je fasse plus de doc notamment) donc si vous avez des remarques/critiques/insultes je suis preneur !
Ca mérite un tweet. Et de ton côté, n’hésite pas à publier ça sur reddit.
Super astuce, c’est exactement ce que je recherchais pour mon problème actuel :). Et gros +1 pour autobahn-sync.
ya une typo dans le app.py sur le protocol qui semble etre soit ws soit wss
apres le code je me mange un ‘NoneType’ object has no attribute ‘publish’ sur le publish(‘ping’) parce qu’a priori dans la methode publish ; wapp.session vaudrait None
Et en creusant, Il semblerait que le code correct (à moins que ca ait bougé depuis ces années) soit :
apres j’ai une erreur
mais j’ai aucune idée du pourquoi du comment