From 5431941fe7daf71d59472bc5c1e449a75f72f794 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Tue, 21 Oct 2025 20:00:10 +0800 Subject: [PATCH] Upgrade Python version to 3.10-3.14 --- .github/workflows/code-check.yml | 2 +- .github/workflows/python-build-test.yml | 5 +++-- apps/auracast.py | 20 +++++++------------- bumble/audio/io.py | 6 +++--- bumble/device.py | 6 +----- bumble/utils.py | 6 +----- docs/mkdocs/src/getting_started.md | 2 +- docs/mkdocs/src/index.md | 2 +- docs/mkdocs/src/platforms/index.md | 2 +- environment.yml | 9 --------- pyproject.toml | 2 +- 11 files changed, 20 insertions(+), 42 deletions(-) delete mode 100644 environment.yml diff --git a/.github/workflows/code-check.yml b/.github/workflows/code-check.yml index 42a5c7a..c2d50f6 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.9", "3.10", "3.11", "3.12", "3.13.0"] + python-version: ["3.10", "3.11", "3.12", "3.13.0", "3.14"] fail-fast: false steps: diff --git a/.github/workflows/python-build-test.yml b/.github/workflows/python-build-test.yml index 54a1e0c..f433a06 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.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] fail-fast: false steps: @@ -48,7 +48,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + # Rust runtime doesn't support 3.14 yet. + python-version: ["3.10", "3.11", "3.12", "3.13"] rust-version: [ "1.80.0", "stable" ] fail-fast: false steps: diff --git a/apps/auracast.py b/apps/auracast.py index 1e7413c..70e290b 100644 --- a/apps/auracast.py +++ b/apps/auracast.py @@ -742,10 +742,9 @@ async def run_receive( ] packet_stats = [0, 0] - audio_output = await audio_io.create_audio_output(output) - # This try should be replaced with contextlib.aclosing() when python 3.9 is no - # longer needed. - try: + async with contextlib.AsyncExitStack() as stack: + audio_output = await audio_io.create_audio_output(output) + stack.push_async_callback(audio_output.aclose) await audio_output.open( audio_io.PcmFormat( audio_io.PcmFormat.Endianness.LITTLE, @@ -793,8 +792,6 @@ async def run_receive( terminated = asyncio.Event() big_sync.on(big_sync.Event.TERMINATION, lambda _: terminated.set()) await terminated.wait() - finally: - await audio_output.aclose() async def run_transmit( @@ -891,11 +888,10 @@ async def run_transmit( print('Start Periodic Advertising') await advertising_set.start_periodic() - audio_input = await audio_io.create_audio_input(input, input_format) - pcm_format = await audio_input.open() - # This try should be replaced with contextlib.aclosing() when python 3.9 is no - # longer needed. - try: + async with contextlib.AsyncExitStack() as stack: + audio_input = await audio_io.create_audio_input(input, input_format) + pcm_format = await audio_input.open() + stack.push_async_callback(audio_input.aclose) if pcm_format.channels != 2: print("Only 2 channels PCM configurations are supported") return @@ -967,8 +963,6 @@ async def run_transmit( await iso_queues[1].write(lc3_frame[mid:]) frame_count += 1 - finally: - await audio_input.aclose() def run_async(async_command: Coroutine) -> None: diff --git a/bumble/audio/io.py b/bumble/audio/io.py index 3718930..98bc1a0 100644 --- a/bumble/audio/io.py +++ b/bumble/audio/io.py @@ -19,13 +19,13 @@ from __future__ import annotations import abc import asyncio +import concurrent.futures import dataclasses import enum import logging import pathlib import sys import wave -from concurrent.futures import ThreadPoolExecutor from typing import TYPE_CHECKING, AsyncGenerator, BinaryIO from bumble.colors import color @@ -176,7 +176,7 @@ class ThreadedAudioOutput(AudioOutput): """ def __init__(self) -> None: - self._thread_pool = ThreadPoolExecutor(1) + self._thread_pool = concurrent.futures.ThreadPoolExecutor(1) self._pcm_samples: asyncio.Queue[bytes] = asyncio.Queue() self._write_task = asyncio.create_task(self._write_loop()) @@ -405,7 +405,7 @@ class ThreadedAudioInput(AudioInput): """Base class for AudioInput implementation where reading samples may block.""" def __init__(self) -> None: - self._thread_pool = ThreadPoolExecutor(1) + self._thread_pool = concurrent.futures.ThreadPoolExecutor(1) self._pcm_samples: asyncio.Queue[bytes] = asyncio.Queue() @abc.abstractmethod diff --git a/bumble/device.py b/bumble/device.py index f80be30..4ba9bfd 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -2374,11 +2374,7 @@ class Device(utils.CompositeEventEmitter): hci.Address.ANY: [] } # Futures, by BD address OR [Futures] for hci.Address.ANY - # In Python <= 3.9 + Rust Runtime, asyncio.Lock cannot be properly initiated. - if sys.version_info >= (3, 10): - self._cis_lock = asyncio.Lock() - else: - self._cis_lock = AsyncExitStack() + self._cis_lock = asyncio.Lock() # Own address type cache self.connect_own_address_type = None diff --git a/bumble/utils.py b/bumble/utils.py index 4b9f074..fcb7429 100644 --- a/bumble/utils.py +++ b/bumble/utils.py @@ -241,11 +241,7 @@ def cancel_on_event( return msg = f'abort: {event} event occurred.' if isinstance(future, asyncio.Task): - # python < 3.9 does not support passing a message on `Task.cancel` - if sys.version_info < (3, 9, 0): - future.cancel() - else: - future.cancel(msg) + future.cancel(msg) else: future.set_exception(asyncio.CancelledError(msg)) diff --git a/docs/mkdocs/src/getting_started.md b/docs/mkdocs/src/getting_started.md index 9570849..960e37f 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.9 or above. +You need Python 3.10 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/index.md b/docs/mkdocs/src/index.md index 12a3cf3..a5b6929 100644 --- a/docs/mkdocs/src/index.md +++ b/docs/mkdocs/src/index.md @@ -31,7 +31,7 @@ Some of the configurations that may be useful: See the [use cases page](use_cases/index.md) for more use cases. -The project is implemented in Python (Python >= 3.9 is required). A number of APIs for functionality that is inherently I/O bound is implemented in terms of python coroutines with async IO. This means that all of the concurrent tasks run in the same thread, which makes everything much simpler and more predictable. +The project is implemented in Python (Python >= 3.10 is required). A number of APIs for functionality that is inherently I/O bound is implemented in terms of python coroutines with async IO. This means that all of the concurrent tasks run in the same thread, which makes everything much simpler and more predictable. ![layers](images/bumble_layers.svg) diff --git a/docs/mkdocs/src/platforms/index.md b/docs/mkdocs/src/platforms/index.md index 9bf7c29..d07aaa9 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.9. 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.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). For platform-specific information, see the following pages: diff --git a/environment.yml b/environment.yml deleted file mode 100644 index bb65722..0000000 --- a/environment.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: bumble -channels: - - defaults - - conda-forge -dependencies: - - pip=23 - - python=3.9 - - pip: - - --editable .[development,documentation,test] diff --git a/pyproject.toml b/pyproject.toml index 184ccf0..30e5d52 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.9" +requires-python = ">=3.10" dependencies = [ "aiohttp ~= 3.8; platform_system!='Emscripten'", "appdirs >= 1.4; platform_system!='Emscripten'",