D5237: fix: add suboption for configuring execution order of tools

hooper (Danny Hooper) phabricator at mercurial-scm.org
Tue Nov 6 21:03:33 EST 2018


hooper updated this revision to Diff 12461.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D5237?vs=12460&id=12461

REVISION DETAIL
  https://phab.mercurial-scm.org/D5237

AFFECTED FILES
  hgext/fix.py
  tests/test-fix.t

CHANGE DETAILS

diff --git a/tests/test-fix.t b/tests/test-fix.t
--- a/tests/test-fix.t
+++ b/tests/test-fix.t
@@ -165,6 +165,26 @@
     [fix]
     failure = abort
   
+  When multiple tools are configured to affect a file, they execute in an order
+  defined by the :priority suboption. The priority suboption has a default value
+  of zero for each tool. Tools are executed in order of ascending priority. The
+  execution order of tools with equal priority is unspecified. For example, you
+  could use the 'sort' and 'head' utilities to keep only the 10 smallest numbers
+  in a text file by ensuring that 'sort' runs before 'head':
+  
+    [fix]
+    sort:command = sort --numeric-sort
+    head:command = head --lines=10
+    sort:pattern = numbers.txt
+    head:pattern = numbers.txt
+    sort:priority = 1
+    head:priority = 2
+  
+  To account for changes made by each tool, the line numbers used for
+  incremental formatting are recomputed before executing the next tool. So, each
+  tool may see different values for the arguments added by the :linerange
+  suboption.
+  
   list of commands:
   
    fix           rewrite file content in changesets or working directory
@@ -1127,3 +1147,51 @@
   first
 
   $ cd ..
+
+The execution order of tools can be controlled. This example doesn't work if
+you sort after truncating, but the config defines the correct order while the
+definitions are out of order (which might imply the incorrect order given the
+implementation of fix). The goal is to use multiple tools to select the lowest
+5 numbers in the file.
+
+  $ hg init priorityexample
+  $ cd priorityexample
+
+  $ cat >> .hg/hgrc <<EOF
+  > [fix]
+  > head:command = head --lines=5
+  > head:pattern = numbers.txt
+  > head:priority = 2
+  > sort:command = sort --numeric-sort
+  > sort:pattern = numbers.txt
+  > sort:priority = 1
+  > EOF
+
+  $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
+  $ hg add -q
+  $ hg fix -w
+  $ cat numbers.txt
+  0
+  1
+  2
+  3
+  4
+
+And of course we should be able to break this by reversing the execution order.
+Test negative priorities while we're at it.
+
+  $ cat >> .hg/hgrc <<EOF
+  > [fix]
+  > head:priority = -2
+  > sort:priority = -1
+  > EOF
+  $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
+  $ hg fix -w
+  $ cat numbers.txt
+  2
+  3
+  6
+  7
+  8
+
+  $ cd ..
diff --git a/hgext/fix.py b/hgext/fix.py
--- a/hgext/fix.py
+++ b/hgext/fix.py
@@ -54,6 +54,24 @@
   [fix]
   failure = abort
 
+When multiple tools are configured to affect a file, they execute in an order
+defined by the :priority suboption. The priority suboption has a default value
+of zero for each tool. Tools are executed in order of ascending priority. The
+execution order of tools with equal priority is unspecified. For example, you
+could use the 'sort' and 'head' utilities to keep only the 10 smallest numbers
+in a text file by ensuring that 'sort' runs before 'head'::
+
+  [fix]
+  sort:command = sort --numeric-sort
+  head:command = head --lines=10
+  sort:pattern = numbers.txt
+  head:pattern = numbers.txt
+  sort:priority = 1
+  head:priority = 2
+
+To account for changes made by each tool, the line numbers used for incremental
+formatting are recomputed before executing the next tool. So, each tool may see
+different values for the arguments added by the :linerange suboption.
 """
 
 from __future__ import absolute_import
@@ -100,10 +118,16 @@
 configitem = registrar.configitem(configtable)
 
 # Register the suboptions allowed for each configured fixer.
-FIXER_ATTRS = ('command', 'linerange', 'fileset', 'pattern')
+FIXER_ATTRS = {
+    'command': None,
+    'linerange': None,
+    'fileset': None,
+    'pattern': None,
+    'priority': 0,
+}
 
-for key in FIXER_ATTRS:
-    configitem('fix', '.*(:%s)?' % key, default=None, generic=True)
+for key, default in FIXER_ATTRS.items():
+    configitem('fix', '.*(:%s)?' % key, default=default, generic=True)
 
 # A good default size allows most source code files to be fixed, but avoids
 # letting fixer tools choke on huge inputs, which could be surprising to the
@@ -602,18 +626,20 @@
     Each value is a Fixer object with methods that implement the behavior of the
     fixer's config suboptions. Does not validate the config values.
     """
-    result = {}
+    fixers = {}
     for name in fixernames(ui):
-        result[name] = Fixer()
+        fixers[name] = Fixer()
         attrs = ui.configsuboptions('fix', name)[1]
         if 'fileset' in attrs and 'pattern' not in attrs:
             ui.warn(_('the fix.tool:fileset config name is deprecated; '
                       'please rename it to fix.tool:pattern\n'))
             attrs['pattern'] = attrs['fileset']
-        for key in FIXER_ATTRS:
-            setattr(result[name], pycompat.sysstr('_' + key),
-                    attrs.get(key, ''))
-    return result
+        for key, default in FIXER_ATTRS.items():
+            setattr(fixers[name], pycompat.sysstr('_' + key),
+                    attrs.get(key, default))
+        fixers[name]._priority = int(fixers[name]._priority)
+    return collections.OrderedDict(
+        sorted(fixers.items(), key=lambda item: item[1]._priority))
 
 def fixernames(ui):
     """Returns the names of [fix] config options that have suboptions"""



To: hooper, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list