diff --git a/Tests/test_image.py b/Tests/test_image.py index 6d188e740..9e664a7c8 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -87,6 +87,26 @@ class TestImage: # with pytest.raises(MemoryError): # Image.new("L", (1000000, 1000000)) + def test_open_formats(self): + PNGFILE = "Tests/images/hopper.png" + JPGFILE = "Tests/images/hopper.jpg" + + with pytest.raises(TypeError): + Image.open(PNGFILE, formats=123) + + for formats in [["JPEG"], ("JPEG",)]: + with pytest.raises(UnidentifiedImageError): + Image.open(PNGFILE, formats=formats) + + with Image.open(JPGFILE, formats=formats) as im: + assert im.mode == "RGB" + assert im.size == (128, 128) + + for file in [PNGFILE, JPGFILE]: + with Image.open(file, formats=None) as im: + assert im.mode == "RGB" + assert im.size == (128, 128) + def test_width_height(self): im = Image.new("RGB", (1, 2)) assert im.width == 1 diff --git a/docs/releasenotes/8.0.0.rst b/docs/releasenotes/8.0.0.rst index bbb954e25..991699727 100644 --- a/docs/releasenotes/8.0.0.rst +++ b/docs/releasenotes/8.0.0.rst @@ -50,6 +50,17 @@ Add MIME type to PsdImagePlugin API Additions ============= +Image.open: add formats parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Added a new ``formats`` parameter to :py:func:`.Image.open`: + +* A list or tuple of formats to attempt to load the file in. + This can be used to restrict the set of formats checked. + Pass ``None`` to try all supported formats. You can print the set of + available formats by running ``python -m PIL`` or using + the :py:func:`PIL.features.pilinfo` function. + ImageOps.autocontrast: add mask parameter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 30ee8606a..a56494104 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2837,7 +2837,7 @@ def _decompression_bomb_check(size): ) -def open(fp, mode="r"): +def open(fp, mode="r", formats=None): """ Opens and identifies the given image file. @@ -2852,12 +2852,18 @@ def open(fp, mode="r"): ``file.seek``, and ``file.tell`` methods, and be opened in binary mode. :param mode: The mode. If given, this argument must be "r". + :param formats: A list or tuple of formats to attempt to load the file in. + This can be used to restrict the set of formats checked. + Pass ``None`` to try all supported formats. You can print the set of + available formats by running ``python -m PIL`` or using + the :py:func:`PIL.features.pilinfo` function. :returns: An :py:class:`~PIL.Image.Image` object. :exception FileNotFoundError: If the file cannot be found. :exception PIL.UnidentifiedImageError: If the image cannot be opened and identified. :exception ValueError: If the ``mode`` is not "r", or if a ``StringIO`` instance is used for ``fp``. + :exception TypeError: If ``formats`` is not ``None``, a list or a tuple. """ if mode != "r": @@ -2868,6 +2874,11 @@ def open(fp, mode="r"): "Binary data must be used instead." ) + if formats is None: + formats = ID + elif not isinstance(formats, (list, tuple)): + raise TypeError("formats must be a list or tuple") + exclusive_fp = False filename = "" if isinstance(fp, Path): @@ -2891,8 +2902,8 @@ def open(fp, mode="r"): accept_warnings = [] - def _open_core(fp, filename, prefix): - for i in ID: + def _open_core(fp, filename, prefix, formats): + for i in formats: try: factory, accept = OPEN[i] result = not accept or accept(prefix) @@ -2914,11 +2925,11 @@ def open(fp, mode="r"): raise return None - im = _open_core(fp, filename, prefix) + im = _open_core(fp, filename, prefix, formats) if im is None: if init(): - im = _open_core(fp, filename, prefix) + im = _open_core(fp, filename, prefix, formats) if im: im._exclusive_fp = exclusive_fp