From cc9ee366577502a7321d2cfd9dbeed77b7901128 Mon Sep 17 00:00:00 2001 From: "M. Kocher" Date: Mon, 15 Sep 2025 19:00:17 -0700 Subject: [PATCH 1/3] Close file after running the generator --- Lib/json/tool.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 050c2fe2161e3e..4bb7f7a74e05a7 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -87,14 +87,11 @@ def main(): 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.json_lines: + objs = (json.loads(line) for line in infile) + else: + objs = (json.load(infile),) if options.outfile is None: outfile = sys.stdout @@ -111,6 +108,8 @@ def main(): for obj in objs: json.dump(obj, outfile, **dump_args) outfile.write('\n') + if infile is not sys.stdin: + infile.close() except ValueError as e: raise SystemExit(e) From f72edc5d059ef08df078b9b29b490f2809c36815 Mon Sep 17 00:00:00 2001 From: "M. Kocher" Date: Mon, 15 Sep 2025 19:00:41 -0700 Subject: [PATCH 2/3] Add test for json lines with an explicit file --- Lib/test/test_json/test_tool.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index 30f9bb3331605c..f49e98c857fe11 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -156,6 +156,14 @@ def test_jsonlines(self): self.assertEqual(process.stdout, self.jsonlines_expect) self.assertEqual(process.stderr, '') + @force_not_colorized + def test_jsonlines_with_file(self): + infile = self._create_infile(self.jsonlines_raw) + args = sys.executable, '-m', self.module, '--json-lines', infile + process = subprocess.run(args, capture_output=True, text=True, check=True) + self.assertEqual(process.stdout, self.jsonlines_expect) + self.assertEqual(process.stderr, '') + def test_help_flag(self): rc, out, err = assert_python_ok('-m', self.module, '-h', PYTHON_COLORS='0') From 2d5e1bf59ca2447d0743b57a982cff37811a907b Mon Sep 17 00:00:00 2001 From: "M. Kocher" Date: Tue, 16 Sep 2025 16:19:16 -0700 Subject: [PATCH 3/3] Use context managers for in and out --- Lib/json/tool.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 4bb7f7a74e05a7..7f17e949f00a96 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -84,20 +84,23 @@ def main(): try: if options.infile == '-': - infile = sys.stdin + in_f = sys.stdin.fileno() else: - infile = open(options.infile, encoding='utf-8') - - if options.json_lines: - objs = (json.loads(line) for line in infile) - else: - objs = (json.load(infile),) + in_f = options.infile if options.outfile is None: - outfile = sys.stdout + out_f = sys.stdout.fileno() else: - outfile = open(options.outfile, 'w', encoding='utf-8') - with outfile: + out_f = options.outfile + + with (open(in_f, 'r', encoding='utf-8') as infile, + open(out_f, 'w', encoding='utf-8') as outfile): + + if options.json_lines: + objs = (json.loads(line) for line in infile) + else: + objs = [json.load(infile)] + if can_colorize(file=outfile): t = get_theme(tty_file=outfile).syntax for obj in objs: @@ -108,8 +111,7 @@ def main(): for obj in objs: json.dump(obj, outfile, **dump_args) outfile.write('\n') - if infile is not sys.stdin: - infile.close() + except ValueError as e: raise SystemExit(e)