diff --git a/Lib/lzma.py b/Lib/lzma.py --- a/Lib/lzma.py +++ b/Lib/lzma.py @@ -19,20 +19,21 @@ container formats, as well as raw compre "LZMACompressor", "LZMADecompressor", "LZMAFile", "LZMAError", "open", "compress", "decompress", "is_check_supported", ] import builtins import io from _lzma import * from _lzma import _encode_filter_properties, _decode_filter_properties import _compression +from os import PathLike _MODE_CLOSED = 0 _MODE_READ = 1 # Value 2 no longer used _MODE_WRITE = 3 class LZMAFile(_compression.BaseStream): @@ -42,23 +43,24 @@ class LZMAFile(_compression.BaseStream): refer directly to a named file on disk. Note that LZMAFile provides a *binary* file interface - data read is returned as bytes, and data to be written must be given as bytes. """ def __init__(self, filename=None, mode="r", *, format=None, check=-1, preset=None, filters=None): """Open an LZMA-compressed file in binary mode. - filename can be either an actual file name (given as a str or - bytes object), in which case the named file is opened, or it can - be an existing file object to read from or write to. + filename can be either an actual file name (given as a str, + bytes, or PathLike object), in which case the named file is + opened, or it can be an existing file object to read from or + write to. mode can be "r" for reading (default), "w" for (over)writing, "x" for creating exclusively, or "a" for appending. These can equivalently be given as "rb", "wb", "xb" and "ab" respectively. format specifies the container format to use for the file. If mode is "r", this defaults to FORMAT_AUTO. Otherwise, the default is FORMAT_XZ. check specifies the integrity check to use. This argument can @@ -105,21 +107,21 @@ class LZMAFile(_compression.BaseStream): elif mode in ("w", "wb", "a", "ab", "x", "xb"): if format is None: format = FORMAT_XZ mode_code = _MODE_WRITE self._compressor = LZMACompressor(format=format, check=check, preset=preset, filters=filters) self._pos = 0 else: raise ValueError("Invalid mode: {!r}".format(mode)) - if isinstance(filename, (str, bytes)): + if isinstance(filename, (str, bytes, PathLike)): if "b" not in mode: mode += "b" self._fp = builtins.open(filename, mode) self._closefp = True self._mode = mode_code elif hasattr(filename, "read") or hasattr(filename, "write"): self._fp = filename self._mode = mode_code else: raise TypeError("filename must be a str or bytes object, or a file") @@ -256,23 +258,23 @@ class LZMAFile(_compression.BaseStream): if self._mode == _MODE_READ: return self._buffer.tell() return self._pos def open(filename, mode="rb", *, format=None, check=-1, preset=None, filters=None, encoding=None, errors=None, newline=None): """Open an LZMA-compressed file in binary or text mode. - filename can be either an actual file name (given as a str or bytes - object), in which case the named file is opened, or it can be an - existing file object to read from or write to. + filename can be either an actual file name (given as a str, bytes, + or PathLike object), in which case the named file is opened, or it + can be an existing file object to read from or write to. The mode argument can be "r", "rb" (default), "w", "wb", "x", "xb", "a", or "ab" for binary mode, or "rt", "wt", "xt", or "at" for text mode. The format, check, preset and filters arguments specify the compression settings, as for LZMACompressor, LZMADecompressor and LZMAFile. For binary mode, this function is equivalent to the LZMAFile diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -1,13 +1,14 @@ import _compression from io import BytesIO, UnsupportedOperation, DEFAULT_BUFFER_SIZE import os +from pathlib import Path import pickle import random import unittest from test.support import ( _4G, TESTFN, import_module, bigmemtest, run_unittest, unlink ) lzma = import_module("lzma") from lzma import LZMACompressor, LZMADecompressor, LZMAError, LZMAFile @@ -474,20 +475,30 @@ class FileTestCase(unittest.TestCase): def test_init(self): with LZMAFile(BytesIO(COMPRESSED_XZ)) as f: pass with LZMAFile(BytesIO(), "w") as f: pass with LZMAFile(BytesIO(), "x") as f: pass with LZMAFile(BytesIO(), "a") as f: pass + def test_init_with_PathLike(self): + pTESTFN = Path(TESTFN) + with TempFile(pTESTFN, COMPRESSED_XZ): + with LZMAFile(pTESTFN) as f: + pass + with LZMAFile(pTESTFN, "w") as f: + pass + with LZMAFile(pTESTFN, "a") as f: + pass + def test_init_with_filename(self): with TempFile(TESTFN, COMPRESSED_XZ): with LZMAFile(TESTFN) as f: pass with LZMAFile(TESTFN, "w") as f: pass with LZMAFile(TESTFN, "a") as f: pass def test_init_mode(self):