[PATCH 4 of 5 STABLE RFC] dispatch: ignore --early-bool-option that might not be a flag (BC)

Yuya Nishihara yuya at tcha.org
Wed Nov 15 07:54:23 EST 2017


# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1510321154 -32400
#      Fri Nov 10 22:39:14 2017 +0900
# Branch stable
# Node ID c2c34cf080aefbd983320bdaca111ebbd0826ff3
# Parent  5838a6130d07b588e4640542865e3c175a124bff
dispatch: ignore --early-bool-option that might not be a flag (BC)

The basic idea is if the preceding arg doesn't look like a flag taking a
value, the current arg can be parsed as a flag.

  hg ci -m --foo --config      # bad: --foo may be a flag
  hg ci -m --verbose --config  # good: --verbose known to take no value
                               # whether it is a value for -m or a flag

--debugger/--profile/--traceback are all for developers. They don't need to
be any fancy. So let's turn it to safer side.

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -106,6 +106,22 @@ globalopts = [
 # TODO: perhaps --debugger should be included
 earlyoptflags = ("--cwd", "-R", "--repository", "--repo", "--config")
 
+# core options known to be always boolean type (i.e. takes no value)
+corebooloptflags = {
+    '-y', '--noninteractive',
+    '-q', '--quiet',
+    '-v', '--verbose',
+    '--debug',
+    '--debugger',
+    '--traceback',
+    '--time',
+    '--profile',
+    '--version',
+    '-h', '--help',
+    '--hidden',
+    '--mq',  # mostly global as mqopt is inserted into all repo commands
+}
+
 dryrunopts = cmdutil.dryrunopts
 remoteopts = cmdutil.remoteopts
 walkopts = cmdutil.walkopts
diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -644,6 +644,13 @@ def _parseconfig(ui, config):
 
     return configs
 
+def _argmaytakevalue(arg):
+    """True if the given arg looks like an option flag taking the next item
+    as its value"""
+    return (arg.startswith('-')
+            and not (arg.startswith('--') and '=' in arg)  # --opt=val
+            and arg not in commands.corebooloptflags)
+
 def _earlygetopt(aliases, args, strip=True):
     """Return list of values for an option (or aliases).
 
@@ -751,17 +758,48 @@ def _earlyreqoptbool(req, name, aliases)
 
     >>> req = request([b'x', b'--', b'--debugger'])
     >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
+
+    Options can't appear immediately after any option-like flag:
+
+    >>> req = request([b'x', b'-z', b'--debugger'])
+    >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
+
+    >>> req = request([b'x', b'-z', b'--zzz', b'--debugger'])
+    >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
+
+    unless the preceding flag is known to be a boolean:
+
+    >>> req = request([b'x', b'-v', b'--debugger'])
+    >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
+    True
+
+    >>> req = request([b'x', b'--debug', b'--debugger'])
+    >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
+    True
+
+    >>> req = request([b'x', b'-z', b'--debugger', b'--debugger'])
+    >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
+    True
+
+    or it takes an immediate value:
+
+    >>> req = request([b'x', b'--foo=bar', b'--debugger'])
+    >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
+    True
     """
     try:
         argcount = req.args.index("--")
     except ValueError:
         argcount = len(req.args)
     value = None
+    inopt = False
     pos = 0
     while pos < argcount:
         arg = req.args[pos]
-        if arg in aliases:
+        if arg in aliases and not inopt:
             value = True
+        else:
+            inopt = _argmaytakevalue(arg)
         pos += 1
     req.earlyoptions[name] = value
     return value
@@ -897,7 +935,8 @@ def _dispatch(req):
                 "option -R has to be separated from other options (e.g. not "
                 "-qR) and --repository may only be abbreviated as --repo!"))
         if options["debugger"] != req.earlyoptions["debugger"]:
-            raise error.Abort(_("option --debugger may not be abbreviated!"))
+            raise error.Abort(_("option --debugger may not be abbreviated "
+                                "and should come very first!"))
         # don't validate --profile/--traceback, which can be enabled from now
 
         if options["encoding"]:
diff --git a/tests/test-dispatch.t b/tests/test-dispatch.t
--- a/tests/test-dispatch.t
+++ b/tests/test-dispatch.t
@@ -54,7 +54,12 @@ Parsing of early options should stop at 
 Unparsable form of early options:
 
   $ hg cat --debugg
-  abort: option --debugger may not be abbreviated!
+  abort: option --debugger may not be abbreviated and should come very first!
+  [255]
+  $ hg log -T --debugger
+  --debugger (no-eol)
+  $ hg log -T -T --debugger
+  abort: option --debugger may not be abbreviated and should come very first!
   [255]
 
 Parsing failure of early options should be detected before executing the


More information about the Mercurial-devel mailing list