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