internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: python-pillow/Pillow

Issue:
8.3.0 Regression: Unhandled OSError running under Apache2/mod_wsgi 
### What did you do?
After upgrading one of our Django projects to Pillow 8.3.0 we got  a 500 error when trying to create a thumbnail on any page. Downgrading to Pillow 8.2.0 resolved (worked around) the error.

### What did you expect to happen?
Image resizing to continue as normal.

### What actually happened?
A 500 error and a long backtrace in the logs:
```python

Internal Server Error: /case-studies/chronic-pelvic-pain/
Traceback (most recent call last):
    ...
    File "...lib/python3.8/site-packages/PIL/ImageFile.py", line 496, in _save
        if fp == sys.stdout or (hasattr(sys.stdout, "buffer") and fp == sys.stdout.buffer):
    OSError: sys.stdout access restricted by mod_wsgi

```

### What are your OS, Python and Pillow versions?

* OS: Ubuntu 20.04.2 LTS (Linux 5.8.0-1038-aws)
* Python: Python 3.8.10
* Django 3.2.5
* Pillow: 8.3.0 (in a venv)
* Apache (v2.4.41) / mod_wsgi (v4.6.8)

### My guess

The default [settings for mod_wsgi](https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIRestrictStdout.html) restrict access to ``sys.stdout``.

> In the interests of promoting portability of WSGI applications, mod_wsgi restricts 
> access to sys.stdout and will raise an exception if an attempt is made to use sys.stdout explicitly.

Recent commit [0f68e63](https://github.com/python-pillow/Pillow/commit/0f68e63793ec116728589264358050b2ce14f4df#diff-4668a28f508bbcb68e61f9b257415dc814c22e8bb24850629fe7788ae5b7475c) accesses ``sys.stdout`` explicitly in [line 496 of PIL/ImageFile.py](https://github.com/python-pillow/Pillow/blob/b057f5f40f1f17efeb5f441453b3274d325a1f0e/src/PIL/ImageFile.py#L496), as per the traceback above.

### Thanks

Thank you for looking into this. Please let me know how I can help with testing - or anything else that might be required.



Discussion:

Resolving patch:
diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py
--- a/src/PIL/ImageFile.py
+++ b/src/PIL/ImageFile.py
@@ -493,7 +493,11 @@ def _save(im, fp, tile, bufsize=0):
     # But, it would need at least the image size in most cases. RawEncode is
     # a tricky case.
     bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4)  # see RawEncode.c
-    if fp == sys.stdout or (hasattr(sys.stdout, "buffer") and fp == sys.stdout.buffer):
+    try:
+        stdout = fp == sys.stdout or fp == sys.stdout.buffer
+    except (OSError, AttributeError):
+        stdout = False
+    if stdout:
         fp.flush()
         return
     try:


Test patch:
Repository: python-pillow/Pillow

Issue:
YCbCr mode TIFF saving not functional
This is w/ Pillow 8.3.1.

1. Uncompressed saving (i.e. not using libtiff_encode) doesn't set tag 530 to (1,1), and mandatory tag 532:

```python
import PIL.Image
image = PIL.Image.new('YCbCr', (1760, 1046))
image.save('test_ycbcr_nocomp.tif')
```

  Resulting file is invalid - compare to tags in tiff_strip_ycbcr_jpeg_1x1_sampling.tif

2. When using compression and/or libtiff_encode, even if added somehow, tag 530 doesn't get stored correctly and encoding fails:

```python
import PIL.Image
image = PIL.Image.new('YCbCr', (1760, 1046))
image.save('test_ycbcr_libtiff.tif', compression='jpeg')
```

See also https://github.com/python-pillow/Pillow/pull/5588#issuecomment-875490034



Discussion:
I've created #5597 to fix 1.
Thanks, that's what I had in mind as well for 1.
As far as item 2 goes, would removing 530 and 532 from "core tags" in both PIL/TiffTags.py and encode.c perhaps let them be encoded properly from the ifd dictionary?
I've updated the PR so that it should now cover 2 as well.
In combination with #5588, that resolves this.

Resolving patch:
diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py
--- a/src/PIL/TiffImagePlugin.py
+++ b/src/PIL/TiffImagePlugin.py
@@ -93,6 +93,7 @@
 EXTRASAMPLES = 338
 SAMPLEFORMAT = 339
 JPEGTABLES = 347
+YCBCRSUBSAMPLING = 530
 REFERENCEBLACKWHITE = 532
 COPYRIGHT = 33432
 IPTC_NAA_CHUNK = 33723  # newsphoto properties
