1+ # src/network/interface.py
12"""Network interface detection and management"""
23
34import netifaces
5+ import platform
46from typing import Optional , List
57
8+ # Use psutil on Windows to get friendly interface names
9+ try :
10+ import psutil
11+ except Exception :
12+ psutil = None
13+
614
715class NetworkInterface :
816 """Handles network interface detection and management"""
9-
17+
1018 def __init__ (self , interface : str = None ):
19+ self ._platform = platform .system ().lower ()
1120 self .interface = interface or self ._get_default_interface ()
12-
21+
1322 def _get_default_interface (self ) -> str :
14- """Get the default network interface"""
23+ """Get the default network interface (Windows-friendly names when available) """
1524 try :
16- interfaces = netifaces .interfaces ()
17-
18- # Filter out loopback and virtual interfaces
19- physical_interfaces = [
20- iface for iface in interfaces
21- if not iface .startswith (('lo' , 'docker' , 'veth' , 'br-' ))
22- ]
23-
24- # Prefer ethernet interfaces, then wireless
25- for iface in physical_interfaces :
26- if iface .startswith (('eth' , 'en' )):
27- return iface
28-
29- for iface in physical_interfaces :
30- if iface .startswith (('wl' , 'wlan' )):
31- return iface
32-
33- # Fallback to first available interface
34- return physical_interfaces [0 ] if physical_interfaces else 'eth0'
35-
25+ # On Windows prefer psutil names (friendly names)
26+ if self ._platform == 'windows' and psutil is not None :
27+ # psutil returns a mapping of friendly names
28+ names = [name for name in psutil .net_if_addrs ().keys ()
29+ if not name .startswith (('Loopback' , 'loopback' , 'vEthernet' ))]
30+ # Prefer "Ethernet" or "Wi-Fi" heuristically
31+ for n in names :
32+ if n .lower ().startswith ('ether' ) or 'ethernet' in n .lower ():
33+ return n
34+ for n in names :
35+ if 'wi' in n .lower () or 'wifi' in n .lower () or 'wi-fi' in n .lower ():
36+ return n
37+ return names [0 ] if names else 'Ethernet'
38+ else :
39+ interfaces = netifaces .interfaces ()
40+
41+ # Filter out loopback and virtual interfaces
42+ physical_interfaces = [
43+ iface for iface in interfaces
44+ if not iface .startswith (('lo' , 'docker' , 'veth' , 'br-' ))
45+ ]
46+
47+ # Prefer ethernet interfaces, then wireless
48+ for iface in physical_interfaces :
49+ if iface .startswith (('eth' , 'en' )):
50+ return iface
51+
52+ for iface in physical_interfaces :
53+ if iface .startswith (('wl' , 'wlan' )):
54+ return iface
55+
56+ # Fallback to first available interface
57+ return physical_interfaces [0 ] if physical_interfaces else 'eth0'
58+
3659 except Exception :
37- return 'eth0 '
38-
60+ return ''
61+
3962 def get_interface_info (self ) -> dict :
4063 """Get information about the current interface"""
4164 try :
42- addrs = netifaces .ifaddresses (self .interface )
65+ addrs = netifaces .ifaddresses (self .interface ) if self . interface else {}
4366 info = {
4467 'name' : self .interface ,
4568 'ipv4' : [],
4669 'ipv6' : [],
4770 'mac' : None
4871 }
49-
72+
5073 # Get IPv4 addresses
51- if netifaces .AF_INET in addrs :
74+ if addrs and netifaces .AF_INET in addrs :
5275 for addr in addrs [netifaces .AF_INET ]:
5376 info ['ipv4' ].append ({
5477 'addr' : addr .get ('addr' ),
5578 'netmask' : addr .get ('netmask' ),
5679 'broadcast' : addr .get ('broadcast' )
5780 })
58-
81+
5982 # Get IPv6 addresses
60- if netifaces .AF_INET6 in addrs :
83+ if addrs and netifaces .AF_INET6 in addrs :
6184 for addr in addrs [netifaces .AF_INET6 ]:
6285 info ['ipv6' ].append ({
6386 'addr' : addr .get ('addr' ),
6487 'netmask' : addr .get ('netmask' )
6588 })
66-
89+
6790 # Get MAC address
68- if netifaces .AF_LINK in addrs :
91+ if addrs and netifaces .AF_LINK in addrs :
6992 info ['mac' ] = addrs [netifaces .AF_LINK ][0 ].get ('addr' )
70-
93+
7194 return info
72-
95+
7396 except Exception as e :
7497 return {'name' : self .interface , 'error' : str (e )}
75-
98+
7699 def list_all_interfaces (self ) -> List [str ]:
77- """List all available network interfaces"""
100+ """List all available network interfaces (friendly names preferred on Windows) """
78101 try :
102+ if self ._platform == 'windows' and psutil is not None :
103+ return list (psutil .net_if_addrs ().keys ())
79104 return netifaces .interfaces ()
80105 except Exception :
81106 return []
82-
107+
83108 def is_interface_up (self ) -> bool :
84109 """Check if the interface is up and running"""
85110 try :
111+ if self ._platform == 'windows' and psutil is not None :
112+ return self .interface in psutil .net_if_stats ()
86113 return self .interface in netifaces .interfaces ()
87114 except Exception :
88- return False
115+ return False
0 commit comments