Package rats :: Module flatconfig
[hide private]
[frames] | no frames]

Source Code for Module rats.flatconfig

  1  #!/usr/bin/env python 
  2  """ 
  3  Simple configuration file/line parser. 
  4  """ 
  5  import shlex 
  6   
7 -class FileNotFoundError(Exception):
8 """ 9 Exception raised when a config file is not found. 10 """ 11 pass
12
13 -class NoSuchOptionError(Exception):
14 """ 15 Exception raised when a specified option is not found. 16 """ 17 pass
18
19 -class ParsingError(Exception):
20 """ 21 Exception raised when errors occur attempting to parse a file. 22 """ 23 pass
24 25 # def parse_line(txt): 26 # """ 27 # Parses one line. 28 # Separates correctly the arguments when using quotes. 29 # """ 30 # try: 31 # return shlex.split(txt) 32 # except ValueError, e: 33 # raise ParsingError("Error parsing text '%s': %s" %(txt, e.message)) 34
35 -class ConfigParser(object):
36 """ 37 Parses flat key-value pairs in a text file. 38 39 Removes quotes from quoted strings. 40 41 A list of tuples makes possible the use of many times the same key. 42 That is lost if you convert the result to a dict. 43 44 This ConfigParser is different from the one from the ConfigParser module, 45 since it does not use the concept of sections. Also, there can be many options 46 with the same key name. This class is partly insprired from ConfigParser. 47 """ 48 STRIPPED = "\"' " # quotes are stripped. 49 ASSIGNATION_OPERATORS = "=:" # key/values are separated by those 50 COMMENTERS = "#" 51
52 - def __init__(self):
53 self.file_name = None 54 self._options = [] # list of (key, value) tuples 55 self.verbose = False
56
57 - def read(self, file_name):
58 """ 59 Attempts to read a file and parse its configuration entries. 60 It might throw a ParsingError. 61 """ 62 self._read(file_name)
63
64 - def options(self):
65 """ 66 Returns a list of options available. 67 """ 68 return dict(self._options).keys()
69
70 - def has_option(self, option):
71 """ 72 Checks for the existence of a given option. 73 """ 74 return option in self.options()
75
76 - def get(self, option, cast=str):
77 """ 78 Get an option value as a string. 79 80 The cast argument allows you to cast the type of the value to int, float or bool. 81 Just pass it a type. 82 83 Might raise a NoSuchOptionError if the option is not found. 84 Might raise a ValueError if the value is not of the given type. 85 86 If a key is defined many times, behavious is undefined. It it likely to return 87 the last item with that key. 88 89 Note that the accepted values for the option are "1", "yes", "true", and "on", which cause this method to return True, and "0", "no", "false", and "off", which cause it to return False. These string values are checked in a case-insensitive manner. Any other value will cause it to raise ValueError. 90 """ 91 try: 92 value = dict(self._options)[option] 93 except KeyError, e: 94 raise NoSuchOptionError("Could not find option named %s." % (e.message)) 95 return self.cast(value, cast)
96 97 _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True, 98 '0': False, 'no': False, 'false': False, 'off': False} 99
100 - def get_list(self, option, cast=str):
101 """ 102 Returns a list of values for a given key name. 103 """ 104 ret = [] 105 for k, v in self._options: 106 if k == option: 107 ret.append(self.cast(v, cast)) 108 if len(ret) == 0: 109 raise NoSuchOptionError("Could not find option named %s." % (option)) 110 return ret
111
112 - def cast(self, value, cast):
113 """ 114 Casts a value to a given type. 115 Type can be int, float, str or bool. 116 """ 117 if cast in [float, int]: 118 return cast(value) 119 elif cast is bool: 120 if value.lower() not in self._boolean_states: 121 raise ValueError("Value %s is not a valid boolean." % (value)) 122 return self._boolean_states[value.lower()] 123 else: # str 124 return value
125
126 - def items(self):
127 """ 128 Return a list of tuples with (name, value) for each option. 129 """ 130 return self._options
131
132 - def _read(self, file_name):
133 self.file_name = file_name 134 self._options = [] 135 try: 136 f = open(file_name, 'r') 137 except IOError, e: 138 raise FileNotFoundError("Could not open file '%s': %s" % (file_name, e.message)) 139 try: 140 lexer = shlex.shlex(f, file_name, posix=False) 141 lexer.commenters = self.COMMENTERS # default 142 #lexer.wordchars += "|/.,$^\\():;@-+?<>!%&*`~" 143 key = None 144 for token in lexer: 145 if token in self.ASSIGNATION_OPERATORS: 146 if key is None: # assignation 147 raise ParsingError("No key defined, but found '%s' in file '%s' on line %d. Try using quotes." % (token, file_name, lexer.lineno)) 148 else: 149 if key is None: # key 150 key = token.strip(self.STRIPPED) 151 #if key in ret.keys(): # TODO: allow non-uniques 152 # raise ParsingError("Key %s already defined but found again in file '%s' on line %d." % (key, file_name, lexer.lineno)) 153 if self.verbose: 154 print("Found key '%s'" % (key)) 155 else: # value 156 value = token.strip(self.STRIPPED) 157 #ret[key] = value 158 self._options.append((key, value)) 159 if self.verbose: 160 print("Found value '%s' for key '%s'" % (value, key)) 161 key = None 162 if key is not None: 163 raise ParsingError("No value for key %s" % (key)) 164 except ValueError, e: 165 raise ParsingError("Error parsing file '%s': %s" % (file_name, e.message)) 166 f.close() 167 return self._options
168 169 # if __name__ == "__main__": 170 # # this is just for test purposes. 171 # # better tests are in the test/ directory 172 # """ 173 # # EXAMPLE FILE: 174 # # comment 175 # hello = "ma belle" 176 # "you are" = "the best" 177 # banane = blue 178 # fraise = "je taime" 179 # """ 180 # print(parse_line("c -l -o qwerqwe 'hello dude'")) 181 # 182 # p = ConfigParser() 183 # print(p.read('/var/tmp/cfg')) 184