Skip to content

Commit

Permalink
bluez: Create a bluez manager instance per-event-loop
Browse files Browse the repository at this point in the history
Avoids "got Future <Future pending> attached to a different loop" if Bleak
instances are constructed in context of more than one event loop.
  • Loading branch information
projectgus authored and dlech committed Sep 27, 2022
1 parent 18f7e9a commit 433a811
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 11 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ Added
-----
* Added support for Python 3.11.

Fixed
-----
* On BlueZ, support creating additional instances running on a different event
loops (i.e. multiple pytest-asyncio cases)

`0.18.1`_ (2022-09-25)
======================

Expand Down
16 changes: 11 additions & 5 deletions bleak/backends/bluezdbus/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
Set,
cast,
)
from weakref import WeakKeyDictionary

from dbus_fast import BusType, Message, MessageType, Variant
from dbus_fast.aio.message_bus import MessageBus
Expand Down Expand Up @@ -843,15 +844,20 @@ def _run_advertisement_callbacks(
callback(device_path, device.copy())


_global_instances: Dict[Any, BlueZManager] = WeakKeyDictionary()


async def get_global_bluez_manager() -> BlueZManager:
"""
Gets the initialized global BlueZ manager instance.
Gets an existing initialized global BlueZ manager instance associated with the current event loop,
or initializes a new instance.
"""

if not hasattr(get_global_bluez_manager, "instance"):
setattr(get_global_bluez_manager, "instance", BlueZManager())

instance: BlueZManager = getattr(get_global_bluez_manager, "instance")
loop = asyncio.get_running_loop()
try:
instance = _global_instances[loop]
except KeyError:
instance = _global_instances[loop] = BlueZManager()

await instance.async_init()

Expand Down
21 changes: 15 additions & 6 deletions docs/backends/linux.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
Linux backend
=============

The Linux backend of Bleak is written using the
`TxDBus <https://github.com/cocagne/txdbus>`_
package. It is written for
`Twisted <https://twistedmatrix.com/trac/>`_, but by using the
`twisted.internet.asyncioreactor <https://twistedmatrix.com/documents/current/api/twisted.internet.asyncioreactor.html>`_
one can use it with `asyncio`.
The Linux backend of Bleak communicates with `BlueZ <http://www.bluez.org/>`_
over DBus. Communication uses the `dbus-fast
<https://github.com/Bluetooth-Devices/dbus-fast>`_ package for async access to
DBus messaging.


Special handling for ``write_gatt_char``
Expand All @@ -33,6 +31,15 @@ return the cached services without waiting for them to be resolved again. This
is useful when you know services have not changed, and you want to use the
services immediately, but don't want to wait for them to be resolved again.

Parallel Access
---------------

Each Bleak object should be created and used from a single `asyncio event
loop`_. Simple asyncio programs will only have a single event loop. It's also
possible to use Bleak with multiple event loops, even at the same time, but
individual Bleak objects should not be shared between event loops. Otherwise,
RuntimeErrors similar to ``[...] got Future <Future pending> attached to a
different loop`` will be thrown.

API
---
Expand All @@ -48,3 +55,5 @@ Client

.. automodule:: bleak.backends.bluezdbus.client
:members:

.. _`asyncio event loop`: https://docs.python.org/3/library/asyncio-eventloop.html

0 comments on commit 433a811

Please sign in to comment.