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

« back to all changes in this revision

Viewing changes to enthought/preferences/tests/py_config_file.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 Python based configuration file with hierarchical sections. """
2
 
 
3
 
 
4
 
class PyConfigFile(dict):
5
 
    """ A Python based configuration file with hierarchical sections. """
6
 
 
7
 
    ###########################################################################
8
 
    # 'object' interface.
9
 
    ###########################################################################
10
 
 
11
 
    def __init__(self, file_or_filename=None):
12
 
        """ Constructor.
13
 
 
14
 
        If 'file_or_filename' is specified it will be loaded immediately. It
15
 
        can be either:-
16
 
 
17
 
        a) a filename
18
 
        b) a file-like object that must be open for reading
19
 
        
20
 
        """
21
 
 
22
 
        # A dictionary containing one namespace instance for each root of the
23
 
        # config hierarchy (see the '_Namespace' class for more details).
24
 
        #
25
 
        # e.g. If the following sections have been loaded:-
26
 
        #
27
 
        # [acme.foo]
28
 
        # ...
29
 
        # [acme.bar]
30
 
        # ...
31
 
        # [tds]
32
 
        # ...
33
 
        # [tds.baz]
34
 
        # ...
35
 
        #
36
 
        # Then the dictionary will contain:-
37
 
        #
38
 
        # {'acme' : <A _Namespace instance>, 'tds' : <A _Namespace instance>}
39
 
        #
40
 
        self._namespaces = {}
41
 
 
42
 
        if file_or_filename is not None:
43
 
            self.load(file_or_filename)
44
 
 
45
 
        return
46
 
 
47
 
    ###########################################################################
48
 
    # 'PyConfigFile' interface.
49
 
    ###########################################################################
50
 
    
51
 
    def load(self, file_or_filename):
52
 
        """ Load the configuration from a file.
53
 
 
54
 
        'file_or_filename' can be either:-
55
 
 
56
 
        a) a filename
57
 
        b) a file-like object that must be open for reading
58
 
 
59
 
        """
60
 
 
61
 
        # Get an open file to read from.
62
 
        f = self._get_file(file_or_filename)
63
 
 
64
 
        section_name = None
65
 
        for line in f:
66
 
            stripped = line.strip()
67
 
 
68
 
            # Is this line a section header?
69
 
            #
70
 
            # If so then parse the preceding section (if there is one) and
71
 
            # start collecting the body of the new section.
72
 
            if stripped.startswith('[') and stripped.endswith(']'):
73
 
                if section_name is not None:
74
 
                    self._parse_section(section_name, section_body)
75
 
 
76
 
                section_name = stripped[1:-1]
77
 
                section_body = ''
78
 
 
79
 
            # Otherwise, this is *not* a section header so add the line to the
80
 
            # body of the current section. If there is no current section then
81
 
            # we simply ignore it!
82
 
            else:
83
 
                if section_name is not None:
84
 
                    section_body += line
85
 
 
86
 
        # Parse the last section in the file.
87
 
        if section_name is not None:
88
 
            self._parse_section(section_name, section_body)
89
 
 
90
 
        f.close()
91
 
        
92
 
        return
93
 
 
94
 
    def save(self, file_or_filename):
95
 
        """ Save the configuration to a file.
96
 
 
97
 
        'file_or_filename' can be either:-
98
 
 
99
 
        a) a filename
100
 
        b) a file-like object that must be open for writing
101
 
 
102
 
        """
103
 
 
104
 
        f = self._get_file(file_or_filename, 'w')
105
 
 
106
 
        for section_name, section_data in self.items():
107
 
            self._write_section(f, section_name, section_data)
108
 
 
109
 
        f.close()
110
 
        
111
 
        return
112
 
            
113
 
    ###########################################################################
114
 
    # Private interface.
115
 
    ###########################################################################
116
 
 
117
 
    def _get_file(self, file_or_filename, mode='r'):
118
 
        """ Return an open file object from a file or a filename.
119
 
 
120
 
        The mode is only used if a filename is specified.
121
 
 
122
 
        """
123
 
 
124
 
        if isinstance(file_or_filename, basestring):
125
 
            f = file(file_or_filename, mode)
126
 
                
127
 
        else:
128
 
            f = file_or_filename
129
 
 
130
 
        return f
131
 
 
132
 
    def _get_namespace(self, section_name):
133
 
        """ Return the namespace that represents the section. """
134
 
 
135
 
        components = section_name.split('.')
136
 
        namespace = self._namespaces.setdefault(components[0], _Namespace())
137
 
 
138
 
        for component in components[1:]:
139
 
            namespace = getattr(namespace, component)
140
 
 
141
 
        return namespace
142
 
    
143
 
    def _parse_section(self, section_name, section_body):
144
 
        """ Parse a section.
145
 
 
146
 
        In this implementation, we don't actually 'parse' anything - we just
147
 
        execute the body of the section as Python code ;^)
148
 
 
149
 
        """
150
 
 
151
 
        # If this is the first time that we have come across the section then
152
 
        # start with an empty dictionary for its contents. Otherwise, we will
153
 
        # update its existing contents.
154
 
        section = self.setdefault(section_name, {})
155
 
 
156
 
        # Execute the Python code in the section dictionary.
157
 
        #
158
 
        # We use 'self._namespaces' as the globals for the code execution so
159
 
        # that config values can refer to other config values using familiar
160
 
        # Python syntax (see the '_Namespace' class for more details).
161
 
        #
162
 
        # e.g.
163
 
        #
164
 
        # [acme.foo]
165
 
        # bar = 1
166
 
        # baz = 99
167
 
        #
168
 
        # [acme.blargle]
169
 
        # blitzel = acme.foo.bar + acme.foo.baz
170
 
        exec section_body in self._namespaces, section
171
 
 
172
 
        # The '__builtins__' dictionary gets added to 'self._namespaces' as
173
 
        # by the call to 'exec'. However, we want 'self._namespaces' to only
174
 
        # contain '_Namespace' instances, so we do the cleanup here.
175
 
        del self._namespaces['__builtins__']
176
 
 
177
 
        # Get the section's corresponding node in the 'dotted' namespace and
178
 
        # update it with the config values.
179
 
        namespace = self._get_namespace(section_name)
180
 
        namespace.__dict__.update(section)
181
 
 
182
 
        return
183
 
 
184
 
    def _write_section(self, f, section_name, section_data):
185
 
        """ Write a section to a file. """
186
 
 
187
 
        f.write('[%s]\n' % section_name)
188
 
 
189
 
        for name, value in section_data.items():
190
 
            f.write('%s = %s\n' % (name, repr(value)))
191
 
 
192
 
        f.write('\n')
193
 
 
194
 
        return
195
 
 
196
 
    ###########################################################################
197
 
    # Debugging interface.
198
 
    ###########################################################################
199
 
 
200
 
    def _pretty_print_namespaces(self):
201
 
        """ Pretty print the 'dotted' namespaces. """
202
 
 
203
 
        for name, value in self._namespaces.items():
204
 
            print 'Namespace:', name
205
 
            value.pretty_print('  ')
206
 
 
207
 
        return
208
 
 
209
 
 
210
 
###############################################################################
211
 
# Internal use only.
212
 
###############################################################################
213
 
 
214
 
class _Namespace(object):
215
 
    """ An object that represents a node in a dotted namespace.
216
 
 
217
 
    We build up a dotted namespace so that config values can refer to other
218
 
    config values using familiar Python syntax.
219
 
    
220
 
    e.g.
221
 
 
222
 
    [acme.foo]
223
 
    bar = 1
224
 
    baz = 99
225
 
    
226
 
    [acme.blargle]
227
 
    blitzel = acme.foo.bar + acme.foo.baz
228
 
 
229
 
    """
230
 
 
231
 
    ###########################################################################
232
 
    # 'object' interface.
233
 
    ###########################################################################
234
 
 
235
 
    def __getattr__(self, name):
236
 
        """ Return the attribute with the specified name. """
237
 
 
238
 
        # This looks a little weird, but we are simply creating the next level
239
 
        # in the namespace hierarchy 'on-demand'.
240
 
        namespace = self.__dict__[name] = _Namespace()
241
 
        
242
 
        return namespace
243
 
    
244
 
    ###########################################################################
245
 
    # Debugging interface.
246
 
    ###########################################################################
247
 
 
248
 
    def pretty_print(self, indent=''):
249
 
        """ Pretty print the namespace. """
250
 
 
251
 
        for name, value in self.__dict__.items():
252
 
            if isinstance(value, _Namespace):
253
 
                print indent, 'Namespace:', name
254
 
                value.pretty_print(indent + '  ')
255
 
 
256
 
            else:
257
 
                print indent, name, ':', value
258
 
 
259
 
        return
260
 
    
261
 
#### EOF ######################################################################