mercurial/ui.py
changeset 27266 4dccc37b87bd
parent 27265 47539ea08bdb
child 27274 82910fdc216f
--- 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