From bac9e87917fe3b801e0311e5064dd1a6773bd783 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Tue, 18 Jan 2022 18:26:01 +0900 Subject: [PATCH 1/3] bpo-45644: json.tool: Use staged write for outfile. Support python -m json.tool --json-lines infile infile --- Lib/json/tool.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 0490b8c0be11dfe..410f7712b2ed65c 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -11,9 +11,28 @@ """ import argparse +import contextlib import json +import os.path import sys -from pathlib import Path +import tempfile + + +@contextlib.contextmanager +def _staged_outfile(path): + # Use tempfile to support outfile == infile case. + # See https://discuss.python.org/t/adding-atomicwrite-in-stdlib/11899 + dir, base = os.path.split(path) + fd, tempname = tempfile.mkstemp(prefix=base + '.', dir=dir) + file = os.fdopen(fd, "w", encoding="utf-8") + try: + with file: + yield file + except BaseException as e: + os.remove(tempname) + raise + else: + os.replace(tempname, path) def main(): @@ -26,7 +45,7 @@ def main(): help='a JSON file to be validated or pretty-printed', default=sys.stdin) parser.add_argument('outfile', nargs='?', - type=Path, + type=str, help='write the output of infile to outfile', default=None) parser.add_argument('--sort-keys', action='store_true', default=False, @@ -66,10 +85,12 @@ def main(): else: objs = (json.load(infile),) - if options.outfile is None: + if options.outfile is None or options.outfile == '-': + # infile (argparse.FileType) supports '-' so outfile + # supports '-' for consistency. out = sys.stdout else: - out = options.outfile.open('w', encoding='utf-8') + out = _staged_outfile(options.outfile) with out as outfile: for obj in objs: json.dump(obj, outfile, **dump_args) From 30aed801fabd1b94396980e28d32db6d96932009 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Tue, 18 Jan 2022 18:32:56 +0900 Subject: [PATCH 2/3] Add news. --- .../next/Library/2022-01-18-18-32-49.bpo-33927.2dO0-u.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2022-01-18-18-32-49.bpo-33927.2dO0-u.rst diff --git a/Misc/NEWS.d/next/Library/2022-01-18-18-32-49.bpo-33927.2dO0-u.rst b/Misc/NEWS.d/next/Library/2022-01-18-18-32-49.bpo-33927.2dO0-u.rst new file mode 100644 index 000000000000000..e3bd89785d07cfc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-01-18-18-32-49.bpo-33927.2dO0-u.rst @@ -0,0 +1,3 @@ +``python -m json.tool infile outfile`` uses staged write idiom for +``outfile``. This allows that outfile is same to infile even when +``--json-lines`` option is used. From cf4cc58080cb2e9d940d0d08d90cd807e8ba0291 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Tue, 18 Jan 2022 20:23:21 +0900 Subject: [PATCH 3/3] Update Lib/json/tool.py Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> --- 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 410f7712b2ed65c..b9155535adbc804 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -28,7 +28,7 @@ def _staged_outfile(path): try: with file: yield file - except BaseException as e: + except: os.remove(tempname) raise else: