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