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.19.2 #1116

Merged
merged 6 commits into from
Nov 6, 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
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.19.2`_ (2022-11-06)
======================

Fixed
------
* Fixed crash when getting services in WinRT backend in Python 3.11. Fixes #1112.
* Fixed cache mode when retrying get services in WinRT backend. Merged #1102.
* Fixed ``KeyError`` crash in BlueZ backend when removing non-existent property. Fixes #1107.

`0.19.1`_ (2022-10-29)
======================

Expand Down Expand Up @@ -850,7 +859,8 @@ Fixed
* Bleak created.


.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.19.1...develop
.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.19.2...develop
.. _0.19.2: https://github.com/hbldh/bleak/compare/v0.19.1...v0.19.2
.. _0.19.1: https://github.com/hbldh/bleak/compare/v0.19.0...v0.19.1
.. _0.19.0: https://github.com/hbldh/bleak/compare/v0.18.1...v0.19.0
.. _0.18.1: https://github.com/hbldh/bleak/compare/v0.18.0...v0.18.1
Expand Down
7 changes: 6 additions & 1 deletion bleak/backends/bluezdbus/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,12 @@ def _parse_msg(self, message: Message):
self_interface.update(unpack_variants(changed))

for name in invalidated:
del self_interface[name]
try:
del self_interface[name]
except KeyError:
# sometimes there BlueZ tries to remove properties
# that were never added
pass

# then call any callbacks so they will be called with the
# updated state
Expand Down
82 changes: 76 additions & 6 deletions bleak/backends/winrt/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
"""

import asyncio
import functools
import logging
import sys
import uuid
import warnings
from ctypes import pythonapi
from typing import Any, Dict, List, Optional, Sequence, Union, cast

import async_timeout
Expand Down Expand Up @@ -44,11 +46,15 @@
DevicePairingResultStatus,
DeviceUnpairingResultStatus,
)
from bleak_winrt.windows.foundation import EventRegistrationToken
from bleak_winrt.windows.foundation import (
AsyncStatus,
EventRegistrationToken,
IAsyncOperation,
)
from bleak_winrt.windows.storage.streams import Buffer

from ... import BleakScanner
from ...exc import PROTOCOL_ERROR_CODES, BleakError, BleakDeviceNotFoundError
from ...exc import PROTOCOL_ERROR_CODES, BleakDeviceNotFoundError, BleakError
from ..characteristic import BleakGATTCharacteristic
from ..client import BaseBleakClient, NotifyCallback
from ..device import BLEDevice
Expand Down Expand Up @@ -250,7 +256,7 @@ async def connect(self, **kwargs) -> bool:

def handle_services_changed():
if not self._services_changed_events:
logger.warn("%s: unhandled services changed event", self.address)
logger.warning("%s: unhandled services changed event", self.address)
else:
for event in self._services_changed_events:
event.set()
Expand Down Expand Up @@ -568,7 +574,10 @@ async def get_services(self, **kwargs) -> BleakGATTServiceCollection:
services_changed_event.wait()
)
self._services_changed_events.append(services_changed_event)
get_services_task = self._requester.get_gatt_services_async(*args)

get_services_task = FutureLike(
self._requester.get_gatt_services_async(*args)
)

try:
await asyncio.wait(
Expand All @@ -587,10 +596,10 @@ async def get_services(self, **kwargs) -> BleakGATTServiceCollection:
"%s: restarting get services due to services changed event",
self.address,
)
args = [BluetoothCacheMode.UNCACHED]
args = [BluetoothCacheMode.CACHED]

services: Sequence[GattDeviceService] = _ensure_success(
get_services_task.get_results(),
get_services_task.result(),
"services",
"Could not get GATT services",
)
Expand Down Expand Up @@ -885,3 +894,64 @@ async def stop_notify(

event_handler_token = self._notification_callbacks.pop(characteristic.handle)
characteristic.obj.remove_value_changed(event_handler_token)


class FutureLike:
"""
Wraps a WinRT IAsyncOperation in a "future-like" object so that it can
be passed to Python APIs.

Needed until https://github.com/pywinrt/pywinrt/issues/14
"""

_asyncio_future_blocking = True

def __init__(self, async_result: IAsyncOperation) -> None:
self._async_result = async_result
self._callbacks = []
self._loop = asyncio.get_running_loop()

def call_callbacks(op: IAsyncOperation, status: AsyncStatus):
for c in self._callbacks:
c(self)

async_result.completed = functools.partial(
self._loop.call_soon_threadsafe, call_callbacks
)

def result(self) -> Any:
return self._async_result.get_results()

def done(self) -> bool:
return self._async_result.status != AsyncStatus.STARTED

def cancelled(self) -> bool:
return self._async_result.status == AsyncStatus.CANCELED

def add_done_callback(self, callback, *, context=None) -> None:
self._callbacks.append(callback)

def remove_done_callback(self, callback) -> None:
self._callbacks.remove(callback)

def cancel(self, msg=None) -> bool:
if self._async_result.status != AsyncStatus.STARTED:
return False
self._async_result.cancel()
return True

def exception(self) -> Optional[Exception]:
if self._async_result.status == AsyncStatus.STARTED:
raise asyncio.InvalidStateError
if self._async_result.status == AsyncStatus.COMPLETED:
return None
if self._async_result.status == AsyncStatus.CANCELED:
raise asyncio.CancelledError
if self._async_result.status == AsyncStatus.ERROR:
try:
pythonapi.PyErr_SetFromWindowsErr(self._async_result.error_code)
except OSError as e:
return e

def get_loop(self) -> asyncio.AbstractEventLoop:
return self._loop
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "bleak"
version = "0.19.1"
version = "0.19.2"
description = "Bluetooth Low Energy platform Agnostic Klient"
authors = ["Henrik Blidh <henrik.blidh@nedomkull.com>"]
license = "MIT"
Expand Down