From 970a2c663e83b412d0cc15e423789a3948cbfc39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micka=C3=ABl=20Desfr=C3=AAnes?=
 <mickael.desfrenes@unicaen.fr>
Date: Mon, 31 Mar 2025 16:43:47 +0200
Subject: [PATCH] switch to pyvips

---
 poetry.lock              | 31 ++++++++++++++++++++++++++++++-
 pount/apps/iiif/tasks.py | 29 ++++++++++++-----------------
 pyproject.toml           |  1 +
 requirements/common.txt  |  6 ++++--
 requirements/dev.txt     |  6 ++++--
 requirements/preprod.txt |  9 +++++++--
 requirements/prod.txt    |  9 +++++++--
 requirements/test.txt    |  9 +++++++--
 8 files changed, 72 insertions(+), 28 deletions(-)

diff --git a/poetry.lock b/poetry.lock
index 9cf9875b..9938a561 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1707,6 +1707,17 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
 typing = ["typing-extensions"]
 xmp = ["defusedxml"]
 
+[[package]]
+name = "pkgconfig"
+version = "1.5.5"
+description = "Interface Python with pkg-config"
+optional = false
+python-versions = ">=3.3,<4.0"
+files = [
+    {file = "pkgconfig-1.5.5-py3-none-any.whl", hash = "sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209"},
+    {file = "pkgconfig-1.5.5.tar.gz", hash = "sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899"},
+]
+
 [[package]]
 name = "platformdirs"
 version = "4.3.6"
