Patchwork [1,of,7] extdata: add extdatasource reader

login
register
mail settings
Submitter Yuya Nishihara
Date Oct. 1, 2017, noon
Message ID <4bcad24c850624811d6f.1506859212@mimosa>
Download mbox | patch
Permalink /patch/24331/
State Accepted
Headers show

Comments

Yuya Nishihara - Oct. 1, 2017, noon
# HG changeset patch
# User Matt Mackall <mpm@selenic.com>
# Date 1473794045 18000
#      Tue Sep 13 14:14:05 2016 -0500
# Node ID 4bcad24c850624811d6f4d1aa6d982c01270d2fd
# Parent  8a89dfa01997bb3dd9e89aa167f2b28ed865e9fc
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

Patch

diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -35,6 +35,7 @@  from . import (
     pycompat,
     revsetlang,
     similar,
+    url,
     util,
 )
 
@@ -1016,6 +1017,56 @@  class filecache(object):
         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(