changeset 45909:9dc1351d0b5f

errors: raise ConfigError on failure to parse config file This replaces two raises of `ParseError` by `ConfigError`, which makes it so we get the desired exit code when `ui.detailed-exit-code` is enabled. Because the exceptions include a location, I had to add that to `ConfigError` as well. I considered making `ConfigError` a subclass of `ParseError`, but it doesn't feel like it quite passes the "is-a" test. I used "config error: " as prefix for these errors instead of the previous "hg: parse error: ", which seems a little less accurate now (and, as I've said before, I don't know what the "hg: " part is supposed to signify anyway). I can easily be convinced to change the prefix to something else (including "abort: "). Some of the exceptions raised here mean that we fail to even load the `ui` object in the `dispatch` module. When that happens, we don't know to use detailed exit codes, so some tests (e.g. `test-hgrc.t`) still see exit code 255. I'll try to get back to that later. It should be possible to give detailed exit codes if at least part of the config can be read (e.g. when the system-wide one enables detailed exit codes and the user's config fails to parse). Differential Revision: https://phab.mercurial-scm.org/D9355
author Martin von Zweigbergk <martinvonz@google.com>
date Fri, 20 Nov 2020 14:43:21 -0800
parents f4065c3f09b8
children fc4fb2f17dd4
files hgext/eol.py mercurial/config.py mercurial/error.py mercurial/ui.py tests/test-add.t tests/test-audit-subrepo.t tests/test-config.t tests/test-convert-git.t tests/test-default-push.t tests/test-dispatch.t tests/test-hgrc.t tests/test-histedit-arguments.t tests/test-lfs.t tests/test-merge1.t tests/test-phases.t tests/test-repo-compengines.t tests/test-trusted.py tests/test-trusted.py.out
diffstat 18 files changed, 57 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/eol.py	Mon Nov 16 10:56:54 2020 -0800
+++ b/hgext/eol.py	Fri Nov 20 14:43:21 2020 -0800
@@ -274,7 +274,7 @@
                 return eolfile(ui, repo.root, data)
             except (IOError, LookupError):
                 pass
