[packages/python3-blockdiag] - apply fixes for Pillow 10+ and pytest instead of nose; release 2

qboosh qboosh at pld-linux.org
Sun Jul 13 09:21:30 CEST 2025


commit 3dd34b69204cdde79ba5b74366ab4bf1217bd154
Author: Jakub Bogusz <qboosh at pld-linux.org>
Date:   Sun Jul 13 09:23:02 2025 +0200

    - apply fixes for Pillow 10+ and pytest instead of nose; release 2

 blockdiag-pillow10-setup.patch |  12 +++
 blockdiag-pillow10.patch       | 117 +++++++++++++++++++++++++
 blockdiag-pytest.patch         | 195 +++++++++++++++++++++++++++++++++++++++++
 python3-blockdiag.spec         |  50 +++++++----
 4 files changed, 355 insertions(+), 19 deletions(-)
---
diff --git a/python3-blockdiag.spec b/python3-blockdiag.spec
index 90d4fa6..90a2fec 100644
--- a/python3-blockdiag.spec
+++ b/python3-blockdiag.spec
@@ -1,33 +1,39 @@
 #
 # Conditional build:
-%bcond_with	tests	# unit tests
+%bcond_without	tests	# unit tests
 
 %define		module	blockdiag
 Summary:	Blockdiag generate block-diagram image file from spec-text file
 Summary(pl.UTF-8):	Generowanie obrazków diagramów blokowych z opisu tekstowego
 Name:		python3-%{module}
 Version:	3.0.0
-Release:	1
+Release:	2
 License:	Apache v2.0
 Group:		Libraries/Python
 #Source0Download: https://pypi.org/simple/blockdiag/
 Source0:	https://files.pythonhosted.org/packages/source/b/blockdiag/%{module}-%{version}.tar.gz
 # Source0-md5:	e1bfc69b83254ad3565c572ff4b3ad97
-URL:		http://blockdiag.com/en/blockdiag/index.html
-BuildRequires:	python3-modules >= 1:3.5
+# https://github.com/blockdiag/blockdiag/pull/175.patch
+Patch0:		blockdiag-pytest.patch
+# https://github.com/blockdiag/blockdiag/pull/179.patch
+Patch1:		blockdiag-pillow10.patch
+Patch2:		blockdiag-pillow10-setup.patch
+URL:		http://blockdiag.com/
+BuildRequires:	python3-build
+BuildRequires:	python3-installer
+BuildRequires:	python3-modules >= 1:3.7
 BuildRequires:	python3-setuptools
 %if %{with tests}
 BuildRequires:	python3-docutils
-BuildRequires:	python3-funcparserlib
-#BuildRequires:	python3-nose
-#BuildRequires:	python3-nose_exclude
-BuildRequires:	python3-pillow >= 3.0
+BuildRequires:	python3-funcparserlib >= 1.0.0
+BuildRequires:	python3-pillow >= 3.1.0
+BuildRequires:	python3-pytest
 BuildRequires:	python3-reportlab
 BuildRequires:	python3-webcolors
 %endif
 BuildRequires:	rpmbuild(macros) >= 1.714
 BuildRequires:	sed >= 4.0
-Requires:	python3-modules >= 1:3.5
+Requires:	python3-modules >= 1:3.7
 BuildArch:	noarch
 BuildRoot:	%{tmpdir}/%{name}-%{version}-root-%(id -u -n)
 
@@ -48,25 +54,31 @@ Funkcje:
 
 %prep
 %setup -q -n %{module}-%{version}
+%patch -P0 -p1
+%patch -P1 -p1
+%patch -P2 -p1
+
+# contains Pillow<10 dependency
+%{__rm} -r src/blockdiag.egg-info
 
 %build
-%py3_build
+# although project uses just setup.py, use PEP-517 build in order to get metadata for tests
+%py3_build_pyproject
 
 %if %{with tests}
-# disable tests requiring network: test_command.TestBlockdiagApp.test_app_cleans_up_images, test_generate_diagram.test_generate, test_generate_diagram.ghostscript_not_found_test
-# test_setup_inline_svg_is_true_with_multibytes fails on utf-8 vs latin-1 inconsistency
-#nosetests-%{py3_ver} src/blockdiag/tests -e 'test_app_cleans_up_images|test_generate|ghostscript_not_found_test|test_setup_inline_svg_is_true_with_multibytes'
-# use explicit plugins list for reliable builds (delete PYTEST_PLUGINS if empty)
-PYTHONPATH=$(pwd)/src \
+%{__python3} -m zipfile -e build-3/*.whl build-3-test
+# disable tests requiring network:
+#  test_command.py::TestBlockdiagApp::test_app_cleans_up_images
+#  test_generate_diagram.py::test_generate_with_separate[.../diagrams/node_icon.diag-svg-options260]
+PYTHONPATH=$(pwd)/build-3-test \
 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 \
-PYTEST_PLUGINS= \
-%{__python3} -m pytest src/blockdiag/tests
+%{__python3} -m pytest src/blockdiag/tests -k 'not test_app_cleans_up_images and not test_generate_with_separate'
 %endif
 
 %install
 rm -rf $RPM_BUILD_ROOT
 
-%py3_install
+%py3_install_pyproject
 
 %{__rm} -r $RPM_BUILD_ROOT%{py3_sitescriptdir}/blockdiag/tests
 
@@ -85,6 +97,6 @@ rm -rf $RPM_BUILD_ROOT
 %attr(755,root,root) %{_bindir}/blockdiag
 %attr(755,root,root) %{_bindir}/blockdiag-3
 %{py3_sitescriptdir}/blockdiag
-%{py3_sitescriptdir}/%{module}-%{version}-py*.egg-info
+%{py3_sitescriptdir}/blockdiag-%{version}.dist-info
 %{_mandir}/man1/blockdiag.1*
 %{_mandir}/man1/blockdiag-3.1*
diff --git a/blockdiag-pillow10-setup.patch b/blockdiag-pillow10-setup.patch
new file mode 100644
index 0000000..175519d
--- /dev/null
+++ b/blockdiag-pillow10-setup.patch
@@ -0,0 +1,12 @@
+--- blockdiag-3.0.0/setup.py.orig	2025-07-13 08:33:43.082657681 +0200
++++ blockdiag-3.0.0/setup.py	2025-07-13 08:35:30.948739986 +0200
+@@ -54,7 +54,7 @@ setup(
+     install_requires=[
+         'setuptools',
+         'funcparserlib>=1.0.0a0',
+-        'Pillow > 3.0, < 10.0',
++        'Pillow > 3.0',
+         'webcolors',
+     ],
+     extras_require={
+
diff --git a/blockdiag-pillow10.patch b/blockdiag-pillow10.patch
new file mode 100644
index 0000000..e45c96b
--- /dev/null
+++ b/blockdiag-pillow10.patch
@@ -0,0 +1,117 @@
+From 20d780cad84e7b010066cb55f848477957870165 Mon Sep 17 00:00:00 2001
+From: Theodore Ni <3806110+tjni at users.noreply.github.com>
+Date: Sat, 5 Aug 2023 10:43:46 -0700
+Subject: [PATCH] Add support for Pillow 10
+
+Fix a bunch of breaking changes in a backwards compatible way.
+---
+ src/blockdiag/imagedraw/png.py | 47 ++++++++++++++++++++++++++++------
+ 1 file changed, 39 insertions(+), 8 deletions(-)
+
+diff --git a/src/blockdiag/imagedraw/png.py b/src/blockdiag/imagedraw/png.py
+index 3cac05a..12f0514 100644
+--- a/src/blockdiag/imagedraw/png.py
++++ b/src/blockdiag/imagedraw/png.py
+@@ -30,6 +30,21 @@
+ from blockdiag.utils.myitertools import istep, stepslice
+ 
+ 
++# to support pillow < 9.1.0
++if not hasattr(Image, 'Resampling'):
++    from enum import IntEnum
++
++    class Resampling(IntEnum):
++        NEAREST = 0
++        BOX = 4
++        BILINEAR = 2
++        HAMMING = 5
++        BICUBIC = 3
++        LANCZOS = 1
++
++    Image.Resampling = Resampling
++
++
+ def point_pairs(xylist):
+     iterable = iter(xylist)
+     for pt in iterable:
+@@ -147,7 +162,7 @@ def set_canvas_size(self, size):
+         self.draw = ImageDraw.Draw(self._image)
+ 
+     def resizeCanvas(self, size):
+-        self._image = self._image.resize(size, Image.ANTIALIAS)
++        self._image = self._image.resize(size, Image.Resampling.LANCZOS)
+         self.draw = ImageDraw.Draw(self._image)
+ 
+     def arc(self, box, start, end, **kwargs):
+@@ -273,13 +288,21 @@ def textfolder(self):
+     def textlinesize(self, string, font):
+         ttfont = ttfont_for(font)
+         if ttfont is None:
+-            size = self.draw.textsize(string, font=None)
++            if hasattr(self.draw, 'textbbox'):
++                left, top, right, bottom = self.draw.textbbox((0, 0), string)
++                size = (right - left, bottom - top)
++            else:
++                size = self.draw.textsize(string, font=None)
+ 
+             font_ratio = font.size * 1.0 / FontMap.BASE_FONTSIZE
+             size = Size(int(size[0] * font_ratio),
+                         int(size[1] * font_ratio))
+         else:
+-            size = Size(*ttfont.getsize(string))
++            if hasattr(ttfont, 'getbbox'):
++                left, top, right, bottom = ttfont.getbbox(string)
++                size = Size(right - left, bottom - top)
++            else:
++                size = Size(*ttfont.getsize(string))
+ 
+         return size
+ 
+@@ -291,7 +314,11 @@ def text(self, xy, string, font, **kwargs):
+             if self.scale_ratio == 1 and font.size == FontMap.BASE_FONTSIZE:
+                 self.draw.text(xy, string, fill=fill)
+             else:
+-                size = self.draw.textsize(string)
++                if hasattr(self.draw, 'textbbox'):
++                    left, top, right, bottom = self.draw.textbbox((0, 0), string)
++                    size = (right - left, bottom - top)
++                else:
++                    size = self.draw.textsize(string)
+                 image = Image.new('RGBA', size)
+                 draw = ImageDraw.Draw(image)
+                 draw.text((0, 0), string, fill=fill)
+@@ -299,10 +326,14 @@ def text(self, xy, string, font, **kwargs):
+ 
+                 basesize = (size[0] * self.scale_ratio,
+                             size[1] * self.scale_ratio)
+-                text_image = image.resize(basesize, Image.ANTIALIAS)
++                text_image = image.resize(basesize, Image.Resampling.LANCZOS)
+                 self.paste(text_image, xy, text_image)
+         else:
+-            size = ttfont.getsize(string)
++            if hasattr(ttfont, 'getbbox'):
++                left, top, right, bottom = ttfont.getbbox(string)
++                size = (right - left, bottom - top)
++            else:
++                size = ttfont.getsize(string)
+ 
+             # Generate mask to support BDF(bitmap font)
+             mask = Image.new('1', size)
+@@ -370,7 +401,7 @@ def image(self, box, url):
+             # resize image.
+             w = min([box.width, image.size[0] * self.scale_ratio])
+             h = min([box.height, image.size[1] * self.scale_ratio])
+-            image.thumbnail((w, h), Image.ANTIALIAS)
++            image.thumbnail((w, h), Image.Resampling.LANCZOS)
+ 
+             # centering image.
+             w, h = image.size
+@@ -404,7 +435,7 @@ def save(self, filename, size, _format):
+             y = int(self._image.size[1] / self.scale_ratio)
+             size = (x, y)
+ 
+-        self._image.thumbnail(size, Image.ANTIALIAS)
++        self._image.thumbnail(size, Image.Resampling.LANCZOS)
+ 
+         if self.filename:
+             self._image.save(self.filename, _format)
diff --git a/blockdiag-pytest.patch b/blockdiag-pytest.patch
new file mode 100644
index 0000000..6061779
--- /dev/null
+++ b/blockdiag-pytest.patch
@@ -0,0 +1,195 @@
+From c1dd1a4427f6158dd653cdfaa9368a2746b02747 Mon Sep 17 00:00:00 2001
+From: Guillaume Grossetie <ggrossetie at yuzutech.fr>
+Date: Sun, 9 Jul 2023 23:31:11 +0200
+Subject: [PATCH 1/2] Blockdiag is incompatible with Pillow 3.10
+
+---
+ setup.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/setup.py b/setup.py
+index ece3ae4..27ef5f2 100644
+--- a/setup.py
++++ b/setup.py
+@@ -54,7 +54,7 @@ def get_version():
+     install_requires=[
+         'setuptools',
+         'funcparserlib>=1.0.0a0',
+-        'Pillow > 3.0',
++        'Pillow > 3.0, < 10.0',
+         'webcolors',
+     ],
+     extras_require={
+
+From 4f4f726252084f17ecc6c524592222af09d37da4 Mon Sep 17 00:00:00 2001
+From: Guillaume Grossetie <ggrossetie at yuzutech.fr>
+Date: Mon, 10 Jul 2023 00:31:37 +0200
+Subject: [PATCH 2/2] Switch to pytest (nose is unmaintained and does not work
+ on Python3.10)
+
+---
+ setup.py                                     |  3 +-
+ src/blockdiag/tests/test_generate_diagram.py | 95 ++++++++++----------
+ tox.ini                                      |  2 +-
+ 3 files changed, 49 insertions(+), 51 deletions(-)
+
+diff --git a/setup.py b/setup.py
+index 27ef5f2..33f6e26 100644
+--- a/setup.py
++++ b/setup.py
+@@ -65,7 +65,7 @@ def get_version():
+             'docutils'
+         ],
+         'testing': [
+-            'nose',
++            'pytest',
+             'flake8',
+             'flake8-coding',
+             'flake8-copyright',
+@@ -74,7 +74,6 @@ def get_version():
+             'docutils',
+         ],
+     },
+-    test_suite='nose.collector',
+     entry_points="""
+        [console_scripts]
+        blockdiag = blockdiag.command:main
+diff --git a/src/blockdiag/tests/test_generate_diagram.py b/src/blockdiag/tests/test_generate_diagram.py
+index a212d89..cc22896 100644
+--- a/src/blockdiag/tests/test_generate_diagram.py
++++ b/src/blockdiag/tests/test_generate_diagram.py
+@@ -19,7 +19,7 @@
+ import unittest
+ from xml.etree import ElementTree
+ 
+-from nose.tools import nottest
++import pytest
+ 
+ import blockdiag
+ import blockdiag.command
+@@ -46,66 +46,65 @@ def get_diagram_files(testdir):
+             yield os.path.join(diagramsdir, file)
+ 
+ 
+-def test_generate():
+-    mainfunc = blockdiag.command.main
+-    basepath = os.path.dirname(__file__)
+-    files = get_diagram_files(basepath)
+-    options = []
++base_path = os.path.dirname(__file__)
++files = get_diagram_files(base_path)
++generate_testdata = []
++generate_with_separate_testdata = []
++for file_source in files:
++    generate_testdata.append((file_source, 'svg', []))
++    generate_testdata.append((file_source, 'png', []))
++    generate_testdata.append((file_source, 'png', ['--antialias']))
++    generate_testdata.append((file_source, 'pdf', []))
++    if re.search('separate', file_source):
++        generate_with_separate_testdata.append((file_source, 'svg', ['--separate']))
++        generate_with_separate_testdata.append((file_source, 'png', ['--separate']))
++        generate_with_separate_testdata.append((file_source, 'png', ['--separate', '--antialias']))
++        generate_with_separate_testdata.append((file_source, 'pdf', ['--separate']))
+ 
+-    for testcase in testcase_generator(basepath, mainfunc, files, options):
+-        yield testcase
+ 
++ at pytest.mark.parametrize("source,file_type,options", generate_with_separate_testdata)
++def test_generate_with_separate_option(source, file_type, options):
++    generate(source, file_type, options)
+ 
+-def test_generate_with_separate():
+-    mainfunc = blockdiag.command.main
+-    basepath = os.path.dirname(__file__)
+-    files = get_diagram_files(basepath)
+-    filtered = (f for f in files if re.search('separate', f))
+-    options = ['--separate']
+ 
+-    for testcase in testcase_generator(basepath, mainfunc, filtered, options):
+-        yield testcase
++ at pytest.mark.parametrize("source,file_type,options", generate_testdata)
++def test_generate_with_separate(source, file_type, options):
++    generate(source, file_type, options)
+ 
+ 
+- at nottest
+-def testcase_generator(basepath, mainfunc, files, options):
+-    fontpath = get_fontpath(basepath)
+-    options = options + ['-f', fontpath]
+-
+-    for source in files:
+-        yield generate, mainfunc, 'svg', source, options
+-
++ at capture_stderr
++def generate(source, file_type, options):
++    if file_type == 'png':
+         if not supported_pil():
+-            yield unittest.skip("Pillow is not available")(generate)
+-            yield unittest.skip("Pillow is not available")(generate)
+-        elif os.environ.get('ALL_TESTS') is None:
+-            message = "Skipped by default. To enable it, specify $ALL_TESTS=1"
+-            yield unittest.skip(message)(generate)
+-            yield unittest.skip(message)(generate)
+-        else:
+-            yield generate, mainfunc, 'png', source, options
+-            yield generate, mainfunc, 'png', source, options + ['--antialias']
+-
++            unittest.skip('Pillow is not available')
++            return
++        if os.environ.get('ALL_TESTS') is None:
++            unittest.skip('Skipped by default. To enable it, specify $ALL_TESTS=1')
++            return
++    elif file_type == 'pdf':
+         if not supported_pdf():
+-            yield unittest.skip("reportlab is not available")(generate)
+-        elif os.environ.get('ALL_TESTS') is None:
+-            message = "Skipped by default. To enable it, specify $ALL_TESTS=1"
+-            yield unittest.skip(message)(generate)
+-        else:
+-            yield generate, mainfunc, 'pdf', source, options
++            unittest.skip('reportlab is not available')
++            return
++        if os.environ.get('ALL_TESTS') is None:
++            unittest.skip('Skipped by default. To enable it, specify $ALL_TESTS=1')
++            return
+ 
+-
+- at capture_stderr
+-def generate(mainfunc, filetype, source, options):
++    tmpdir = None
+     try:
+         tmpdir = TemporaryDirectory()
+-        fd, tmpfile = tmpdir.mkstemp()
++        fd, tmp_file = tmpdir.mkstemp()
+         os.close(fd)
+-
+-        mainfunc(['--debug', '-T', filetype, '-o', tmpfile, source] +
+-                 list(options))
++        blockdiag.command.main(
++            [
++                '--debug',
++                '-T',
++                file_type,
++                '-o', tmp_file, source
++            ] + list(options)
++        )
+     finally:
+-        tmpdir.clean()
++        if tmpdir is not None:
++            tmpdir.clean()
+ 
+ 
+ def not_exist_font_config_option_test():
+diff --git a/tox.ini b/tox.ini
+index 0aafed8..aec660a 100644
+--- a/tox.ini
++++ b/tox.ini
+@@ -18,7 +18,7 @@ deps =
+ passenv =
+     ALL_TESTS
+ commands =
+-    nosetests
++    pytest
+ 
+ [testenv:flake8]
+ description =
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/python3-blockdiag.git/commitdiff/3dd34b69204cdde79ba5b74366ab4bf1217bd154



More information about the pld-cvs-commit mailing list