Source code for fandango.dicts

#!/usr/bin/env python2.5
"""
#############################################################################
##
## file :       dicts.py
##
## description : see below
##
## project :     Tango Control System
##
## $Author: Sergi Rubio Manrique, srubio@cells.es $
##
## $Revision: 2008 $
##
## copyleft :    ALBA Synchrotron Controls Section, CELLS
##               Bellaterra
##               Spain
##
#############################################################################
##
## This file is part of Tango Control System
##
## Tango Control System is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as published
## by the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## Tango Control System is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
###########################################################################
@package dicts

Some extensions to python dictionary
ThreadDict: Thread safe dictionary with redefinable read/write methods and a backgroud thread for hardware update.
defaultdict_fromkey: Creates a dictionary with a default_factory function that creates new elements using key as argument.
CaselessDict: caseless dictionary
CaselessDefaultDict: a join venture between caseless and default dict from key

@deprecated
@note see in tau.core.utils.containers

by Sergi Rubio, 
srubio@cells.es, 
2008 
"""

import time,traceback
import collections
from objects import self_locked
            
try:
    import numpy
[docs] class fuzzyDict(dict): ## @todo TODO def __getitem__(self,key): try: return dict.__getitem__(self,key) except: a=numpy.arange(6)*1.1 array([ 0. , 1.1, 2.2, 3.3, 4.4, 5.5]) #The keys of the dictionary numpy.abs(a-key).argmin() #returns the index of the nearest key pass
except: pass
[docs]class ThreadDict(dict): ''' Thread safe dictionary with redefinable read/write methods and a backgroud thread for hardware update. All methods are thread-safe using @self_lock decorator. NOTE: any method decorated in this way CANNOT call other decorated methods! All values of the dictionary will be automatically updated in a separate Thread using read_method provided. Any value overwritten in the dict should launch the write_method. Briefing: a[2] equals to a[2]=read_method(2) a[2]=1 equals to a[2]=write_method(2,1) threadkeys(): returns the list of keys that are being automatically updated in a background thread append(key,value=None,period=3000): adds a new key to the dictionary and to the list of keys to be updated __setitem__(key,value):tdict[key]=value will also add a new key to the dictionary, but this key value will not be automatically updated. timewait is a pause inserted between readings If read_method defined: If threaded: the Thread inserts data in the dictionary, __getitem__ retrieves this data else: __getitem__ directly executes read_method else: __getitem__ as a normal thread-safe dictionary If write_method defined: any __setitem__ call executes the write_method (dictionary is no affected) else: __setitem__ as a normal thread-safe dictionary @deprecated now in tau.core.utils.containers ''' def __init__(self,other=None,read_method=None,write_method=None,timewait=0.1,threaded=True,trace=False): self.read_method = read_method self.write_method = write_method self.timewait = timewait self.threaded = threaded self._threadkeys = [] self._periods = {} self._updates = {} self.trace = trace self.last_update = 0 self._last_read = '' self.last_cycle_start = 0 self.parent = type(self).mro()[1] #equals to self.__class__.__base__ or type(self).__bases__[0] if other: dict.update(self,other)
[docs] def tracer(self,text,level=0): #if isinstance(self.trace,int) and level<self.trace: print text #@self_locked
[docs] def start(self): if not self.threaded: print 'ThreadDict.start(): This Dict has no Thread!' return if hasattr(self,'_Thread') and self._Thread and self._Thread.isAlive(): print 'ThreadDict.start(): ThreadDict.stop() must be executed first!' return print 'In ThreadDict.start(), keys are: %s' % self.threadkeys() import threading self.event = threading.Event() self.event.clear() self._Thread = threading.Thread(target=self.run) self._Thread.setDaemon(True) self._Thread.start() self.tracer('ThreadDict started!') #@self_locked
[docs] def stop(self): print 'Stopping ThreadDict ...' if self.threaded and hasattr(self,'event'): self.event.set() if hasattr(self,'_Thread'): self._Thread.join() print 'ThreadDict Stopped'
[docs] def alive(self): if not hasattr(self,'_Thread') or not self._Thread: return False else: return self._Thread.isAlive()
def __del__(self): self.stop()
[docs] def run(self): self.tracer('In ThreadDict.run()') while not self.event.isSet(): keys = sorted(self.threadkeys()) if self._last_read and self._last_read!=keys[-1]: #Thread stopped before finishing the cycle! keys = keys[keys.index(self._last_read)+1:] for k in keys: if self.trace: self.tracer('ThreadDict::run(): updating %s'%str(k)) try: if (time.time()-self._updates.get(k,0))>self._periods.get(k,0): #if period=0 or if not updated the condition applies self.__getitem__(k,hw=True) except Exception,e: if self.trace: print('!!! ThreadDict Exception !!!'+'\n'+'%s ...'%str(traceback.format_exc())[:1000]) self.__setitem__(k,e,hw=False) finally: self._last_read = k if self.event.isSet(): break else: self.event.wait(self.timewait) if self.event.isSet(): break timewait = self.get_timewait() self.event.wait(timewait) self.tracer('End of ThreadDict')
@self_locked
[docs] def get_last_update(self): return self.last_update
@self_locked
[docs] def set_last_update(self,value): self.last_update=value
@self_locked
[docs] def get_last_cycle_start(self): return self.last_cycle_start
@self_locked
[docs] def set_last_cycle_start(self,value): self.last_cycle_start=value
@self_locked
[docs] def get_timewait(self): return self.timewait
@self_locked
[docs] def set_timewait(self,value): self.timewait=value
@self_locked
[docs] def append(self,key,value=None,period=0): #period=0 means that attribute will be updated always if not dict.has_key(self,key): self.parent.__setitem__(self,key,value) if key not in self._threadkeys: self._threadkeys.append(key) self._periods[key] = period if self.trace: self.tracer('ThreadDict.append(%s,%s,%s)'%(key,value,period))
@self_locked
[docs] def threadkeys(self): return self._threadkeys[:]
@self_locked def __locked_setitem__(self,key,value): return dict.__setitem__(self,key,value) @self_locked def __locked_getitem__(self,key): return dict.__getitem__(self,key) def __getitem__(self,key,hw=False): ''' This method launches a read_method execution if there's no thread on charge of doing that or if the hw flag is set to True. ''' if self.trace: print 'In ThreadDict.__getitem__(%s,%s)'%(key,hw) if (hw or not self.threaded): #HW ACCESS MUST NOT BE DONE WITHOUT ASKING EXPLICITLY! (Use __setitem__(k,None) instead) self._updates[key] = time.time() if self.read_method: value = self.read_method(key) self.__locked_setitem__(key,value) self.set_last_update(self._updates[key]) return self.__locked_getitem__(key) def __setitem__(self,key,value,hw=True): ''' This method launches a write_method execution if the hw flag is not explicitly set to False. ''' if self.trace: print 'In ThreadDict.__setitem__(%s,...,%s)'%(key,hw) if hw and self.write_method: #It implies that a key will not be added here to read thread! nvalue = self.write_method(*[key,value]) self.__locked_setitem__(key,nvalue) else: self.__locked_setitem__(key,value) self.set_last_update(time.time()) @self_locked
[docs] def get(self,key,default=None): if not self.threaded and self.read_method: dict.__setitem__(self,key,self.read_method(key)) self.last_update = time.time() if default is False: return dict.get(self,key) else: return dict.get(self,key,default)
@self_locked def __delitem__(self, key): return dict.__delitem__(self, key) @self_locked def __contains__(self, key): return dict.__contains__(self, key) @self_locked def __iter__(self): return dict.__iter__(self) @self_locked
[docs] def pop(self, key): return dict.pop(self, key)
@self_locked def __str__(self): return "{" +",".join(["'"+str(k)+"'"+":"+"'"+str(v)+"'" for k,v in zip(dict.keys(self),dict.values(self))])+ "}" @self_locked def __repr__(self): return "{\n" +"\n,".join(["'"+str(k)+"'"+":"+"'"+str(v)+"'" for k,v in zip(dict.keys(self),dict.values(self))])+ "\n}"
[docs]class defaultdict_fromkey(collections.defaultdict): """ Creates a dictionary with a default_factory function that creates new elements using key as argument. Usage : new_dict = defaultdict_fromkey(method); where method like (lambda key: return new_obj(key)) Each time that new_dict[key] is called with a key that doesn't exist, method(key) is used to create the value Copied from PyAlarm device server @deprecated now in tau.core.utils.containers """ def __missing__(self, key): if self.default_factory is None: raise KeyError(key) try: self[key] = value = self.default_factory(key) except Exception,e: try: self[key] = value = self.default_factory() except: raise e return value
[docs]class CaselessList(list): """ Python list with caseless index,contains,remove methods """ def _lowstreq(self,a,b): return (a==b) or (hasattr(a,'lower') and hasattr(b,'lower') and a.lower()==b.lower()) def __contains__(self,item): for k in self: if self._lowstreq(k,item): return True return False
[docs] def index(self,item): for i,k in enumerate(self): if self._lowstreq(k,item): return i return None
def __contains__(self,item): return self.index(item) is not None
[docs] def remove(self,item): list.pop(self,self.index(item))
[docs]class CaselessDict(dict): """ Dictionary with caseless key resolution Copied from tau.core.utils.CaselessDict @deprecated now in tau.core.utils.containers """ def __init__(self, other=None): if other: try: # Doesn't do keyword args if hasattr(other,'items'): for k,v in other.items(): dict.__setitem__(self, k.lower() if hasattr(k,'lower') else k, v) else: for k,v in other: dict.__setitem__(self, k.lower() if hasattr(k,'lower') else k, v) except Exception,e: print 'CaselessDict(%s): failed!\n%s'%(type(other),e) raise e def __getitem__(self, key): key = str(key) if dict.has_key(self,key): return dict.__getitem__(self,key) return dict.__getitem__(self, key.lower() if hasattr(key,'lower') else key) def __setitem__(self, key, value): key = str(key) dict.__setitem__(self, key.lower() if hasattr(key,'lower') else key, value) def __contains__(self, key): key = str(key) return dict.__contains__(self, key.lower() if hasattr(key,'lower') else key)
[docs] def has_key(self, key): key = str(key) return dict.has_key(self, key.lower() if hasattr(key,'lower') else key)
[docs] def get(self, key, def_val=None): key = str(key) return dict.get(self, key.lower() if hasattr(key,'lower') else key, def_val)
[docs] def setdefault(self, key, def_val=None): key = str(key) return dict.setdefault(self, key.lower() if hasattr(key,'lower') else key, def_val)
[docs] def update(self, other): for k,v in other.items(): k = str(k) dict.__setitem__(self, k.lower() if hasattr(k,'lower') else k, v)
[docs] def fromkeys(self, iterable, value=None): d = CaselessDict() for k in iterable: k = str(k) dict.__setitem__(d, k.lower() if hasattr(k,'lower') else k, value) return d
[docs] def pop(self, key, def_val=None): key = str(key) return dict.pop(self, key.lower() if hasattr(key,'lower') else key, def_val)
def __delitem__(self, k): k = str(k) dict.__delitem__(self, k.lower() if hasattr(k,'lower') else k)
[docs]class CaselessDefaultDict(defaultdict_fromkey,CaselessDict): """ a join venture between caseless and defaultdict_fromkey This class merges the two previous ones. This declaration equals to: CaselessDefaultDict = type('CaselessDefaultType',(CaselessDict,defaultdict_fromkey),{}) """ def __getitem__(self, key): key = str(key) return defaultdict_fromkey.__getitem__(self, key.lower()) pass
[docs]class DefaultThreadDict(defaultdict_fromkey,ThreadDict): """ a join venture between thread and default dict This class merges the two previous ones. @deprecated now in tau.core.utils.containers @todo This two classes are not yet well integrated ... the way a new key is added to the dict must be rewritten explicitly. """ def __init__(self,other=None,default_factory=None,read_method=None,write_method=None,timewait=0.1,threaded=True): defaultdict_fromkey.__init__(self,default_factory) ThreadDict.__init__(self,other,read_method,write_method,timewait,threaded) pass ##################################################################################################
[docs]class SortedDict(dict): """ This class implements a dictionary that returns keys in the same order they were inserted. """ def __init__(self,other=None): dict.__init__(self) self._keys = [] if other is not None: self.update(other) return
[docs] def sort(self,key): """ This method modifies the sorting of the dictionary overriding the existing sort key. :param key: it can be a sequence containing all the keys already existing in the dictionary or a callable providing a sorting key algorithm. """ import operator if operator.isCallable(key): self._keys = sorted(self._keys,key=key) else: for k in self._keys: if k not in self._keys: raise KeyError(k) self._keys = list(key) return self._keys[:]
def __setitem__(self,k,v): if k not in self._keys: self._keys.append(k) dict.__setitem__(self,k,v)
[docs] def update(self,other): if hasattr(other,'items'): other = other.items() for k,v in other: self.__setitem__(k,v)
@staticmethod
[docs] def fromkeys(S,v=None): return SortedDict((s,v) for s in S)
[docs] def pop(self,k,d=None): """Removes key and returns its (self[key] or d or None)""" if k not in self: return d self._keys.remove(k) return dict.pop(self,k)
[docs] def popitem(self): """Removes and returns last key,value pair""" k = self._keys[-1] v = self[k] self.pop(k) return k,v
[docs] def clear(self): self._keys = [] return dict.clear(self)
[docs] def keys(self): return self._keys[:]
[docs] def values(self): return [self[k] for k in self._keys]
[docs] def items(self): return [(k,self[k]) for k in self._keys]
def __iter__(self): return (i for i in self._keys)
[docs] def iteritems(self): return ((k,self[k]) for k in self._keys)
[docs] def iterkeys(self): return (i for i in self._keys)
[docs] def itervalues(self): return (self[k] for k in self._keys)
[docs]class CaselessSortedDict(SortedDict,CaselessDict): """ This class implements a dictionary that returns keys in the same order they were inserted. """ def __init__(self,other=None): dict.__init__(self) self._keys = [] if other is not None: CaselessDict.__init__(self,other) return @staticmethod
[docs] def caseless(key): return str(key).lower()
[docs] def sort(self,key): """ This method modifies the sorting of the dictionary overriding the existing sort key. :param key: it can be a sequence containing all the keys already existing in the dictionary or a callable providing a sorting key algorithm. """ import operator if operator.isCallable(key): self._keys = sorted(self._keys,key=key) else: for k in self._keys: if k not in self._keys: raise KeyError(k) self._keys = list(key) return self._keys[:]
def __setitem__(self,k,v): k = self.caseless(k) if k not in self._keys: self._keys.append(k) dict.__setitem__(self,k,v)
[docs] def update(self,other): if hasattr(other,'items'): other = other.items() for k,v in other: self.__setitem__(k,v)
@staticmethod
[docs] def fromkeys(S,v=None): S = map(self.caseless,S) return SortedDict((s,v) for s in S)
[docs] def pop(self,k,d=None): """Removes key and returns its (self[key] or d or None)""" k = self.caseless(k) if k not in self: return d self._keys.remove(k) return dict.pop(self,k) ##################################################################################################
from collections import defaultdict
[docs]def reversedict(dct,key=None,default=None): #it just exchanges keys/values in a dictionary if key is None: return dict((v,k) for k,v in dct.items()) for k,v in dct.items(): if v == key: return k return default
[docs]class ReversibleDict(object):#dict): """ Dictionary that searches in both directions within a list of tuples acting like a nested dictionary. * Negative indexes and levels are used to reverse the direction of key,values retrieval (i>=0: left-to-right; i<0: rigth-to-left) * Both directions are not exclusive!!! ... search direction is managed by the tuple index sign (+/-) * The _level is always positive, and could relate to both sides of the tuple [+level,-(level-1)][i<0] * When no sublevel is entered, keys() returns both sides of each tuple * Due to this behaviour may be: len(self) < len(self.items()) * It inherits from dictionary just to be recognized by isinstance! * keys()[x] != values()[x] ... use .items() or iteritems() when you need to match it * There's no check for duplicate keys, if there's a duplicate key then the first match is returned :TODO : A nice search algorithm in sorted tables would speed up retrieving a lot dict methods implemeted: __contains__,__delitem__,__getitem__,__iter__,__len__,__repr__,__setitem__,__str__ get,has_key,items,iteritems,iterkeys,itervalues,keys,pop,values NotImplemented: clear,copy,fromkeys,setdefault,update,popitem """ DEFAULT = None SORTKEY = lambda s: ord(s[0])%3 def __init__(self, table = None, subset = None, index = None, level = 0, sorted = False, trace = False): """ :param table: The table must be a list of tuples having all the same length and must be sorted! :param subset: it will be a list of lines to be searched, the rest will be ignored; used for nesting :param index: the index in tuples to be searched, if None both begin (0) and end (-1) are searched :param level: always a positive value """ table,subset,index = table or [], subset or (), index or [] #There are strange problems if persistent types are used as __init__ arguments!?! if isinstance(table,ReversibleDict): table = table.data() elif hasattr(table,'items'): table = [t for t in table.items()] #Updating from a dictionary #Attributes persistent self._depth = table and len(table[0]) or 0 self._table = table or [] self._sorted = index or sorted self.trace(trace) if sorted and not index: self._index = self.sort(True) else: self._index = index #Attributes that changed in sub-instances self._level = level #It's always positive! self._subset = subset
[docs] def update(self, other): if not other: return if hasattr(other,'iteritems'): [self.set(*t) for t in other.iteritems()] else: [self.set(*t) for t in other] #def __new__(klass,*args): #return klass(*args)
def __len__(self): """ It returns the number of raw lines, not keys or values """ return len(self._subset or self._table)
[docs] def range(self,full=True): """ And appropiated iterator to check all lines in the table """ return self._subset or range(full and -len(self._table) or 0,len(self._table))
def __repr__(self): return '{'+',\n'.join('\t%s%s:%s'%(k,'',v) for k,v in self.iteritems())+'}' def __str__(self): return self.__repr__()
[docs] def trace(self,val=None): if val in (True,False): self._trace = val return self._trace
[docs] def sort(self,update=False): """ creates indexes of the keys at each level using the self.SORTKEY method for each level there's a dictionary {SORTKEY(s):[lines where SORTKEY(key)==SORTKEY(s)]} SORTKEY is used instead of key to improve retrieve times. """ if update: self._index = {} for l in range(self._depth): self._index[l] = self._index[-(l+1)] =defaultdict(set) for i in self.range(): self._index[l][self.SORTKEY(self._table[i][l])].add(i) self._sorted = True return self._index
[docs] def prune(self,filters=[]): """ This method should do a cleanup of all repeated key-chains in both directions (or delete anything matched by filters) """ raise Exception,'NotImplemented'
[docs] def depth(self): return self._depth
[docs] def data(self): return self._table
[docs] def size(self): return len(self._table)
[docs] def sorted(self): return self._sorted
[docs] def nextlevel(self,i=None): if i is not None and i<0: return self.level(i)-1 else: return self._level+1 #(1 if direction>=0 else -1) #Level always positive
[docs] def prevlevel(self,i=None): if i is not None and i<0: return self.level(i)+1 else: return self._level-1 #(1 if directionl>=0 else -1) #Level always positive #def iterlevel(self): #""" Returns (level,) or if level is None then it will try left-to-right and right-to-left first levels """ #return ((0,-1) if self._level is None else (self._level,))
[docs] def level(self,i = None): """ The direction depends on the sign of the tuple index """ if i is not None and i<0: return -(self._level+1) else: return self._level
[docs] def last(self): return self.nextlevel()==self.depth()-1
def __iter__(self): """ It returns distinct keys """ previous = set() for i in self.range(): k = self._table[i][self.level(i)] if k in previous: continue previous.add(k) yield k pass
[docs] def iterkeys(self): return self.__iter__()
[docs] def keys(self): return set([k for k in self.__iter__()])
[docs] def keysets(self,key=None): """ It returns a dictionary of {key:[index set]} at actual level. The sign +/- of the index refers to left-to-right/right-to-left order The level/direcition is not initialized until a key is found by this method. """ keys = defaultdict(set) if key is None: #Don't check it at every loop! for i in self.range(): [keys[self._table[i][self.level(i)]].add(i) for i in self.range()] else: #Searching for a given key for i in self.range(): j = self.level(i) if self._table[i][j]!=key: continue keys[self._table[i][j]].add(i) return keys
[docs] def itervalues(self): """ It returns values for actual keys """ if self.level()==self.depth()-1: for i in self.range(): yield self._table[i][self.level(i)] else: for ks in self.keysets().values(): yield ReversibleDict(table=self._table,index=self._index,subset=ks,level=self.nextlevel(),trace=self._trace) pass
[docs] def values(self): return [v for v in self.itervalues()]
[docs] def iteritems(self): """ returns key,value pairs at self.level() """ if self.nextlevel()==self.depth()-1: for i in self.range(): yield self._table[i][self.level(i)],self._table[i][self.nextlevel(i)] #Last key,value pair else: for k,ks in self.keysets().items(): yield k,ReversibleDict(table=self._table,index=self._index,subset=ks,level=self.nextlevel(),trace=self._trace) pass
[docs] def items(self): return [t for t in self.iteritems()]
[docs] def line(self,i): """ It returns an arranged tuple slice of the selected index of the table :param i: it must be the RAW (positive or negative) index of the line """ if i>self.size(): i = i-2*self.size() #converting to a negative index rightleft = i<0 #left-to-right or right-to-left order t = self._table[i] if not self._level: return tuple(reversed(t)) if rightleft else t else: level = self.level(i) if rightleft: return (t[level],)+tuple(reversed(t[-self._depth:level])) else: return t[level:self._depth]
[docs] def iterlines(self): """ Instead of returning key,value pairs it returns a tuple with self.depth() values """ for i in self.range(full=False): yield self.line(i)
[docs] def lines(self): """ Instead of returning key,value pairs it returns a tuple with self.depth() values """ return [i for i in self.iterlines()]
[docs] def has_key(self,key): """ Implemented separately of __getitem__ to be more efficient. """ for i in self.range(full=True): if self._table[i][self.level(i)]==key: return True return False
def __contains__(self, key): return self.has_key(key)
[docs] def get(self, *keys): """Arguments are keys separated by commas, it is a recursive call to __getitem__ """ if len(keys)>self._depth: return self.DEFAULT try: v = self[keys[0]] if isinstance(v,ReversibleDict): return v.get(*keys[1:]) else: return v except: return self.DEFAULT
def __getitem__(self, key,raw=False): """ It scans the dict table in both directions, returning value or a ReversibleDict instance """ ks = self.keysets(key=key) if not ks.get(key,[]): raise Exception,'KeyNotFound(%s)'%str(key) if self.nextlevel() == self.depth()-1: i = ks[key].pop() return self._table[i][self.nextlevel(i)] #Returning a first/last element of tuple else: return ReversibleDict(table=self._table,subset=ks[key],index=self._index,level=self.nextlevel(),trace=self._trace) #Returning a ReversibleDict with the subset of tuples that matched previous searches.
[docs] def set(self, *keys): """ Arguments are values separated by commas, it is a recursive call to __setitem__ """ if len(keys)==1 and any(isinstance(keys[0],t) for t in (list,tuple,set)): keys = tuple(keys[0]) self[keys[0]] = keys[1:]
def __setitem__(self, key, value): """ It may accept two ways: * Entering a tuple of length = depth-level * Entering directly the last value (level = depth-1); it will override existing ones!!! * Order will depend of the previously inserted tuples for the same key * If key doesn't exist it will be added as a left-to-right tuple """ #print 'In ReversibleDict.__setitem__(%s,%s), level is %s'%(key,value,self.level()) #Checking all the conditions for the arguments if not hasattr(value,'__iter__') or isinstance(value,str): value = (value,) elif not isinstance(value,tuple): value = tuple(value) if not len(value): raise Exception,'EmptyTuple!' elif self._table and (len(value))!=self.depth()-self.level()-1: raise Exception,'WrongTupleSize(%s!=%s)' % (len(value),self.depth()-self.level()-1) if self._trace: print 'In ReversibleDict[%s] = %s' % (key,value) #Creating a new table if the dict was empty if not self._table: self._table.append((key,)+value) self._depth = 1+len(value) if self._trace: print 'Creating a table ...' #Check if the key already exist elif self.has_key(key): if self._trace: print 'Updating a key ...' i = iter(self.keysets(key)[key]).next() if self.last(): #If it's a final leaf the value is overriden self._table[i] = (self._table[i][:self.nextlevel(i)]+value) if i>=0 else (value+self._table[i][self.level(i):]) else: #If not the tuple is passed to the next dictionary return self[key].__setitem__(value[0],value[1:]) #if i>=0: #else: return self[key].__setitem__(value[-1],value[:-1]) #The key exists but in reversed order (only for root dictionary) elif self.level() in (0,-1) and self.has_key(value[-1]): if self._trace: print 'Inserting reversed key ...' self[value[-1]]=tuple(reversed(value[:-1]))+(key,) #Adding new keys elif self.level(): i = iter(self._subset).next() #print 'adding new key %s at level %s, i = %s' % (key,self.level(i),i) if i>=0: self._table.append(self._table[i][:self.level(i)]+(key,)+value) else: self._table.append(tuple(reversed(value))+(key,)+ self._table[i][self.level(i)+1:] ) #+1 because slices are not symmetric! if self._trace: print 'Adding a new key ...' else: if self._trace: print 'Adding a new line ...' self._table.append((key,)+value) def __del__(self): del self._table def __delitem__(self, k): raise Exception,'NotImplemented!'
[docs] def setdefault(self, key, def_val=None): raise Exception,'NotImplemented!'
[docs] def fromkeys(self, iterable, value=None): raise Exception,'NotImplemented!'
[docs] def pop(self, key, def_val=None): raise Exception,'NotImplemented!' ##################################################################################################
""" enumeration.py: borrowed from tcoutinho@cells.es tau.core.utils library Enumeration module. In C, enums allow you to declare a bunch of constants with unique values, without necessarily specifying the actual values (except in cases where you need to). Python has an accepted idiom that's fine for very small numbers of constants (A, B, C, D = range(4)) but it doesn't scale well to large numbers, and it doesn't allow you to specify values for some constants while leaving others unspecified. This approach does those things, while verifying that all values (specified and unspecified) are unique. Enum values then are attributes of an Enumeration class (Insect.BEETLE, Car.PASSAT, etc.). """ import types
[docs]class EnumException(Exception): pass
[docs]class Enumeration: """ @DEPRECATED: Use python Enum type instead! Enumeration class intended to provide the 'enum' feature present in many programming languages. Usage: car = ThingWithType(fruit.Lemon) print whatkind(fruit.type, Lemon) bug = ThingWithType(Insect.BEETLE) print whatkind(bug.type, Insect) Notice that car's and bug's attributes don't include any of the enum machinery, because that machinery is all CLASS attributes and not INSTANCE attributes. So you can generate thousands of cars and bugs with reckless abandon, never worrying that time or memory will be wasted on redundant copies of the enum stuff. print car.__dict__ print bug.__dict__ pprint.pprint(Cars.__dict__) pprint.pprint(Insect.__dict__) """ def __init__(self, name, enumList): self.__doc__ = name lookup = { } reverseLookup = { } uniqueNames = [ ] self._uniqueValues = uniqueValues = [ ] self._uniqueId = 0 for x in enumList: if type(x) == types.TupleType: x, i = x if type(x) != types.StringType: raise EnumException("enum name is not a string: %s" % str(x)) if type(i) != types.IntType: raise EnumException("enum value is not an integer: %s" % str(i)) if x in uniqueNames: raise EnumException("enum name is not unique: " % str(x)) if i in uniqueValues: raise EnumException("enum value is not unique for " % str(x)) uniqueNames.append(x) uniqueValues.append(i) lookup[x] = i reverseLookup[i] = x for x in enumList: if type(x) != types.TupleType: if type(x) != types.StringType: raise EnumException("enum name is not a string: " % str(x)) if x in uniqueNames: raise EnumException("enum name is not unique: " % str(x)) uniqueNames.append(x) i = self.generateUniqueId() uniqueValues.append(i) lookup[x] = i reverseLookup[i] = x self.lookup = lookup self.reverseLookup = reverseLookup
[docs] def generateUniqueId(self): while self._uniqueId in self._uniqueValues: self._uniqueId += 1 n = self._uniqueId self._uniqueId += 1 return n
def __getitem__(self, i): if type(i) == types.IntType: return self.whatis(i) elif type(i) == types.StringType: return self.lookup[i] def __getattr__(self, attr): if not self.lookup.has_key(attr): raise AttributeError return self.lookup[attr]
[docs] def whatis(self, value): return self.reverseLookup[value]
from . import doc __doc__ = doc.get_fn_autodoc(__name__,vars())