@@ -1593,6 +1594,13 @@ def _save(im, fp, filename):
     # no compression by default:
     ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1)
 
+    if im.mode == "YCbCr":
+        for tag, value in {
+            YCBCRSUBSAMPLING: (1, 1),
+            REFERENCEBLACKWHITE: (0, 255, 128, 255, 128, 255),
+        }.items():
+            ifd.setdefault(tag, value)
+
     if libtiff:
         if "quality" in im.encoderinfo:
             quality = im.encoderinfo["quality"]


Test patch:
diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py
--- a/Tests/test_file_libtiff.py
+++ b/Tests/test_file_libtiff.py
@@ -670,6 +670,15 @@ def save_bytesio(compression=None):
         TiffImagePlugin.WRITE_LIBTIFF = False
         TiffImagePlugin.READ_LIBTIFF = False
 
+    def test_save_ycbcr(self, tmp_path):
+        im = hopper("YCbCr")
+        outfile = str(tmp_path / "temp.tif")
+        im.save(outfile, compression="jpeg")
+
+        with Image.open(outfile) as reloaded:
+            assert reloaded.tag_v2[530] == (1, 1)
+            assert reloaded.tag_v2[532] == (0, 255, 128, 255, 128, 255)
+
     def test_crashing_metadata(self, tmp_path):
         # issue 1597
         with Image.open("Tests/images/rdf.tif") as im:
Repository: python-pillow/Pillow

Issue:
Saving an image with JPEG2000 format doesn't accept Path object
### What did you do?

Save a JPEG 2000 image using a `Path` argument:
```python
from pathlib import Path
from PIL import Image

img = Image.new('RGBA', (5, 5), '#00000000')
img.save(Path('image.jp2'), format='JPEG2000')
```

### What did you expect to happen?

It should save the file correctly, since [the documentation says that passing in a `Path` object is allowed](https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.save).

### What actually happened?

```
Traceback (most recent call last):
  File "/home/jtai/Desktop/pillow-fail/fail.py", line 5, in <module>
    img.save(Path('image.jp2'), format='JPEG2000')
  File "/home/jtai/Desktop/pillow-fail/venv/lib/python3.9/site-packages/PIL/Image.py", line 2235, in save
    save_handler(self, fp, filename)
  File "/home/jtai/Desktop/pillow-fail/venv/lib/python3.9/site-packages/PIL/Jpeg2KImagePlugin.py", line 247, in _save
    if filename.endswith(".j2k"):
AttributeError: 'PosixPath' object has no attribute 'endswith'
```

### What are your OS, Python and Pillow versions?

* OS: Pop!_OS 21.04
* Python: 3.9.5
* Pillow: 8.3.1


Discussion:

Resolving patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -2180,12 +2180,12 @@ def save(self, fp, format=None, **params):
 
         filename = ""
         open_fp = False
-        if isPath(fp):
-            filename = fp
-            open_fp = True
-        elif isinstance(fp, Path):
+        if isinstance(fp, Path):
             filename = str(fp)
             open_fp = True
+        elif isPath(fp):
+            filename = fp
+            open_fp = True
         elif fp == sys.stdout:
             try:
                 fp = sys.stdout.buffer


Test patch:
diff --git a/Tests/test_image.py b/Tests/test_image.py
--- a/Tests/test_image.py
+++ b/Tests/test_image.py
@@ -149,10 +149,11 @@ def test_pathlib(self, tmp_path):
             assert im.mode == "RGB"
             assert im.size == (128, 128)
 
-            temp_file = str(tmp_path / "temp.jpg")
-            if os.path.exists(temp_file):
-                os.remove(temp_file)
-            im.save(Path(temp_file))
+            for ext in (".jpg", ".jp2"):
+                temp_file = str(tmp_path / ("temp." + ext))
+                if os.path.exists(temp_file):
+                    os.remove(temp_file)
+                im.save(Path(temp_file))
 
     def test_fp_name(self, tmp_path):
         temp_file = str(tmp_path / "temp.jpg")
Repository: python-pillow/Pillow

Issue:
Problem rotating P images
```python
from PIL import Image
import random

img = Image.open('test.png')
rate = random.randint(30, 330)
img = img.rotate(rate)
img.show()
img.save('test_rotate.png')
```
![test](https://user-images.githubusercontent.com/37957822/127455530-f7437db9-b2df-4b89-be5b-5582b67b4eb8.png)
![test_rotate](https://user-images.githubusercontent.com/37957822/127455537-05a6a378-4ba9-4018-a22f-5f2dabbe6eea.png)


Discussion:
The image generated by rotating the picture is a binary image
If you're looking for an immediate solution, you can convert it to RGB after opening.

```python
from PIL import Image
import random

img = Image.open('test.png').convert('RGB')
rate = random.randint(30, 330)
img = img.rotate(rate)
img.show()
img.save('test_rotate.png')
```
Thank you for your answer. It's useful
This problem was introduced by #5552

Resolving patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -2479,6 +2479,8 @@ def getdata(self):
             raise ValueError("missing method data")
 
         im = new(self.mode, size, fillcolor)
+        if self.mode == "P" and self.palette:
+            im.palette = self.palette.copy()
         im.info = self.info.copy()
         if method == MESH:
             # list of quads


Test patch:
diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py
--- a/Tests/test_image_transform.py
+++ b/Tests/test_image_transform.py
@@ -32,6 +32,11 @@ def test_info(self):
             new_im = im.transform((100, 100), transform)
         assert new_im.info["comment"] == comment
 
+    def test_palette(self):
+        with Image.open("Tests/images/hopper.gif") as im:
+            transformed = im.transform(im.size, Image.AFFINE, [1, 0, 0, 0, 1, 0])
+            assert im.palette.palette == transformed.palette.palette
+
     def test_extent(self):
         im = hopper("RGB")
         (w, h) = im.size
Repository: python-pillow/Pillow

Issue:
Add width to polygon
Feature request: add `width` as an option to method `polygon()`.


As you can see, `rectangle()` has the `width` as a parameter, and many other similar methods. Why can't `polygon()`?


Discussion:
I've created PR #5694 to resolve this.

Resolving patch:
diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py
--- a/src/PIL/ImageDraw.py
+++ b/src/PIL/ImageDraw.py
@@ -233,13 +233,35 @@ def point(self, xy, fill=None):
         if ink is not None:
             self.draw.draw_points(xy, ink)
 
-    def polygon(self, xy, fill=None, outline=None):
+    def polygon(self, xy, fill=None, outline=None, width=1):
         """Draw a polygon."""
         ink, fill = self._getink(outline, fill)
         if fill is not None:
             self.draw.draw_polygon(xy, fill, 1)
-        if ink is not None and ink != fill:
-            self.draw.draw_polygon(xy, ink, 0)
+        if ink is not None and ink != fill and width != 0:
+            if width == 1:
+                self.draw.draw_polygon(xy, ink, 0, width)
+            else:
+                # To avoid expanding the polygon outwards,
+                # use the fill as a mask
+                mask = Image.new("1", self.im.size)
+                mask_ink = self._getink(1)[0]
+
+                fill_im = mask.copy()
+                draw = Draw(fill_im)
+                draw.draw.draw_polygon(xy, mask_ink, 1)
+
+                ink_im = mask.copy()
+                draw = Draw(ink_im)
+                width = width * 2 - 1
+                draw.draw.draw_polygon(xy, mask_ink, 0, width)
+
+                mask.paste(ink_im, mask=fill_im)
+
+                im = Image.new(self.mode, self.im.size)
+                draw = Draw(im)
+                draw.draw.draw_polygon(xy, ink, 0, width)
+                self.im.paste(im.im, (0, 0) + im.size, mask.im)
 
     def regular_polygon(
         self, bounding_circle, n_sides, rotation=0, fill=None, outline=None


Test patch:
diff --git a/Tests/images/imagedraw/triangle_right_width.png b/Tests/images/imagedraw/triangle_right_width.png
new file mode 100644
Binary files /dev/null and b/Tests/images/imagedraw/triangle_right_width.png differ
diff --git a/Tests/images/imagedraw/triangle_right_width_no_fill.png b/Tests/images/imagedraw/triangle_right_width_no_fill.png
new file mode 100644
Binary files /dev/null and b/Tests/images/imagedraw/triangle_right_width_no_fill.png differ
diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py
--- a/Tests/test_imagedraw.py
+++ b/Tests/test_imagedraw.py
@@ -945,6 +945,18 @@ def test_triangle_right():
     )
 
 
+@pytest.mark.parametrize(
+    "fill, suffix",
+    ((BLACK, "width"), (None, "width_no_fill")),
+)
+def test_triangle_right_width(fill, suffix):
+    img, draw = create_base_image_draw((100, 100))
+    draw.polygon([(15, 25), (85, 25), (50, 60)], fill, WHITE, width=5)
+    assert_image_equal_tofile(
+        img, os.path.join(IMAGES_PATH, "triangle_right_" + suffix + ".png")
+    )
+
+
 def test_line_horizontal():
     img, draw = create_base_image_draw((20, 20))
     draw.line((5, 5, 14, 5), BLACK, 2)
Repository: python-pillow/Pillow

Issue:
PNG palette quantized save functionality different between 8.1.0 and 8.3.1
### What did you do?
Create a palette, quantize an image using this palette and save it.

### What did you expect to happen?
The palette information is stored correctly. I.e. the list of RGB values is the same in the saved image.
E.g. [0, 0, 0, 209, 254, 76, 217, 3, 3, 10, 66, 192,...]

### What actually happened?
The list of RGB values is corrupted.
E.g. [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3,...]

### What are your OS, Python and Pillow versions?

* OS: Tested on Windows and Azure Databricks (Ubuntu)
* Python: 3.6 & 3.8
* Pillow: 8.3.1 has the issue

```python
from PIL import Image
from itertools import cycle
import numpy as np

palette = [0, 0, 0, 209, 254, 76, 217, 3, 3, 10, 66, 192]
# palette must contain 768 values (256 * 3)
# repeat the colours until we get there
color_iter = cycle(palette)
expanded_palette_format = []
while True:
    expanded_palette_format.append(next(color_iter))
    if len(expanded_palette_format) == 768:
        break

assert len(expanded_palette_format) == 768
palette_image = Image.new('P', (16, 16))
palette_image.putpalette(expanded_palette_format)

# a new image that we want to apply the palette to
im_to_palette = np.array(
    [
        [[0, 0, 0,],[209, 254, 76], [217, 3, 3], [10, 66, 192]],
        [[0, 0, 0,],[209, 254, 76], [217, 3, 3], [10, 66, 192]],
        [[0, 0, 0,],[209, 254, 76], [217, 3, 3], [10, 66, 192]],
        [[0, 0, 0,],[209, 254, 76], [217, 3, 3], [10, 66, 192]]
    ],
    np.uint8
    )
im = Image.fromarray(im_to_palette).quantize(palette=palette_image, dither=0)

im.save(r"test.png")
saved_im = Image.open(r"test.png")

assert im.getpalette() == saved_im.getpalette()
# AssertionError
```

Please note that if I specifically pass the 'GIF' argument on save, it has the expected behaviour.
```python
im.save(r"test.png", "GIF")
saved_im = Image.open(r"test.png")

assert im.getpalette() == saved_im.getpalette()
```


Discussion:

Resolving patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -1130,7 +1130,9 @@ def quantize(self, colors=256, method=None, kmeans=0, palette=None, dither=1):
                     "only RGB or L mode images can be quantized to a palette"
                 )
             im = self.im.convert("P", dither, palette.im)
-            return self._new(im)
+            new_im = self._new(im)
+            new_im.palette = palette.palette.copy()
+            return new_im
 
         im = self._new(self.im.quantize(colors, method, kmeans))
 


Test patch:
diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py
--- a/Tests/test_image_quantize.py
+++ b/Tests/test_image_quantize.py
@@ -63,6 +63,7 @@ def test_quantize_no_dither():
 
     converted = image.quantize(dither=0, palette=palette)
     assert_image(converted, "P", converted.size)
+    assert converted.palette.palette == palette.palette.palette
 
 
 def test_quantize_dither_diff():
Repository: python-pillow/Pillow

Issue:
 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe7 | pillow 8.4.0
* OS: ubuntu 18.04
* Python: 3.6
* Pillow: 8.4.0
I got the following error while installing 8.4.0:
```
    writing manifest file 'src/Pillow.egg-info/SOURCES.txt'
    running build_ext
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-68m6eqt_/pillow/setup.py", line 1024, in <module>
        zip_safe=not (debug_build() or PLATFORM_MINGW),
      File "/home/thachdo/.local/lib/python3.6/site-packages/setuptools/__init__.py", line 153, in setup
        return distutils.core.setup(**attrs)
      File "/usr/lib/python3.6/distutils/core.py", line 148, in setup
        dist.run_commands()
      File "/usr/lib/python3.6/distutils/dist.py", line 955, in run_commands
        self.run_command(cmd)
      File "/usr/lib/python3.6/distutils/dist.py", line 974, in run_command
        cmd_obj.run()
      File "/home/thachdo/.local/lib/python3.6/site-packages/setuptools/command/install.py", line 68, in run
        return orig.install.run(self)
      File "/usr/lib/python3.6/distutils/command/install.py", line 589, in run
        self.run_command('build')
      File "/usr/lib/python3.6/distutils/cmd.py", line 313, in run_command
        self.distribution.run_command(command)
      File "/usr/lib/python3.6/distutils/dist.py", line 974, in run_command
        cmd_obj.run()
      File "/usr/lib/python3.6/distutils/command/build.py", line 135, in run
        self.run_command(cmd_name)
      File "/usr/lib/python3.6/distutils/cmd.py", line 313, in run_command
        self.distribution.run_command(command)
      File "/usr/lib/python3.6/distutils/dist.py", line 974, in run_command
        cmd_obj.run()
      File "/home/thachdo/.local/lib/python3.6/site-packages/setuptools/command/build_ext.py", line 79, in run
        _build_ext.run(self)
      File "/usr/lib/python3.6/distutils/command/build_ext.py", line 339, in run
        self.build_extensions()
      File "/tmp/pip-build-68m6eqt_/pillow/setup.py", line 554, in build_extensions
        for dirname in _find_library_dirs_ldconfig():
      File "/tmp/pip-build-68m6eqt_/pillow/setup.py", line 189, in _find_library_dirs_ldconfig
        data = data.decode()
    in position 46: invalid continuation byte
```


Discussion:
0xe7 is ç. I'm guessing that you have that in a path on your filesystem? Maybe in the name of your home directory?

If I'm right, could you copy and paste one such path here?

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -185,7 +185,7 @@ def _find_library_dirs_ldconfig():
         return []
     [data, _] = p.communicate()
     if isinstance(data, bytes):
-        data = data.decode()
+        data = data.decode("latin1")
 
     dirs = []
     for dll in re.findall(expr, data):


Test patch:
Repository: python-pillow/Pillow

Issue:
Pillow is saving unnecessary values in paletted pngs
Pillow is currently saving unnecessarily long palette information even in case we are saving just 2color png. 

### What did you do?

Pillow is saving all 256 colors in palette even if only 2 colors are defined thus increasing the size of the pngs significantly. 
We use paletted pngs to save space and bandwidth but we have to manually remove palette from all pngs saved by Pillow. (In old version we would simply remove img.palette.palette altogether and png would be saved properly. In new versions it's no longer possible so we have to cut it to correct size)
Python code provided should show exact way how palettes are being increased in size by this issue.

### What did you expect to happen?

I'd expect that output png would save only palette required. (2colors instead of 256colors)

### What actually happened?

Pillow keeps saving unnecessary data that increases size of the paletted output pngs.

### What are your OS, Python and Pillow versions?

* OS: Ubuntu 20
* Python: 3.9.1
* Pillow: Pillow==8.4.0

```python
from PIL import Image, ImageDraw

img = Image.new("RGB", (64, 64), (255,255,255))
# get a drawing context
draw = ImageDraw.Draw(img)
draw.line((0, 0) + img.size, fill=(0,255,0))
img.save('line.png', "PNG")  # Size 354 bytes
palette_img = img.quantize(colors=2)
palette_img.save('line_palette.png', "PNG")  # Size 878 bytes
# Saving with adaptive and optimize doesn't solve bloated png output:
pil_adaptive = img.convert("P", palette=Image.ADAPTIVE, colors=2)
pil_adaptive.save('line_palette_adaptive.png', "PNG", optimize=True)  # Size 878 bytes
# Remove all unneeded balast from pallete output, keeping only 6 bytes needed
# for pallete. (2x 3 rgb values)
palette_img.palette.palette = palette_img.palette.palette[:6]
palette_img.save('line_palette_no_balast.png', "PNG")  # Size 113 bytes
```



Discussion:

Resolving patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -1111,7 +1111,8 @@ def quantize(self, colors=256, method=None, kmeans=0, palette=None, dither=1):
         from . import ImagePalette
 
         mode = im.im.getpalettemode()
-        im.palette = ImagePalette.ImagePalette(mode, im.im.getpalette(mode, mode))
+        palette = im.im.getpalette(mode, mode)[: colors * len(mode)]
+        im.palette = ImagePalette.ImagePalette(mode, palette)
 
         return im
 


Test patch:
diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py
--- a/Tests/test_image_quantize.py
+++ b/Tests/test_image_quantize.py
@@ -77,6 +77,13 @@ def test_quantize_dither_diff():
     assert dither.tobytes() != nodither.tobytes()
 
 
+def test_colors():
+    im = hopper()
+    colors = 2
+    converted = im.quantize(colors)
+    assert len(converted.palette.palette) == colors * len("RGB")
+
+
 def test_transparent_colors_equal():
     im = Image.new("RGBA", (1, 2), (0, 0, 0, 0))
     px = im.load()