diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5ef2053d3..aa825f94f 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -7,6 +7,7 @@ on: pull_request: branches: [ master, develop ] paths: [ 'bleak/**', 'tests/**' ] + workflow_dispatch: jobs: build_desktop: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 91f1c18b2..8d5c770e1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,22 @@ and this project adheres to `Semantic Versioning =9` + + if sys.implementation.version <= (9,): + raise NotImplementedError("Bleak does not support CircuitPython < 9") + else: + + class _AsyncTimeout: + def __init__(self, timeout): + if sys.implementation.name != "circuitpython": + print( + "Warning: This timeout manager is for CircuitPython. " + "Use the native `asyncio.timeout` on standard Python." + ) + self.timeout = timeout + self._task = None + + async def __aenter__(self): + self._task = asyncio.create_task(asyncio.sleep(self.timeout)) + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self._task and not self._task.done(): + self._task.cancel() + try: + await self._task + except asyncio.CancelledError: + pass + + if exc_type is asyncio.CancelledError and self._task.done(): + if self._task.cancelled(): + raise asyncio.TimeoutError("Operation timed out") from exc_val + + # If any other exception occurred, let it propagate. + return False + + async_timeout = _AsyncTimeout + +elif sys.implementation.name == "pypy": + raise NotImplementedError("Unsupported Python implementation: pypy") + +else: + # can't use on pypy for example + raise NotImplementedError( + f"Unsupported Python implementation: {sys.implementation.name}" + ) + +assert async_timeout # Ensure we have async_timeout defined diff --git a/bleak/backends/bluezdbus/client.py b/bleak/backends/bluezdbus/client.py index 6d1227ba6..4dafc4be5 100644 --- a/bleak/backends/bluezdbus/client.py +++ b/bleak/backends/bluezdbus/client.py @@ -17,11 +17,7 @@ from contextlib import AsyncExitStack from typing import Any, Optional, Union -if sys.version_info < (3, 12): - from typing_extensions import Buffer, override -else: - from collections.abc import Buffer - from typing import override +from typing_extensions import Buffer, override if sys.version_info < (3, 11): from async_timeout import timeout as async_timeout @@ -49,7 +45,7 @@ logger = logging.getLogger(__name__) # prevent tasks from being garbage collected -_background_tasks = set[asyncio.Task[None]]() +_background_tasks: set[asyncio.Task[None]] = set() class BleakClientBlueZDBus(BaseBleakClient): diff --git a/bleak/backends/bluezdbus/scanner.py b/bleak/backends/bluezdbus/scanner.py index 1d5c9983b..e0ee3d64c 100644 --- a/bleak/backends/bluezdbus/scanner.py +++ b/bleak/backends/bluezdbus/scanner.py @@ -10,12 +10,8 @@ from typing import Any, Literal, Optional from warnings import warn -if sys.version_info < (3, 12): - from typing_extensions import override -else: - from typing import override - from dbus_fast import Variant +from typing_extensions import override from bleak.args.bluez import BlueZDiscoveryFilters as _BlueZDiscoveryFilters from bleak.args.bluez import BlueZScannerArgs as _BlueZScannerArgs diff --git a/bleak/backends/client.py b/bleak/backends/client.py index c0e8cec00..b647c26b7 100644 --- a/bleak/backends/client.py +++ b/bleak/backends/client.py @@ -10,10 +10,7 @@ from collections.abc import Callable from typing import Any, Optional, Union -if sys.version_info < (3, 12): - from typing_extensions import Buffer -else: - from collections.abc import Buffer +from typing_extensions import Buffer from bleak.backends.characteristic import BleakGATTCharacteristic from bleak.backends.descriptor import BleakGATTDescriptor diff --git a/bleak/backends/corebluetooth/client.py b/bleak/backends/corebluetooth/client.py index 173915d1f..3962082a7 100644 --- a/bleak/backends/corebluetooth/client.py +++ b/bleak/backends/corebluetooth/client.py @@ -14,12 +14,6 @@ import logging from typing import Any, Optional, Union -if sys.version_info < (3, 12): - from typing_extensions import Buffer, override -else: - from collections.abc import Buffer - from typing import override - from CoreBluetooth import ( CBUUID, CBCharacteristicWriteWithoutResponse, @@ -28,6 +22,7 @@ CBPeripheralStateConnected, ) from Foundation import NSArray, NSData +from typing_extensions import Buffer, override from bleak import BleakScanner from bleak.args.corebluetooth import CBStartNotifyArgs diff --git a/bleak/backends/corebluetooth/scanner.py b/bleak/backends/corebluetooth/scanner.py index 39a85da4c..6803d160d 100644 --- a/bleak/backends/corebluetooth/scanner.py +++ b/bleak/backends/corebluetooth/scanner.py @@ -9,14 +9,10 @@ from typing import Any, Literal, Optional from warnings import warn -if sys.version_info < (3, 12): - from typing_extensions import override -else: - from typing import override - import objc from CoreBluetooth import CBPeripheral from Foundation import NSBundle, NSDictionary +from typing_extensions import override from bleak.args.corebluetooth import CBScannerArgs as _CBScannerArgs from bleak.backends.corebluetooth.CentralManagerDelegate import CentralManagerDelegate diff --git a/bleak/backends/p4android/client.py b/bleak/backends/p4android/client.py index 0ee2fe095..5b884da3b 100644 --- a/bleak/backends/p4android/client.py +++ b/bleak/backends/p4android/client.py @@ -15,13 +15,9 @@ import warnings from typing import Any, Optional, Union -if sys.version_info < (3, 12): - from typing_extensions import override -else: - from typing import override - from android.broadcast import BroadcastReceiver from jnius import java_method +from typing_extensions import override from bleak.assigned_numbers import gatt_char_props_to_strs from bleak.backends.characteristic import BleakGATTCharacteristic diff --git a/bleak/backends/p4android/scanner.py b/bleak/backends/p4android/scanner.py index 7476f2d4e..ac7a454c9 100644 --- a/bleak/backends/p4android/scanner.py +++ b/bleak/backends/p4android/scanner.py @@ -10,16 +10,13 @@ import warnings from typing import Literal, Optional +from typing_extensions import override + if sys.version_info < (3, 11): from async_timeout import timeout as async_timeout else: from asyncio import timeout as async_timeout -if sys.version_info < (3, 12): - from typing_extensions import override -else: - from typing import override - from android.broadcast import BroadcastReceiver from android.permissions import Permission, request_permissions from jnius import cast, java_method diff --git a/bleak/backends/scanner.py b/bleak/backends/scanner.py index 84af3ee56..4ec613e03 100644 --- a/bleak/backends/scanner.py +++ b/bleak/backends/scanner.py @@ -11,7 +11,7 @@ from bleak.exc import BleakError # prevent tasks from being garbage collected -_background_tasks = set[asyncio.Task[None]]() +_background_tasks: set[asyncio.Task[None]] = set() class AdvertisementData(NamedTuple): diff --git a/bleak/backends/winrt/client.py b/bleak/backends/winrt/client.py index da5800c63..4b927a2a6 100644 --- a/bleak/backends/winrt/client.py +++ b/bleak/backends/winrt/client.py @@ -19,18 +19,12 @@ from typing import Any, Generic, Optional, Protocol, Sequence, TypeVar, Union, cast from warnings import warn -if sys.version_info < (3, 12): - from typing_extensions import Buffer, override -else: - from collections.abc import Buffer - from typing import override +from typing_extensions import Buffer, Self, assert_never, override if sys.version_info < (3, 11): from async_timeout import timeout as async_timeout - from typing_extensions import Self, assert_never else: from asyncio import timeout as async_timeout - from typing import Self, assert_never from winrt.system import Object from winrt.windows.devices.bluetooth import ( diff --git a/bleak/backends/winrt/scanner.py b/bleak/backends/winrt/scanner.py index d25318f2e..92e95b227 100644 --- a/bleak/backends/winrt/scanner.py +++ b/bleak/backends/winrt/scanner.py @@ -10,11 +10,7 @@ from typing import Literal, NamedTuple, Optional from uuid import UUID -if sys.version_info < (3, 12): - from typing_extensions import override -else: - from typing import override - +from typing_extensions import override from winrt.windows.devices.bluetooth.advertisement import ( BluetoothLEAdvertisementReceivedEventArgs, BluetoothLEAdvertisementType, diff --git a/typings/Foundation/__init__.pyi b/typings/Foundation/__init__.pyi index 669dda24a..7fd4a004f 100644 --- a/typings/Foundation/__init__.pyi +++ b/typings/Foundation/__init__.pyi @@ -2,15 +2,7 @@ import sys from collections.abc import Iterator, Mapping, Sequence from typing import Any, NewType, Optional, TypeVar, overload -if sys.version_info < (3, 12): - from typing_extensions import Buffer -else: - from collections.abc import Buffer - -if sys.version_info < (3, 11): - from typing_extensions import Self -else: - from typing import Self +from typing_extensions import Buffer, Self TNSObject = TypeVar("TNSObject", bound=NSObject)