diff mercurial/scmutil.py @ 34456:7757cc48b766

extdata: add extdatasource reader This adds basic support for extdata, a way to add external data sources for revsets and templates. An extdata data source is simply a list of lines of the form: <revision identifier>[<space><freeform text>]\n An extdata source is configured thusly: [extdata] name = <a url or path> urls of the form shell: are launch shell commands to generate data. This patch is slightly modified by Yuya Nishihara as follows: - fix typo - remove unused function - remove future expansion point for parameter (which can be added later as the extdata revset/template are experimental) You can see the original patch at https://www.mercurial-scm.org/pipermail/mercurial-devel/2016-September/088426.html
author Matt Mackall <mpm@selenic.com>
date Tue, 13 Sep 2016 14:14:05 -0500
parents f61f5af5ed31
children d5c5cc767b7e
line wrap: on
line diff
--- a/mercurial/scmutil.py	Wed Oct 04 10:02:15 2017 +0200
+++ b/mercurial/scmutil.py	Tue Sep 13 14:14:05 2016 -0500
@@ -35,6 +35,7 @@
     pycompat,
     revsetlang,
     similar,
+    url,
     util,
 )
 
@@ -1016,6 +1017,56 @@
         except KeyError:
             raise AttributeError(self.name)
 
+def extdatasource(repo, source):
+    """Gather a map of rev -> value dict from the specified source
+
+    A source spec is treated as a URL, with a special case shell: type
+    for parsing the output from a shell command.
+
+    The data is parsed as a series of newline-separated records where
+    each record is a revision specifier optionally followed by a space
+    and a freeform string value. If the revision is known locally, it
+    is converted to a rev, otherwise the record is skipped.
+
+    Note that both key and value are treated as UTF-8 and converted to
+    the local encoding. This allows uniformity between local and
+    remote data sources.
+    """
+
+    spec = repo.ui.config("extdata", source)
+    if not spec:
+        raise error.Abort(_("unknown extdata source '%s'") % source)
+
+    data = {}
+    if spec.startswith("shell:"):
+        # external commands should be run relative to the repo root
+        cmd = spec[6:]
+        cwd = os.getcwd()
+        os.chdir(repo.root)
+        try:
+            src = util.popen(cmd)
+        finally:
+            os.chdir(cwd)
+    else:
+        # treat as a URL or file
+        src = url.open(repo.ui, spec)
+
+    try:
+        for l in src.readlines():
+            if " " in l:
+                k, v = l.strip().split(" ", 1)
+            else:
+                k, v = l.strip(), ""
+
+            k = encoding.tolocal(k)
+            if k in repo:
+                # we ignore data for nodes that don't exist locally
+                data[repo[k].rev()] = encoding.tolocal(v)
+    finally:
+        src.close()
+
+    return data
+
 def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
     if lock is None:
         raise error.LockInheritanceContractViolation(