10d24de9dSSimon Glass# Copyright (c) 2011 The Chromium OS Authors. 20d24de9dSSimon Glass# 31a459660SWolfgang Denk# SPDX-License-Identifier: GPL-2.0+ 40d24de9dSSimon Glass# 50d24de9dSSimon Glass 60d24de9dSSimon Glassimport ConfigParser 70d24de9dSSimon Glassimport os 80d24de9dSSimon Glassimport re 90d24de9dSSimon Glass 100d24de9dSSimon Glassimport command 1187d65558SVikram Narayananimport gitutil 120d24de9dSSimon Glass 13a1dcee84SDoug Anderson"""Default settings per-project. 14a1dcee84SDoug Anderson 15a1dcee84SDoug AndersonThese are used by _ProjectConfigParser. Settings names should match 16a1dcee84SDoug Andersonthe "dest" of the option parser from patman.py. 17a1dcee84SDoug Anderson""" 18a1dcee84SDoug Anderson_default_settings = { 19a1dcee84SDoug Anderson "u-boot": {}, 20a1dcee84SDoug Anderson "linux": { 21a1dcee84SDoug Anderson "process_tags": "False", 22a1dcee84SDoug Anderson } 23a1dcee84SDoug Anderson} 24a1dcee84SDoug Anderson 25a1dcee84SDoug Andersonclass _ProjectConfigParser(ConfigParser.SafeConfigParser): 26a1dcee84SDoug Anderson """ConfigParser that handles projects. 27a1dcee84SDoug Anderson 28a1dcee84SDoug Anderson There are two main goals of this class: 29a1dcee84SDoug Anderson - Load project-specific default settings. 30a1dcee84SDoug Anderson - Merge general default settings/aliases with project-specific ones. 31a1dcee84SDoug Anderson 32a1dcee84SDoug Anderson # Sample config used for tests below... 33a1dcee84SDoug Anderson >>> import StringIO 34a1dcee84SDoug Anderson >>> sample_config = ''' 35a1dcee84SDoug Anderson ... [alias] 36a1dcee84SDoug Anderson ... me: Peter P. <likesspiders@example.com> 37a1dcee84SDoug Anderson ... enemies: Evil <evil@example.com> 38a1dcee84SDoug Anderson ... 39a1dcee84SDoug Anderson ... [sm_alias] 40a1dcee84SDoug Anderson ... enemies: Green G. <ugly@example.com> 41a1dcee84SDoug Anderson ... 42a1dcee84SDoug Anderson ... [sm2_alias] 43a1dcee84SDoug Anderson ... enemies: Doc O. <pus@example.com> 44a1dcee84SDoug Anderson ... 45a1dcee84SDoug Anderson ... [settings] 46a1dcee84SDoug Anderson ... am_hero: True 47a1dcee84SDoug Anderson ... ''' 48a1dcee84SDoug Anderson 49a1dcee84SDoug Anderson # Check to make sure that bogus project gets general alias. 50a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("zzz") 51a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 52a1dcee84SDoug Anderson >>> config.get("alias", "enemies") 53a1dcee84SDoug Anderson 'Evil <evil@example.com>' 54a1dcee84SDoug Anderson 55a1dcee84SDoug Anderson # Check to make sure that alias gets overridden by project. 56a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("sm") 57a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 58a1dcee84SDoug Anderson >>> config.get("alias", "enemies") 59a1dcee84SDoug Anderson 'Green G. <ugly@example.com>' 60a1dcee84SDoug Anderson 61a1dcee84SDoug Anderson # Check to make sure that settings get merged with project. 62a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("linux") 63a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 64a1dcee84SDoug Anderson >>> sorted(config.items("settings")) 65a1dcee84SDoug Anderson [('am_hero', 'True'), ('process_tags', 'False')] 66a1dcee84SDoug Anderson 67a1dcee84SDoug Anderson # Check to make sure that settings works with unknown project. 68a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("unknown") 69a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 70a1dcee84SDoug Anderson >>> sorted(config.items("settings")) 71a1dcee84SDoug Anderson [('am_hero', 'True')] 72a1dcee84SDoug Anderson """ 73a1dcee84SDoug Anderson def __init__(self, project_name): 74a1dcee84SDoug Anderson """Construct _ProjectConfigParser. 75a1dcee84SDoug Anderson 76a1dcee84SDoug Anderson In addition to standard SafeConfigParser initialization, this also loads 77a1dcee84SDoug Anderson project defaults. 78a1dcee84SDoug Anderson 79a1dcee84SDoug Anderson Args: 80a1dcee84SDoug Anderson project_name: The name of the project. 81a1dcee84SDoug Anderson """ 82a1dcee84SDoug Anderson self._project_name = project_name 83a1dcee84SDoug Anderson ConfigParser.SafeConfigParser.__init__(self) 84a1dcee84SDoug Anderson 85a1dcee84SDoug Anderson # Update the project settings in the config based on 86a1dcee84SDoug Anderson # the _default_settings global. 87a1dcee84SDoug Anderson project_settings = "%s_settings" % project_name 88a1dcee84SDoug Anderson if not self.has_section(project_settings): 89a1dcee84SDoug Anderson self.add_section(project_settings) 90a1dcee84SDoug Anderson project_defaults = _default_settings.get(project_name, {}) 91a1dcee84SDoug Anderson for setting_name, setting_value in project_defaults.iteritems(): 92a1dcee84SDoug Anderson self.set(project_settings, setting_name, setting_value) 93a1dcee84SDoug Anderson 94a1dcee84SDoug Anderson def get(self, section, option, *args, **kwargs): 95a1dcee84SDoug Anderson """Extend SafeConfigParser to try project_section before section. 96a1dcee84SDoug Anderson 97a1dcee84SDoug Anderson Args: 98a1dcee84SDoug Anderson See SafeConfigParser. 99a1dcee84SDoug Anderson Returns: 100a1dcee84SDoug Anderson See SafeConfigParser. 101a1dcee84SDoug Anderson """ 102a1dcee84SDoug Anderson try: 103a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 104a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), option, 105a1dcee84SDoug Anderson *args, **kwargs 106a1dcee84SDoug Anderson ) 107a1dcee84SDoug Anderson except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): 108a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 109a1dcee84SDoug Anderson self, section, option, *args, **kwargs 110a1dcee84SDoug Anderson ) 111a1dcee84SDoug Anderson 112a1dcee84SDoug Anderson def items(self, section, *args, **kwargs): 113a1dcee84SDoug Anderson """Extend SafeConfigParser to add project_section to section. 114a1dcee84SDoug Anderson 115a1dcee84SDoug Anderson Args: 116a1dcee84SDoug Anderson See SafeConfigParser. 117a1dcee84SDoug Anderson Returns: 118a1dcee84SDoug Anderson See SafeConfigParser. 119a1dcee84SDoug Anderson """ 120a1dcee84SDoug Anderson project_items = [] 121a1dcee84SDoug Anderson has_project_section = False 122a1dcee84SDoug Anderson top_items = [] 123a1dcee84SDoug Anderson 124a1dcee84SDoug Anderson # Get items from the project section 125a1dcee84SDoug Anderson try: 126a1dcee84SDoug Anderson project_items = ConfigParser.SafeConfigParser.items( 127a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), *args, **kwargs 128a1dcee84SDoug Anderson ) 129a1dcee84SDoug Anderson has_project_section = True 130a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 131a1dcee84SDoug Anderson pass 132a1dcee84SDoug Anderson 133a1dcee84SDoug Anderson # Get top-level items 134a1dcee84SDoug Anderson try: 135a1dcee84SDoug Anderson top_items = ConfigParser.SafeConfigParser.items( 136a1dcee84SDoug Anderson self, section, *args, **kwargs 137a1dcee84SDoug Anderson ) 138a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 139a1dcee84SDoug Anderson # If neither section exists raise the error on... 140a1dcee84SDoug Anderson if not has_project_section: 141a1dcee84SDoug Anderson raise 142a1dcee84SDoug Anderson 143a1dcee84SDoug Anderson item_dict = dict(top_items) 144a1dcee84SDoug Anderson item_dict.update(project_items) 145a1dcee84SDoug Anderson return item_dict.items() 146a1dcee84SDoug Anderson 1470d24de9dSSimon Glassdef ReadGitAliases(fname): 1480d24de9dSSimon Glass """Read a git alias file. This is in the form used by git: 1490d24de9dSSimon Glass 1500d24de9dSSimon Glass alias uboot u-boot@lists.denx.de 1510d24de9dSSimon Glass alias wd Wolfgang Denk <wd@denx.de> 1520d24de9dSSimon Glass 1530d24de9dSSimon Glass Args: 1540d24de9dSSimon Glass fname: Filename to read 1550d24de9dSSimon Glass """ 1560d24de9dSSimon Glass try: 1570d24de9dSSimon Glass fd = open(fname, 'r') 1580d24de9dSSimon Glass except IOError: 1590d24de9dSSimon Glass print "Warning: Cannot find alias file '%s'" % fname 1600d24de9dSSimon Glass return 1610d24de9dSSimon Glass 1620d24de9dSSimon Glass re_line = re.compile('alias\s+(\S+)\s+(.*)') 1630d24de9dSSimon Glass for line in fd.readlines(): 1640d24de9dSSimon Glass line = line.strip() 1650d24de9dSSimon Glass if not line or line[0] == '#': 1660d24de9dSSimon Glass continue 1670d24de9dSSimon Glass 1680d24de9dSSimon Glass m = re_line.match(line) 1690d24de9dSSimon Glass if not m: 1700d24de9dSSimon Glass print "Warning: Alias file line '%s' not understood" % line 1710d24de9dSSimon Glass continue 1720d24de9dSSimon Glass 1730d24de9dSSimon Glass list = alias.get(m.group(1), []) 1740d24de9dSSimon Glass for item in m.group(2).split(','): 1750d24de9dSSimon Glass item = item.strip() 1760d24de9dSSimon Glass if item: 1770d24de9dSSimon Glass list.append(item) 1780d24de9dSSimon Glass alias[m.group(1)] = list 1790d24de9dSSimon Glass 1800d24de9dSSimon Glass fd.close() 1810d24de9dSSimon Glass 18287d65558SVikram Narayanandef CreatePatmanConfigFile(config_fname): 18387d65558SVikram Narayanan """Creates a config file under $(HOME)/.patman if it can't find one. 18487d65558SVikram Narayanan 18587d65558SVikram Narayanan Args: 18687d65558SVikram Narayanan config_fname: Default config filename i.e., $(HOME)/.patman 18787d65558SVikram Narayanan 18887d65558SVikram Narayanan Returns: 18987d65558SVikram Narayanan None 19087d65558SVikram Narayanan """ 19187d65558SVikram Narayanan name = gitutil.GetDefaultUserName() 19287d65558SVikram Narayanan if name == None: 19387d65558SVikram Narayanan name = raw_input("Enter name: ") 19487d65558SVikram Narayanan 19587d65558SVikram Narayanan email = gitutil.GetDefaultUserEmail() 19687d65558SVikram Narayanan 19787d65558SVikram Narayanan if email == None: 19887d65558SVikram Narayanan email = raw_input("Enter email: ") 19987d65558SVikram Narayanan 20087d65558SVikram Narayanan try: 20187d65558SVikram Narayanan f = open(config_fname, 'w') 20287d65558SVikram Narayanan except IOError: 20387d65558SVikram Narayanan print "Couldn't create patman config file\n" 20487d65558SVikram Narayanan raise 20587d65558SVikram Narayanan 20687d65558SVikram Narayanan print >>f, "[alias]\nme: %s <%s>" % (name, email) 20787d65558SVikram Narayanan f.close(); 20887d65558SVikram Narayanan 2098568baedSDoug Andersondef _UpdateDefaults(parser, config): 2108568baedSDoug Anderson """Update the given OptionParser defaults based on config. 2118568baedSDoug Anderson 2128568baedSDoug Anderson We'll walk through all of the settings from the parser 2138568baedSDoug Anderson For each setting we'll look for a default in the option parser. 2148568baedSDoug Anderson If it's found we'll update the option parser default. 2158568baedSDoug Anderson 2168568baedSDoug Anderson The idea here is that the .patman file should be able to update 2178568baedSDoug Anderson defaults but that command line flags should still have the final 2188568baedSDoug Anderson say. 2198568baedSDoug Anderson 2208568baedSDoug Anderson Args: 2218568baedSDoug Anderson parser: An instance of an OptionParser whose defaults will be 2228568baedSDoug Anderson updated. 223a1dcee84SDoug Anderson config: An instance of _ProjectConfigParser that we will query 2248568baedSDoug Anderson for settings. 2258568baedSDoug Anderson """ 2268568baedSDoug Anderson defaults = parser.get_default_values() 2278568baedSDoug Anderson for name, val in config.items('settings'): 2288568baedSDoug Anderson if hasattr(defaults, name): 2298568baedSDoug Anderson default_val = getattr(defaults, name) 2308568baedSDoug Anderson if isinstance(default_val, bool): 2318568baedSDoug Anderson val = config.getboolean('settings', name) 2328568baedSDoug Anderson elif isinstance(default_val, int): 2338568baedSDoug Anderson val = config.getint('settings', name) 2348568baedSDoug Anderson parser.set_default(name, val) 2358568baedSDoug Anderson else: 2368568baedSDoug Anderson print "WARNING: Unknown setting %s" % name 2378568baedSDoug Anderson 238*8895b3e1SSimon Glassdef _ReadAliasFile(fname): 239*8895b3e1SSimon Glass """Read in the U-Boot git alias file if it exists. 240*8895b3e1SSimon Glass 241*8895b3e1SSimon Glass Args: 242*8895b3e1SSimon Glass fname: Filename to read. 243*8895b3e1SSimon Glass """ 244*8895b3e1SSimon Glass if os.path.exists(fname): 245*8895b3e1SSimon Glass bad_line = None 246*8895b3e1SSimon Glass with open(fname) as fd: 247*8895b3e1SSimon Glass linenum = 0 248*8895b3e1SSimon Glass for line in fd: 249*8895b3e1SSimon Glass linenum += 1 250*8895b3e1SSimon Glass line = line.strip() 251*8895b3e1SSimon Glass if not line or line.startswith('#'): 252*8895b3e1SSimon Glass continue 253*8895b3e1SSimon Glass words = line.split(' ', 2) 254*8895b3e1SSimon Glass if len(words) < 3 or words[0] != 'alias': 255*8895b3e1SSimon Glass if not bad_line: 256*8895b3e1SSimon Glass bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum, 257*8895b3e1SSimon Glass line) 258*8895b3e1SSimon Glass continue 259*8895b3e1SSimon Glass alias[words[1]] = [s.strip() for s in words[2].split(',')] 260*8895b3e1SSimon Glass if bad_line: 261*8895b3e1SSimon Glass print bad_line 262*8895b3e1SSimon Glass 263a1dcee84SDoug Andersondef Setup(parser, project_name, config_fname=''): 2640d24de9dSSimon Glass """Set up the settings module by reading config files. 2650d24de9dSSimon Glass 2660d24de9dSSimon Glass Args: 2678568baedSDoug Anderson parser: The parser to update 268a1dcee84SDoug Anderson project_name: Name of project that we're working on; we'll look 269a1dcee84SDoug Anderson for sections named "project_section" as well. 2700d24de9dSSimon Glass config_fname: Config filename to read ('' for default) 2710d24de9dSSimon Glass """ 272*8895b3e1SSimon Glass # First read the git alias file if available 273*8895b3e1SSimon Glass _ReadAliasFile('doc/git-mailrc') 274a1dcee84SDoug Anderson config = _ProjectConfigParser(project_name) 2750d24de9dSSimon Glass if config_fname == '': 2762b36c75dSVikram Narayanan config_fname = '%s/.patman' % os.getenv('HOME') 27787d65558SVikram Narayanan 27887d65558SVikram Narayanan if not os.path.exists(config_fname): 27987d65558SVikram Narayanan print "No config file found ~/.patman\nCreating one...\n" 28087d65558SVikram Narayanan CreatePatmanConfigFile(config_fname) 28187d65558SVikram Narayanan 2828568baedSDoug Anderson config.read(config_fname) 2830d24de9dSSimon Glass 2848568baedSDoug Anderson for name, value in config.items('alias'): 2850d24de9dSSimon Glass alias[name] = value.split(',') 2860d24de9dSSimon Glass 2878568baedSDoug Anderson _UpdateDefaults(parser, config) 2880d24de9dSSimon Glass 2890d24de9dSSimon Glass# These are the aliases we understand, indexed by alias. Each member is a list. 2900d24de9dSSimon Glassalias = {} 291a1dcee84SDoug Anderson 292a1dcee84SDoug Andersonif __name__ == "__main__": 293a1dcee84SDoug Anderson import doctest 294a1dcee84SDoug Anderson 295a1dcee84SDoug Anderson doctest.testmod() 296