-    except errormod.ParseError as inst:
+    except errormod.ConfigError as inst:
         ui.warn(
             _(
                 b"warning: ignoring .hgeol file due to parse error "
--- a/mercurial/config.py	Mon Nov 16 10:56:54 2020 -0800
+++ b/mercurial/config.py	Fri Nov 20 14:43:21 2020 -0800
@@ -165,7 +165,7 @@
                     include(expanded, remap=remap, sections=sections)
                 except IOError as inst:
                     if inst.errno != errno.ENOENT:
-                        raise error.ParseError(
+                        raise error.ConfigError(
                             _(b"cannot include %s (%s)")
                             % (expanded, encoding.strtolocal(inst.strerror)),
                             b"%s:%d" % (src, line),
@@ -203,7 +203,7 @@
             message = l.rstrip()
             if l.startswith(b' '):
                 message = b"unexpected leading whitespace: %s" % message
-            raise error.ParseError(message, (b"%s:%d" % (src, line)))
+            raise error.ConfigError(message, (b"%s:%d" % (src, line)))
 
     def read(self, path, fp=None, sections=None, remap=None):
         if not fp:
--- a/mercurial/error.py	Mon Nov 16 10:56:54 2020 -0800
+++ b/mercurial/error.py	Fri Nov 20 14:43:21 2020 -0800
@@ -227,6 +227,24 @@
 class ConfigError(Abort):
     """Exception raised when parsing config files"""
 
+    def __init__(self, message, location=None, hint=None):
+        super(ConfigError, self).__init__(message, hint=hint)
+        self.location = location
+
+    def format(self):
+        from .i18n import _
+
+        if self.location is not None:
+            message = _(b"config error at %s: %s\n") % (
+                pycompat.bytestr(self.location),
+                self.message,
+            )
+        else:
+            message = _(b"config error: %s\n") % self.message
+        if self.hint:
+            message += _(b"(%s)\n") % self.hint
+        return message
+
 
 class UpdateAbort(Abort):
     """Raised when an update is aborted for destination issue"""
--- a/mercurial/ui.py	Mon Nov 16 10:56:54 2020 -0800
+++ b/mercurial/ui.py	Fri Nov 20 14:43:21 2020 -0800
@@ -466,7 +466,7 @@
 
             try:
                 cfg.read(filename, fp, sections=sections, remap=remap)
-            except error.ParseError as inst:
+            except error.ConfigError as inst:
                 if trusted:
                     raise
                 self.warn(
--- a/tests/test-add.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-add.t	Fri Nov 20 14:43:21 2020 -0800
@@ -44,7 +44,7 @@
 #if no-windows
   $ echo foo > con.xml
   $ hg --config ui.portablefilenames=jump add con.xml
-  abort: ui.portablefilenames value is invalid ('jump')
+  config error: ui.portablefilenames value is invalid ('jump')
   [30]
   $ hg --config ui.portablefilenames=abort add con.xml
   abort: filename contains 'con', which is reserved on Windows: con.xml
--- a/tests/test-audit-subrepo.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-audit-subrepo.t	Fri Nov 20 14:43:21 2020 -0800
@@ -120,8 +120,8 @@
   $ hg init sub
   $ echo '= sub' >> .hgsub
   $ hg ci -qAm 'add subrepo ""'
-  hg: parse error at .hgsub:1: = sub
-  [255]
+  config error at .hgsub:1: = sub
+  [30]
 
 prepare tampered repo (including the commit above):
 
@@ -144,8 +144,8 @@
 on clone (and update):
 
   $ hg clone -q emptypath emptypath2
-  hg: parse error at .hgsub:1: = sub
-  [255]
+  config error at .hgsub:1: = sub
+  [30]
 
 Test current path
 -----------------
--- a/tests/test-config.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-config.t	Fri Nov 20 14:43:21 2020 -0800
@@ -7,8 +7,8 @@
   > novaluekey
   > EOF
   $ hg showconfig
-  hg: parse error at $TESTTMP/.hg/hgrc:1: novaluekey
-  [255]
+  config error at $TESTTMP/.hg/hgrc:1: novaluekey
+  [30]
 
 Invalid syntax: no key
 
@@ -16,8 +16,8 @@
   > =nokeyvalue
   > EOF
   $ hg showconfig
-  hg: parse error at $TESTTMP/.hg/hgrc:1: =nokeyvalue
-  [255]
+  config error at $TESTTMP/.hg/hgrc:1: =nokeyvalue
+  [30]
 
 Test hint about invalid syntax from leading white space
 
@@ -25,16 +25,16 @@
   >  key=value
   > EOF
   $ hg showconfig
-  hg: parse error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace:  key=value
-  [255]
+  config error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace:  key=value
+  [30]
 
   $ cat > .hg/hgrc << EOF
   >  [section]
   > key=value
   > EOF
   $ hg showconfig
-  hg: parse error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace:  [section]
-  [255]
+  config error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace:  [section]
+  [30]
 
 Reset hgrc
 
--- a/tests/test-convert-git.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-convert-git.t	Fri Nov 20 14:43:21 2020 -0800
@@ -332,7 +332,7 @@
 
 input validation
   $ hg convert --config convert.git.similarity=foo --datesort git-repo2 fullrepo
-  abort: convert.git.similarity is not a valid integer ('foo')
+  config error: convert.git.similarity is not a valid integer ('foo')
   [30]
   $ hg convert --config convert.git.similarity=-1 --datesort git-repo2 fullrepo
   abort: similarity must be between 0 and 100
--- a/tests/test-default-push.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-default-push.t	Fri Nov 20 14:43:21 2020 -0800
@@ -18,7 +18,7 @@
 Push should provide a hint when both 'default' and 'default-push' not set:
   $ cd c
   $ hg push --config paths.default=
-  abort: default repository not configured!
+  config error: default repository not configured!
   (see 'hg help config.paths')
   [30]
 
--- a/tests/test-dispatch.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-dispatch.t	Fri Nov 20 14:43:21 2020 -0800
@@ -91,8 +91,8 @@
   $ mkdir -p badrepo/.hg
   $ echo 'invalid-syntax' > badrepo/.hg/hgrc
   $ hg log -b -Rbadrepo default
-  hg: parse error at badrepo/.hg/hgrc:1: invalid-syntax
-  [255]
+  config error at badrepo/.hg/hgrc:1: invalid-syntax
+  [30]
 
   $ hg log -b --cwd=inexistent default
   abort: $ENOENT$: 'inexistent'
--- a/tests/test-hgrc.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-hgrc.t	Fri Nov 20 14:43:21 2020 -0800
@@ -16,7 +16,7 @@
 
   $ echo "invalid" > $HGRC
   $ hg version
-  hg: parse error at $TESTTMP/hgrc:1: invalid
+  config error at $TESTTMP/hgrc:1: invalid
   [255]
   $ echo "" > $HGRC
 
@@ -59,7 +59,7 @@
 #if unix-permissions no-root
   $ chmod u-r $TESTTMP/included
   $ hg showconfig section
-  hg: parse error at $TESTTMP/hgrc:2: cannot include $TESTTMP/included (Permission denied)
+  config error at $TESTTMP/hgrc:2: cannot include $TESTTMP/included (Permission denied)
   [255]
 #endif
 
@@ -68,7 +68,7 @@
   $ echo '[foo]' > $HGRC
   $ echo '  x = y' >> $HGRC
   $ hg version
-  hg: parse error at $TESTTMP/hgrc:2: unexpected leading whitespace:   x = y
+  config error at $TESTTMP/hgrc:2: unexpected leading whitespace:   x = y
   [255]
 
   $ "$PYTHON" -c "from __future__ import print_function; print('[foo]\nbar = a\n b\n c \n  de\n fg \nbaz = bif cb \n')" \
@@ -275,7 +275,7 @@
   > EOF
 
   $ hg path
-  hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
+  config error at $TESTTMP/.hg/hgrc:3: [broken
   [255]
   $ HGRCSKIPREPO=1 hg path
   foo = $TESTTMP/bar
@@ -283,7 +283,7 @@
 Check that hgweb respect HGRCSKIPREPO=1
 
   $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
-  hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
+  config error at $TESTTMP/.hg/hgrc:3: [broken
   [255]
   $ test -f hg.pid && (cat hg.pid >> $DAEMON_PIDS)
   [1]
@@ -302,7 +302,7 @@
 Check that zeroconf respect HGRCSKIPREPO=1
 
   $ hg paths --config extensions.zeroconf=
-  hg: parse error at $TESTTMP/.hg/hgrc:3: [broken
+  config error at $TESTTMP/.hg/hgrc:3: [broken
   [255]
   $ HGRCSKIPREPO=1 hg paths --config extensions.zeroconf=
   foo = $TESTTMP/bar
--- a/tests/test-histedit-arguments.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-histedit-arguments.t	Fri Nov 20 14:43:21 2020 -0800
@@ -126,7 +126,7 @@
 ---------------------------
 
   $ hg histedit --config "histedit.defaultrev="
-  abort: config option histedit.defaultrev can't be empty
+  config error: config option histedit.defaultrev can't be empty
   [30]
 
 Run on a revision not descendants of the initial parent
--- a/tests/test-lfs.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-lfs.t	Fri Nov 20 14:43:21 2020 -0800
@@ -1118,8 +1118,8 @@
 
   $ echo x > file.txt
   $ hg ci -Aqm 'should fail'
-  hg: parse error at .hglfs:3: bad file ... no commit
-  [255]
+  config error at .hglfs:3: bad file ... no commit
+  [30]
 
   $ cat > .hglfs << EOF
   > [track]
--- a/tests/test-merge1.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-merge1.t	Fri Nov 20 14:43:21 2020 -0800
@@ -138,7 +138,7 @@
 
 bad config
   $ hg merge 1 --config merge.checkunknown=x
-  abort: merge.checkunknown not valid ('x' is none of 'abort', 'ignore', 'warn')
+  config error: merge.checkunknown not valid ('x' is none of 'abort', 'ignore', 'warn')
   [30]
 this merge should fail
   $ hg merge 1 --config merge.checkunknown=abort
--- a/tests/test-phases.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-phases.t	Fri Nov 20 14:43:21 2020 -0800
@@ -512,7 +512,7 @@
   $ mkcommit I --config phases.new-commit='babar'
   transaction abort!
   rollback completed
-  abort: phases.new-commit: not a valid phase name ('babar')
+  config error: phases.new-commit: not a valid phase name ('babar')
   [30]
 Test phase command
 ===================
--- a/tests/test-repo-compengines.t	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-repo-compengines.t	Fri Nov 20 14:43:21 2020 -0800
@@ -129,8 +129,8 @@
   > revlog.zlib.level=foobar
   > EOF
   $ commitone zlib-level-invalid
-  abort: storage.revlog.zlib.level is not a valid integer ('foobar')
-  abort: storage.revlog.zlib.level is not a valid integer ('foobar')
+  config error: storage.revlog.zlib.level is not a valid integer ('foobar')
+  config error: storage.revlog.zlib.level is not a valid integer ('foobar')
   [30]
 
   $ hg init zlib-level-out-of-range
@@ -186,8 +186,8 @@
   > revlog.zstd.level=foobar
   > EOF
   $ commitone zstd-level-invalid
-  abort: storage.revlog.zstd.level is not a valid integer ('foobar')
-  abort: storage.revlog.zstd.level is not a valid integer ('foobar')
+  config error: storage.revlog.zstd.level is not a valid integer ('foobar')
+  config error: storage.revlog.zstd.level is not a valid integer ('foobar')
   [30]
 
   $ hg init zstd-level-out-of-range --config format.revlog-compression=zstd
--- a/tests/test-trusted.py	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-trusted.py	Fri Nov 20 14:43:21 2020 -0800
@@ -256,12 +256,12 @@
 
 try:
     testui(user=b'abc', group=b'def', silent=True)
-except error.ParseError as inst:
+except error.ConfigError as inst:
     bprint(inst.format())
 
 try:
     testui(debug=True, silent=True)
-except error.ParseError as inst:
+except error.ConfigError as inst:
     bprint(inst.format())
 
 print()
--- a/tests/test-trusted.py.out	Mon Nov 16 10:56:54 2020 -0800
+++ b/tests/test-trusted.py.out	Fri Nov 20 14:43:21 2020 -0800
@@ -176,7 +176,7 @@
 not trusting file .hg/hgrc from untrusted user abc, group def
 ignored .hg/hgrc:1: foo
 # same user, same group
-hg: parse error at .hg/hgrc:1: foo
+config error at .hg/hgrc:1: foo
 
 
 # access typed information