~nchohan/appscale/zk3.3.4

« back to all changes in this revision

Viewing changes to AppServer/lib/grizzled/grizzled/config.py

  • Committer: Chris Bunch
  • Date: 2012-02-17 08:19:21 UTC
  • mfrom: (787.2.3 appscale-raj-merge)
  • Revision ID: cgb@cs.ucsb.edu-20120217081921-pakidyksaenlpzur
merged with main branch, gaining rabbitmq and upgrades for hbase, cassandra, and hypertable, as well as upgrading to gae 1.6.1 for python and go

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
'''
 
2
Introduction
 
3
============
 
4
 
 
5
Based on the standard Python ``ConfigParser`` module, this module provides an
 
6
enhanced configuration parser capabilities. ``Configuration`` is a drop-in
 
7
replacement for ``ConfigParser``.
 
8
 
 
9
A configuration file is broken into sections, and each section is
 
10
introduced by a section name in brackets. For example::
 
11
 
 
12
    [main]
 
13
    installationDirectory=/usr/local/foo
 
14
    programDirectory: /usr/local/foo/programs
 
15
 
 
16
    [search]
 
17
    searchCommand: find /usr/local/foo -type f -name "*.class"
 
18
 
 
19
    [display]
 
20
    searchFailedMessage=Search failed, sorry.
 
21
 
 
22
 
 
23
Section Name Syntax
 
24
===================
 
25
 
 
26
A section name can consist of alphabetics, numerics, underscores and
 
27
periods. There can be any amount of whitespace before and after the
 
28
brackets in a section name; the whitespace is ignored.
 
29
 
 
30
Variable Syntax
 
31
===============
 
32
 
 
33
Each section contains zero or more variable settings.
 
34
 
 
35
 - Similar to a Java ``Properties`` file, the variables are specified as
 
36
   name/value pairs, separated by an equal sign ("=") or a colon (":").
 
37
 - Variable names are case-sensitive and may contain alphabetics, numerics,
 
38
   underscores and periods (".").
 
39
 - Variable values may contain anything at all. Leading whitespace in the
 
40
   value is skipped. The way to include leading whitespace in a value is
 
41
   escape the whitespace characters with backslashes.
 
42
 
 
43
Variable Substitution
 
44
=====================
 
45
 
 
46
A variable value can interpolate the values of other variables, using a
 
47
variable substitution syntax. The general form of a variable reference is
 
48
``${section_name:var_name}``.
 
49
 
 
50
  - *section_name* is the name of the section containing the variable to
 
51
    substitute; if omitted, it defaults to the current section.
 
52
  - *var_name* is the name of the variable to substitute.
 
53
 
 
54
Default values
 
55
--------------
 
56
 
 
57
You can also specify a default value for a variable, using this syntax::
 
58
 
 
59
    ${foo?default}
 
60
    ${section:foo?default}
 
61
 
 
62
That is, the sequence ``?default`` after a variable name specifies the
 
63
default value if the variable has no value. (Normally, if a variable has
 
64
no value, it is replaced with an empty string.) Defaults can be useful,
 
65
for instance, to allow overrides from the environment. The following example
 
66
defines a log file directory that defaults to "/tmp", unless environment
 
67
variable LOGDIR is set to a non-empty value::
 
68
 
 
69
    logDirectory: ${env:LOGDIR?/var/log}
 
70
 
 
71
Special section names
 
72
---------------------
 
73
 
 
74
The section names "env", and "program" are reserved for special
 
75
pseudosections.
 
76
 
 
77
The ``env`` pseudosection
 
78
~~~~~~~~~~~~~~~~~~~~~~~~~
 
79
 
 
80
The "env" pseudosection is used to interpolate values from the environment. On
 
81
UNIX systems, for instance, ``${env:HOME}`` substitutes home directory of the
 
82
current user. On some versions of Windows, ``${env:USERNAME}`` will substitute
 
83
the name of the user.
 
84
 
 
85
Note: On UNIX systems, environment variable names are typically
 
86
case-sensitive; for instance, ``${env:USER}`` and ``${env:user}`` refer to
 
87
different environment variables. On Windows systems, environment variable
 
88
names are typically case-insensitive; ``${env:USERNAME}`` and
 
89
``${env:username}`` are equivalent.
 
90
 
 
91
The ``program`` pseudosection
 
92
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
93
 
 
94
The "program" pseudosection is a placeholder for various special variables
 
95
provided by the Configuration class. Those variables are:
 
96
 
 
97
``cwd``
 
98
    The current working directory. Thus, ``${program:cwd}`` will substitute
 
99
    the working directory, using the appropriate system-specific
 
100
    file separator (e.g., "/" on Unix, "\\" on Windows).
 
101
 
 
102
``name``
 
103
    The calling program name. Equivalent to the Python expression
 
104
    ``os.path.basename(sys.argv[0])``
 
105
 
 
106
``now``
 
107
    The current time, formatted using the ``time.strftime()`` format
 
108
    ``"%Y-%m-%d %H:%M:%S"`` (e.g., "2008-03-03 16:15:27")
 
109
 
 
110
Includes
 
111
--------
 
112
 
 
113
A special include directive permits inline inclusion of another
 
114
configuration file. The include directive takes two forms::
 
115
 
 
116
   %include "path"
 
117
   %include "URL"
 
118
 
 
119
For example::
 
120
 
 
121
    %include "/home/bmc/mytools/common.cfg"
 
122
    %include "http://configs.example.com/mytools/common.cfg"
 
123
 
 
124
The included file may contain any content that is valid for this parser. It
 
125
may contain just variable definitions (i.e., the contents of a section,
 
126
without the section header), or it may contain a complete configuration
 
127
file, with individual sections.
 
128
 
 
129
Note: Attempting to include a file from itself, either directly or
 
130
indirectly, will cause the parser to throw an exception.
 
131
 
 
132
Replacing ``ConfigParser``
 
133
==========================
 
134
 
 
135
You can use this class anywhere you would use the standard Python
 
136
``ConfigParser`` class. Thus, to change a piece of code to use enhanced
 
137
configuration, you might change this:
 
138
 
 
139
.. python::
 
140
 
 
141
    import ConfigParser
 
142
 
 
143
    config = ConfigParser.SafeConfigParser()
 
144
    config.read(configPath)
 
145
 
 
146
to this:
 
147
 
 
148
.. python::
 
149
 
 
150
    from grizzled.config import Configuration
 
151
 
 
152
    config = Configuration()
 
153
    config.read(configPath)
 
154
 
 
155
 
 
156
Sometimes, however, you have to use an API that expects a path to a
 
157
configuration file that can *only* be parsed with the (unenhanced)
 
158
``ConfigParser`` class. In that case, you simply use the ``preprocess()``
 
159
method:
 
160
 
 
161
.. python::
 
162
 
 
163
    import logging
 
164
    from grizzled import config
 
165
 
 
166
    logging.config.fileConfig(config.preprocess(pathToConfig))
 
167
 
 
168
That will preprocess the enhanced configuration file, producing a file
 
169
that is suitable for parsing by the standard Python ``config`` module.
 
170
'''
 
