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 tools
16 """Default settings per-project.
18 These are used by _ProjectConfigParser. Settings names should match
19 the "dest" of the option parser from patman.py.
24 "process_tags": "False",
28 class _ProjectConfigParser(ConfigParser.SafeConfigParser):
29 """ConfigParser that handles projects.
31 There are two main goals of this class:
32 - Load project-specific default settings.
33 - Merge general default settings/aliases with project-specific ones.
35 # Sample config used for tests below...
36 >>> from io import StringIO
37 >>> sample_config = '''
39 ... me: Peter P. <likesspiders@example.com>
40 ... enemies: Evil <evil@example.com>
43 ... enemies: Green G. <ugly@example.com>
46 ... enemies: Doc O. <pus@example.com>
52 # Check to make sure that bogus project gets general alias.
53 >>> config = _ProjectConfigParser("zzz")
54 >>> config.readfp(StringIO(sample_config))
55 >>> str(config.get("alias", "enemies"))
56 'Evil <evil@example.com>'
58 # Check to make sure that alias gets overridden by project.
59 >>> config = _ProjectConfigParser("sm")
60 >>> config.readfp(StringIO(sample_config))
61 >>> str(config.get("alias", "enemies"))
62 'Green G. <ugly@example.com>'
64 # Check to make sure that settings get merged with project.
65 >>> config = _ProjectConfigParser("linux")
66 >>> config.readfp(StringIO(sample_config))
67 >>> sorted((str(a), str(b)) for (a, b) in config.items("settings"))
68 [('am_hero', 'True'), ('process_tags', 'False')]
70 # Check to make sure that settings works with unknown project.
71 >>> config = _ProjectConfigParser("unknown")
72 >>> config.readfp(StringIO(sample_config))
73 >>> sorted((str(a), str(b)) for (a, b) in config.items("settings"))
76 def __init__(self, project_name):
77 """Construct _ProjectConfigParser.
79 In addition to standard SafeConfigParser initialization, this also loads
83 project_name: The name of the project.
85 self._project_name = project_name
86 ConfigParser.SafeConfigParser.__init__(self)
88 # Update the project settings in the config based on
89 # the _default_settings global.
90 project_settings = "%s_settings" % project_name
91 if not self.has_section(project_settings):
92 self.add_section(project_settings)
93 project_defaults = _default_settings.get(project_name, {})
94 for setting_name, setting_value in project_defaults.items():
95 self.set(project_settings, setting_name, setting_value)
97 def get(self, section, option, *args, **kwargs):
98 """Extend SafeConfigParser to try project_section before section.
101 See SafeConfigParser.
103 See SafeConfigParser.
106 val = ConfigParser.SafeConfigParser.get(
107 self, "%s_%s" % (self._project_name, section), option,
110 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
111 val = ConfigParser.SafeConfigParser.get(
112 self, section, option, *args, **kwargs
114 return tools.ToUnicode(val)
116 def items(self, section, *args, **kwargs):
117 """Extend SafeConfigParser to add project_section to section.
120 See SafeConfigParser.
122 See SafeConfigParser.
125 has_project_section = False
128 # Get items from the project section
130 project_items = ConfigParser.SafeConfigParser.items(
131 self, "%s_%s" % (self._project_name, section), *args, **kwargs
133 has_project_section = True
134 except ConfigParser.NoSectionError:
137 # Get top-level items
139 top_items = ConfigParser.SafeConfigParser.items(
140 self, section, *args, **kwargs
142 except ConfigParser.NoSectionError:
143 # If neither section exists raise the error on...
144 if not has_project_section:
147 item_dict = dict(top_items)
148 item_dict.update(project_items)
149 return {(tools.ToUnicode(item), tools.ToUnicode(val))
150 for item, val in item_dict.items()}
152 def ReadGitAliases(fname):
153 """Read a git alias file. This is in the form used by git:
155 alias uboot u-boot@lists.denx.de
156 alias wd Wolfgang Denk <wd@denx.de>
159 fname: Filename to read
162 fd = open(fname, 'r', encoding='utf-8')
164 print("Warning: Cannot find alias file '%s'" % fname)
167 re_line = re.compile('alias\s+(\S+)\s+(.*)')
168 for line in fd.readlines():
170 if not line or line[0] == '#':
173 m = re_line.match(line)
175 print("Warning: Alias file line '%s' not understood" % line)
178 list = alias.get(m.group(1), [])
179 for item in m.group(2).split(','):
183 alias[m.group(1)] = list
187 def CreatePatmanConfigFile(gitutil, config_fname):
188 """Creates a config file under $(HOME)/.patman if it can't find one.
191 config_fname: Default config filename i.e., $(HOME)/.patman
196 name = gitutil.GetDefaultUserName()
198 name = raw_input("Enter name: ")
200 email = gitutil.GetDefaultUserEmail()
203 email = raw_input("Enter email: ")
206 f = open(config_fname, 'w')
208 print("Couldn't create patman config file\n")
215 nxp = Zhikang Zhang <zhikang.zhang@nxp.com>
216 ''' % (name, email), file=f)
219 def _UpdateDefaults(parser, config):
220 """Update the given OptionParser defaults based on config.
222 We'll walk through all of the settings from the parser
223 For each setting we'll look for a default in the option parser.
224 If it's found we'll update the option parser default.
226 The idea here is that the .patman file should be able to update
227 defaults but that command line flags should still have the final
231 parser: An instance of an OptionParser whose defaults will be
233 config: An instance of _ProjectConfigParser that we will query
236 defaults = parser.get_default_values()
237 for name, val in config.items('settings'):
238 if hasattr(defaults, name):
239 default_val = getattr(defaults, name)
240 if isinstance(default_val, bool):
241 val = config.getboolean('settings', name)
242 elif isinstance(default_val, int):
243 val = config.getint('settings', name)
244 parser.set_default(name, val)
246 print("WARNING: Unknown setting %s" % name)
248 def _ReadAliasFile(fname):
249 """Read in the U-Boot git alias file if it exists.
252 fname: Filename to read.
254 if os.path.exists(fname):
256 with open(fname, encoding='utf-8') as fd:
261 if not line or line.startswith('#'):
263 words = line.split(None, 2)
264 if len(words) < 3 or words[0] != 'alias':
266 bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum,
269 alias[words[1]] = [s.strip() for s in words[2].split(',')]
273 def _ReadBouncesFile(fname):
274 """Read in the bounces file if it exists
277 fname: Filename to read.
279 if os.path.exists(fname):
280 with open(fname) as fd:
282 if line.startswith('#'):
284 bounces.add(line.strip())
286 def GetItems(config, section):
287 """Get the items from a section of the config.
290 config: _ProjectConfigParser object containing settings
291 section: name of section to retrieve
294 List of (name, value) tuples for the section
297 return config.items(section)
298 except ConfigParser.NoSectionError as e:
303 def Setup(gitutil, parser, project_name, config_fname=''):
304 """Set up the settings module by reading config files.
307 parser: The parser to update
308 project_name: Name of project that we're working on; we'll look
309 for sections named "project_section" as well.
310 config_fname: Config filename to read ('' for default)
312 # First read the git alias file if available
313 _ReadAliasFile('doc/git-mailrc')
314 config = _ProjectConfigParser(project_name)
315 if config_fname == '':
316 config_fname = '%s/.patman' % os.getenv('HOME')
318 if not os.path.exists(config_fname):
319 print("No config file found ~/.patman\nCreating one...\n")
320 CreatePatmanConfigFile(gitutil, config_fname)
322 config.read(config_fname)
324 for name, value in GetItems(config, 'alias'):
325 alias[name] = value.split(',')
327 _ReadBouncesFile('doc/bounces')
328 for name, value in GetItems(config, 'bounces'):
331 _UpdateDefaults(parser, config)
333 # These are the aliases we understand, indexed by alias. Each member is a list.
337 if __name__ == "__main__":