Can someone please tell me the difference between
urls = (
"/count", "count",
"/reset", "reset")
app = web.application(urls, locals())
store = web.session.DiskStore('sessions')
session = web.session.Session(app, store, initializer={'count': 0})
class count:
def GET(self):
session.count += 1
return str(session.count)
class reset:
def GET(self):
session.kill()
return ""
if __name__ == "__main__":
app.run()
and
urls = (
"/count", "count",
"/reset", "reset")
app = web.application(urls, locals())
class count:
counting = 0
def GET(self):
count.counting += 1
return str(count.counting)
class reset:
def GET(self):
count.counting = 0
return ""
if __name__ == "__main__":
app.run()
Both their output is exactly the same as far as I can tell. If there is no difference then what is the advantage of using Session objects over variables like this ?
I am pretty new to Python and going through Zed Shaw's Learn Python the Hard Way. I was on exercise 52 where he introduces sessions when this question popped into my head.
In the second instance, all browsers that connect to your application share a single counter. In the first instance, each browser counts according to its own session.
And, as kubked pointed out, the counter is persisted on disk in the first instance.
Reference: http://webpy.org/cookbook/sessions
As far as i see in https://gitorious.org/lpthw-web/lpthw-web/source/b1ab4df58746c5d4e3dfb41e502a8192caec3ef1:web/session.py DiskStore session will keep session in file system. If server crashes you can run it once again and opened session will be still stored. With keeping data in variable, which means it will be stored in RAM, server crash cause losing it.
Of course keeping user data in session will be also better when you decide to use threads.
Related
I have several classes in my program.
The main one called WebServer creates the web.py application itself, and calls to other classes for the webpages. Can I pass self.var1 for example to the search class __init__? Because I thought of just creating a method in the index class like set_var1 or something like that, then I don't know how to access the specific instance of this class the the web application creates.
The class:
import sys
import os
import web
from pages.search import search
from pages.index import index
class WebServer:
def __init__(self):
self.var1 = "test"
self.urls = (
'/', 'index',
'/search', 'search'
)
self.app = web.application(self.urls, globals())
self.app.run()
if __name__ == "__main__":
w = WebServer()
Not really, no. Specific instances of search and index are created by web.py in response to an incoming request. There are better / easier ways.
Also, putting this initialization in a WebServer class, while possible, isn't the common way of doing it with web.py. There's no need for the class to do this: it's a singleton and this file is essentially a startup / configuration file.
To have application-wide information available to your "response" classes (search, index, etc.), make that information either global, or hook it into web.config which is a web.Storage(). For example:
app = web.application(urs, globals())
web.config.update({"var1" : "test"})
app.run()
Which is then available to you responses. For example:
class search(object):
def GET(self):
if web.config.var1 == 'test':
return do_test_search()
return do_regular_search()
I have a Python Tornado app. The app contains request handlers, for which I am passing data to like (the code below is not complete, and is just to illustrate what I want):
configs = {'some_data': 1, # etc.
}
class Application(tornado.web.Application):
def __init__(self):
handlers = [('/pageone', PageOneHandler, configs),
('/pagetwo', PageTwoHandler, configs)]
settings = dict(template_path='/templates',
static_path='/static', debug=False)
tornado.web.Application.__init__(self, handlers, **settings)
# Run the instance
# ... code goes here ...
application = Application()
http_server = tornado.httpserver.HTTPServer(application)
# ... other code (bind to port, etc.)
# Callback function to update configs
some_time_period = 1000 # Once an second
tornado.ioloop.PeriodicCallback(update_configs, some_time_period).start()
tornado.ioloop.IOLoop.instance().start()
I want the update_configs function to update the configs variable defined above and have this change propagate through the handlers. For example (I know this doesn't work):
def update_configs():
configs['some_data'] += 1
# Now, assuming PageOneHandler just prints out configs['some_data'], I'd expect
# the output to be: "1" on the first load, "2" if I load the page a second
# later, "4" if I load the page two seconds after that, etc.
The problem is, the configs variable is passed along to the handlers during creation in the constructor for the Application class. How can I update configs['some_data'] in the periodic callback function?
My actual use case for this mechanism is to refresh the data stored in the configs dictionary from the database every so often.
Is there an easy way to do this without fiddling around with application.handlers (which I have tried for the past hour or so)?
Well, the simplest thing would be to pass the entire config dict to the handlers, rather than just the individual values inside the dict. Because dicts are mutable, any change you make to the values in the dict would then propagate to all the handlers:
import tornado.web
import tornado.httpserver
configs = {'some_data': 1, # etc.
}
def update_configs():
print("updating")
configs['some_data'] += 1
class PageOneHandler(tornado.web.RequestHandler):
def initialize(self, configs):
self.configs = configs
def get(self):
self.write(str(self.configs) + "\n")
class PageTwoHandler(tornado.web.RequestHandler):
def initialize(self, configs):
self.configs = configs
def get(self):
self.write(str(self.configs) + "\n")
class Application(tornado.web.Application):
def __init__(self):
handlers = [('/pageone', PageOneHandler, {'configs' : configs}),
('/pagetwo', PageTwoHandler, {'configs': configs})]
settings = dict(template_path='/templates',
static_path='/static', debug=False)
tornado.web.Application.__init__(self, handlers, **settings)
# Run the instance
application = Application()
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
# Callback function to update configs
some_time_period = 1000 # Once an second
tornado.ioloop.PeriodicCallback(update_configs, some_time_period).start()
tornado.ioloop.IOLoop.instance().start()
Output:
dan#dantop:~> curl localhost:8888/pageone
{'some_data': 2}
dan#dantop:~> curl localhost:8888/pageone
{'some_data': 3}
dan#dantop:~> curl localhost:8888/pagetwo
{'some_data': 4}
dan#dantop:~> curl localhost:8888/pageone
{'some_data': 4}
To me this approach makes the most sense; the data contained in configs doesn't really belong to any one instance of a RequestHandler, it's global state shared by all RequsetHandlers, as well as your PeriodicCallback. So I don't think it makes sense to try to create X numbers of copies of that state, and then try to keep all those different copies in sync manually. Instead, just share the state across your whole process using either a custom object with class variables, or a dict, as shown above.
Another strategy, in addition to what dano mentions above is to attach the shared data to the Application object.
class MyApplication(tornado.web.Application):
def __init__(self):
self.shared_attribute = foo;
handlers = [#your handlers here]
settings = dict(#your application settings here)
super().__init__(handlers, **settings)
server = tornado.httpserver.HTTPServer(MyApplication())
server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Next you can access shared_attribute defined above in all your request handlers using self.application.shared_attribute.
You update it at one place and it immediately reflects in all your subsequent calls to the request handlers.
The code below runs a simple WSGI app that increments both a local counter and a global counter. The purpose of this test is to make sure the app object is created fresh for each HTTP request, so the expected behavior is for the global counter to increment each request while the local counter should always be ‘1’, because it is only incremented once for each instantiation of the app class.
Actual result? Pretty much what I expected, but the surprise is that the global counter is getting incremented TWICE for each HTTP request after the first one. (The sequence is something like [1, 3, 5, 7, 9, . . .].) I know that the iter() method is only being called once per app object instantiation because the local variable is always ‘1’. So what’s going on?
This may not matter as all I really care about is making sure the WSGI container always creates a new object for each request and uses it only once, but it is really odd. I would like to know why.
I do not like unexplained side effects. Can someone give me some insight?
gctr = 0
class app(object):
html = '''<html><head><title>Simple WSGI App Class Test</title></head>
<body><h2>Simple WSGI App Class Test</h2>
<p>module counter = {}</p>
<p>global counter = {}</p>
</body></html>'''
ctr = 0
def __init__(self, environ, start_response):
self.environ = environ
self.start = start_response
def __iter__(self):
self.start('200 OK', [('Content-Type', 'text/html')])
global gctr
gctr += 1
self.ctr += 1
yield self.html.format(self.ctr, gctr)
if __name__ == '__main__':
# Using simple_server
from wsgiref.simple_server import make_server
srv = make_server('localhost', 8080, app)
srv.serve_forever()
I expect this is your browser making another request alongside the main one, which is also getting captured by your WSGI app - probably for favicon.ico. Log self.environ['PATH_INFO'] to see the exact request.
I have the following two handlers for a web.py setup:
class count1:
def GET(self):
s.session.count += 1
return str(s.session.count)
class count2:
def GET(self):
s.session.count += 1
yield str(s.session.count)
The app runs on web.py shipped cherrypy (app.run()) or gevent server.
urls = (
"/count1", "count.count1",
"/count2", "count.count2",
)
session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'count': 0})
s.session = session
app = web.application(urls, locals())
print "Main: setting count to 1"
from gevent.wsgi import WSGIServer
if __name__ == "__main__":
usecherrypy = False
if usecherrypy:
app.run()
else: # gevent wsgiserver
wsgifunc = app.wsgifunc()
server = WSGIServer(('0.0.0.0', 8080), wsgifunc, log=None)
server.serve_forever()
Session works fine in count1 case but not always in count2. In the first time a page of /count2 is loaded the counter is increased once, but refreshing after that doesn't increase the counter in session i.e. the update to session is never saved. What would be wrong here?
Webpy installed from pypi or latest from github behaves the same in this case.
After digging into the code, the actual reason seems to be that, when the handler using yield, it is only being called to return the generator object, and then are returned from all enclosing processors (e.g. Session._processor which calls _save in the finally block). Web.py makes sure that the generator is completely unrolled before returning the data to the client, but the unroll process is after all processors which is completely different behavior comparing to normal function handlers.
So the question is as: is there any fixes, or workarounds (apart from calling Session._save manually) to this?
Thanks in advance for any answers!
Maybe it happens because yield returns a generator and not a value.
Refs:
http://od-eon.com/blogs/calvin/python-yield-versus-return/
What does the "yield" keyword do in Python?
I can use a task queue to change the database value, but how can I get the return value like Ajax using task queue?
This is my code:
from google.appengine.api.labs import taskqueue
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
import os
class Counter(db.Model):
count = db.IntegerProperty(indexed=False)
class BaseRequestHandler(webapp.RequestHandler):
def render_template(self, filename, template_values={}):
values={
}
template_values.update(values)
path = os.path.join(os.path.dirname(__file__), 'templates', filename)
self.response.out.write(template.render(path, template_values))
class CounterHandler(BaseRequestHandler):
def get(self):
self.render_template('counters.html',{'counters': Counter.all()})
def post(self):
key = self.request.get('key')
# Add the task to the default queue.
for loop in range(0,1):
a=taskqueue.add(url='/worker', params={'key': key})
#self.redirect('/')
self.response.out.write(a)
class CounterWorker(webapp.RequestHandler):
def post(self): # should run at most 1/s
key = self.request.get('key')
def txn():
counter = Counter.get_by_key_name(key)
if counter is None:
counter = Counter(key_name=key, count=1)
else:
counter.count += 1
counter.put()
db.run_in_transaction(txn)
self.response.out.write('sss')#used for get by task queue
def main():
run_wsgi_app(webapp.WSGIApplication([
('/', CounterHandler),
('/worker', CounterWorker),
]))
if __name__ == '__main__':
main()
How can I show the 'sss'?
The current Task Queue API doesn't support processing return values or sending them back to the point of origin. Your appengine process isn't long-lived enough for that programming paradigm to work.
In your example, it looks like what you want is something like this:
Create task
Return AJAX code that will poll a task-status handler
Task processes, updates datastore with a return value
Task-status url returns updated value
Alternatively, if you don't want to return the 'sss' to the client but instead need it for further processing, you'll need to split your method into multiple parts. The first part creates the task and then exits. At the end of the task's process, it adds a new task itself to call back into the second part with the return value.