如何使用 javascript 客户端设置 Python 服务器端
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11727145/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to set up Python server side with javascript client side
提问by Nikhil Unni
So there is already a Python program set up that runs on the console that I must build upon. I will be building a web GUI interface for the application using Javascript. How would I:
所以已经有一个 Python 程序设置在我必须构建的控制台上运行。我将使用 Javascript 为应用程序构建一个 Web GUI 界面。我将如何:
a. Go about handling the input/output of this Python program without touching the original code.
一个。在不接触原始代码的情况下处理这个 Python 程序的输入/输出。
b. Go about sending console-line inputs to the Python program via Javascript calls. I've looked into raw HTTP requests/AJAX, but I'm not sure how exactly I would go about sending that as input to the Python program.
湾 通过 Javascript 调用将控制台行输入发送到 Python 程序。我已经研究了原始 HTTP 请求/AJAX,但我不确定如何将其作为输入发送到 Python 程序。
采纳答案by Logan
a. To handle input/output of the program: Pexpect. It's fairly easy to use, and reading some of the examples distributed with it should teach you enough to get the basics down.
一个。处理程序的输入/输出:Pexpect。它相当容易使用,阅读一些随它分发的示例应该可以教会您足够的基础知识。
b. Javascript interface:
湾 Javascript接口:
Well, I use gevent and it's built-in WSGI server. (look up what a WSGI server(another) is). I should note that this program will keep a state, so you can manage your open sessions by returning a session ID to your javascript client and storing your pexpect session in a global variable or some other container so that you can complete the program's input and output across multiple independent AJAX requests. I leave that up to you, however, as that is not as simple.
好吧,我使用 gevent 并且它是内置的 WSGI 服务器。(查看WSGI 服务器(另一个)是什么)。我应该注意到这个程序将保持一个状态,因此您可以通过将会话 ID 返回到您的 javascript 客户端并将您的 pexpect 会话存储在全局变量或其他容器中来管理您打开的会话,以便您可以完成程序的输入和输出跨多个独立的 AJAX 请求。然而,我把这留给你,因为这并不那么简单。
All my example will do is put the POST request in some after clicking something of your choice. (it won't actually work because some of the variables are not set. Set them.)
我的所有示例都会做的是在单击您选择的某些内容后将 POST 请求放入某些内容中。(它实际上不会工作,因为一些变量没有设置。设置它们。)
Heres the relevant parts:
以下是相关部分:
<!-- 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>
Then the server:
然后服务器:
# 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()
If your program is Object Oriented, it'd be fairly easy to integrate this. EDIT: Doesn't need to be object oriented. and I have now included some Pexpect 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"
Another option I suggest is Nginx + uWSGI. If you would prefer that, I can give you some examples of that as well. It gives you the benefit of incorporating the webserver into the setup.
我建议的另一个选择是 Nginx + uWSGI。如果你愿意,我也可以给你一些例子。它为您提供了将网络服务器合并到设置中的好处。
回答by jfs
To pass transparently your data from javascript to external Python program you could use WebSocket protocol to connect your server and javascript, and use stdin/stdout to communicate with the external program from the server.
要将数据从 javascript 透明地传递到外部 Python 程序,您可以使用 WebSocket 协议连接服务器和 javascript,并使用 stdin/stdout 与来自服务器的外部程序进行通信。
Here's an example Python program client.py
:
这是一个示例 Python 程序client.py
:
#!/usr/bin/env python
"""Convert stdin to upper case."""
for line in iter(raw_input, 'quit'):
print line.upper()
I've created a server using code from hello world websocket exampleand SO answer about how to create a new process on each incoming connection and to redirect all input data to the process' stdin:
我已经使用hello world websocket 示例中的代码创建了一个服务器,并回答了有关如何在每个传入连接上创建新进程并将所有输入数据重定向到进程的 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)
The web-client part, sendkeys.html
:
网络客户端部分,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>
and sendkeys.js
:
和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");
});
}
});
To try it:
试试看:
- download this gist
install
twisted
,txws
:$ pip install twisted txws
run:
$ twistd -ny wscli.py
visit
http://localhost:8080/
click on
sendkeys.html
and type something
- 下载这个要点
安装
twisted
,txws
:$ pip install twisted txws
跑:
$ twistd -ny wscli.py
访问
http://localhost:8080/
点击
sendkeys.html
并输入一些东西
回答by Colin Dunklau
回答by Ivan P
This depends on what sort of application you are wrapping and on how your GUI options translate into application commands. But you have two goals here:
这取决于您包装的应用程序类型以及您的 GUI 选项如何转换为应用程序命令。但是你在这里有两个目标:
Writing a wrapper to allow you to read your program's output and provide it input.
Making a web-server to receive GUI events and convert them to commands to pass to your "wrapper"
编写包装器以允许您读取程序的输出并为其提供输入。
制作一个网络服务器来接收 GUI 事件并将它们转换为命令以传递给您的“包装器”
I have done something like what you need to do.
我已经做了类似你需要做的事情。
Essentially you need to turn socket streams into discreet commands. The defacto tool for this is expect, and any of it's wrappers (I've used pexpect, the Python wrapper, and had a good experience with it).
This part might not be simple. The problem is that your underlying program is persistently running, and so your web-server should be statefull to know about the program across requests. The other option is for your web-server to simply re-attach to the process and issue it commands, and send back a response when it is encountered in the stdout stream, but you might end up with long response times depending on how fast the program is. Also there is the mismatch that AJAX requests are asynchronous, while your underlying program is synchronous. So yes, this can get quite complex. It really depends on your program. If you could add some details about what the program and the GUI are like, it'd help.
本质上,您需要将套接字流转换为谨慎的命令。事实上,这个工具是expect,以及它的任何包装器(我使用过pexpect,Python 包装器,并且对它有很好的体验)。
这部分可能并不简单。问题是您的底层程序一直在运行,因此您的 Web 服务器应该是有状态的,以便跨请求了解该程序。另一种选择是让您的 Web 服务器简单地重新附加到进程并发出命令,并在 stdout 流中遇到响应时发回响应,但最终响应时间可能会很长,具体取决于响应的速度程序是。此外,AJAX 请求是异步的,而您的底层程序是同步的,这是不匹配的。所以是的,这可能会变得非常复杂。这真的取决于你的程序。如果您可以添加一些有关程序和 GUI 的详细信息,它会有所帮助。