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 = '''
44 ... me: Peter P. <likesspiders@example.com>
45 ... enemies: Evil <evil@example.com>
48 ... enemies: Green G. <ugly@example.com>
51 ... enemies: Doc O. <pus@example.com>
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")
61 u'Evil <evil@example.com>'
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")
67 u'Green G. <ugly@example.com>'
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 [(u'am_hero', u'True'), (u'process_tags', u'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"))
79 [(u'am_hero', u'True')]
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 _to_unicode(self, val):
103 """Make sure a value is of type 'unicode'
106 val: string or unicode object
109 unicode version of val
111 return val if isinstance(val, unicode) else val.decode('utf-8')
113 def get(self, section, option, *args, **kwargs):
114 """Extend SafeConfigParser to try project_section before section.
117 See SafeConfigParser.
119 See SafeConfigParser.
122 val = ConfigParser.SafeConfigParser.get(
123 self, "%s_%s" % (self._project_name, section), option,
126 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
127 val = ConfigParser.SafeConfigParser.get(
128 self, section, option, *args, **kwargs
130 return self._to_unicode(val)
132 def items(self, section, *args, **kwargs):
133 """Extend SafeConfigParser to add project_section to section.
136 See SafeConfigParser.
138 See SafeConfigParser.
141 has_project_section = False
144 # Get items from the project section
146 project_items = ConfigParser.SafeConfigParser.items(
147 self, "%s_%s" % (self._project_name, section), *args, **kwargs
149 has_project_section = True
150 except ConfigParser.NoSectionError:
153 # Get top-level items
155 top_items = ConfigParser.SafeConfigParser.items(
156 self, section, *args, **kwargs
158 except ConfigParser.NoSectionError:
159 # If neither section exists raise the error on...
160 if not has_project_section:
163 item_dict = dict(top_items)
164 item_dict.update(project_items)
165 return {(self._to_unicode(item), self._to_unicode(val))
166 for item, val in item_dict.iteritems()}
168 def ReadGitAliases(fname):
169 """Read a git alias file. This is in the form used by git:
171 alias uboot u-boot@lists.denx.de
172 alias wd Wolfgang Denk <wd@denx.de>
175 fname: Filename to read
178 fd = open(fname, 'r')
180 print("Warning: Cannot find alias file '%s'" % fname)
183 re_line = re.compile('alias\s+(\S+)\s+(.*)')
184 for line in fd.readlines():
186 if not line or line[0] == '#':
189 m = re_line.match(line)
191 print("Warning: Alias file line '%s' not understood" % line)
194 list = alias.get(m.group(1), [])
195 for item in m.group(2).split(','):
199 alias[m.group(1)] = list
203 def CreatePatmanConfigFile(config_fname):
204 """Creates a config file under $(HOME)/.patman if it can't find one.
207 config_fname: Default config filename i.e., $(HOME)/.patman
212 name = gitutil.GetDefaultUserName()
214 name = raw_input("Enter name: ")
216 email = gitutil.GetDefaultUserEmail()
219 email = raw_input("Enter email: ")
222 f = open(config_fname, 'w')
224 print("Couldn't create patman config file\n")
231 nxp = Zhikang Zhang <zhikang.zhang@nxp.com>
232 ''' % (name, email), file=f)
235 def _UpdateDefaults(parser, config):
236 """Update the given OptionParser defaults based on config.
238 We'll walk through all of the settings from the parser
239 For each setting we'll look for a default in the option parser.
240 If it's found we'll update the option parser default.
242 The idea here is that the .patman file should be able to update
243 defaults but that command line flags should still have the final
247 parser: An instance of an OptionParser whose defaults will be
249 config: An instance of _ProjectConfigParser that we will query
252 defaults = parser.get_default_values()
253 for name, val in config.items('settings'):
254 if hasattr(defaults, name):
255 default_val = getattr(defaults, name)
256 if isinstance(default_val, bool):
257 val = config.getboolean('settings', name)
258 elif isinstance(default_val, int):
259 val = config.getint('settings', name)
260 parser.set_default(name, val)
262 print("WARNING: Unknown setting %s" % name)
264 def _ReadAliasFile(fname):
265 """Read in the U-Boot git alias file if it exists.
268 fname: Filename to read.
270 if os.path.exists(fname):
272 with open(fname) as fd:
277 if not line or line.startswith('#'):
279 words = line.split(None, 2)
280 if len(words) < 3 or words[0] != 'alias':
282 bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum,
285 alias[words[1]] = [s.strip() for s in words[2].split(',')]
289 def _ReadBouncesFile(fname):
290 """Read in the bounces file if it exists
293 fname: Filename to read.
295 if os.path.exists(fname):
296 with open(fname) as fd:
298 if line.startswith('#'):
300 bounces.add(line.strip())
302 def GetItems(config, section):
303 """Get the items from a section of the config.
306 config: _ProjectConfigParser object containing settings
307 section: name of section to retrieve
310 List of (name, value) tuples for the section
313 return config.items(section)
314 except ConfigParser.NoSectionError as e:
319 def Setup(parser, project_name, config_fname=''):
320 """Set up the settings module by reading config files.
323 parser: The parser to update
324 project_name: Name of project that we're working on; we'll look
325 for sections named "project_section" as well.
326 config_fname: Config filename to read ('' for default)
328 # First read the git alias file if available
329 _ReadAliasFile('doc/git-mailrc')
330 config = _ProjectConfigParser(project_name)
331 if config_fname == '':
332 config_fname = '%s/.patman' % os.getenv('HOME')
334 if not os.path.exists(config_fname):
335 print("No config file found ~/.patman\nCreating one...\n")
336 CreatePatmanConfigFile(config_fname)
338 config.read(config_fname)
340 for name, value in GetItems(config, 'alias'):
341 alias[name] = value.split(',')
343 _ReadBouncesFile('doc/bounces')
344 for name, value in GetItems(config, 'bounces'):
347 _UpdateDefaults(parser, config)
349 # These are the aliases we understand, indexed by alias. Each member is a list.
353 if __name__ == "__main__":