<s> import hashlib <EOL> try : <EOL> import cPickle as pickle <EOL> except : <EOL> import pickle <EOL> from functools import partial <EOL> from itertools import imap <EOL> from . backend import Backend <EOL> from . . query import Query <EOL> class RedisBackend ( Backend ) : <EOL> def __init__ ( self , redis , name ) : <EOL> self . _r = redis <EOL> self . _name = name <EOL> make_key = partial ( lambda * parts : '<STR_LIT::>' . join ( parts ) , self . _name ) <EOL> self . tag_key = partial ( make_key , '<STR_LIT>' ) <EOL> self . result_key = partial ( make_key , '<STR_LIT:result>' ) <EOL> self . items_key = make_key ( '<STR_LIT>' ) <EOL> self . tags_key = make_key ( '<STR_LIT>' ) <EOL> self . cache_key = make_key ( '<STR_LIT>' ) <EOL> @ property <EOL> def redis ( self ) : <EOL> return self . _r <EOL> @ property <EOL> def name ( self ) : <EOL> return self . _name <EOL> def encode ( self , data ) : <EOL> return pickle . dumps ( data ) <EOL> def decode ( self , data ) : <EOL> return pickle . loads ( data ) <EOL> def tag_items ( self , tag , * items ) : <EOL> items = list ( set ( self . encode ( item ) for item in items ) - self . _r . smembers ( self . tag_key ( tag ) ) ) <EOL> if len ( items ) == <NUM_LIT:0> : <EOL> return [ ] <EOL> with self . _r . pipeline ( ) as pipe : <EOL> pipe . zincrby ( self . tags_key , tag , len ( items ) ) <EOL> pipe . sadd ( self . tag_key ( tag ) , * items ) <EOL> for item in items : <EOL> pipe . zincrby ( self . items_key , item , <NUM_LIT:1> ) <EOL> pipe . execute ( ) <EOL> return map ( self . decode , items ) <EOL> def untag_items ( self , tag , * items ) : <EOL> items = list ( set ( self . encode ( item ) for item in items ) & self . _r . smembers ( self . tag_key ( tag ) ) ) <EOL> if len ( items ) == <NUM_LIT:0> : <EOL> return [ ] <EOL> with self . _r . pipeline ( ) as pipe : <EOL> pipe . zincrby ( self . tags_key , tag , - len ( items ) ) <EOL> pipe . srem ( self . tag_key ( tag ) , * items ) <EOL> for item in items : <EOL> pipe . zincrby ( self . items_key , item , - <NUM_LIT:1> ) <EOL> pipe . execute ( ) <EOL> self . _clear_cache ( ) <EOL> return map ( self . decode , items ) <EOL> def remove_items ( self , * items ) : <EOL> removed = [ ] <EOL> if not len ( items ) : <EOL> return removed <EOL> for item in imap ( self . encode , items ) : <EOL> score = self . _r . zscore ( self . items_key , item ) <EOL> if not score : <EOL> continue <EOL> for tag in self . all_tags ( ) : <EOL> srem_ok = self . _r . srem ( self . tag_key ( tag ) , item ) <EOL> if not srem_ok : <EOL> continue <EOL> with self . _r . pipeline ( ) as pipe : <EOL> pipe . zincrby ( self . tags_key , tag , - <NUM_LIT:1> ) <EOL> pipe . zincrby ( self . items_key , item , - <NUM_LIT:1> ) <EOL> pipe . execute ( ) <EOL> removed . append ( self . decode ( item ) ) <EOL> self . _clear_cache ( ) <EOL> return removed <EOL> def all_tags ( self ) : <EOL> return list ( self . _r . zrangebyscore ( self . tags_key , <NUM_LIT:1> , '<STR_LIT>' ) ) <EOL> def all_items ( self ) : <EOL> return map ( self . decode , self . _r . zrangebyscore ( self . items_key , <NUM_LIT:1> , '<STR_LIT>' ) ) <EOL> def query ( self , q ) : <EOL> if isinstance ( q , Query ) : <EOL> fn , args = q . freeze ( ) <EOL> return self . _raw_query ( fn , args ) <EOL> elif isinstance ( q , tuple ) : <EOL> fn , args = q <EOL> return self . _raw_query ( fn , args ) <EOL> else : <EOL> raise TypeError ( "<STR_LIT>" % q ) <EOL> def _raw_query ( self , fn , args ) : <EOL> "<STR_LIT>" <EOL> h = hashlib . sha1 ( pickle . dumps ( ( fn , args ) ) ) <EOL> keyname = self . result_key ( h . hexdigest ( ) ) <EOL> if self . _r . exists ( keyname ) : <EOL> return ( keyname , map ( self . decode , self . _r . smembers ( keyname ) ) ) <EOL> if fn == '<STR_LIT>' : <EOL> if len ( args ) == <NUM_LIT:1> : <EOL> key = self . tag_key ( args [ <NUM_LIT:0> ] ) <EOL> return ( key , map ( self . decode , self . _r . smembers ( key ) ) ) <EOL> else : <EOL> keys = [ self . tag_key ( k ) for k in args ] <EOL> self . _r . sunionstore ( keyname , * keys ) <EOL> self . _r . sadd ( self . cache_key , keyname ) <EOL> return ( keyname , map ( self . decode , self . _r . smembers ( keyname ) ) ) <EOL> elif fn == '<STR_LIT>' : <EOL> interkeys = [ key for key , _ in [ self . _raw_query ( * a ) for a in args ] ] <EOL> self . _r . sinterstore ( keyname , * interkeys ) <EOL> self . _r . sadd ( self . cache_key , keyname ) <EOL> return ( keyname , map ( self . decode , self . _r . smembers ( keyname ) ) ) <EOL> elif fn == '<STR_LIT>' : <EOL> interkeys = [ key for key , _ in [ self . _raw_query ( * a ) for a in args ] ] <EOL> self . _r . sunionstore ( keyname , * interkeys ) <EOL> self . _r . sadd ( self . cache_key , keyname ) <EOL> return ( keyname , map ( self . decode , self . _r . smembers ( keyname ) ) ) <EOL> elif fn == '<STR_LIT>' : <EOL> interkeys = [ key for key , _ in [ self . _raw_query ( * a ) for a in args ] ] <EOL> tags = self . all_tags ( ) <EOL> scratchpad_key = self . result_key ( '<STR_LIT:_>' ) <EOL> self . _r . sunionstore ( scratchpad_key , * map ( self . tag_key , tags ) ) <EOL> self . _r . sdiffstore ( keyname , scratchpad_key , * interkeys ) <EOL> self . _r . sadd ( self . cache_key , keyname ) <EOL> return ( keyname , map ( self . decode , self . _r . smembers ( keyname ) ) ) <EOL> else : <EOL> raise ValueError ( "<STR_LIT>" % fn ) <EOL> def _clear_cache ( self ) : <EOL> cached_keys = self . _r . smembers ( self . cache_key ) <EOL> if len ( cached_keys ) > <NUM_LIT:0> : <EOL> self . _r . delete ( * cached_keys ) <EOL> return True <EOL> def empty ( self ) : <EOL> self . _r . flushdb ( ) <EOL> def __str__ ( self ) : <EOL> return unicode ( self ) . encode ( '<STR_LIT:utf-8>' ) <EOL> def __unicode__ ( self ) : <EOL> return u"<STR_LIT>" % ( self . __class__ . __name__ , self . redis , self . name ) </s>
