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

Release/0.22.3 #1652

Merged
merged 15 commits into from
Oct 5, 2024
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
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 <robbe.gaeremynck@outlook.com>
* David Johansen <davejohansen@gmail.com>
* JP Hutchins <jphutchins@gmail.com>
* Bram Duvigneau <bram@bramd.nl>

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
Loading