171
 
 
172
from __future__ import absolute_import
 
173
 
 
174
__docformat__ = "restructuredtext en"
 
175
 
 
176
# ---------------------------------------------------------------------------
 
177
# Imports
 
178
# ---------------------------------------------------------------------------
 
179
 
 
180
import ConfigParser
 
181
import logging
 
182
import string
 
183
import os
 
184
import time
 
185
import sys
 
186
import re
 
187
 
 
188
from grizzled.exception import ExceptionWithMessage
 
189
from grizzled.collections import OrderedDict
 
190
 
 
191
# ---------------------------------------------------------------------------
 
192
# Exports
 
193
# ---------------------------------------------------------------------------
 
194
 
 
195
__all__ = ['Configuration', 'preprocess',
 
196
           'NoOptionError', 'NoSectionError', 'NoVariableError']
 
197
 
 
198
# ---------------------------------------------------------------------------
 
199
# Globals
 
200
# ---------------------------------------------------------------------------
 
201
 
 
202
log = logging.getLogger('grizzled.config')
 
203
NoOptionError = ConfigParser.NoOptionError
 
204
NoSectionError = ConfigParser.NoSectionError
 
205
 
 
206
# ---------------------------------------------------------------------------
 
207
# Constants
 
208
# ---------------------------------------------------------------------------
 
209
 
 
210
# Used with _ConfigDict
 
211
 
 
212
SECTION_OPTION_DELIM = r':'
 
213
 
 
214
# Section name pattern
 
215
SECTION_NAME_PATTERN = r'([_.a-zA-Z][_.a-zA-Z0-9]+)'
 
216
 
 
217
# Pattern of an identifier local to a section.
 
218
VARIABLE_NAME_PATTERN = r'([_a-zA-Z][_a-zA-Z0-9]+)(\?[^}]+)?'
 
219
 
 
220
# Pattern of an identifier matched by our version of string.Template.
 
221
# Intended to match:
 
222
#
 
223
#     ${section:option}         variable 'option' in section 'section'
 
224
#     ${section:option?default} variable 'option' in section 'section', default
 
225
#                               value 'default'
 
226
#     ${option}                 variable 'option' in the current section
 
227
#     ${option?default}         variable 'option' in the current section,
 
228
#                               default value 'default'
 
229
VARIABLE_REF_PATTERN = SECTION_NAME_PATTERN + SECTION_OPTION_DELIM +\
 
230
                       VARIABLE_NAME_PATTERN +\
 
231
                       r'|' +\
 
232
                       VARIABLE_NAME_PATTERN
 
233
 
 
234
# Simple variable reference
 
235
SIMPLE_VARIABLE_REF_PATTERN = r'\$\{' + VARIABLE_NAME_PATTERN + '\}'
 
236
 
 
237
# Special sections
 
238
ENV_SECTION = 'env'
 
239
PROGRAM_SECTION = 'program'
 
240
 
 
241
# ---------------------------------------------------------------------------
 
242
# Classes
 
243
# ---------------------------------------------------------------------------
 
244
 
 
245
class NoVariableError(ExceptionWithMessage):
 
246
    """
 
247
    Thrown when a configuration file attempts to substitute a nonexistent
 
248
    variable, and the ``Configuration`` object was instantiated with
 
249
    ``strict_substitution`` set to ``True``.
 
250
    """
 
251
    pass
 
252
 
 
253
class Configuration(ConfigParser.SafeConfigParser):
 
254
    """
 
255
    Configuration file parser. See the module documentation for details.
 
256
    """
 
257
 
 
258
    def __init__(self,
 
259
                 defaults=None,
 
260
                 permit_includes=True,
 
261
                 use_ordered_sections=False,
 
262
                 strict_substitution=False):
 
263
        """
 
264
        Construct a new ``Configuration`` object.
 
265
 
 
266
        :Parameters:
 
267
            defaults : dict
 
268
                dictionary of default values
 
269
            permit_includes : bool
 
270
                whether or not to permit includes
 
271
            use_ordered_sections : bool
 
272
                whether or not to use an ordered dictionary for the section
 
273
                names. If ``True``, then a call to ``sections()`` will return
 
274
                the sections in the order they were encountered in the file.
 
275
                If ``False``, the order is based on the hash keys for the
 
276
                sections' names.
 
277
            strict_substitution : bool
 
278
                If ``True``, then throw an exception if attempting to
 
279
                substitute a non-existent variable. Otherwise, simple
 
280
                substitute an empty value.
 
281
        """
 
282
        ConfigParser.SafeConfigParser.__init__(self, defaults)
 
283
        self.__permit_includes = permit_includes
 
284
        self.__use_ordered_sections = use_ordered_sections
 
285
        self.__strict_substitution = strict_substitution
 
286
 
 
287
        if use_ordered_sections:
 
288
            self._sections = OrderedDict()
 
289
 
 
290
    def defaults(self):
 
291
        """
 
292
        Returns the instance-wide defaults.
 
293
 
 
294
        :rtype:  dict
 
295
        :return: the instance-wide defaults, or ``None`` if there aren't any
 
296
        """
 
297
        return ConfigParser.SafeConfigParser.defaults(self)
 
298
 
 
299
    @property
 
300
    def sections(self):
 
301
        """
 
302
        Get the list of available sections, not including ``DEFAULT``. It's
 
303
        not really useful to call this method before calling ``read()`` or
 
304
        ``readfp()``.
 
305
        
 
306
        Returns a list of sections.
 
307
        """
 
308
        return ConfigParser.SafeConfigParser.sections(self)
 
309
 
 
310
    def add_section(self, section):
 
311
        """
 
312
        Add a section named *section* to the instance. If a section by the
 
313
        given name already exists, ``DuplicateSectionError`` is raised.
 
314
 
 
315
        :Parameters:
 
316
            section : str
 
317
                name of section to add
 
318
 
 
319
        :raise DuplicateSectionError: section already exists
 
320
        """
 
321
        ConfigParser.SafeConfigParser.add_section(self, section)
 
322
 
 
323
    def has_section(self, section):
 
324
        """
 
325
        Determine whether a section exists in the configuration. Ignores
 
326
        the ``DEFAULT`` section.
 
327
 
 
328
        :Parameters:
 
329
            section : str
 
330
                name of section
 
331
 
 
332
        :rtype:  bool
 
333
        :return: ``True`` if the section exists in the configuration, ``False``
 
334
                 if not.
 
335
        """
 
336
        return ConfigParser.SafeConfigParser.has_section(self, section)
 
337
 
 
338
    def options(self, section):
 
339
        """
 
340
        Get a list of options available in the specified section.
 
341
 
 
342
        :Parameters:
 
343
            section : str
 
344
                name of section
 
345
 
 
346
        :rtype:  list
 
347
        :return: list of available options. May be empty.
 
348
 
 
349
        :raise NoSectionError: no such section
 
350
        """
 
351
        return ConfigParser.SafeConfigParser.options(self, section)
 
352
 
 
353
    def has_option(self, section, option):
 
354
        """
 
355
        Determine whether a section has a specific option.
 
356
 
 
357
        :Parameters:
 
358
            section : str
 
359
                name of section
 
360
            option : str
 
361
                name of option to check
 
362
 
 
363
        :rtype:  bool
 
364
        :return: ``True`` if the section exists in the configuration and
 
365
                 has the specified option, ``False`` if not.
 
366
        """
 
367
        return ConfigParser.SafeConfigParser.has_option(self, section, option)
 
368
 
 
369
    def read(self, filenames):
 
370
        """
 
371
        Attempt to read and parse a list of filenames or URLs, returning a
 
372
        list of filenames or URLs which were successfully parsed. If
 
373
        *filenames* is a string or Unicode string, it is treated as a single
 
374
        filename or URL. If a file or URL named in filenames cannot be opened,
 
375
        that file will be ignored. This is designed so that you can specify a
 
376
        list of potential configuration file locations (for example, the
 
377
        current directory, the user's home directory, and some system-wide
 
378
        directory), and all existing configuration files in the list will be
 
379
        read. If none of the named files exist, the ``Configuration`` instance
 
380
        will contain an empty dataset. An application which requires initial
 
381
        values to be loaded from a file should load the required file or files
 
382
        using ``readfp()`` before calling ``read()`` for any optional files:
 
383
 
 
384
        .. python::
 
385
 
 
386
            import Configuration
 
387
            import os
 
388
 
 
389
            config = Configuration.Configuration()
 
390
            config.readfp(open('defaults.cfg'))
 
391
            config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')])
 
392
 
 
393
        :Parameters:
 
394
            filenames : list or string
 
395
                list of file names or URLs, or the string for a single filename
 
396
                or URL
 
397
 
 
398
        :rtype:  list
 
399
        :return: list of successfully parsed filenames or URLs
 
400
        """
 
401
        if isinstance(filenames, basestring):
 
402
            filenames = [filenames]
 
403
 
 
404
        newFilenames = []
 
405
        for filename in filenames:
 
406
            try:
 
407
                self.__preprocess(filename, filename)
 
408
                newFilenames += [filename]
 
409
            except IOError:
 
410
                log.exception('Error reading "%s"' % filename)
 
411
 
 
412
        return newFilenames
 
413
 
 
414
    def readfp(self, fp, filename=None):
 
415
        '''
 
416
        Read and parse configuration data from a file or file-like object.
 
417
        (Only the ``readline()`` moethod is used.)
 
418
 
 
419
        :Parameters:
 
420
            fp : file
 
421
                File-like object with a ``readline()`` method
 
422
            filename : str
 
423
                Name associated with ``fp``, for error messages. If omitted or
 
424
                ``None``, then ``fp.name`` is used. If ``fp`` has no ``name``
 
425
                attribute, then ``"<???">`` is used.
 
426
        '''
 
427
        self.__preprocess(fp, filename)
 
428
 
 
429
    def get(self, section, option, optional=False):
 
430
        """
 
431
        Get an option from a section.
 
432
 
 
433
        :Parameters:
 
434
            section : str
 
435
                name of section
 
436
            option : str
 
437
                name of option to check
 
438
            optional : bool
 
439
                ``True`` to return None if the option doesn't exist. ``False``
 
440
                to throw an exception if the option doesn't exist.
 
441
 
 
442
        :rtype:  str
 
443
        :return: the option value
 
444
 
 
445
        :raise NoSectionError: no such section
 
446
        :raise NoOptionError: no such option in the section
 
447
        """
 
448
        def do_get(section, option):
 
449
            val = ConfigParser.SafeConfigParser.get(self, section, option)
 
450
            if len(val.strip()) == 0:
 
451
                raise ConfigParser.NoOptionError(option, section)
 
452
            return val
 
453
 
 
454
        if optional:
 
455
            return self.__get_optional(do_get, section, option)
 
456
        else:
 
457
            return do_get(section, option)
 
458
 
 
459
    def getint(self, section, option, optional=False):
 
460
        """
 
461
        Convenience method that coerces the result of a call to
 
462
        ``get()`` to an ``int``.
 
463
 
 
464
        :Parameters:
 
465
            section : str
 
466
                name of section
 
467
            option : str
 
468
                name of option to check
 
469
            optional : bool
 
470
                ``True`` to return None if the option doesn't exist. ``False``
 
471
                to throw an exception if the option doesn't exist.
 
472
 
 
473
        :rtype:  int
 
474
        :return: the option value
 
475
 
 
476
        :raise NoSectionError: no such section
 
477
        :raise NoOptionError: no such option in the section
 
478
        """
 
