Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: workaround fcntl and dbus_fast not available on windows #102

Merged
merged 8 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ jobs:
- "3.11"
os:
- ubuntu-latest
- macos-latest
- windows-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
Expand All @@ -50,8 +52,10 @@ jobs:
python-version: ${{ matrix.python-version }}
- uses: snok/install-poetry@v1
- name: Install Dependencies
shell: bash
run: poetry install
- name: Test with Pytest
shell: bash
run: poetry run pytest --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
Expand Down
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ myst-parser = {version = ">=0.18,<2.1", optional = true}
async-timeout = {version = ">=3.0.0", python = "<3.11"}
dbus-fast = ">=1.21.0"
bleak = ">=0.15.1"
usb-devices = ">=0.4.1"
usb-devices = ">=0.4.5"
aiohttp = ">=3.8.1"
mac-vendor-lookup = ">=0.1.12"

Expand Down
13 changes: 11 additions & 2 deletions src/bluetooth_adapters/dbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@
from pathlib import Path
from typing import Any

from dbus_fast import BusType, Message, MessageType, unpack_variants
from dbus_fast.aio import MessageBus
try:
from dbus_fast import BusType, Message, MessageType, unpack_variants
from dbus_fast.aio import MessageBus
except (AttributeError, ImportError):
# dbus_fast is not available on Windows
BusType = None
Message = None
MessageType = None
unpack_variants = None
MessageBus = None


from .history import AdvertisementHistory, load_history_from_managed_objects
from .util import asyncio_timeout
Expand Down
9 changes: 8 additions & 1 deletion src/bluetooth_adapters/systems/linux_hci.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from __future__ import annotations

import ctypes
import fcntl

try:
import fcntl
except ImportError:
# fcntl is not available on Windows
fcntl = None # type: ignore
import logging
import socket
from typing import Any
Expand Down Expand Up @@ -70,6 +75,8 @@

def get_adapters_from_hci() -> dict[int, dict[str, Any]]:
"""Get bluetooth adapters from HCI."""
if not fcntl:
raise RuntimeError("fcntl is not available")

Check warning on line 79 in src/bluetooth_adapters/systems/linux_hci.py

View check run for this annotation

Codecov / codecov/patch

src/bluetooth_adapters/systems/linux_hci.py#L79

Added line #L79 was not covered by tests
out: dict[int, dict[str, Any]] = {}
sock: socket.socket | None = None
try:
Expand Down
36 changes: 32 additions & 4 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
import asyncio
import time
from platform import system
from typing import Any
from unittest.mock import ANY, AsyncMock, MagicMock, patch

import pytest
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from dbus_fast import MessageType

try:
from dbus_fast import MessageType
except (AttributeError, ImportError):
MessageType = None
# dbus_fast is not available on Windows
from usb_devices import BluetoothDevice, USBDevice

import bluetooth_adapters.dbus as bluetooth_adapters_dbus

if system() != "Windows":
from bluetooth_adapters import (
BlueZDBusObjects,
get_bluetooth_adapters,
get_dbus_managed_objects,
)

from bluetooth_adapters import (
DEFAULT_ADDRESS,
AdapterDetails,
AdvertisementHistory,
BlueZDBusObjects,
DiscoveredDeviceAdvertisementData,
DiscoveredDeviceAdvertisementDataDict,
adapter_human_name,
Expand All @@ -24,13 +37,12 @@
discovered_device_advertisement_data_to_dict,
expire_stale_scanner_discovered_device_advertisement_data,
get_adapters,
get_bluetooth_adapters,
get_dbus_managed_objects,
load_history_from_managed_objects,
)


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_file_not_found():
"""Test get_bluetooth_adapters()."""

Expand All @@ -43,6 +55,7 @@ def __init__(self, *args, **kwargs):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_connection_refused():
"""Test get_bluetooth_adapters with connection refused."""

Expand All @@ -55,6 +68,7 @@ def __init__(self, *args, **kwargs):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_connect_refused_docker():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand All @@ -73,6 +87,7 @@ async def call(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_connect_fails():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand All @@ -89,6 +104,7 @@ async def call(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_connect_fails_docker():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand All @@ -107,6 +123,7 @@ async def call(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_connect_broken_pipe():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand All @@ -123,6 +140,7 @@ async def call(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_connect_broken_pipe_docker():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand All @@ -141,6 +159,7 @@ async def call(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_connect_eof_error():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand All @@ -159,6 +178,7 @@ async def call(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_no_call_return():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand All @@ -175,6 +195,7 @@ async def call(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_times_out():
async def _stall(*args: Any) -> None:
await asyncio.sleep(10)
Expand All @@ -196,6 +217,7 @@ async def connect(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_no_wrong_return():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -223,6 +245,7 @@ async def connect(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_bluetooth_adapters_correct_return_valid_message():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -251,6 +274,7 @@ async def connect(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_dbus_managed_objects():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -284,6 +308,7 @@ async def connect(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_BlueZDBusObjects():
class MockMessageBus:
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -379,6 +404,7 @@ async def connect(self):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_adapters_linux():
"""Test get_adapters."""

Expand Down Expand Up @@ -540,6 +566,7 @@ def setup(self, *args, **kwargs):


@pytest.mark.asyncio
@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
async def test_get_adapters_linux_no_usb_device():
"""Test get_adapters."""

Expand Down Expand Up @@ -923,6 +950,7 @@ def test_discovered_device_advertisement_data_from_dict():
)


@pytest.mark.skipif(MessageType is None, reason="dbus_fast is not available")
def test_expire_stale_scanner_discovered_device_advertisement_data():
"""Test expire_stale_scanner_discovered_device_advertisement_data."""
now = time.time()
Expand Down
Loading