/src/googlecl/config/parser.py
Python | 173 lines | 80 code | 14 blank | 79 comment | 21 complexity | 929661103924e9a4ba82d36b5295627f MD5 | raw file
1# Copyright (C) 2010 Google Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Enhanced configuration file parser.""" 15 16from __future__ import with_statement 17 18import logging 19import os.path 20 21LOGGER_NAME = __name__ 22LOG = logging.getLogger(LOGGER_NAME) 23 24 25class ConfigParser(object): 26 def __init__(self, config_parser_class): 27 """Initializes the object. 28 29 Args: 30 config_parser: Class that acts as a configuration file parser. 31 """ 32 self.parser = config_parser_class() 33 try: # Because default ConfigParser converts to lower case 34 self.parser.optionxform = str 35 except: 36 pass 37 self.path = None 38 39 def associate(self, config_file_path): 40 """Associates parser with a config file. 41 42 Config file is read from config_file_path as well. 43 """ 44 if os.path.exists(config_file_path): 45 LOG.debug('Reading configuration from %s', config_file_path) 46 self.parser.read(config_file_path) 47 else: 48 LOG.debug('Config file does not exist, starting with empty parser') 49 self.path = config_file_path 50 51 def ensure_basic_options(self, basic_options): 52 """Sets options if they are missing. 53 54 Args: 55 basic_options: Nested dictionary in the form of 56 {section header: {option: value, option: value}, 57 section_header: {option: value, option: value} 58 ...} 59 Returns: 60 True if some of the options in basic_options were not set already, False 61 otherwise. 62 """ 63 made_changes = False 64 for section_name, section_options in basic_options.iteritems(): 65 if not self.parser.has_section(section_name): 66 self.parser.add_section(section_name) 67 missing_options = (set(section_options.keys()) - 68 set(self.parser.options(section_name))) 69 for option in missing_options: 70 self.set(section_name, option, section_options[option]) 71 if missing_options and not made_changes: 72 made_changes = True 73 return made_changes 74 75 def get(self, section, option): 76 """Returns option in section. 77 78 No backup sections or defaults are returned by this function. If the section 79 or option does not exist, the config parser will raise an error. 80 81 Returns: 82 String from config file. 83 """ 84 return self.parser.get(section, option) 85 86 def lazy_get(self, section, option, default=None, option_type=None, 87 backup_section='GENERAL'): 88 """Returns option from config file. 89 90 Tries to retrieve <option> from the given section. If that fails, tries to 91 retrieve the same option from the backup section. If that fails, 92 returns value of <default> parameter. 93 94 Args: 95 section: Name of the section to initially try to retrieve the option from. 96 option: Name of the option to retrieve. 97 default: Value to return if the option does not exist in a searched 98 section. 99 option_type: Conversion function to use on the string, or None to leave as 100 string. For example, if you want an integer value returned, this 101 should be set to int. Not applied to the <default> parameter. 102 backup_section: Section to check if option does not exist in given 103 section. Default 'GENERAL'. 104 105 Returns: 106 Value of the option if it exists in the config file, or value of "default" 107 if option does not exist. 108 """ 109 value = self.safe_get(section, option) 110 if value is None: 111 value = self.safe_get(backup_section, option) 112 if value is None: 113 return default 114 115 if option_type: 116 # bool() function doesn't actually do what we wanted, so intercept it 117 # and replace with comparison 118 if option_type == bool: 119 return value.lower() == 'true' 120 else: 121 return option_type(value) 122 else: 123 return value 124 125 def safe_get(self, section, option): 126 """Returns option if section and option exist, None if they do not.""" 127 if (self.parser.has_section(section) and 128 self.parser.has_option(section, option)): 129 return self.parser.get(section, option) 130 else: 131 return None 132 133 def set(self, section, option, value): 134 """Sets option in a section.""" 135 return self.parser.set(section, option, value) 136 137 def set_missing_default(self, section, option, value): 138 """Sets the option for a section if not defined already. 139 140 If the section does not exist, it is created. 141 142 Args: 143 section: Title of the section to set the option in. 144 option: Option to set. 145 value: Value to give the option. 146 config_path: Path to the configuration file. 147 Default None to use the default path defined in this module. 148 """ 149 if type(value) not in [unicode, str]: 150 value = unicode(value) 151 existing_value = self.safe_get(section, option) 152 153 if existing_value is None: 154 if not self.parser.has_section(section): 155 self.parser.add_section(section) 156 self.set(section, option, value) 157 158 def write_out_parser(self, path=None): 159 """Writes options in config parser to file. 160 161 Args: 162 path: Path to write to. Default None for path associated with instance. 163 164 Raises: 165 IOError: No path given and instance is not associated with a path. 166 """ 167 if not path: 168 if self.path: 169 path = self.path 170 else: 171 raise IOError('No path given or associated') 172 with open(path, 'w') as config_file: 173 self.parser.write(config_file)