479
        def do_get(section, option):
 
480
            return ConfigParser.SafeConfigParser.getint(self, section, option)
 
481
 
 
482
        if optional:
 
483
            return self.__get_optional(do_xget, section, option)
 
484
        else:
 
485
            return do_get(section, option)
 
486
 
 
487
    def getfloat(self, section, option, optional=False):
 
488
        """
 
489
        Convenience method that coerces the result of a call to ``get()`` to a
 
490
        ``float``.
 
491
 
 
492
        :Parameters:
 
493
            section : str
 
494
                name of section
 
495
            option : str
 
496
                name of option to check
 
497
            optional : bool
 
498
                ``True`` to return None if the option doesn't exist. ``False``
 
499
                to throw an exception if the option doesn't exist.
 
500
 
 
501
        :rtype:  float
 
502
        :return: the option value
 
503
 
 
504
        :raise NoSectionError: no such section
 
505
        :raise NoOptionError: no such option in the section
 
506
        """
 
507
        def do_get(section, option):
 
508
            return ConfigParser.SafeConfigParser.getfloat(self, section, option)
 
509
 
 
510
        if optional:
 
511
            return self.__get_optional(do_get, section, option)
 
512
        else:
 
513
            return do_get(section, option)
 
514
 
 
515
    def getboolean(self, section, option, optional=False):
 
516
        '''
 
517
        Convenience method that coerces the result of a call to ``get()`` to a
 
518
        boolean. Accepted boolean values are "1", "yes", "true", and "on",
 
519
        which cause this method to return True, and "0", "no", "false", and
 
520
        "off", which cause it to return False. These string values are checked
 
521
        in a case-insensitive manner. Any other value will cause it to raise
 
522
        ``ValueError``.
 
523
 
 
524
        :Parameters:a
 
525
            section : str
 
526
                name of section
 
527
            option : str
 
528
                name of option to check
 
529
            optional : bool
 
530
                ``True`` to return None if the option doesn't exist. ``False``
 
531
                to throw an exception if the option doesn't exist.
 
532
 
 
533
        :rtype:  bool
 
534
        :return: the option value (``True`` or ``False``)
 
535
 
 
536
        :raise NoSectionError: no such section
 
537
        :raise NoOptionError: no such option in the section
 
538
        :raise ValueError: non-boolean value encountered
 
539
        '''
 
540
        def do_get(section, option):
 
541
            return ConfigParser.SafeConfigParser.getboolean(self,
 
542
                                                            section,
 
543
                                                            option)
 
544
 
 
545
        if optional:
 
546
            return self.__get_optional(do_get, section, option)
 
547
        else:
 
548
            return do_get(section, option)
 
549
 
 
550
    def getlist(self, section, option, sep=None, optional=False):
 
551
        '''
 
552
        Convenience method that coerces the result of a call to ``get()`` to a
 
553
        list. The value is split using the separator(s) specified by the
 
554
        ``sep`` argument. A ``sep`` value of ``None`` uses white space. The
 
555
        result is a list of string values.
 
556
 
 
557
        :Parameters:
 
558
            section : str
 
559
                name of section
 
560
            option : str
 
561
                name of option to check
 
562
            sep : str
 
563
                list element separator to use. Defaults to white space.
 
564
            optional : bool
 
565
                ``True`` to return None if the option doesn't exist. ``False``
 
566
                to throw an exception if the option doesn't exist.
 
567
 
 
568
        :rtype:  bool
 
569
        :return: the option value (``True`` or ``False``)
 
570
 
 
571
        :raise NoSectionError: no such section
 
572
        :raise NoOptionError: no such option in the section
 
573
        '''
 
574
        def do_get(section, option):
 
575
            value = ConfigParser.SafeConfigParser.get(self, section, option)
 
576
            return value.split(sep)
 
577
 
 
578
        if optional:
 
579
            return self.__get_optional(do_get, section, option)
 
580
        else:
 
581
            return do_get(section, option)
 
582
 
 
583
    def get_one_of(self,
 
584
                   section,
 
585
                   options,
 
586
                   optional=False,
 
587
                   default=None,
 
588
                   value_type=str):
 
