Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 1736

Show
Ignore:
Timestamp:
10/04/07 03:10:31
Author:
fumanchu
Message:

New MemcachedSession? class. The Session class API had to change a bit: 'setup' must now be a classmethod.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/lib/sessions.py

    r1721 r1736  
    243243    LOCK_SUFFIX = '.lock' 
    244244     
    245     def setup(self): 
     245    def setup(cls, **kwargs): 
    246246        """Set up the storage system for file-based sessions. 
    247247         
     
    249249        automatically when using sessions.init (as the built-in Tool does). 
    250250        """ 
     251        for k, v in kwargs.iteritems(): 
     252            setattr(cls, k, v) 
     253         
    251254        # Warn if any lock files exist at startup. 
    252         lockfiles = [fname for fname in os.listdir(self.storage_path) 
    253                      if (fname.startswith(self.SESSION_PREFIX) 
    254                          and fname.endswith(self.LOCK_SUFFIX))] 
     255        lockfiles = [fname for fname in os.listdir(cls.storage_path) 
     256                     if (fname.startswith(cls.SESSION_PREFIX) 
     257                         and fname.endswith(cls.LOCK_SUFFIX))] 
    255258        if lockfiles: 
    256259            plural = ('', 's')[len(lockfiles) > 1] 
     
    259262                 "manually delete the lockfiles found at %r." 
    260263                 % (len(lockfiles), plural, 
    261                     os.path.abspath(self.storage_path))) 
     264                    os.path.abspath(cls.storage_path))) 
     265    setup = classmethod(setup) 
    262266     
    263267    def _get_file_path(self): 
     
    397401 
    398402 
     403class MemcachedSession(Session): 
     404     
     405    locks = {} 
     406    servers = ['127.0.0.1:11211'] 
     407     
     408    def setup(cls, **kwargs): 
     409        """Set up the storage system for memcached-based sessions. 
     410         
     411        This should only be called once per process; this will be done 
     412        automatically when using sessions.init (as the built-in Tool does). 
     413        """ 
     414        for k, v in kwargs.iteritems(): 
     415            setattr(cls, k, v) 
     416         
     417        import memcache 
     418        cls.cache = memcache.Client(cls.servers) 
     419    setup = classmethod(setup) 
     420     
     421    def _load(self): 
     422        return self.cache.get(self.id) 
     423     
     424    def _save(self, expiration_time): 
     425        # Send the expiration time as "Unix time" (seconds since 1/1/1970) 
     426        zeroday = datetime.datetime(1970, 1, 1, tzinfo=expiration_time.tzinfo) 
     427        td = expiration_time - zeroday 
     428        td = (td.days * 86400) + td.seconds 
     429        if not self.cache.set(self.id, (self._data, expiration_time), td): 
     430            raise AssertionError("Session data for id %r not set." % self.id) 
     431     
     432    def _delete(self): 
     433        self.cache.delete(self.id) 
     434     
     435    def acquire_lock(self): 
     436        """Acquire an exclusive lock on the currently-loaded session data.""" 
     437        self.locked = True 
     438        self.locks.setdefault(self.id, threading.RLock()).acquire() 
     439     
     440    def release_lock(self): 
     441        """Release the lock on the currently-loaded session data.""" 
     442        self.locks[self.id].release() 
     443        self.locked = False 
     444 
     445 
    399446# Hook functions (for CherryPy tools) 
    400447 
     
    466513        id = request.cookie[name].value 
    467514     
     515    # Find the storage class and call setup (first time only). 
     516    storage_class = storage_type.title() + 'Session' 
     517    storage_class = globals()[storage_class] 
     518    if not hasattr(cherrypy, "session"): 
     519        if hasattr(storage_class, "setup"): 
     520            storage_class.setup(**kwargs) 
     521     
    468522    # Create and attach a new Session instance to cherrypy.serving. 
    469523    # It will possess a reference to (and lock, and lazily load) 
    470524    # the requested session data. 
    471     storage_class = storage_type.title() + 'Session' 
    472525    kwargs['timeout'] = timeout 
    473526    kwargs['clean_freq'] = clean_freq 
    474     cherrypy.serving.session = sess = globals()[storage_class](id, **kwargs) 
     527    cherrypy.serving.session = sess = storage_class(id, **kwargs) 
    475528     
    476529    # Create cherrypy.session which will proxy to cherrypy.serving.session 
    477530    if not hasattr(cherrypy, "session"): 
    478531        cherrypy.session = cherrypy._ThreadLocalProxy('session') 
    479         if hasattr(sess, "setup"): 
    480             sess.setup() 
    481532     
    482533    # Set response cookie 
  • trunk/cherrypy/test/test_session.py

    r1708 r1736  
    4444        def setsessiontype(self, newtype): 
    4545            self.__class__._cp_config.update({'tools.sessions.storage_type': newtype}) 
     46            if hasattr(cherrypy, "session"): 
     47                del cherrypy.session 
    4648        setsessiontype.exposed = True 
     49        setsessiontype._cp_config = {'tools.sessions.on': False} 
    4750         
    4851        def index(self): 
     
    202205 
    203206 
     207try: 
     208    import memcache 
     209except ImportError: 
     210    pass 
     211else: 
     212    class MemcachedSessionTest(helper.CPWebCase): 
     213         
     214        def test_0_Session(self): 
     215            self.getPage('/setsessiontype/memcached') 
     216             
     217            self.getPage('/testStr') 
     218            self.assertBody('1') 
     219            self.getPage('/testGen', self.cookies) 
     220            self.assertBody('2') 
     221            self.getPage('/testStr', self.cookies) 
     222            self.assertBody('3') 
     223            self.getPage('/delkey?key=counter', self.cookies) 
     224            self.assertStatus(200) 
     225             
     226            # Wait for the session.timeout (1.02 secs) 
     227            time.sleep(1.25) 
     228            self.getPage('/') 
     229            self.assertBody('1') 
     230             
     231            # Test session __contains__ 
     232            self.getPage('/keyin?key=counter', self.cookies) 
     233            self.assertBody("True") 
     234             
     235            # Test session delete 
     236            self.getPage('/delete', self.cookies) 
     237            self.assertBody("done") 
     238         
     239        def test_1_Concurrency(self): 
     240            client_thread_count = 5 
     241            request_count = 30 
     242             
     243            # Get initial cookie 
     244            self.getPage("/") 
     245            self.assertBody("1") 
     246            cookies = self.cookies 
     247             
     248            data_dict = {} 
     249             
     250            def request(index): 
     251                for i in xrange(request_count): 
     252                    self.getPage("/", cookies) 
     253                    # Uncomment the following line to prove threads overlap. 
     254##                    print index, 
     255                if not self.body.isdigit(): 
     256                    self.fail(self.body) 
     257                data_dict[index] = v = int(self.body) 
     258             
     259            # Start <request_count> concurrent requests from 
     260            # each of <client_thread_count> clients 
     261            ts = [] 
     262            for c in xrange(client_thread_count): 
     263                data_dict[c] = 0 
     264                t = threading.Thread(target=request, args=(c,)) 
     265                ts.append(t) 
     266                t.start() 
     267             
     268            for t in ts: 
     269                t.join() 
     270             
     271            hitcount = max(data_dict.values()) 
     272            expected = 1 + (client_thread_count * request_count) 
     273            self.assertEqual(hitcount, expected) 
     274         
     275        def test_3_Redirect(self): 
     276            # Start a new session 
     277            self.getPage('/testStr') 
     278            self.getPage('/iredir', self.cookies) 
     279            self.assertBody("memcached") 
     280         
     281        def test_5_Error_paths(self): 
     282            self.getPage('/unknown/page') 
     283            self.assertErrorPage(404, "The path '/unknown/page' was not found.") 
     284             
     285            # Note: this path is *not* the same as above. The above 
     286            # takes a normal route through the session code; this one 
     287            # skips the session code's before_handler and only calls 
     288            # before_finalize (save) and on_end (close). So the session 
     289            # code has to survive calling save/close without init. 
     290            self.getPage('/restricted', self.cookies, method='POST') 
     291            self.assertErrorPage(405, "Specified method is invalid for this server.") 
     292 
     293 
     294 
     295 
    204296if __name__ == "__main__": 
    205297    setup_server() 

Hosted by WebFaction

Log in as guest/cpguest to create tickets