Skip to content

Commit

Permalink
Merge pull request #1652 from hbldh/release/0.22.3
Browse files Browse the repository at this point in the history
Release/0.22.3
  • Loading branch information
dlech authored Oct 5, 2024
2 parents ce48c7f + b094466 commit 2e6890f
Show file tree
Hide file tree
Showing 16 changed files with 333 additions and 261 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13-dev']
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
id: py
with:
python-version: ${{ matrix.python-version }}
Expand All @@ -33,7 +33,7 @@ jobs:
run: |
pipx run poetry run pytest tests --junitxml=junit/test-results-${{ matrix.os }}-${{ matrix.python-version }}.xml --cov=com --cov-report=xml --cov-report=html
- name: Upload pytest test results
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: pytest-results-${{ matrix.os }}-${{ matrix.python-version }}
path: junit/test-results-${{ matrix.os }}-${{ matrix.python-version }}.xml
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build_android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ jobs:
name: "Build Android"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install dependencies
run: pip install buildozer cython
- name: Cache buildozer files
uses: actions/cache@v3
uses: actions/cache@v4
id: buildozer-cache
with:
path: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/format_and_lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
name: "Format and lint"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
- name: Install development dependencies
run: pipx run poetry install --only docs,lint
- name: Check import sort with isort
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pypi-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Build
run: pipx run poetry build
- name: Publish
Expand Down
21 changes: 14 additions & 7 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,21 @@ version: 2

# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
os: ubuntu-24.04
tools:
python: "3.9"
python: "3.12"
jobs:
post_create_environment:
# Install poetry
# https://python-poetry.org/docs/#installing-manually
- pip install poetry
post_install:
# Install dependencies with 'docs' dependency group
# https://python-poetry.org/docs/managing-dependencies/#dependency-groups
# VIRTUAL_ENV needs to be set manually for now.
# See https://github.com/readthedocs/readthedocs.org/pull/11152/
- VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --only docs


# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand All @@ -18,8 +30,3 @@ sphinx:
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats:
- pdf

# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: docs/requirements.txt
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Contributors
* Robbe Gaeremynck <[email protected]>
* David Johansen <[email protected]>
* JP Hutchins <[email protected]>
* Bram Duvigneau <[email protected]>

Sponsors
--------
Expand Down
Binary file added Bleak_logo2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 11 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0
`Unreleased`_
=============

`0.22.3`_ (2024-10-05)
======================

Changed
-------
* Don't change ctypes' global state ``bleak.backends.winrt.util``.
* Improved performance of BlueZ backend when there are many adapters.
* Added support for Python 3.13.

`0.22.2`_ (2024-06-01)
======================

Expand Down Expand Up @@ -1038,7 +1047,8 @@ Fixed
* Bleak created.


.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.22.2...develop
.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.22.3...develop
.. _0.22.3: https://github.com/hbldh/bleak/compare/v0.22.2...v0.22.3
.. _0.22.2: https://github.com/hbldh/bleak/compare/v0.22.1...v0.22.2
.. _0.22.1: https://github.com/hbldh/bleak/compare/v0.22.0...v0.22.1
.. _0.22.0: https://github.com/hbldh/bleak/compare/v0.21.1...v0.22.0
Expand Down
4 changes: 1 addition & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
bleak
=====

.. figure:: https://raw.githubusercontent.com/hbldh/bleak/master/Bleak_logo.png
.. image:: https://raw.githubusercontent.com/hbldh/bleak/master/Bleak_logo2.png
:target: https://github.com/hbldh/bleak
:alt: Bleak Logo
:scale: 50%


.. image:: https://github.com/hbldh/bleak/workflows/Build%20and%20Test/badge.svg
:target: https://github.com/hbldh/bleak/actions?query=workflow%3A%22Build+and+Test%22
Expand Down
60 changes: 23 additions & 37 deletions bleak/backends/bluezdbus/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
import contextlib
import logging
import os
from collections import defaultdict
from typing import (
Any,
Callable,
Coroutine,
Dict,
Iterable,
List,
MutableMapping,
NamedTuple,
Expand Down Expand Up @@ -55,22 +55,6 @@
"""


class CallbackAndState(NamedTuple):
"""
Encapsulates an :data:`AdvertisementCallback` and some state.
"""

callback: AdvertisementCallback
"""
The callback.
"""

adapter_path: str
"""
The D-Bus object path of the adapter associated with the callback.
"""


DevicePropertiesChangedCallback = Callable[[Optional[Any]], None]
"""
A callback that is called when the properties of a device change in BlueZ.
Expand Down Expand Up @@ -194,7 +178,9 @@ def __init__(self):
# map of characteristic d-bus object paths to set of descriptor d-bus object paths
self._descriptor_map: Dict[str, Set[str]] = {}

self._advertisement_callbacks: List[CallbackAndState] = []
self._advertisement_callbacks: defaultdict[str, List[AdvertisementCallback]] = (
defaultdict(list)
)
self._device_removed_callbacks: List[DeviceRemovedCallbackAndState] = []
self._device_watchers: Dict[str, Set[DeviceWatcher]] = {}
self._condition_callbacks: Dict[str, Set[DeviceConditionCallback]] = {}
Expand Down Expand Up @@ -404,8 +390,7 @@ async def active_scan(
# error message.
self._check_adapter(adapter_path)

callback_and_state = CallbackAndState(advertisement_callback, adapter_path)
self._advertisement_callbacks.append(callback_and_state)
self._advertisement_callbacks[adapter_path].append(advertisement_callback)

device_removed_callback_and_state = DeviceRemovedCallbackAndState(
device_removed_callback, adapter_path
Expand Down Expand Up @@ -441,7 +426,9 @@ async def stop() -> None:
# need to remove callbacks first, otherwise we get TxPower
# and RSSI properties removed during stop which causes
# incorrect advertisement data callbacks
self._advertisement_callbacks.remove(callback_and_state)
self._advertisement_callbacks[adapter_path].remove(
advertisement_callback
)
self._device_removed_callbacks.remove(
device_removed_callback_and_state
)
Expand Down Expand Up @@ -478,7 +465,9 @@ async def stop() -> None:
return stop
except BaseException:
# if starting scanning failed, don't leak the callbacks
self._advertisement_callbacks.remove(callback_and_state)
self._advertisement_callbacks[adapter_path].remove(
advertisement_callback
)
self._device_removed_callbacks.remove(device_removed_callback_and_state)
raise

Expand Down Expand Up @@ -512,8 +501,7 @@ async def passive_scan(
# error message.
self._check_adapter(adapter_path)

callback_and_state = CallbackAndState(advertisement_callback, adapter_path)
self._advertisement_callbacks.append(callback_and_state)
self._advertisement_callbacks[adapter_path].append(advertisement_callback)

device_removed_callback_and_state = DeviceRemovedCallbackAndState(
device_removed_callback, adapter_path
Expand Down Expand Up @@ -556,7 +544,9 @@ async def stop() -> None:
# need to remove callbacks first, otherwise we get TxPower
# and RSSI properties removed during stop which causes
# incorrect advertisement data callbacks
self._advertisement_callbacks.remove(callback_and_state)
self._advertisement_callbacks[adapter_path].remove(
advertisement_callback
)
self._device_removed_callbacks.remove(
device_removed_callback_and_state
)
Expand All @@ -580,7 +570,9 @@ async def stop() -> None:

except BaseException:
# if starting scanning failed, don't leak the callbacks
self._advertisement_callbacks.remove(callback_and_state)
self._advertisement_callbacks[adapter_path].remove(
advertisement_callback
)
self._device_removed_callbacks.remove(device_removed_callback_and_state)
raise

Expand Down Expand Up @@ -926,7 +918,7 @@ def _parse_msg(self, message: Message) -> None:
# devices that only advertise once and then go to sleep for a while.
elif interface == defs.DEVICE_INTERFACE:
self._run_advertisement_callbacks(
obj_path, cast(Device1, unpacked_props), unpacked_props.keys()
obj_path, cast(Device1, unpacked_props)
)
elif message.member == "InterfacesRemoved":
obj_path, interfaces = message.body
Expand Down Expand Up @@ -1003,7 +995,7 @@ def _parse_msg(self, message: Message) -> None:
device_path = message_path

self._run_advertisement_callbacks(
device_path, cast(Device1, self_interface), changed.keys()
device_path, cast(Device1, self_interface)
)

# handle device condition watchers
Expand Down Expand Up @@ -1035,22 +1027,16 @@ def _parse_msg(self, message: Message) -> None:
message_path, new_value
)

def _run_advertisement_callbacks(
self, device_path: str, device: Device1, changed: Iterable[str]
) -> None:
def _run_advertisement_callbacks(self, device_path: str, device: Device1) -> None:
"""
Runs any registered advertisement callbacks.
Args:
device_path: The D-Bus object path of the remote device.
device: The current D-Bus properties of the device.
changed: A list of properties that have changed since the last call.
"""
for callback, adapter_path in self._advertisement_callbacks:
# filter messages from other adapters
if adapter_path != device["Adapter"]:
continue

adapter_path = device["Adapter"]
for callback in self._advertisement_callbacks[adapter_path]:
callback(device_path, device.copy())


Expand Down
41 changes: 30 additions & 11 deletions bleak/backends/winrt/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,43 @@ def _check_hresult(result, func, args):
)

# https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-settimer
_SetTimer = ctypes.windll.user32.SetTimer
_SetTimer.restype = _UINT_PTR
_SetTimer.argtypes = [wintypes.HWND, _UINT_PTR, wintypes.UINT, _TIMERPROC]
_SET_TIMER_PROTOTYPE = ctypes.WINFUNCTYPE(
_UINT_PTR, wintypes.HWND, _UINT_PTR, wintypes.UINT, _TIMERPROC
)
_SET_TIMER_PARAM_FLAGS = (
(1, "hwnd", None),
(1, "nidevent"),
(1, "uelapse"),
(1, "lptimerfunc", None),
)
_SetTimer = _SET_TIMER_PROTOTYPE(
("SetTimer", ctypes.windll.user32), _SET_TIMER_PARAM_FLAGS
)
_SetTimer.errcheck = _check_result

# https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-killtimer
_KillTimer = ctypes.windll.user32.KillTimer
_KillTimer.restype = wintypes.BOOL
_KillTimer.argtypes = [wintypes.HWND, wintypes.UINT]

_KILL_TIMER_PROTOTYPE = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, _UINT_PTR)
_KILL_TIMER_PARAM_FLAGS = (
(1, "hwnd", None),
(1, "uidevent"),
)
_KillTimer = _KILL_TIMER_PROTOTYPE(
("KillTimer", ctypes.windll.user32), _KILL_TIMER_PARAM_FLAGS
)

# https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cogetapartmenttype
_CoGetApartmentType = ctypes.windll.ole32.CoGetApartmentType
_CoGetApartmentType.restype = ctypes.c_int
_CoGetApartmentType.argtypes = [
_CO_GET_APARTMENT_TYPE_PROTOTYPE = ctypes.WINFUNCTYPE(
ctypes.c_int,
ctypes.POINTER(ctypes.c_int),
ctypes.POINTER(ctypes.c_int),
]
)
_CO_GET_APARTMENT_TYPE_PARAM_FLAGS = (
(1, "papttype", None),
(1, "paptqualifier", None),
)
_CoGetApartmentType = _CO_GET_APARTMENT_TYPE_PROTOTYPE(
("CoGetApartmentType", ctypes.windll.ole32), _CO_GET_APARTMENT_TYPE_PARAM_FLAGS
)
_CoGetApartmentType.errcheck = _check_hresult

_CO_E_NOTINITIALIZED = -2147221008
Expand Down
2 changes: 1 addition & 1 deletion bleak/uuids.py
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,7 @@
"e95d9775-251d-470a-a062-fa1922dfa9a8": "MicroBit Event Data",
"e95d23c4-251d-470a-a062-fa1922dfa9a8": "MicroBit Client Requirements",
"e95d5404-251d-470a-a062-fa1922dfa9a8": "MicroBit Client Events",
"e95d93b0-251d-470a-a062-fa1922dfa9a8": "MicroBit DFU Control Service" "",
"e95d93b0-251d-470a-a062-fa1922dfa9a8": "MicroBit DFU Control Service",
"e95d93b1-251d-470a-a062-fa1922dfa9a8": "MicroBit DFU Control",
"e95d6100-251d-470a-a062-fa1922dfa9a8": "MicroBit Temperature Service",
"e95d1b25-251d-470a-a062-fa1922dfa9a8": "MicroBit Temperature Period",
Expand Down
Loading

0 comments on commit 2e6890f

Please sign in to comment.