1
2
3 __doc__ = """
4 A store is a conceptual representation of your APP instance.
5 It is the outter envelop of the underlying entities involved.
6 APP does not define the concept of store per se but it is
7 a common way of naming and representing an APP instance.
8
9 A store is not really concerned about the meaning of members and
10 media resources however. All it cares about is 'meta-data' and 'content'.
11
12 A store needs at least one meta-data storage which will be used
13 to persist the representation of APP members.
14
15 A store can accept a second store which will be used to persist
16 media resources. If not provided the meta-data storage is then used.
17 """
18
20 - def __init__(self, storage, media_storage=None,
21 autocommit=False, enable_lock=False):
22 """
23 Store acting has a container of APP entities.
24
25 Operations on the store are only commited to the storage
26 automatically when the 'autocommit' parameter is True. This is
27 however not advised for performance issue.
28
29
30 In any case operations put each resource in distinct internal
31 lists which are processed when commit() is called.
32
33 Keyword arguments:
34 storage -- amplee.storage.* instance to persist member resources
35 media_storage -- amplee.storage.* instance to persist media resources
36 If not provided 'storage' will be used. (default=None)
37 autocommit -- True if the store should automatically call the
38 persist method of the storage (default=False)
39 enable_lock -- True if a thread lock should be used when performing
40 operations against the store (default=False)
41 """
42 self.storage = storage
43 if not media_storage:
44 media_storage = self.storage
45 self.media_storage = media_storage
46 self.dirty = False
47 self.autocommit = autocommit
48 self.locking_enabled = enable_lock
49 if enable_lock:
50 import threading
51 self.lock = threading.RLock()
52
53
54 self._pending_meta_data_to_add = {}
55 self._pending_resource_to_add = {}
56 self._pending_meta_data_to_remove = []
57 self._pending_resource_to_remove = []
58
59 - def commit(self, message=None):
60 """
61 Persist modification to the store into the storages
62
63 Keyword arguments:
64 message -- if the storage accept a string message,
65 like the subversion one (default=None)
66 """
67 try:
68 if self.locking_enabled:
69 self.lock.acquire()
70
71 for path in self._pending_meta_data_to_remove:
72 self.storage.remove_meta_data(path)
73
74 for path in self._pending_resource_to_remove:
75 self.media_storage.remove_content(path)
76
77 paths = []
78 media_paths = []
79
80 for path, infos in self._pending_meta_data_to_add.items():
81 obj = infos['obj']
82 if hasattr(obj, 'read'):
83 content = obj.read()
84 else:
85 content = obj
86 del infos['obj']
87 self.storage.put_meta_data(path, content, **infos)
88 paths.extend(self._pending_meta_data_to_remove)
89 paths.extend(self._pending_meta_data_to_add.keys())
90
91 for path, infos in self._pending_resource_to_add.items():
92 obj = infos['obj']
93 if hasattr(obj, 'read'):
94 content = obj.read()
95 else:
96 content = obj
97 del infos['obj']
98 self.media_storage.put_content(path, content, **infos)
99 media_paths.extend(self._pending_resource_to_remove)
100 media_paths.extend(self._pending_resource_to_add.keys())
101
102 if paths:
103 self.storage.persist(paths, msg=message)
104 if media_paths:
105 self.media_storage.persist(media_paths, msg=message)
106
107 self._pending_meta_data_to_remove = []
108 self._pending_resource_to_remove = []
109 self._pending_meta_data_to_add.clear()
110 self._pending_resource_to_add.clear()
111 finally:
112 if self.locking_enabled:
113 self.lock.release()
114
123
124 - def get_content_path(self, *args):
125 """
126 Returns the path of resource used by the content storage.
127 The returned value should only be seen as read-only.
128
129 Arguments depend on the underlying storage.
130 """
131 return self.media_storage.path(*args)
132
133 - def add_content(self, path, obj, **kwargs):
134 """
135 Add contents to the content storage.
136 If self.autocommit is True, the content is immediatly persisted.
137
138 Keyword arguments:
139 path -- value returned by get_content_path()
140 obj -- whatever Python object that should be persisted
141 **kwargs -- any additional parameters to provide to the storage
142 """
143 try:
144 if self.locking_enabled:
145 self.lock.acquire()
146 infos = {'obj': obj}
147 if kwargs:
148 infos.update(kwargs)
149 self._pending_resource_to_add[path] = infos
150 if self.autocommit:
151 self.commit()
152 finally:
153 if self.locking_enabled:
154 self.lock.release()
155
178
198
199 - def remove_content(self, path):
200 """
201 Remove the resource at 'path' from the content storage.
202 If self.autocommit is True, the meta-date object is immediatly persisted.
203
204 Keyword arguments:
205 path -- value returned by get_meta_data_path()
206 """
207 try:
208 if self.locking_enabled:
209 self.lock.acquire()
210 if path in self._pending_resource_to_add:
211 del self._pending_resource_to_add[path]
212 self._pending_resource_to_remove.append(path)
213 if self.autocommit:
214 self.commit()
215 finally:
216 if self.locking_enabled:
217 self.lock.release()
218
220 """
221 Returns True if the resource 'path' does exist.
222
223 Keyword arguments:
224 path -- value returned by get_meta_data_path() or get_content_path()
225 """
226 return self.storage.exists(path)
227
228 - def fetch_content(self, path):
229 """
230 Returns the content object at 'path'.
231
232 Keyword arguments:
233 path -- value returned by get_content_path()
234 """
235 return self.media_storage.get_content(path)
236
245
247 """
248 Returns a dictionary of the form {resource-name: {'path': resource-path}} filtered
249 by the given parameters.
250
251 Keyword arguments:
252 path -- Base path where to perform the lookup
253 ext -- filter by extension of the resource (default=None)
254 """
255 return self.storage.ls(path, ext=ext)
256