bpo-33927: Add support for same infile and outfile to json.tool by remilapeyre · Pull Request #7865 · python/cpython (original) (raw)

Expand Up

@@ -4,9 +4,12 @@

import textwrap

import unittest

import subprocess

import io

import types

from test import support

from test.support.script_helper import assert_python_ok

from test.support.script_helper import assert_python_ok, assert_python_failure

from unittest import mock

class TestTool(unittest.TestCase):

Expand Down Expand Up

@@ -100,7 +103,6 @@ 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)

self.assertEqual(rc, 0)

self.assertEqual(out.splitlines(), self.expect.encode().splitlines())

self.assertEqual(err, b'')

Expand All

@@ -126,10 +128,22 @@ def test_infile_outfile(self):

self.addCleanup(os.remove, outfile)

with open(outfile, "r") as fp:

self.assertEqual(fp.read(), self.expect)

self.assertEqual(rc, 0)

self.assertEqual(out, b'')

self.assertEqual(err, b'')

def test_infile_same_outfile(self):

infile = self._create_infile()

rc, out, err = assert_python_ok('-m', 'json.tool', '-i', infile)

self.assertEqual(out, b'')

self.assertEqual(err, b'')

def test_unavailable_outfile(self):

infile = self._create_infile()

rc, out, err = assert_python_failure('-m', 'json.tool', infile, '/bla/outfile')

self.assertEqual(rc, 2)

self.assertEqual(out, b'')

self.assertIn(b"error: can't open '/bla/outfile': [Errno 2]", err)

def test_jsonlines(self):

args = sys.executable, '-m', 'json.tool', '--json-lines'

process = subprocess.run(args, input=self.jsonlines_raw, capture_output=True, text=True, check=True)

Expand All

@@ -138,18 +152,64 @@ def test_jsonlines(self):

def test_help_flag(self):

rc, out, err = assert_python_ok('-m', 'json.tool', '-h')

self.assertEqual(rc, 0)

self.assertTrue(out.startswith(b'usage: '))

self.assertEqual(err, b'')

def test_inplace_flag(self):

rc, out, err = assert_python_failure('-m', 'json.tool', '-i')

self.assertEqual(out, b'')

self.assertIn(b"error: infile must be set when -i / --in-place is used", err)

rc, out, err = assert_python_failure('-m', 'json.tool', '-i', '-')

self.assertEqual(out, b'')

self.assertIn(b"error: infile must be set when -i / --in-place is used", err)

infile = self._create_infile()

rc, out, err = assert_python_failure('-m', 'json.tool', '-i', infile, 'test.json')

self.assertEqual(out, b'')

self.assertIn(b"error: outfile cannot be set when -i / --in-place is used", err)

def test_inplace_jsonlines(self):

infile = self._create_infile(data=self.jsonlines_raw)

rc, out, err = assert_python_ok('-m', 'json.tool', '--json-lines', '-i', infile)

self.assertEqual(out, b'')

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)

self.assertEqual(rc, 0)

self.assertEqual(out.splitlines(),

self.expect_without_sort_keys.encode().splitlines())

self.assertEqual(err, b'')

def test_no_fd_leak_infile_outfile(self):

infile = self._create_infile()

closed, opened, open = mock_open()

with mock.patch('builtins.open', side_effect=open):

with mock.patch.object(sys, 'argv', ['tool.py', infile, infile + '.out']):

import json.tool

json.tool.main()

os.unlink(infile + '.out')

self.assertEqual(set(opened), set(closed))

self.assertEqual(len(opened), 2)

self.assertEqual(len(opened), 2)

def test_no_fd_leak_same_infile_outfile(self):

infile = self._create_infile()

closed, opened, open = mock_open()

with mock.patch('builtins.open', side_effect=open):

with mock.patch.object(sys, 'argv', ['tool.py', '-i', infile]):

try:

import json.tool

json.tool.main()

except SystemExit:

pass

self.assertEqual(opened, closed)

self.assertEqual(len(opened), 2)

self.assertEqual(len(opened), 2)

def test_indent(self):

input_ = '[1, 2]'

expect = textwrap.dedent('''\

Expand Down Expand Up

@@ -219,3 +279,20 @@ def test_broken_pipe_error(self):

proc.stdout.close()

proc.communicate(b'"{}"')

self.assertEqual(proc.returncode, errno.EPIPE)

def mock_open():

closed = []

opened = []

io_open = io.open

def _open(*args, **kwargs):

fd = io_open(*args, **kwargs)

opened.append(fd)

fd_close = fd.close

def close(self):

closed.append(self)

fd_close()

fd.close = types.MethodType(close, fd)

return fd

return closed, opened, _open