Skip to content

Commit 8b14cf7

Browse files
committed
fix: Sort interfaces before passing index.
Signed-off-by: Mateusz Chrominski <[email protected]>
1 parent aa5c916 commit 8b14cf7

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

mfd_network_adapter/network_adapter_owner/base.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,21 @@
5757

5858
InterfaceInfoType = Union[InterfaceInfo, WindowsInterfaceInfo, LinuxInterfaceInfo]
5959

60+
_nat_key_re = re.compile(r"(\d+)")
61+
62+
63+
def natural_key(interface_name: str) -> list[str | int]:
64+
"""
65+
Use as a sorter key to properly sort interfaces.
66+
67+
This resolves sorting cases like ["eth0", "eth9", "eth10", "eth1"].
68+
69+
:param interface_name: interface name
70+
:return: list of strings and integers
71+
"""
72+
parts = _nat_key_re.split(interface_name)
73+
return [int(p) if p.isdigit() else p for p in parts]
74+
6075

6176
class NetworkAdapterOwner:
6277
"""Class for utility."""
@@ -525,7 +540,7 @@ def _filter_interfaces_info(
525540
return []
526541

527542
if interface_indexes:
528-
return [selected[idx] for idx in interface_indexes]
543+
return [sorted(selected, key=lambda i: natural_key(i.name))[idx] for idx in interface_indexes]
529544

530545
# by default ALL flag will be set
531546
if random_interface is None and all_interfaces is None:

tests/unit/test_mfd_network_adapter/test_network_adapter_owner/test_network_owner_base.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,29 @@ def test_filter_interfaces_info(self, owner):
364364
assert len(filtered) == 1
365365
assert filtered[0].pci_address == _pci_address
366366

367+
# additional coverage: check that interface_indexes are counted against naturally sorted names
368+
# prepare non-lexicographic names: eth0, eth9, eth10, eth1
369+
extra_infos = [
370+
InterfaceInfo(pci_address=_pci_address, pci_device=_pci_device, name="eth0"),
371+
InterfaceInfo(pci_address=_pci_address, pci_device=_pci_device, name="eth9"),
372+
InterfaceInfo(pci_address=_pci_address, pci_device=_pci_device, name="eth10"),
373+
InterfaceInfo(pci_address=_pci_address, pci_device=_pci_device, name="eth1"),
374+
]
375+
376+
filtered_extra = owner._filter_interfaces_info(
377+
extra_infos,
378+
pci_address=_pci_address,
379+
interface_indexes=[0, 1, 2, 3],
380+
)
381+
assert [i.name for i in filtered_extra] == ["eth0", "eth1", "eth9", "eth10"]
382+
383+
filtered_middle = owner._filter_interfaces_info(
384+
extra_infos,
385+
pci_address=_pci_address,
386+
interface_indexes=[1, 2],
387+
)
388+
assert [i.name for i in filtered_middle] == ["eth1", "eth9"]
389+
367390

368391
class TestSortedInterfaces:
369392
@pytest.fixture
@@ -426,3 +449,30 @@ def test_sorted_interfaces_error(self, mocker, interfaces):
426449
NetworkInterfaceIncomparableObject, match="Incorrect object passed for comparison with PCIAddress"
427450
):
428451
sorted(interfaces)
452+
453+
454+
class TestNaturalKey:
455+
def test_natural_key_simple_eth(self):
456+
from mfd_network_adapter.network_adapter_owner.base import natural_key
457+
458+
names = ["eth0", "eth9", "eth10", "eth1"]
459+
assert sorted(names, key=natural_key) == ["eth0", "eth1", "eth9", "eth10"]
460+
461+
def test_natural_key_mixed_patterns(self):
462+
from mfd_network_adapter.network_adapter_owner.base import natural_key
463+
464+
names = [
465+
"enp0s10f1v2",
466+
"enp0s2f1v10",
467+
"enp0s2f1v2",
468+
"Ethernet 10",
469+
"Ethernet 2",
470+
]
471+
472+
assert sorted(names, key=natural_key) == [
473+
"Ethernet 2",
474+
"Ethernet 10",
475+
"enp0s2f1v2",
476+
"enp0s2f1v10",
477+
"enp0s10f1v2",
478+
]

0 commit comments

Comments
 (0)