rcutil: let environ override system configs (BC)
authorJun Wu <quark@fb.com>
Sun, 26 Mar 2017 21:33:37 -0700
changeset 31690 d83e51654c8a
parent 31689 0be96ac9199a
child 31691 fbc4eb8e2433
rcutil: let environ override system configs (BC) This is BC because system configs won't be able to override $EDITOR, $PAGER. The new behavior is arguably more rational.
mercurial/commands.py
mercurial/rcutil.py
mercurial/ui.py
tests/test-config-env.py
tests/test-config-env.py.out
tests/test-config.t
--- a/mercurial/commands.py	Sun Mar 26 21:27:02 2017 -0700
+++ b/mercurial/commands.py	Sun Mar 26 21:33:37 2017 -0700
@@ -1807,6 +1807,8 @@
     for t, f in rcutil.rccomponents():
         if t == 'path':
             ui.debug('read config from: %s\n' % f)
+        elif t == 'items':
+            pass
         else:
             raise error.ProgrammingError('unknown rctype: %s' % t)
     untrusted = bool(opts.get('untrusted'))
--- a/mercurial/rcutil.py	Sun Mar 26 21:27:02 2017 -0700
+++ b/mercurial/rcutil.py	Sun Mar 26 21:33:37 2017 -0700
@@ -76,15 +76,22 @@
     and is the config file path. if type is 'items', obj is a list of (section,
     name, value, source) that should fill the config directly.
     '''
+    envrc = ('items', envrcitems())
+
     global _rccomponents
     if _rccomponents is None:
         if 'HGRCPATH' in encoding.environ:
-            _rccomponents = []
+            # assume HGRCPATH is all about user configs so environments can be
+            # overridden.
+            _rccomponents = [envrc]
             for p in encoding.environ['HGRCPATH'].split(pycompat.ospathsep):
                 if not p:
                     continue
                 _rccomponents.extend(('path', p) for p in _expandrcpath(p))
         else:
-            paths = defaultrcpath() + systemrcpath() + userrcpath()
+            paths = defaultrcpath() + systemrcpath()
             _rccomponents = [('path', os.path.normpath(p)) for p in paths]
+            _rccomponents.append(envrc)
+            paths = userrcpath()
+            _rccomponents.extend(('path', os.path.normpath(p)) for p in paths)
     return _rccomponents
--- a/mercurial/ui.py	Sun Mar 26 21:27:02 2017 -0700
+++ b/mercurial/ui.py	Sun Mar 26 21:33:37 2017 -0700
@@ -211,10 +211,20 @@
     def load(cls):
         """Create a ui and load global and user configs"""
         u = cls()
-        # we always trust global config files
+        # we always trust global config files and environment variables
         for t, f in rcutil.rccomponents():
             if t == 'path':
                 u.readconfig(f, trust=True)
+            elif t == 'items':
+                sections = set()
+                for section, name, value, source in f:
+                    # do not set u._ocfg
+                    # XXX clean this up once immutable config object is a thing
+                    u._tcfg.set(section, name, value, source)
+                    u._ucfg.set(section, name, value, source)
+                    sections.add(section)
+                for section in sections:
+                    u.fixconfig(section=section)
             else:
                 raise error.ProgrammingError('unknown rctype: %s' % t)
         return u
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-config-env.py	Sun Mar 26 21:33:37 2017 -0700
@@ -0,0 +1,48 @@
+# Test the config layer generated by environment variables
+
+from __future__ import absolute_import, print_function
+
+import os
+
+from mercurial import (
+    encoding,
+    rcutil,
+    ui as uimod,
+)
+
+testtmp = encoding.environ['TESTTMP']
+
+# prepare hgrc files
+def join(name):
+    return os.path.join(testtmp, name)
+
+with open(join('sysrc'), 'w') as f:
+    f.write('[ui]\neditor=e0\n[pager]\npager=p0\n')
+
+with open(join('userrc'), 'w') as f:
+    f.write('[ui]\neditor=e1')
+
+# replace rcpath functions so they point to the files above
+def systemrcpath():
+    return [join('sysrc')]
+
+def userrcpath():
+    return [join('userrc')]
+
+rcutil.systemrcpath = systemrcpath
+rcutil.userrcpath = userrcpath
+os.path.isdir = lambda x: False # hack: do not load default.d/*.rc
+
+# utility to print configs
+def printconfigs(env):
+    encoding.environ = env
+    rcutil._rccomponents = None # reset cache
+    ui = uimod.ui.load()
+    for section, name, value in ui.walkconfig():
+        source = ui.configsource(section, name)
+        print('%s.%s=%s # %s' % (section, name, value, source))
+    print('')
+
+# environment variable overrides
+printconfigs({})
+printconfigs({'EDITOR': 'e2', 'PAGER': 'p2'})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-config-env.py.out	Sun Mar 26 21:33:37 2017 -0700
@@ -0,0 +1,6 @@
+pager.pager=p0 # $TESTTMP/sysrc:4
+ui.editor=e1 # $TESTTMP/userrc:2
+
+pager.pager=p2 # $PAGER
+ui.editor=e1 # $TESTTMP/userrc:2
+
--- a/tests/test-config.t	Sun Mar 26 21:27:02 2017 -0700
+++ b/tests/test-config.t	Sun Mar 26 21:33:37 2017 -0700
@@ -164,3 +164,17 @@
   $ HGEDITOR=false hg config --edit
   abort: edit failed: false exited with status 1
   [255]
+
+config affected by environment variables
+
+  $ EDITOR=e1 VISUAL=e2 hg config --debug | grep 'ui\.editor'
+  $VISUAL: ui.editor=e2
+
+  $ VISUAL=e2 hg config --debug --config ui.editor=e3 | grep 'ui\.editor'
+  --config: ui.editor=e3
+
+  $ PAGER=p1 hg config --debug | grep 'pager\.pager'
+  $PAGER: pager.pager=p1
+
+  $ PAGER=p1 hg config --debug --config pager.pager=p2 | grep 'pager\.pager'
+  --config: pager.pager=p2