freeze function calls

S

Santiago Caracol

Hello,

I want to write a web application that does this:

(1) The user submits a query:

---------------------------------
| What is the answer? |
---------------------------------
<Submit>

(2) The web server gives the user N answers and a button saying "More
answers":

.. answer 1
.. answer 2
.. answer 3

<More answers>

I am aware of several ways to do this: I could calculate all
answers, but show only the first N of them. For certain kinds of
calulations,
I could use a kind of setoff argument. But I would like to do it in a
more general
and (hopefully) efficient way:

I want the function or object that calculates the answers to be
"frozen" at the point at which it has already calculated N answers. If
the function gets a <More answers>-signal within a reasonable period
of time, it goes on producing more answers exactly at the point at
which it got frozen. If no signal is sent, the function call is
terminated automatically after
M seconds.

Note that, although the program to be written is a web application,
this is not a question about web application specific things. My only
difficulty is how to "freeze" function calls.

Has anyone done something of this kind?

Santiago
 
P

Peter Otten

Santiago said:
Hello,

I want to write a web application that does this:

(1) The user submits a query:

---------------------------------
| What is the answer? |
---------------------------------
<Submit>

(2) The web server gives the user N answers and a button saying "More
answers":

. answer 1
. answer 2
. answer 3

<More answers>

I am aware of several ways to do this: I could calculate all
answers, but show only the first N of them. For certain kinds of
calulations,
I could use a kind of setoff argument. But I would like to do it in a
more general
and (hopefully) efficient way:

I want the function or object that calculates the answers to be
"frozen" at the point at which it has already calculated N answers. If
the function gets a <More answers>-signal within a reasonable period
of time, it goes on producing more answers exactly at the point at
which it got frozen. If no signal is sent, the function call is
terminated automatically after
M seconds.

Note that, although the program to be written is a web application,
this is not a question about web application specific things. My only
difficulty is how to "freeze" function calls.

Has anyone done something of this kind?

Python offers an elegant mechanism to calculate values on demand: the
generator function:
.... for i in range(100):
.... print "calculating answer #%d" % i
.... yield i * i
....
This builds the generator but doesn't run the code inside. Now let's look at
the first three "answers":
.... print "the answer is", answer
....
calculating answer #0
the answer is 0
calculating answer #1
the answer is 1
calculating answer #2
the answer is 4

If you repeat the last step you get the next three answers:
.... print "the answer is", answer
....
calculating answer #3
the answer is 9
calculating answer #4
the answer is 16
calculating answer #5
the answer is 25

Peter
 
S

Santiago Caracol

Python offers an elegant mechanism to calculate values on demand: the
generator function:


...     for i in range(100):
...             print "calculating answer #%d" % i
...             yield i * i
...

Thanks for pointing this out. I was aware of the yield statement.

My problem is this:

(1) The user submits a query:

---------------------------------
| What is the answer? |
---------------------------------
<Submit>

(2) The web server gives the user N answers and a button saying "More
answers":

.. answer 1
.. answer 2
.. answer 3

<More answers>

At this stage the function that writes html to the user has been
called. The call must be terminated, or else, the user doesn't get any
html. This means that the call of the answer-calculating function,
whether it uses yield or not, is also terminated. This means, when the
user presses the <More answers>-button, the calculation has to start
at the beginning.
 
P

Peter Otten

Santiago said:
Thanks for pointing this out. I was aware of the yield statement.

My problem is this:

(1) The user submits a query:

---------------------------------
| What is the answer? |
---------------------------------
<Submit>

(2) The web server gives the user N answers and a button saying "More
answers":

. answer 1
. answer 2
. answer 3

<More answers>

At this stage the function that writes html to the user has been
called. The call must be terminated, or else, the user doesn't get any
html. This means that the call of the answer-calculating function,
whether it uses yield or not, is also terminated. This means, when the
user presses the <More answers>-button, the calculation has to start
at the beginning.

Adapted from the wsgiref documentation at

http://docs.python.org/library/wsgiref.html

