diff --git a/src/monty/io.py b/src/monty/io.py index 0ddc566d..86bde2c5 100644 --- a/src/monty/io.py +++ b/src/monty/io.py @@ -43,12 +43,11 @@ def zopen( - Always explicitly specify binary/text in `mode`, i.e. always pass `t` or `b` in `mode`, implicit binary/text mode would not be allow in future versions. - - When using text mode, always provide an explicit `encoding`. + - Always provide an explicit `encoding` in text mode. Args: - filename (str | Path): Filename. - mode (str): The mode in which the file is opened ( - e.g., "r" for read, "w" for write.), you MUST + filename (str | Path): The file to open. + mode (str): The mode in which the file is opened, you MUST explicitly specify "b" for binary or "t" for text. **kwargs: Additional keyword arguments to pass to `open`. diff --git a/tests/test_io.py b/tests/test_io.py index 2ca159af..bf4a0e3f 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -7,6 +7,7 @@ import pytest from monty.io import ( + EncodingWarning, FileLock, FileLockException, reverse_readfile, @@ -162,50 +163,80 @@ def test_line_ending(self): class TestZopen: - """TODO: Test objectives: - - Test read/write for each type - - Test str/Path as `filename` - - Test all possible file extensions of each type - """ + @pytest.mark.parametrize("extension", [".txt", ".bz2", ".gz", ".xz", ".lzma"]) + def test_read_write_files(self, extension): + """Test read/write in binary/text mode: + - uncompressed text file: .txt + - compressed files: bz2/gz/xz/lzma + """ + filename = f"test_file{extension}" + content = "This is a test file.\n" + + with ScratchDir("."): + # Test write and read in text mode + with zopen(filename, "wt", encoding="utf-8") as f: + f.write(content) - def test_uncompressed_files(self): - pass + with zopen(Path(filename), "rt", encoding="utf-8") as f: + assert f.read() == content - def test_bzip2_files(self): - pass + # Test write and read in binary mode + with zopen(Path(filename), "wb") as f: + f.write(content.encode()) - def test_gzip_files(self): - pass + with zopen(filename, "rb") as f: + assert f.read() == content.encode() def test_fake_lzw_files(self): - # Test warning - pass - - def test_lzma_files(self): - pass - - def test_warnings(self): - # Implicit `mode` warning - - # Implicit text/binary `mode` warning - - # Default `encoding` warning - pass - - # def test_text_files(self): - # # TODO: remove static local test file - # with zopen(os.path.join(TEST_DIR, "myfile_gz.gz"), mode="rt") as f: - # assert f.read() == "HelloWorld.\n\n" - # with zopen(os.path.join(TEST_DIR, "myfile_bz2.bz2"), mode="rt") as f: - # assert f.read() == "HelloWorld.\n\n" - # with zopen(os.path.join(TEST_DIR, "myfile_bz2.bz2"), "rt") as f: - # assert f.read() == "HelloWorld.\n\n" - # with zopen(os.path.join(TEST_DIR, "myfile_xz.xz"), "rt") as f: - # assert f.read() == "HelloWorld.\n\n" - # with zopen(os.path.join(TEST_DIR, "myfile_lzma.lzma"), "rt") as f: - # assert f.read() == "HelloWorld.\n\n" - # with zopen(os.path.join(TEST_DIR, "myfile"), mode="rt") as f: - # assert f.read() == "HelloWorld.\n\n" + filename = "test.Z" + content = "This is not a real LZW compressed file.\n" + + with ( + ScratchDir("."), + pytest.warns(FutureWarning, match="compress LZW-compressed files"), + ): + # Test write and read in text mode + with zopen(filename, "wt", encoding="utf-8") as f: + f.write(content) + + with zopen(filename, "rt", encoding="utf-8") as f: + assert f.read() == content + + # Test write and read in binary mode + with zopen(filename, "wb") as f: + f.write(content.encode()) + + with zopen(filename, "rb") as f: + assert f.read() == content.encode() + + @pytest.mark.parametrize("extension", [".txt", ".bz2", ".gz", ".xz", ".lzma"]) + def test_warnings(self, extension): + filename = f"test_warning{extension}" + content = "Test warning\n" + + with ScratchDir("."): + # Default `encoding` warning + with ( + pytest.warns(EncodingWarning, match="use UTF-8 by default"), + zopen(filename, "wt") as f, + ): + f.write(content) + + # Implicit text/binary `mode` warning + with ( + pytest.warns( + FutureWarning, match="discourage using implicit binary/text" + ), + zopen(filename, "r", encoding="utf-8") as f, + ): + assert f.readline() == content + + # Implicit `mode` warning + with ( + pytest.warns(FutureWarning, match="discourage using a default `mode`"), + zopen(filename, encoding="utf-8") as f, + ): + assert f.readline() == content class TestFileLock: