From af2aeba737cca3f3d2f531099a2fde5929edef31 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 11 Oct 2023 16:43:19 +0200
Subject: [PATCH] Add BDPluginSpec constructor and use it in
 plugin_specs_from_names

Starting with Python 3.12 we got some weird complains when setting
attributes of BlockDev.PluginSpec in plugin_specs_from_names, it's
better to add a fully working constructor to create the struct
correctly from the Python overrides.
---
 docs/libblockdev-sections.txt       |  1 +
 src/lib/plugins.c                   | 18 ++++++++++++++++++
 src/lib/plugins.h                   |  7 +++++++
 src/python/gi/overrides/BlockDev.py | 13 ++++++++++---
 tests/library_test.py               | 16 ++++------------
 tests/lvm_dbus_tests.py             |  7 ++-----
 6 files changed, 42 insertions(+), 20 deletions(-)

diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt
index 8421e24..aa0b4b5 100644
--- a/docs/libblockdev-sections.txt
+++ b/docs/libblockdev-sections.txt
@@ -447,6 +447,7 @@ bd_mpath_is_tech_avail
 BDPlugin
 bd_plugin_spec_copy
 bd_plugin_spec_free
+bd_plugin_spec_new
 bd_is_plugin_available
 bd_get_available_plugin_names
 bd_get_plugin_soname
diff --git a/src/lib/plugins.c b/src/lib/plugins.c
index 53f56b2..3cb6d0d 100644
--- a/src/lib/plugins.c
+++ b/src/lib/plugins.c
@@ -40,6 +40,24 @@ void bd_plugin_spec_free (BDPluginSpec *spec) {
     g_free (spec);
 }
 
+/**
+ * bd_plugin_spec_new: (constructor)
+ * @name: %BDPlugin name, e.g. %BD_PLUGIN_LVM
+ * @so_name: (nullable): SO name of the plugin to load or %NULL for default
+ *
+ * Returns: (transfer full): a new plugin spec
+ */
+BDPluginSpec* bd_plugin_spec_new (BDPlugin name, const gchar *so_name) {
+    BDPluginSpec* ret = g_new0 (BDPluginSpec, 1);
+    ret->name = name;
+    /* FIXME: this should be const, but we are already allocating a new string in _copy
+    *         and freeing it in _free
+    */
+    ret->so_name = so_name ? g_strdup (so_name) : NULL;
+
+    return ret;
+}
+
 GType bd_plugin_spec_get_type (void) {
     static GType type = 0;
 
diff --git a/src/lib/plugins.h b/src/lib/plugins.h
index 8b7c541..3574916 100644
--- a/src/lib/plugins.h
+++ b/src/lib/plugins.h
@@ -24,6 +24,12 @@ typedef enum {
 #define BD_TYPE_PLUGIN_SPEC (bd_plugin_spec_get_type ())
 GType bd_plugin_spec_get_type(void);
 
+/**
+ * BDPluginSpec:
+ * @name: %BDPlugin name, e.g. %BD_PLUGIN_LVM
+ * @so_name: (nullable): SO name of the plugin to load or %NULL for default
+ *
+ */
 typedef struct BDPluginSpec {
     BDPlugin name;
     const gchar *so_name;
@@ -31,6 +37,7 @@ typedef struct BDPluginSpec {
 
 BDPluginSpec* bd_plugin_spec_copy (BDPluginSpec *spec);
 void bd_plugin_spec_free (BDPluginSpec *spec);
+BDPluginSpec* bd_plugin_spec_new (BDPlugin name, const gchar *so_name);
 
 gboolean bd_is_plugin_available (BDPlugin plugin);
 gchar** bd_get_available_plugin_names (void);
diff --git a/src/python/gi/overrides/BlockDev.py b/src/python/gi/overrides/BlockDev.py
index e2cb695..8aac896 100644
--- a/src/python/gi/overrides/BlockDev.py
+++ b/src/python/gi/overrides/BlockDev.py
@@ -85,6 +85,15 @@ for _cname, cls in all_boxed:
     cls.__repr__ = _default_repr
 
 
+class PluginSpec(BlockDev.PluginSpec):
+    def __new__(cls, name=BlockDev.Plugin.UNDEF, so_name=None):
+        ret = BlockDev.PluginSpec.new(name, so_name)
+        ret.__class__ = cls
+        return ret
+PluginSpec = override(PluginSpec)
+__all__.append("PluginSpec")
+
+
 class ExtraArg(BlockDev.ExtraArg):
     def __new__(cls, opt, val=""):
         ret = BlockDev.ExtraArg.new(opt, val)
@@ -1122,9 +1131,7 @@ __all__.append("nvme_connect")
 def plugin_specs_from_names(plugin_names):
     ret = []
     for name in plugin_names:
-        plugin = BlockDev.PluginSpec()
-        plugin.name = bd_plugins[name.lower()]
-        plugin.so_name = None
+        plugin = PluginSpec(name=bd_plugins[name.lower()], so_name=None)
         ret.append(plugin)
 
     return ret
diff --git a/tests/library_test.py b/tests/library_test.py
index 1814d13..cb93da9 100644
--- a/tests/library_test.py
+++ b/tests/library_test.py
@@ -71,9 +71,7 @@ class LibraryOpsTestCase(unittest.TestCase):
     def test_require_plugins(self):
         """Verify that loading only required plugins works as expected"""
 
-        ps = BlockDev.PluginSpec()
-        ps.name = BlockDev.Plugin.SWAP
-        ps.so_name = ""
+        ps = BlockDev.PluginSpec(name=BlockDev.Plugin.SWAP, so_name="")
         self.assertTrue(BlockDev.reinit([ps], True, None))
         self.assertEqual(BlockDev.get_available_plugin_names(), ["swap"])
         self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
@@ -85,9 +83,7 @@ class LibraryOpsTestCase(unittest.TestCase):
         # should be loaded and working
         self.assertTrue(BlockDev.md_canonicalize_uuid("3386ff85:f5012621:4a435f06:1eb47236"))
 
-        ps = BlockDev.PluginSpec()
-        ps.name = BlockDev.Plugin.SWAP
-        ps.so_name = ""
+        ps = BlockDev.PluginSpec(name=BlockDev.Plugin.SWAP, so_name="")
         self.assertTrue(BlockDev.reinit([ps], True, None))
         self.assertEqual(BlockDev.get_available_plugin_names(), ["swap"])
 
@@ -237,9 +233,7 @@ class PluginsTestCase(unittest.TestCase):
         self.assertEqual(ret, 0, "Failed to recompile libblockdev for force plugin test")
 
         # force the new plugin to be used
-        ps = BlockDev.PluginSpec()
-        ps.name = BlockDev.Plugin.LVM
-        ps.so_name = "libbd_lvm2.so"
+        ps = BlockDev.PluginSpec(name=BlockDev.Plugin.LVM, so_name="libbd_lvm2.so")
         self.assertTrue(BlockDev.reinit([ps], True, None))
 
         # new LVM plugin loaded, max LV size should be 1024 bytes
@@ -249,9 +243,7 @@ class PluginsTestCase(unittest.TestCase):
         os.system ("rm -f src/plugins/.libs/libbd_lvm2.so")
 
         # force the old plugin to be used
-        ps = BlockDev.PluginSpec()
-        ps.name = BlockDev.Plugin.LVM
-        ps.so_name = "libbd_lvm.so"
+        ps = BlockDev.PluginSpec(name=BlockDev.Plugin.LVM, so_name="libbd_lvm.so")
         self.assertTrue(BlockDev.reinit([ps], True, None))
 
         self.assertEqual(BlockDev.lvm_get_max_lv_size(), orig_max_size)
diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py
index c0b36b2..327211d 100644
--- a/tests/lvm_dbus_tests.py
+++ b/tests/lvm_dbus_tests.py
@@ -43,11 +43,8 @@ class LVMTestCase(unittest.TestCase):
     def setUpClass(cls):
         if lvm_dbus_running:
             # force the new plugin to be used
-            cls.ps = BlockDev.PluginSpec()
-            cls.ps.name = BlockDev.Plugin.LVM
-            cls.ps.so_name = "libbd_lvm-dbus.so.3"
-            cls.ps2 = BlockDev.PluginSpec()
-            cls.ps2.name = BlockDev.Plugin.LOOP
+            cls.ps = BlockDev.PluginSpec(name=BlockDev.Plugin.LVM, so_name="libbd_lvm-dbus.so.3")
+            cls.ps2 = BlockDev.PluginSpec(name=BlockDev.Plugin.LOOP)
             if not BlockDev.is_initialized():
                 BlockDev.init([cls.ps, cls.ps2], None)
             else:
