diff --git a/libmodulemd.spec b/libmodulemd.spec index e33c76a..724d2f1 100644 --- a/libmodulemd.spec +++ b/libmodulemd.spec @@ -24,7 +24,7 @@ Name: %{upstream_name}%{?v2_suffix} Version: 2.15.2 -Release: 5%{?dist} +Release: 6%{?dist} Summary: Module metadata manipulation library # COPYING: MIT @@ -43,6 +43,8 @@ Source1: %{url}/releases/download/%{version}/modulemd-%{version}.tar.xz.a Source2: gpgkey-E3F42FCE156830A80358E6E94FD1AEC3365AF7BF.gpg # Adapt tests to glib2-2.87.0, in upstream after 2.15.2, bug #2423153 Patch0: modulemd-2.15.2-tests-Adapt-to-glib-2.87.0.patch +# Adapt tests to pygobject 3.55.0, in upstream after 2.15.2, bug #2440570 +Patch1: modulemd-2.15.2-tests-Adapt-to-pygobject-3.55.0.patch BuildRequires: gnupg2 BuildRequires: meson >= 0.47 @@ -191,6 +193,9 @@ mv %{buildroot}%{_mandir}/man1/modulemd-validator.1 \ %changelog +* Tue Feb 24 2026 Petr Pisar - 2.15.2-6 +- Adapt tests to pygobject 3.55.0 (bug #2440570) + * Wed Jan 14 2026 Petr Pisar - 2.15.2-5 - Adapt tests to glib2-2.87.0 (bug #2423153) diff --git a/modulemd-2.15.2-tests-Adapt-to-pygobject-3.55.0.patch b/modulemd-2.15.2-tests-Adapt-to-pygobject-3.55.0.patch new file mode 100644 index 0000000..6c4c517 --- /dev/null +++ b/modulemd-2.15.2-tests-Adapt-to-pygobject-3.55.0.patch @@ -0,0 +1,167 @@ +From e33ecf1cc15383b9563bc4cd9a6908277bf8039d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= +Date: Tue, 24 Feb 2026 13:35:17 +0100 +Subject: [PATCH] tests: Adapt to pygobject 3.55.0 + +After upgrading pygobject from 3.54.5 to 3.55.2, Python tests +expecting a process failure on setting an immutable +(G_PARAM_CONSTRUCT_ONLY) property started to fail like this: + + Traceback (most recent call last): + File "/home/test/libmodulemd-devel/redhat-linux-build/../modulemd/tests/ModulemdTests/defaults.py", line 114, in test_module_name + self.assertProcessFailure(_set_module_name_to_none, defs) + ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/home/test/libmodulemd-devel/modulemd/tests/ModulemdTests/base.py", line 60, in assertProcessFailure + callable(*args) + ~~~~~~~~^^^^^^^ + File "/home/test/libmodulemd-devel/redhat-linux-build/../modulemd/tests/ModulemdTests/defaults.py", line 43, in _set_module_name_to_none + defs.props.module_name = None + ^^^^^^^^^^^^^^^^^^^^^^ + TypeError: property 'module-name' can only be set in constructor + +The cause was that pygobject-3.55.0 started to raise a Python +TypeError exception instead of calling Glib functions which would fail +on its own depending on Glib warning fatality and Glib version. An example: + + cat /tmp/test.py + #!/usr/bin/python3 + + import gi + gi.require_version("Modulemd", "2.0") + from gi.repository import Modulemd + + object = Modulemd.Defaults.new(Modulemd.DefaultsVersionEnum.LATEST, "foo") + object.props.module_name = "bar" + +Before: + + $ /tmp/test.py + /tmp/test.py:8: Warning: g_object_set_is_valid_property: construct property "module-name" for object 'ModulemdDefaultsV1' can't be set after construction + object.props.module_name = "bar" + +After: + + $ /tmp/test.py + Traceback (most recent call last): + File "/tmp/test.py", line 8, in + object.props.module_name = "bar" + ^^^^^^^^^^^^^^^^^^^^^^^^ + TypeError: property 'module-name' can only be set in constructor + +That was an intentional change in pygobject +3b6e4804de4f26cfb9472666f18f44ac731d874c commit (gi: Factor out +pygi_set_property_gvalue_from_property_info). Probably an +optimization. + +This patch adjusts the tests to pass if TypeError exception is raised +regardless of Glib warning fatality. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2440570 +--- + modulemd/tests/ModulemdTests/base.py | 30 +++++++++++++++++++ + modulemd/tests/ModulemdTests/defaults.py | 4 ++- + modulemd/tests/ModulemdTests/profile.py | 4 ++- + modulemd/tests/ModulemdTests/servicelevel.py | 2 +- + .../tests/ModulemdTests/translationentry.py | 2 +- + 5 files changed, 38 insertions(+), 4 deletions(-) + +diff --git a/modulemd/tests/ModulemdTests/base.py b/modulemd/tests/ModulemdTests/base.py +index 55ac742..16b90e0 100644 +--- a/modulemd/tests/ModulemdTests/base.py ++++ b/modulemd/tests/ModulemdTests/base.py +@@ -67,6 +67,36 @@ class TestBase(unittest.TestCase): + if os.WIFSIGNALED(status): + raise AssertionError("Child process was unexpectedly aborted") + ++ def assertTypeExceptionOrProcessFailure(self, callable, *args): ++ """Calls the callable in a subprocess and checks that the process ++ raised a TypeError exception, or was killed depending on Glib warning ++ fatality. ++ ++ Since pygobject-3.55.0 setting a G_PARAM_CONSTRUCT_ONLY property ++ raises a Python exception. Old pygobject continues down to Glib ++ which kills the process if Glib warnings a fatal, otherwise Glib ++ warning is printed and the code continues. ++ """ ++ pid = os.fork() ++ if pid == 0: ++ try: ++ callable(*args) ++ except TypeError: ++ os._exit(1) ++ os._exit(0) ++ _, status = os.waitpid(pid, 0) ++ if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 1: ++ return ++ if self.warnings_fatal: ++ if not os.WIFSIGNALED(status): ++ raise AssertionError( ++ "Child process did not raise TypeError " ++ "exception or was not aborted" ++ ) ++ else: ++ if os.WIFSIGNALED(status): ++ raise AssertionError("Child process was unexpectedly aborted") ++ + @property + def warnings_fatal(self): + gdebug = os.getenv("G_DEBUG", "").split(",") +diff --git a/modulemd/tests/ModulemdTests/defaults.py b/modulemd/tests/ModulemdTests/defaults.py +index 3a1b9fe..2f2ec02 100644 +--- a/modulemd/tests/ModulemdTests/defaults.py ++++ b/modulemd/tests/ModulemdTests/defaults.py +@@ -111,7 +111,9 @@ class TestDefaults(TestBase): + assert defs.get_module_name() == "foo" + + # Ensure we cannot set the module_name +- self.assertProcessFailure(_set_module_name_to_none, defs) ++ self.assertTypeExceptionOrProcessFailure( ++ _set_module_name_to_none, defs ++ ) + + def test_modified(self): + defs = Modulemd.Defaults.new( +diff --git a/modulemd/tests/ModulemdTests/profile.py b/modulemd/tests/ModulemdTests/profile.py +index 765c57d..f8c7b93 100644 +--- a/modulemd/tests/ModulemdTests/profile.py ++++ b/modulemd/tests/ModulemdTests/profile.py +@@ -94,7 +94,9 @@ class TestProfile(TestBase): + assert p.get_name() == "testprofile" + assert p.props.name == "testprofile" + +- self.assertProcessFailure(_set_props_name, p, "notadrill") ++ self.assertTypeExceptionOrProcessFailure( ++ _set_props_name, p, "notadrill" ++ ) + + def test_get_set_description(self): + p = Modulemd.Profile(name="testprofile") +diff --git a/modulemd/tests/ModulemdTests/servicelevel.py b/modulemd/tests/ModulemdTests/servicelevel.py +index fc9c648..71289eb 100644 +--- a/modulemd/tests/ModulemdTests/servicelevel.py ++++ b/modulemd/tests/ModulemdTests/servicelevel.py +@@ -103,7 +103,7 @@ class TestServiceLevel(TestBase): + assert sl.props.name == "foo" + + # This property is not writable, make sure it fails to attempt it +- self.assertProcessFailure(_set_props_name, sl, "bar") ++ self.assertTypeExceptionOrProcessFailure(_set_props_name, sl, "bar") + + def test_get_set_eol(self): + sl = Modulemd.ServiceLevel.new("foo") +diff --git a/modulemd/tests/ModulemdTests/translationentry.py b/modulemd/tests/ModulemdTests/translationentry.py +index 9fce443..685349e 100644 +--- a/modulemd/tests/ModulemdTests/translationentry.py ++++ b/modulemd/tests/ModulemdTests/translationentry.py +@@ -172,7 +172,7 @@ class TestTranslationEntry(TestBase): + assert te.get_locale() == "en_US" + assert te.props.locale == "en_US" + +- self.assertProcessFailure(_set_locale, te) ++ self.assertTypeExceptionOrProcessFailure(_set_locale, te) + + def test_get_set_summary(self): + te = Modulemd.TranslationEntry(locale="en_US") +-- +2.53.0 +