Patchwork [1,of,5,paths,v3] ui: represent paths as classes

mail settings
Submitter Gregory Szorc
Date March 8, 2015, 7:54 p.m.
Message ID <>
Download mbox | patch
Permalink /patch/7941/
State Accepted
Delegated to: Pierre-Yves David
Headers show


Gregory Szorc - March 8, 2015, 7:54 p.m.
# HG changeset patch
# User Gregory Szorc <>
# Date 1423341730 28800
#      Sat Feb 07 12:42:10 2015 -0800
# Node ID 7df546527053a134d03777d89e8c98ec75edaf6f
# Parent  02d7b5cd373bbb4e8263dad9bfbf9c4c3b0e4e3a
ui: represent paths as classes

Many have long wanted for paths to have expanded functionality and

In order to make that transition possible, we need to start
representing paths as something more than simple strings.

This patch introduces two classes:

1) "path" for representing a single path instance
2) "paths" for representing a collection of "paths"

Since we don't like patches that introduce new code without any
consumers, we convert ui.expandpath() to use the new APIs internally.
Upcoming patches will start exposing "path" instances to consumers
that currently interface with string paths.

The new "paths" attribute of ui is populated from config data the first
time it is accessed. Since it isn't updated when the configs are
modified, this could lead to some inaccurate caching behavior. It
shouldn't be an issue, as paths information is typically not accessed
until command dispatch, which occurs after the repository config
and extensions have been loaded. Time will tell if we need to refresh
paths information when the underlying config changes.


diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -8,8 +8,9 @@ 
 from i18n import _
 import errno, getpass, os, socket, sys, tempfile, traceback
 import config, scmutil, util, error, formatter
 from node import hex
+from util import propertycache
 samplehgrcs = {
 """# example user config (see "hg help config" for more info)
@@ -530,12 +531,16 @@  class ui(object):
         """Return repository location relative to cwd or from [paths]"""
         if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
             return loc
-        path = self.config('paths', loc)
-        if not path and default is not None:
-            path = self.config('paths', default)
-        return path or loc
+        p = self.paths.getpath(loc, default=default)
+        if p:
+            return p.loc
+        return loc
+    @propertycache
+    def paths(self):
+        return paths(self)
     def pushbuffer(self, error=False):
         """install a buffer to capture standard output of the ui object
@@ -922,4 +927,49 @@  class ui(object):
         ui.write(s, 'label') is equivalent to
         ui.write(ui.label(s, 'label')).
         return msg
+class paths(dict):
+    """Represents a collection of paths and their configs.
+    Data is initially derived from ui instances and the config files they have
+    loaded.
+    """
+    def __init__(self, ui):
+        dict.__init__(self)
+        for name, loc in ui.configitems('paths'):
+            # No location is the same as not existing.
+            if not loc:
+                continue
+            self[name] = path(name, rawloc=loc)
+    def getpath(self, name, default=None):
+        """Return a ``path`` for the specified name, falling back to a default.
+        Returns the first of ``name`` or ``default`` that is present, or None
+        if neither is present.
+        """
+        try:
+            return self[name]
+        except KeyError:
+            if default is not None:
+                try:
+                    return self[default]
+                except KeyError:
+                    pass
+        return None
+class path(object):
+    """Represents an individual path and its configuration."""
+    def __init__(self, name, rawloc=None):
+        """Construct a path from its config options.
+        ``name`` is the symbolic name of the path.
+        ``rawloc`` is the raw location, as defined in the config.
+        """
+ = name
+        # We'll do more intelligent things with rawloc in the future.
+        self.loc = rawloc