Skip to content

Commit

Permalink
backends.winrt: add allow_sta() utility function
Browse files Browse the repository at this point in the history
We had lots of users reporting issues with the WinRT backend hanging
forever when trying to connect to a device. This was happening because
some other imported library was initializing the the main thread to STA
which caused some async callback to never be called.

To work around this, we added a check to make sure the main thread is
set to MTA rather than STA. Unfortunately, in cases where there is a
graphical user interface library being used AND that library is properly
interated with asynco so that Bleak runs in the main thread rather than
in a background thread, the thread type does need to be STA for the GUI
to work. So in those very specific conditions, we need to not raise an
exception.

We don't know of a way to detect this automatically, so we added a new
utility function `allow_sta()` that the user can call to allow Bleak to
run in an STA thread.
  • Loading branch information
dlech committed May 7, 2024
1 parent 4ffc916 commit 3a7ec69
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0
`Unreleased`_
=============

### Added
- Added ``bleak.backends.winrt.util.allow_sta()`` method to allow integration
with graphical user interfaces on Windows. Fixes #1565.

`0.22.0`_ (2024-04-04)
======================

Expand Down
20 changes: 20 additions & 0 deletions bleak/backends/winrt/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ def assert_mta() -> None:
.. versionadded:: 0.22
"""
if hasattr(allow_sta, "_allowed"):
return

try:
apt_type, _ = _get_apartment_type()
if apt_type != _AptType.MTA:
Expand All @@ -80,6 +83,23 @@ def assert_mta() -> None:
if e.winerror != _CO_E_NOTINITIALIZED:
raise

def allow_sta():
"""
Suppress check for MTA thread type and allow STA.
Bleak will hang forever if the current thread is not MTA - unless there is
a Windows event loop running that is properly integrated with asyncio in
Python.
If your program meets that condition, you must call this function do disable
the check for MTA. If your program doesn't have a graphical user interface
you probably shouldn't call this function. and use ``uninitialize_sta()``
instead.
.. versionadded:: unreleased
"""
allow_sta._allowed = True


def uninitialize_sta():
"""
Expand Down
21 changes: 19 additions & 2 deletions docs/troubleshooting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,25 @@ Bleak should detect this and raise an exception with a message similar to::

The current thread apartment type is not MTA: STA.

To work around this, you can use a utility function provided by Bleak to
uninitialize the threading model after importing an offending package::
To work around this, you can use one of the utility functions provided by Bleak.

If your program has a graphical user interface and the UI framework *and* it is
properly integrated with asyncio *and* Bleak is not running on a background
thread then call ``allow_sta()`` before calling any other Bleak APis::

try:
from bleak.backends.winrt.util import allow_sta
# tell Bleak we are using a graphical user interface that has been properly
# configured to work with asyncio
allow_sta()
except ImportError:
# other OSes and older versions of Bleak will raise ImportError which we
# can safely ignore
pass

The more typical case, though, is that some library has imported something like
``pywin32`` which breaks Bleak. In this case, you can uninitialize the threading
model like this::

import win32com # this sets current thread to STA :-(
from bleak.backends.winrt.utils import uninitialize_sta
Expand Down

0 comments on commit 3a7ec69

Please sign in to comment.