Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image._check_size should allow tuple-like values #6282

Closed
Julian-O opened this issue May 9, 2022 · 3 comments
Closed

Image._check_size should allow tuple-like values #6282

Julian-O opened this issue May 9, 2022 · 3 comments

Comments

@Julian-O
Copy link

Julian-O commented May 9, 2022

When you pass an image size to methods in the Image module, they are checked with the _check_size(size) function. This checks whether they explicitly instances of list or tuple.

That is an overly-restrictive check. In keeping with Python's duck-typing, the size just needs to be a sequence (e.g. as defined by the collections.abc.Sequence of two numbers that support int().

For example, this means a user cannot create a custom Size class that supports __len__(), __iter__() and __getitem__() and may be passed to the Image.new() method.

Compare ImageFilter's _check_size(), which doesn't specify the type, but checks that the type supports the needed operations.

(I considered that the size might be being passed to C code deeper in the internals, and needed to fit in a known data-structure. I also considered that there may be concerns about performance in the typical case (where tuples are passed). In that case, _check_size should do the current check, and if it fails, explicitly convert the value to a tuple of integers (if possible), and only fail if not.

  • OS: Windows
  • Python: 3.10
  • Pillow: 9.1.0
from PIL import Image


class SizeInInches:
    def __init__(self, width_inches, height_inches, pixels_per_inch):
        self.width_inches = width_inches
        self.height_inches = height_inches
        self.pixels_per_inch = pixels_per_inch

    def __iter__(self):
        yield from (
            self.width_inches * self.pixels_per_inch,
            self.height_inches * self.pixels_per_inch,
        )

    def __len__(self):
        return 2

    def __getitem__(self, ind):
        return (
            self.width_inches * self.pixels_per_inch,
            self.height_inches * self.pixels_per_inch,
        )[ind]


size = SizeInInches(10, 8, 300)

# The `size` instance looks and quacks like a tuple:
print(len(size))
print(size[1])
print(tuple(size))

# But it can't be passed as a tuple.
im = Image.new(mode="RGB", size=size)
# This raises ValueError: Size must be a tuple
@Julian-O Julian-O changed the title Image._checksize should allow Image._checksize should allow tuple-like values May 9, 2022
@Julian-O Julian-O changed the title Image._checksize should allow tuple-like values Image._check_size should allow tuple-like values May 9, 2022
@radarhere
Copy link
Member

Hi. Allow me to tell a story that might not seem related at first.

https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#PIL.ImageDraw.ImageDraw.line

xy – Sequence of either 2-tuples like [(x, y), (x, y), ...] or numeric values like [x, y, x, y, ...].

In #3738, we had a discussion about whether to also allow a lists of lists. The conclusion was to stick with the current behaviour - #3738 (comment). In short, the idea was that tuples were more appropriate.

We can have the discussion again if you like, but I imagine that the same argument from that issue would also apply here.

@Julian-O
Copy link
Author

Julian-O commented May 9, 2022

@radarhere:

Thanks very much for the pointer. I agree it is a very similar discussion. I happen to disagree with the thinking, and hence the conclusion, but if the argument is to be continued (and not just repeated), I think it should probably continue in that comment thread to keep it together.

Now, I am mulling over whether I think it is worth marshalling my (ever-so-polite, logical and persuasive!) arguments, given I will be starting with the wind against me.

Again, I appreciate the background info.

@radarhere
Copy link
Member

Closing, as a preference has been stated for this conversation to continue in another issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants