gitstats: Bugfix for the config parsing mechanism
[git-stats.git] / src / git_stats / config.py
blob4fe9ed2548da88180f814d665bf152ad7b91096b
1 #!/usr/bin/env python
3 import os
5 from git import Repo
7 class OptionList:
8 """OptionList class
9 """
11 pass
13 def _readVerse(verse, config, convert_camel_case):
14 """Reads in a verse from a config file and stores the result in config
16 verse: The lines for the current verse, not including the header.
17 config: A dictionary to store the found configurations in.
18 convert_camel_case: Whether to convert camelCase words to dashed_form.
19 """
21 for line in verse:
22 # Remove spaces and newlines
23 stripline = line.lstrip().rstrip()
25 # Stop if the line is not left indented, or empty
26 if stripline == line.rstrip() or not stripline:
27 break
29 # Extract the key:value pair
30 splitline = stripline.split(' = ')
31 key = splitline[0].rstrip()
32 value = splitline[1].lstrip()
34 # Try to convert to a number
35 try:
36 intval = int(value)
37 value = intval
38 except ValueError:
39 pass
41 # Try to convert boolean values
42 if value == "False":
43 value = False
45 if value == "True":
46 value = True
48 if value == "None":
49 value = None
51 # If desired, convert camelCase to dashed_form
52 if convert_camel_case and key.lower() != key:
53 dashed_form = ""
55 for c in key:
56 if c.isupper():
57 dashed_form += '_'
59 dashed_form += c.lower()
61 key = dashed_form
63 # Store the parsed and converted result
64 config[key] = value
66 def read(lines, convert_camel_case=True):
67 """Reads in the specified file
69 Understands 'True', 'False', 'None' and integer values.
70 All camelCase keys are converted to dashed_form.
71 The file is expected to have the following format:
73 [GitStats]
74 [key = value]*
75 <any unindented or empty line>
78 Args:
79 path: The location of the file to read in
81 Returns: A dictionary with the key:value pairs specified in the file.
82 """
84 config = {}
86 while True:
87 # Find our verse
88 try:
89 pos = lines.index("[GitStats]\n")
90 except ValueError:
91 try:
92 pos = lines.index("[GitStats]")
93 except ValueError:
94 return config
96 # Kick off the header
97 pos += 1
98 lines = lines[pos:]
100 # Read in this verse, updates config with the found configurations
101 _readVerse(lines, config, convert_camel_case)
103 return config
105 def _getDefaultConfigPaths():
106 """Returns a list of config paths to use by default
109 paths = []
111 path = "~/.gitconfig"
112 path = os.path.expanduser(path)
113 paths.append(path)
115 repo = Repo(".")
116 path = os.path.join(repo.wd, '.git', 'config')
117 paths.append(path)
119 paths.append("config")
121 return paths
123 def _readConfigPaths(paths, convert_camel_case):
124 """Reads the config files at the specified paths
126 When multiple values for a key are read in, the one that
127 is read in the last is used. (E.g., when reading in the
128 config files, no effort is made to prevent existing
129 values being overwritten).
131 Args:
132 paths: The paths to the config files.
133 convert_camel_case: Whether to convert camelCase to dashed_form.
136 result = {}
138 # Check all the configuration files and parse them
139 for path in paths:
140 if not os.path.isfile(path):
141 continue
143 file = open(path)
144 lines = file.readlines()
146 config = read(lines, convert_camel_case)
148 # Add the found keys to our result
149 for key, value in config.iteritems():
150 result[key] = value
152 return result
154 def readDefaultConfigs(convert_camel_case=True):
155 """Reads all default config files and returns the result
157 When a value is read in multiple times the one read in
158 the last is used. The following files are tried in the
159 listed order:
160 ~/.gitconfig
161 .git/config
162 config
165 paths = _getDefaultConfigPaths()
166 result = _readConfigPaths(paths, convert_camel_case)
168 return result
170 def extractOptions(options, options_list):
171 """Extracts options and returns the result
173 The options are extracted from the specified options and
174 from the 'config' file.
177 opts = readDefaultConfigs()
179 result = OptionList()
181 for opt in options_list:
182 if getattr(options, opt, None) == None:
183 val = opts.get(opt, None)
184 else:
185 val = getattr(options, opt)
187 if val == None and getattr(result, opt, None) != None:
188 continue
190 setattr(result, opt, val)
192 return result
194 def main(args):
195 """Parses all config files and prints the result
197 Runs once with convert_camel_case on, and once with it off.
199 Params:
200 args: Not used
203 result = readDefaultConfigs()
205 print("convert_camel_case is ON:")
206 for key, value in result.iteritems():
207 print("%s: %s" % (key, str(value)))
209 result = readDefaultConfigs(False)
211 print("")
212 print("convertCamelCase is OFF:")
213 for key, value in result.iteritems():
214 print("%s: %s" % (key, str(value)))
216 if __name__ == '__main__':
217 import sys
218 main(sys.argv)