~ubuntu-branches/ubuntu/utopic/python-apptools/utopic

« back to all changes in this revision

Viewing changes to enthought/preferences/scoped_preferences.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-07-08 23:55:50 UTC
  • mfrom: (2.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20110708235550-yz5u79ubeo4dhyfx
Tags: 4.0.0-1
* New upstream release
* Update debian/watch file

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
""" A preferences node that adds the notion of preferences scopes. """
2
 
 
3
 
 
4
 
# Standard library imports.
5
 
from os.path import join
6
 
 
7
 
# Enthought library imports.
8
 
from enthought.etsconfig.api import ETSConfig
9
 
from enthought.traits.api import List, Str, Undefined
10
 
 
11
 
# Local imports.
12
 
from i_preferences import IPreferences
13
 
from preferences import Preferences
14
 
 
15
 
 
16
 
class ScopedPreferences(Preferences):
17
 
    """ A preferences node that adds the notion of preferences scopes.
18
 
 
19
 
    Path names passed to the node can either contain scope information or can
20
 
    simply be a preference path. In the latter case, the operation takes place
21
 
    in the first scope in the node's list of scopes.
22
 
 
23
 
    The syntax of a fully qualified path name is::
24
 
 
25
 
      scope_name/some/arbitrary/scope/context/path.to.a.preference
26
 
 
27
 
    The scope is up to the first '/'. The scope context (if any) is from the
28
 
    first '/' to the last '/', and the actual preference path is everything
29
 
    after the last '/'.
30
 
 
31
 
    e.g. A preference path might look like this::
32
 
    
33
 
    'project/My Project/my.plugin.id/acme.ui.bgcolor'
34
 
    
35
 
    The scope is           'project'.
36
 
    The scope context is   'My Project/my.plugin.id'
37
 
    The preference path is 'acme.ui.bgcolor' 
38
 
 
39
 
    There is one drawback to this scheme. If you want to access a scope node
40
 
    itself via the 'clear', 'keys', 'node', 'node_exists' or 'node_names'
41
 
    methods then you have to append a trailing '/' to the path. Without that,
42
 
    the node would try to perform the operation in the first scope.
43
 
 
44
 
    e.g. To get the names of the children of the 'application' scope, use::
45
 
 
46
 
      scoped.node_names('application/')
47
 
 
48
 
    If you did this::
49
 
 
50
 
      scoped.node_names('application')
51
 
 
52
 
    Then the node would get the first scope and try to find its child node
53
 
    called 'application'.
54
 
 
55
 
    Of course you can just get the scope via::
56
 
 
57
 
      scoped.get_scope('application')
58
 
 
59
 
    and then call whatever methods you like on it!
60
 
      
61
 
    """
62
 
 
63
 
    #### 'ScopedPreferences' interface ########################################
64
 
 
65
 
    # The scopes (in the order that they should be searched when looking for
66
 
    # preferences).
67
 
    scopes = List(IPreferences)
68
 
    
69
 
    ###########################################################################
70
 
    # 'IPreferences' interface.
71
 
    ###########################################################################
72
 
    
73
 
    #### Methods where 'path' refers to a preference ####
74
 
 
75
 
    def get(self, path, default=None, inherit=False):
76
 
        """ Get the value of the preference at the specified path. """
77
 
 
78
 
        if len(path) == 0:
79
 
            raise ValueError('empty path')
80
 
 
81
 
        # If the path contains a specific scope then lookup the preference in
82
 
        # just that scope.
83
 
        if self._path_contains_scope(path):
84
 
            scope_name, path = self._parse_path(path)
85
 
            nodes = [self._get_scope(scope_name)]
86
 
            
87
 
        # Otherwise, try each scope in turn.
88
 
        else:
89
 
            nodes = self.scopes
90
 
 
91
 
        # Try all nodes first (without inheritance even if specified).
92
 
        value = self._get(path, Undefined, nodes, inherit=False)
93
 
        if value is Undefined:
94
 
            if inherit:
95
 
                value = self._get(path, default, nodes, inherit=True)
96
 
 
97
 
            else:
98
 
                value = default
99
 
                
100
 
        return value
101
 
 
102
 
    def remove(self, path):
103
 
        """ Remove the preference at the specified path. """
104
 
 
105
 
        if len(path) == 0:
106
 
            raise ValueError('empty path')
107
 
 
108
 
        # If the path contains a specific scope then remove the preference from
109
 
        # just that scope.
110
 
        if self._path_contains_scope(path):
111
 
            scope_name, path = self._parse_path(path)
112
 
            node = self._get_scope(scope_name)
113
 
 
114
 
        # Otherwise, remove the preference from the first scope.
115
 
        else:
116
 
            node = self.scopes[0]
117
 
 
118
 
        node.remove(path)
119
 
 
120
 
        return
121
 
        
122
 
    def set(self, path, value):
123
 
        """ Set the value of the preference at the specified path. """
124
 
 
125
 
        if len(path) == 0:
126
 
            raise ValueError('empty path')
127
 
 
128
 
        # If the path contains a specific scope then set the value in that
129
 
        # scope.
130
 
        if self._path_contains_scope(path):
131
 
            scope_name, path = self._parse_path(path)
132
 
            node = self._get_scope(scope_name)
133
 
 
134
 
        # Otherwise, set the value in the first scope.
135
 
        else:
136
 
            node = self.scopes[0]
137
 
 
138
 
        node.set(path, value)
139
 
 
140
 
        return
141
 
 
142
 
    #### Methods where 'path' refers to a node ####
143
 
 
144
 
    def clear(self, path=''):
145
 
        """ Remove all preference from the node at the specified path. """
146
 
 
147
 
        # If the path contains a specific scope then remove the preferences
148
 
        # from a node in that scope.
149
 
        if self._path_contains_scope(path):
150
 
            scope_name, path = self._parse_path(path)
151
 
            node = self._get_scope(scope_name)
152
 
 
153
 
        # Otherwise, remove the preferences from a node in the first scope.
154
 
        else:
155
 
            node = self.scopes[0]
156
 
 
157
 
        return node.clear(path)
158
 
 
159
 
    def keys(self, path=''):
160
 
        """ Return the preference keys of the node at the specified path. """
161
 
 
162
 
        # If the path contains a specific scope then get the keys of the node
163
 
        # in that scope.
164
 
        if self._path_contains_scope(path):
165
 
            scope_name, path = self._parse_path(path)
166
 
            nodes = [self._get_scope(scope_name)]
167
 
 
168
 
        # Otherwise, merge the keys of the node in all scopes.
169
 
        else:
170
 
            nodes = self.scopes
171
 
 
172
 
        keys = set()
173
 
        for node in nodes:
174
 
            keys.update(node.node(path).keys())
175
 
 
176
 
        return list(keys)
177
 
 
178
 
    def node(self, path=''):
179
 
        """ Return the node at the specified path. """
180
 
 
181
 
        if len(path) == 0:
182
 
            node = self
183
 
 
184
 
        else:
185
 
            # If the path contains a specific scope then we get the node that
186
 
            # scope.
187
 
            if self._path_contains_scope(path):
188
 
                scope_name, path = self._parse_path(path)
189
 
                node = self._get_scope(scope_name)
190
 
 
191
 
            # Otherwise, get the node from the first scope.
192
 
            else:
193
 
                node = self.scopes[0]
194
 
 
195
 
            node = node.node(path)
196
 
 
197
 
        return node
198
 
 
199
 
    def node_exists(self, path=''):
200
 
        """ Return True if the node at the specified path exists. """
201
 
 
202
 
        # If the path contains a specific scope then look for the node in that
203
 
        # scope.
204
 
        if self._path_contains_scope(path):
205
 
            scope_name, path = self._parse_path(path)
206
 
            node = self._get_scope(scope_name)
207
 
 
208
 
        # Otherwise, look for the node in the first scope.
209
 
        else:
210
 
            node = self.scopes[0]
211
 
 
212
 
        return node.node_exists(path)
213
 
 
214
 
    def node_names(self, path=''):
215
 
        """ Return the names of the children of the node at the specified path.
216
 
 
217
 
        """
218
 
 
219
 
        # If the path contains a specific scope then get the names of the
220
 
        # children of the node in that scope.
221
 
        if self._path_contains_scope(path):
222
 
            scope_name, path = self._parse_path(path)
223
 
            nodes = [self._get_scope(scope_name)]
224
 
 
225
 
        # Otherwise, merge the names of the children of the node in all scopes.
226
 
        else:
227
 
            nodes = self.scopes
228
 
 
229
 
        names = set()
230
 
        for node in nodes:
231
 
            names.update(node.node(path).node_names())
232
 
 
233
 
        return list(names)
234
 
 
235
 
    ###########################################################################
236
 
    # 'Preferences' interface.
237
 
    ###########################################################################
238
 
 
239
 
    #### Listener methods ####
240
 
 
241
 
    def add_preferences_listener(self, listener, path=''):
242
 
        """ Add a listener for changes to a node's preferences. """
243
 
 
244
 
        # If the path contains a specific scope then add a preferences listener
245
 
        # to the node in that scope.
246
 
        if self._path_contains_scope(path):
247
 
            scope_name, path = self._parse_path(path)
248
 
            nodes = [self._get_scope(scope_name)]
249
 
 
250
 
        # Otherwise, add a preferences listener to the node in all scopes.
251
 
        else:
252
 
            nodes = self.scopes
253
 
 
254
 
        for node in nodes:
255
 
            node.add_preferences_listener(listener, path)
256
 
 
257
 
        return
258
 
 
259
 
    def remove_preferences_listener(self, listener, path=''):
260
 
        """ Remove a listener for changes to a node's preferences. """
261
 
 
262
 
        # If the path contains a specific scope then remove a preferences
263
 
        # listener from the node in that scope.
264
 
        if self._path_contains_scope(path):
265
 
            scope_name, path = self._parse_path(path)
266
 
            nodes = [self._get_scope(scope_name)]
267
 
 
268
 
        # Otherwise, remove a preferences listener from the node in all scopes.
269
 
        else:
270
 
            nodes = self.scopes
271
 
 
272
 
        for node in nodes:
273
 
            node.remove_preferences_listener(listener, path)
274
 
 
275
 
        return
276
 
 
277
 
    #### Persistence methods ####
278
 
 
279
 
    def load(self, file_or_filename=None):
280
 
        """ Load preferences from a file.
281
 
 
282
 
        This loads the preferences into the first scope.
283
 
 
284
 
        """
285
 
 
286
 
        if file_or_filename is None and len(self.filename) > 0:
287
 
            file_or_filename = self.filename
288
 
            
289
 
        node = self.scopes[0]
290
 
        node.load(file_or_filename)
291
 
 
292
 
        return
293
 
 
294
 
    def save(self, file_or_filename=None):
295
 
        """ Save the node's preferences to a file.
296
 
 
297
 
        This asks each scope in turn to save its preferences.
298
 
 
299
 
        If a file or filename is specified then it is only passed to the first
300
 
        scope.
301
 
 
302
 
        """
303
 
 
304
 
        if file_or_filename is None and len(self.filename) > 0:
305
 
            file_or_filename = self.filename
306
 
 
307
 
        self.scopes[0].save(file_or_filename)
308
 
        for scope in self.scopes[1:]:
309
 
            scope.save()
310
 
 
311
 
        return
312
 
 
313
 
    ###########################################################################
314
 
    # 'ScopedPreferences' interface.
315
 
    ###########################################################################
316
 
    
317
 
    def _scopes_default(self):
318
 
        """ Trait initializer. """
319
 
        
320
 
        # The application scope is a persistent scope.
321
 
        application_scope = Preferences(
322
 
            name     = 'application',
323
 
            filename = join(ETSConfig.get_application_home(create=False), 
324
 
                            'preferences.ini')
325
 
        )
326
 
 
327
 
        # The default scope is a transient scope.
328
 
        default_scope = Preferences(name='default')
329
 
 
330
 
        return [application_scope, default_scope]
331
 
 
332
 
    def get_scope(self, scope_name):
333
 
        """ Return the scope with the specified name.
334
 
 
335
 
        Return None if no such scope exists.
336
 
 
337
 
        """
338
 
 
339
 
        for scope in self.scopes:
340
 
            if scope_name == scope.name:
341
 
                break
342
 
 
343
 
        else:
344
 
            scope = None
345
 
 
346
 
        return scope
347
 
        
348
 
    ###########################################################################
349
 
    # Private interface.
350
 
    ###########################################################################
351
 
 
352
 
    def _get(self, path, default, nodes, inherit):
353
 
        """ Get a preference from a list of nodes. """
354
 
        
355
 
        for node in nodes:
356
 
            value = node.get(path, Undefined, inherit)
357
 
            if value is not Undefined:
358
 
                break
359
 
 
360
 
        else:
361
 
            value = default
362
 
 
363
 
        return value
364
 
 
365
 
    def _get_scope(self, scope_name):
366
 
        """ Return the scope with the specified name.
367
 
 
368
 
        Raise a 'ValueError' is no such scope exists.
369
 
 
370
 
        """
371
 
 
372
 
        scope = self.get_scope(scope_name)
373
 
        if scope is None:
374
 
            raise ValueError('no such scope %s' % scope_name)
375
 
 
376
 
        return scope
377
 
 
378
 
    def _path_contains_scope(self, path):
379
 
        """ Return True if the path contains a scope component. """
380
 
 
381
 
        return '/' in path
382
 
    
383
 
    def _parse_path(self, path):
384
 
        """ 'Parse' the path into two parts, the scope name and the rest! """
385
 
 
386
 
        components = path.split('/')
387
 
 
388
 
        return components[0], '/'.join(components[1:])
389
 
 
390
 
    ###########################################################################
391
 
    # Debugging interface.
392
 
    ###########################################################################
393
 
 
394
 
    def dump(self, indent=''):
395
 
        """ Dump the preferences hierarchy to stdout. """
396
 
 
397
 
        if indent == '':
398
 
            print
399
 
            
400
 
        print indent, 'Node(%s)' % self.name, self._preferences
401
 
        indent += '  '
402
 
 
403
 
        for child in self.scopes:
404
 
            child.dump(indent)
405
 
        
406
 
        return
407
 
    
408
 
#### EOF ######################################################################