Hi all,
I’ve made somewhat of a hack that I’d like to you by you to hear what you think of it.
In a nutshell, sending a request to a web-server is one way; you send, the server responds. It’s a “pull” relationthip. There’s no way to get the server to send any data to a client without it having asked for it, that’s why you’ve got things like socket.io and gevent. A “push” relationship.
The hack then, is a way around this and assumes that you have control over both server and client. It works like this.
Sending a request from server-to-client
The handle can be held indefinitely, and may be re-established per response. Effectively allowing the server to make any amount of “requests” to the client.
Here’s an implementation.
"""Request-response inversion
Inverse the typical request-response pattern so as to allow a host to make
requests to the client.
Usage:
# Terminal 1
# The client
>>> import server
>>> import threading
>>> t = threading.Thread(target=server.app.run, kwargs={"threaded": True})
>>> t.start()
# Terminal 2
# The host
$ curl -X POST http://127.0.0.1:5000/dispatch
... blocking
# Terminal 1
# Client sending "request"
>>> server.queue.put("show")
# Terminal 2
# Host receiving "request"
{"status": "ok", "result": "Showing.."}
Requests may be made *before* having been responded to. With that, we've
got a true bi-directional communication link going.
Usage:
# Terminal 1
# The client
>>> import server
>>> import threading
>>> t = threading.Thread(target=server.app.run, kwargs={"threaded": True})
>>> t.start()
# Terminal 2
# First request from host
$ curl -X POST http://127.0.0.1:5000/dispatch
... blocking
# Terminal 3
# Second request from host
$ curl -X GET http://127.0.0.1:5000/dispatch
{"status": "ok", "queue": []}
# Terminal 1
# Client sending "request"
>>> server.queue.put("show")
# Terminal 2
# Host receiving "request".
{"status": "ok", "result": "Showing.."}
"""
# Standard library
import Queue
# Third-party dependencies
import flask
app = flask.Flask(__name__)
# This queue is used for communication between threads.
# It'll be queried (and may be empty) by the client,
# and filled by the host. When filled, the blocking query
# is released and processed.
queue = Queue.Queue()
@app.route("/dispatch", methods=["GET", "POST"])
def dispatch():
if flask.request.method == "GET":
return flask.jsonify(status="ok", queue=list(queue.queue))
else:
cmd = queue.get()
if cmd == "show":
return flask.jsonify(status="ok", result="Showing..")
return flask.jsonify(status="fail",
result="Command not recognised: %s" % cmd)
```
Technically, no requests are being made to the client. Practically on the other hand, any data can be sent, asynchronously, in both directions.
Thoughts?
Best,
Marcus
I’d like to you by you to hear what you think of it.
That’s some literacy excellence, right there. :)
I’d like to you by you to hear what you think of it.
That’s some literacy excellence, right there. :)
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOCCmk5mrOcz0dXtGCvGaN7RqMZZgiTXmg3TPnzTZDu_0A%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Then it sounds like the long polling solution should work just fine if you only need a one-off notification.
Thanks Justin, long-polling sounds like what this is.The specific problem is having a client receive a single notification from the server, with as little effort as possible. There is no data involved, and the event will only happen once. Because of this, I'm looking for alternatives to more complete solutions, like socket.io, that fit the bill.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOB3%2Bo4GvwjB5P1%3DHwZxRsfdrS_STyMTB4F%3DSRUTQNxseQ%40mail.gmail.com.
Cool, thanks.
At the risk of going off topic, I was also looking into the signals
standard library for this, as I’ve got a handle to the Popen
instance of the client in this case.
Have you, or anyone, had any experience in passing user-defined events across processes using signals?
Does this mean the communication is taking place all on the same machine? If that is the case, why not just use sockets? Why the whole http server/client?
I've used signals for specific things and you don't necessarily have to have launched or own the target process. In a certain daemon service I maintain, I use USR1 to signal a specific type of shutdown request as opposed to a SIGTERM.
If you own the child process, you can just use a pipe between them to communicate.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOA-n_wPNHCgnyWr0HebuJMaN5onD4LMYYDCRXkREQVr%2Bw%40mail.gmail.com.
Ah, excellent. This is what I’ve been looking for, I merely used this inverse request thing because I didn’t understand the alternatives well enough.
If you own the child process, you can just use a pipe between them to communicate.
I’ve seen this mentioned, read about it, attempted it, but never really understood it. Could you provide a small example?
Here’s how my situation looks.
>>> import subprocess
>>> proc = subprocess.Popen(["python"], creationflags=subprocess.CREATE_NEW_PROCESS)
>>> send_signal_somehow(proc)
It’ll open up a new interpreter, how can I handle the incoming signal in there, and how do I send it from the parent process?
In a certain daemon service I maintain, I use USR1 to signal a specific type of shutdown request as opposed to a SIGTERM.
That also sounds viable; in this case, it’s a request for a GUI in an external process to show, which I’d imagine being similar if not identical to a shutdown request.
Sorry, that’s:
>>> import subprocess
>>> proc = subprocess.Popen(["python"], creationflags=subprocess.CREATE_NEW_CONSOLE)
>>> send_signal_somehow(proc)
Unfortunately I can't give a custom example at this moment, but you have a perfectly good stdin pipe that can be used in your subprocess Popen command. Just google how to use it in the meantime. You can write to stdin in the parent process and read from it in the child. It would be exactly like what you originally described with http.The child can block and wait on stdin until the parent sends something.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOAavU0BSO%3DBnOWZ1WuJNN5zXZk1WDtOKq8xu1cc%3D%3D9FVg%40mail.gmail.com.
No problem, Justin.
Also, I missed this.
why not just use sockets?
Sockets, as in starting another server within the client? Or are you referring to a more light-weight way of utilising sockets here? The weight of instantiating another server was one reason for not doing that initially.
My suggestion of sockets was a lighter weight suggestion than using flask to start an httpserver and route full requests between processes. Maybe I am missing something from that example. But flask is an extremely heavy handed approach to getting to processes talking on the same machine. Especially when you have a parent child process relationship. That was why I ended up suggesting just a pipe when you are launching the process. A pipe, say using stdin, is like a socket connection that just gets connected automatically, with one end have a read side and one having a write side.
That was why I had originally asked about the concrete problem, because an httpserver is completely reasonable under one circumstance a heavy in another. Same with a pipe being reasonable in one, and not feasible in another.
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOCcU2x9LERBt1nMaD89FYz7tnR1OCOxQqgUUAwrWpUDwg%40mail.gmail.com.
But flask is an extremely heavy handed approach to getting to processes talking on the same machine.
That’s exactly what I’ve got, and can understand it’s heavy-handed.
I did have a closer look at subprocess
and pipes, but quickly remembered what my struggles were last time I tried. Maybe in time I’ll transition that way, because at the moment it’s above my head.
I have no experience with sockets either; I assumed it was the same as communicating via GET and POST using urllib2
.
At the end of the day, I have two processes, on the same machine, in a parent/child relationship, passing plain-old-data between each other (one-way, mostly) using Flask and HTTP requests. If you find the time for an example of an alternative, that would be really great.
Best,
Marcus
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmODhJmYKa62Dv4gkTwQyqaj1OJ__bAgrB1Kw2-%2BkS71sBA%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAPGFgA2coSA4qCSpj%2BXBkAaGA6NBbbeRzNqbh01tBUxcDpK0GA%40mail.gmail.com.
The long-polling approach presented another challenge; how do you cancel an ongoing HTTP request?
As the request is ongoing until given a reply from the server, when the server finally dies, the client will be left without a response and throws an exception.
Is it safe to discard the exception, and force the termination?
I ended up using
os._exit(1)
Instead of
sys.exit()
Which works, but probably bypasses more than just clean-up of the sockets involved.
I noticed you’ve gotten a similar question before, Justin.
http://stackoverflow.com/questions/9389414/how-to-kill-a-requests-request-object-that-is-in-progress