Home
entries friends calendar user info

Advertisement

Friends
jcalderone
[info]jcalderone
Add to Memories
Tell a Friend

Welcome back to "Twisted Web in 60 seconds". Over the previous two entries, I introduced Twisted Web's session APIs. This included accessing the session object, storing state on it, and retrieving it later. I described how the Session object has a lifetime which is tied to the notional session it represents. In this installment, I'll describe how you can exert some control over that lifetime and react when it expires.

The lifetime of a session is controlled by the sessionTimeout attribute of the Session class. This attribute gives the number of seconds a session may go without being accessed before it expires. The default is 15 minutes. In this example, I'll show you change that to a different value.

One way to override the value is with a subclass:

  from twisted.web.server import Session

 class ShortSession(Session):
     sessionTimeout = 60

To have Twisted Web actually make use of this session class, rather than the default, it is also necessary to override the sessionFactory attribute of Site. I could do this with another subclass, but I can also do it to just one instance of Site:

  from twisted.web.server import Site

 factory = Site(rootResource)
 factory.sessionFactory = ShortSession

Sessions given out for requests served by this Site will use ShortSession and only last one minute without activity.

You can have arbitrary functions run when sessions expire, too. This can be useful for cleaning up external resources associated with the session, tracking usage statistics, and more. This functionality is provided via Session.notifyOnExpire. It accepts a single argument: a function to call when the session expires. Here's a trivial example which prints a message whenever a session expires:

  from twisted.web.resource import Resource

 class ExpirationLogger(Resource):
     sessions = set()

     def render_GET(self, request):
         session = request.getSession()
         if session.uid not in self.sessions:
             self.sessions.add(session.uid)
             session.notifyOnExpire(lambda: self._expired(session.uid))
         return ""

     def _expired(self, uid):
         print "Session", uid, "has expired."
         self.sessions.remove(uid)

Keep in mind that using a method as the callback will keep the instance (in this case, the ExpirationLogger resource) in memory until the session expires.

With those pieces in hand, here's an example that prints a message whenever a session expires, and uses sessions which last for 5 seconds:

from twisted.web.server import Site, Session
from twisted.web.resource import Resource
from twisted.internet import reactor

class ShortSession(Session):
   sessionTimeout = 5

class ExpirationLogger(Resource):
   sessions = set()

   def render_GET(self, request):
       session = request.getSession()
       if session.uid not in self.sessions:
           self.sessions.add(session.uid)
           session.notifyOnExpire(lambda: self._expired(session.uid))
       return ""

   def _expired(self, uid):
       print "Session", uid, "has expired."
       self.sessions.remove(uid)

rootResource = Resource()
rootResource.putChild("logme", ExpirationLogger())
factory = Site(rootResource)
factory.sessionFactory = ShortSession

reactor.listenTCP(8080, factory)
reactor.run()

Since Site customization is required, this example can't be rpy-based, so it brings back the manual reactor.listenTCP and reactor.run calls. Run it and visit /logme to see it in action. Keep visiting it to keep your session active. Stop visiting it for five seconds to see your session expiration message.

That pretty much wraps things up for Twisted Web's built in session support. Next time I'll cover some of Twisted Web's proxying features.

Tags: , , , , ,

jcalderone
[info]jcalderone
Add to Memories
Tell a Friend

I've made some new releases of software formerly developed by Divmod, Inc. and now maintained by the community. These releases include changes from both before and after the end of Divmod. Click through to find links to release details.

Enjoy.

Tags: , , , , , , , , , , , , ,

jcalderone
[info]jcalderone
Add to Memories
Tell a Friend

Welcome to the 16th installment of "Twisted Web in 60 seconds". Last time I introduced the basic APIs for interacting with Twisted Web sessions. In this installment, I'll show you how you can persist objects across requests in the session object.

As I discussed last time, instances of Session last as long as the notional session itself does. Each time Request.getSession is called, if the session for the request is still active, then the same Session instance is returned as was returned previously. Because of this, Session instances can be used to keep other objects around for as long as the session exists.

