Comment configurer le côté du serveur Python avec le côté du client javascript

Donc, il existe déjà un programme Python configuré qui s'exécute sur la console sur laquelle je dois développer. Je vais créer une interface GUI Web pour l'application en utilisant Javascript. Comment aurais-je:

une. Suivez la gestion de l'entrée / sortie de ce programme Python sans toucher le code original.

B. Passez à envoyer des entrées de ligne console au programme Python via des appels Javascript. J'ai examiné les requêtes HTTP brutes / AJAX, mais je ne sais pas exactement comment j'allais envoyer cela en tant que contribution au programme Python.

une. Pour gérer l'entrée / sortie du programme: Pexpect . Il est assez facile à utiliser, et la lecture de certains des exemples distribués avec celui-ci devrait vous enseigner assez pour réduire les bases.

B. Interface Javascript:

Eh bien, j'utilise Gevent et c'est un serveur WSGI intégré. (Recherchez ce qu'un serveur WSGI ( un autre ) est). Je devrais noter que ce programme conservera un état afin que vous puissiez gérer vos sessions ouvertes en renvoyant une ID de session sur votre client javascript et en stockant votre session pexpect dans une variable globale ou un autre conteneur afin que vous puissiez compléter l'entrée et la sortie du programme À travers plusieurs demandes AJAX indépendantes. Je vous laisse cependant, car ce n'est pas aussi simple.

Tout ce que mon exemple fera est de mettre la demande POST dans certains après avoir cliqué sur quelque chose de votre choix. (Il ne fonctionnera pas réellement car certaines des variables ne sont pas définies. Réglez-les.)

Voici les éléments pertinents:

<!-- JavaScript --> <script src="jquery.js"></script> <script type="text/javascript"> function toPython(usrdata){ $.ajax({ url: "http://yoursite.com:8080", type: "POST", data: { information : "You have a very nice website, sir." , userdata : usrdata }, dataType: "json", success: function(data) { <!-- do something here --> $('#somediv').html(data); }}); $("#someButton").bind('click', toPython(something)); </script> 

Ensuite, le serveur:

 # Python and Gevent from gevent.pywsgi import WSGIServer from gevent import monkey monkey.patch_all() # makes many blocking calls asynchronous def application(environ, start_response): if environ["REQUEST_METHOD"]!="POST": # your JS uses post, so if it isn't post, it isn't you start_response("403 Forbidden", [("Content-Type", "text/html; charset=utf-8")]) return "403 Forbidden" start_response("200 OK", [("Content-Type", "text/html; charset=utf-8")]) r = environ["wsgi.input"].read() # get the post data return r address = "youraddresshere", 8080 server = WSGIServer(address, application) server.backlog = 256 server.serve_forever() 

Si votre programme est orienté objet, il serait assez facile d'intégrer cela. EDIT: n'a pas besoin d'être orienté objet. Et j'ai maintenant inclus un code Pexpect

 global d d = someClass() def application(environ, start_response): # get the instruction password = somethingfromwsgi # read the tutorials on WSGI to get the post stuff # figure out WHAT to do global d success = d.doSomething() # or success = funccall() prog = pexpect.spawn('python someprogram.py') prog.expect("Password: ") prog.sendline(password) i = prog.expect(["OK","not OK", "error"]) if i==0: start_response("200 OK", [("Content-Type", "text/html; charset=utf-8")]) return "Success" elif i==1: start_response("500 Internal Server Error", [("Content-Type", "text/html; charset=utf-8")]) return "Failure" elif i==2: start_response("500 Internal Server Error", [("Content-Type", "text/html; charset=utf-8")]) return "Error" 

Une autre option que je suggère est Nginx + uWSGI. Si vous préférez cela, je peux vous en donner quelques exemples. Cela vous permet d'intégrer le serveur Web dans la configuration.

Pour passer de manière transparente vos données de javascript au programme Python externe, vous pouvez utiliser le protocole WebSocket pour connecter votre serveur et javascript et utiliser stdin / stdout pour communiquer avec le programme externe à partir du serveur.

Voici un exemple de programme Python client.py :

 #!/usr/bin/env python """Convert stdin to upper case.""" for line in iter(raw_input, 'quit'): print line.upper() 

J'ai créé un serveur à l'aide d'un code à partir de l' exemple de l'hébergeur world websocket et de la réponse SO sur la façon de créer un nouveau processus sur chaque connexion entrante et de rediriger toutes les données d'entrée vers le processus 'stdin :

 #!/usr/bin/python """WebSocket CLI interface.""" import sys from twisted.application import strports # pip install twisted from twisted.application import service from twisted.internet import protocol from twisted.python import log from twisted.web.server import Site from twisted.web.static import File from txws import WebSocketFactory # pip install txws class Protocol(protocol.Protocol): def connectionMade(self): from twisted.internet import reactor log.msg("launch a new process on each new connection") self.pp = ProcessProtocol() self.pp.factory = self reactor.spawnProcess(self.pp, sys.executable, [sys.executable, '-u', 'client.py']) def dataReceived(self, data): log.msg("redirect received data to process' stdin: %r" % data) self.pp.transport.write(data) def connectionLost(self, reason): self.pp.transport.loseConnection() def _send(self, data): self.transport.write(data) # send back class ProcessProtocol(protocol.ProcessProtocol): def connectionMade(self): log.msg("connectionMade") def outReceived(self, data): log.msg("send stdout back %r" % data) self._sendback(data) def errReceived(self, data): log.msg("send stderr back %r" % data) self._sendback(data) def processExited(self, reason): log.msg("processExited") def processEnded(self, reason): log.msg("processEnded") def _sendback(self, data): self.factory._send(data) application = service.Application("ws-cli") _echofactory = protocol.Factory() _echofactory.protocol = Protocol strports.service("tcp:8076:interface=127.0.0.1", WebSocketFactory(_echofactory)).setServiceParent(application) resource = File('.') # serve current directory INCLUDING *.py files strports.service("tcp:8080:interface=127.0.0.1", Site(resource)).setServiceParent(application) 

La partie web-client, sendkeys.html :

 <!doctype html> <title>Send keys using websocket and echo the response</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"> </script> <script src="sendkeys.js"></script> <input type=text id=entry value="type something"> <div id=output>Here you should see the typed text in UPPER case</div> 

Et sendkeys.js :

 // send keys to websocket and echo the response $(document).ready(function() { // create websocket if (! ("WebSocket" in window)) WebSocket = MozWebSocket; // firefox var socket = new WebSocket("ws://localhost:8076"); // open the socket socket.onopen = function(event) { socket.send('connected\n'); // show server response socket.onmessage = function(e) { $("#output").text(e.data); } // for each typed key send #entry's text to server $("#entry").keyup(function (e) { socket.send($("#entry").attr("value")+"\n"); }); } }); 

Pour l'essayer:

  • Téléchargez ce gist
  • Installation twisted , txws :

     $ pip install twisted txws 
  • courir:

     $ twistd -ny wscli.py 
  • Visitez http://localhost:8080/

  • Cliquez sur sendkeys.html et tapez quelque chose

Vous voulez probablement Flask , avec le module json .

Django est une autre option, mais il est probablement trop élevé pour vos besoins.

Cela dépend du type d'application que vous utilisez et de la façon dont vos options GUI se transforment en commandes d'application. Mais vous avez deux objectifs ici:

  1. Rédaction d'une enveloppe pour vous permettre de lire la sortie de votre programme et de lui fournir une entrée.

  2. Faire un serveur Web pour recevoir des événements GUI et les convertir en commandes pour passer à votre "wrapper"

J'ai fait quelque chose comme ce que vous devez faire.

  1. Essentiellement, vous devez transformer les flux de socket en commandes discrètes. L'outil defacto pour cela s'attend , et l'un de ses wrappers (j'ai utilisé pexpect , le wrapper Python et j'ai eu une bonne expérience).

  2. Cette partie pourrait ne pas être simple. Le problème est que votre programme sous-jacent fonctionne de manière persistante, et que votre serveur Web doit être informé sur le programme à travers les demandes. L'autre option est que votre serveur Web ne se relie plus au processus et ne le lance pas, et renvoie une réponse lorsqu'il se trouve dans le flux stdout, mais vous pouvez finir par des temps de réponse longs selon la rapidité avec laquelle Programme est. En outre, il y a le décalage que les requêtes AJAX sont asynchrones, alors que votre programme sous-jacent est synchrone. Donc oui, cela peut devenir assez complexe. Cela dépend vraiment de votre programme. Si vous pouviez ajouter des détails sur le programme et l'interface graphique, cela aiderait.