1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2011 The Chromium OS Authors.
5 from __future__ import print_function
8 import configparser as ConfigParser
18 """Default settings per-project.
20 These are used by _ProjectConfigParser. Settings names should match
21 the "dest" of the option parser from patman.py.
26 "process_tags": "False",
30 class _ProjectConfigParser(ConfigParser.SafeConfigParser):
31 """ConfigParser that handles projects.
33 There are two main goals of this class:
34 - Load project-specific default settings.
35 - Merge general default settings/aliases with project-specific ones.
37 # Sample config used for tests below...
39 ... from StringIO import StringIO
40 ... except ImportError:
41 ... from io import StringIO
42 >>> sample_config = '''
57 # Check to make sure that bogus project gets general alias.
58 >>> config = _ProjectConfigParser("zzz")
59 >>> config.readfp(StringIO(sample_config))
60 >>> config.get("alias", "enemies")
63 # Check to make sure that alias gets overridden by project.
64 >>> config = _ProjectConfigParser("sm")
65 >>> config.readfp(StringIO(sample_config))
66 >>> config.get("alias", "enemies")
69 # Check to make sure that settings get merged with project.
70 >>> config = _ProjectConfigParser("linux")
71 >>> config.readfp(StringIO(sample_config))
72 >>> sorted(config.items("settings"))
73 [('am_hero', 'True'), ('process_tags', 'False')]
75 # Check to make sure that settings works with unknown project.
76 >>> config = _ProjectConfigParser("unknown")
77 >>> config.readfp(StringIO(sample_config))
78 >>> sorted(config.items("settings"))
81 def __init__(self, project_name):
82 """Construct _ProjectConfigParser.
84 In addition to standard SafeConfigParser initialization, this also loads
88 project_name: The name of the project.
90 self._project_name = project_name
91 ConfigParser.SafeConfigParser.__init__(self)
93 # Update the project settings in the config based on
94 # the _default_settings global.
95 project_settings = "%s_settings" % project_name
96 if not self.has_section(project_settings):
97 self.add_section(project_settings)
98 project_defaults = _default_settings.get(project_name, {})
99 for setting_name, setting_value in project_defaults.items():
100 self.set(project_settings, setting_name, setting_value)
102 def get(self, section, option, *args, **kwargs):
103 """Extend SafeConfigParser to try project_section before section.
106 See SafeConfigParser.
108 See SafeConfigParser.
111 return ConfigParser.SafeConfigParser.get(
112 self, "%s_%s" % (self._project_name, section), option,
115 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
116 return ConfigParser.SafeConfigParser.get(
117 self, section, option, *args, **kwargs
120 def items(self, section, *args, **kwargs):
121 """Extend SafeConfigParser to add project_section to section.
124 See SafeConfigParser.
126 See SafeConfigParser.
129 has_project_section = False
132 # Get items from the project section
134 project_items = ConfigParser.SafeConfigParser.items(
135 self, "%s_%s" % (self._project_name, section), *args, **kwargs
137 has_project_section = True
138 except ConfigParser.NoSectionError:
141 # Get top-level items
143 top_items = ConfigParser.SafeConfigParser.items(
144 self, section, *args, **kwargs
146 except ConfigParser.NoSectionError:
147 # If neither section exists raise the error on...
148 if not has_project_section:
151 item_dict = dict(top_items)
152 item_dict.update(project_items)
153 return item_dict.items()
155 def ReadGitAliases(fname):
156 """Read a git alias file. This is in the form used by git:
162 fname: Filename to read
165 fd = open(fname, 'r')
167 print("Warning: Cannot find alias file '%s'" % fname)
170 re_line = re.compile('alias\s+(\S+)\s+(.*)')
171 for line in fd.readlines():
173 if not line or line[0] == '#':
176 m = re_line.match(line)
178 print("Warning: Alias file line '%s' not understood" % line)
181 list = alias.get(m.group(1), [])
182 for item in m.group(2).split(','):
186 alias[m.group(1)] = list
190 def CreatePatmanConfigFile(config_fname):
191 """Creates a config file under $(HOME)/.patman if it can't find one.
194 config_fname: Default config filename i.e., $(HOME)/.patman
199 name = gitutil.GetDefaultUserName()
201 name = raw_input("Enter name: ")
203 email = gitutil.GetDefaultUserEmail()
206 email = raw_input("Enter email: ")
209 f = open(config_fname, 'w')
211 print("Couldn't create patman config file\n")
219 ''' % (name, email), file=f)
222 def _UpdateDefaults(parser, config):
223 """Update the given OptionParser defaults based on config.
225 We'll walk through all of the settings from the parser
226 For each setting we'll look for a default in the option parser.
227 If it's found we'll update the option parser default.
229 The idea here is that the .patman file should be able to update
230 defaults but that command line flags should still have the final
234 parser: An instance of an OptionParser whose defaults will be
236 config: An instance of _ProjectConfigParser that we will query
239 defaults = parser.get_default_values()
240 for name, val in config.items('settings'):
241 if hasattr(defaults, name):
242 default_val = getattr(defaults, name)
243 if isinstance(default_val, bool):
244 val = config.getboolean('settings', name)
245 elif isinstance(default_val, int):
246 val = config.getint('settings', name)
247 parser.set_default(name, val)
249 print("WARNING: Unknown setting %s" % name)
251 def _ReadAliasFile(fname):
252 """Read in the U-Boot git alias file if it exists.
255 fname: Filename to read.
257 if os.path.exists(fname):
259 with open(fname) as fd:
264 if not line or line.startswith('#'):
266 words = line.split(None, 2)
267 if len(words) < 3 or words[0] != 'alias':
269 bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum,
272 alias[words[1]] = [s.strip() for s in words[2].split(',')]
276 def _ReadBouncesFile(fname):
277 """Read in the bounces file if it exists
280 fname: Filename to read.
282 if os.path.exists(fname):
283 with open(fname) as fd:
285 if line.startswith('#'):
287 bounces.add(line.strip())
289 def GetItems(config, section):
290 """Get the items from a section of the config.
293 config: _ProjectConfigParser object containing settings
294 section: name of section to retrieve
297 List of (name, value) tuples for the section
300 return config.items(section)
301 except ConfigParser.NoSectionError as e:
306 def Setup(parser, project_name, config_fname=''):
307 """Set up the settings module by reading config files.
310 parser: The parser to update
311 project_name: Name of project that we're working on; we'll look
312 for sections named "project_section" as well.
313 config_fname: Config filename to read ('' for default)
315 # First read the git alias file if available
316 _ReadAliasFile('doc/git-mailrc')
317 config = _ProjectConfigParser(project_name)
318 if config_fname == '':
319 config_fname = '%s/.patman' % os.getenv('HOME')
321 if not os.path.exists(config_fname):
322 print("No config file found ~/.patman\nCreating one...\n")
323 CreatePatmanConfigFile(config_fname)
325 config.read(config_fname)
327 for name, value in GetItems(config, 'alias'):
328 alias[name] = value.split(',')
330 _ReadBouncesFile('doc/bounces')
331 for name, value in GetItems(config, 'bounces'):
334 _UpdateDefaults(parser, config)
336 # These are the aliases we understand, indexed by alias. Each member is a list.
340 if __name__ == "__main__":