@@ -2035,6 +2046,24 @@ files = [
     {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
 ]
 
+[[package]]
+name = "pyvips"
+version = "2.2.3"
+description = "binding for the libvips image processing library, API mode"
+optional = false
+python-versions = "*"
+files = [
+    {file = "pyvips-2.2.3.tar.gz", hash = "sha256:43bceced0db492654c93008246a58a508e0373ae1621116b87b322f2ac72212f"},
+]
+
+[package.dependencies]
+cffi = ">=1.0.0"
+pkgconfig = "*"
+
+[package.extras]
+doc = ["sphinx", "sphinx_rtd_theme"]
+test = ["cffi (>=1.0.0)", "pyperf", "pytest"]
+
 [[package]]
 name = "pyyaml"
 version = "6.0.2"
@@ -2642,4 +2671,4 @@ files = [
 [metadata]
 lock-version = "2.0"
 python-versions = "~3.9"
-content-hash = "11f98c82e7582d4ec57e51ddb0c7abede4b73e0414974959150f9ff457649c58"
+content-hash = "4facd551fa7c0094578a4d553e496908450ec640336ae9ffe8bc0850dd401783"
diff --git a/pount/apps/iiif/tasks.py b/pount/apps/iiif/tasks.py
index b0259ec1..9bc95f5c 100644
--- a/pount/apps/iiif/tasks.py
+++ b/pount/apps/iiif/tasks.py
@@ -20,11 +20,11 @@ def clean_iiif_media(media_id: Union[str, UUID]) -> None:
 @shared_task
 def ensure_media_has_iiif(media_id: Union[str, UUID]) -> None:
     import os
-    import subprocess
     import tempfile
     from pathlib import Path
     from typing import Union
 
+    import pyvips
     import requests
 
     from pount.apps.api.libs.file import item_mediafile_fullname
@@ -32,23 +32,18 @@ def ensure_media_has_iiif(media_id: Union[str, UUID]) -> None:
     from pount.apps.api.storage_backends import ItemMediaFileStorage
     from pount.apps.iiif.storage_backends import IIIFFileStorage
 
-    def pic_to_tiled_tiff(source: Union[str, Path], destination: Union[str, Path], dpi: int = 300):
-        tif_source = f"{source}.delme.tif"
-        _, extension = os.path.splitext(source)
-        if extension.lower() in [".pdf", ".ai"]:
-            pdftoppm_dest = f"{source}.delme"  # pdftoppm adds .tif extension already
-            subprocess.run(["pdftoppm", "-singlefile", "-tiff", source, "-r", str(dpi), pdftoppm_dest])
-        else:
-            subprocess.run(["convert", "-quiet", "-auto-orient", source, tif_source])
-        subprocess.run(
-            [
-                "vips",
-                "im_vips2tiff",
-                tif_source,
-                f"{destination}:deflate,tile:256x256,pyramid",
-            ]
+    def pic_to_tiled_tiff(source: Union[str, Path], destination: Union[str, Path]):
+        img = pyvips.Image.new_from_file(source, access="sequential")
+        img = img.autorot()
+        img.tiffsave(
+            destination,
+            tile=True,
+            tile_width=256,
+            tile_height=256,
+            pyramid=True,
+            Q=90,
+            compression="none",
         )
-        os.remove(tif_source)
 
     media_file: MediaFile = MediaFile.objects.filter(id=media_id).first()
 
diff --git a/pyproject.toml b/pyproject.toml
index 45d36f4b..5b735f6d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -59,6 +59,7 @@ nh3 = "^0.2.14"
 pillow = "^10.1.0"
 psycopg2-binary = "^2.9.9"
 pymongo = {extras = ["srv"], version = "^4.5.0"}
+pyvips = "^2.2.3"
 redis = "^5.0.1"
 requests = "^2.32.0"
 responses = "^0.25.0"
diff --git a/requirements/common.txt b/requirements/common.txt
index e47e6eb2..07bfd7be 100644
--- a/requirements/common.txt
+++ b/requirements/common.txt
@@ -7,7 +7,7 @@ boto3==1.35.73 ; python_version >= "3.9" and python_version < "3.10"
 botocore==1.35.73 ; python_version >= "3.9" and python_version < "3.10"
 celery==5.4.0 ; python_version >= "3.9" and python_version < "3.10"
 certifi==2024.8.30 ; python_version >= "3.9" and python_version < "3.10"
-cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy"
+cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10"
 charset-normalizer==3.4.0 ; python_version >= "3.9" and python_version < "3.10"
 click-didyoumean==0.3.1 ; python_version >= "3.9" and python_version < "3.10"
 click-plugins==1.1.1 ; python_version >= "3.9" and python_version < "3.10"
@@ -50,14 +50,16 @@ markdown-it-py==3.0.0 ; python_version >= "3.9" and python_version < "3.10"
 mdurl==0.1.2 ; python_version >= "3.9" and python_version < "3.10"
 nh3==0.2.19 ; python_version >= "3.9" and python_version < "3.10"
 pillow==10.4.0 ; python_version >= "3.9" and python_version < "3.10"
+pkgconfig==1.5.5 ; python_version >= "3.9" and python_version < "3.10"
 prompt-toolkit==3.0.48 ; python_version >= "3.9" and python_version < "3.10"
 psycopg2-binary==2.9.10 ; python_version >= "3.9" and python_version < "3.10"
-pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy"
+pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10"
 pygments==2.18.0 ; python_version >= "3.9" and python_version < "3.10"
 pyjwt==2.10.1 ; python_version >= "3.9" and python_version < "3.10"
 pymongo[srv]==4.10.1 ; python_version >= "3.9" and python_version < "3.10"
 python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "3.10"
 pytz==2024.2 ; python_version >= "3.9" and python_version < "3.10"
+pyvips==2.2.3 ; python_version >= "3.9" and python_version < "3.10"
 pyyaml==6.0.2 ; python_version >= "3.9" and python_version < "3.10"
 redis==5.2.0 ; python_version >= "3.9" and python_version < "3.10"
 referencing==0.35.1 ; python_version >= "3.9" and python_version < "3.10"
diff --git a/requirements/dev.txt b/requirements/dev.txt
index e451a52e..8ae222bc 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -10,7 +10,7 @@ botocore==1.35.73 ; python_version >= "3.9" and python_version < "3.10"
 cachetools==5.5.0 ; python_version >= "3.9" and python_version < "3.10"
 celery==5.4.0 ; python_version >= "3.9" and python_version < "3.10"
 certifi==2024.8.30 ; python_version >= "3.9" and python_version < "3.10"
-cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy"
+cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10"
 cfgv==3.4.0 ; python_version >= "3.9" and python_version < "3.10"
 chardet==5.2.0 ; python_version >= "3.9" and python_version < "3.10"
 charset-normalizer==3.4.0 ; python_version >= "3.9" and python_version < "3.10"
@@ -73,13 +73,14 @@ nodeenv==1.9.1 ; python_version >= "3.9" and python_version < "3.10"
 packaging==24.2 ; python_version >= "3.9" and python_version < "3.10"
 pathspec==0.12.1 ; python_version >= "3.9" and python_version < "3.10"
 pillow==10.4.0 ; python_version >= "3.9" and python_version < "3.10"
+pkgconfig==1.5.5 ; python_version >= "3.9" and python_version < "3.10"
 platformdirs==4.3.6 ; python_version >= "3.9" and python_version < "3.10"
 pluggy==1.5.0 ; python_version >= "3.9" and python_version < "3.10"
 pre-commit==3.8.0 ; python_version >= "3.9" and python_version < "3.10"
 prompt-toolkit==3.0.48 ; python_version >= "3.9" and python_version < "3.10"
 psycopg2-binary==2.9.10 ; python_version >= "3.9" and python_version < "3.10"
 pycodestyle==2.12.1 ; python_version >= "3.9" and python_version < "3.10"
-pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy"
+pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10"
 pyflakes==3.2.0 ; python_version >= "3.9" and python_version < "3.10"
 pygments==2.18.0 ; python_version >= "3.9" and python_version < "3.10"
 pyjwt==2.10.1 ; python_version >= "3.9" and python_version < "3.10"
@@ -87,6 +88,7 @@ pymongo[srv]==4.10.1 ; python_version >= "3.9" and python_version < "3.10"
 pyproject-api==1.8.0 ; python_version >= "3.9" and python_version < "3.10"
 python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "3.10"
 pytz==2024.2 ; python_version >= "3.9" and python_version < "3.10"
+pyvips==2.2.3 ; python_version >= "3.9" and python_version < "3.10"
 pyyaml==6.0.2 ; python_version >= "3.9" and python_version < "3.10"
 redis==5.2.0 ; python_version >= "3.9" and python_version < "3.10"
 referencing==0.35.1 ; python_version >= "3.9" and python_version < "3.10"
diff --git a/requirements/preprod.txt b/requirements/preprod.txt
index 819a08ea..478c5803 100644
--- a/requirements/preprod.txt
+++ b/requirements/preprod.txt
@@ -25,7 +25,7 @@ celery==5.4.0 ; python_version >= "3.9" and python_version < "3.10" \
 certifi==2024.8.30 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
     --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9
-cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy" \
+cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \
     --hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \
     --hash=sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1 \
@@ -543,6 +543,9 @@ pillow==10.4.0 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27 \
     --hash=sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e \
     --hash=sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1
+pkgconfig==1.5.5 ; python_version >= "3.9" and python_version < "3.10" \
+    --hash=sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209 \
+    --hash=sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899
 prompt-toolkit==3.0.48 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90 \
     --hash=sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e
@@ -614,7 +617,7 @@ psycopg2-binary==2.9.10 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1 \
     --hash=sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567 \
     --hash=sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863
-pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy" \
+pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
     --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
 pygments==2.18.0 ; python_version >= "3.9" and python_version < "3.10" \
@@ -689,6 +692,8 @@ python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "3.1
 pytz==2024.2 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a \
     --hash=sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725
+pyvips==2.2.3 ; python_version >= "3.9" and python_version < "3.10" \
+    --hash=sha256:43bceced0db492654c93008246a58a508e0373ae1621116b87b322f2ac72212f
 pyyaml==6.0.2 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
     --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
diff --git a/requirements/prod.txt b/requirements/prod.txt
index 819a08ea..478c5803 100644
--- a/requirements/prod.txt
+++ b/requirements/prod.txt
@@ -25,7 +25,7 @@ celery==5.4.0 ; python_version >= "3.9" and python_version < "3.10" \
 certifi==2024.8.30 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
     --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9
-cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy" \
+cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \
     --hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \
     --hash=sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1 \
@@ -543,6 +543,9 @@ pillow==10.4.0 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27 \
     --hash=sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e \
     --hash=sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1
+pkgconfig==1.5.5 ; python_version >= "3.9" and python_version < "3.10" \
+    --hash=sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209 \
+    --hash=sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899
 prompt-toolkit==3.0.48 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90 \
     --hash=sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e
@@ -614,7 +617,7 @@ psycopg2-binary==2.9.10 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1 \
     --hash=sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567 \
     --hash=sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863
-pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy" \
+pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
     --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
 pygments==2.18.0 ; python_version >= "3.9" and python_version < "3.10" \
@@ -689,6 +692,8 @@ python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "3.1
 pytz==2024.2 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a \
     --hash=sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725
+pyvips==2.2.3 ; python_version >= "3.9" and python_version < "3.10" \
+    --hash=sha256:43bceced0db492654c93008246a58a508e0373ae1621116b87b322f2ac72212f
 pyyaml==6.0.2 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
     --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
diff --git a/requirements/test.txt b/requirements/test.txt
index 819a08ea..478c5803 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -25,7 +25,7 @@ celery==5.4.0 ; python_version >= "3.9" and python_version < "3.10" \
 certifi==2024.8.30 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
     --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9
-cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy" \
+cffi==1.17.1 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \
     --hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \
     --hash=sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1 \
@@ -543,6 +543,9 @@ pillow==10.4.0 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27 \
     --hash=sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e \
     --hash=sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1
+pkgconfig==1.5.5 ; python_version >= "3.9" and python_version < "3.10" \
+    --hash=sha256:d20023bbeb42ee6d428a0fac6e0904631f545985a10cdd71a20aa58bc47a4209 \
+    --hash=sha256:deb4163ef11f75b520d822d9505c1f462761b4309b1bb713d08689759ea8b899
 prompt-toolkit==3.0.48 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90 \
     --hash=sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e
@@ -614,7 +617,7 @@ psycopg2-binary==2.9.10 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1 \
     --hash=sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567 \
     --hash=sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863
-pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10" and platform_python_implementation != "PyPy" \
+pycparser==2.22 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
     --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
 pygments==2.18.0 ; python_version >= "3.9" and python_version < "3.10" \
@@ -689,6 +692,8 @@ python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "3.1
 pytz==2024.2 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a \
     --hash=sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725
+pyvips==2.2.3 ; python_version >= "3.9" and python_version < "3.10" \
+    --hash=sha256:43bceced0db492654c93008246a58a508e0373ae1621116b87b322f2ac72212f
 pyyaml==6.0.2 ; python_version >= "3.9" and python_version < "3.10" \
     --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
     --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
-- 
GitLab