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.parse_known_args()[0]
237 defaults = vars(defaults)
238 for name, val in config.items('settings'):
240 default_val = 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)
247 print("WARNING: Unknown setting %s" % name)
248 parser.set_defaults(**defaults)
250 def _ReadAliasFile(fname):
251 """Read in the U-Boot git alias file if it exists.
254 fname: Filename to read.
256 if os.path.exists(fname):
258 with open(fname, encoding='utf-8') as fd:
263 if not line or line.startswith('#'):
265 words = line.split(None, 2)
266 if len(words) < 3 or words[0] != 'alias':
268 bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum,
271 alias[words[1]] = [s.strip() for s in words[2].split(',')]
275 def _ReadBouncesFile(fname):
276 """Read in the bounces file if it exists
279 fname: Filename to read.
281 if os.path.exists(fname):
282 with open(fname) as fd:
284 if line.startswith('#'):
286 bounces.add(line.strip())
288 def GetItems(config, section):
289 """Get the items from a section of the config.
292 config: _ProjectConfigParser object containing settings
293 section: name of section to retrieve
296 List of (name, value) tuples for the section
299 return config.items(section)
300 except ConfigParser.NoSectionError as e:
305 def Setup(gitutil, parser, project_name, config_fname=''):
306 """Set up the settings module by reading config files.
309 parser: The parser to update
310 project_name: Name of project that we're working on; we'll look
311 for sections named "project_section" as well.
312 config_fname: Config filename to read ('' for default)
314 # First read the git alias file if available
315 _ReadAliasFile('doc/git-mailrc')
316 config = _ProjectConfigParser(project_name)
317 if config_fname == '':
318 config_fname = '%s/.patman' % os.getenv('HOME')
320 if not os.path.exists(config_fname):
321 print("No config file found ~/.patman\nCreating one...\n")
322 CreatePatmanConfigFile(gitutil, config_fname)
324 config.read(config_fname)
326 for name, value in GetItems(config, 'alias'):
327 alias[name] = value.split(',')
329 _ReadBouncesFile('doc/bounces')
330 for name, value in GetItems(config, 'bounces'):
333 _UpdateDefaults(parser, config)
335 # These are the aliases we understand, indexed by alias. Each member is a list.
339 if __name__ == "__main__":