$ cat wsgi_demo.py
from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server

from itertools import count, islice

def answers():
for i in count():
yield "Answer #%d\n" % i

gen = answers()

def simple_app(environ, start_response):
setup_testing_defaults(environ)

status = '200 OK'
headers = [('Content-type', 'text/plain')]

start_response(status, headers)

return islice(gen, 3)

httpd = make_server('', 8000, simple_app)
print "Serving on port 8000..."
httpd.serve_forever()

Run the above with

$ python wsgi_demo.py
Serving on port 8000...

Now point your favourite browser to http://localhost:8000/ and hit refresh.

Peter
 
S

Santiago Caracol

Run the above with
$ python wsgi_demo.py
Serving on port 8000...

Thanks a lot for this code. The problem with it is that the whole
application IS a generator function. That means that if I run the code
at, say foo.org, then any user that visits the site will augment the
answer number of the server running at foo.org. What I am trying to do
is to process specific queries for different users. Each user is
supposed to get his very own answers to his very own questions. And if
a user doesn't hit the <More answers>-button during a certain period
of time, the generator or function call reponsible for answering his
question is supposed to be killed or thrown-away or forgotten. If the
user asks a new question (while the answers to the old question are
still being displayed), then the generator or function call is also
supposed to be forgotten and a new generator or function call -- one
that matches the user's new question -- is supposed to be initiated.

Santiago
 
P

Peter Otten

Santiago said:
Thanks a lot for this code. The problem with it is that the whole
application IS a generator function. That means that if I run the code
at, say foo.org, then any user that visits the site will augment the
answer number of the server running at foo.org. What I am trying to do
is to process specific queries for different users. Each user is
supposed to get his very own answers to his very own questions. And if
a user doesn't hit the <More answers>-button during a certain period
of time, the generator or function call reponsible for answering his
question is supposed to be killed or thrown-away or forgotten. If the
user asks a new question (while the answers to the old question are
still being displayed), then the generator or function call is also
supposed to be forgotten and a new generator or function call -- one
that matches the user's new question -- is supposed to be initiated.

Didn't you say you weren't interested in the web specific aspects?
You may be able to do it by hand (the environ argument will probably include
an IP that you can use to look up the generator in a dictionary) but I'd
rather say you need a web framework that provides the session management.
Cherrypy is a lightweight one, and after some try and way too much error I
came up with

# warning: newbie code
import cherrypy
from itertools import islice, count

def answers():
for i in count():
yield "Answer #%d\n" % i

class PythonRunner(object):
def index(self):
try:
gen = cherrypy.session["gen"]
except KeyError:
cherrypy.session["gen"] = gen = answers()
return "<html><body>%s</body></html>" % "".join(islice(gen, 3))
index.exposed = True

if __name__ == '__main__':
cherrypy.quickstart(PythonRunner(), config="cherry.conf")

The contents of cherry.conf are:

[global]
server.socket_host = "127.0.0.1"
server.socket_port = 8080
server.thread_pool = 10

[/]
tools.sessions.on = True
#tools.sessions.storage_type = "file"
#tools.sessions.storage_path = "./sessions"
tools.sessions.timeout = 60

See the result on http://localhost:8080/index
I opened the page in Firefox and Konqueror, and the numbers were
independent, so I'm pretty sure it works on a bigger scale, too.

$ python -c 'import cherrypy; print cherrypy.__version__'
3.1.2

Note that storage_type = "file" is straight out for generators because they
cannot be pickled. The workaround here would be a class like

class Answers(object):
def __init__(self):
self.i = 0
def __iter__(self):
return self
def next(self):
result = "Answer #%d\n" % self.i
self.i += 1
return result

Peter
 
S

Santiago Caracol

Peter,

thanks again for all this code. You helped me a lot.
Didn't you say you weren't interested in the web specific aspects?

I thought that, although my problem had to do with client-server
stuff, it wasn't really web-specific. But now I think that that was
part of my problem. I failed to see that using sessions could be the
solution.

Santiago
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top