Package grassyknoll :: Package collection :: Module Collection
[hide private]

Source Code for Module grassyknoll.collection.Collection

  1  """basic API for collections""" 
  2  from functools import wraps 
  3  import time 
  4  import os 
  5  import threading 
  6  import socket 
  7  from grassyknoll.lib.meta import FactoryMixin 
  8   
  9  __all__=['CollectionDocument', 'CollectionResult', 'CollectionResultSet', 
 10           'CollectionIds', 'Collection', 'addMetaData'] 
 11   
12 -def addMetaData(func):
13 """decorator that adds metadata to the results of function call""" 14 15 pid=os.getpid() 16 host=socket.gethostname() 17 18 @wraps(func) 19 def timedFunc(self, *args, **kwargs): 20 _t=time.time() 21 try: 22 ret=func(self, *args, **kwargs) 23 except: 24 raise 25 else: 26 metadata=getattr(ret, 'metadata', None) 27 if isinstance(metadata, dict): 28 name=self.__class__.__name__ 29 metadata['%s_time'%name]=time.time()-_t 30 metadata['%s_pid'%name]=pid 31 metadata['%s_host'%name]=host 32 metadata['%s_thread'%name]=threading.currentThread().getName() 33 return ret
34 return timedFunc 35 36
37 -class _ListLike(list):
38 """base class for things that inherit from L{list} 39 40 @ivar metadata: metadata about the items 41 @type metadata: dict 42 """ 43 __slots__=['metadata'] 44
45 - def __init__(self, items, metadata=None):
46 """ 47 @arg results: the results 48 @type results: list of L{CollectionResult} 49 """ 50 super(_ListLike, self).__init__(items) 51 52 self.metadata=metadata if metadata is not None else {} 53 assert isinstance(self.metadata, dict)
54
55 - def __repr__(self):
56 return '<%s: %d items>' % (type(self).__name__, len(self))
57
58 - def __getstate__(self):
59 return self[:], self.metadata.copy()
60
61 - def __setstate__(self, state):
62 self[:], self.metadata = state
63
64 -class _DictLike(dict):
65 """base class for things that inherit from L{dict} 66 67 68 This class maps attribute C{name} to item C{__name__} 69 70 >>> d=_DictLike() 71 >>> d.pants=42 72 >>> d['__pants__'] 73 42 74 >>> d['__shirt__']=101 75 >>> d.shirt 76 101 77 78 """ 79 __slots__=[] 80 81 ## these attrs are read-only on dict, so we make them read-only here. This 82 ## s.b. faster/clearer than hasattr/super. You shouldn't be modifying these 83 ## methods anyway. 84 __read_only_attrs=set(dir(dict)) 85 __read_only_attrs.remove('__class__') 86
87 - def __repr__(self):
88 return '<%s: %s>' % (type(self).__name__, self.get('__id__'))
89
90 - def __getattr__(self, name):
91 try: 92 return self['__%s__'%name] 93 except KeyError: 94 raise AttributeError, name
95
96 - def __setattr__(self, name, value):
97 if name in self.__read_only_attrs: 98 raise AttributeError, "attribute %s is read-only"%name 99 elif name in self.__slots__: # NOTE __slots__ don't recursively inherit 100 object.__setattr__(self, name, value) 101 else: 102 self['__%s__'%name]=value
103
104 - def __delattr__(self, name):
105 if name in self.__read_only_attrs: 106 raise AttributeError, "attribute %s is read-only"%name 107 else: 108 try: 109 del self['__%s__'%name] 110 except KeyError: 111 raise AttributeError, name
112
113 -class CollectionDocument(_DictLike):
114 """An item that may be I{added} to a L{Collection} 115 116 the C{id} attribute / C{__id__} value should probably be 117 L{unicode}. 118 119 @ivar norman: name of the L{Norman} for this document 120 @type norman: string or None 121 """ 122 __slots__=['norman'] 123
124 - def __init__(self, fields=None, norman=None, **kwargs):
125 """ 126 @ivar fields: a dict of C{field name => field value}. Names should be 127 strings. 128 @type fields: dict 129 """ 130 super(CollectionDocument, self).__init__(fields if fields is not None else (), **kwargs) 131 132 assert norman is None or (isinstance(norman, basestring) and norman) 133 self.norman=norman
134
135 -class CollectionResult(_DictLike):
136 """An item that may be I{retrieved} from a L{Collection} 137 138 Items are a map of C{field name => field value}. Names should be 139 str. 140 """ 141 __slots__=[] 142
143 - def dump(self):
144 """ 145 @returns: a representation consisting of soleley builtin types 146 @rtype: dict 147 """ 148 return dict(self)
149
150 -class CollectionResultSet(_ListLike):
151 """a set of L{CollectionResult}s""" 152 153 __slots__=[] 154
155 - def dump(self):
156 """ 157 @returns: a representation consisting of soleley builtin types. 158 Includes: metadata=>dict & results=>list of L{CollectionResult}s 159 @rtype: dict 160 """ 161 return {'metadata':self.metadata.copy(), 162 'results': [r.dump() for r in self]}
163
164 -class CollectionIds(_ListLike):
165 """a set of ids in the collection""" 166 167 __slots__=[] 168
169 - def dump(self):
170 """ 171 @returns: a representation consisting of soleley builtin types. 172 Includes: metadata=>dict & ids=>list of strings 173 @rtype: dict 174 """ 175 return {'metadata':self.metadata.copy(), 176 'ids':self[:]}
177
178 -class Collection(FactoryMixin): #pragma: no cover
179 """A bunch of items. 180 181 Items in a collection are uniquely identified by a C{id}. 182 Implementations are responsible for maintaining this constraint. 183 184 Items are I{added} as instances of L{CollectionDocument} and I{retrieved} 185 as instances of L{CollectionResult}. 186 187 Implementations are free to interpret various methods however is 188 reasonable. They may also choose not to implement methods if they are not 189 applicable. The methods in this class all raise L{NotImplementedError}. 190 191 Implementations may also provide query methods, named like I{foo}Query. 192 These methods may take whatever keyword arguments desired. They should 193 return the results of the query as a L{CollectionResultSet}. 194 195 XXX: should we have an subclass attr describing what methods are provided? 196 """ 197
198 - def create(self, docs):
199 """ 200 XXX this should prolly return None 201 202 @arg docs: several new docs to be added to the collection 203 @type docs: list of L{CollectionDocument}s 204 205 @returns: the created ids 206 @rtype: L{CollectionIds} 207 """ 208 raise NotImplementedError
209
210 - def delete(self, ids):
211 """delete several L{CollectionDocument}s by id 212 213 XXX this should prolly return None 214 215 @arg ids: the ids to delete 216 @type ids: list of string 217 218 @returns: the deleted ids 219 @rtype: L{CollectionIds} 220 """ 221 raise NotImplementedError
222
223 - def list(self):
224 """ 225 @returns: the ids in the collection 226 @rtype: L{CollectionIds} 227 """ 228 raise NotImplementedError
229
230 - def retrieve(self, ids, fields=None):
231 """retrive several L{CollectionResult}s by id 232 233 @arg ids: the ids of the results 234 @type ids: list of string 235 236 @arg fields: a sub-L{set} of fields that should be returned. Defaults 237 to None, meaning all available fields. XXX why a set? we use tuples 238 everywhere... 239 @type fields: set 240 241 @returns: the results 242 @rtype: L{CollectionResultSet} 243 """ 244 raise NotImplementedError
245
246 - def __len__(self):
247 raise NotImplementedError
248
249 - def close(self):
250 """free resources associated with this collection""" 251 pass
252
253 - def cleanUp(self):
254 """remove the collection from disk. Useful for testing""" 255 pass
256