1
2 __docformat__ = 'epytext en'
3
4 __doc__ = """
5 Representation of a store.
6
7 A store is a conceptual representation of your APP instance.
8 It is the outter envelop of the underlying entities involved.
9 APP does not define the concept of store per se but it is
10 a common way of naming and representing an APP instance.
11
12 A store is not really concerned about the meaning of members and
13 media resources however. All it cares about is 'meta-data' and 'content'.
14
15 A store needs at least one meta-data storage which will be used
16 to persist the representation of APP members.
17
18 A store can accept a second store which will be used to persist
19 media resources. If not provided the meta-data storage is then used.
20
21 @undocumented: __doc__
22 """
23
24 __all__ = ['AtomPubStore']
25
27 - def __init__(self, storage, media_storage=None,
28 enable_lock=True):
29 """Store acting has a container of AtomPub entities.
30
31 Operations on the store are only commited to the storage
32 automatically when the C{autocommit} parameter is C{True}. This is
33 however not advised for performance issue.
34
35 In any case operations put each resource in distinct internal
36 lists which are processed when L{commit()} is called.
37
38 @type storage: L{Storage}
39 @param storage: instance to persist member resources.
40
41 @type media_storage: L{Storage}
42 @param media_storage: instance to persist media resources.
43 If not provided C{storage} will be used.
44
45 @type enable_lock: bool
46 @param enable_lock: when C{True}, a thread lock will be used when performing
47 operations against the store. This is enabled by default.
48 """
49 self.storage = storage
50 if not media_storage:
51 media_storage = self.storage
52 self.media_storage = media_storage
53 self.locking_enabled = enable_lock
54 if enable_lock:
55 import threading
56 self.lock = threading.Lock()
57
58
59 self._pending_meta_data_to_add = {}
60 self._pending_resource_to_add = {}
61 self._pending_meta_data_to_remove = []
62 self._pending_resource_to_remove = []
63
64 - def commit(self, message=None):
65 """
66 Persists modification to the store into the storages.
67
68 @type message: string
69 @param message: if the storage accepts it (like subversion),
70 message to pass to the storage.
71 """
72 try:
73 if self.locking_enabled:
74 self.lock.acquire()
75
76 for info in self._pending_meta_data_to_remove:
77 self.storage.remove_meta_data(info)
78
79 for info in self._pending_resource_to_remove:
80 self.media_storage.remove_content(info)
81
82 member_infos = []
83 media_infos = []
84
85 for info, infos in self._pending_meta_data_to_add.items():
86 content = infos.get('__obj__', None)
87 if content:
88 del infos['__obj__']
89 self.storage.put_meta_data(info, content, **infos)
90
91 member_infos.extend(self._pending_meta_data_to_remove)
92 member_infos.extend(self._pending_meta_data_to_add.keys())
93
94 for info, infos in self._pending_resource_to_add.items():
95 content = infos.get('__obj__', None)
96 if content:
97 del infos['__obj__']
98 self.media_storage.put_content(info, content, **infos)
99
100 media_infos.extend(self._pending_resource_to_remove)
101 media_infos.extend(self._pending_resource_to_add.keys())
102
103 if member_infos:
104 self.storage.persist(member_infos, msg=message)
105 if media_infos:
106 self.media_storage.persist(media_infos, msg=message)
107
108 self._pending_meta_data_to_remove = []
109 self._pending_resource_to_remove = []
110 self._pending_meta_data_to_add.clear()
111 self._pending_resource_to_add.clear()
112 finally:
113 if self.locking_enabled:
114 self.lock.release()
115
124
125 - def get_content_info(self, *args):
126 """
127 Returns the info of resource used by the content storage.
128 The returned value should only be seen as read-only.
129
130 Arguments depend on the underlying storage.
131 """
132 return self.media_storage.info(*args)
133
134 - def add_content(self, info, obj, **kwargs):
135 """
136 Adds content to the content storage.
137 If C{self.autocommit} is C{True} the content is immediatly persisted.
138
139 @type info: L{StorageResourceInfo} or subclass
140 @param info: value returned vy L{get_content_info()}
141
142 @type obj: object
143 @param obj: whatever Python object that should be persisted
144
145 @type **kwargs: dict
146 @param **kwargs: any additional parameters to provide to the storage
147 """
148 try:
149 if self.locking_enabled:
150 self.lock.acquire()
151 infos = {'__obj__': obj}
152 if kwargs:
153 infos.update(kwargs)
154 self._pending_resource_to_add[info] = infos
155 finally:
156 if self.locking_enabled:
157 self.lock.release()
158
183
201
202 - def remove_content(self, info):
203 """
204 Removes the resource at C{info} from the content storage.
205 If C{self.autocommit} is C{True} the content object is immediatly persisted.
206
207 @type info: L{StorageResourceInfo} or subclass
208 @param info: value returned by L{get_meta_data_info()}
209 """
210 try:
211 if self.locking_enabled:
212 self.lock.acquire()
213 if info in self._pending_resource_to_add:
214 del self._pending_resource_to_add[info]
215 self._pending_resource_to_remove.append(info)
216 finally:
217 if self.locking_enabled:
218 self.lock.release()
219
220 - def exists(self, info, as_media=False):
221 """
222 Returns C{True} if the resource C{info} does exist.
223
224 @type info: L{StorageResourceInfo} or subclass
225 @param info: value returned by L{get_meta_data_info()}
226 or L{get_content_info()}
227
228 @type as_media: bool
229 @param as_media: if C{False} the method checks in the member storage
230 otherwise it checks in the media storage. When both are the same it does
231 not matter but if both are different you should set this value
232 accordingly.
233
234 @rtype: bool
235 @return: C{True} if the resource exists in the storage queried
236 """
237 if as_media:
238 return self.media_storage.exists(info)
239 return self.storage.exists(info)
240
241 - def fetch_content(self, info):
242 """
243 Returns the content object at C{info}.
244
245 @type info: L{StorageResourceInfo} or subclass
246 @param info: value returned by L{get_content_info()}
247
248 @rtype: object
249 @return: the object as returned by the underlying storage
250 and representing the content.
251 """
252 return self.media_storage.get_content(info)
253
266
268 """
269 Returns a dictionary of the form C{{resource-name: StorageResourceInfo}}
270 filtered by the given parameters.
271
272 @type info: string
273 @param info: name of the collection to browse through
274
275 @type ext: string or C{NoneType}
276 @param ext: allows to filter by extension of the resource
277
278 @rtype: dict
279 @return: the resources matching the filter and the info
280 """
281 return self.storage.ls(info, ext=ext)
282
284 """
285 Yield a tuple of the form C{(resource-name, StorageResourceInfo)}
286 filtered by the given parameters.
287
288 @type info: string
289 @param info: name of the collection to browse through
290
291 @type ext: string or C{NoneType}
292 @param ext: allows to filter by extension of the resource
293
294 @rtype: tuple
295 @return: the resources matching the filter and the info
296 """
297 for item in self.storage.ils(info, ext=ext):
298 yield item
299
300 raise StopIteration()
301