It's easier to demonstrate how this works than explain it, so here's an example:

  >>> from zope.interface import Interface, Attribute, implements
 >>> from twisted.python.components import registerAdapter
 >>> from twisted.web.server import Session
 >>> class ICounter(Interface):
 ...     value = Attribute("An int value which counts up once per page view.")
 ...
 >>> class Counter(object):
 ...     implements(ICounter)
 ...     def __init__(self, session):
 ...         self.value = 0
 ...
 >>> registerAdapter(Counter, Session, ICounter)
 >>> ses = Session(None, None)
 >>> data = ICounter(ses)
 >>> print data
 <__main__.Counter object at 0x8d535ec>
 >>> print data is ICounter(ses)
 True
 >>>

What?, I hear you say.

What's shown in this example is the interface and adaption based API which Session exposes for persisting state. There are several critical pieces interacting here:

  • ICounter is an interface which serves several purposes. Like all interfaces, it documents the API of some class of objects (in this case, just the value attribute). It also serves as a key into what is basically a dictionary within the session object: the interface is used to store or retrieve a value on the session (the Counter instance, in this case).
  • Counter is the class which actually holds the session data in this example. It implements ICounter (again, mostly for documentation purposes). It also has a value attribute, as the interface declared.
  • The registerAdapter call sets up the relationship between its three arguments so that adaption will do what we want in this case.
  • Adaption is performed by the expression ICounter(ses). This is read as adapt ses to ICounter. Because of the registerAdapter call, it is roughly equivalent to Counter(ses). However (because of certain things Session does), it also saves the Counter instance created so that it will be returned the next time this adaption is done. This is why the last statement produces True.

If you're still not clear on some of the details there, don't worry about it and just remember this: ICounter(ses) gives you an object you can persist state on. It can be as much or as little state as you want, and you can use as few or as many different Interface classes as you want on a single Session instance.

With those conceptual dependencies out of the way, it's a very short step to actually getting persistent state into a Twisted Web application. Here's an example which implements a simple counter, re-using the definitions from the example above:

  from twisted.web.resource import Resource

 class CounterResource(Resource):
     def render_GET(self, request):
         session = request.getSession()
         counter = ICounter(session)
         counter.value += 1
         return "Visit #%d for you!" % (counter.value,)

Pretty simple from this side, eh? All this does is use Request.getSession and the adaption from above, plus some integer math to give you a session-based visit counter.

Here's the complete source for an rpy script based on this example:

cache()

from zope.interface import Interface, Attribute, implements
from twisted.python.components import registerAdapter
from twisted.web.server import Session
from twisted.web.resource import Resource

class ICounter(Interface):
   value = Attribute("An int value which counts up once per page view.")

class Counter(object):
   implements(ICounter)
   def __init__(self, session):
       self.value = 0

registerAdapter(Counter, Session, ICounter)

class CounterResource(Resource):
   def render_GET(self, request):
       session = request.getSession()
       counter = ICounter(session)  
       counter.value += 1
       return "Visit #%d for you!" % (counter.value,)

resource = CounterResource()

One more thing to note is the cache() call at the top of this example. As with the previous example where this came up, this rpy script is stateful. This time, it's the ICounter definition and the registerAdapter call that need to be executed only once. If we didn't use cache, every request would define a new, different interface named ICounter. Each of these would be a different key in the session, so the counter would never get past one.

There's one more interesting thing you can do with sessions in Twisted Web right out of the box. Tune in next time to find out what.

Tags: , , , , ,

deeptape
[info]deeptape
Add to Memories
Tell a Friend
Ent_iPhone_smaller_tm-2.png
Monstrous is proud to announce that 
Entranced is now available in the Apple iTunes store!


To celebrate the launch of Entranced, we've lowered the price by 50%!
Click here to buy the game for $1.99!


Entranced is a fresh, finely-tuned visual music game that takes the experience of listening to music to an entirely new level, blending brilliant colors and intoxicating sounds into full synesthesia.

Each track is perfectly matched with custom visuals, creating a dreamlike experience that takes you deep into the song. Tap the beats to the rhythm of the music and watch the notes transform into beautiful and imaginative blooms. With 5 musical worlds and many more to come, you'll enjoy hours of entertainment that will intensify your love for music.

Entranced showcases songs from some of the best DJs and musicians from around the world, including Makyo, Adham Shaikh, Sounds from the Ground, and Karsh Kale.


If you enjoy Entranced, please help us spread the word:
Join our Facebook fan page
Follow us on Twitter
Write a review on iTunes

Tell your friends! :-)


 



Tags: ,

profile
User: [info]amiramir
Name: Divine Modern
calendar
Back November 2005
12345
6789101112
13141516171819
20212223242526
27282930
page summary
tags