Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bluez: Create a bluez manager instance per-event-loop #1034

Merged
merged 1 commit into from
Sep 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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