Skip to content

Commit 211581b

Browse files
committed
fix: type correctness audit and lint cleanup
- Remove _python_type overrides from 40 characteristic classes (metadata-only, not used for decoding; contradicted BaseCharacteristic[T] generic param) - Remove _manual_role from 10 enum characteristics (classifier rule 12 handles enum→STATUS automatically) - Enforce list[CharacteristicTestData] return type in valid_test_data fixture (base class + 55 subclass test files); remove isinstance guards - Move Appearance, PnP ID, System ID from MEASUREMENT to INFO test group (they have _manual_role=INFO — device metadata, not sensor data) - Refactor classify_role() into 5 tier functions to fix PLR0911 (too many return statements) and remove ERA001 false-positive comment - Fix test_alert_level: pass AlertLevel(int) instead of raw int to build_value() - Fix examples: remove invalid properties kwarg from BaseCharacteristic and UnknownCharacteristic constructors in 3 connection manager examples - Fix test_readme_badges: add socket_enabled fixture and graceful skip when pytest-socket blocks network access - Fix test_magnetic_flux_density_2d/3d: correct python_type assertions
1 parent 5ae7746 commit 211581b

150 files changed

Lines changed: 535 additions & 419 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

examples/connection_managers/bleak_retry.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,16 +192,15 @@ async def get_services(self) -> list[DeviceService]:
192192
char_class = CharacteristicRegistry.get_characteristic_class_by_uuid(char_uuid)
193193

194194
if char_class:
195-
# Create characteristic instance with runtime properties from device
196-
char_instance = char_class(properties=properties)
195+
char_instance = char_class()
197196
characteristics[str(char_uuid)] = char_instance
198197
else:
199198
# Fallback: Create UnknownCharacteristic for unrecognized UUIDs
200199
char_info = CharacteristicInfo(
201200
uuid=char_uuid,
202201
name=char.description or f"Unknown Characteristic ({char_uuid.short_form}...)",
203202
)
204-
char_instance = UnknownCharacteristic(info=char_info, properties=properties)
203+
char_instance = UnknownCharacteristic(info=char_info)
205204
characteristics[str(char_uuid)] = char_instance
206205

207206
# Type ignore needed due to dict invariance with union types

examples/connection_managers/bluepy.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,16 +279,15 @@ def _get_services() -> list[DeviceService]:
279279
char_class = CharacteristicRegistry.get_characteristic_class_by_uuid(char_uuid)
280280

281281
if char_class:
282-
# Create characteristic instance with runtime properties from device
283-
char_instance = char_class(properties=properties)
282+
char_instance = char_class()
284283
characteristics[str(char_uuid)] = char_instance
285284
else:
286285
# Fallback: Create UnknownCharacteristic for unrecognized UUIDs
287286
char_info = CharacteristicInfo(
288287
uuid=char_uuid,
289288
name=f"Unknown Characteristic ({char_uuid.short_form}...)",
290289
)
291-
char_instance = UnknownCharacteristic(info=char_info, properties=properties)
290+
char_instance = UnknownCharacteristic(info=char_info)
292291
characteristics[str(char_uuid)] = char_instance
293292

294293
# Type ignore needed due to dict invariance with union types

examples/connection_managers/simpleble.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,16 +285,15 @@ def _get_services() -> list[DeviceService]:
285285
char_class = CharacteristicRegistry.get_characteristic_class_by_uuid(char_uuid)
286286

287287
if char_class:
288-
# Create characteristic instance with runtime properties from device
289-
char_instance = char_class(properties=properties)
288+
char_instance = char_class()
290289
characteristics[str(char_uuid)] = char_instance
291290
else:
292291
# Fallback: Create UnknownCharacteristic for unrecognized UUIDs
293292
char_info = CharacteristicInfo(
294293
uuid=char_uuid,
295294
name=f"Unknown Characteristic ({char_uuid.short_form}...)",
296295
)
297-
char_instance = UnknownCharacteristic(info=char_info, properties=properties)
296+
char_instance = UnknownCharacteristic(info=char_info)
298297
characteristics[str(char_uuid)] = char_instance
299298

300299
# Type ignore needed due to dict invariance with union types

scripts/ble_device_debugger.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,7 @@ async def debug_ble_device(target_address: str) -> None:
123123
try:
124124
if len(value) == 1:
125125
int_val = value[0]
126-
elif len(value) == 2:
127-
int_val = int.from_bytes(value, byteorder="little")
128-
elif len(value) == 4:
126+
elif len(value) == 2 or len(value) == 4:
129127
int_val = int.from_bytes(value, byteorder="little")
130128
else:
131129
int_val = None

scripts/extract_validation_info.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,18 @@
4747
import importlib
4848
import inspect
4949
import json
50-
import tempfile
51-
from pathlib import Path
52-
from typing import Any
5350

5451
# Add src to path
5552
import sys
53+
import tempfile
54+
from pathlib import Path
55+
from typing import Any
5656

5757
src_path = Path(__file__).parent.parent / "src"
5858
if str(src_path) not in sys.path:
5959
sys.path.insert(0, str(src_path))
6060

6161
from bluetooth_sig.gatt.characteristics.base import BaseCharacteristic
62-
from bluetooth_sig.types.gatt_enums import ValueType
6362

