Skip to content

fix(hardware_utils): get_akida_device only checks the first device - #24

Merged
dmclellandbc merged 1 commit into
Brainchip-Inc:mainfrom
MalakhatkoVadym:fix/device-selection-loop
Sep 2, 2026
Merged

dmclellandbc merged 1 commit into
Brainchip-Inc:mainfrom
MalakhatkoVadym:fix/device-selection-loop

Conversation

@MalakhatkoVadym

Copy link
Copy Markdown
Contributor

In get_akida_device(), the return None in the target_version branch is inside
the for loop, so only the first enumerated device is ever compared against the
requested IP version:

for dd in akida.devices():
    if dd.ip_version == target_version:
        print('Target Akida device found')
        return dd
    print('Connected Akida Device does not match the requested IPVersion.')
    print('Calls to akida will run on the software backend.')
    return None

On a host with more than one Akida device, a matching device in any position after
the first is never found, and the caller silently falls back to the software
backend — which is easy to miss, because the printed message looks like a normal
"no matching hardware" result.

This moves the fallback after the loop, and reuses the devices list already
fetched at the top of the function instead of enumerating a second time.

Added test/test_hardware_utils.py with three cases (match after the first device,
no match, no target requested). They stub akida.devices, so they need no hardware.
Verified locally: the first test fails against the unmodified function (returns None)
and all three pass with the fix applied.

One thing to flag: ci.yml and hardware.yml both invoke pytest against
test/test_models.py specifically rather than the test/ directory, so this new
file wouldn't be collected as-is. Happy to add it to the CI target in this PR if
you'd like, or leave that to you.

Comment on lines +12 to +27
def test_matching_device_after_the_first_is_found(monkeypatch):
"""A device matching target_version must be found wherever it sits in the list."""
first, second = _FakeDevice(akida.IpVersion.v2), _FakeDevice(akida.IpVersion.v1)
monkeypatch.setattr(akida, "devices", lambda: [first, second])
assert get_akida_device(target_version=akida.IpVersion.v1) is second


def test_returns_none_when_no_device_matches(monkeypatch):
monkeypatch.setattr(akida, "devices", lambda: [_FakeDevice(akida.IpVersion.v2)])
assert get_akida_device(target_version=akida.IpVersion.v1) is None


def test_returns_first_device_when_no_target_requested(monkeypatch):
first = _FakeDevice(akida.IpVersion.v1)
monkeypatch.setattr(akida, "devices", lambda: [first, _FakeDevice(akida.IpVersion.v2)])
assert get_akida_device() is first

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not mandatory but you are able to parametrize the test

Suggested change
def test_matching_device_after_the_first_is_found(monkeypatch):
"""A device matching target_version must be found wherever it sits in the list."""
first, second = _FakeDevice(akida.IpVersion.v2), _FakeDevice(akida.IpVersion.v1)
monkeypatch.setattr(akida, "devices", lambda: [first, second])
assert get_akida_device(target_version=akida.IpVersion.v1) is second
def test_returns_none_when_no_device_matches(monkeypatch):
monkeypatch.setattr(akida, "devices", lambda: [_FakeDevice(akida.IpVersion.v2)])
assert get_akida_device(target_version=akida.IpVersion.v1) is None
def test_returns_first_device_when_no_target_requested(monkeypatch):
first = _FakeDevice(akida.IpVersion.v1)
monkeypatch.setattr(akida, "devices", lambda: [first, _FakeDevice(akida.IpVersion.v2)])
assert get_akida_device() is first
import pytest
_device_ip_v1=_FakeDevice(akida.IpVersion.v1)
_device_ip_v2=_FakeDevice(akida.IpVersion.v2)
@pytest.mark.parametrize("list_devices, target_version, expected",
[([_device_ip_v1, _device_ip_v2], akida.IpVersion.v1, _device_ip_v2), ...])
def test_returns_first_device_when_no_target_requested(monkeypatch, list_devices, target_version, expected):
monkeypatch.setattr(akida, "devices", lambda: list_devices)
assert get_akida_device(target_version=target_version) is expected

print('Connected Akida Device does not match the requested IPVersion.')
print('Calls to akida will run on the software backend.')
return None
print('Connected Akida Device does not match the requested IPVersion.')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the fix, but we could improve error message.

available_versions = [dd.ip_version for dd in devices]
print(f'Connected Akida Device does not match the requested IPVersion. '
      f'Requested: {target_version}, available: {available_versions}')

@dmclellandbc dmclellandbc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks.
And I'll take care of extending the CI to include the test/ directory in some follow-up work.

@dmclellandbc
dmclellandbc merged commit 468377d into Brainchip-Inc:main Sep 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants