bpo-31553: add --json-lines option to json.tool (#10051)

* add jsonlines option to json.tool

* code review

* fix:avoid read infile after it close

* improve doc in whatsnew 3.8
This commit is contained in:
HongWeipeng 2018-11-07 18:09:32 +08:00 committed by Serhiy Storchaka
parent 0e7497cb46
commit f194479949
5 changed files with 53 additions and 5 deletions

View File

@ -717,6 +717,12 @@ Command line options
.. versionadded:: 3.5 .. versionadded:: 3.5
.. cmdoption:: --json-lines
Parse every input line as separate JSON object.
.. versionadded:: 3.8
.. cmdoption:: -h, --help .. cmdoption:: -h, --help
Show the help message. Show the help message.

View File

@ -151,6 +151,12 @@ by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.)
The changes above have been backported to 3.7 maintenance releases. The changes above have been backported to 3.7 maintenance releases.
json.tool
---------
Add option ``--json-lines`` to parse every input line as separate JSON object.
(Contributed by Weipeng Hong in :issue:`31553`.)
os.path os.path
------- -------

View File

@ -26,19 +26,25 @@ def main():
help='write the output of infile to outfile') help='write the output of infile to outfile')
parser.add_argument('--sort-keys', action='store_true', default=False, parser.add_argument('--sort-keys', action='store_true', default=False,
help='sort the output of dictionaries alphabetically by key') help='sort the output of dictionaries alphabetically by key')
parser.add_argument('--json-lines', action='store_true', default=False,
help='parse input using the jsonlines format')
options = parser.parse_args() options = parser.parse_args()
infile = options.infile or sys.stdin infile = options.infile or sys.stdin
outfile = options.outfile or sys.stdout outfile = options.outfile or sys.stdout
sort_keys = options.sort_keys sort_keys = options.sort_keys
with infile: json_lines = options.json_lines
with infile, outfile:
try: try:
obj = json.load(infile) if json_lines:
objs = (json.loads(line) for line in infile)
else:
objs = (json.load(infile), )
for obj in objs:
json.dump(obj, outfile, sort_keys=sort_keys, indent=4)
outfile.write('\n')
except ValueError as e: except ValueError as e:
raise SystemExit(e) raise SystemExit(e)
with outfile:
json.dump(obj, outfile, sort_keys=sort_keys, indent=4)
outfile.write('\n')
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -60,6 +60,28 @@ class TestTool(unittest.TestCase):
] ]
""") """)
jsonlines_raw = textwrap.dedent("""\
{"ingredients":["frog", "water", "chocolate", "glucose"]}
{"ingredients":["chocolate","steel bolts"]}
""")
jsonlines_expect = textwrap.dedent("""\
{
"ingredients": [
"frog",
"water",
"chocolate",
"glucose"
]
}
{
"ingredients": [
"chocolate",
"steel bolts"
]
}
""")
def test_stdin_stdout(self): def test_stdin_stdout(self):
args = sys.executable, '-m', 'json.tool' args = sys.executable, '-m', 'json.tool'
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc: with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc:
@ -92,6 +114,13 @@ class TestTool(unittest.TestCase):
self.assertEqual(out, b'') self.assertEqual(out, b'')
self.assertEqual(err, b'') self.assertEqual(err, b'')
def test_jsonlines(self):
args = sys.executable, '-m', 'json.tool', '--json-lines'
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc:
out, err = proc.communicate(self.jsonlines_raw.encode())
self.assertEqual(out.splitlines(), self.jsonlines_expect.encode().splitlines())
self.assertEqual(err, b'')
def test_help_flag(self): def test_help_flag(self):
rc, out, err = assert_python_ok('-m', 'json.tool', '-h') rc, out, err = assert_python_ok('-m', 'json.tool', '-h')
self.assertEqual(rc, 0) self.assertEqual(rc, 0)

View File

@ -0,0 +1 @@
Add the --json-lines option to json.tool. Patch by hongweipeng.