diff -r 47539ea08bdb -r 4dccc37b87bd mercurial/ui.py --- a/mercurial/ui.py Sun Dec 06 12:31:46 2015 -0800 +++ b/mercurial/ui.py Sat Dec 05 21:11:04 2015 -0800 @@ -1063,7 +1063,7 @@ def __init__(self, ui): dict.__init__(self) - for name, loc in ui.configitems('paths'): + for name, loc in ui.configitems('paths', ignoresub=True): # No location is the same as not existing. if not loc: continue @@ -1071,7 +1071,8 @@ # TODO ignore default-push once all consumers stop referencing it # since it is handled specifically below. - self[name] = path(ui, name, rawloc=loc) + loc, sub = ui.configsuboptions('paths', name) + self[name] = path(ui, name, rawloc=loc, suboptions=sub) # Handle default-push, which is a one-off that defines the push URL for # the "default" path. @@ -1120,10 +1121,48 @@ assert False +_pathsuboptions = {} + +def pathsuboption(option, attr): + """Decorator used to declare a path sub-option. + + Arguments are the sub-option name and the attribute it should set on + ``path`` instances. + + The decorated function will receive as arguments a ``ui`` instance, + ``path`` instance, and the string value of this option from the config. + The function should return the value that will be set on the ``path`` + instance. + + This decorator can be used to perform additional verification of + sub-options and to change the type of sub-options. + """ + def register(func): + _pathsuboptions[option] = (attr, func) + return func + return register + +@pathsuboption('pushurl', 'pushloc') +def pushurlpathoption(ui, path, value): + u = util.url(value) + # Actually require a URL. + if not u.scheme: + ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name) + return None + + # Don't support the #foo syntax in the push URL to declare branch to + # push. + if u.fragment: + ui.warn(_('("#fragment" in paths.%s:pushurl not supported; ' + 'ignoring)\n') % path.name) + u.fragment = None + + return str(u) + class path(object): """Represents an individual path and its configuration.""" - def __init__(self, ui, name, rawloc=None, pushloc=None): + def __init__(self, ui, name, rawloc=None, suboptions=None): """Construct a path from its config options. ``ui`` is the ``ui`` instance the path is coming from. @@ -1151,7 +1190,6 @@ self.name = name self.rawloc = rawloc self.loc = str(u) - self.pushloc = pushloc # When given a raw location but not a symbolic name, validate the # location is valid. @@ -1159,6 +1197,19 @@ raise ValueError('location is not a URL or path to a local ' 'repo: %s' % rawloc) + suboptions = suboptions or {} + + # Now process the sub-options. If a sub-option is registered, its + # attribute will always be present. The value will be None if there + # was no valid sub-option. + for suboption, (attr, func) in _pathsuboptions.iteritems(): + if suboption not in suboptions: + setattr(self, attr, None) + continue + + value = func(ui, self, suboptions[suboption]) + setattr(self, attr, value) + def _isvalidlocalpath(self, path): """Returns True if the given path is a potentially valid repository. This is its own function so that extensions can change the definition of @@ -1166,6 +1217,19 @@ one).""" return os.path.isdir(os.path.join(path, '.hg')) + @property + def suboptions(self): + """Return sub-options and their values for this path. + + This is intended to be used for presentation purposes. + """ + d = {} + for subopt, (attr, _func) in _pathsuboptions.iteritems(): + value = getattr(self, attr) + if value is not None: + d[subopt] = value + return d + # we instantiate one globally shared progress bar to avoid # competing progress bars when multiple UI objects get created _progresssingleton = None