From 4fb501a0efed9d5e2cb1bd7810aa2e7a3e132ee7 Mon Sep 17 00:00:00 2001 From: Gilles Boccon-Gibod Date: Mon, 29 Dec 2025 19:28:45 -0800 Subject: [PATCH] use ruff for linting and import sorting --- apps/auracast.py | 1 - apps/console.py | 4 +- apps/controller_info.py | 2 +- apps/device_info.py | 1 - apps/gatt_dump.py | 1 - apps/lea_unicast/app.py | 1 - apps/pair.py | 13 +-- apps/pandora_server.py | 2 +- apps/player/player.py | 9 +- apps/scan.py | 4 +- bumble/a2dp.py | 2 +- bumble/at.py | 2 +- bumble/avctp.py | 2 +- bumble/avrcp.py | 2 +- bumble/codecs.py | 30 +++---- bumble/controller.py | 8 +- bumble/crypto/__init__.py | 3 +- bumble/crypto/builtin.py | 5 -- bumble/device.py | 6 +- bumble/drivers/intel.py | 6 +- bumble/drivers/rtk.py | 2 +- bumble/gatt_adapters.py | 1 - bumble/hci.py | 44 +++++----- bumble/hfp.py | 8 +- bumble/host.py | 8 +- bumble/l2cap.py | 2 - bumble/pandora/device.py | 4 +- bumble/pandora/host.py | 24 ++++-- bumble/pandora/l2cap.py | 5 +- bumble/pandora/security.py | 10 ++- bumble/profiles/aics.py | 1 - bumble/profiles/gatt_service.py | 1 - bumble/profiles/le_audio.py | 2 +- bumble/profiles/vocs.py | 2 - bumble/sdp.py | 2 +- bumble/smp.py | 2 - bumble/transport/pyusb.py | 8 +- bumble/vendor/android/hci.py | 3 + examples/hid_report_parser.py | 39 ++++----- examples/mobly/bench/one_device_bench_test.py | 1 - examples/run_a2dp_info.py | 3 +- examples/run_ancs_client.py | 5 +- examples/run_cig_setup.py | 1 - examples/run_classic_discovery.py | 4 +- examples/run_classic_l2cap.py | 2 - examples/run_controller_with_scanner.py | 86 ------------------- examples/run_csis_servers.py | 2 +- examples/run_extended_advertiser_2.py | 2 +- examples/run_hid_device.py | 4 +- examples/run_hid_host.py | 9 +- examples/run_mcp_client.py | 2 +- examples/run_rfcomm_client.py | 3 +- examples/run_vcp_renderer.py | 2 +- pyproject.toml | 10 ++- tasks.py | 21 +++-- tests/bap_test.py | 4 +- tests/device_test.py | 19 ++-- tests/test_utils.py | 2 +- tools/rtk_fw_download.py | 2 +- 59 files changed, 182 insertions(+), 274 deletions(-) delete mode 100644 examples/run_controller_with_scanner.py diff --git a/apps/auracast.py b/apps/auracast.py index 10e4011..8ab89d5 100644 --- a/apps/auracast.py +++ b/apps/auracast.py @@ -646,7 +646,6 @@ async def run_assist( async def run_pair(transport: str, address: str) -> None: async with create_device(transport) as device: - # Connect to the server print(f'=== Connecting to {address}...') async with device.connect_as_gatt(address) as peer: diff --git a/apps/console.py b/apps/console.py index e57bc54..f99694c 100644 --- a/apps/console.py +++ b/apps/console.py @@ -1096,9 +1096,7 @@ class DeviceListener(Device.Listener, Connection.Listener): if self.app.connected_peer.connection.is_encrypted else 'not encrypted' ) - self.app.append_to_output( - 'connection encryption change: ' f'{encryption_state}' - ) + self.app.append_to_output(f'connection encryption change: {encryption_state}') def on_connection_data_length_change(self): self.app.append_to_output( diff --git a/apps/controller_info.py b/apps/controller_info.py index bbb2db5..d27c100 100644 --- a/apps/controller_info.py +++ b/apps/controller_info.py @@ -275,7 +275,7 @@ async def async_main( ( f'min={min(latencies):.2f}, ' f'max={max(latencies):.2f}, ' - f'average={sum(latencies)/len(latencies):.2f},' + f'average={sum(latencies) / len(latencies):.2f},' ), [f'{latency:.4}' for latency in latencies], '\n', diff --git a/apps/device_info.py b/apps/device_info.py index c7f33a5..9ad5088 100644 --- a/apps/device_info.py +++ b/apps/device_info.py @@ -215,7 +215,6 @@ async def show_device_info(peer, done: Optional[asyncio.Future]) -> None: # ----------------------------------------------------------------------------- async def async_main(device_config, encrypt, transport, address_or_name): async with await open_transport(transport) as (hci_source, hci_sink): - # Create a device if device_config: device = Device.from_config_file_with_hci( diff --git a/apps/gatt_dump.py b/apps/gatt_dump.py index b93b441..4658f6b 100644 --- a/apps/gatt_dump.py +++ b/apps/gatt_dump.py @@ -61,7 +61,6 @@ async def dump_gatt_db(peer, done): # ----------------------------------------------------------------------------- async def async_main(device_config, encrypt, transport, address_or_name): async with await open_transport(transport) as (hci_source, hci_sink): - # Create a device if device_config: device = Device.from_config_file_with_hci( diff --git a/apps/lea_unicast/app.py b/apps/lea_unicast/app.py index 25567b3..c96bb10 100644 --- a/apps/lea_unicast/app.py +++ b/apps/lea_unicast/app.py @@ -268,7 +268,6 @@ class UiServer: # ----------------------------------------------------------------------------- class Speaker: - def __init__( self, device_config_path: str | None, diff --git a/apps/pair.py b/apps/pair.py index efb899f..b9f3ae9 100644 --- a/apps/pair.py +++ b/apps/pair.py @@ -527,7 +527,9 @@ async def pair( if advertise_appearance: advertise_appearance = advertise_appearance.upper() try: - advertise_appearance_int = int(advertise_appearance) + appearance = data_types.Appearance.from_int( + int(advertise_appearance) + ) except ValueError: category, subcategory = advertise_appearance.split('/') try: @@ -545,12 +547,11 @@ async def pair( except ValueError: print(color(f'Invalid subcategory {subcategory}', 'red')) return - advertise_appearance_int = int( - Appearance(category_enum, subcategory_enum) + appearance = data_types.Appearance( + category_enum, subcategory_enum ) - advertising_data_types.append( - data_types.Appearance(category_enum, subcategory_enum) - ) + + advertising_data_types.append(appearance) device.advertising_data = bytes(AdvertisingData(advertising_data_types)) await device.start_advertising( auto_restart=True, diff --git a/apps/pandora_server.py b/apps/pandora_server.py index d62d3a8..02d523e 100644 --- a/apps/pandora_server.py +++ b/apps/pandora_server.py @@ -19,7 +19,7 @@ ROOTCANAL_PORT_CUTTLEFISH = 7300 @click.option( '--transport', help='HCI transport', - default=f'tcp-client:127.0.0.1:', + default='tcp-client:127.0.0.1:', ) @click.option( '--config', diff --git a/apps/player/player.py b/apps/player/player.py index 19e4b56..1edfb22 100644 --- a/apps/player/player.py +++ b/apps/player/player.py @@ -47,14 +47,13 @@ from bumble.avdtp import ( AVDTP_DELAY_REPORTING_SERVICE_CATEGORY, MediaCodecCapabilities, MediaPacketPump, + find_avdtp_service_with_connection, ) from bumble.avdtp import Protocol as AvdtpProtocol -from bumble.avdtp import find_avdtp_service_with_connection from bumble.avrcp import Protocol as AvrcpProtocol from bumble.colors import color -from bumble.core import AdvertisingData +from bumble.core import AdvertisingData, DeviceClass, PhysicalTransport from bumble.core import ConnectionError as BumbleConnectionError -from bumble.core import DeviceClass, PhysicalTransport from bumble.device import Connection, Device, DeviceConfiguration from bumble.hci import HCI_CONNECTION_ALREADY_EXISTS_ERROR, Address, HCI_Constant from bumble.pairing import PairingConfig @@ -381,11 +380,11 @@ class Player: print(f">>> {color(address.to_string(False), 'yellow')}:") print(f" Device Class (raw): {class_of_device:06X}") major_class_name = DeviceClass.major_device_class_name(major_device_class) - print(" Device Major Class: " f"{major_class_name}") + print(f" Device Major Class: {major_class_name}") minor_class_name = DeviceClass.minor_device_class_name( major_device_class, minor_device_class ) - print(" Device Minor Class: " f"{minor_class_name}") + print(f" Device Minor Class: {minor_class_name}") print( " Device Services: " f"{', '.join(DeviceClass.service_class_labels(service_classes))}" diff --git a/apps/scan.py b/apps/scan.py index 164d64c..7de628e 100644 --- a/apps/scan.py +++ b/apps/scan.py @@ -217,9 +217,7 @@ async def scan( @click.option( '--irk', metavar=':
', - help=( - 'Use this IRK for resolving private addresses ' '(may be used more than once)' - ), + help=('Use this IRK for resolving private addresses (may be used more than once)'), multiple=True, ) @click.option( diff --git a/bumble/a2dp.py b/bumble/a2dp.py index 7b1c864..1ec2f7e 100644 --- a/bumble/a2dp.py +++ b/bumble/a2dp.py @@ -490,7 +490,7 @@ class VendorSpecificMediaCodecInformation(MediaCodecInformation): 'VendorSpecificMediaCodecInformation(', f' vendor_id: {self.vendor_id:08X} ({name_or_number(COMPANY_IDENTIFIERS, self.vendor_id & 0xFFFF)})', f' codec_id: {self.codec_id:04X}', - f' value: {self.value.hex()}' ')', + f' value: {self.value.hex()})', ] ) diff --git a/bumble/at.py b/bumble/at.py index ad44a4c..30d1931 100644 --- a/bumble/at.py +++ b/bumble/at.py @@ -36,7 +36,7 @@ def tokenize_parameters(buffer: bytes) -> list[bytes]: if in_quotes: token.extend(char) - if char == b'\"': + if char == b'"': in_quotes = False tokens.append(token[1:-1]) token = bytearray() diff --git a/bumble/avctp.py b/bumble/avctp.py index 690e7a7..252d4c7 100644 --- a/bumble/avctp.py +++ b/bumble/avctp.py @@ -258,7 +258,7 @@ class Protocol: def send_ipid(self, transaction_label: int, pid: int) -> None: logger.debug( - ">>> AVCTP ipid: " f"transaction_label={transaction_label}, " f"pid={pid}" + f">>> AVCTP ipid: transaction_label={transaction_label}, pid={pid}" ) self.send_message(transaction_label, False, True, pid, b'') diff --git a/bumble/avrcp.py b/bumble/avrcp.py index 52bd7b2..de38182 100644 --- a/bumble/avrcp.py +++ b/bumble/avrcp.py @@ -2011,7 +2011,7 @@ class Protocol(utils.EventEmitter): f"{command} is not a valid AV/C Command Frame" ) logger.debug( - f"<<< AVCTP Command, transaction_label={transaction_label}: " f"{command}" + f"<<< AVCTP Command, transaction_label={transaction_label}: {command}" ) # Only addressing the unit, or the PANEL subunit with subunit ID 0 is supported diff --git a/bumble/codecs.py b/bumble/codecs.py index d5a2aed..ec7dce6 100644 --- a/bumble/codecs.py +++ b/bumble/codecs.py @@ -163,23 +163,23 @@ class AacAudioRtpPacket: cls, reader: BitReader, channel_configuration: int, audio_object_type: int ) -> Self: # GASpecificConfig - ISO/EIC 14496-3 Table 4.1 - frame_length_flag = reader.read(1) + reader.read(1) # frame_length_flag depends_on_core_coder = reader.read(1) if depends_on_core_coder: - core_coder_delay = reader.read(14) + reader.read(14) # core_coder_delay extension_flag = reader.read(1) if not channel_configuration: raise core.InvalidPacketError('program_config_element not supported') if audio_object_type in (6, 20): - layer_nr = reader.read(3) + reader.read(3) # layer_nr if extension_flag: if audio_object_type == 22: - num_of_sub_frame = reader.read(5) - layer_length = reader.read(11) + reader.read(5) # num_of_sub_frame + reader.read(11) # layer_length if audio_object_type in (17, 19, 20, 23): - aac_section_data_resilience_flags = reader.read(1) - aac_scale_factor_data_resilience_flags = reader.read(1) - aac_spectral_data_resilience_flags = reader.read(1) + reader.read(1) # aac_section_data_resilience_flags + reader.read(1) # aac_scale_factor_data_resilience_flags + reader.read(1) # aac_spectral_data_resilience_flags extension_flag_3 = reader.read(1) if extension_flag_3 == 1: raise core.InvalidPacketError('extensionFlag3 == 1 not supported') @@ -364,10 +364,10 @@ class AacAudioRtpPacket: if audio_mux_version_a != 0: raise core.InvalidPacketError('audioMuxVersionA != 0 not supported') if audio_mux_version == 1: - tara_buffer_fullness = AacAudioRtpPacket.read_latm_value(reader) - stream_cnt = 0 - all_streams_same_time_framing = reader.read(1) - num_sub_frames = reader.read(6) + AacAudioRtpPacket.read_latm_value(reader) # tara_buffer_fullness + # stream_cnt = 0 + reader.read(1) # all_streams_same_time_framing + reader.read(6) # num_sub_frames num_program = reader.read(4) if num_program != 0: raise core.InvalidPacketError('num_program != 0 not supported') @@ -391,9 +391,9 @@ class AacAudioRtpPacket: reader.skip(asc_len) frame_length_type = reader.read(3) if frame_length_type == 0: - latm_buffer_fullness = reader.read(8) + reader.read(8) # latm_buffer_fullness elif frame_length_type == 1: - frame_length = reader.read(9) + reader.read(9) # frame_length else: raise core.InvalidPacketError( f'frame_length_type {frame_length_type} not supported' @@ -413,7 +413,7 @@ class AacAudioRtpPacket: break crc_check_present = reader.read(1) if crc_check_present: - crc_checksum = reader.read(8) + reader.read(8) # crc_checksum return cls(other_data_present, other_data_len_bits, audio_specific_config) diff --git a/bumble/controller.py b/bumble/controller.py index a3c99bd..79b67d4 100644 --- a/bumble/controller.py +++ b/bumble/controller.py @@ -25,10 +25,8 @@ import random import struct from typing import TYPE_CHECKING, Any, Optional, Union, cast -from bumble import hci -from bumble import link +from bumble import hci, link, ll, lmp from bumble import link as bumble_link -from bumble import ll, lmp from bumble.colors import color from bumble.core import PhysicalTransport @@ -170,10 +168,6 @@ class AdvertisingSet: def send_extended_advertising_data(self) -> None: if self.controller.link: - properties = ( - self.parameters.advertising_event_properties if self.parameters else 0 - ) - address = self.address assert address diff --git a/bumble/crypto/__init__.py b/bumble/crypto/__init__.py index 5d8aa4a..a759977 100644 --- a/bumble/crypto/__init__.py +++ b/bumble/crypto/__init__.py @@ -25,10 +25,11 @@ try: from bumble.crypto.cryptography import EccKey, aes_cmac, e except ImportError: logging.getLogger(__name__).debug( - "Unable to import cryptography, use built-in primitives." + "Unable to import cryptography, using built-in primitives." ) from bumble.crypto.builtin import EccKey, aes_cmac, e # type: ignore[assignment] +_EccKey = EccKey # For the linter only # ----------------------------------------------------------------------------- # Logging diff --git a/bumble/crypto/builtin.py b/bumble/crypto/builtin.py index 91b6c9c..55c1580 100644 --- a/bumble/crypto/builtin.py +++ b/bumble/crypto/builtin.py @@ -85,7 +85,6 @@ class _AES: # fmt: on def __init__(self, key: bytes) -> None: - if len(key) not in (16, 24, 32): raise core.InvalidArgumentError(f'Invalid key size {len(key)}') @@ -112,7 +111,6 @@ class _AES: r_con_pointer = 0 t = kc while t < round_key_count: - tt = tk[kc - 1] tk[0] ^= ( (self._S[(tt >> 16) & 0xFF] << 24) @@ -269,7 +267,6 @@ class _ECB: class _CBC: - def __init__(self, key: bytes, iv: bytes = bytes(16)) -> None: if len(iv) != 16: raise core.InvalidArgumentError( @@ -302,7 +299,6 @@ class _CBC: class _CMAC: - def __init__( self, key: bytes, @@ -414,7 +410,6 @@ class _CMAC: self._last_pt = _xor(second_last, data_block[-bs:]) def digest(self) -> bytes: - bs = self._block_size if self._mac_tag is not None and not self._update_after_digest: diff --git a/bumble/device.py b/bumble/device.py index 73ac399..7314045 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -5248,8 +5248,7 @@ class Device(utils.CompositeEventEmitter): if status != hci.HCI_SUCCESS: logger.debug( - f'advertising set {advertising_handle} ' - f'terminated with status {status}' + f'advertising set {advertising_handle} terminated with status {status}' ) return @@ -5628,7 +5627,8 @@ class Device(utils.CompositeEventEmitter): self.host.send_command_sync( hci.HCI_Accept_Connection_Request_Command( - bd_addr=bd_addr, role=0x01 # Remain the peripheral + bd_addr=bd_addr, + role=0x01, # Remain the peripheral ) ) diff --git a/bumble/drivers/intel.py b/bumble/drivers/intel.py index 4fe6bf8..90f52a4 100644 --- a/bumble/drivers/intel.py +++ b/bumble/drivers/intel.py @@ -380,7 +380,7 @@ class Driver(common.Driver): if (vendor_id, product_id) not in INTEL_USB_PRODUCTS: logger.debug( - f"USB device ({vendor_id:04X}, {product_id:04X}) " "not in known list" + f"USB device ({vendor_id:04X}, {product_id:04X}) not in known list" ) return False @@ -483,9 +483,7 @@ class Driver(common.Driver): raise DriverError("insufficient device info, missing CNVI or CNVR") firmware_base_name = ( - "ibt-" - f"{device_info[ValueType.CNVI]:04X}-" - f"{device_info[ValueType.CNVR]:04X}" + f"ibt-{device_info[ValueType.CNVI]:04X}-{device_info[ValueType.CNVR]:04X}" ) logger.debug(f"FW base name: {firmware_base_name}") diff --git a/bumble/drivers/rtk.py b/bumble/drivers/rtk.py index 4ac659d..cc55ba5 100644 --- a/bumble/drivers/rtk.py +++ b/bumble/drivers/rtk.py @@ -484,7 +484,7 @@ class Driver(common.Driver): if (vendor_id, product_id) not in RTK_USB_PRODUCTS: logger.debug( - f"USB device ({vendor_id:04X}, {product_id:04X}) " "not in known list" + f"USB device ({vendor_id:04X}, {product_id:04X}) not in known list" ) return False diff --git a/bumble/gatt_adapters.py b/bumble/gatt_adapters.py index ba3e38b..cdaaef3 100644 --- a/bumble/gatt_adapters.py +++ b/bumble/gatt_adapters.py @@ -361,5 +361,4 @@ class EnumCharacteristicProxyAdapter(CharacteristicProxyAdapter[_T3]): def decode_value(self, value: bytes) -> _T3: int_value = int.from_bytes(value, self.byteorder) - a = self.cls(int_value) return self.cls(int_value) diff --git a/bumble/hci.py b/bumble/hci.py index 0bb7586..02c746d 100644 --- a/bumble/hci.py +++ b/bumble/hci.py @@ -119,7 +119,6 @@ def phy_list_to_bits(phys: Optional[Iterable[Phy]]) -> int: class SpecableEnum(utils.OpenIntEnum): - @classmethod def type_spec(cls, size: int, byteorder: Literal['little', 'big'] = 'little'): return { @@ -147,7 +146,6 @@ class SpecableEnum(utils.OpenIntEnum): class SpecableFlag(enum.IntFlag): - @classmethod def type_spec(cls, size: int, byteorder: Literal['little', 'big'] = 'little'): return { @@ -1786,20 +1784,20 @@ class HCI_Object: @classmethod def dict_and_offset_from_bytes( - cls, data: bytes, offset: int, fields: Fields + cls, data: bytes, offset: int, object_fields: Fields ) -> tuple[int, collections.OrderedDict[str, Any]]: result = collections.OrderedDict[str, Any]() - for field in fields: - if isinstance(field, list): + for object_field in object_fields: + if isinstance(object_field, list): # This is an array field, starting with a 1-byte item count. item_count = data[offset] offset += 1 # Set fields first, because item_count might be 0. - for sub_field_name, _ in field: + for sub_field_name, _ in object_field: result[sub_field_name] = [] for _ in range(item_count): - for sub_field_name, sub_field_type in field: + for sub_field_name, sub_field_type in object_field: value, size = HCI_Object.parse_field( data, offset, sub_field_type ) @@ -1807,7 +1805,7 @@ class HCI_Object: offset += size continue - field_name, field_type = field + field_name, field_type = object_field assert isinstance(field_name, str) field_value, field_size = HCI_Object.parse_field( data, offset, cast(FieldSpec, field_type) @@ -1890,26 +1888,26 @@ class HCI_Object: return field_bytes @staticmethod - def dict_to_bytes(hci_object, fields): + def dict_to_bytes(hci_object, object_fields): result = bytearray() - for field in fields: - if isinstance(field, list): + for object_field in object_fields: + if isinstance(object_field, list): # The field is an array. The serialized form starts with a 1-byte # item count. We use the length of the first array field as the # array count, since all array fields have the same number of items. - item_count = len(hci_object[field[0][0]]) + item_count = len(hci_object[object_field[0][0]]) result += bytes([item_count]) + b''.join( b''.join( HCI_Object.serialize_field( hci_object[sub_field_name][i], sub_field_type ) - for sub_field_name, sub_field_type in field + for sub_field_name, sub_field_type in object_field ) for i in range(item_count) ) continue - (field_name, field_type) = field + (field_name, field_type) = object_field result += HCI_Object.serialize_field(hci_object[field_name], field_type) return bytes(result) @@ -1967,15 +1965,15 @@ class HCI_Object: ) @staticmethod - def format_fields(hci_object, fields, indentation='', value_mappers=None): - if not fields: + def format_fields(hci_object, object_fields, indentation='', value_mappers=None): + if not object_fields: return '' # Build array of formatted key:value pairs field_strings = [] - for field in fields: - if isinstance(field, list): - for sub_field in field: + for object_field in object_fields: + if isinstance(object_field, list): + for sub_field in object_field: sub_field_name, sub_field_type = sub_field item_count = len(hci_object[sub_field_name]) for i in range(item_count): @@ -1993,7 +1991,7 @@ class HCI_Object: ) continue - field_name, field_type = field + field_name, field_type = object_field field_value = hci_object[field_name] field_strings.append( ( @@ -2016,16 +2014,16 @@ class HCI_Object: @classmethod def fields_from_dataclass(cls, obj: Any) -> list[Any]: stack: list[list[Any]] = [[]] - for field in dataclasses.fields(obj): + for object_field in dataclasses.fields(obj): # Fields without metadata should be ignored. if not isinstance( - (metadata := field.metadata.get("bumble.hci")), FieldMetadata + (metadata := object_field.metadata.get("bumble.hci")), FieldMetadata ): continue if metadata.list_begin: stack.append([]) if metadata.spec: - stack[-1].append((field.name, metadata.spec)) + stack[-1].append((object_field.name, metadata.spec)) if metadata.list_end: top = stack.pop() stack[-1].append(top) diff --git a/bumble/hfp.py b/bumble/hfp.py index b80737f..c5eecf8 100644 --- a/bumble/hfp.py +++ b/bumble/hfp.py @@ -597,7 +597,7 @@ class AgIndicatorState: supported_values_text = ( f'({",".join(str(v) for v in self.supported_values)})' ) - return f'(\"{self.indicator.value}\",{supported_values_text})' + return f'("{self.indicator.value}",{supported_values_text})' @classmethod def call(cls: type[Self]) -> Self: @@ -1351,7 +1351,7 @@ class AgProtocol(utils.EventEmitter): logger.warning(f'AG indicator {indicator} is disabled') indicator_state.current_status = value - self.send_response(f'+CIEV: {index+1},{value}') + self.send_response(f'+CIEV: {index + 1},{value}') async def negotiate_codec(self, codec: AudioCodec) -> None: """Starts codec negotiation.""" @@ -1417,7 +1417,7 @@ class AgProtocol(utils.EventEmitter): operation_code = operation_code[:1] + b'x' try: operation = CallHoldOperation(operation_code.decode()) - except: + except Exception: logger.error(f'Invalid operation: {operation_code.decode()}') self.send_cme_error(CmeError.OPERATION_NOT_SUPPORTED) return @@ -1589,7 +1589,7 @@ class AgProtocol(utils.EventEmitter): def _on_clcc(self) -> None: for call in self.calls: - number_text = f',\"{call.number}\"' if call.number is not None else '' + number_text = f',"{call.number}"' if call.number is not None else '' type_text = f',{call.type}' if call.type is not None else '' response = ( f'+CLCC: {call.index}' diff --git a/bumble/host.py b/bumble/host.py index b470eee..06a049c 100644 --- a/bumble/host.py +++ b/bumble/host.py @@ -108,8 +108,7 @@ class DataPacketQueue(utils.EventEmitter): if self._packets: logger.debug( - f'{self._in_flight} packets in flight, ' - f'{len(self._packets)} in queue' + f'{self._in_flight} packets in flight, {len(self._packets)} in queue' ) def flush(self, connection_handle: int) -> None: @@ -1394,8 +1393,7 @@ class Host(utils.EventEmitter): if event.status == hci.HCI_SUCCESS: # Create/update the connection logger.debug( - f'### SCO CONNECTION: [0x{event.connection_handle:04X}] ' - f'{event.bd_addr}' + f'### SCO CONNECTION: [0x{event.connection_handle:04X}] {event.bd_addr}' ) self.sco_links[event.connection_handle] = ScoLink( @@ -1447,7 +1445,7 @@ class Host(utils.EventEmitter): def on_hci_le_data_length_change_event( self, event: hci.HCI_LE_Data_Length_Change_Event ): - if (connection := self.connections.get(event.connection_handle)) is None: + if event.connection_handle not in self.connections: logger.warning('!!! DATA LENGTH CHANGE: unknown handle') return diff --git a/bumble/l2cap.py b/bumble/l2cap.py index 72b39f7..a2a178a 100644 --- a/bumble/l2cap.py +++ b/bumble/l2cap.py @@ -331,7 +331,6 @@ class InformationEnhancedControlField(EnhancedControlField): @dataclasses.dataclass class SupervisoryEnhancedControlField(EnhancedControlField): - supervision_function: int = ControlField.SupervisoryFunction.RR poll: int = 0 req_seq: int = 0 @@ -884,7 +883,6 @@ class Processor: # TODO: Handle retransmission class EnhancedRetransmissionProcessor(Processor): - MAX_SEQ_NUM = 64 @dataclasses.dataclass diff --git a/bumble/pandora/device.py b/bumble/pandora/device.py index 95b784b..584703c 100644 --- a/bumble/pandora/device.py +++ b/bumble/pandora/device.py @@ -74,7 +74,9 @@ class PandoraDevice: # open HCI transport & set device host. self._hci = await transport.open_transport(self._hci_name) - self.device.host = Host(controller_source=self._hci.source, controller_sink=self._hci.sink) # type: ignore[no-untyped-call] + self.device.host = Host( + controller_source=self._hci.source, controller_sink=self._hci.sink + ) # type: ignore[no-untyped-call] # power-on. await self.device.power_on() diff --git a/bumble/pandora/host.py b/bumble/pandora/host.py index b8ac7c3..2f64633 100644 --- a/bumble/pandora/host.py +++ b/bumble/pandora/host.py @@ -21,8 +21,10 @@ from typing import AsyncGenerator, Optional, cast import grpc import grpc.aio -from google.protobuf import any_pb2 # pytype: disable=pyi-error -from google.protobuf import empty_pb2 # pytype: disable=pyi-error +from google.protobuf import ( + any_pb2, # pytype: disable=pyi-error + empty_pb2, # pytype: disable=pyi-error +) from pandora import host_pb2 from pandora.host_grpc_aio import HostServicer from pandora.host_pb2 import ( @@ -302,7 +304,9 @@ class HostService(HostServicer): await disconnection_future self.log.debug("Disconnected") finally: - connection.remove_listener(connection.EVENT_DISCONNECTION, on_disconnection) # type: ignore + connection.remove_listener( + connection.EVENT_DISCONNECTION, on_disconnection + ) # type: ignore return empty_pb2.Empty() @@ -539,7 +543,7 @@ class HostService(HostServicer): await bumble.utils.cancel_on_event( self.device, 'flush', self.device.stop_advertising() ) - except: + except Exception: pass @utils.rpc @@ -609,7 +613,7 @@ class HostService(HostServicer): await bumble.utils.cancel_on_event( self.device, 'flush', self.device.stop_scanning() ) - except: + except Exception: pass @utils.rpc @@ -644,14 +648,18 @@ class HostService(HostServicer): ) finally: - self.device.remove_listener(self.device.EVENT_INQUIRY_COMPLETE, complete_handler) # type: ignore - self.device.remove_listener(self.device.EVENT_INQUIRY_RESULT, result_handler) # type: ignore + self.device.remove_listener( + self.device.EVENT_INQUIRY_COMPLETE, complete_handler + ) # type: ignore + self.device.remove_listener( + self.device.EVENT_INQUIRY_RESULT, result_handler + ) # type: ignore try: self.log.debug('Stop inquiry') await bumble.utils.cancel_on_event( self.device, 'flush', self.device.stop_discovery() ) - except: + except Exception: pass @utils.rpc diff --git a/bumble/pandora/l2cap.py b/bumble/pandora/l2cap.py index 969728e..4b192b8 100644 --- a/bumble/pandora/l2cap.py +++ b/bumble/pandora/l2cap.py @@ -24,9 +24,9 @@ from typing import AsyncGenerator, Optional, Union import grpc from google.protobuf import any_pb2, empty_pb2 # pytype: disable=pyi-error from pandora.l2cap_grpc_aio import L2CAPServicer # pytype: disable=pyi-error -from pandora.l2cap_pb2 import COMMAND_NOT_UNDERSTOOD, INVALID_CID_IN_REQUEST -from pandora.l2cap_pb2 import Channel as PandoraChannel # pytype: disable=pyi-error from pandora.l2cap_pb2 import ( + COMMAND_NOT_UNDERSTOOD, + INVALID_CID_IN_REQUEST, ConnectRequest, ConnectResponse, CreditBasedChannelRequest, @@ -41,6 +41,7 @@ from pandora.l2cap_pb2 import ( WaitDisconnectionRequest, WaitDisconnectionResponse, ) +from pandora.l2cap_pb2 import Channel as PandoraChannel # pytype: disable=pyi-error from bumble.core import InvalidArgumentError, OutOfResourcesError from bumble.device import Device diff --git a/bumble/pandora/security.py b/bumble/pandora/security.py index 1ec25a7..65c72ea 100644 --- a/bumble/pandora/security.py +++ b/bumble/pandora/security.py @@ -21,9 +21,11 @@ from collections.abc import Awaitable from typing import Any, AsyncGenerator, AsyncIterator, Callable, Optional, Union import grpc -from google.protobuf import any_pb2 # pytype: disable=pyi-error -from google.protobuf import empty_pb2 # pytype: disable=pyi-error -from google.protobuf import wrappers_pb2 # pytype: disable=pyi-error +from google.protobuf import ( + any_pb2, # pytype: disable=pyi-error + empty_pb2, # pytype: disable=pyi-error + wrappers_pb2, # pytype: disable=pyi-error +) from pandora.host_pb2 import Connection from pandora.security_grpc_aio import SecurityServicer, SecurityStorageServicer from pandora.security_pb2 import ( @@ -455,7 +457,7 @@ class SecurityService(SecurityServicer): def pair(*_: Any) -> None: if self.need_pairing(connection, level): - pair_task = asyncio.create_task(connection.pair()) + bumble.utils.AsyncRunner.spawn(connection.pair()) listeners: dict[str, Callable[..., Union[None, Awaitable[None]]]] = { 'disconnection': set_failure('connection_died'), diff --git a/bumble/profiles/aics.py b/bumble/profiles/aics.py index 36c8534..984ee10 100644 --- a/bumble/profiles/aics.py +++ b/bumble/profiles/aics.py @@ -199,7 +199,6 @@ class AudioInputControlPoint: gain_settings_properties: GainSettingsProperties async def on_write(self, connection: Connection, value: bytes) -> None: - opcode = AudioInputControlPointOpCode(value[0]) if opcode == AudioInputControlPointOpCode.SET_GAIN_SETTING: diff --git a/bumble/profiles/gatt_service.py b/bumble/profiles/gatt_service.py index fa1e374..ac3efec 100644 --- a/bumble/profiles/gatt_service.py +++ b/bumble/profiles/gatt_service.py @@ -40,7 +40,6 @@ class GenericAttributeProfileService(gatt.TemplateService): database_hash_enabled: bool = True, service_change_enabled: bool = True, ) -> None: - if server_supported_features is not None: self.server_supported_features_characteristic = gatt.Characteristic( uuid=gatt.GATT_SERVER_SUPPORTED_FEATURES_CHARACTERISTIC, diff --git a/bumble/profiles/le_audio.py b/bumble/profiles/le_audio.py index e51bfff..97f419d 100644 --- a/bumble/profiles/le_audio.py +++ b/bumble/profiles/le_audio.py @@ -137,7 +137,7 @@ class Metadata: values.append(str(decoded)) return '\n'.join( - f'{indent}{key}: {" " * (max_key_length-len(key))}{value}' + f'{indent}{key}: {" " * (max_key_length - len(key))}{value}' for key, value in zip(keys, values) ) diff --git a/bumble/profiles/vocs.py b/bumble/profiles/vocs.py index 611103a..13b1250 100644 --- a/bumble/profiles/vocs.py +++ b/bumble/profiles/vocs.py @@ -118,7 +118,6 @@ class VolumeOffsetControlPoint: volume_offset_state: VolumeOffsetState async def on_write(self, connection: Connection, value: bytes) -> None: - opcode = value[0] if opcode != SetVolumeOffsetOpCode.SET_VOLUME_OFFSET: raise ATT_Error(ErrorCode.OPCODE_NOT_SUPPORTED) @@ -177,7 +176,6 @@ class VolumeOffsetControlService(TemplateService): audio_location: Optional[VocsAudioLocation] = None, audio_output_description: Optional[AudioOutputDescription] = None, ) -> None: - self.volume_offset_state = ( VolumeOffsetState() if volume_offset_state is None else volume_offset_state ) diff --git a/bumble/sdp.py b/bumble/sdp.py index 185e902..90e04a7 100644 --- a/bumble/sdp.py +++ b/bumble/sdp.py @@ -528,7 +528,7 @@ class ServiceAttribute: def to_string(self, with_colors=False): if with_colors: return ( - f'Attribute(id={color(self.id_name(self.id),"magenta")},' + f'Attribute(id={color(self.id_name(self.id), "magenta")},' f'value={self.value})' ) diff --git a/bumble/smp.py b/bumble/smp.py index 0005957..e91c061 100644 --- a/bumble/smp.py +++ b/bumble/smp.py @@ -997,7 +997,6 @@ class Session: self.send_command(response) def send_pairing_confirm_command(self) -> None: - if self.pairing_method != PairingMethod.OOB: self.r = crypto.r() logger.debug(f'generated random: {self.r.hex()}') @@ -1833,7 +1832,6 @@ class Session: self.send_public_key_command() def next_steps() -> None: - if self.pairing_method in ( PairingMethod.JUST_WORKS, PairingMethod.NUMERIC_COMPARISON, diff --git a/bumble/transport/pyusb.py b/bumble/transport/pyusb.py index 2c333ec..360d85a 100644 --- a/bumble/transport/pyusb.py +++ b/bumble/transport/pyusb.py @@ -284,7 +284,9 @@ async def open_pyusb_transport(spec: str) -> Transport: device = await _power_cycle(device) # type: ignore except Exception as e: logging.debug(e, stack_info=True) - logging.info(f"Unable to power cycle {hex(device.idVendor)} {hex(device.idProduct)}") # type: ignore + logging.info( + f"Unable to power cycle {hex(device.idVendor)} {hex(device.idProduct)}" + ) # type: ignore # Collect the metadata device_metadata = {'vendor_id': device.idVendor, 'product_id': device.idProduct} @@ -370,7 +372,9 @@ async def _power_cycle(device: UsbDevice) -> UsbDevice: # Device needs to be find again otherwise it will appear as disconnected return usb.core.find(idVendor=device.idVendor, idProduct=device.idProduct) # type: ignore except USBError: - logger.exception(f"Adjustment needed: Please revise the udev rule for device {hex(device.idVendor)}:{hex(device.idProduct)} for proper recognition.") # type: ignore + logger.exception( + f"Adjustment needed: Please revise the udev rule for device {hex(device.idVendor)}:{hex(device.idProduct)} for proper recognition." + ) # type: ignore return device diff --git a/bumble/vendor/android/hci.py b/bumble/vendor/android/hci.py index a023980..fb49fad 100644 --- a/bumble/vendor/android/hci.py +++ b/bumble/vendor/android/hci.py @@ -51,6 +51,7 @@ class HCI_LE_Get_Vendor_Capabilities_Command(hci.HCI_Command): ''' See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#vendor-specific-capabilities ''' + return_parameters_fields = [ ('status', hci.STATUS_SPEC), ('max_advt_instances', 1), @@ -137,6 +138,7 @@ class HCI_Get_Controller_Activity_Energy_Info_Command(hci.HCI_Command): ''' See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_get_controller_activity_energy_info ''' + return_parameters_fields = [ ('status', hci.STATUS_SPEC), ('total_tx_time_ms', 4), @@ -229,6 +231,7 @@ class HCI_Bluetooth_Quality_Report_Event(HCI_Android_Vendor_Event): ''' See https://source.android.com/docs/core/connect/bluetooth/hci_requirements#bluetooth-quality-report-sub-event ''' + quality_report_id: int = field(metadata=hci.metadata(1)) packet_types: int = field(metadata=hci.metadata(1)) connection_handle: int = field(metadata=hci.metadata(2)) diff --git a/examples/hid_report_parser.py b/examples/hid_report_parser.py index 6af331f..434f8ba 100644 --- a/examples/hid_report_parser.py +++ b/examples/hid_report_parser.py @@ -59,28 +59,28 @@ class Keyboard: def print_keyboard_report(self) -> None: print(color('\tKeyboard Input Received', 'green', None, 'bold')) - print(color(f'Keys:', 'white', None, 'bold')) + print(color('Keys:', 'white', None, 'bold')) for i in range(1, 7): print( color(f' Key{i}{" ":>8s}= ', 'cyan', None, 'bold'), self.report[i + 1] ) - print(color(f'\nModifier Keys:', 'white', None, 'bold')) + print(color('\nModifier Keys:', 'white', None, 'bold')) print( - color(f' Left Ctrl : ', 'cyan'), + color(' Left Ctrl : ', 'cyan'), f'{self.report[0][0] == 1!s:<5}', # type: ignore - color(f' Left Shift : ', 'cyan'), + color(' Left Shift : ', 'cyan'), f'{self.report[0][1] == 1!s:<5}', # type: ignore - color(f' Left ALT : ', 'cyan'), + color(' Left ALT : ', 'cyan'), f'{self.report[0][2] == 1!s:<5}', # type: ignore - color(f' Left GUI : ', 'cyan'), + color(' Left GUI : ', 'cyan'), f'{self.report[0][3] == 1!s:<5}\n', # type: ignore - color(f' Right Ctrl : ', 'cyan'), + color(' Right Ctrl : ', 'cyan'), f'{self.report[0][4] == 1!s:<5}', # type: ignore - color(f' Right Shift : ', 'cyan'), + color(' Right Shift : ', 'cyan'), f'{self.report[0][5] == 1!s:<5}', # type: ignore - color(f' Right ALT : ', 'cyan'), + color(' Right ALT : ', 'cyan'), f'{self.report[0][6] == 1!s:<5}', # type: ignore - color(f' Right GUI : ', 'cyan'), + color(' Right GUI : ', 'cyan'), f'{self.report[0][7] == 1!s:<5}', # type: ignore ) @@ -117,23 +117,23 @@ class Mouse: def print_mouse_report(self) -> None: print(color('\tMouse Input Received', 'green', None, 'bold')) print( - color(f' Button 1 (primary/trigger) = ', 'cyan'), + color(' Button 1 (primary/trigger) = ', 'cyan'), self.report[0][0] == 1, # type: ignore - color(f'\n Button 2 (secondary) = ', 'cyan'), + color('\n Button 2 (secondary) = ', 'cyan'), self.report[0][1] == 1, # type: ignore - color(f'\n Button 3 (tertiary) = ', 'cyan'), + color('\n Button 3 (tertiary) = ', 'cyan'), self.report[0][2] == 1, # type: ignore - color(f'\n Button4 = ', 'cyan'), + color('\n Button4 = ', 'cyan'), self.report[0][3] == 1, # type: ignore - color(f'\n Button5 = ', 'cyan'), + color('\n Button5 = ', 'cyan'), self.report[0][4] == 1, # type: ignore - color(f'\n X (X-axis displacement) = ', 'cyan'), + color('\n X (X-axis displacement) = ', 'cyan'), self.report[1], - color(f'\n Y (Y-axis displacement) = ', 'cyan'), + color('\n Y (Y-axis displacement) = ', 'cyan'), self.report[2], - color(f'\n Wheel = ', 'cyan'), + color('\n Wheel = ', 'cyan'), self.report[3], - color(f'\n AC PAN = ', 'cyan'), + color('\n AC PAN = ', 'cyan'), self.report[4], ) @@ -142,7 +142,6 @@ class Mouse: class ReportParser: @staticmethod def parse_input_report(input_report: bytes) -> None: - report_id = input_report[0] # pylint: disable=unsubscriptable-object report_length = len(input_report) diff --git a/examples/mobly/bench/one_device_bench_test.py b/examples/mobly/bench/one_device_bench_test.py index 82a818d..9482251 100644 --- a/examples/mobly/bench/one_device_bench_test.py +++ b/examples/mobly/bench/one_device_bench_test.py @@ -3,7 +3,6 @@ from mobly.controllers import android_device class OneDeviceBenchTest(base_test.BaseTestClass): - def setup_class(self): self.ads = self.register_controller(android_device) self.dut = self.ads[0] diff --git a/examples/run_a2dp_info.py b/examples/run_a2dp_info.py index eda6ff1..b33235e 100644 --- a/examples/run_a2dp_info.py +++ b/examples/run_a2dp_info.py @@ -33,9 +33,10 @@ from bumble.sdp import ( SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + DataElement, + ServiceAttribute, ) from bumble.sdp import Client as SDP_Client -from bumble.sdp import DataElement, ServiceAttribute from bumble.transport import open_transport diff --git a/examples/run_ancs_client.py b/examples/run_ancs_client.py index 467c0a3..8f6c2f7 100644 --- a/examples/run_ancs_client.py +++ b/examples/run_ancs_client.py @@ -30,6 +30,7 @@ from bumble.profiles.ancs import ( NotificationAttributeId, ) from bumble.transport import open_transport +from bumble.utils import AsyncRunner # ----------------------------------------------------------------------------- _cached_app_names: dict[str, str] = {} @@ -192,9 +193,7 @@ async def main() -> None: ancs_client.on("notification", on_ancs_notification) # Process all notifications in a task. - notification_processing_task = asyncio.create_task( - process_notifications(ancs_client) - ) + AsyncRunner.spawn(process_notifications(ancs_client)) # Accept a TCP connection to handle commands. tcp_server = await asyncio.start_server( diff --git a/examples/run_cig_setup.py b/examples/run_cig_setup.py index a01934f..8578fb1 100644 --- a/examples/run_cig_setup.py +++ b/examples/run_cig_setup.py @@ -51,7 +51,6 @@ async def main() -> None: devices[1].cis_enabled = True await asyncio.gather(*[device.power_on() for device in devices]) - advertising_set = await devices[0].create_advertising_set() connection = await devices[1].connect( devices[0].random_address, own_address_type=OwnAddressType.RANDOM diff --git a/examples/run_classic_discovery.py b/examples/run_classic_discovery.py index 1b4a636..77d036e 100644 --- a/examples/run_classic_discovery.py +++ b/examples/run_classic_discovery.py @@ -38,11 +38,11 @@ class DiscoveryListener(Device.Listener): print(f'>>> {color(address, "yellow")}:') print(f' Device Class (raw): {class_of_device:06X}') major_class_name = DeviceClass.major_device_class_name(major_device_class) - print(' Device Major Class: ' f'{major_class_name}') + print(f' Device Major Class: {major_class_name}') minor_class_name = DeviceClass.minor_device_class_name( major_device_class, minor_device_class ) - print(' Device Minor Class: ' f'{minor_class_name}') + print(f' Device Minor Class: {minor_class_name}') print( ' Device Services: ' f'{", ".join(DeviceClass.service_class_labels(service_classes))}' diff --git a/examples/run_classic_l2cap.py b/examples/run_classic_l2cap.py index b780065..9d8475a 100644 --- a/examples/run_classic_l2cap.py +++ b/examples/run_classic_l2cap.py @@ -32,7 +32,6 @@ from bumble.transport import open_transport async def main( config_file: str, transport: str, mode: int, peer_address: str, psm: int ) -> None: - print('<<< connecting to HCI...') async with await open_transport(transport) as hci_transport: print('<<< connected') @@ -59,7 +58,6 @@ async def main( active_channel: l2cap.ClassicChannel | None = None def on_connection(channel: l2cap.ClassicChannel): - def on_sdu(sdu: bytes): print(f'<<< {sdu.decode()}') diff --git a/examples/run_controller_with_scanner.py b/examples/run_controller_with_scanner.py deleted file mode 100644 index ff6cd3e..0000000 --- a/examples/run_controller_with_scanner.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright 2021-2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# ----------------------------------------------------------------------------- -# Imports -# ----------------------------------------------------------------------------- -import asyncio -import sys - -import bumble.logging -from bumble.colors import color -from bumble.controller import Controller -from bumble.device import Device -from bumble.hci import Address -from bumble.link import LocalLink -from bumble.transport import open_transport - - -# ----------------------------------------------------------------------------- -class ScannerListener(Device.Listener): - def on_advertisement(self, advertisement): - address_type_string = ('P', 'R', 'PI', 'RI')[advertisement.address.address_type] - address_color = 'yellow' if advertisement.is_connectable else 'red' - if address_type_string.startswith('P'): - type_color = 'green' - else: - type_color = 'cyan' - - print( - f'>>> {color(advertisement.address, address_color)} ' - f'[{color(address_type_string, type_color)}]: ' - f'RSSI={advertisement.rssi}, {advertisement.data}' - ) - - -# ----------------------------------------------------------------------------- -async def main() -> None: - if len(sys.argv) != 2: - print('Usage: run_controller.py ') - print('example: run_controller_with_scanner.py serial:/dev/pts/14,1000000') - return - - print('>>> connecting to HCI...') - async with await open_transport(sys.argv[1]) as hci_transport: - print('>>> connected') - - # Create a local link - link = LocalLink() - - # Create a first controller using the packet source/sink as its host interface - controller1 = Controller( - 'C1', - host_source=hci_transport.source, - host_sink=hci_transport.sink, - link=link, - public_address='E0:E1:E2:E3:E4:E5', - ) - - # Create a second controller using the same link - controller2 = Controller('C2', link=link) - - # Create a device with a scanner listener - device = Device.with_hci( - 'Bumble', Address('F0:F1:F2:F3:F4:F5'), controller2, controller2 - ) - device.listener = ScannerListener() - await device.power_on() - await device.start_scanning() - - await hci_transport.source.wait_for_termination() - - -# ----------------------------------------------------------------------------- -bumble.logging.setup_basic_logging('DEBUG') -asyncio.run(main()) diff --git a/examples/run_csis_servers.py b/examples/run_csis_servers.py index ea609fb..a461f39 100644 --- a/examples/run_csis_servers.py +++ b/examples/run_csis_servers.py @@ -36,7 +36,7 @@ async def main() -> None: 'Usage: run_csis_servers.py ' ' ' ) - print('example: run_csis_servers.py device1.json ' 'hci-socket:0 hci-socket:1') + print('example: run_csis_servers.py device1.json hci-socket:0 hci-socket:1') return print('<<< connecting to HCI...') diff --git a/examples/run_extended_advertiser_2.py b/examples/run_extended_advertiser_2.py index 21f3de5..defb968 100644 --- a/examples/run_extended_advertiser_2.py +++ b/examples/run_extended_advertiser_2.py @@ -90,7 +90,7 @@ async def main() -> None: ), scan_response_data=bytes(scan_response_data3), ) - print("Selected TX power 3:", set2.selected_tx_power) + print("Selected TX power 3:", set3.selected_tx_power) await hci_transport.source.terminated diff --git a/examples/run_hid_device.py b/examples/run_hid_device.py index 887c493..0c7fb36 100644 --- a/examples/run_hid_device.py +++ b/examples/run_hid_device.py @@ -30,9 +30,8 @@ from bumble.core import ( PhysicalTransport, ) from bumble.device import Device -from bumble.hid import HID_CONTROL_PSM, HID_INTERRUPT_PSM +from bumble.hid import HID_CONTROL_PSM, HID_INTERRUPT_PSM, Message from bumble.hid import Device as HID_Device -from bumble.hid import Message from bumble.sdp import ( SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, @@ -423,7 +422,6 @@ deviceData = DeviceData() # ----------------------------------------------------------------------------- async def keyboard_device(hid_device: HID_Device): - # Start a Websocket server to receive events from a web page async def serve(websocket: websockets.asyncio.server.ServerConnection): global deviceData diff --git a/examples/run_hid_host.py b/examples/run_hid_host.py index 2691c92..f6153f1 100644 --- a/examples/run_hid_host.py +++ b/examples/run_hid_host.py @@ -67,7 +67,6 @@ SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID = 0x0210 async def get_hid_device_sdp_record(connection): - # Connect to the SDP Server sdp_client = SDP_Client(connection) await sdp_client.connect() @@ -84,7 +83,7 @@ async def get_hid_device_sdp_record(connection): if len(service_record_handles) < 1: await sdp_client.disconnect() raise Exception( - color(f'BT HID Device service not found on peer device!!!!', 'red') + color('BT HID Device service not found on peer device!!!!', 'red') ) # For BT_HUMAN_INTERFACE_DEVICE_SERVICE service, get all its attributes @@ -92,8 +91,8 @@ async def get_hid_device_sdp_record(connection): attributes = await sdp_client.get_attributes( service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE] ) - print(color(f'SERVICE {service_record_handle:04X} attributes:', 'yellow')) - print(color(f'SDP attributes for HID device', 'magenta')) + print(color('SERVICE {service_record_handle:04X} attributes:', 'yellow')) + print(color('SDP attributes for HID device', 'magenta')) for attribute in attributes: if attribute.id == SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID: print( @@ -287,7 +286,7 @@ async def main() -> None: def on_hid_interrupt_data_cb(pdu: bytes): report_type = pdu[0] & 0x0F if len(pdu) == 1: - print(color(f'Warning: No report received', 'yellow')) + print(color('Warning: No report received', 'yellow')) return report_length = len(pdu[1:]) report_id = pdu[1] diff --git a/examples/run_mcp_client.py b/examples/run_mcp_client.py index 7161130..5f688a9 100644 --- a/examples/run_mcp_client.py +++ b/examples/run_mcp_client.py @@ -55,7 +55,7 @@ from bumble.transport import open_transport # ----------------------------------------------------------------------------- async def main() -> None: if len(sys.argv) < 3: - print('Usage: run_mcp_client.py ' '') + print('Usage: run_mcp_client.py ') return print('<<< connecting to HCI...') diff --git a/examples/run_rfcomm_client.py b/examples/run_rfcomm_client.py index 153d868..d801797 100644 --- a/examples/run_rfcomm_client.py +++ b/examples/run_rfcomm_client.py @@ -28,9 +28,10 @@ from bumble.sdp import ( SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + DataElement, + ServiceAttribute, ) from bumble.sdp import Client as SDP_Client -from bumble.sdp import DataElement, ServiceAttribute from bumble.transport import open_transport diff --git a/examples/run_vcp_renderer.py b/examples/run_vcp_renderer.py index 4234348..263d480 100644 --- a/examples/run_vcp_renderer.py +++ b/examples/run_vcp_renderer.py @@ -57,7 +57,7 @@ def dumps_volume_state(volume_setting: int, muted: int, change_counter: int) -> # ----------------------------------------------------------------------------- async def main() -> None: if len(sys.argv) < 3: - print('Usage: run_vcp_renderer.py ' '') + print('Usage: run_vcp_renderer.py ') return print('<<< connecting to HCI...') diff --git a/pyproject.toml b/pyproject.toml index 0ef717f..cbc15c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,12 +48,12 @@ development = [ "bt-test-interfaces >= 0.0.6", "grpcio-tools >= 1.62.1", "invoke >= 1.7.3", - "isort ~= 5.13.2", "mobly >= 1.12.2", "mypy == 1.12.0", "nox >= 2022", "pylint == 3.3.1", "pyyaml >= 6.0", + "ruff == 0.14.10", "types-appdirs >= 1.4.3", "types-invoke >= 1.7.3", "types-protobuf >= 4.21.0", @@ -208,3 +208,11 @@ ignore_missing_imports = true [tool.isort] profile = "black" skip = ["_version.py", "grpc_protobuf"] + +[tool.ruff] +exclude = [ + "bumble/transport/grpc_protobuf" +] + +[tool.ruff.format] +quote-style = "preserve" \ No newline at end of file diff --git a/tasks.py b/tasks.py index 0b49859..7d928cf 100644 --- a/tasks.py +++ b/tasks.py @@ -127,6 +127,9 @@ def lint(ctx, disable='C,R', errors_only=False): print(f">>> Running the linter{qualifier}...") try: + print("+++ Checking with ruff...") + ctx.run("ruff check") + print("+++ Checking with pylint...") ctx.run(f"pylint {' '.join(options)} bumble apps examples tasks.py") print("The linter is happy. ✅ 😊 🐝") except UnexpectedExit as exc: @@ -138,20 +141,24 @@ def lint(ctx, disable='C,R', errors_only=False): @task def format_code(ctx, check=False, diff=False): - options = [] - if check: - options.append("--check") - if diff: - options.append("--diff") - print(">>> Sorting imports...") + options = [] try: - ctx.run(f"isort {' '.join(options)} .") + if diff: + options.append("--diff") + else: + options.append("--fix") + ctx.run(f"ruff check --select I {' '.join(options)} .") except UnexpectedExit as exc: print("Please run 'invoke project.format' or 'isort .' to format the code. ❌") raise Exit(code=1) from exc print(">>> Running the formatter...") + options = [] + if check: + options.append("--check") + if diff: + options.append("--diff") try: ctx.run(f"black -S {' '.join(options)} .") except UnexpectedExit as exc: diff --git a/tests/bap_test.py b/tests/bap_test.py index 03bff72..d2945f7 100644 --- a/tests/bap_test.py +++ b/tests/bap_test.py @@ -301,9 +301,7 @@ async def test_pacs(): await devices.setup_connection() peer = device.Peer(devices.connections[1]) - pacs_client = await peer.discover_service_and_create_proxy( - PublishedAudioCapabilitiesServiceProxy - ) + await peer.discover_service_and_create_proxy(PublishedAudioCapabilitiesServiceProxy) # ----------------------------------------------------------------------------- diff --git a/tests/device_test.py b/tests/device_test.py index 768ba7d..8f2df8a 100644 --- a/tests/device_test.py +++ b/tests/device_test.py @@ -23,9 +23,10 @@ from unittest import mock import pytest -from bumble import device, gatt, hci, utils +from bumble import gatt, hci, utils from bumble.core import PhysicalTransport from bumble.device import ( + Advertisement, AdvertisingEventProperties, AdvertisingParameters, CigParameters, @@ -146,7 +147,7 @@ async def test_device_connect_parallel(): ) ) - assert (yield) == None + assert (yield) is None def d1_flow(): packet = HCI_Packet.from_bytes((yield)) @@ -180,7 +181,7 @@ async def test_device_connect_parallel(): ) ) - assert (yield) == None + assert (yield) is None def d2_flow(): packet = HCI_Packet.from_bytes((yield)) @@ -214,7 +215,7 @@ async def test_device_connect_parallel(): ) ) - assert (yield) == None + assert (yield) is None d0.host.set_packet_sink(Sink(d0_flow())) d1.host.set_packet_sink(Sink(d1_flow())) @@ -239,10 +240,10 @@ async def test_device_connect_parallel(): ] ) - assert type(c01) == Connection - assert type(c02) == Connection - assert type(a10) == Connection - assert type(a20) == Connection + assert isinstance(c01, Connection) + assert isinstance(c02, Connection) + assert isinstance(a10, Connection) + assert isinstance(a20, Connection) assert c01.handle == a10.handle and c01.handle == 0x100 assert c02.handle == a20.handle and c02.handle == 0x101 @@ -317,7 +318,7 @@ async def test_advertising_and_scanning(): await dev.power_on() # Start scanning - advertisements = asyncio.Queue[device.Advertisement]() + advertisements = asyncio.Queue[Advertisement]() devices[1].on(devices[1].EVENT_ADVERTISEMENT, advertisements.put_nowait) await devices[1].start_scanning() diff --git a/tests/test_utils.py b/tests/test_utils.py index b50abc2..86a4324 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -40,7 +40,7 @@ class Devices: self.link = LocalLink() addresses = [":".join([f"F{i}"] * 6) for i in range(num_devices)] self.controllers = [ - Controller(f'C{i+i}', link=self.link, public_address=addresses[i]) + Controller(f'C{i + i}', link=self.link, public_address=addresses[i]) for i in range(num_devices) ] self.devices = [ diff --git a/tools/rtk_fw_download.py b/tools/rtk_fw_download.py index 4d99498..fbf03dd 100644 --- a/tools/rtk_fw_download.py +++ b/tools/rtk_fw_download.py @@ -21,11 +21,11 @@ import urllib.error import urllib.request import click +from bumble.tools import rtk_util import bumble.logging from bumble.colors import color from bumble.drivers import rtk -from bumble.tools import rtk_util # ----------------------------------------------------------------------------- # Logging