589
        '''
 
590
        Retrieve at most one of a list or set of options from a section. This
 
591
        method is useful if there are multiple possible names for a single
 
592
        option. For example, suppose you permit either a ``user_name`` or a
 
593
        ``login_name`` option, but not both, in a section called
 
594
        ``credentials``. You can use the following code to retrieve the
 
595
        option value:
 
596
 
 
597
        .. python::
 
598
 
 
599
            from grizzled.config import Configuration
 
600
 
 
601
            config = Configuration()
 
602
            config.read('/path/to/config')
 
603
            user = config.get_one_of('credentials', ['user_name', 'login_name'])
 
604
 
 
605
        If both options exist, ``get_one_of()`` will a ``NoOptionError``. If
 
606
        neither option exists, ``get_one_of()`` will throw a ``NoOptionError``
 
607
        if ``optional`` is ``False`` and there's no default value; otherwise,
 
608
        it will return the default value.
 
609
 
 
610
        :Parameters:
 
611
            section : str
 
612
                name of section
 
613
            options : list or set
 
614
                list or set of allowable option names
 
615
            optional : bool
 
616
                ``True`` to return None if the option doesn't exist. ``False``
 
617
                to throw an exception if the option doesn't exist.
 
618
            default : str
 
619
                The default value, if the option does not exist.
 
620
            value_type : type
 
621
                The type to which to coerce the value. The value is coerced by
 
622
                casting.
 
623
 
 
624
        :rtype:  str
 
625
        :return: the option value, or ``None`` if nonexistent and
 
626
                 ``optional`` is ``True``
 
627
 
 
628
        :raise NoSectionError: no such section
 
629
        :raise NoOptionError: none of the named options are in the section
 
630
        '''
 
631
        value = None
 
632
        if value_type is bool:
 
633
            get = self.getboolean
 
634
        else:
 
635
            get = self.get
 
636
 
 
637
        for option in options:
 
638
            value = get(section, option, optional=True)
 
639
            if value:
 
640
                break
 
641
 
 
642
        if value is None:
 
643
            value = default
 
644
 
 
645
        if (value is None) and (not optional):
 
646
            raise NoOptionError('Section "%s" must contain exactly one of the '
 
647
                                'following options: %s' %
 
648
                                (section, ', '.join(list(options))))
 
649
 
 
650
        if value is not None:
 
651
            if not (value_type in (bool, str)):
 
652
                value = eval('%s(%s)' % (value_type.__name__, value))
 
653
 
 
654
        return value
 
655
 
 
656
    def items(self, section):
 
657
        """
 
658
        Get all items in a section.
 
659
 
 
660
        :Parameters:
 
661
            section : str
 
662
                name of section
 
663
 
 
664
        :rtype:  list
 
665
        :return: a list of (*name*, *value*) tuples for each option in
 
666
                 in *section*
 
667
 
 
668
        :raise NoSectionError: no such section
 
669
        """
 
670
        return ConfigParser.SafeConfigParser.items(self, section)
 
671
 
 
672
    def set(self, section, option, value):
 
673
        """
 
674
        If the given section exists, set the given option to the specified
 
675
        value; otherwise raise ``NoSectionError``.
 
676
 
 
677
        :Parameters:
 
678
            section : str
 
679
                name of section
 
680
            option : str
 
681
                name of option to check
 
682
            value : str
 
683
                the value to set
 
684
 
 
685
        :raise NoSectionError: no such section
 
686
        """
 
687
        ConfigParser.SafeConfigParser.set(self, section, option, value)
 
688
 
 
689
    def write(self, fileobj):
 
690
        """
 
691
        Write a representation of the configuration to the specified file-like
 
692
        object. This output can be parsed by a future ``read()`` call.
 
693
 
 
694
        NOTE: Includes and variable references are ``not`` reconstructed.
 
695
        That is, the configuration data is written in *expanded* form.
 
696
 
 
697
        :Parameters:
 
698
            fileobj : file
 
699
                file-like object to which to write the configuration
 
700
        """
 
701
        ConfigParser.SafeConfigParser.write(self, fileobj)
 
702
 
 
703
    def remove_section(self, section):
 
704
        """
 
705
        Remove a section from the instance. If a section by the given name
 
706
        does not exist, ``NoSectionError`` is raised.
 
707
 
 
708
        :Parameters:
 
709
            section : str
 
710
                name of section to remove
 
711
 
 
712
        :raise NoSectionError: no such section
 
713
        """
 
714
        ConfigParser.SafeConfigParser.remove_section(self, section)
 
715
 
 
716
    def optionxform(self, option_name):
 
717
        """
 
718
        Transforms the option name in ``option_name`` as found in an input
 
719
        file or as passed in by client code to the form that should be used in
 
720
        the internal structures. The default implementation returns a
 
721
        lower-case version of ``option_name``; subclasses may override this or
 
722
        client code can set an attribute of this name on instances to affect
 
723
        this behavior. Setting this to ``str()``, for example, would make
 
724
        option names case sensitive.
 
725
        """
 
