1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2011 The Chromium OS Authors.
6 import configparser as ConfigParser
13 from patman import command
14 from patman import gitutil
15 from patman import tools
17 """Default settings per-project.
19 These are used by _ProjectConfigParser. Settings names should match
20 the "dest" of the option parser from patman.py.
25 "process_tags": "False",
29 class _ProjectConfigParser(ConfigParser.SafeConfigParser):
30 """ConfigParser that handles projects.
32 There are two main goals of this class:
33 - Load project-specific default settings.
34 - Merge general default settings/aliases with project-specific ones.
36 # Sample config used for tests below...
37 >>> from io import StringIO
38 >>> sample_config = '''
40 ... me: Peter P. <likesspiders@example.com>
41 ... enemies: Evil <evil@example.com>
44 ... enemies: Green G. <ugly@example.com>
47 ... enemies: Doc O. <pus@example.com>
53 # Check to make sure that bogus project gets general alias.
54 >>> config = _ProjectConfigParser("zzz")
55 >>> config.readfp(StringIO(sample_config))
56 >>> str(config.get("alias", "enemies"))
57 'Evil <evil@example.com>'
59 # Check to make sure that alias gets overridden by project.
60 >>> config = _ProjectConfigParser("sm")
61 >>> config.readfp(StringIO(sample_config))
62 >>> str(config.get("alias", "enemies"))
63 'Green G. <ugly@example.com>'
65 # Check to make sure that settings get merged with project.
66 >>> config = _ProjectConfigParser("linux")
67 >>> config.readfp(StringIO(sample_config))
68 >>> sorted((str(a), str(b)) for (a, b) in config.items("settings"))
69 [('am_hero', 'True'), ('process_tags', 'False')]
71 # Check to make sure that settings works with unknown project.
72 >>> config = _ProjectConfigParser("unknown")
73 >>> config.readfp(StringIO(sample_config))
74 >>> sorted((str(a), str(b)) for (a, b) in config.items("settings"))
77 def __init__(self, project_name):
78 """Construct _ProjectConfigParser.
80 In addition to standard SafeConfigParser initialization, this also loads
84 project_name: The name of the project.
86 self._project_name = project_name
87 ConfigParser.SafeConfigParser.__init__(self)
89 # Update the project settings in the config based on
90 # the _default_settings global.
91 project_settings = "%s_settings" % project_name
92 if not self.has_section(project_settings):
93 self.add_section(project_settings)
94 project_defaults = _default_settings.get(project_name, {})
95 for setting_name, setting_value in project_defaults.items():
96 self.set(project_settings, setting_name, setting_value)
98 def get(self, section, option, *args, **kwargs):
99 """Extend SafeConfigParser to try project_section before section.
102 See SafeConfigParser.
104 See SafeConfigParser.
107 val = ConfigParser.SafeConfigParser.get(
108 self, "%s_%s" % (self._project_name, section), option,
111 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
112 val = ConfigParser.SafeConfigParser.get(
113 self, section, option, *args, **kwargs
115 return tools.ToUnicode(val)
117 def items(self, section, *args, **kwargs):
118 """Extend SafeConfigParser to add project_section to section.
121 See SafeConfigParser.
123 See SafeConfigParser.
126 has_project_section = False
129 # Get items from the project section
131 project_items = ConfigParser.SafeConfigParser.items(
132 self, "%s_%s" % (self._project_name, section), *args, **kwargs
134 has_project_section = True
135 except ConfigParser.NoSectionError:
138 # Get top-level items
140 top_items = ConfigParser.SafeConfigParser.items(
141 self, section, *args, **kwargs
143 except ConfigParser.NoSectionError:
144 # If neither section exists raise the error on...
145 if not has_project_section:
148 item_dict = dict(top_items)
149 item_dict.update(project_items)
150 return {(tools.ToUnicode(item), tools.ToUnicode(val))
151 for item, val in item_dict.items()}
153 def ReadGitAliases(fname):
154 """Read a git alias file. This is in the form used by git:
156 alias uboot u-boot@lists.denx.de
157 alias wd Wolfgang Denk <wd@denx.de>
160 fname: Filename to read
163 fd = open(fname, 'r', encoding='utf-8')
165 print("Warning: Cannot find alias file '%s'" % fname)
168 re_line = re.compile('alias\s+(\S+)\s+(.*)')
169 for line in fd.readlines():
171 if not line or line[0] == '#':
174 m = re_line.match(line)
176 print("Warning: Alias file line '%s' not understood" % line)
179 list = alias.get(m.group(1), [])
180 for item in m.group(2).split(','):
184 alias[m.group(1)] = list
188 def CreatePatmanConfigFile(config_fname):
189 """Creates a config file under $(HOME)/.patman if it can't find one.
192 config_fname: Default config filename i.e., $(HOME)/.patman
197 name = gitutil.GetDefaultUserName()
199 name = raw_input("Enter name: ")
201 email = gitutil.GetDefaultUserEmail()
204 email = raw_input("Enter email: ")
207 f = open(config_fname, 'w')
209 print("Couldn't create patman config file\n")
216 nxp = Zhikang Zhang <zhikang.zhang@nxp.com>
217 ''' % (name, email), file=f)
220 def _UpdateDefaults(parser, config):
221 """Update the given OptionParser defaults based on config.
223 We'll walk through all of the settings from the parser
224 For each setting we'll look for a default in the option parser.
225 If it's found we'll update the option parser default.
227 The idea here is that the .patman file should be able to update
228 defaults but that command line flags should still have the final
232 parser: An instance of an OptionParser whose defaults will be
234 config: An instance of _ProjectConfigParser that we will query
237 defaults = parser.get_default_values()
238 for name, val in config.items('settings'):
239 if hasattr(defaults, name):
240 default_val = getattr(defaults, name)
241 if isinstance(default_val, bool):
242 val = config.getboolean('settings', name)
243 elif isinstance(default_val, int):
244 val = config.getint('settings', name)
245 parser.set_default(name, val)
247 print("WARNING: Unknown setting %s" % name)
249 def _ReadAliasFile(fname):
250 """Read in the U-Boot git alias file if it exists.
253 fname: Filename to read.
255 if os.path.exists(fname):
257 with open(fname, encoding='utf-8') as fd:
262 if not line or line.startswith('#'):
264 words = line.split(None, 2)
265 if len(words) < 3 or words[0] != 'alias':
267 bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum,
270 alias[words[1]] = [s.strip() for s in words[2].split(',')]
274 def _ReadBouncesFile(fname):
275 """Read in the bounces file if it exists
278 fname: Filename to read.
280 if os.path.exists(fname):
281 with open(fname) as fd:
283 if line.startswith('#'):
285 bounces.add(line.strip())
287 def GetItems(config, section):
288 """Get the items from a section of the config.
291 config: _ProjectConfigParser object containing settings
292 section: name of section to retrieve
295 List of (name, value) tuples for the section
298 return config.items(section)
299 except ConfigParser.NoSectionError as e:
304 def Setup(parser, project_name, config_fname=''):
305 """Set up the settings module by reading config files.
308 parser: The parser to update
309 project_name: Name of project that we're working on; we'll look
310 for sections named "project_section" as well.
311 config_fname: Config filename to read ('' for default)
313 # First read the git alias file if available
314 _ReadAliasFile('doc/git-mailrc')
315 config = _ProjectConfigParser(project_name)
316 if config_fname == '':
317 config_fname = '%s/.patman' % os.getenv('HOME')
319 if not os.path.exists(config_fname):
320 print("No config file found ~/.patman\nCreating one...\n")
321 CreatePatmanConfigFile(config_fname)
323 config.read(config_fname)
325 for name, value in GetItems(config, 'alias'):
326 alias[name] = value.split(',')
328 _ReadBouncesFile('doc/bounces')
329 for name, value in GetItems(config, 'bounces'):
332 _UpdateDefaults(parser, config)
334 # These are the aliases we understand, indexed by alias. Each member is a list.
338 if __name__ == "__main__":