From fadb45e81a0ac5a4a78fa62ecc0013ca93959f2b Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Sat, 15 Jul 2017 14:48:10 -0400 Subject: [PATCH 1/6] bpo-30971: Improve code readability of json.tool Originall from https://github.com/python/cpython/pull/2720 Set default option for infile and outfile as per https://github.com/python/cpython/pull/2720#pullrequestreview-50224377 Use --sort-keys help message based on the json.tool docs at https://docs.python.org/3.6/library/json.html#basic-usage: Remove commens as per https://bugs.python.org/msg298692. Code was descriptive without the comments. --- Lib/json/tool.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 4f3182c0c1e7f1..076d130bad282d 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -22,27 +22,24 @@ def main(): 'to validate and pretty-print JSON objects.') parser = argparse.ArgumentParser(prog=prog, description=description) parser.add_argument('infile', nargs='?', type=argparse.FileType(), + default=sys.stdin, help='a JSON file to be validated or pretty-printed') parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'), + default=sys.stdout, help='write the output of infile to outfile') - parser.add_argument('--sort-keys', action='store_true', default=False, - help='sort the output of dictionaries alphabetically by key') + parser.add_argument('--sort-keys', action='store_true', + help='sort the output of dictionaries by key') options = parser.parse_args() - infile = options.infile or sys.stdin - outfile = options.outfile or sys.stdout - sort_keys = options.sort_keys - with infile: + hook = collections.OrderedDict if options.sort_keys else None + with options.infile as infile: try: - if sort_keys: - obj = json.load(infile) - else: - obj = json.load(infile, - object_pairs_hook=collections.OrderedDict) + obj = json.load(infile, object_pairs_hook=hook) except ValueError as e: raise SystemExit(e) - with outfile: - json.dump(obj, outfile, sort_keys=sort_keys, indent=4) + + with options.outfile as outfile: + json.dump(obj, outfile, sort_keys=options.sort_keys, indent=4) outfile.write('\n') From 3c67514a2c92386f17d009809b3b3a53742f5b57 Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Mon, 27 Feb 2017 10:31:45 -0500 Subject: [PATCH 2/6] Add --indent / --no-indent arguments to json.tool From https://github.com/python/cpython/pull/201 which is being split into two pull requests. See http://bugs.python.org/issue29636 --- Lib/json/tool.py | 11 ++++++++++- Lib/test/test_json/test_tool.py | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 076d130bad282d..5056bf9255193b 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -27,6 +27,12 @@ def main(): parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'), default=sys.stdout, help='write the output of infile to outfile') + group = parser.add_mutually_exclusive_group() + group.add_argument('--indent', default=4, type=int, + help='Indent level (number of spaces) for ' + 'pretty-printing. Defaults to 4.') + group.add_argument('--no-indent', action='store_const', dest='indent', + const=None, help='Use compact mode.') parser.add_argument('--sort-keys', action='store_true', help='sort the output of dictionaries by key') options = parser.parse_args() @@ -39,7 +45,10 @@ def main(): raise SystemExit(e) with options.outfile as outfile: - json.dump(obj, outfile, sort_keys=options.sort_keys, indent=4) + json.dump(obj, outfile, + indent=options.indent, + sort_keys=options.sort_keys, + ) outfile.write('\n') diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index 9d93f931ca3958..64c327382f3b8d 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -105,3 +105,26 @@ def test_sort_keys_flag(self): self.assertEqual(out.splitlines(), self.expect_without_sort_keys.encode().splitlines()) self.assertEqual(err, b'') + + def test_indent(self): + json_stdin = b'[1, 2]' + expect = textwrap.dedent('''\ + [ + 1, + 2 + ] + ''').encode() + args = sys.executable, '-m', 'json.tool', '--indent', '2' + with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc: + json_stdout, err = proc.communicate(json_stdin) + self.assertEqual(expect.splitlines(), json_stdout.splitlines()) + self.assertEqual(err, b'') + + def test_no_indent(self): + json_stdin = b'[1,\n2]' + expect = b'[1, 2]' + args = sys.executable, '-m', 'json.tool', '--no-indent' + with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc: + json_stdout, err = proc.communicate(json_stdin) + self.assertEqual(expect.splitlines(), json_stdout.splitlines()) + self.assertEqual(err, b'') From 70cc07b4cd0930ab973bcc5108f7dc0f1d764129 Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Thu, 13 Apr 2017 14:10:13 -0400 Subject: [PATCH 3/6] json.tool: --tab identation option --- Lib/json/tool.py | 2 ++ Lib/test/test_json/test_tool.py | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 5056bf9255193b..3b1883f98e2eb0 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -33,6 +33,8 @@ def main(): 'pretty-printing. Defaults to 4.') group.add_argument('--no-indent', action='store_const', dest='indent', const=None, help='Use compact mode.') + group.add_argument('--tab', action='store_const', dest='indent', + const='\t', help='Use tabs for indentation.') parser.add_argument('--sort-keys', action='store_true', help='sort the output of dictionaries by key') options = parser.parse_args() diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index 64c327382f3b8d..6ac4787078f8d1 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -128,3 +128,12 @@ def test_no_indent(self): json_stdout, err = proc.communicate(json_stdin) self.assertEqual(expect.splitlines(), json_stdout.splitlines()) self.assertEqual(err, b'') + + def test_tab(self): + json_stdin = b'[1, 2]' + expect = b'[\n\t1,\n\t2\n]\n' + args = sys.executable, '-m', 'json.tool', '--tab' + with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc: + json_stdout, err = proc.communicate(json_stdin) + self.assertEqual(expect.splitlines(), json_stdout.splitlines()) + self.assertEqual(err, b'') From a098b5de1d571a92fe678362c7ac572122423501 Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Thu, 13 Apr 2017 14:21:56 -0400 Subject: [PATCH 4/6] json.tool: --compact identation option --- Lib/json/tool.py | 14 ++++++++++---- Lib/test/test_json/test_tool.py | 9 +++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 3b1883f98e2eb0..1f74e01551f7f6 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -35,6 +35,8 @@ def main(): const=None, help='Use compact mode.') group.add_argument('--tab', action='store_const', dest='indent', const='\t', help='Use tabs for indentation.') + group.add_argument('--compact', action='store_true', + help='Use most compact whitespace format.') parser.add_argument('--sort-keys', action='store_true', help='sort the output of dictionaries by key') options = parser.parse_args() @@ -46,11 +48,15 @@ def main(): except ValueError as e: raise SystemExit(e) + kwargs = { + 'indent': options.indent, + 'sort_keys': options.sort_keys, + } + if options.compact: + kwargs['indent'] = None + kwargs['separators'] = ',', ':' with options.outfile as outfile: - json.dump(obj, outfile, - indent=options.indent, - sort_keys=options.sort_keys, - ) + json.dump(obj, outfile, **kwargs) outfile.write('\n') diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index 6ac4787078f8d1..1126d22c5ec854 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -137,3 +137,12 @@ def test_tab(self): json_stdout, err = proc.communicate(json_stdin) self.assertEqual(expect.splitlines(), json_stdout.splitlines()) self.assertEqual(err, b'') + + def test_compact(self): + json_stdin = b'[ 1 ,\n 2]' + expect = b'[1,2]' + args = sys.executable, '-m', 'json.tool', '--compact' + with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc: + json_stdout, err = proc.communicate(json_stdin) + self.assertEqual(expect.splitlines(), json_stdout.splitlines()) + self.assertEqual(err, b'') From 3d3477d51f0eea38ba5c511da0f31711e4591cba Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Thu, 22 Feb 2018 11:24:40 -0500 Subject: [PATCH 5/6] Add NEWS entry Use `blurb add` command to assist with adding the NEWS entry. --- .../next/Library/2018-02-22-11-24-33.bpo-29636.ogGRE2.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2018-02-22-11-24-33.bpo-29636.ogGRE2.rst diff --git a/Misc/NEWS.d/next/Library/2018-02-22-11-24-33.bpo-29636.ogGRE2.rst b/Misc/NEWS.d/next/Library/2018-02-22-11-24-33.bpo-29636.ogGRE2.rst new file mode 100644 index 00000000000000..9f96ed28f927fa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-02-22-11-24-33.bpo-29636.ogGRE2.rst @@ -0,0 +1,5 @@ +Add whitespace options for formatting JSON with the ``json.tool`` CLI. The +following mutually exclusive options are now supported: ``--indent`` for +setting the indent level in spaces; ``--tab`` for indenting with tabs; +``--no-indent`` for suppressing newlines; and ``--compact`` for suppressing +all whitespace. The default behavior remains the same as ``--indent=4``. From b8fee34d83c97d0815fd4885cec7b39fd786143b Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Thu, 22 Feb 2018 11:53:44 -0500 Subject: [PATCH 6/6] Update whitespace option documentation --- Lib/json/tool.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 139c986e31dd26..f51a11be12ecaf 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -26,18 +26,20 @@ def main(): parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'), default=sys.stdout, help='write the output of infile to outfile') + parser.add_argument('--sort-keys', action='store_true', + help='sort the output of dictionaries by key') group = parser.add_mutually_exclusive_group() group.add_argument('--indent', default=4, type=int, - help='Indent level (number of spaces) for ' - 'pretty-printing. Defaults to 4.') - group.add_argument('--no-indent', action='store_const', dest='indent', - const=None, help='Use compact mode.') + help='separate items with newlines and use this number ' + 'of spaces for indentation') group.add_argument('--tab', action='store_const', dest='indent', - const='\t', help='Use tabs for indentation.') + const='\t', help='separate items with newlines and use ' + 'tabs for indentation') + group.add_argument('--no-indent', action='store_const', dest='indent', + const=None, + help='separate items with spaces rather than newlines') group.add_argument('--compact', action='store_true', - help='Use most compact whitespace format.') - parser.add_argument('--sort-keys', action='store_true', - help='sort the output of dictionaries by key') + help='suppress all whitespace separation (most compact)') options = parser.parse_args() with options.infile as infile: