shep

Multi-state key stores using bit masks for python3
git clone git://git.defalsify.org/shep.git
Log | Files | Refs | LICENSE

persist.py (7953B)


      1 # standard imports
      2 import datetime
      3 
      4 # local imports
      5 from .state import (
      6         State,
      7         split_elements,
      8         )
      9 from .error import (
     10         StateItemExists,
     11         StateLockedKey,
     12         StateExists,
     13         )
     14 
     15 
     16 class PersistedState(State):
     17     """Adapter for persisting state changes and synchronising states between memory and persisted backend.
     18 
     19     :param factory: A function capable of returning a persisted store from a single path argument.
     20     :type factory: function
     21     :param bits: Number of pure states. Passed to the superclass.
     22     :type bits: int
     23     :param logger: Logger to capture logging output, or None for no logging.
     24     :type logger: object
     25     """
     26 
     27     def __init__(self, factory, bits, logger=None, verifier=None, check_alias=True, event_callback=None, default_state=None):
     28         super(PersistedState, self).__init__(bits, logger=logger, verifier=verifier, check_alias=check_alias, event_callback=event_callback, default_state=default_state)
     29         self.__store_factory = factory
     30         self.__stores = {}
     31         self.__ensure_store(self.base_state_name)
     32 
     33 
     34     # Create state store container if missing.
     35     def __ensure_store(self, k):
     36         k = k.upper()
     37         if self.__stores.get(k) == None:
     38             self.__stores[k] = self.__store_factory(k)
     39 
     40 
     41     def put(self, key, contents=None, state=None):
     42         """Persist a key or key/content pair.
     43 
     44         See shep.state.State.put
     45         """
     46         k = self.to_name(state)
     47 
     48         self.__ensure_store(k)
     49 
     50         self.__stores[k].put(key, contents)
     51 
     52         super(PersistedState, self).put(key, state=state, contents=contents)
     53 
     54         self.register_modify(key)
     55 
     56 
     57     def set(self, key, or_state):
     58         """Persist a new state for a key or key/content.
     59 
     60         See shep.state.State.set
     61         """
     62         from_state = self.state(key)
     63         if from_state & or_state == or_state:
     64             return
     65         k_from = self.name(from_state)
     66 
     67         to_state = super(PersistedState, self).set(key, or_state)
     68         k_to = self.name(to_state)
     69         self.__ensure_store(k_to)
     70 
     71         contents = None
     72         try:
     73             contents = self.__stores[k_from].get(key)
     74             self.__stores[k_to].put(key, contents)
     75             self.__stores[k_from].remove(key)
     76         except StateLockedKey as e:
     77             super(PersistedState, self).unset(key, or_state, allow_base=True)
     78             raise e
     79        
     80         #self.sync(to_state)
     81 
     82         return to_state
     83 
     84 
     85     def unset(self, key, not_state, allow_base=False):
     86         """Persist a new state for a key or key/content.
     87 
     88         See shep.state.State.unset
     89         """
     90         from_state = self.state(key)
     91         k_from = self.name(from_state)
     92 
     93         to_state = super(PersistedState, self).unset(key, not_state, allow_base=allow_base)
     94 
     95         k_to = self.name(to_state)
     96         self.__ensure_store(k_to)
     97 
     98         contents = self.__stores[k_from].get(key)
     99         self.__stores[k_to].put(key, contents)
    100         self.__stores[k_from].remove(key)
    101 
    102         return to_state
    103 
    104 
    105     def change(self, key, bits_set, bits_unset):
    106         """Persist a new state for a key or key/content.
    107 
    108         See shep.state.State.unset
    109         """
    110         from_state = self.state(key)
    111         k_from = self.name(from_state)
    112 
    113         to_state = super(PersistedState, self).change(key, bits_set, bits_unset)
    114 
    115         k_to = self.name(to_state)
    116         self.__ensure_store(k_to)
    117 
    118         contents = self.__stores[k_from].get(key)
    119         self.__stores[k_to].put(key, contents)
    120         self.__stores[k_from].remove(key)
    121 
    122         self.register_modify(key)
    123 
    124         return to_state
    125 
    126 
    127     def move(self, key, to_state):
    128         """Persist a new state for a key or key/content.
    129 
    130         See shep.state.State.move
    131         """
    132         from_state = self.state(key)
    133         to_state = super(PersistedState, self).move(key, to_state)
    134         return self.__movestore(key, from_state, to_state)
    135 
    136 
    137     def __ensure_parts(self, state):
    138         if self.is_pure(state):
    139             return
    140         state_name = self.name(state)
    141         parts = split_elements(state_name)
    142         for k in parts:
    143             try:
    144                 self.add(k)
    145             except StateExists:
    146                 pass
    147             self.__ensure_store(k)
    148 
    149 
    150     # common procedure for safely moving a persisted resource from one state to another.
    151     def __movestore(self, key, from_state, to_state):
    152         k_from = self.name(from_state)
    153         k_to = self.name(to_state)
    154 
    155         self.__ensure_store(k_to)
    156 
    157         contents = self.__stores[k_from].get(key)
    158         self.__stores[k_to].put(key, contents)
    159         self.__stores[k_from].remove(key)
    160 
    161         self.__ensure_parts(to_state)
    162 
    163         self.register_modify(key)
    164 
    165         self.sync(to_state)
    166 
    167         return to_state
    168 
    169 
    170     def sync(self, state=None, not_state=None, ignore_auto=True):
    171         """Reload resources for a single state in memory from the persisted state store.
    172 
    173         :param state: State to load
    174         :type state: int
    175         :raises StateItemExists: A content key is already recorded with a different state in memory than in persisted store.
    176         # :todo: if sync state is none, sync all
    177         """
    178 
    179         states_numeric = []
    180         if state == None:
    181             states_numeric = list(self.all(numeric=True, ignore_auto=ignore_auto))
    182         else:
    183             states_numeric = [state]
    184        
    185         states = []
    186         for state in states_numeric:
    187             if not_state != None:
    188                 if state & not_state == 0:
    189                     states.append(self.name(state))
    190             else:
    191                 states.append(self.name(state))
    192 
    193         ks = []
    194         for k in states:
    195             ks.append(k)
    196 
    197         for k in ks:
    198             self.__ensure_store(k)
    199             for o in self.__stores[k].list():
    200                 state = self.from_name(k)
    201                 try:
    202                     super(PersistedState, self).put(o[0], state=state, contents=o[1])
    203                 except StateItemExists as e:
    204                     pass
    205 
    206 
    207     def list(self, state):
    208         """List all content keys for a particular state.
    209 
    210         This method will return from memory, and will not sync the persisted state first.
    211    
    212         See shep.state.State.list
    213         """
    214         k = self.name(state)
    215         self.__ensure_store(k)
    216         return super(PersistedState, self).list(state)
    217 
    218 
    219     def path(self, state, key=None):
    220         """Return a file path or URL pointing to the persisted state.
    221         
    222         If the key is omitted, the URL to the state item's container must be returned, and None if no such container exists.
    223          
    224         :param state: State to locate
    225         :type state: int
    226         :param key: Content key to locate
    227         :type key: str
    228         :rtype: str
    229         :returns: Locator pointng to persisted state
    230         :todo: rename to "location"
    231         """
    232         k = self.name(state)
    233         self.__ensure_store(k)
    234         return self.__stores[k].path(k=key)
    235 
    236 
    237     def next(self, key=None):
    238         """Advance and persist to the next pure state.
    239 
    240         See shep.state.State.next
    241         """
    242         from_state = self.state(key)
    243         to_state = super(PersistedState, self).next(key)
    244         return self.__movestore(key, from_state, to_state)
    245 
    246 
    247     def replace(self, key, contents):
    248         """Replace contents associated by content key.
    249 
    250         See shep.state.State.replace
    251         """
    252         state = self.state(key)
    253         k = self.name(state)
    254         r = self.__stores[k].replace(key, contents)
    255         super(PersistedState, self).replace(key, contents)
    256         return r
    257 
    258 
    259     def modified(self, key):
    260         state = self.state(key)
    261         k = self.name(state)
    262         return self.__stores[k].modified(key)
    263 
    264 
    265     def add(self, key):
    266         self.__ensure_store(key)
    267         return super(PersistedState, self).add(key)
    268 
    269 
    270     def alias(self, key, *args):
    271         self.__ensure_store(key)
    272         super(PersistedState, self).alias(key, *args)