From 27e449de1e9056908bd93f3d0ebb1d1a3cb911ba Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Wed, 31 Jul 2024 17:35:25 -0700 Subject: [PATCH 01/24] Rename json.tool to json.__main__ --- Lib/json/{tool.py => __main__.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Lib/json/{tool.py => __main__.py} (100%) diff --git a/Lib/json/tool.py b/Lib/json/__main__.py similarity index 100% rename from Lib/json/tool.py rename to Lib/json/__main__.py From 423c971b54eab1827853dac44eda93b96567bc6f Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Wed, 31 Jul 2024 17:36:40 -0700 Subject: [PATCH 02/24] Make json.tool delegate to json.__main__ --- Lib/json/tool.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Lib/json/tool.py diff --git a/Lib/json/tool.py b/Lib/json/tool.py new file mode 100644 index 00000000000000..e46a9bb03820c0 --- /dev/null +++ b/Lib/json/tool.py @@ -0,0 +1,10 @@ +import sys + +from .__main__ import main + + +if __name__ == '__main__': + try: + main() + except BrokenPipeError as exc: + sys.exit(exc.errno) From 1c1a8fa84a21fca30989d4d42b7a5ead2b7d9f08 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Wed, 31 Jul 2024 18:32:16 -0700 Subject: [PATCH 03/24] Note json.tool deprecation --- Lib/json/tool.py | 9 ++++- Lib/test/test_json/test_tool.py | 71 ++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index e46a9bb03820c0..fb9e4592b411c2 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -1,6 +1,13 @@ import sys +import warnings -from .__main__ import main +from . import __main__ + + +def main(): + warnings.warn('The json.tool module is deprecated', + DeprecationWarning, stacklevel=2) + __main__.main() if __name__ == '__main__': diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index 2b63810d53981e..fc9149dbd8d363 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -11,7 +11,7 @@ @support.requires_subprocess() -class TestTool(unittest.TestCase): +class TestMain(unittest.TestCase): data = """ [["blorpie"],[ "whoops" ] , [ @@ -19,6 +19,9 @@ class TestTool(unittest.TestCase): "i-vhbjkhnth", {"nifty":87}, {"morefield" :\tfalse,"field" :"yes"} ] """ + module = 'json' + env_vars = {} + warnings_expected = False expect_without_sort_keys = textwrap.dedent("""\ [ @@ -87,10 +90,11 @@ class TestTool(unittest.TestCase): """) def test_stdin_stdout(self): - args = sys.executable, '-m', 'json.tool' + args = sys.executable, '-m', self.module process = subprocess.run(args, input=self.data, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, self.expect) - self.assertEqual(process.stderr, '') + if not self.warnings_expected: + self.assertEqual(process.stderr, '') def _create_infile(self, data=None): infile = os_helper.TESTFN @@ -101,7 +105,7 @@ def _create_infile(self, data=None): def test_infile_stdout(self): infile = self._create_infile() - rc, out, err = assert_python_ok('-m', 'json.tool', infile) + rc, out, err = assert_python_ok('-m', self.module, infile, **self.env_vars) self.assertEqual(rc, 0) self.assertEqual(out.splitlines(), self.expect.encode().splitlines()) self.assertEqual(err, b'') @@ -115,7 +119,7 @@ def test_non_ascii_infile(self): ''').encode() infile = self._create_infile(data) - rc, out, err = assert_python_ok('-m', 'json.tool', infile) + rc, out, err = assert_python_ok('-m', self.module, infile, **self.env_vars) self.assertEqual(rc, 0) self.assertEqual(out.splitlines(), expect.splitlines()) @@ -124,7 +128,7 @@ def test_non_ascii_infile(self): def test_infile_outfile(self): infile = self._create_infile() outfile = os_helper.TESTFN + '.out' - rc, out, err = assert_python_ok('-m', 'json.tool', infile, outfile) + rc, out, err = assert_python_ok('-m', self.module, infile, outfile, **self.env_vars) self.addCleanup(os.remove, outfile) with open(outfile, "r", encoding="utf-8") as fp: self.assertEqual(fp.read(), self.expect) @@ -134,7 +138,7 @@ def test_infile_outfile(self): def test_writing_in_place(self): infile = self._create_infile() - rc, out, err = assert_python_ok('-m', 'json.tool', infile, infile) + rc, out, err = assert_python_ok('-m', self.module, infile, infile, **self.env_vars) with open(infile, "r", encoding="utf-8") as fp: self.assertEqual(fp.read(), self.expect) self.assertEqual(rc, 0) @@ -142,20 +146,21 @@ def test_writing_in_place(self): self.assertEqual(err, b'') def test_jsonlines(self): - args = sys.executable, '-m', 'json.tool', '--json-lines' + args = sys.executable, '-m', self.module, '--json-lines' process = subprocess.run(args, input=self.jsonlines_raw, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, self.jsonlines_expect) - self.assertEqual(process.stderr, '') + if not self.warnings_expected: + self.assertEqual(process.stderr, '') def test_help_flag(self): - rc, out, err = assert_python_ok('-m', 'json.tool', '-h') + rc, out, err = assert_python_ok('-m', self.module, '-h', **self.env_vars) self.assertEqual(rc, 0) self.assertTrue(out.startswith(b'usage: ')) self.assertEqual(err, b'') def test_sort_keys_flag(self): infile = self._create_infile() - rc, out, err = assert_python_ok('-m', 'json.tool', '--sort-keys', infile) + rc, out, err = assert_python_ok('-m', self.module, '--sort-keys', infile, **self.env_vars) self.assertEqual(rc, 0) self.assertEqual(out.splitlines(), self.expect_without_sort_keys.encode().splitlines()) @@ -169,40 +174,44 @@ def test_indent(self): 2 ] ''') - args = sys.executable, '-m', 'json.tool', '--indent', '2' + args = sys.executable, '-m', self.module, '--indent', '2' process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, expect) - self.assertEqual(process.stderr, '') + if not self.warnings_expected: + self.assertEqual(process.stderr, '') def test_no_indent(self): input_ = '[1,\n2]' expect = '[1, 2]\n' - args = sys.executable, '-m', 'json.tool', '--no-indent' + args = sys.executable, '-m', self.module, '--no-indent' process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, expect) - self.assertEqual(process.stderr, '') + if not self.warnings_expected: + self.assertEqual(process.stderr, '') def test_tab(self): input_ = '[1, 2]' expect = '[\n\t1,\n\t2\n]\n' - args = sys.executable, '-m', 'json.tool', '--tab' + args = sys.executable, '-m', self.module, '--tab' process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, expect) - self.assertEqual(process.stderr, '') + if not self.warnings_expected: + self.assertEqual(process.stderr, '') def test_compact(self): input_ = '[ 1 ,\n 2]' expect = '[1,2]\n' - args = sys.executable, '-m', 'json.tool', '--compact' + args = sys.executable, '-m', self.module, '--compact' process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, expect) - self.assertEqual(process.stderr, '') + if not self.warnings_expected: + self.assertEqual(process.stderr, '') def test_no_ensure_ascii_flag(self): infile = self._create_infile('{"key":"💩"}') outfile = os_helper.TESTFN + '.out' self.addCleanup(os.remove, outfile) - assert_python_ok('-m', 'json.tool', '--no-ensure-ascii', infile, outfile) + assert_python_ok('-m', self.module, '--no-ensure-ascii', infile, outfile, **self.env_vars) with open(outfile, "rb") as f: lines = f.read().splitlines() # asserting utf-8 encoded output file @@ -213,7 +222,7 @@ def test_ensure_ascii_default(self): infile = self._create_infile('{"key":"💩"}') outfile = os_helper.TESTFN + '.out' self.addCleanup(os.remove, outfile) - assert_python_ok('-m', 'json.tool', infile, outfile) + assert_python_ok('-m', self.module, infile, outfile, **self.env_vars) with open(outfile, "rb") as f: lines = f.read().splitlines() # asserting an ascii encoded output file @@ -222,11 +231,27 @@ def test_ensure_ascii_default(self): @unittest.skipIf(sys.platform =="win32", "The test is failed with ValueError on Windows") def test_broken_pipe_error(self): - cmd = [sys.executable, '-m', 'json.tool'] + cmd = [sys.executable, '-m', self.module] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE) - # bpo-39828: Closing before json.tool attempts to write into stdout. + # bpo-39828: Closing before json attempts to write into stdout. proc.stdout.close() proc.communicate(b'"{}"') self.assertEqual(proc.returncode, errno.EPIPE) + + +@support.requires_subprocess() +class TestTool(TestMain): + module = 'json.tool' + env_vars = {'PYTHONWARNINGS': 'ignore'} + warnings_expected = True + + def test_with_warnings(self): + import json.tool + filename = json.tool.__file__ + infile = self._create_infile() + rc, out, err = assert_python_ok('-m', self.module, infile) + self.assertEqual(rc, 0) + self.assertEqual(out.splitlines(), self.expect.encode().splitlines()) + self.assertEqual(err.decode(), f'{filename}:15: DeprecationWarning: The json.tool module is deprecated\n main()\n') From f8f69315ba0ed7d3257ff787e98f3aa6bc05b7e6 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Fri, 2 Aug 2024 13:37:15 -0700 Subject: [PATCH 04/24] Move main function from json.__main__ to json.tool --- Lib/json/__main__.py | 76 ++------------------------------- Lib/json/tool.py | 72 ++++++++++++++++++++++++++++--- Lib/test/test_json/test_tool.py | 3 +- 3 files changed, 73 insertions(+), 78 deletions(-) diff --git a/Lib/json/__main__.py b/Lib/json/__main__.py index fdfc3372bcca02..7d2dc2ff02e32d 100644 --- a/Lib/json/__main__.py +++ b/Lib/json/__main__.py @@ -2,88 +2,20 @@ Usage:: - $ echo '{"json":"obj"}' | python -m json.tool + $ echo '{"json":"obj"}' | python -m json { "json": "obj" } - $ echo '{ 1.2:3.4}' | python -m json.tool + $ echo '{ 1.2:3.4}' | python -m json Expecting property name enclosed in double quotes: line 1 column 3 (char 2) """ -import argparse -import json +import json.tool import sys -def main(): - prog = 'python -m json.tool' - description = ('A simple command line interface for json module ' - 'to validate and pretty-print JSON objects.') - parser = argparse.ArgumentParser(prog=prog, description=description) - parser.add_argument('infile', nargs='?', - help='a JSON file to be validated or pretty-printed', - default='-') - parser.add_argument('outfile', nargs='?', - help='write the output of infile to outfile', - default=None) - parser.add_argument('--sort-keys', action='store_true', default=False, - help='sort the output of dictionaries alphabetically by key') - parser.add_argument('--no-ensure-ascii', dest='ensure_ascii', action='store_false', - help='disable escaping of non-ASCII characters') - parser.add_argument('--json-lines', action='store_true', default=False, - help='parse input using the JSON Lines format. ' - 'Use with --no-indent or --compact to produce valid JSON Lines output.') - group = parser.add_mutually_exclusive_group() - group.add_argument('--indent', default=4, type=int, - 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='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='suppress all whitespace separation (most compact)') - options = parser.parse_args() - - dump_args = { - 'sort_keys': options.sort_keys, - 'indent': options.indent, - 'ensure_ascii': options.ensure_ascii, - } - if options.compact: - dump_args['indent'] = None - dump_args['separators'] = ',', ':' - - try: - if options.infile == '-': - infile = sys.stdin - else: - infile = open(options.infile, encoding='utf-8') - try: - if options.json_lines: - objs = (json.loads(line) for line in infile) - else: - objs = (json.load(infile),) - finally: - if infile is not sys.stdin: - infile.close() - - if options.outfile is None: - outfile = sys.stdout - else: - outfile = open(options.outfile, 'w', encoding='utf-8') - with outfile: - for obj in objs: - json.dump(obj, outfile, **dump_args) - outfile.write('\n') - except ValueError as e: - raise SystemExit(e) - - if __name__ == '__main__': try: - main() + json.tool.main() except BrokenPipeError as exc: sys.exit(exc.errno) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index fb9e4592b411c2..615343acc4e729 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -1,16 +1,78 @@ +import argparse +import json import sys import warnings -from . import __main__ - def main(): - warnings.warn('The json.tool module is deprecated', - DeprecationWarning, stacklevel=2) - __main__.main() + prog = 'python -m json.tool' + description = ('A simple command line interface for json module ' + 'to validate and pretty-print JSON objects.') + parser = argparse.ArgumentParser(prog=prog, description=description) + parser.add_argument('infile', nargs='?', + help='a JSON file to be validated or pretty-printed', + default='-') + parser.add_argument('outfile', nargs='?', + help='write the output of infile to outfile', + default=None) + parser.add_argument('--sort-keys', action='store_true', default=False, + help='sort the output of dictionaries alphabetically by key') + parser.add_argument('--no-ensure-ascii', dest='ensure_ascii', action='store_false', + help='disable escaping of non-ASCII characters') + parser.add_argument('--json-lines', action='store_true', default=False, + help='parse input using the JSON Lines format. ' + 'Use with --no-indent or --compact to produce valid JSON Lines output.') + group = parser.add_mutually_exclusive_group() + group.add_argument('--indent', default=4, type=int, + 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='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='suppress all whitespace separation (most compact)') + options = parser.parse_args() + + dump_args = { + 'sort_keys': options.sort_keys, + 'indent': options.indent, + 'ensure_ascii': options.ensure_ascii, + } + if options.compact: + dump_args['indent'] = None + dump_args['separators'] = ',', ':' + + try: + if options.infile == '-': + infile = sys.stdin + else: + infile = open(options.infile, encoding='utf-8') + try: + if options.json_lines: + objs = (json.loads(line) for line in infile) + else: + objs = (json.load(infile),) + finally: + if infile is not sys.stdin: + infile.close() + + if options.outfile is None: + outfile = sys.stdout + else: + outfile = open(options.outfile, 'w', encoding='utf-8') + with outfile: + for obj in objs: + json.dump(obj, outfile, **dump_args) + outfile.write('\n') + except ValueError as e: + raise SystemExit(e) if __name__ == '__main__': + warnings.warn('The json.tool module is deprecated', DeprecationWarning) try: main() except BrokenPipeError as exc: diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index fc9149dbd8d363..eb568359c6b132 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -254,4 +254,5 @@ def test_with_warnings(self): rc, out, err = assert_python_ok('-m', self.module, infile) self.assertEqual(rc, 0) self.assertEqual(out.splitlines(), self.expect.encode().splitlines()) - self.assertEqual(err.decode(), f'{filename}:15: DeprecationWarning: The json.tool module is deprecated\n main()\n') + self.assertRegex(err.decode(), + r'^.+/json/tool\.py:\d+: DeprecationWarning: The json.tool module is deprecated\n.+\n$') From 834fbd7b37fdcf398da14c452e0673dcf11907ef Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Fri, 9 Aug 2024 15:55:44 -0700 Subject: [PATCH 05/24] Remove deprecation warning --- Lib/json/tool.py | 2 -- Lib/test/test_json/test_tool.py | 48 ++++++++++----------------------- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 615343acc4e729..8f3899a9b7861c 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -1,7 +1,6 @@ import argparse import json import sys -import warnings def main(): @@ -72,7 +71,6 @@ def main(): if __name__ == '__main__': - warnings.warn('The json.tool module is deprecated', DeprecationWarning) try: main() except BrokenPipeError as exc: diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index eb568359c6b132..5da7cdcad709fa 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -20,8 +20,6 @@ class TestMain(unittest.TestCase): :"yes"} ] """ module = 'json' - env_vars = {} - warnings_expected = False expect_without_sort_keys = textwrap.dedent("""\ [ @@ -93,8 +91,7 @@ def test_stdin_stdout(self): args = sys.executable, '-m', self.module process = subprocess.run(args, input=self.data, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, self.expect) - if not self.warnings_expected: - self.assertEqual(process.stderr, '') + self.assertEqual(process.stderr, '') def _create_infile(self, data=None): infile = os_helper.TESTFN @@ -105,7 +102,7 @@ def _create_infile(self, data=None): def test_infile_stdout(self): infile = self._create_infile() - rc, out, err = assert_python_ok('-m', self.module, infile, **self.env_vars) + rc, out, err = assert_python_ok('-m', self.module, infile) self.assertEqual(rc, 0) self.assertEqual(out.splitlines(), self.expect.encode().splitlines()) self.assertEqual(err, b'') @@ -119,7 +116,7 @@ def test_non_ascii_infile(self): ''').encode() infile = self._create_infile(data) - rc, out, err = assert_python_ok('-m', self.module, infile, **self.env_vars) + rc, out, err = assert_python_ok('-m', self.module, infile) self.assertEqual(rc, 0) self.assertEqual(out.splitlines(), expect.splitlines()) @@ -128,7 +125,7 @@ def test_non_ascii_infile(self): def test_infile_outfile(self): infile = self._create_infile() outfile = os_helper.TESTFN + '.out' - rc, out, err = assert_python_ok('-m', self.module, infile, outfile, **self.env_vars) + rc, out, err = assert_python_ok('-m', self.module, infile, outfile) self.addCleanup(os.remove, outfile) with open(outfile, "r", encoding="utf-8") as fp: self.assertEqual(fp.read(), self.expect) @@ -138,7 +135,7 @@ def test_infile_outfile(self): def test_writing_in_place(self): infile = self._create_infile() - rc, out, err = assert_python_ok('-m', self.module, infile, infile, **self.env_vars) + rc, out, err = assert_python_ok('-m', self.module, infile, infile) with open(infile, "r", encoding="utf-8") as fp: self.assertEqual(fp.read(), self.expect) self.assertEqual(rc, 0) @@ -149,18 +146,17 @@ def test_jsonlines(self): args = sys.executable, '-m', self.module, '--json-lines' process = subprocess.run(args, input=self.jsonlines_raw, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, self.jsonlines_expect) - if not self.warnings_expected: - self.assertEqual(process.stderr, '') + self.assertEqual(process.stderr, '') def test_help_flag(self): - rc, out, err = assert_python_ok('-m', self.module, '-h', **self.env_vars) + rc, out, err = assert_python_ok('-m', self.module, '-h') self.assertEqual(rc, 0) self.assertTrue(out.startswith(b'usage: ')) self.assertEqual(err, b'') def test_sort_keys_flag(self): infile = self._create_infile() - rc, out, err = assert_python_ok('-m', self.module, '--sort-keys', infile, **self.env_vars) + rc, out, err = assert_python_ok('-m', self.module, '--sort-keys', infile) self.assertEqual(rc, 0) self.assertEqual(out.splitlines(), self.expect_without_sort_keys.encode().splitlines()) @@ -177,8 +173,7 @@ def test_indent(self): args = sys.executable, '-m', self.module, '--indent', '2' process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, expect) - if not self.warnings_expected: - self.assertEqual(process.stderr, '') + self.assertEqual(process.stderr, '') def test_no_indent(self): input_ = '[1,\n2]' @@ -186,8 +181,7 @@ def test_no_indent(self): args = sys.executable, '-m', self.module, '--no-indent' process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, expect) - if not self.warnings_expected: - self.assertEqual(process.stderr, '') + self.assertEqual(process.stderr, '') def test_tab(self): input_ = '[1, 2]' @@ -195,8 +189,7 @@ def test_tab(self): args = sys.executable, '-m', self.module, '--tab' process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, expect) - if not self.warnings_expected: - self.assertEqual(process.stderr, '') + self.assertEqual(process.stderr, '') def test_compact(self): input_ = '[ 1 ,\n 2]' @@ -204,14 +197,13 @@ def test_compact(self): args = sys.executable, '-m', self.module, '--compact' process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True) self.assertEqual(process.stdout, expect) - if not self.warnings_expected: - self.assertEqual(process.stderr, '') + self.assertEqual(process.stderr, '') def test_no_ensure_ascii_flag(self): infile = self._create_infile('{"key":"💩"}') outfile = os_helper.TESTFN + '.out' self.addCleanup(os.remove, outfile) - assert_python_ok('-m', self.module, '--no-ensure-ascii', infile, outfile, **self.env_vars) + assert_python_ok('-m', self.module, '--no-ensure-ascii', infile, outfile) with open(outfile, "rb") as f: lines = f.read().splitlines() # asserting utf-8 encoded output file @@ -222,7 +214,7 @@ def test_ensure_ascii_default(self): infile = self._create_infile('{"key":"💩"}') outfile = os_helper.TESTFN + '.out' self.addCleanup(os.remove, outfile) - assert_python_ok('-m', self.module, infile, outfile, **self.env_vars) + assert_python_ok('-m', self.module, infile, outfile) with open(outfile, "rb") as f: lines = f.read().splitlines() # asserting an ascii encoded output file @@ -244,15 +236,3 @@ def test_broken_pipe_error(self): @support.requires_subprocess() class TestTool(TestMain): module = 'json.tool' - env_vars = {'PYTHONWARNINGS': 'ignore'} - warnings_expected = True - - def test_with_warnings(self): - import json.tool - filename = json.tool.__file__ - infile = self._create_infile() - rc, out, err = assert_python_ok('-m', self.module, infile) - self.assertEqual(rc, 0) - self.assertEqual(out.splitlines(), self.expect.encode().splitlines()) - self.assertRegex(err.decode(), - r'^.+/json/tool\.py:\d+: DeprecationWarning: The json.tool module is deprecated\n.+\n$') From 62afcf181ebe3e79f705f3735dbb2e805cbdbb46 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 08:26:09 -0700 Subject: [PATCH 06/24] Fix module name as shown at command-line --- Lib/json/tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 8f3899a9b7861c..c9c1a4b4d19d85 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -4,7 +4,7 @@ def main(): - prog = 'python -m json.tool' + prog = 'python -m json' description = ('A simple command line interface for json module ' 'to validate and pretty-print JSON objects.') parser = argparse.ArgumentParser(prog=prog, description=description) From f58e60ae5a8599d6c9a14046f1ce37223e7d114d Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 12:32:10 -0700 Subject: [PATCH 07/24] Update docs and what's new for json.tool to json --- Doc/library/json.rst | 31 +++++++++++++++++++------------ Doc/whatsnew/3.14.rst | 3 +++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 42cb1f850fe9c5..c1891b346839f1 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -116,15 +116,15 @@ Extending :class:`JSONEncoder`:: ['[2.0', ', 1.0', ']'] -Using :mod:`json.tool` from the shell to validate and pretty-print: +Using :mod:`json` from the shell to validate and pretty-print: .. code-block:: shell-session - $ echo '{"json":"obj"}' | python -m json.tool + $ echo '{"json":"obj"}' | python -m json { "json": "obj" } - $ echo '{1.2:3.4}' | python -m json.tool + $ echo '{1.2:3.4}' | python -m json Expecting property name enclosed in double quotes: line 1 column 2 (char 1) See :ref:`json-commandline` for detailed documentation. @@ -678,31 +678,32 @@ when serializing instances of "exotic" numerical types such as .. _json-commandline: -.. program:: json.tool +.. program:: json -Command Line Interface +Command-Line Interface ---------------------- -.. module:: json.tool +.. module:: json :synopsis: A command line to validate and pretty-print JSON. **Source code:** :source:`Lib/json/tool.py` -------------- -The :mod:`json.tool` module provides a simple command line interface to validate -and pretty-print JSON objects. +The :mod:`json` module can be invoked as a script, +using the interpreter's :option:`-m` switch, +to validate and pretty-print JSON objects. If the optional ``infile`` and ``outfile`` arguments are not specified, :data:`sys.stdin` and :data:`sys.stdout` will be used respectively: .. code-block:: shell-session - $ echo '{"json": "obj"}' | python -m json.tool + $ echo '{"json": "obj"}' | python -m json { "json": "obj" } - $ echo '{1.2:3.4}' | python -m json.tool + $ echo '{1.2:3.4}' | python -m json Expecting property name enclosed in double quotes: line 1 column 2 (char 1) .. versionchanged:: 3.5 @@ -710,8 +711,14 @@ specified, :data:`sys.stdin` and :data:`sys.stdout` will be used respectively: :option:`--sort-keys` option to sort the output of dictionaries alphabetically by key. +.. versionchanged:: 3.14 + The :mod:`json` module may now be executed as a script. + + Previously :mod:`json.tool` provided the :mod:`json` module's + command-line interface. + -Command line options +Command-line options ^^^^^^^^^^^^^^^^^^^^ .. option:: infile @@ -720,7 +727,7 @@ Command line options .. code-block:: shell-session - $ python -m json.tool mp_films.json + $ python -m json mp_films.json [ { "title": "And Now for Something Completely Different", diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index a7c817c47d66c6..0fce13189f1890 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -124,6 +124,9 @@ Add notes for JSON serialization errors that allow to identify the source of the error. (Contributed by Serhiy Storchaka in :gh:`122163`.) +Enable :mod:`json` module to work as a script using the :option:`-m` switch. +(Contributed by Trey Hunner in :gh:`122873`.) + operator -------- From 168082a3a4965ea6a56bf49b0b855b4eea430d9d Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 12:19:18 -0700 Subject: [PATCH 08/24] Remove duplicate json module description from docs --- Doc/library/json.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index c1891b346839f1..b1bbe2a9bc1333 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -683,9 +683,6 @@ when serializing instances of "exotic" numerical types such as Command-Line Interface ---------------------- -.. module:: json - :synopsis: A command line to validate and pretty-print JSON. - **Source code:** :source:`Lib/json/tool.py` -------------- From 52039ee1c2072d02a0b45b0ecd560dc866c8fbb4 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 12:22:33 -0700 Subject: [PATCH 09/24] Give up on marking `-m` as an option --- Doc/library/json.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index b1bbe2a9bc1333..3f9394a8c0018a 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -688,7 +688,7 @@ Command-Line Interface -------------- The :mod:`json` module can be invoked as a script, -using the interpreter's :option:`-m` switch, +using the interpreter's ``-m`` switch, to validate and pretty-print JSON objects. If the optional ``infile`` and ``outfile`` arguments are not From 2e3747596efc6cb4a52c1c6ca5ec86d7a2d1845f Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 12:23:43 -0700 Subject: [PATCH 10/24] Remove unnecessary newline in changed note --- Doc/library/json.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 3f9394a8c0018a..fd1977ada83c29 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -710,7 +710,6 @@ specified, :data:`sys.stdin` and :data:`sys.stdout` will be used respectively: .. versionchanged:: 3.14 The :mod:`json` module may now be executed as a script. - Previously :mod:`json.tool` provided the :mod:`json` module's command-line interface. From efd0503d37a0d586773f2d802ca02d1b874b8df5 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 12:43:26 -0700 Subject: [PATCH 11/24] Remove now-invalid json.tool module reference --- Doc/library/json.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index fd1977ada83c29..ee3fb7b173a476 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -710,7 +710,7 @@ specified, :data:`sys.stdin` and :data:`sys.stdout` will be used respectively: .. versionchanged:: 3.14 The :mod:`json` module may now be executed as a script. - Previously :mod:`json.tool` provided the :mod:`json` module's + Previously ``json.tool`` provided the :mod:`json` module's command-line interface. From c6bb21136cfda61786cb4d0f1940b494eea48c57 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 14:17:19 -0700 Subject: [PATCH 12/24] Add news entry with blurb --- .../next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst diff --git a/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst b/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst new file mode 100644 index 00000000000000..3247ef2ffda083 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst @@ -0,0 +1 @@ +Enable :mod:`json` module to work as a script using the :option:`-m` switch. From e768e39dd12f74314460e7b97d6983240a59c303 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 14:38:34 -0700 Subject: [PATCH 13/24] Remove older raw string usage in docstring Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Lib/json/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/json/__main__.py b/Lib/json/__main__.py index 7d2dc2ff02e32d..809a463c9762f1 100644 --- a/Lib/json/__main__.py +++ b/Lib/json/__main__.py @@ -1,4 +1,4 @@ -r"""Command-line tool to validate and pretty-print JSON +"""Command-line tool to validate and pretty-print JSON Usage:: From a6b5c0d6c82aee29a02a4eb7c46dccad26c02997 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 14:39:31 -0700 Subject: [PATCH 14/24] Use sentence case in section title Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/json.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index ee3fb7b173a476..ac6e5c653e13aa 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -680,7 +680,7 @@ when serializing instances of "exotic" numerical types such as .. _json-commandline: .. program:: json -Command-Line Interface +Command-line interface ---------------------- **Source code:** :source:`Lib/json/tool.py` From 3105bc2def58bbe31e0eb939abf33a7ffacf8734 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 14:40:21 -0700 Subject: [PATCH 15/24] Add CLI invocation for easier copy-pasting Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/json.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index ac6e5c653e13aa..0d91e37463afcb 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -687,8 +687,7 @@ Command-line interface -------------- -The :mod:`json` module can be invoked as a script, -using the interpreter's ``-m`` switch, +The :mod:`json` module can be invoked as a script via ``python -m json`` to validate and pretty-print JSON objects. If the optional ``infile`` and ``outfile`` arguments are not From 238fc57fbb843d5925e360b6ad56a95ddbf70f7a Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 15:53:41 -0700 Subject: [PATCH 16/24] Update json.__init__ docstring for python -m json --- Lib/json/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py index ed2c74771ea87d..1d972d22ded072 100644 --- a/Lib/json/__init__.py +++ b/Lib/json/__init__.py @@ -86,13 +86,13 @@ '[2.0, 1.0]' -Using json.tool from the shell to validate and pretty-print:: +Using json from the shell to validate and pretty-print:: - $ echo '{"json":"obj"}' | python -m json.tool + $ echo '{"json":"obj"}' | python -m json { "json": "obj" } - $ echo '{ 1.2:3.4}' | python -m json.tool + $ echo '{ 1.2:3.4}' | python -m json Expecting property name enclosed in double quotes: line 1 column 3 (char 2) """ __version__ = '2.0.9' From 7f704bf7018dc7706c529c48243fd8504b395bc6 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 15:57:07 -0700 Subject: [PATCH 17/24] Note `python -m json` in What's New --- Doc/whatsnew/3.14.rst | 3 ++- .../Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 0fce13189f1890..d317355ffc6a3c 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -124,7 +124,8 @@ Add notes for JSON serialization errors that allow to identify the source of the error. (Contributed by Serhiy Storchaka in :gh:`122163`.) -Enable :mod:`json` module to work as a script using the :option:`-m` switch. +Enable :mod:`json` module to work as a script using the :option:`-m` switch: ``python -m json``. +See :ref:`JSON command-line interface`. (Contributed by Trey Hunner in :gh:`122873`.) operator diff --git a/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst b/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst index 3247ef2ffda083..7072b0ebf52441 100644 --- a/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst +++ b/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst @@ -1 +1,3 @@ -Enable :mod:`json` module to work as a script using the :option:`-m` switch. +Enable :mod:`json` module to work as a script using the :option:`-m` switch: ``python -m json``. +See :ref:`JSON command-line interface`. +Patch by Trey Hunner. From fba68acf14ca504521645b509246e6a12c5eb07a Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 10 Aug 2024 16:08:37 -0700 Subject: [PATCH 18/24] Use SystemExit consistently in json package --- Lib/json/__main__.py | 3 +-- Lib/json/tool.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/json/__main__.py b/Lib/json/__main__.py index 809a463c9762f1..1808eaddb62776 100644 --- a/Lib/json/__main__.py +++ b/Lib/json/__main__.py @@ -11,11 +11,10 @@ """ import json.tool -import sys if __name__ == '__main__': try: json.tool.main() except BrokenPipeError as exc: - sys.exit(exc.errno) + raise SystemExit(exc.errno) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index c9c1a4b4d19d85..8ea2343415688a 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -74,4 +74,4 @@ def main(): try: main() except BrokenPipeError as exc: - sys.exit(exc.errno) + raise SystemExit(exc.errno) From 8662c81a621807aee09aedd6dc6599f4c83b8c50 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sun, 11 Aug 2024 20:16:31 -0700 Subject: [PATCH 19/24] Improve grammar in news --- Doc/whatsnew/3.14.rst | 2 +- .../next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index d317355ffc6a3c..7bb7de11a88c57 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -125,7 +125,7 @@ of the error. (Contributed by Serhiy Storchaka in :gh:`122163`.) Enable :mod:`json` module to work as a script using the :option:`-m` switch: ``python -m json``. -See :ref:`JSON command-line interface`. +See the :ref:`JSON command-line interface ` documentation. (Contributed by Trey Hunner in :gh:`122873`.) operator diff --git a/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst b/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst index 7072b0ebf52441..002ebd9d925956 100644 --- a/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst +++ b/Misc/NEWS.d/next/Library/2024-08-10-14-16-59.gh-issue-122873.XlHaUn.rst @@ -1,3 +1,3 @@ Enable :mod:`json` module to work as a script using the :option:`-m` switch: ``python -m json``. -See :ref:`JSON command-line interface`. +See the :ref:`JSON command-line interface ` documentation. Patch by Trey Hunner. From 52129e6aa5ec451f2aad394ebc842c4ee9e3e337 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Mon, 12 Aug 2024 07:00:47 -0700 Subject: [PATCH 20/24] Add json.tool submodule documentation reference back in. Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/library/json.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 0d91e37463afcb..e0d59ea57e3839 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -683,6 +683,9 @@ when serializing instances of "exotic" numerical types such as Command-line interface ---------------------- +.. module:: json.tool + :synopsis: A command-line interface to validate and pretty-print JSON. + **Source code:** :source:`Lib/json/tool.py` -------------- From 016206dc78cf7557a2d6df5efebcb5efdcbe9a0c Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Mon, 12 Aug 2024 07:01:05 -0700 Subject: [PATCH 21/24] Note that json.tool still works for compatibility reasons Co-authored-by: Alyssa Coghlan --- Doc/library/json.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index e0d59ea57e3839..ded4fad4fd990f 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -711,9 +711,9 @@ specified, :data:`sys.stdin` and :data:`sys.stdout` will be used respectively: alphabetically by key. .. versionchanged:: 3.14 - The :mod:`json` module may now be executed as a script. - Previously ``json.tool`` provided the :mod:`json` module's - command-line interface. + The :mod:`json` module may now be directly executed as + ``python -m json``. For backwards compatibility, invoking + the CLI as ``python -m json.tool`` remains supported. Command-line options From 9b22285505b58d1da50619f41396245e2bf8221b Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Mon, 12 Aug 2024 07:02:22 -0700 Subject: [PATCH 22/24] Note that json.tool implements the CLI Co-authored-by: Alyssa Coghlan --- Doc/library/json.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/json.rst b/Doc/library/json.rst index ded4fad4fd990f..26f85b5ddf8d82 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -691,7 +691,8 @@ Command-line interface -------------- The :mod:`json` module can be invoked as a script via ``python -m json`` -to validate and pretty-print JSON objects. +to validate and pretty-print JSON objects. The :mod:`json.tool` submodule +implements this interface. If the optional ``infile`` and ``outfile`` arguments are not specified, :data:`sys.stdin` and :data:`sys.stdout` will be used respectively: From 9fd0c1c017638a7c163e7576bf9863ef237f840d Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Mon, 12 Aug 2024 07:10:32 -0700 Subject: [PATCH 23/24] Reference json.__main__ in json.tool docstring Co-authored-by: Alyssa Coghlan --- Lib/json/tool.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 8ea2343415688a..9028e517fb9f7d 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -1,3 +1,8 @@ +"""Command-line tool to validate and pretty-print JSON + +See `json.__main__` for a usage example (invocation as +`python -m json.tool` is supported for backwards compatibility). +""" import argparse import json import sys From 734fbf9e3bdf5120360b6ebdb69cc1d0e2924496 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Tue, 13 Aug 2024 07:58:11 -0700 Subject: [PATCH 24/24] Switch json.tool to json on listing of Python CLIs --- Doc/library/cmdline.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/cmdline.rst b/Doc/library/cmdline.rst index 5174515ffc23ed..dd538cdb754efb 100644 --- a/Doc/library/cmdline.rst +++ b/Doc/library/cmdline.rst @@ -23,7 +23,7 @@ The following modules have a command-line interface. * :ref:`http.server ` * :mod:`!idlelib` * :ref:`inspect ` -* :ref:`json.tool ` +* :ref:`json ` * :mod:`mimetypes` * :mod:`pdb` * :mod:`pickle`