726
        return option_name.lower()
 
727
 
 
728
    def __get_optional(self, func, section, option):
 
729
        try:
 
730
            return func(section, option)
 
731
        except ConfigParser.NoOptionError:
 
732
            return None
 
733
        except ConfigParser.NoSectionError:
 
734
            return None
 
735
 
 
736
    def __preprocess(self, fp, name):
 
737
 
 
738
        try:
 
739
            fp.name
 
740
        except AttributeError:
 
741
            try:
 
742
                fp.name = name
 
743
            except TypeError:
 
744
                # Read-only. Oh, well.
 
745
                pass
 
746
            except AttributeError:
 
747
                # Read-only. Oh, well.
 
748
                pass
 
749
 
 
750
        if self.__permit_includes:
 
751
            # Preprocess includes.
 
752
            from grizzled.file import includer
 
753
            tempFile = includer.preprocess(fp)
 
754
            fp = tempFile
 
755
 
 
756
        # Parse the resulting file into a local ConfigParser instance.
 
757
 
 
758
        parsedConfig = ConfigParser.SafeConfigParser()
 
759
 
 
760
        if self.__use_ordered_sections:
 
761
            parsedConfig._sections = OrderedDict()
 
762
 
 
763
        parsedConfig.optionxform = str
 
764
        parsedConfig.read(fp)
 
765
 
 
766
        # Process the variable substitutions.
 
767
 
 
768
        self.__normalizeVariableReferences(parsedConfig)
 
769
        self.__substituteVariables(parsedConfig)
 
770
 
 
771
    def __normalizeVariableReferences(self, sourceConfig):
 
772
        """
 
773
        Convert all section-local variable references (i.e., those that don't
 
774
        specify a section) to fully-qualified references. Necessary for
 
775
        recursive references to work.
 
776
        """
 
777
        simpleVarRefRe = re.compile(SIMPLE_VARIABLE_REF_PATTERN)
 
778
        for section in sourceConfig.sections():
 
779
            for option in sourceConfig.options(section):
 
780
                value = sourceConfig.get(section, option, raw=True)
 
781
                oldValue = value
 
782
                match = simpleVarRefRe.search(value)
 
783
                while match:
 
784
                    value = value[0:match.start(1)] +\
 
785
                            section +\
 
786
                            SECTION_OPTION_DELIM +\
 
787
                            value[match.start(1):]
 
788
                    match = simpleVarRefRe.search(value)
 
789
 
 
790
                sourceConfig.set(section, option, value)
 
791
 
 
792
    def __substituteVariables(self, sourceConfig):
 
793
        mapping = _ConfigDict(sourceConfig, self.__strict_substitution)
 
794
        for section in sourceConfig.sections():
 
795
            mapping.section = section
 
796
            self.add_section(section)
 
797
            for option in sourceConfig.options(section):
 
798
                value = sourceConfig.get(section, option, raw=True)
 
799
 
 
800
                # Repeatedly substitute, to permit recursive references
 
801
 
 
802
                previousValue = ''
 
803
                while value != previousValue:
 
804
                    previousValue = value
 
805
                    value = _ConfigTemplate(value).safe_substitute(mapping)
 
806
 
 
807
                self.set(section, option, value)
 
808
 
 
809
class _ConfigTemplate(string.Template):
 
810
    """
 
811
    Subclass of string.Template that handles our configuration variable
 
812
    reference syntax.
 
813
    """
 
814
    idpattern = VARIABLE_REF_PATTERN
 
815
 
 
816
class _ConfigDict(dict):
 
817
    """
 
818
    Dictionary that knows how to dereference variables within a parsed config.
 
819
    Only used internally.
 
820
    """
 
821
    idPattern = re.compile(VARIABLE_REF_PATTERN)
 
822
    def __init__(self, parsedConfig, strict_substitution):
 
823
        self.__config = parsedConfig
 
824
        self.__strict_substitution = strict_substitution
 
825
        self.section = None
 
826
 
 
827
    def __getitem__(self, key):
 
828
        try:
 
829
            # Match against the ID regular expression. (If the match fails,
 
830
            # it's a bug, since we shouldn't be in here unless it does.)
 
831
 
 
832
            match = self.idPattern.search(key)
 
833
            assert(match)
 
834
 
 
835
            # Now, get the value.
 
836
 
 
837
            default = None
 
838
            if SECTION_OPTION_DELIM in key:
 
839
                if match.group(3):
 
840
                    default = self.__extract_default(match.group(3))
 