6463

6564
def get_all_characteristic_classes() -> list[type[BaseCharacteristic]]:
@@ -349,7 +348,7 @@ def main() -> None:
349348
# Generate report
350349
try:
351350
generate_validation_report(output_file)
352-
print(f"\n📊 Report summary:")
351+
print("\n📊 Report summary:")
353352
print(f" Output file: {output_file}")
354353

355354
# Show stats
@@ -362,14 +361,14 @@ def main() -> None:
362361
if with_errors:
363362
print(f" With errors: {with_errors}")
364363

365-
print(f"\n📋 Output format:")
366-
print(f" Each value includes a 'source' field indicating origin:")
367-
print(f" - resolved_sig_info: From Bluetooth SIG registry")
368-
print(f" - yaml_spec: From Bluetooth SIG YAML specifications")
369-
print(f" - class_level_or_yaml: From characteristic class or YAML defaults")
370-
print(f" - manual_override: Explicit manual override")
371-
print(f" - runtime_descriptor_check: Discovered at runtime")
372-
print(f" - (see script header for full source legend)")
364+
print("\n📋 Output format:")
365+
print(" Each value includes a 'source' field indicating origin:")
366+
print(" - resolved_sig_info: From Bluetooth SIG registry")
367+
print(" - yaml_spec: From Bluetooth SIG YAML specifications")
368+
print(" - class_level_or_yaml: From characteristic class or YAML defaults")
369+
print(" - manual_override: Explicit manual override")
370+
print(" - runtime_descriptor_check: Discovered at runtime")
371+
print(" - (see script header for full source legend)")
373372

374373
except Exception as e:
375374
print(f"❌ Error: {e}", file=sys.stderr)

scripts/test_real_device.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import logging
1010
import sys
1111
from pathlib import Path
12-
from typing import Optional
1312

1413
# Configure path for imports
1514
script_dir = Path(__file__).parent
@@ -32,7 +31,7 @@
3231
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
3332

3433

35-
async def test_device_connection(mac_address: str) -> Optional[bool]:
34+
async def test_device_connection(mac_address: str) -> bool | None:
3635
"""Test connection to a real device using Bleak."""
3736
print(f"\n🔍 Testing connection to device: {mac_address}")
3837
print("=" * 60)

src/bluetooth_sig/gatt/characteristics/acceleration_detection_status.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
from __future__ import annotations
44

55
from enum import IntEnum
6+
from typing import cast
67

78
from .base import BaseCharacteristic
8-
from .templates import EnumTemplate
9+
from .templates import CodingTemplate, EnumTemplate
910

1011

1112
class AccelerationDetectionStatus(IntEnum):
@@ -15,12 +16,12 @@ class AccelerationDetectionStatus(IntEnum):
1516
CHANGE_DETECTED = 1
1617

1718

18-
class AccelerationDetectionStatusCharacteristic(BaseCharacteristic[int]):
19+
class AccelerationDetectionStatusCharacteristic(BaseCharacteristic[AccelerationDetectionStatus]):
1920
"""Acceleration Detection Status characteristic (0x2C0B).
2021
2122
org.bluetooth.characteristic.acceleration_detection_status
2223
2324
The Acceleration Detection Status characteristic represents the status of detected acceleration.
2425
"""
2526

26-
_template = EnumTemplate.uint8(AccelerationDetectionStatus)
27+
_template = cast(CodingTemplate[AccelerationDetectionStatus], EnumTemplate.uint8(AccelerationDetectionStatus))

src/bluetooth_sig/gatt/characteristics/alert_level.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class AlertLevel(IntEnum):
2222
HIGH_ALERT = 0x02
2323

2424

25-
class AlertLevelCharacteristic(BaseCharacteristic[int]):
25+
class AlertLevelCharacteristic(BaseCharacteristic[AlertLevel]):
2626
"""Alert Level characteristic (0x2A06).
2727
2828
org.bluetooth.characteristic.alert_level

src/bluetooth_sig/gatt/characteristics/appearance.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from ...registry.core.appearance_values import appearance_values_registry
66
from ...types.appearance import AppearanceData
7+
from ...types.gatt_enums import CharacteristicRole
78
from ..context import CharacteristicContext
89
from .base import BaseCharacteristic
910
from .utils import DataParser
@@ -17,6 +18,7 @@ class AppearanceCharacteristic(BaseCharacteristic[AppearanceData]):
1718
Appearance characteristic with human-readable device type information.
1819
"""
1920

21+
_manual_role = CharacteristicRole.INFO
2022
expected_length = 2
2123

2224
def _decode_value(

src/bluetooth_sig/gatt/characteristics/blood_pressure_common.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ class BaseBloodPressureCharacteristic(BaseCharacteristic[Any]):
5151

5252
_is_base_class = True # Exclude from characteristic discovery
5353

54-
_python_type = str # Override since decode_value returns dataclass
55-
5654
# Declare optional dependency on Blood Pressure Feature for status interpretation
5755
_optional_dependencies: ClassVar[list[type[BaseCharacteristic[Any]]]] = [BloodPressureFeatureCharacteristic]
5856

0 commit comments

Comments
 (0)