We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CI run against latest deps is failing
See https://github.com/matrix-org/synapse/actions/runs/2292542675


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `synapse/rest/media/v1/thumbnailer.py`
Content:
```
1 # Copyright 2014-2016 OpenMarket Ltd
2 # Copyright 2020-2021 The Matrix.org Foundation C.I.C.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import logging
16 from io import BytesIO
17 from typing import Tuple
18 
19 from PIL import Image
20 
21 logger = logging.getLogger(__name__)
22 
23 EXIF_ORIENTATION_TAG = 0x0112
24 EXIF_TRANSPOSE_MAPPINGS = {
25     2: Image.FLIP_LEFT_RIGHT,
26     3: Image.ROTATE_180,
27     4: Image.FLIP_TOP_BOTTOM,
28     5: Image.TRANSPOSE,
29     6: Image.ROTATE_270,
30     7: Image.TRANSVERSE,
31     8: Image.ROTATE_90,
32 }
33 
34 
35 class ThumbnailError(Exception):
36     """An error occurred generating a thumbnail."""
37 
38 
39 class Thumbnailer:
40 
41     FORMATS = {"image/jpeg": "JPEG", "image/png": "PNG"}
42 
43     @staticmethod
44     def set_limits(max_image_pixels: int) -> None:
45         Image.MAX_IMAGE_PIXELS = max_image_pixels
46 
47     def __init__(self, input_path: str):
48         try:
49             self.image = Image.open(input_path)
50         except OSError as e:
51             # If an error occurs opening the image, a thumbnail won't be able to
52             # be generated.
53             raise ThumbnailError from e
54         except Image.DecompressionBombError as e:
55             # If an image decompression bomb error occurs opening the image,
56             # then the image exceeds the pixel limit and a thumbnail won't
57             # be able to be generated.
58             raise ThumbnailError from e
59 
60         self.width, self.height = self.image.size
61         self.transpose_method = None
62         try:
63             # We don't use ImageOps.exif_transpose since it crashes with big EXIF
64             #
65             # Ignore safety: Pillow seems to acknowledge that this method is
66             # "private, experimental, but generally widely used". Pillow 6
67             # includes a public getexif() method (no underscore) that we might
68             # consider using instead when we can bump that dependency.
69             #
70             # At the time of writing, Debian buster (currently oldstable)
71             # provides version 5.4.1. It's expected to EOL in mid-2022, see
72             # https://wiki.debian.org/DebianReleases#Production_Releases
73             image_exif = self.image._getexif()  # type: ignore
74             if image_exif is not None:
75                 image_orientation = image_exif.get(EXIF_ORIENTATION_TAG)
76                 assert isinstance(image_orientation, int)
77                 self.transpose_method = EXIF_TRANSPOSE_MAPPINGS.get(image_orientation)
78         except Exception as e:
79             # A lot of parsing errors can happen when parsing EXIF
80             logger.info("Error parsing image EXIF information: %s", e)
81 
82     def transpose(self) -> Tuple[int, int]:
83         """Transpose the image using its EXIF Orientation tag
84 
85         Returns:
86             A tuple containing the new image size in pixels as (width, height).
87         """
88         if self.transpose_method is not None:
89             # Safety: `transpose` takes an int rather than e.g. an IntEnum.
90             # self.transpose_method is set above to be a value in
91             # EXIF_TRANSPOSE_MAPPINGS, and that only contains correct values.
92             self.image = self.image.transpose(self.transpose_method)  # type: ignore[arg-type]
93             self.width, self.height = self.image.size
94             self.transpose_method = None
95             # We don't need EXIF any more
96             self.image.info["exif"] = None
97         return self.image.size
98 
99     def aspect(self, max_width: int, max_height: int) -> Tuple[int, int]:
100         """Calculate the largest size that preserves aspect ratio which
101         fits within the given rectangle::
102 
103             (w_in / h_in) = (w_out / h_out)
104             w_out = max(min(w_max, h_max * (w_in / h_in)), 1)
105             h_out = max(min(h_max, w_max * (h_in / w_in)), 1)
106 
107         Args:
108             max_width: The largest possible width.
109             max_height: The largest possible height.
110         """
111 
112         if max_width * self.height < max_height * self.width:
113             return max_width, max((max_width * self.height) // self.width, 1)
114         else:
115             return max((max_height * self.width) // self.height, 1), max_height
116 
117     def _resize(self, width: int, height: int) -> Image.Image:
118         # 1-bit or 8-bit color palette images need converting to RGB
119         # otherwise they will be scaled using nearest neighbour which
120         # looks awful.
121         #
122         # If the image has transparency, use RGBA instead.
123         if self.image.mode in ["1", "L", "P"]:
124             mode = "RGB"
125             if self.image.info.get("transparency", None) is not None:
126                 mode = "RGBA"
127             self.image = self.image.convert(mode)
128         return self.image.resize((width, height), Image.ANTIALIAS)
129 
130     def scale(self, width: int, height: int, output_type: str) -> BytesIO:
131         """Rescales the image to the given dimensions.
132 
133         Returns:
134             BytesIO: the bytes of the encoded image ready to be written to disk
135         """
136         scaled = self._resize(width, height)
137         return self._encode_image(scaled, output_type)
138 
139     def crop(self, width: int, height: int, output_type: str) -> BytesIO:
140         """Rescales and crops the image to the given dimensions preserving
141         aspect::
142             (w_in / h_in) = (w_scaled / h_scaled)
143             w_scaled = max(w_out, h_out * (w_in / h_in))
144             h_scaled = max(h_out, w_out * (h_in / w_in))
145 
146         Args:
147             max_width: The largest possible width.
148             max_height: The largest possible height.
149 
150         Returns:
151             BytesIO: the bytes of the encoded image ready to be written to disk
152         """
153         if width * self.height > height * self.width:
154             scaled_height = (width * self.height) // self.width
155             scaled_image = self._resize(width, scaled_height)
156             crop_top = (scaled_height - height) // 2
157             crop_bottom = height + crop_top
158             cropped = scaled_image.crop((0, crop_top, width, crop_bottom))
159         else:
160             scaled_width = (height * self.width) // self.height
161             scaled_image = self._resize(scaled_width, height)
162             crop_left = (scaled_width - width) // 2
163             crop_right = width + crop_left
164             cropped = scaled_image.crop((crop_left, 0, crop_right, height))
165         return self._encode_image(cropped, output_type)
166 
167     def _encode_image(self, output_image: Image.Image, output_type: str) -> BytesIO:
168         output_bytes_io = BytesIO()
169         fmt = self.FORMATS[output_type]
170         if fmt == "JPEG":
171             output_image = output_image.convert("RGB")
172         output_image.save(output_bytes_io, fmt, quality=80)
173         return output_bytes_io
174
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/synapse/rest/media/v1/thumbnailer.py b/synapse/rest/media/v1/thumbnailer.py
--- a/synapse/rest/media/v1/thumbnailer.py
+++ b/synapse/rest/media/v1/thumbnailer.py
@@ -121,10 +121,10 @@
         #
         # If the image has transparency, use RGBA instead.
         if self.image.mode in ["1", "L", "P"]:
-            mode = "RGB"
             if self.image.info.get("transparency", None) is not None:
-                mode = "RGBA"
-            self.image = self.image.convert(mode)
+                self.image = self.image.convert("RGBA")
+            else:
+                self.image = self.image.convert("RGB")
         return self.image.resize((width, height), Image.ANTIALIAS)
 
     def scale(self, width: int, height: int, output_type: str) -> BytesIO:

{"golden_diff": "diff --git a/synapse/rest/media/v1/thumbnailer.py b/synapse/rest/media/v1/thumbnailer.py\n--- a/synapse/rest/media/v1/thumbnailer.py\n+++ b/synapse/rest/media/v1/thumbnailer.py\n@@ -121,10 +121,10 @@\n         #\n         # If the image has transparency, use RGBA instead.\n         if self.image.mode in [\"1\", \"L\", \"P\"]:\n-            mode = \"RGB\"\n             if self.image.info.get(\"transparency\", None) is not None:\n-                mode = \"RGBA\"\n-            self.image = self.image.convert(mode)\n+                self.image = self.image.convert(\"RGBA\")\n+            else:\n+                self.image = self.image.convert(\"RGB\")\n         return self.image.resize((width, height), Image.ANTIALIAS)\n \n     def scale(self, width: int, height: int, output_type: str) -> BytesIO:\n", "issue": "CI run against latest deps is failing\nSee https://github.com/matrix-org/synapse/actions/runs/2292542675\n\n", "before_files": [{"content": "# Copyright 2014-2016 OpenMarket Ltd\n# Copyright 2020-2021 The Matrix.org Foundation C.I.C.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport logging\nfrom io import BytesIO\nfrom typing import Tuple\n\nfrom PIL import Image\n\nlogger = logging.getLogger(__name__)\n\nEXIF_ORIENTATION_TAG = 0x0112\nEXIF_TRANSPOSE_MAPPINGS = {\n    2: Image.FLIP_LEFT_RIGHT,\n    3: Image.ROTATE_180,\n    4: Image.FLIP_TOP_BOTTOM,\n    5: Image.TRANSPOSE,\n    6: Image.ROTATE_270,\n    7: Image.TRANSVERSE,\n    8: Image.ROTATE_90,\n}\n\n\nclass ThumbnailError(Exception):\n    \"\"\"An error occurred generating a thumbnail.\"\"\"\n\n\nclass Thumbnailer:\n\n    FORMATS = {\"image/jpeg\": \"JPEG\", \"image/png\": \"PNG\"}\n\n    @staticmethod\n    def set_limits(max_image_pixels: int) -> None:\n        Image.MAX_IMAGE_PIXELS = max_image_pixels\n\n    def __init__(self, input_path: str):\n        try:\n            self.image = Image.open(input_path)\n        except OSError as e:\n            # If an error occurs opening the image, a thumbnail won't be able to\n            # be generated.\n            raise ThumbnailError from e\n        except Image.DecompressionBombError as e:\n            # If an image decompression bomb error occurs opening the image,\n            # then the image exceeds the pixel limit and a thumbnail won't\n            # be able to be generated.\n            raise ThumbnailError from e\n\n        self.width, self.height = self.image.size\n        self.transpose_method = None\n        try:\n            # We don't use ImageOps.exif_transpose since it crashes with big EXIF\n            #\n            # Ignore safety: Pillow seems to acknowledge that this method is\n            # \"private, experimental, but generally widely used\". Pillow 6\n            # includes a public getexif() method (no underscore) that we might\n            # consider using instead when we can bump that dependency.\n            #\n            # At the time of writing, Debian buster (currently oldstable)\n            # provides version 5.4.1. It's expected to EOL in mid-2022, see\n            # https://wiki.debian.org/DebianReleases#Production_Releases\n            image_exif = self.image._getexif()  # type: ignore\n            if image_exif is not None:\n                image_orientation = image_exif.get(EXIF_ORIENTATION_TAG)\n                assert isinstance(image_orientation, int)\n                self.transpose_method = EXIF_TRANSPOSE_MAPPINGS.get(image_orientation)\n        except Exception as e:\n            # A lot of parsing errors can happen when parsing EXIF\n            logger.info(\"Error parsing image EXIF information: %s\", e)\n\n    def transpose(self) -> Tuple[int, int]:\n        \"\"\"Transpose the image using its EXIF Orientation tag\n\n        Returns:\n            A tuple containing the new image size in pixels as (width, height).\n        \"\"\"\n        if self.transpose_method is not None:\n            # Safety: `transpose` takes an int rather than e.g. an IntEnum.\n            # self.transpose_method is set above to be a value in\n            # EXIF_TRANSPOSE_MAPPINGS, and that only contains correct values.\n            self.image = self.image.transpose(self.transpose_method)  # type: ignore[arg-type]\n            self.width, self.height = self.image.size\n            self.transpose_method = None\n            # We don't need EXIF any more\n            self.image.info[\"exif\"] = None\n        return self.image.size\n\n    def aspect(self, max_width: int, max_height: int) -> Tuple[int, int]:\n        \"\"\"Calculate the largest size that preserves aspect ratio which\n        fits within the given rectangle::\n\n            (w_in / h_in) = (w_out / h_out)\n            w_out = max(min(w_max, h_max * (w_in / h_in)), 1)\n            h_out = max(min(h_max, w_max * (h_in / w_in)), 1)\n\n        Args:\n            max_width: The largest possible width.\n            max_height: The largest possible height.\n        \"\"\"\n\n        if max_width * self.height < max_height * self.width:\n            return max_width, max((max_width * self.height) // self.width, 1)\n        else:\n            return max((max_height * self.width) // self.height, 1), max_height\n\n    def _resize(self, width: int, height: int) -> Image.Image:\n        # 1-bit or 8-bit color palette images need converting to RGB\n        # otherwise they will be scaled using nearest neighbour which\n        # looks awful.\n        #\n        # If the image has transparency, use RGBA instead.\n        if self.image.mode in [\"1\", \"L\", \"P\"]:\n            mode = \"RGB\"\n            if self.image.info.get(\"transparency\", None) is not None:\n                mode = \"RGBA\"\n            self.image = self.image.convert(mode)\n        return self.image.resize((width, height), Image.ANTIALIAS)\n\n    def scale(self, width: int, height: int, output_type: str) -> BytesIO:\n        \"\"\"Rescales the image to the given dimensions.\n\n        Returns:\n            BytesIO: the bytes of the encoded image ready to be written to disk\n        \"\"\"\n        scaled = self._resize(width, height)\n        return self._encode_image(scaled, output_type)\n\n    def crop(self, width: int, height: int, output_type: str) -> BytesIO:\n        \"\"\"Rescales and crops the image to the given dimensions preserving\n        aspect::\n            (w_in / h_in) = (w_scaled / h_scaled)\n            w_scaled = max(w_out, h_out * (w_in / h_in))\n            h_scaled = max(h_out, w_out * (h_in / w_in))\n\n        Args:\n            max_width: The largest possible width.\n            max_height: The largest possible height.\n\n        Returns:\n            BytesIO: the bytes of the encoded image ready to be written to disk\n        \"\"\"\n        if width * self.height > height * self.width:\n            scaled_height = (width * self.height) // self.width\n            scaled_image = self._resize(width, scaled_height)\n            crop_top = (scaled_height - height) // 2\n            crop_bottom = height + crop_top\n            cropped = scaled_image.crop((0, crop_top, width, crop_bottom))\n        else:\n            scaled_width = (height * self.width) // self.height\n            scaled_image = self._resize(scaled_width, height)\n            crop_left = (scaled_width - width) // 2\n            crop_right = width + crop_left\n            cropped = scaled_image.crop((crop_left, 0, crop_right, height))\n        return self._encode_image(cropped, output_type)\n\n    def _encode_image(self, output_image: Image.Image, output_type: str) -> BytesIO:\n        output_bytes_io = BytesIO()\n        fmt = self.FORMATS[output_type]\n        if fmt == \"JPEG\":\n            output_image = output_image.convert(\"RGB\")\n        output_image.save(output_bytes_io, fmt, quality=80)\n        return output_bytes_io\n", "path": "synapse/rest/media/v1/thumbnailer.py"}], "after_files": [{"content": "# Copyright 2014-2016 OpenMarket Ltd\n# Copyright 2020-2021 The Matrix.org Foundation C.I.C.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport logging\nfrom io import BytesIO\nfrom typing import Tuple\n\nfrom PIL import Image\n\nlogger = logging.getLogger(__name__)\n\nEXIF_ORIENTATION_TAG = 0x0112\nEXIF_TRANSPOSE_MAPPINGS = {\n    2: Image.FLIP_LEFT_RIGHT,\n    3: Image.ROTATE_180,\n    4: Image.FLIP_TOP_BOTTOM,\n    5: Image.TRANSPOSE,\n    6: Image.ROTATE_270,\n    7: Image.TRANSVERSE,\n    8: Image.ROTATE_90,\n}\n\n\nclass ThumbnailError(Exception):\n    \"\"\"An error occurred generating a thumbnail.\"\"\"\n\n\nclass Thumbnailer:\n\n    FORMATS = {\"image/jpeg\": \"JPEG\", \"image/png\": \"PNG\"}\n\n    @staticmethod\n    def set_limits(max_image_pixels: int) -> None:\n        Image.MAX_IMAGE_PIXELS = max_image_pixels\n\n    def __init__(self, input_path: str):\n        try:\n            self.image = Image.open(input_path)\n        except OSError as e:\n            # If an error occurs opening the image, a thumbnail won't be able to\n            # be generated.\n            raise ThumbnailError from e\n        except Image.DecompressionBombError as e:\n            # If an image decompression bomb error occurs opening the image,\n            # then the image exceeds the pixel limit and a thumbnail won't\n            # be able to be generated.\n            raise ThumbnailError from e\n\n        self.width, self.height = self.image.size\n        self.transpose_method = None\n        try:\n            # We don't use ImageOps.exif_transpose since it crashes with big EXIF\n            #\n            # Ignore safety: Pillow seems to acknowledge that this method is\n            # \"private, experimental, but generally widely used\". Pillow 6\n            # includes a public getexif() method (no underscore) that we might\n            # consider using instead when we can bump that dependency.\n            #\n            # At the time of writing, Debian buster (currently oldstable)\n            # provides version 5.4.1. It's expected to EOL in mid-2022, see\n            # https://wiki.debian.org/DebianReleases#Production_Releases\n            image_exif = self.image._getexif()  # type: ignore\n            if image_exif is not None:\n                image_orientation = image_exif.get(EXIF_ORIENTATION_TAG)\n                assert isinstance(image_orientation, int)\n                self.transpose_method = EXIF_TRANSPOSE_MAPPINGS.get(image_orientation)\n        except Exception as e:\n            # A lot of parsing errors can happen when parsing EXIF\n            logger.info(\"Error parsing image EXIF information: %s\", e)\n\n    def transpose(self) -> Tuple[int, int]:\n        \"\"\"Transpose the image using its EXIF Orientation tag\n\n        Returns:\n            A tuple containing the new image size in pixels as (width, height).\n        \"\"\"\n        if self.transpose_method is not None:\n            # Safety: `transpose` takes an int rather than e.g. an IntEnum.\n            # self.transpose_method is set above to be a value in\n            # EXIF_TRANSPOSE_MAPPINGS, and that only contains correct values.\n            self.image = self.image.transpose(self.transpose_method)  # type: ignore[arg-type]\n            self.width, self.height = self.image.size\n            self.transpose_method = None\n            # We don't need EXIF any more\n            self.image.info[\"exif\"] = None\n        return self.image.size\n\n    def aspect(self, max_width: int, max_height: int) -> Tuple[int, int]:\n        \"\"\"Calculate the largest size that preserves aspect ratio which\n        fits within the given rectangle::\n\n            (w_in / h_in) = (w_out / h_out)\n            w_out = max(min(w_max, h_max * (w_in / h_in)), 1)\n            h_out = max(min(h_max, w_max * (h_in / w_in)), 1)\n\n        Args:\n            max_width: The largest possible width.\n            max_height: The largest possible height.\n        \"\"\"\n\n        if max_width * self.height < max_height * self.width:\n            return max_width, max((max_width * self.height) // self.width, 1)\n        else:\n            return max((max_height * self.width) // self.height, 1), max_height\n\n    def _resize(self, width: int, height: int) -> Image.Image:\n        # 1-bit or 8-bit color palette images need converting to RGB\n        # otherwise they will be scaled using nearest neighbour which\n        # looks awful.\n        #\n        # If the image has transparency, use RGBA instead.\n        if self.image.mode in [\"1\", \"L\", \"P\"]:\n            if self.image.info.get(\"transparency\", None) is not None:\n                self.image = self.image.convert(\"RGBA\")\n            else:\n                self.image = self.image.convert(\"RGB\")\n        return self.image.resize((width, height), Image.ANTIALIAS)\n\n    def scale(self, width: int, height: int, output_type: str) -> BytesIO:\n        \"\"\"Rescales the image to the given dimensions.\n\n        Returns:\n            BytesIO: the bytes of the encoded image ready to be written to disk\n        \"\"\"\n        scaled = self._resize(width, height)\n        return self._encode_image(scaled, output_type)\n\n    def crop(self, width: int, height: int, output_type: str) -> BytesIO:\n        \"\"\"Rescales and crops the image to the given dimensions preserving\n        aspect::\n            (w_in / h_in) = (w_scaled / h_scaled)\n            w_scaled = max(w_out, h_out * (w_in / h_in))\n            h_scaled = max(h_out, w_out * (h_in / w_in))\n\n        Args:\n            max_width: The largest possible width.\n            max_height: The largest possible height.\n\n        Returns:\n            BytesIO: the bytes of the encoded image ready to be written to disk\n        \"\"\"\n        if width * self.height > height * self.width:\n            scaled_height = (width * self.height) // self.width\n            scaled_image = self._resize(width, scaled_height)\n            crop_top = (scaled_height - height) // 2\n            crop_bottom = height + crop_top\n            cropped = scaled_image.crop((0, crop_top, width, crop_bottom))\n        else:\n            scaled_width = (height * self.width) // self.height\n            scaled_image = self._resize(scaled_width, height)\n            crop_left = (scaled_width - width) // 2\n            crop_right = width + crop_left\n            cropped = scaled_image.crop((crop_left, 0, crop_right, height))\n        return self._encode_image(cropped, output_type)\n\n    def _encode_image(self, output_image: Image.Image, output_type: str) -> BytesIO:\n        output_bytes_io = BytesIO()\n        fmt = self.FORMATS[output_type]\n        if fmt == \"JPEG\":\n            output_image = output_image.convert(\"RGB\")\n        output_image.save(output_bytes_io, fmt, quality=80)\n        return output_bytes_io\n", "path": "synapse/rest/media/v1/thumbnailer.py"}]}