gitcfg: Fix gitcmds test by clearing the cache key during reset()
[git-cola.git] / cola / gitcfg.py
blob3e660738208876d4cbdb68e333d9ef5038426f3f
1 import os
2 import sys
3 import copy
4 import fnmatch
6 from cola import core
7 from cola import git
8 from cola.decorators import memoize
11 @memoize
12 def instance():
13 """Return a static GitConfig instance."""
14 return GitConfig()
17 def _appendifexists(category, path, result):
18 try:
19 mtime = os.stat(path).st_mtime
20 result.append((category, path, mtime))
21 except OSError:
22 pass
25 def _stat_info():
26 data = []
27 # Try /etc/gitconfig as a fallback for the system config
28 userconfig = os.path.expanduser(os.path.join('~', '.gitconfig'))
29 _appendifexists('system', '/etc/gitconfig', data)
30 _appendifexists('user', userconfig, data)
31 _appendifexists('repo', git.instance().git_path('config'), data)
32 return data
35 class GitConfig(object):
36 """Encapsulate access to git-config values."""
38 def __init__(self):
39 self.git = git.instance()
40 self._system = {}
41 self._user = {}
42 self._repo = {}
43 self._all = {}
44 self._cache_key = None
45 self._configs = []
46 self._config_files = {}
47 self._find_config_files()
49 def reset(self):
50 self._system = {}
51 self._user = {}
52 self._repo = {}
53 self._all = {}
54 self._cache_key = None
55 self._configs = []
56 self._config_files = {}
57 self._find_config_files()
59 def user(self):
60 return copy.deepcopy(self._user)
62 def repo(self):
63 return copy.deepcopy(self._repo)
65 def all(self):
66 return copy.deepcopy(self._all)
68 def _find_config_files(self):
69 """
70 Classify git config files into 'system', 'user', and 'repo'.
72 Populates self._configs with a list of the files in
73 reverse-precedence order. self._config_files is populated with
74 {category: path} where category is one of 'system', 'user', or 'repo'.
76 """
77 # Try the git config in git's installation prefix
78 statinfo = _stat_info()
79 self._configs = map(lambda x: x[1], statinfo)
80 self._config_files = {}
81 for (cat, path, mtime) in statinfo:
82 self._config_files[cat] = path
84 def update(self):
85 """Read config values from git."""
86 if self._cached():
87 return
88 self._read_configs()
90 def _cached(self):
91 """
92 Return True when the cache matches.
94 Updates the cache and returns False when the cache does not match.
96 """
97 cache_key = _stat_info()
98 if not self._cache_key or cache_key != self._cache_key:
99 self._cache_key = cache_key
100 return False
101 return True
103 def _read_configs(self):
104 """Read git config value into the system, user and repo dicts."""
105 self.reset()
107 if 'system' in self._config_files:
108 self._system = self.read_config(self._config_files['system'])
110 if 'user' in self._config_files:
111 self._user = self.read_config(self._config_files['user'])
113 if 'repo' in self._config_files:
114 self._repo = self.read_config(self._config_files['repo'])
116 self._all = {}
117 for dct in (self._system, self._user, self._repo):
118 self._all.update(dct)
120 def read_config(self, path):
121 """Return git config data from a path as a dictionary."""
122 dest = {}
123 args = ('--null', '--file', path, '--list')
124 config_lines = self.git.config(*args).split('\0')
125 for line in config_lines:
126 try:
127 k, v = line.split('\n')
128 except:
129 # the user has an invalid entry in their git config
130 continue
131 v = core.decode(v)
132 if v == 'yes':
133 v = 'true'
134 elif v == 'no':
135 v = 'false'
136 if v == 'true' or v == 'false':
137 v = bool(eval(v.title()))
138 try:
139 v = int(eval(v))
140 except:
141 pass
142 dest[k] = v
143 return dest
145 def get(self, key, default=None):
146 """Return the string value for a config key."""
147 self.update()
148 return self._all.get(key, default)
150 def find(self, pat):
151 result = {}
152 for key, val in self._all.items():
153 if fnmatch.fnmatch(key, pat):
154 result[key] = val
155 return result
157 def get_encoding(self, default='utf-8'):
158 return self.get('gui.encoding', default=default)
160 guitool_opts = ('cmd', 'needsfile', 'noconsole', 'norescan', 'confirm',
161 'argprompt', 'revprompt', 'revunmerged', 'title', 'prompt')
163 def get_guitool_opts(self, name):
164 """Return the guitool.<name> namespace as a dict"""
165 keyprefix = 'guitool.' + name + '.'
166 opts = {}
167 for cfg in self.guitool_opts:
168 value = self.get(keyprefix + cfg)
169 if value is None:
170 continue
171 opts[cfg] = value
172 return opts
174 def get_guitool_names(self):
175 cmds = []
176 guitools = self.find('guitool.*.cmd')
177 for name, cmd in guitools.items():
178 name = name[len('guitool.'):-len('.cmd')]
179 cmds.append(name)
180 return cmds