diff --git a/bumble/att.py b/bumble/att.py index 213b03b..b97aef1 100644 --- a/bumble/att.py +++ b/bumble/att.py @@ -22,9 +22,11 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import struct from colors import color from pyee import EventEmitter +from typing import Dict, Type from bumble.core import UUID, name_or_number from bumble.hci import HCI_Object, key_with_value @@ -197,7 +199,7 @@ class ATT_PDU: See Bluetooth spec @ Vol 3, Part F - 3.3 ATTRIBUTE PDU ''' - pdu_classes = {} + pdu_classes: Dict[int, Type[ATT_PDU]] = {} op_code = 0 name = None diff --git a/bumble/avdtp.py b/bumble/avdtp.py index 8cad9c3..62e7b49 100644 --- a/bumble/avdtp.py +++ b/bumble/avdtp.py @@ -15,12 +15,14 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import asyncio import struct import time import logging from colors import color from pyee import EventEmitter +from typing import Dict, Type from .core import ( BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE, @@ -627,7 +629,8 @@ class Message: # pylint:disable=attribute-defined-outside-init RESPONSE_REJECT: 'RESPONSE_REJECT', } - subclasses = {} # Subclasses, by signal identifier and message type + # Subclasses, by signal identifier and message type + subclasses: Dict[int, Dict[int, Type[Message]]] = {} @staticmethod def message_type_name(message_type): diff --git a/bumble/core.py b/bumble/core.py index b00170a..489d2f1 100644 --- a/bumble/core.py +++ b/bumble/core.py @@ -15,6 +15,7 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import struct from .company_ids import COMPANY_IDENTIFIERS @@ -145,7 +146,7 @@ class UUID: ''' BASE_UUID = bytes.fromhex('00001000800000805F9B34FB') - UUIDS = [] # Registry of all instances created + UUIDS: list[UUID] = [] # Registry of all instances created def __init__(self, uuid_str_or_int, name=None): if isinstance(uuid_str_or_int, int): diff --git a/bumble/device.py b/bumble/device.py index 1ffc5c6..2277771 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -15,6 +15,7 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations from enum import IntEnum import functools import json @@ -22,6 +23,8 @@ import asyncio import logging from contextlib import asynccontextmanager, AsyncExitStack from dataclasses import dataclass +from typing import ClassVar + from colors import color from .att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU @@ -494,6 +497,7 @@ class Peer: # ----------------------------------------------------------------------------- @dataclass class ConnectionParametersPreferences: + default: ClassVar[ConnectionParametersPreferences] connection_interval_min: int = DEVICE_DEFAULT_CONNECTION_INTERVAL_MIN connection_interval_max: int = DEVICE_DEFAULT_CONNECTION_INTERVAL_MAX max_latency: int = DEVICE_DEFAULT_CONNECTION_MAX_LATENCY @@ -833,7 +837,7 @@ def host_event_handler(function): # List of host event handlers for the Device class. # (we define this list outside the class, because referencing a class in method # decorators is not straightforward) -device_host_event_handlers = [] +device_host_event_handlers: list[str] = [] # ----------------------------------------------------------------------------- @@ -2355,7 +2359,7 @@ class Device(CompositeEventEmitter): if transport == BT_BR_EDR_TRANSPORT: # Create a new connection - connection: Connection = self.pending_connections.pop(peer_address) + connection = self.pending_connections.pop(peer_address) connection.complete( connection_handle, peer_resolvable_address, role, connection_parameters ) diff --git a/bumble/gatt.py b/bumble/gatt.py index 7e2eeaa..de4c9a7 100644 --- a/bumble/gatt.py +++ b/bumble/gatt.py @@ -217,7 +217,7 @@ class Service(Attribute): uuid.to_pdu_bytes(), ) self.uuid = uuid - self.included_services = [] + # self.included_services = [] self.characteristics = characteristics[:] self.primary = primary diff --git a/bumble/hci.py b/bumble/hci.py index b721700..91a1012 100644 --- a/bumble/hci.py +++ b/bumble/hci.py @@ -15,11 +15,13 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import struct import collections import logging import functools from colors import color +from typing import Dict, Type from .core import ( BT_BR_EDR_TRANSPORT, @@ -1690,6 +1692,11 @@ class Address: RANDOM_IDENTITY_ADDRESS: 'RANDOM_IDENTITY_ADDRESS', } + # Type declarations + NIL: Address + ANY: Address + ANY_RANDOM: Address + # pylint: disable-next=unnecessary-lambda ADDRESS_TYPE_SPEC = {'size': 1, 'mapper': lambda x: Address.address_type_name(x)} @@ -1874,7 +1881,7 @@ class HCI_Command(HCI_Packet): ''' hci_packet_type = HCI_COMMAND_PACKET - command_classes = {} + command_classes: Dict[int, Type[HCI_Command]] = {} @staticmethod def command(fields=(), return_parameters_fields=()): @@ -4020,8 +4027,8 @@ class HCI_Event(HCI_Packet): ''' hci_packet_type = HCI_EVENT_PACKET - event_classes = {} - meta_event_classes = {} + event_classes: Dict[int, Type[HCI_Event]] = {} + meta_event_classes: Dict[int, Type[HCI_LE_Meta_Event]] = {} @staticmethod def event(fields=()): diff --git a/bumble/l2cap.py b/bumble/l2cap.py index a7787fa..ffe48ff 100644 --- a/bumble/l2cap.py +++ b/bumble/l2cap.py @@ -15,6 +15,7 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import asyncio import logging import struct @@ -22,6 +23,7 @@ import struct from collections import deque from colors import color from pyee import EventEmitter +from typing import Dict, Type from .core import BT_CENTRAL_ROLE, InvalidStateError, ProtocolError from .hci import ( @@ -184,7 +186,7 @@ class L2CAP_Control_Frame: See Bluetooth spec @ Vol 3, Part A - 4 SIGNALING PACKET FORMATS ''' - classes = {} + classes: Dict[int, Type[L2CAP_Control_Frame]] = {} code = 0 name = None diff --git a/bumble/profiles/device_information_service.py b/bumble/profiles/device_information_service.py index 5a8b336..2ad9cae 100644 --- a/bumble/profiles/device_information_service.py +++ b/bumble/profiles/device_information_service.py @@ -17,7 +17,7 @@ # Imports # ----------------------------------------------------------------------------- import struct -from typing import Tuple +from typing import Optional, Tuple from ..gatt_client import ProfileServiceProxy from ..gatt import ( @@ -52,14 +52,14 @@ class DeviceInformationService(TemplateService): def __init__( self, - manufacturer_name: str = None, - model_number: str = None, - serial_number: str = None, - hardware_revision: str = None, - firmware_revision: str = None, - software_revision: str = None, - system_id: Tuple[int, int] = None, # (OUI, Manufacturer ID) - ieee_regulatory_certification_data_list: bytes = None + manufacturer_name: Optional[str] = None, + model_number: Optional[str] = None, + serial_number: Optional[str] = None, + hardware_revision: Optional[str] = None, + firmware_revision: Optional[str] = None, + software_revision: Optional[str] = None, + system_id: Optional[Tuple[int, int]] = None, # (OUI, Manufacturer ID) + ieee_regulatory_certification_data_list: Optional[bytes] = None # TODO: pnp_id ): characteristics = [ diff --git a/bumble/profiles/py.typed b/bumble/profiles/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/bumble/py.typed b/bumble/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/bumble/sdp.py b/bumble/sdp.py index 4bfcbdb..0c773ef 100644 --- a/bumble/sdp.py +++ b/bumble/sdp.py @@ -15,10 +15,12 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import logging import struct from colors import color import colors +from typing import Dict, Type from . import core from .core import InvalidStateError @@ -520,7 +522,7 @@ class SDP_PDU: See Bluetooth spec @ Vol 3, Part B - 4.2 PROTOCOL DATA UNIT FORMAT ''' - sdp_pdu_classes = {} + sdp_pdu_classes: Dict[int, Type[SDP_PDU]] = {} name = None pdu_id = 0 diff --git a/bumble/smp.py b/bumble/smp.py index 158b65a..d3feb6b 100644 --- a/bumble/smp.py +++ b/bumble/smp.py @@ -22,9 +22,12 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import logging import asyncio import secrets +from typing import Dict, Type + from pyee import EventEmitter from colors import color @@ -184,7 +187,7 @@ class SMP_Command: See Bluetooth spec @ Vol 3, Part H - 3 SECURITY MANAGER PROTOCOL ''' - smp_classes = {} + smp_classes: Dict[int, Type[SMP_Command]] = {} code = 0 name = '' diff --git a/bumble/transport/android_emulator.py b/bumble/transport/android_emulator.py index f9aabcc..6d9e4d1 100644 --- a/bumble/transport/android_emulator.py +++ b/bumble/transport/android_emulator.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Google LLC +# Copyright 2021-2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,9 +20,11 @@ import grpc from .common import PumpedTransport, PumpedPacketSource, PumpedPacketSink from .emulated_bluetooth_pb2_grpc import EmulatedBluetoothServiceStub -from .emulated_bluetooth_packets_pb2 import HCIPacket from .emulated_bluetooth_vhci_pb2_grpc import VhciForwardingServiceStub +# pylint: disable-next=no-name-in-module +from .emulated_bluetooth_packets_pb2 import HCIPacket + # ----------------------------------------------------------------------------- # Logging diff --git a/bumble/transport/emulated_bluetooth_packets_pb2.py b/bumble/transport/emulated_bluetooth_packets_pb2.py index 9fafd61..2802ff1 100644 --- a/bumble/transport/emulated_bluetooth_packets_pb2.py +++ b/bumble/transport/emulated_bluetooth_packets_pb2.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Google LLC +# Copyright 2021-2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,10 +16,9 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: emulated_bluetooth_packets.proto """Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) @@ -31,20 +30,10 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( b'\n emulated_bluetooth_packets.proto\x12\x1b\x61ndroid.emulation.bluetooth\"\xfb\x01\n\tHCIPacket\x12?\n\x04type\x18\x01 \x01(\x0e\x32\x31.android.emulation.bluetooth.HCIPacket.PacketType\x12\x0e\n\x06packet\x18\x02 \x01(\x0c\"\x9c\x01\n\nPacketType\x12\x1b\n\x17PACKET_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17PACKET_TYPE_HCI_COMMAND\x10\x01\x12\x13\n\x0fPACKET_TYPE_ACL\x10\x02\x12\x13\n\x0fPACKET_TYPE_SCO\x10\x03\x12\x15\n\x11PACKET_TYPE_EVENT\x10\x04\x12\x13\n\x0fPACKET_TYPE_ISO\x10\x05\x42J\n\x1f\x63om.android.emulation.bluetoothP\x01\xf8\x01\x01\xa2\x02\x03\x41\x45\x42\xaa\x02\x1b\x41ndroid.Emulation.Bluetoothb\x06proto3' ) - -_HCIPACKET = DESCRIPTOR.message_types_by_name['HCIPacket'] -_HCIPACKET_PACKETTYPE = _HCIPACKET.enum_types_by_name['PacketType'] -HCIPacket = _reflection.GeneratedProtocolMessageType( - 'HCIPacket', - (_message.Message,), - { - 'DESCRIPTOR': _HCIPACKET, - '__module__': 'emulated_bluetooth_packets_pb2' - # @@protoc_insertion_point(class_scope:android.emulation.bluetooth.HCIPacket) - }, +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, 'emulated_bluetooth_packets_pb2', globals() ) -_sym_db.RegisterMessage(HCIPacket) - if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None diff --git a/bumble/transport/emulated_bluetooth_packets_pb2.pyi b/bumble/transport/emulated_bluetooth_packets_pb2.pyi new file mode 100644 index 0000000..a823e78 --- /dev/null +++ b/bumble/transport/emulated_bluetooth_packets_pb2.pyi @@ -0,0 +1,41 @@ +# Copyright 2021-2023 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. + +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HCIPacket(_message.Message): + __slots__ = ["packet", "type"] + + class PacketType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + PACKET_FIELD_NUMBER: _ClassVar[int] + PACKET_TYPE_ACL: HCIPacket.PacketType + PACKET_TYPE_EVENT: HCIPacket.PacketType + PACKET_TYPE_HCI_COMMAND: HCIPacket.PacketType + PACKET_TYPE_ISO: HCIPacket.PacketType + PACKET_TYPE_SCO: HCIPacket.PacketType + PACKET_TYPE_UNSPECIFIED: HCIPacket.PacketType + TYPE_FIELD_NUMBER: _ClassVar[int] + packet: bytes + type: HCIPacket.PacketType + def __init__( + self, + type: _Optional[_Union[HCIPacket.PacketType, str]] = ..., + packet: _Optional[bytes] = ..., + ) -> None: ... diff --git a/bumble/transport/emulated_bluetooth_packets_pb2_grpc.py b/bumble/transport/emulated_bluetooth_packets_pb2_grpc.py new file mode 100644 index 0000000..3450039 --- /dev/null +++ b/bumble/transport/emulated_bluetooth_packets_pb2_grpc.py @@ -0,0 +1,17 @@ +# Copyright 2021-2023 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. + +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc diff --git a/bumble/transport/emulated_bluetooth_pb2.py b/bumble/transport/emulated_bluetooth_pb2.py index 2689a99..42015f6 100644 --- a/bumble/transport/emulated_bluetooth_pb2.py +++ b/bumble/transport/emulated_bluetooth_pb2.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Google LLC +# Copyright 2021-2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,10 +16,9 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: emulated_bluetooth.proto """Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) @@ -34,20 +33,8 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( b'\n\x18\x65mulated_bluetooth.proto\x12\x1b\x61ndroid.emulation.bluetooth\x1a emulated_bluetooth_packets.proto\"\x19\n\x07RawData\x12\x0e\n\x06packet\x18\x01 \x01(\x0c\x32\xcb\x02\n\x18\x45mulatedBluetoothService\x12\x64\n\x12registerClassicPhy\x12$.android.emulation.bluetooth.RawData\x1a$.android.emulation.bluetooth.RawData(\x01\x30\x01\x12`\n\x0eregisterBlePhy\x12$.android.emulation.bluetooth.RawData\x1a$.android.emulation.bluetooth.RawData(\x01\x30\x01\x12g\n\x11registerHCIDevice\x12&.android.emulation.bluetooth.HCIPacket\x1a&.android.emulation.bluetooth.HCIPacket(\x01\x30\x01\x42\"\n\x1e\x63om.android.emulator.bluetoothP\x01\x62\x06proto3' ) - -_RAWDATA = DESCRIPTOR.message_types_by_name['RawData'] -RawData = _reflection.GeneratedProtocolMessageType( - 'RawData', - (_message.Message,), - { - 'DESCRIPTOR': _RAWDATA, - '__module__': 'emulated_bluetooth_pb2' - # @@protoc_insertion_point(class_scope:android.emulation.bluetooth.RawData) - }, -) -_sym_db.RegisterMessage(RawData) - -_EMULATEDBLUETOOTHSERVICE = DESCRIPTOR.services_by_name['EmulatedBluetoothService'] +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'emulated_bluetooth_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None diff --git a/bumble/transport/emulated_bluetooth_pb2.pyi b/bumble/transport/emulated_bluetooth_pb2.pyi new file mode 100644 index 0000000..fb87a52 --- /dev/null +++ b/bumble/transport/emulated_bluetooth_pb2.pyi @@ -0,0 +1,26 @@ +# Copyright 2021-2023 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. + +import emulated_bluetooth_packets_pb2 as _emulated_bluetooth_packets_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class RawData(_message.Message): + __slots__ = ["packet"] + PACKET_FIELD_NUMBER: _ClassVar[int] + packet: bytes + def __init__(self, packet: _Optional[bytes] = ...) -> None: ... diff --git a/bumble/transport/emulated_bluetooth_pb2_grpc.py b/bumble/transport/emulated_bluetooth_pb2_grpc.py index c7ea6d0..8559c9e 100644 --- a/bumble/transport/emulated_bluetooth_pb2_grpc.py +++ b/bumble/transport/emulated_bluetooth_pb2_grpc.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Google LLC +# Copyright 2021-2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/bumble/transport/emulated_bluetooth_vhci_pb2.py b/bumble/transport/emulated_bluetooth_vhci_pb2.py index d046577..e8009ad 100644 --- a/bumble/transport/emulated_bluetooth_vhci_pb2.py +++ b/bumble/transport/emulated_bluetooth_vhci_pb2.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Google LLC +# Copyright 2021-2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,10 +16,9 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: emulated_bluetooth_vhci.proto """Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) @@ -27,15 +26,17 @@ from google.protobuf import symbol_database as _symbol_database _sym_db = _symbol_database.Default() -import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2 +from . import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( b'\n\x1d\x65mulated_bluetooth_vhci.proto\x12\x1b\x61ndroid.emulation.bluetooth\x1a emulated_bluetooth_packets.proto2y\n\x15VhciForwardingService\x12`\n\nattachVhci\x12&.android.emulation.bluetooth.HCIPacket\x1a&.android.emulation.bluetooth.HCIPacket(\x01\x30\x01\x42J\n\x1f\x63om.android.emulation.bluetoothP\x01\xf8\x01\x01\xa2\x02\x03\x41\x45\x42\xaa\x02\x1b\x41ndroid.Emulation.Bluetoothb\x06proto3' ) - -_VHCIFORWARDINGSERVICE = DESCRIPTOR.services_by_name['VhciForwardingService'] +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, 'emulated_bluetooth_vhci_pb2', globals() +) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None diff --git a/bumble/transport/emulated_bluetooth_vhci_pb2.pyi b/bumble/transport/emulated_bluetooth_vhci_pb2.pyi new file mode 100644 index 0000000..0877ad0 --- /dev/null +++ b/bumble/transport/emulated_bluetooth_vhci_pb2.pyi @@ -0,0 +1,19 @@ +# Copyright 2021-2023 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. + +import emulated_bluetooth_packets_pb2 as _emulated_bluetooth_packets_pb2 +from google.protobuf import descriptor as _descriptor +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor diff --git a/bumble/transport/emulated_bluetooth_vhci_pb2_grpc.py b/bumble/transport/emulated_bluetooth_vhci_pb2_grpc.py index 9495217..41f0feb 100644 --- a/bumble/transport/emulated_bluetooth_vhci_pb2_grpc.py +++ b/bumble/transport/emulated_bluetooth_vhci_pb2_grpc.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Google LLC +# Copyright 2021-2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/bumble/transport/py.typed b/bumble/transport/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/bumble/transport/pyusb.py b/bumble/transport/pyusb.py index f804d66..6b3152a 100644 --- a/bumble/transport/pyusb.py +++ b/bumble/transport/pyusb.py @@ -204,7 +204,7 @@ async def open_pyusb_transport(spec): await self.sink.stop() usb.util.release_interface(self.device, 0) - usb_find = usb.core.find + usb_find = usb.core.find try: import libusb_package except ImportError: @@ -215,9 +215,7 @@ async def open_pyusb_transport(spec): # Find the device according to the spec moniker if ':' in spec: vendor_id, product_id = spec.split(':') - device = usb_find( - idVendor=int(vendor_id, 16), idProduct=int(product_id, 16) - ) + device = usb_find(idVendor=int(vendor_id, 16), idProduct=int(product_id, 16)) else: device_index = int(spec) devices = list( diff --git a/bumble/transport/usb.py b/bumble/transport/usb.py index 928e5b5..f0efa32 100644 --- a/bumble/transport/usb.py +++ b/bumble/transport/usb.py @@ -51,8 +51,12 @@ def load_libusb(): else: if libusb_path := libusb_package.get_library_path(): logger.debug(f'loading libusb library at {libusb_path}') - dll_loader = ctypes.WinDLL if platform.system() == 'Windows' else ctypes.CDLL - libusb_dll = dll_loader(str(libusb_path), use_errno=True, use_last_error=True) + dll_loader = ( + ctypes.WinDLL if platform.system() == 'Windows' else ctypes.CDLL + ) + libusb_dll = dll_loader( + str(libusb_path), use_errno=True, use_last_error=True + ) usb1.loadLibrary(libusb_dll) diff --git a/pyproject.toml b/pyproject.toml index 0124914..263d405 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,3 +50,44 @@ signature-mutators="AsyncRunner.run_in_task" [tool.black] skip-string-normalization = true + +[[tool.mypy.overrides]] +module = "bumble.transport.emulated_bluetooth_pb2_grpc" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "bumble.transport.emulated_bluetooth_packets_pb2" +ignore_errors = true + +[[tool.mypy.overrides]] +module = "aioconsole.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "bitstruct.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "colors.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "emulated_bluetooth_packets_pb2.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "grpc.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "serial_asyncio.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "usb.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "usb1.*" +ignore_missing_imports = true + diff --git a/scripts/process_android_emulator_protos.sh b/scripts/process_android_emulator_protos.sh new file mode 100644 index 0000000..e60e4cf --- /dev/null +++ b/scripts/process_android_emulator_protos.sh @@ -0,0 +1,27 @@ +# Invoke this script with an argument pointing to where the Android emulator .proto files are. +# The .proto files should be slightly modified from their original version (as distributed with +# the Android emulator): +# --> Remove unused types/methods from emulated_bluetooth.proto + +PROTOC_OUT=bumble/transport +LICENSE_FILE_INPUT=bumble/transport/android_emulator.py + +proto_files=(emulated_bluetooth.proto emulated_bluetooth_vhci.proto emulated_bluetooth_packets.proto) +for proto_file in "${proto_files[@]}" +do + python -m grpc_tools.protoc -I$1 --proto_path=bumble/transport --python_out=$PROTOC_OUT --pyi_out=$PROTOC_OUT --grpc_python_out=$PROTOC_OUT $1/$proto_file +done + +python_files=(emulated_bluetooth_pb2.py emulated_bluetooth_pb2_grpc.py emulated_bluetooth_packets_pb2.py emulated_bluetooth_packets_pb2_grpc.py emulated_bluetooth_vhci_pb2_grpc.py emulated_bluetooth_vhci_pb2.py) +for python_file in "${python_files[@]}" +do + sed -i '' 's/^import .*_pb2 as/from . &/' $PROTOC_OUT/$python_file +done + +stub_files=(emulated_bluetooth_pb2.pyi emulated_bluetooth_packets_pb2.pyi emulated_bluetooth_vhci_pb2.pyi) +for source_file in "${python_files[@]}" "${stub_files[@]}" +do + head -14 $LICENSE_FILE_INPUT > $PROTOC_OUT/${source_file}.lic + cat $PROTOC_OUT/$source_file >> $PROTOC_OUT/${source_file}.lic + mv $PROTOC_OUT/${source_file}.lic $PROTOC_OUT/$source_file +done \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index c73d15e..f14f3c6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,6 +28,7 @@ packages = bumble, bumble.transport, bumble.profiles, bumble.apps, bumble.apps.l package_dir = bumble = bumble bumble.apps = apps +include-package-data = True install_requires = aioconsole >= 0.4.1 ansicolors >= 1.1 @@ -60,6 +61,9 @@ console_scripts = bumble-usb-probe = bumble.apps.usb_probe:main bumble-link-relay = bumble.apps.link_relay.link_relay:main +[options.package_data] +* = py.typed, *.pyi + [options.extras_require] build = build >= 0.7 @@ -71,8 +75,12 @@ test = development = black >= 22.10 invoke >= 1.7.3 + mypy >= 0.991 nox >= 2022 pylint >= 2.15.8 + types-appdirs >= 1.4.3 + types-invoke >= 1.7.3 + types-protobuf >= 4.21.0 documentation = mkdocs >= 1.4.0 mkdocs-material >= 8.5.6 diff --git a/tasks.py b/tasks.py index 6fd5d6e..3a3a01a 100644 --- a/tasks.py +++ b/tasks.py @@ -22,7 +22,7 @@ Invoke tasks import os from invoke import task, call, Collection -from invoke.exceptions import UnexpectedExit +from invoke.exceptions import Exit, UnexpectedExit # ----------------------------------------------------------------------------- @@ -126,9 +126,9 @@ def lint(ctx, disable='C,R', errors_only=False): try: ctx.run(f"pylint {' '.join(options)} bumble apps examples tasks.py") print("The linter is happy. ✅ 😊 🐝'") - except UnexpectedExit: + except UnexpectedExit as exc: print("Please check your code against the linter messages. ❌") - print(">>> Linter done.") + raise Exit(code=1) from exc # ----------------------------------------------------------------------------- @@ -143,13 +143,31 @@ def format_code(ctx, check=False, diff=False): print(">>> Running the formatter...") try: ctx.run(f"black -S {' '.join(options)} .") - except UnexpectedExit: + except UnexpectedExit as exc: print("Please run 'invoke project.format' or 'black .' to format the code. ❌") - print(">>> formatter done.") + raise Exit(code=1) from exc # ----------------------------------------------------------------------------- -@task(pre=[call(format_code, check=True), call(lint, errors_only=True), test]) +@task +def check_types(ctx): + checklist = ["apps", "bumble", "examples", "tests", "tasks.py"] + try: + ctx.run(f"mypy {' '.join(checklist)}") + except UnexpectedExit as exc: + print("Please check your code against the mypy messages.") + raise Exit(code=1) from exc + + +# ----------------------------------------------------------------------------- +@task( + pre=[ + call(format_code, check=True), + call(lint, errors_only=True), + call(check_types), + test, + ] +) def pre_commit(_ctx): print("All good!") @@ -157,4 +175,5 @@ def pre_commit(_ctx): # ----------------------------------------------------------------------------- project_tasks.add_task(lint) project_tasks.add_task(format_code, name="format") +project_tasks.add_task(check_types, name="check-types") project_tasks.add_task(pre_commit) diff --git a/web/scanner.py b/web/scanner.py index 1f9d0e8..59eda67 100644 --- a/web/scanner.py +++ b/web/scanner.py @@ -16,7 +16,7 @@ # Imports # ----------------------------------------------------------------------------- from bumble.device import Device -from bumble.transport import PacketParser +from bumble.transport.common import PacketParser # -----------------------------------------------------------------------------