841
 
 
842
                section = match.group(1)
 
843
                option = match.group(2)
 
844
            else:
 
845
                section = self.section
 
846
                default = None
 
847
                option = match.group(3)
 
848
                if match.group(4):
 
849
                    default = self.__extract_default(match.group(3))
 
850
 
 
851
            result = self.__value_from_section(section, option)
 
852
 
 
853
        except KeyError:
 
854
            result = default
 
855
 
 
856
        except ConfigParser.NoSectionError:
 
857
            result = default
 
858
 
 
859
        except ConfigParser.NoOptionError:
 
860
            result = default
 
861
 
 
862
        if not result:
 
863
            if self.__strict_substitution:
 
864
                raise NoVariableError, 'No such variable: "%s"' % key
 
865
            else:
 
866
                result = ''
 
867
 
 
868
        return result
 
869
 
 
870
    def __extract_default(self, s):
 
871
        default = s
 
872
        if default:
 
873
            default = default[1:]  # strip leading '?'
 
874
            if len(default) == 0:
 
875
                default = None
 
876
 
 
877
        return default
 
878
 
 
879
    def __value_from_program_section(self, option):
 
880
        return {
 
881
            'cwd'  : os.getcwd(),
 
882
            'now'  : time.strftime('%Y-%m-%d %H:%M:%S'),
 
883
            'name' : os.path.basename(sys.argv[0])
 
884
               }[option]
 
885
 
 
886
    def __value_from_section(self, section, option):
 
887
        result = None
 
888
        if section == 'env':
 
889
            result = os.environ[option]
 
890
            if len(result) == 0:
 
891
                raise KeyError, option
 
892
 
 
893
        elif section == 'program':
 
894
            result = self.__value_from_program_section(option)
 
895
 
 
896
        else:
 
897
            result = self.__config.get(section, option)
 
898
 
 
899
        return result
 
900
 
 
901
# ---------------------------------------------------------------------------
 
902
# Functions
 
903
# ---------------------------------------------------------------------------
 
904
 
 
905
def preprocess(file_or_url, defaults=None):
 
906
    """
 
907
    This function preprocesses a file or URL for a configuration file,
 
908
    processing all includes and substituting all variables. It writes a new
 
909
    configuration file to a temporary file (or specified output file). The
 
910
    new configuration file can be read by a standard ``ConfigParser``
 
911
    object. Thus, this method is useful when you have an extended
 
912
    configuration file that must be passed to a function or object that can
 
913
    only read a standard ``ConfigParser`` file.
 
914
 
 
915
    For example, here's how you might use the Python ``logging`` API with an
 
916
    extended configuration file:
 
917
 
 
918
    .. python::
 
919
 
 
920
        from grizzled.config import Configuration
 
921
        import logging
 
922
 
 
923
        logging.config.fileConfig(Configuration.preprocess('/path/to/config')
 
924
 
 
925
    :Parameters:
 
926
        file_or_url :  str
 
927
           file or URL to read and preprocess
 
928
        defaults : dict
 
929
            defaults to pass through to the config parser
 
930
 
 
931
    :rtype: string
 
932
    :return: Path to a temporary file containing the expanded configuration.
 
933
             The file will be deleted when the program exits, though the caller
 
934
             is free to delete it sooner.
 
935
    """
 
936
    import tempfile
 
937
    import atexit
 
938
 
 
939
    def unlink(path):
 
940
        try:
 
941
            os.unlink(path)
 
942
        except:
 
943
            pass
 
944
 
 
945
    parser = Configuration(use_ordered_sections=True)
 
946
    parser.read(file_or_url)
 
947
    fd, path = tempfile.mkstemp(suffix='.cfg')
 
948
    atexit.register(unlink, path)
 
949
    parser.write(os.fdopen(fd, "w"))
 
950
    return path
 
951
 
 
952
 
 
953
# ---------------------------------------------------------------------------
 
954
# Main program (for testing)
 
955
# ---------------------------------------------------------------------------
 
956
 
 
957
if __name__ == '__main__':
 
958
    import sys
 
959
 
 
960
    format = '%(asctime)s %(name)s %(levelname)s %(message)s'
 
961
    logging.basicConfig(level=logging.DEBUG, format=format)
 
962
 
 
963
    configFile = sys.argv[1]
 
964
    config = Configuration()
 
965
    config.read(configFile)
 
966
 
 
967
    if len(sys.argv) > 2:
 
968
        for var in sys.argv[2:]:
 
969
            (section, option) = var.split(':')
 
970
            val = config.get(section, option, optional=True)
 
971
            print '%s=%s' % (var, val)
 
972
    else:
 
973
        config.write(sys.stdout)