Repository: pre-commit/pre-commit-hooks
Pull request #147 (2016-11-03 17:20:42)

adds top keys list of keys in hashes to put at the top of a hash
This adds custom sorting to preferentially add a list of top keys
at the start of any json hash in the json document


Patch:
diff --git a/pre_commit_hooks/pretty_format_json.py b/pre_commit_hooks/pretty_format_json.py
index 36a90eb..91dae8d 100644
--- a/pre_commit_hooks/pretty_format_json.py
+++ b/pre_commit_hooks/pretty_format_json.py
@@ -7,13 +7,19 @@ from collections import OrderedDict
 import simplejson
 
 
-def _get_pretty_format(contents, indent, sort_keys=True):
+def _get_pretty_format(contents, indent, sort_keys=True, top_keys=[]):
+    def pairs_first(pairs):
+        before = [pair for pair in pairs if pair[0] in top_keys]
+        before = sorted(before, key=lambda x: top_keys.index(x[0]))
+        after = [pair for pair in pairs if pair[0] not in top_keys]
+        if sort_keys:
+            after = sorted(after, key=lambda x: x[0])
+        return OrderedDict(before + after)
     return simplejson.dumps(
         simplejson.loads(
             contents,
-            object_pairs_hook=None if sort_keys else OrderedDict,
+            object_pairs_hook=pairs_first,
         ),
-        sort_keys=sort_keys,
         indent=indent
     ) + "\n"  # dumps don't end with a newline
 
@@ -44,6 +50,11 @@ def parse_indent(s):
             )
 
 
+def parse_topkeys(s):
+    # type: (str) -> array
+    return s.split(',')
+
+
 def pretty_format_json(argv=None):
     parser = argparse.ArgumentParser()
     parser.add_argument(
@@ -65,6 +76,13 @@ def pretty_format_json(argv=None):
         default=False,
         help='Keep JSON nodes in the same order',
     )
+    parser.add_argument(
+        '--top-keys',
+        type=parse_topkeys,
+        dest='top_keys',
+        default=[],
+        help='Ordered list of keys to keep at the top of JSON hashes',
+    )
 
     parser.add_argument('filenames', nargs='*', help='Filenames to fix')
     args = parser.parse_args(argv)
@@ -78,6 +96,7 @@ def pretty_format_json(argv=None):
         try:
             pretty_contents = _get_pretty_format(
                 contents, args.indent, sort_keys=not args.no_sort_keys,
+                top_keys=args.top_keys
             )
 
             if contents != pretty_contents:


Test patch:
diff --git a/testing/resources/top_sorted_json.json b/testing/resources/top_sorted_json.json
new file mode 100644
index 0000000..62083e7
--- /dev/null
+++ b/testing/resources/top_sorted_json.json
@@ -0,0 +1,16 @@
+{
+  "01-alist": [
+    2,
+    34,
+    234
+  ],
+  "alist": [
+    2,
+    34,
+    234
+  ],
+  "00-foo": "bar",
+  "02-blah": null,
+  "blah": null,
+  "foo": "bar"
+}
diff --git a/tests/pretty_format_json_test.py b/tests/pretty_format_json_test.py
index d9061d3..7bfc31f 100644
--- a/tests/pretty_format_json_test.py
+++ b/tests/pretty_format_json_test.py
@@ -65,6 +65,21 @@ def test_autofix_pretty_format_json(tmpdir):
     assert ret == 0
 
 
+def test_orderfile_get_pretty_format():
+    ret = pretty_format_json(['--top-keys=alist', get_resource_path('pretty_formatted_json.json')])
+    assert ret == 0
+
+
+def test_not_orderfile_get_pretty_format():
+    ret = pretty_format_json(['--top-keys=blah', get_resource_path('pretty_formatted_json.json')])
+    assert ret == 1
+
+
+def test_top_sorted_get_pretty_format():
+    ret = pretty_format_json(['--top-keys=01-alist,alist', get_resource_path('top_sorted_json.json')])
+    assert ret == 0
+
+
 def test_badfile_pretty_format_json():
     ret = pretty_format_json([get_resource_path('ok_yaml.yaml')])
     assert ret == 1