diff --git a/migration/tests/test_fragmentation_backend_shim.py b/migration/tests/test_fragmentation_backend_shim.py index 281df75..f5b2ea1 100644 --- a/migration/tests/test_fragmentation_backend_shim.py +++ b/migration/tests/test_fragmentation_backend_shim.py @@ -136,6 +136,41 @@ def test_cpp_backend_request_fails_clearly_when_unavailable(): assert "C++ BLE fragmentation backend is not available" in completed.stderr +def test_bleinterface_does_not_silently_fallback_when_cpp_requested_but_unavailable(): + env = os.environ.copy() + env["BLE_RETICULUM_FRAGMENTATION_BACKEND"] = "cpp" + env["PYTHONPATH"] = SRC_DIR + + completed = subprocess.run( + [ + sys.executable, + "-c", + """ +import sys, types +RNS = types.ModuleType('RNS') +sys.modules['RNS'] = RNS +transport = types.ModuleType('RNS.Transport') +sys.modules['RNS.Transport'] = transport +interfaces = types.ModuleType('RNS.Interfaces') +sys.modules['RNS.Interfaces'] = interfaces +interface_mod = types.ModuleType('RNS.Interfaces.Interface') +class Interface: + MODE_FULL = 0 +interface_mod.Interface = Interface +sys.modules['RNS.Interfaces.Interface'] = interface_mod +import ble_reticulum.BLEInterface +""", + ], + cwd=REPO_ROOT, + env=env, + text=True, + capture_output=True, + ) + + assert completed.returncode != 0 + assert "C++ BLE fragmentation backend is not available" in completed.stderr + + def test_selected_backend_exposes_methods_expected_by_current_python_code(): result = run_probe("cpp", include_cpp=True) diff --git a/src/ble_reticulum/BLEInterface.py b/src/ble_reticulum/BLEInterface.py index f113727..7dd5abf 100644 --- a/src/ble_reticulum/BLEInterface.py +++ b/src/ble_reticulum/BLEInterface.py @@ -79,15 +79,23 @@ except ImportError: # Import fragmentation backend selector. # Note: When loaded as external interface, use absolute imports. try: + from BLEFragmentationBackend import BACKEND as BLE_FRAGMENTATION_BACKEND from BLEFragmentationBackend import BLEFragmenter, BLEReassembler -except ImportError: +except ImportError as e: + if "C++ BLE fragmentation backend is not available" in str(e): + raise try: + from ble_reticulum.BLEFragmentationBackend import BACKEND as BLE_FRAGMENTATION_BACKEND from ble_reticulum.BLEFragmentationBackend import BLEFragmenter, BLEReassembler - except ImportError: + except ImportError as e: + if "C++ BLE fragmentation backend is not available" in str(e): + raise # Compatibility fallback if the backend shim is not installed. try: + BLE_FRAGMENTATION_BACKEND = "python-direct" from BLEFragmentation import BLEFragmenter, BLEReassembler except ImportError: + BLE_FRAGMENTATION_BACKEND = "python-direct" from ble_reticulum.BLEFragmentation import BLEFragmenter, BLEReassembler # Import GATT server for peripheral mode @@ -463,6 +471,12 @@ class BLEInterface(Interface): RNS.log(f"{self} initializing with service UUID {self.service_uuid}", RNS.LOG_INFO) + RNS.log( + f"{self} fragmentation backend: {BLE_FRAGMENTATION_BACKEND} " + f"(fragmenter={BLEFragmenter.__module__}.{BLEFragmenter.__name__}, " + f"reassembler={BLEReassembler.__module__}.{BLEReassembler.__name__})", + RNS.LOG_NOTICE + ) RNS.log(f"{self} power mode: {self.power_mode}, max peers: {self.max_peers}", RNS.LOG_DEBUG) RNS.log(f"{self} central mode: {'ENABLED' if self.enable_central else 'DISABLED'}", RNS.LOG_INFO) RNS.log(f"{self} peripheral mode: {'ENABLED' if self.enable_peripheral else 'DISABLED'}", RNS.LOG_INFO)