diff --git a/.github/workflows/code-check.yml b/.github/workflows/code-check.yml index 28e9be2b..75634e03 100644 --- a/.github/workflows/code-check.yml +++ b/.github/workflows/code-check.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10", "3.11", "3.12", "3.13.0", "3.14"] + python-version: ["3.11", "3.12", "3.13.0", "3.14", "3.15"] fail-fast: false steps: diff --git a/.github/workflows/python-build-test.yml b/.github/workflows/python-build-test.yml index aaafd4b3..1638ef3d 100644 --- a/.github/workflows/python-build-test.yml +++ b/.github/workflows/python-build-test.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"] fail-fast: false steps: @@ -49,7 +49,7 @@ jobs: strategy: matrix: # Rust runtime doesn't support 3.14 yet. - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12", "3.13"] rust-version: [ "1.80.0", "1.91.0" ] fail-fast: false steps: diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 39376701..37fcd8d5 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -22,7 +22,7 @@ jobs: - name: Set up Python uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: - python-version: '3.10' + python-version: '3.11' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.vscode/settings.json b/.vscode/settings.json index 78d53afd..1b51dae5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -106,5 +106,6 @@ "python-envs.defaultEnvManager": "ms-python.python:venv", "nrf-connect.applications": [ "${workspaceFolder}/extras/zephyr/hci_usb" - ] + ], + "idf.currentSetup": "/Users/gilles/.espressif/v6.1/esp-idf" } diff --git a/apps/auracast.py b/apps/auracast.py index a886e342..4216e662 100644 --- a/apps/auracast.py +++ b/apps/auracast.py @@ -24,7 +24,7 @@ import functools import logging import secrets -import sys +import tomllib from collections.abc import AsyncGenerator, Awaitable, Callable, Iterable, Sequence from typing import ( Any, @@ -32,11 +32,6 @@ import click -if sys.version_info >= (3, 11): - import tomllib -else: - import tomli as tomllib - try: import lc3 # type: ignore # pylint: disable=E0401 except ImportError as e: diff --git a/bumble/a2dp.py b/bumble/a2dp.py index 9bb2438e..b16de874 100644 --- a/bumble/a2dp.py +++ b/bumble/a2dp.py @@ -22,9 +22,7 @@ import logging import struct from collections.abc import AsyncGenerator, Awaitable, Callable -from typing import ClassVar - -from typing_extensions import Self +from typing import ClassVar, Self from bumble import utils from bumble.codecs import AacAudioRtpPacket diff --git a/bumble/codecs.py b/bumble/codecs.py index ec7dce6c..ee424ad8 100644 --- a/bumble/codecs.py +++ b/bumble/codecs.py @@ -18,8 +18,7 @@ from __future__ import annotations from dataclasses import dataclass - -from typing_extensions import Self +from typing import Self from bumble import core diff --git a/bumble/core.py b/bumble/core.py index 9c7301e9..405cadf9 100644 --- a/bumble/core.py +++ b/bumble/core.py @@ -27,12 +27,11 @@ ClassVar, Final, Literal, + Self, cast, overload, ) -from typing_extensions import Self - from bumble import utils from bumble.company_ids import COMPANY_IDENTIFIERS diff --git a/bumble/data_types.py b/bumble/data_types.py index 8d4ec9ce..cc215c3b 100644 --- a/bumble/data_types.py +++ b/bumble/data_types.py @@ -26,9 +26,7 @@ import math import struct from collections.abc import Sequence -from typing import Any, ClassVar - -from typing_extensions import Self +from typing import Any, ClassVar, Self from bumble import company_ids, core, hci diff --git a/bumble/device.py b/bumble/device.py index 796d6ac6..09769108 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -33,13 +33,12 @@ TYPE_CHECKING, Any, ClassVar, + Self, TypeVar, cast, overload, ) -from typing_extensions import Self - from bumble import ( att, core, @@ -2874,7 +2873,7 @@ async def send_command( return await self.host.send_command( command, check_result, self.command_timeout ) - except asyncio.TimeoutError as error: + except TimeoutError as error: logger.warning(f'!!! Command {command.name} timed out') raise CommandTimeoutError() from error @@ -2893,7 +2892,7 @@ async def send_sync_command(self, command: hci.HCI_SyncCommand[_RP]) -> _RP: ''' try: return await self.host.send_sync_command(command, self.command_timeout) - except asyncio.TimeoutError as error: + except TimeoutError as error: logger.warning(f'!!! Command {command.name} timed out') raise CommandTimeoutError() from error @@ -2911,7 +2910,7 @@ async def send_sync_command_raw( ''' try: return await self.host.send_sync_command_raw(command, self.command_timeout) - except asyncio.TimeoutError as error: + except TimeoutError as error: logger.warning(f'!!! Command {command.name} timed out') raise CommandTimeoutError() from error @@ -2933,7 +2932,7 @@ async def send_async_command( return await self.host.send_async_command( command, check_status, self.command_timeout ) - except asyncio.TimeoutError as error: + except TimeoutError as error: logger.warning(f'!!! Command {command.name} timed out') raise CommandTimeoutError() from error @@ -4030,7 +4029,7 @@ def on_connection_failure(error: core.ConnectionError): return await asyncio.wait_for( asyncio.shield(pending_connection), timeout ) - except asyncio.TimeoutError: + except TimeoutError: await self.send_sync_command( hci.HCI_LE_Create_Connection_Cancel_Command() ) @@ -4128,7 +4127,7 @@ def on_connection_failure(error: core.ConnectionError): return await asyncio.wait_for( asyncio.shield(pending_connection), timeout ) - except asyncio.TimeoutError: + except TimeoutError: await self.send_sync_command( hci.HCI_Create_Connection_Cancel_Command(bd_addr=peer_address) ) diff --git a/bumble/gatt_client.py b/bumble/gatt_client.py index dcd009e6..bbf0b53f 100644 --- a/bumble/gatt_client.py +++ b/bumble/gatt_client.py @@ -36,12 +36,11 @@ Any, ClassVar, Generic, + Self, TypeVar, overload, ) -from typing_extensions import Self - from bumble import att, core, l2cap, utils from bumble.colors import color from bumble.core import UUID, InvalidStateError @@ -369,7 +368,7 @@ async def send_request(self, request: att.ATT_PDU): response = await asyncio.wait_for( self.pending_response, GATT_REQUEST_TIMEOUT ) - except asyncio.TimeoutError as error: + except TimeoutError as error: logger.warning(color('!!! GATT Request timeout', 'red')) raise core.TimeoutError(f'GATT timeout for {request.name}') from error finally: diff --git a/bumble/gatt_server.py b/bumble/gatt_server.py index 7ee89f90..f0b21afc 100644 --- a/bumble/gatt_server.py +++ b/bumble/gatt_server.py @@ -506,7 +506,7 @@ async def _indicate_single_bearer( try: self.send_gatt_pdu(bearer, bytes(indication)) await asyncio.wait_for(pending_confirmation, GATT_REQUEST_TIMEOUT) - except asyncio.TimeoutError as error: + except TimeoutError as error: logger.warning(color('!!! GATT Indicate timeout', 'red')) raise TimeoutError(f'GATT timeout for {indication.name}') from error finally: diff --git a/bumble/hci.py b/bumble/hci.py index ebd63381..1b3a4cf1 100644 --- a/bumble/hci.py +++ b/bumble/hci.py @@ -31,13 +31,12 @@ ClassVar, Generic, Literal, + Self, SupportsBytes, TypeVar, cast, ) -from typing_extensions import Self - from bumble import crypto, utils from bumble.colors import color from bumble.core import ( diff --git a/bumble/hfp.py b/bumble/hfp.py index 2897eb4a..5917b327 100644 --- a/bumble/hfp.py +++ b/bumble/hfp.py @@ -26,9 +26,7 @@ import re import traceback from collections.abc import Iterable -from typing import Any, ClassVar, Literal, overload - -from typing_extensions import Self +from typing import Any, ClassVar, Literal, Self, overload from bumble import at, device, rfcomm, sdp, utils from bumble.colors import color diff --git a/bumble/host.py b/bumble/host.py index 8fc005f2..9df38750 100644 --- a/bumble/host.py +++ b/bumble/host.py @@ -690,7 +690,7 @@ async def _send_command( self.pending_response, timeout=response_timeout ) return response - except asyncio.TimeoutError: + except TimeoutError: raise except Exception: logger.exception(color("!!! Exception while sending command:", "red")) diff --git a/bumble/keys.py b/bumble/keys.py index 15d8aaed..0404d235 100644 --- a/bumble/keys.py +++ b/bumble/keys.py @@ -28,9 +28,7 @@ import logging import os import pathlib -from typing import TYPE_CHECKING, Any - -from typing_extensions import Self +from typing import TYPE_CHECKING, Any, Self from bumble import hci from bumble.colors import color diff --git a/bumble/profiles/bap.py b/bumble/profiles/bap.py index 964d8b06..190852fd 100644 --- a/bumble/profiles/bap.py +++ b/bumble/profiles/bap.py @@ -24,8 +24,7 @@ import logging import struct from collections.abc import Sequence - -from typing_extensions import Self +from typing import Self from bumble import core, data_types, gatt, hci, utils from bumble.profiles import le_audio diff --git a/bumble/profiles/heart_rate_service.py b/bumble/profiles/heart_rate_service.py index ffd7b239..5d9d1660 100644 --- a/bumble/profiles/heart_rate_service.py +++ b/bumble/profiles/heart_rate_service.py @@ -22,9 +22,7 @@ import enum import struct from collections.abc import Callable, Sequence -from typing import Any - -from typing_extensions import Self +from typing import Any, Self from bumble import att, core, device, gatt, gatt_adapters, gatt_client, utils diff --git a/bumble/profiles/le_audio.py b/bumble/profiles/le_audio.py index 97f419df..5bcceee5 100644 --- a/bumble/profiles/le_audio.py +++ b/bumble/profiles/le_audio.py @@ -20,9 +20,7 @@ import dataclasses import enum import struct -from typing import Any - -from typing_extensions import Self +from typing import Any, Self from bumble import utils from bumble.profiles import bap diff --git a/bumble/profiles/mcp.py b/bumble/profiles/mcp.py index c6d38486..820ac15e 100644 --- a/bumble/profiles/mcp.py +++ b/bumble/profiles/mcp.py @@ -22,9 +22,7 @@ import dataclasses import enum import struct -from typing import TYPE_CHECKING, ClassVar - -from typing_extensions import Self +from typing import TYPE_CHECKING, ClassVar, Self from bumble import core, device, gatt, gatt_client, utils diff --git a/bumble/profiles/pbp.py b/bumble/profiles/pbp.py index eb069330..b964d8c9 100644 --- a/bumble/profiles/pbp.py +++ b/bumble/profiles/pbp.py @@ -19,8 +19,7 @@ import dataclasses import enum - -from typing_extensions import Self +from typing import Self from bumble import core, data_types, gatt from bumble.profiles import le_audio diff --git a/bumble/rfcomm.py b/bumble/rfcomm.py index 98f40f5e..fe8ad72c 100644 --- a/bumble/rfcomm.py +++ b/bumble/rfcomm.py @@ -23,9 +23,7 @@ import enum import logging from collections.abc import Callable -from typing import TYPE_CHECKING - -from typing_extensions import Self +from typing import TYPE_CHECKING, Self from bumble import core, l2cap, sdp, utils from bumble.colors import color diff --git a/bumble/sdp.py b/bumble/sdp.py index 1c3b99a9..c729a84f 100644 --- a/bumble/sdp.py +++ b/bumble/sdp.py @@ -22,9 +22,7 @@ import struct from collections.abc import Iterable, Sequence from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, ClassVar, TypeAlias, TypeVar - -from typing_extensions import Self +from typing import TYPE_CHECKING, Any, ClassVar, Self, TypeAlias, TypeVar from bumble import core, hci, l2cap, utils from bumble.colors import color diff --git a/bumble/snoop.py b/bumble/snoop.py index a148e80a..59f184cb 100644 --- a/bumble/snoop.py +++ b/bumble/snoop.py @@ -66,7 +66,7 @@ class BtSnooper(Snooper): """ IDENTIFICATION_PATTERN = b'btsnoop\0' - TIMESTAMP_ANCHOR = datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc) + TIMESTAMP_ANCHOR = datetime.datetime(2000, 1, 1, tzinfo=datetime.UTC) TIMESTAMP_DELTA = 0x00E03AB44A676000 ONE_MS = datetime.timedelta(microseconds=1) @@ -87,10 +87,7 @@ def snoop(self, hci_packet: bytes, direction: Snooper.Direction) -> None: # Compute the current timestamp timestamp = ( int( - ( - datetime.datetime.now(tz=datetime.timezone.utc) - - self.TIMESTAMP_ANCHOR - ) + (datetime.datetime.now(tz=datetime.UTC) - self.TIMESTAMP_ANCHOR) / self.ONE_MS ) + self.TIMESTAMP_DELTA @@ -138,7 +135,7 @@ def __init__(self, output: BinaryIO): ) def snoop(self, hci_packet: bytes, direction: Snooper.Direction): - now = datetime.datetime.now(datetime.timezone.utc) + now = datetime.datetime.now(datetime.UTC) sec = int(now.timestamp()) usec = now.microsecond @@ -237,7 +234,7 @@ def create_snooper(spec: str) -> Generator[Snooper, None, None]: # Process the file name string pattern. file_path = io_name.format( now=datetime.datetime.now(), - utcnow=datetime.datetime.now(tz=datetime.timezone.utc), + utcnow=datetime.datetime.now(tz=datetime.UTC), pid=os.getpid(), instance=_SNOOPER_INSTANCE_COUNT, ) @@ -261,7 +258,7 @@ def create_snooper(spec: str) -> Generator[Snooper, None, None]: # Process the file name string pattern. file_path = io_name.format( now=datetime.datetime.now(), - utcnow=datetime.datetime.now(tz=datetime.timezone.utc), + utcnow=datetime.datetime.now(tz=datetime.UTC), pid=os.getpid(), instance=_SNOOPER_INSTANCE_COUNT, ) diff --git a/bumble/testing/test_utils.py b/bumble/testing/test_utils.py index 861dc263..559ed444 100644 --- a/bumble/testing/test_utils.py +++ b/bumble/testing/test_utils.py @@ -17,8 +17,7 @@ # ----------------------------------------------------------------------------- import asyncio import functools - -from typing_extensions import Self +from typing import Self from bumble.controller import Controller from bumble.device import Connection, Device diff --git a/bumble/transport/serial.py b/bumble/transport/serial.py index 7e32cf90..ce0a5e35 100644 --- a/bumble/transport/serial.py +++ b/bumble/transport/serial.py @@ -119,7 +119,7 @@ async def run() -> None: try: await asyncio.wait_for(run(), max_time) - except asyncio.TimeoutError: + except TimeoutError: transport.close() raise TransportInitError( 'the controller did not answer the commands sent to resynchronize' diff --git a/bumble/utils.py b/bumble/utils.py index 9966473d..65f0d067 100644 --- a/bumble/utils.py +++ b/bumble/utils.py @@ -27,13 +27,13 @@ from typing import ( Any, Protocol, + Self, TypeVar, overload, ) import pyee import pyee.asyncio -from typing_extensions import Self from bumble.colors import color diff --git a/docs/mkdocs/src/getting_started.md b/docs/mkdocs/src/getting_started.md index bdd297c6..30b264d0 100644 --- a/docs/mkdocs/src/getting_started.md +++ b/docs/mkdocs/src/getting_started.md @@ -3,7 +3,7 @@ GETTING STARTED WITH BUMBLE # Prerequisites -You need Python 3.10 or above. +You need Python 3.11 or above. Visit the [Python site](https://www.python.org/) for instructions on how to install Python for your platform. Throughout the documentation, when shell commands are shown, it is assumed that you can diff --git a/docs/mkdocs/src/platforms/index.md b/docs/mkdocs/src/platforms/index.md index d07aaa96..f2671cb1 100644 --- a/docs/mkdocs/src/platforms/index.md +++ b/docs/mkdocs/src/platforms/index.md @@ -1,7 +1,7 @@ PLATFORMS ========= -Most of the code included in the project should run on any platform that supports Python >= 3.10. Not all features are supported on all platforms (for example, USB dongle support is only available on platforms where the python USB library is functional). +Most of the code included in the project should run on any platform that supports Python >= 3.11. Not all features are supported on all platforms (for example, USB dongle support is only available on platforms where the python USB library is functional). For platform-specific information, see the following pages: diff --git a/pyproject.toml b/pyproject.toml index 5fcad195..a6d09d0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" license = "Apache-2.0" license-files = ["LICENSE"] authors = [{ name = "Google", email = "bumble-dev@google.com" }] -requires-python = ">=3.10" +requires-python = ">=3.11" dependencies = [ "click >= 8.1.3; platform_system!='Emscripten'", "cryptography >= 44.0.3; platform_system!='Emscripten' and platform_system!='Android'", @@ -43,7 +43,7 @@ android = [ "protobuf >= 3.12.4; platform_system!='Emscripten'", ] auracast = [ - "lc3py >= 1.1.3; python_version>='3.10' and ((platform_system=='Linux' and platform_machine=='x86_64') or (platform_system=='Darwin' and platform_machine=='arm64'))", + "lc3py >= 1.1.3; (platform_system=='Linux' and platform_machine=='x86_64') or (platform_system=='Darwin' and platform_machine=='arm64')", "sounddevice >= 0.5.1", ] all = [ diff --git a/tests/core_test.py b/tests/core_test.py index 367bd159..a069c76b 100644 --- a/tests/core_test.py +++ b/tests/core_test.py @@ -16,7 +16,7 @@ # Imports # ----------------------------------------------------------------------------- -from typing_extensions import assert_type +from typing import assert_type from bumble.core import ( UUID, diff --git a/tests/gatt_test.py b/tests/gatt_test.py index f2a6b694..b172fcc4 100644 --- a/tests/gatt_test.py +++ b/tests/gatt_test.py @@ -22,11 +22,10 @@ import logging import os import struct -from typing import Any +from typing import Any, Self from unittest.mock import ANY, AsyncMock, Mock import pytest -from typing_extensions import Self from bumble import att, gatt_client, gatt_server, l2cap from bumble.att import ( diff --git a/tests/profiles/hap_test.py b/tests/profiles/hap_test.py index 4647bda3..2fe616d9 100644 --- a/tests/profiles/hap_test.py +++ b/tests/profiles/hap_test.py @@ -17,7 +17,6 @@ # ----------------------------------------------------------------------------- import asyncio import logging -import sys import pytest import pytest_asyncio @@ -60,12 +59,8 @@ async def assert_queue_is_empty(queue: asyncio.Queue): assert queue.empty() # Check that nothing is being added during TIMEOUT secondes - if sys.version_info >= (3, 11): - with pytest.raises(TimeoutError): - await asyncio.wait_for(queue.get(), TIMEOUT) - else: - with pytest.raises(asyncio.TimeoutError): - await asyncio.wait_for(queue.get(), TIMEOUT) + with pytest.raises(TimeoutError): + await asyncio.wait_for(queue.get(), TIMEOUT) # -----------------------------------------------------------------------------