diff --git a/PIL/Image.py b/PIL/Image.py index 861599bf7a4..0c254c61d88 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1640,7 +1640,7 @@ def save(self, fp, format=None, **params): implement the ``seek``, ``tell``, and ``write`` methods, and be opened in binary mode. - :param fp: File name or file object. + :param fp: A filename (string), pathlib.Path object or file object. :param format: Optional format override. If omitted, the format to use is determined from the filename extension. If a file object was used instead of a filename, this @@ -1653,13 +1653,15 @@ def save(self, fp, format=None, **params): may have been created, and may contain partial data. """ + filename = "" if isPath(fp): filename = fp - else: - if hasattr(fp, "name") and isPath(fp.name): - filename = fp.name - else: - filename = "" + elif sys.version_info >= (3, 4): + from pathlib import Path + if isinstance(fp, Path): + filename = str(fp.resolve()) + elif hasattr(fp, "name") and isPath(fp.name): + filename = fp.name # may mutate self! self.load() @@ -1687,8 +1689,8 @@ def save(self, fp, format=None, **params): else: save_handler = SAVE[format.upper()] - if isPath(fp): - fp = builtins.open(fp, "wb") + if filename: + fp = builtins.open(filename, "wb") close = 1 else: close = 0 @@ -2265,9 +2267,9 @@ def open(fp, mode="r"): :py:meth:`~PIL.Image.Image.load` method). See :py:func:`~PIL.Image.new`. - :param fp: A filename (string) or a file object. The file object - must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and - :py:meth:`~file.tell` methods, and be opened in binary mode. + :param fp: A filename (string), pathlib.Path object or a file object. + The file object must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, + and :py:meth:`~file.tell` methods, and be opened in binary mode. :param mode: The mode. If given, this argument must be "r". :returns: An :py:class:`~PIL.Image.Image` object. :exception IOError: If the file cannot be found, or the image cannot be @@ -2277,11 +2279,15 @@ def open(fp, mode="r"): if mode != "r": raise ValueError("bad mode %r" % mode) + filename = "" if isPath(fp): filename = fp - fp = builtins.open(fp, "rb") - else: - filename = "" + elif sys.version_info >= (3, 4): + from pathlib import Path + if isinstance(fp, Path): + filename = str(fp.resolve()) + if filename: + fp = builtins.open(filename, "rb") try: fp.seek(0) diff --git a/Tests/test_image.py b/Tests/test_image.py index bd5fd352227..fa7f8ec064b 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -1,6 +1,7 @@ from helper import unittest, PillowTestCase, hopper from PIL import Image +import sys class TestImage(PillowTestCase): @@ -48,6 +49,14 @@ def test_invalid_image(self): im = io.BytesIO(b'') self.assertRaises(IOError, lambda: Image.open(im)) + @unittest.skipIf(sys.version_info < (3, 4), + "pathlib only available in Python 3.4 or later") + def test_pathlib(self): + from pathlib import Path + im = Image.open(Path("Tests/images/hopper.jpg")) + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + def test_internals(self): im = Image.new("L", (100, 100))