bpo-33927: Add support for same infile and outfile to json.tool by remilapeyre · Pull Request #7865 · python/cpython (original) (raw)
@@ -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):
@@ -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'')
@@ -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)
@@ -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('''\
@@ -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