Skip to content

Commit a932631

Browse files
bpo-45644: Make json.tool read infile before writing to outfile (GH-29273) (GH-29446)
so that $ python -m json.tool foo.json foo.json doesn't result in an empty foo.json. Co-authored-by: Łukasz Langa <[email protected]> (cherry picked from commit 815dad4) Co-authored-by: Chris Wesseling <[email protected]>
1 parent 5017306 commit a932631

File tree

3 files changed

+26
-7
lines changed

3 files changed

+26
-7
lines changed

Lib/json/tool.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import argparse
1414
import json
1515
import sys
16+
from pathlib import Path
1617

1718

1819
def main():
@@ -25,9 +26,9 @@ def main():
2526
help='a JSON file to be validated or pretty-printed',
2627
default=sys.stdin)
2728
parser.add_argument('outfile', nargs='?',
28-
type=argparse.FileType('w', encoding="utf-8"),
29+
type=Path,
2930
help='write the output of infile to outfile',
30-
default=sys.stdout)
31+
default=None)
3132
parser.add_argument('--sort-keys', action='store_true', default=False,
3233
help='sort the output of dictionaries alphabetically by key')
3334
parser.add_argument('--no-ensure-ascii', dest='ensure_ascii', action='store_false',
@@ -58,15 +59,21 @@ def main():
5859
dump_args['indent'] = None
5960
dump_args['separators'] = ',', ':'
6061

61-
with options.infile as infile, options.outfile as outfile:
62+
with options.infile as infile:
6263
try:
6364
if options.json_lines:
6465
objs = (json.loads(line) for line in infile)
6566
else:
66-
objs = (json.load(infile), )
67-
for obj in objs:
68-
json.dump(obj, outfile, **dump_args)
69-
outfile.write('\n')
67+
objs = (json.load(infile),)
68+
69+
if options.outfile is None:
70+
out = sys.stdout
71+
else:
72+
out = options.outfile.open('w', encoding='utf-8')
73+
with out as outfile:
74+
for obj in objs:
75+
json.dump(obj, outfile, **dump_args)
76+
outfile.write('\n')
7077
except ValueError as e:
7178
raise SystemExit(e)
7279

Lib/test/test_json/test_tool.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,15 @@ def test_infile_outfile(self):
130130
self.assertEqual(out, b'')
131131
self.assertEqual(err, b'')
132132

133+
def test_writing_in_place(self):
134+
infile = self._create_infile()
135+
rc, out, err = assert_python_ok('-m', 'json.tool', infile, infile)
136+
with open(infile, "r", encoding="utf-8") as fp:
137+
self.assertEqual(fp.read(), self.expect)
138+
self.assertEqual(rc, 0)
139+
self.assertEqual(out, b'')
140+
self.assertEqual(err, b'')
141+
133142
def test_jsonlines(self):
134143
args = sys.executable, '-m', 'json.tool', '--json-lines'
135144
process = subprocess.run(args, input=self.jsonlines_raw, capture_output=True, text=True, check=True)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
In-place JSON file formatting using ``python3 -m json.tool infile infile``
2+
now works correctly, previously it left the file empty. Patch by Chris
3+
Wesseling.

0 commit comments

Comments
 (0)