Skip to content

Commit

Permalink
bluetooth: don't fail to finalize() if we fail to connect()
Browse files Browse the repository at this point in the history
We have reports of the following stack trace set:

    2024-05-01 18:50:24,670 ERROR libqtile loop.py:_handle_exception():L62 Exception in event loop:
    Traceback (most recent call last):
      File "/usr/lib/python3.12/site-packages/libqtile/widget/bluetooth.py", line 393, in _config_async
        await self._connect()
      File "/usr/lib/python3.12/site-packages/libqtile/widget/bluetooth.py", line 397, in _connect
        self.bus = await MessageBus(bus_type=BusType.SYSTEM).connect()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/lib/python3.12/site-packages/dbus_next/aio/message_bus.py", line 149, in connect
        await self._authenticate()
      File "/usr/lib/python3.12/site-packages/dbus_next/aio/message_bus.py", line 390, in _authenticate
        response = self._auth._receive_line(await self._auth_readline())
                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/lib/python3.12/site-packages/dbus_next/aio/message_bus.py", line 376, in _auth_readline
        buf += await self._loop.sock_recv(self._sock, 2)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/lib/python3.12/asyncio/selector_events.py", line 391, in sock_recv
        handle = self._add_reader(fd, self._sock_recv, fut, sock, n)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/lib/python3.12/asyncio/selector_events.py", line 288, in _add_reader
        self._selector.modify(fd, mask | selectors.EVENT_READ,
      File "/usr/lib/python3.12/selectors.py", line 389, in modify
        self._selector.modify(key.fd, selector_events)
    FileNotFoundError: [Errno 2] No such file or directory
    2024-05-01 18:50:24,906 ERROR libqtile manager.py:_finalize_configurables():L321 exception during finalize
    Traceback (most recent call last):
      File "/usr/lib/python3.12/site-packages/libqtile/core/manager.py", line 309, in _finalize_configurables
        widget.finalize()
      File "/usr/lib/python3.12/site-packages/libqtile/widget/bluetooth.py", line 689, in finalize
        self.object_manager.off_interfaces_added(self._interface_added)
        ^^^^^^^^^^^^^^^^^^^
      File "/usr/lib/python3.12/site-packages/libqtile/command/base.py", line 281, in __getattr__
        raise AttributeError(f"{self.__class__} has no attribute {name}")
    AttributeError: <class 'libqtile.widget.bluetooth.Bluetooth'> has no attribute object_manager
    2024-05-01 18:50:24,920 ERROR libqtile bar.py:_configure_widget():L388 Systray widget crashed during _configure with error:
    Traceback (most recent call last):
      File "/usr/lib/python3.12/site-packages/libqtile/bar.py", line 379, in _configure_widget
        widget._configure(self.qtile, self)
      File "/usr/lib/python3.12/site-packages/libqtile/widget/systray.py", line 154, in _configure
        raise ConfigError("Only one Systray can be used.")
    libqtile.confreader.ConfigError: Only one Systray can be used.

When the bluetooth widget fails to connect, it leaves things in a bad state
where finalize() will fail. When a finalize() fails in
_finalize_configurables(), we skip all other finalization, which Systray
requires to decrement it's class var for the instance count. Once that
fails, you can never create another Systray instance.

Instead, let's test that the bus is reasonable before we try to do
anything, and test that the object manager was actually derived before
trying to use it.

Fixes #4797

Signed-off-by: Tycho Andersen <tycho@tycho.pizza>
  • Loading branch information
tych0 committed May 3, 2024
1 parent 2c2b713 commit 8ab558f
Showing 1 changed file with 9 additions and 3 deletions.
12 changes: 9 additions & 3 deletions libqtile/widget/bluetooth.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ def __init__(self, **config):
{"Button1": self.click, "Button4": self.scroll_up, "Button5": self.scroll_down}
)
self.timer = None
self.object_manager = None

def _configure(self, qtile, bar):
base._TextBox._configure(self, qtile, bar)
Expand Down Expand Up @@ -674,15 +675,20 @@ def hide(self):
self.refresh()

def finalize(self):
"""Remove dbus signal handlers before finalising."""
# if we failed to connect, there is nothing to finalize.
if self.bus is None:
return

# Remove dbus signal handlers before finalising.
# Clearing dicts will call the __del__ method on the stored objects
# which has been defined to remove signal handlers
self.devices.clear()
self.adapters.clear()

# Remove object manager's handlers
self.object_manager.off_interfaces_added(self._interface_added)
self.object_manager.off_interfaces_removed(self._interface_removed)
if self.object_manager is not None:
self.object_manager.off_interfaces_added(self._interface_added)
self.object_manager.off_interfaces_removed(self._interface_removed)

# Disconnect the bus connection
self.bus.disconnect()
Expand Down

0 comments on commit 8ab558f

Please sign in to comment.