1 '''
2 Defines a class responsible for managing the saving and loading of settings
3 from disk.
4
5 @var PROFILES_PATH: Path to all persisted setting for user profiles
6 @type PROFILES_PATH: string
7
8 @author: Peter Parente
9 @organization: IBM Corporation
10 @copyright: Copyright (c) 2005, 2007 IBM Corporation
11
12 @author: Frank Zenker
13 @author: Ramona Bunk
14 @organization: IT Science Center Ruegen gGmbH, Germany
15 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
16
17 @license: I{The BSD License}
18 All rights reserved. This program and the accompanying materials are made
19 available under the terms of the BSD license which accompanies
20 this distribution, and is available at
21 U{http://www.opensource.org/licenses/bsd-license.php}
22 '''
23 from SUEConstants import BUILTIN_PROFILES, HOME_USER, initUserPath
24 from Tools.i18n import _
25 import shelve, os, shutil, whichdb
26
27
28 PROFILES_PATH = os.path.join(HOME_USER, 'profiles')
29
30 if initUserPath(PROFILES_PATH):
31 print 'created ~/.sue/profiles'
32
34 '''
35 Manages the persistence of Python objects.
36
37 Has methods for loading and saving state under a given name in the profile
38 under which SUE is running or the profile named when the manager is
39 instantiated. Maintains an in-memory cache of some objects loaded from disk
40 to ensure multiple calls to L{load} return the same reference.
41
42 @ivar name: Name of the profile
43 @type name: string
44 @ivar profile: Path to the profile database
45 @type profile: string
46 @ivar cache: Cache of previously loaded state objects. Used to ensure all
47 references returned by L{loadState} point to the same state object. All
48 objects in this cache are persisted to disk when the manager is closed.
49 @type cache: dictionary of string : L{AEState <AEState.AEState>}
50 '''
52 '''
53 Creates the empty cache dictionary for state objects.
54 '''
55 self.cache = {}
56
57 self.name = None
58 self.profile = None
59
60 - def init(self, profile):
61 '''
62 Opens the shelved data on disk in the given profile.
63
64 Called by L{AEMain} at startup.
65
66 @param profile: Name of the profile to access using this manager.
67 @type profile: string
68 '''
69 self.name = profile
70 self.profile = os.path.join(PROFILES_PATH, self.name+'.profile')
71
73 '''
74 Persists all state in the L{cache}.
75 '''
76 for name, state in self.cache.items():
77 self.saveState(name, state)
78
79 - def loadStyles(self, device, default_style, style_cls):
80 '''
81 Loads a collection of L{AEOutput.Style} objects from disk.
82
83 The loaded styles are not stored in the L{cache} as the singleton
84 requirement does not hold for output device styles. As a result, the styles
85 loaded here will not be automatically persisted when this object is
86 destroyed. L{saveStyles} should be invoked directly.
87
88 @param device: Output device whose style objects we're loading
89 @type device: L{AEOutput <AEOutput.AEOutput>}
90 @param default_style: Instance of a default style object to be populated
91 with data
92 @type default_style: L{AEOutput.Style}
93 @param style_cls: Class for constructing new styles to populate
94 @type style_cls: L{AEOutput.Style}
95 @raise KeyError: When the name is not a valid key
96 @raise OSError: When the profile file cannot be opened or read
97 '''
98 default_data, styles_data = self.load(device.getClassName())
99
100 default_style.unserialize(default_data)
101 flyweight_styles = {}
102 for key, data in styles_data:
103
104 st = style_cls(default_style)
105 st.init(device)
106 st.unserialize(data)
107 flyweight_styles[key] = st
108 return flyweight_styles
109
110 - def saveStyles(self, device, default_style, other_styles):
111 '''
112 Saves the internal data of a collection L{AEOutput.Style} objects to disk.
113
114 @param device: Output device whose style objects we're persisting
115 @type device: L{AEOutput <AEOutput.AEOutput>}
116 @param default_style: Instance of a default style object to have its data
117 persisted
118 @type default_style: L{AEOutput.Style}
119 @param other_styles: Dictionary of other styles to have their data
120 persisted under the keys used in the dictionary
121 @type other_styles: dictionary of immutable :
122 L{AEOutput.Style}
123 @raise OSError: When the profile file cannot be opened or saved
124 '''
125 flyweight_data = [(key, style.serialize()) for key, style in
126 other_styles.items()]
127 self.save(device.getClassName(),
128 (default_style.serialize(), flyweight_data))
129
131 '''
132 Loads an L{AEState <AEState.AEState>} object from disk.
133
134 If cached is C{True}, stores a copy of
135 the state to be returned in memory such that future calls to L{load} return
136 the same instance with the same state.
137
138 @param name: Name under which the object was previously stored
139 @type name: string
140 @param state: SUE state object to initialize with the loaded data
141 @type state: L{AEState <AEState.AEState>}
142 @return: Singleton instance of the AEState object for the given name
143 @rtype: L{AEState <AEState.AEState>}
144 @raise KeyError: When the name is not a valid key
145 @raise OSError: When the profile file cannot be opened or read
146 '''
147
148 try:
149 return self.cache[name]
150 except KeyError:
151 pass
152
153 self.cache[name] = state
154
155 data = self.load(name)
156
157 state.unserialize(data)
158 return state
159
161 '''
162 Saves the internal data of an L{AEState <AEState.AEState>} object to disk.
163
164 Does not Pickle
165 the state object directly, but rather calls its serialize method to get a
166 simple dictionary. This is done to avoid the problems caused by trying to
167 persist state objects in modules that have been reloaded at runtime. Also
168 does not Pickle states that are not dirty according to
169 L{AEState.isDirty <AEState.Base.AEState.isDirty>}.
170
171 @param name: Name under which to save the object
172 @type name: string
173 @param state: SUE state object whose data should be stored
174 @type state: L{AEState <AEState.AEState>}
175 @raise OSError: When the profile file cannot be opened or saved
176 '''
177 if state.isDirty():
178 self.save(name, state.serialize())
179
180 - def save(self, name, data):
181 '''
182 Saves the given object under the given name in the profile used to
183 initialize this manager.
184
185 Pickles the given object without any further
186 processing on the part of this manager.
187
188 @param name: Name under which to save the object
189 @type name: string
190 @param data: Object to store
191 @type data: object
192 @raise OSError: When the profile file cannot be opened or saved
193 '''
194 db = shelve.open(self.profile, protocol=-1)
195 db[name] = data
196 db.close()
197
198 - def load(self, name):
199 '''
200 Loads the object from the profile used to initialize the manager.
201
202 Unpickles
203 the object under the given name without any additional processing on the
204 part of this mananger.
205
206 @param name: Name under which the object was previously stored
207 @type name: string
208 @return: Object loaded
209 @rtype: object
210 @raise KeyError: When the name is not a valid key
211 @raise OSError: When the profile file cannot be opened or read
212 '''
213 db = shelve.open(self.profile, protocol=-1)
214 try:
215 data = db[name]
216 finally:
217 db.close()
218 return data
219
221 '''
222 Gets the name of the profile.
223
224 @return: the name of the loaded profile
225 @rtype: string
226 '''
227 return self.name
228
230 '''
231 Initializes a new profile on disk.
232
233 @raise ValueError: When the profile already exists
234 @raise OSError: When the profile file cannot be created
235 '''
236 if self.existsProfile():
237 raise ValueError(_('Profile %s already exists' % self.name))
238 db = shelve.open(self.profile, protocol=-1)
239 db.close()
240
242 '''
243 Make sure the managed profile exist on disk.
244
245 @raise ValueError: When the profile does not exist
246 '''
247 if not self.existsProfile():
248 raise ValueError(_('Profile %s does not exist') % self.name)
249
251 '''
252 Look whether the managed profile exist on disk.
253
254 @return: Does the managed profile exist on disk?
255 @rtype: boolean
256 '''
257 return whichdb.whichdb(self.profile) is not None
258
260 '''
261 Deletes the managed profile from disk.
262
263 @raise ValueError: When the profile is built-in and cannot be deleted or
264 the profile does not exist
265 @raise OSError: When the profile database cannot be deleted
266 '''
267 if self.name in BUILTIN_PROFILES:
268 raise ValueError(_('Cannot remove built-in %s profile') % self.name)
269 if not self.existsProfile():
270 raise ValueError(_('Profile %s does not exist' % self.name))
271 os.unlink(self.profile)
272
274 '''
275 Copies this profile to another name on disk.
276
277 This method overwrites the destination if it already exists.
278
279 @param name: Destination profile
280 @type name: string
281 @raise ValueError: When the profile does not exist
282 @raise OSError: When the profile file cannot be copied to the destination
283 '''
284 if not self.existsProfile():
285 raise ValueError(_('Profile %s does not exist' % profile_name))
286 shutil.copy(self.profile, os.path.join(PROFILES_PATH, name+'.profile'))
287
288 @classmethod
290 '''
291 Gets a list of existing profile names.
292
293 @return: List of all profiles stored on disk
294 @rtype: list of string
295 '''
296 return [name.split(os.extsep)[0] for name in os.listdir(PROFILES_PATH)]
297
298 @classmethod
300 '''
301 Gets if the L{SUEConstants.Profile.BUILTIN_PROFILES} exist.
302
303 @todo: RB: The link needs to be reset after the SUEConstants are moved.
304
305 @return: Do the default profiles exist?
306 @rtype: boolean
307 '''
308 profiles = cls.listProfiles()
309 for name in BUILTIN_PROFILES:
310 if name not in profiles:
311 return False
312 return True
313