diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml index 105ce2d..dd4c951 100644 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -1,5 +1,6 @@ + diff --git a/.idea/misc.xml b/.idea/misc.xml index dc9ea49..7cc8dea 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,10 @@ - + + + + + \ No newline at end of file diff --git a/.idea/multicast_expert.iml b/.idea/multicast_expert.iml index e6d3c05..98f8ba9 100644 --- a/.idea/multicast_expert.iml +++ b/.idea/multicast_expert.iml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/Development Setup.md b/Development Setup.md index e0fa288..0685a80 100644 --- a/Development Setup.md +++ b/Development Setup.md @@ -12,13 +12,13 @@ python -m poetry shell # This activates a virtual environment containing the dep ### Running Tests ``` -python -m mypy . # Checks types +mypy . # Checks types # Linux only, allows the loopback tests to pass sudo ip route add 239.2.2.0/24 dev lo sudo ip -6 route add table local ff11::/16 dev lo -python -m pytest . # Checks actual code +pytest . # Checks actual code ``` ### Uploading to PyPi diff --git a/multicast_expert/utils.py b/multicast_expert/utils.py index 0b27a71..4f4e7e4 100644 --- a/multicast_expert/utils.py +++ b/multicast_expert/utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import socket from typing import List, Dict, Optional, Union, Tuple import ipaddress @@ -9,6 +11,12 @@ is_mac = platform.system() == "Darwin" +# The netifaces2 rewritten version allows multicast_expert to work using a maintained backend (yay!). +# However, it uses the same import name as the original netifaces (aww), so we have to get creative to +# detect it. It adds a new default_gateway() function which is much simpler to use. +using_netifaces_2 = hasattr(netifaces, "default_gateway") + + # Type which can represent an IPv4 or an IPv6 address. # For IPv4, address is a tuple of IP address (str) and port number. # For IPv6, address is a tuple of IP address (str), port number, flow info (int), and scope ID (int). @@ -35,17 +43,17 @@ def get_interface_ips(include_ipv4: bool = True, include_ipv6: bool = True) -> L ip_list = [] for interface in netifaces.interfaces(): - all_addresses: List[Dict[str, str]] = [] + all_addresses: List[Dict[netifaces.defs.AddressType, str]] = [] addresses_at_each_level = netifaces.ifaddresses(interface) if include_ipv4: # Note: Check needed because some interfaces do not have an ipv4 or ipv6 address if netifaces.AF_INET in addresses_at_each_level: - all_addresses.extend(addresses_at_each_level[netifaces.AF_INET]) + all_addresses.extend(addresses_at_each_level[netifaces.AF_INET]) # type: ignore[index] if include_ipv6: if netifaces.AF_INET6 in addresses_at_each_level: - all_addresses.extend(addresses_at_each_level[netifaces.AF_INET6]) + all_addresses.extend(addresses_at_each_level[netifaces.AF_INET6]) # type: ignore[index] for address_dict in all_addresses: ip_list.append(address_dict["addr"]) @@ -58,7 +66,7 @@ def get_default_gateway_iface_ip_v6() -> Optional[str]: Get the IP address of the interface that connects to the default IPv6 gateway, if it can be determined. If it cannot be determined, None is returned. """ - return get_default_gateway_iface_ip(netifaces.AF_INET6) + return get_default_gateway_iface_ip(netifaces.AF_INET6) # type: ignore[arg-type] def get_default_gateway_iface_ip_v4() -> Optional[str]: @@ -66,33 +74,43 @@ def get_default_gateway_iface_ip_v4() -> Optional[str]: Get the IP address of the interface that connects to the default IPv4 gateway, if it can be determined. If it cannot be determined, None is returned. """ - return get_default_gateway_iface_ip(netifaces.AF_INET) + return get_default_gateway_iface_ip(netifaces.AF_INET) # type: ignore[arg-type] -def get_default_gateway_iface_ip(addr_family: int) -> Optional[str]: +def get_default_gateway_iface_ip(addr_family: netifaces.InterfaceType) -> Optional[str]: """ Get the IP address of the interface that connects to the default gateway of the given addr_family, if it can be determined. If it cannot be determined, None is returned. """ - # Enumerate all gateways using netifaces - try: - gateways = netifaces.gateways() - except OSError: - return None + if using_netifaces_2: - # If it can, it will identify one of those as the default gateway for traffic. - # If not, return none. - if not "default" in gateways: - return None - if not addr_family in gateways["default"]: - return None + # In netifaces 2, we get the + default_gateways = netifaces.default_gateway() + if addr_family in default_gateways: + default_gateway_iface = default_gateways[addr_family][1] + else: + return None + + else: # netifaces classic (TM) + # Enumerate all gateways using netifaces + try: + gateways = netifaces.gateways() + except OSError: + return None + + # If it can, it will identify one of those as the default gateway for traffic. + # If not, return none. + if not "default" in gateways: + return None + if not addr_family in gateways["default"]: + return None - default_gateway = gateways["default"][addr_family] - default_gateway_iface = default_gateway[1] # element 1 is the iface name, per the docs + default_gateway = gateways["default"][addr_family] + default_gateway_iface = default_gateway[1] # element 1 is the iface name, per the docs # Now, use the interface name to get the IP address of that interface - interface_addresses: Dict[int, List[Dict[str, str]]] = netifaces.ifaddresses(default_gateway_iface) + interface_addresses = netifaces.ifaddresses(default_gateway_iface) if addr_family not in interface_addresses: return None return interface_addresses[addr_family][0]["addr"] diff --git a/mypy.ini b/mypy.ini index 04620cf..b1efbe8 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,6 +1,2 @@ [mypy] strict = True - -# Don't enforce types for netifaces which has no type information -[mypy-netifaces] -ignore_missing_imports = True \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 75f8e06..ee97d5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ repository = "https://github.com/multiplemonomials/multicast_expert" [tool.poetry.dependencies] python = "^3.8" # Need Python 3.8 for socket.IPPROTO_IPV6 -netifaces = "0.*" +netifaces2 = "0.*" [tool.poetry.dev-dependencies] pytest = "^6.2"