Package grizzled :: Module config
[hide private]
[frames] | no frames]

Source Code for Module grizzled.config

  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) 974