Skip to content

Change in behavior with JPEG Images in 11.0.0 #8472

@davidmezzetti

Description

@davidmezzetti

The following code that worked in Pillow 10.4 no longer works with Pillow 11.0.

import pickle

from io import BytesIO

from PIL import Image

im = Image.open("books.jpg")

p = pickle.dumps(im)
im = pickle.loads(p)

# Fails in 11.0, works in 10.4
output = BytesIO()
im.save(output, format=im.format, quality="keep")

Error stack.

Traceback (most recent call last):
  File "test.py", line 15, in <module>
    im.save(output, format=im.format, quality="keep")
  File "python3.9/site-packages/PIL/Image.py", line 2605, in save
    save_handler(self, fp, filename)
  File "python3.9/site-packages/PIL/JpegImagePlugin.py", line 700, in _save
    subsampling = get_sampling(im)
  File "python3.9/site-packages/PIL/JpegImagePlugin.py", line 643, in get_sampling
    if not isinstance(im, JpegImageFile) or im.layers in (1, 4):
  File "python3.9/site-packages/PIL/JpegImagePlugin.py", line 396, in __getattr__
    raise AttributeError(name)
AttributeError: layers

This has been traced to the following change in src/PIL/JpegImagePlugin.py

Pillow 10.4.0

Code

def get_sampling(im):
...
    if not hasattr(im, "layers") or im.layers in (1, 4):
        return -1
...

Pillow 11.0.0

Code

def get_sampling(im: Image.Image) -> int:
...
    if not isinstance(im, JpegImageFile) or im.layers in (1, 4):
        return -1
...

Suggested fix

def get_sampling(im: Image.Image) -> int:
...
    if not isinstance(im, JpegImageFile) or not hasattr(im, "layers") or im.layers in (1, 4):
        return -1
...

Note that the following hack does workaround the issue in 11.0.

im.layers = 1
im.save(output, format=im.format, quality="keep")

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions