Control: severity -1 minor
Control: retitle -1 Improve resolution of conflicting plugins
Control: tags -1 patch upstream

I have investigated this more.  I feel I understand more what the code
in PluginManager was trying to do.

I discovered that I had a gsettings setting, as follows:

$ gsettings get org.blueman.general plugin-list
['!TransferService', '!PPPSupport', '!GameControllerWakelock', 'AuthAgent', 
'DhcpClient', 'Networking', 'NetUsage', 'SerialManager']
$

This was the root cause of the problem.  I didn't set that explicitly.
Presumably it was set by some previous version.  (My home directory
has survived many different Debian releases.)  Using `gsettings reset`
caused the symptoms to go away in my installation.

In my testing I also found that the DhcpClient plugin actually doesn't
work when I try to do bluetooth tethering with my Android phone.  I
didn't investigate the cause any further.  The NMPANSupport plugin
does work.  In the default configuration, the builtin priority
mechanism would prefer NMPANSupport, which is good.

However, I found the logic in PluginManager confusing and a bit
unprincipled.  I attach a patch to rationalise it.  It may print more
warnings now, but it will not crash unless it actually needs to, and
the logic is now the same (and, I hope, reasonable) in all cases.

Ian.


>From 95e85dfbe10deb90dde98101566886ebd50cc410 Mon Sep 17 00:00:00 2001
From: Ian Jackson <ijack...@chiark.greenend.org.uk>
Date: Mon, 3 Feb 2025 12:33:35 +0000
Subject: [PATCH] PluginManager: Rationalise plugin priority/enable/disable

Previously, ad-hoc code was used in the two situations in the
conflict-checking loop, leading to divergent behaviour.

Centralise the calculation of which plugins to prefer.

Only throw an exception if conflicting plugins are explicitly enabled.

Closes: #1082668
---
 blueman/main/PluginManager.py | 35 +++++++++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/blueman/main/PluginManager.py b/blueman/main/PluginManager.py
index 8b2d1a76..024d8a71 100644
--- a/blueman/main/PluginManager.py
+++ b/blueman/main/PluginManager.py
@@ -145,16 +145,18 @@ class PluginManager(GObject.GObject, Generic[_T]):
 
         for cfl in self.__cfls[cls.__name__]:
             if cfl in self.__classes:
-                if self.__classes[cfl].__priority__ > cls.__priority__ and not self.disable_plugin(cfl) \
-                        and not self.enable_plugin(cls.__name__):
-                    logging.warning(f"Not loading {cls.__name__} because its conflict has higher priority")
+                prefer_cfl = self.prefer_plugin(self.__classes[cfl], cls)
+                if prefer_cfl:
+                    logging.warning(f"{prefer_cfl}; not loading {cls.__name__}")
                     return
 
             if cfl in self.__loaded:
-                if cls.__priority__ > self.__classes[cfl].__priority__ and not self.enable_plugin(cfl):
-                    self.unload_plugin(cfl)
+                prefer_cfl = self.prefer_plugin(self.__classes[cfl], cls)
+                if prefer_cfl:
+                    logging.warning(f"{prefer_cfl}; not unloading {cfl}")
+                    return
                 else:
-                    raise LoadException(f"Not loading conflicting plugin {cls.__name__} due to lower priority")
+                    self.unload_plugin(cfl)
 
         logging.info(f"loading {cls}")
         inst = cls(self.parent)
@@ -173,6 +175,27 @@ class PluginManager(GObject.GObject, Generic[_T]):
             self.__loaded.append(cls.__name__)
             self.emit("plugin-loaded", cls.__name__)
 
+    def prefer_plugin(self, a, b):
+        '''
+        Returns a string (for the log) explaining why we prefer A to B, or None if we don't
+        '''
+        if self.enable_plugin(a.__name__):
+            if self.enable_plugin(b.__name__):
+                raise LoadException(f"Plugins {a.__name__} and {b.__name__} both explicitly enabled, but they conflict")
+            else:
+                return f"Preferring {a.__name__} over conflicting {b.__name__} because {a.__name__} is explicitly enabled"
+        else:
+            if self.enable_plugin(b.__name__):
+                return None
+
+        if self.disable_plugin(b.__name__):
+            return f"Preferring {a.__name__} over conflicting {b.__name__} because {b.__name__} is explicitly disabled"
+
+        if a.__priority__ > b.__priority__:
+            return f"Preferring {a.__name__} (priority {a.__priority__}) over conflicting {b.__name__} (priority {b.__priority__})"
+
+        return None
+
     def __getattr__(self, key: str) -> object:
         try:
             return self._plugins[key]
-- 
2.47.2


-- 
Ian Jackson <ijack...@chiark.greenend.org.uk>   These opinions are my own.  

Pronouns: they/he.  If I emailed you from @fyvzl.net or @evade.org.uk,
that is a private address which bypasses my fierce spamfilter.

Reply via email to