diff -r 89821243621b Doc/library/bz2.rst --- a/Doc/library/bz2.rst Thu Jul 14 07:45:24 2016 +0300 +++ b/Doc/library/bz2.rst Thu Jul 14 17:03:46 2016 +0800 @@ -37,8 +37,8 @@ object`. As with the constructor for :class:`BZ2File`, the *filename* argument can be - an actual filename (a :class:`str` or :class:`bytes` object), or an existing - file object to read from or write to. + an actual filename (a :class:`str`, :class:`bytes` object or :class:`os.PathLike` + object), or an existing file object to read from or write to. The *mode* argument can be any of ``'r'``, ``'rb'``, ``'w'``, ``'wb'``, ``'x'``, ``'xb'``, ``'a'`` or ``'ab'`` for binary mode, or ``'rt'``, @@ -61,14 +61,18 @@ .. versionchanged:: 3.4 The ``'x'`` (exclusive creation) mode was added. + .. versionchanged:: 3.6 + Support added to accept objects implementing :class:`os.PathLike`. + .. class:: BZ2File(filename, mode='r', buffering=None, compresslevel=9) Open a bzip2-compressed file in binary mode. - If *filename* is a :class:`str` or :class:`bytes` object, open the named file - directly. Otherwise, *filename* should be a :term:`file object`, which will - be used to read or write the compressed data. + If *filename* is a :class:`str`, :class:`bytes` or :class:`os.PathLike` + object, open the named file directly. Otherwise, *filename* should be + a :term:`file object`, which will be used to read or write the compressed + data. The *mode* argument can be either ``'r'`` for reading (default), ``'w'`` for overwriting, ``'x'`` for exclusive creation, or ``'a'`` for appending. These @@ -128,6 +132,10 @@ The :meth:`~io.BufferedIOBase.read` method now accepts an argument of ``None``. + .. versionchanged:: 3.6 + :class:`BZ2File` constructor now accepts objects implementing + :class:`os.PathLike`. + Incremental (de)compression --------------------------- diff -r 89821243621b Lib/bz2.py --- a/Lib/bz2.py Thu Jul 14 07:45:24 2016 +0300 +++ b/Lib/bz2.py Thu Jul 14 17:03:46 2016 +0800 @@ -91,15 +91,16 @@ else: raise ValueError("Invalid mode: %r" % (mode,)) - if isinstance(filename, (str, bytes)): + try: self._fp = _builtin_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") + except TypeError: + if hasattr(filename, "read") or hasattr(filename, "write"): + self._fp = filename + self._mode = mode_code + else: + raise if self._mode == _MODE_READ: raw = _compression.DecompressReader(self._fp, diff -r 89821243621b Lib/test/test_bz2.py --- a/Lib/test/test_bz2.py Thu Jul 14 07:45:24 2016 +0300 +++ b/Lib/test/test_bz2.py Thu Jul 14 17:03:46 2016 +0800 @@ -560,6 +560,23 @@ with BZ2File(str_filename, "rb") as f: self.assertEqual(f.read(), self.DATA) + def testPathLikeFilename(self): + filename = self.filename + class PathLikeClass: + def __fspath__(self): + return filename + class BadPathLikeClass: + def __fspath__(self): + return BZ2File + with BZ2File(PathLikeClass(), "wb") as f: + f.write(self.DATA) + with BZ2File(PathLikeClass(), "rb") as f: + self.assertEqual(f.read(), self.DATA) + with BZ2File(self.filename, "rb") as f: + self.assertEqual(f.read(), self.DATA) + self.assertRaises(TypeError, BZ2File, []) + self.assertRaises(TypeError, BZ2File, BadPathLikeClass()) + def testDecompressLimited(self): """Decompressed data buffering should be limited""" bomb = bz2.compress(bytes(int(2e6)), compresslevel=9)