From a2f40900911f94fe5bb70e538186d2105f6b7445 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:19:11 +0000 Subject: [PATCH 1/7] Initial plan From 8aa750f7ac5672103a15d76eb4b16d65a8c0e535 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:35:14 +0000 Subject: [PATCH 2/7] Upgrade Pillow from 9.5.0 to 10.3.0 Co-authored-by: pamelafox <297042+pamelafox@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ca1aab5..e49cd9f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ azure-functions>=1.12.0 py-staticmaps==0.4.0 fastapi==0.111.0 -Pillow==9.5.0 +Pillow==10.3.0 nest-asyncio==1.6.0 opentelemetry-instrumentation-fastapi==0.46b0 azure-monitor-opentelemetry-exporter==1.0.0b26 From 87c37c26b753bd3020acd05cb01ebb11d5d83cba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 29 Jul 2025 05:49:37 +0000 Subject: [PATCH 3/7] Fix Pillow 10.x compatibility by adding textsize compatibility patch - Add pillow_textsize_compat.py to restore textsize() method using textbbox() - Import compatibility patch in fastapi_app.py for early application - Ensures py-staticmaps 0.4.0 works with Pillow 10.3.0 security updates - Resolves AttributeError: 'ImageDraw' object has no attribute 'textsize' Co-authored-by: pamelafox <297042+pamelafox@users.noreply.github.com> --- .gitignore | 3 +++ function/fastapi_app.py | 3 +++ pillow_textsize_compat.py | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 pillow_textsize_compat.py diff --git a/.gitignore b/.gitignore index 2c28da9..d7b7c2d 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,6 @@ __queuestorage__ __azurite_db*__.json .python_packages .azure + +# Test scripts +test_pillow_compatibility.py diff --git a/function/fastapi_app.py b/function/fastapi_app.py index 3b8d2cf..2b37524 100644 --- a/function/fastapi_app.py +++ b/function/fastapi_app.py @@ -7,6 +7,9 @@ from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor +# Import Pillow 10.x compatibility patch for py-staticmaps +import pillow_textsize_compat # noqa: F401 + from . import fastapi_routes diff --git a/pillow_textsize_compat.py b/pillow_textsize_compat.py new file mode 100644 index 0000000..dcec97b --- /dev/null +++ b/pillow_textsize_compat.py @@ -0,0 +1,37 @@ +""" +Pillow 10.x compatibility patch for py-staticmaps. + +This module provides a monkey patch to restore the textsize() method +that was removed in Pillow 10.0.0 and replaced with textbbox(). + +This ensures compatibility with py-staticmaps 0.4.0 while maintaining +the security benefits of Pillow 10.3.0. +""" + +from PIL import ImageDraw + + +def textsize(self, text, font=None, *args, **kwargs): + """ + Compatibility method for Pillow 10.x that provides the old textsize() behavior. + + This method uses the new textbbox() method and converts the result to the + (width, height) tuple that textsize() used to return. + + Args: + text: The text to measure + font: The font to use (optional) + *args, **kwargs: Additional arguments passed to textbbox + + Returns: + tuple: (width, height) of the text + """ + # Use textbbox with anchor point (0, 0) and convert to size + bbox = self.textbbox((0, 0), text, font=font, *args, **kwargs) + # bbox is (left, top, right, bottom), so width = right - left, height = bottom - top + return (bbox[2] - bbox[0], bbox[3] - bbox[1]) + + +# Only patch if textsize doesn't exist (i.e., Pillow 10.x) +if not hasattr(ImageDraw.ImageDraw, "textsize"): + ImageDraw.ImageDraw.textsize = textsize From 51a8e849c9bf79c00bf44dcc6a309d6e46663250 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 29 Jul 2025 06:05:33 +0000 Subject: [PATCH 4/7] Replace global monkey patch with targeted textsize compatibility function Co-authored-by: pamelafox <297042+pamelafox@users.noreply.github.com> --- function/fastapi_app.py | 3 --- function/fastapi_routes.py | 42 ++++++++++++++++++++++++++++++++++++++ pillow_textsize_compat.py | 37 --------------------------------- 3 files changed, 42 insertions(+), 40 deletions(-) delete mode 100644 pillow_textsize_compat.py diff --git a/function/fastapi_app.py b/function/fastapi_app.py index 2b37524..3b8d2cf 100644 --- a/function/fastapi_app.py +++ b/function/fastapi_app.py @@ -7,9 +7,6 @@ from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor -# Import Pillow 10.x compatibility patch for py-staticmaps -import pillow_textsize_compat # noqa: F401 - from . import fastapi_routes diff --git a/function/fastapi_routes.py b/function/fastapi_routes.py index 6ece850..2d1cda1 100644 --- a/function/fastapi_routes.py +++ b/function/fastapi_routes.py @@ -4,6 +4,45 @@ import fastapi import fastapi.responses import staticmaps +from PIL import ImageDraw + + +def _pillow_textsize_compat(self, text, font=None, *args, **kwargs): + """ + Compatibility function for Pillow 10.x that provides the old textsize() behavior. + + This function uses the new textbbox() method and converts the result to the + (width, height) tuple that textsize() used to return. + + The textsize() method was removed in Pillow 10.0.0 and replaced with textbbox(). + See: https://github.com/python-pillow/Pillow/pull/6474 + Migration guide: https://pillow.readthedocs.io/en/stable/releasenotes/10.0.0.html#font-size-and-offset-methods + + Args: + text: The text to measure + font: The font to use (optional) + *args, **kwargs: Additional arguments passed to textbbox + + Returns: + tuple: (width, height) of the text + """ + # Use textbbox with anchor point (0, 0) and convert to size + bbox = self.textbbox((0, 0), text, font=font, *args, **kwargs) + # bbox is (left, top, right, bottom), so width = right - left, height = bottom - top + return (bbox[2] - bbox[0], bbox[3] - bbox[1]) + + +def _ensure_pillow_textsize_compatibility(): + """ + Ensure textsize() method is available for py-staticmaps compatibility. + + This function patches ImageDraw.ImageDraw to add the textsize() method + if it doesn't exist (i.e., in Pillow 10.x). This is needed because + py-staticmaps 0.4.0 uses the deprecated textsize() method. + """ + if not hasattr(ImageDraw.ImageDraw, "textsize"): + ImageDraw.ImageDraw.textsize = _pillow_textsize_compat + router = fastapi.APIRouter() @@ -28,6 +67,9 @@ def generate_map( height: int = 400, tile_provider: TileProvider = TileProvider.osm, ) -> ImageResponse: + # Ensure Pillow 10.x compatibility for py-staticmaps + _ensure_pillow_textsize_compatibility() + # Create the static map context context = staticmaps.Context() context.set_tile_provider(staticmaps.default_tile_providers[tile_provider.value]) diff --git a/pillow_textsize_compat.py b/pillow_textsize_compat.py deleted file mode 100644 index dcec97b..0000000 --- a/pillow_textsize_compat.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -Pillow 10.x compatibility patch for py-staticmaps. - -This module provides a monkey patch to restore the textsize() method -that was removed in Pillow 10.0.0 and replaced with textbbox(). - -This ensures compatibility with py-staticmaps 0.4.0 while maintaining -the security benefits of Pillow 10.3.0. -""" - -from PIL import ImageDraw - - -def textsize(self, text, font=None, *args, **kwargs): - """ - Compatibility method for Pillow 10.x that provides the old textsize() behavior. - - This method uses the new textbbox() method and converts the result to the - (width, height) tuple that textsize() used to return. - - Args: - text: The text to measure - font: The font to use (optional) - *args, **kwargs: Additional arguments passed to textbbox - - Returns: - tuple: (width, height) of the text - """ - # Use textbbox with anchor point (0, 0) and convert to size - bbox = self.textbbox((0, 0), text, font=font, *args, **kwargs) - # bbox is (left, top, right, bottom), so width = right - left, height = bottom - top - return (bbox[2] - bbox[0], bbox[3] - bbox[1]) - - -# Only patch if textsize doesn't exist (i.e., Pillow 10.x) -if not hasattr(ImageDraw.ImageDraw, "textsize"): - ImageDraw.ImageDraw.textsize = textsize From bad426c33b384b74672a3af50f885e58363df242 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 29 Jul 2025 06:30:24 +0000 Subject: [PATCH 5/7] Remove test_pillow_compatibility.py from .gitignore and fix black formatting Co-authored-by: pamelafox <297042+pamelafox@users.noreply.github.com> --- .gitignore | 1 - function/fastapi_routes.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d7b7c2d..e264c6f 100644 --- a/.gitignore +++ b/.gitignore @@ -135,4 +135,3 @@ __azurite_db*__.json .azure # Test scripts -test_pillow_compatibility.py diff --git a/function/fastapi_routes.py b/function/fastapi_routes.py index 2d1cda1..042a54f 100644 --- a/function/fastapi_routes.py +++ b/function/fastapi_routes.py @@ -35,7 +35,7 @@ def _pillow_textsize_compat(self, text, font=None, *args, **kwargs): def _ensure_pillow_textsize_compatibility(): """ Ensure textsize() method is available for py-staticmaps compatibility. - + This function patches ImageDraw.ImageDraw to add the textsize() method if it doesn't exist (i.e., in Pillow 10.x). This is needed because py-staticmaps 0.4.0 uses the deprecated textsize() method. @@ -69,7 +69,7 @@ def generate_map( ) -> ImageResponse: # Ensure Pillow 10.x compatibility for py-staticmaps _ensure_pillow_textsize_compatibility() - + # Create the static map context context = staticmaps.Context() context.set_tile_provider(staticmaps.default_tile_providers[tile_provider.value]) From 1695cb42b5d001dea0dce272271fcaefcd49fffb Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 28 Jul 2025 23:37:55 -0700 Subject: [PATCH 6/7] Update .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index e264c6f..539d229 100644 --- a/.gitignore +++ b/.gitignore @@ -133,5 +133,4 @@ __queuestorage__ __azurite_db*__.json .python_packages .azure - # Test scripts From 47cbcb6a2f4142e21e6e01eaf5dcb3e71cb45558 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Mon, 28 Jul 2025 23:38:05 -0700 Subject: [PATCH 7/7] Update .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 539d229..2c28da9 100644 --- a/.gitignore +++ b/.gitignore @@ -133,4 +133,3 @@ __queuestorage__ __azurite_db*__.json .python_packages .azure -# Test scripts