Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-ifaddr for openSUSE:Factory checked in at 2022-07-21 11:33:11 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-ifaddr (Old) and /work/SRC/openSUSE:Factory/.python-ifaddr.new.1523 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ifaddr" Thu Jul 21 11:33:11 2022 rev:6 rq:990233 version:0.2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-ifaddr/python-ifaddr.changes 2020-06-15 20:29:01.186064453 +0200 +++ /work/SRC/openSUSE:Factory/.python-ifaddr.new.1523/python-ifaddr.changes 2022-07-21 11:33:42.466966346 +0200 @@ -1,0 +2,11 @@ +Tue Jul 19 12:01:31 UTC 2022 - Dirk M??ller <[email protected]> + +- update to 0.2.0: + * Added an option to include IP-less adapters, thanks to memory + * Fixed a bug where an interface's name was `bytes`, not `str`, on Windows + * Added an implementation of `netifaces.interfaces()` (available through + `ifaddr.netifaces.interfaces()`) + * Added type hints + * Dropped Python 3.6 support + +------------------------------------------------------------------- Old: ---- ifaddr-0.1.7.tar.gz New: ---- ifaddr-0.2.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-ifaddr.spec ++++++ --- /var/tmp/diff_new_pack.hHhjGx/_old 2022-07-21 11:33:42.874966749 +0200 +++ /var/tmp/diff_new_pack.hHhjGx/_new 2022-07-21 11:33:42.878966753 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-ifaddr # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %bcond_without python2 Name: python-ifaddr -Version: 0.1.7 +Version: 0.2.0 Release: 0 Summary: Module for enumerating IP addresses on system network adapters License: MIT ++++++ ifaddr-0.1.7.tar.gz -> ifaddr-0.2.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/PKG-INFO new/ifaddr-0.2.0/PKG-INFO --- old/ifaddr-0.1.7/PKG-INFO 2020-06-06 21:13:24.046951500 +0200 +++ new/ifaddr-0.2.0/PKG-INFO 2022-06-15 23:40:17.928045700 +0200 @@ -1,106 +1,143 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: ifaddr -Version: 0.1.7 +Version: 0.2.0 Summary: Cross-platform network interface and IP address enumeration library Home-page: https://github.com/pydron/ifaddr Author: Stefan C. Mueller Author-email: [email protected] License: MIT -Description: ifaddr - Enumerate network interfaces/adapters and their IP addresses - ===================================================================== - - .. image:: https://github.com/pydron/ifaddr/workflows/CI/badge.svg - :target: https://github.com/pydron/ifaddr/actions?query=workflow%3ACI+branch%3Amaster - - .. image:: https://img.shields.io/pypi/v/ifaddr.svg - :target: https://pypi.python.org/pypi/ifaddr - - .. image:: https://codecov.io/gh/pydron/ifaddr/branch/master/graph/badge.svg - :target: https://codecov.io/gh/pydron/ifaddr - - `ifaddr` is a small Python library that allows you to find all the - IP addresses of the computer. It is tested on **Linux**, **OS X**, and - **Windows**. Other BSD derivatives like **OpenBSD**, **FreeBSD**, and - **NetBSD** should work too, but I haven't personally tested those. - **Solaris/Illumos** should also work. - - This library is open source and released under the MIT License. It works - with Python 2.7 and 3.5+. - - You can install it with `pip install ifaddr`. It doesn't need to - compile anything, so there shouldn't be any surprises. Even on Windows. - - Project links: - - * `ifaddr GitHub page <https://github.com/smurn/ifaddr>`_ - * `ifaddr documentation (although there isn't much to document) <http://pythonhosted.org/ifaddr/>`_ - * `ifaddr on PyPI <https://pypi.org/project/ifaddr/>`_ - - - ---------------------- - Let's get going! - ---------------------- - - .. code-block:: python - - import ifaddr - - adapters = ifaddr.get_adapters() - - for adapter in adapters: - print("IPs of network adapter " + adapter.nice_name) - for ip in adapter.ips: - print(" %s/%s" % (ip.ip, ip.network_prefix)) - - This will print:: - - IPs of network adapter H5321 gw Mobile Broadband Driver - IP ('fe80::9:ebdf:30ab:39a3', 0L, 17L)/64 - IP 169.254.57.163/16 - IPs of network adapter Intel(R) Centrino(R) Advanced-N 6205 - IP ('fe80::481f:3c9d:c3f6:93f8', 0L, 12L)/64 - IP 192.168.0.51/24 - IPs of network adapter Intel(R) 82579LM Gigabit Network Connection - IP ('fe80::85cd:e07e:4f7a:6aa6', 0L, 11L)/64 - IP 192.168.0.53/24 - IPs of network adapter Software Loopback Interface 1 - IP ('::1', 0L, 0L)/128 - IP 127.0.0.1/8 - - You get both IPv4 and IPv6 addresses. The later complete with - flowinfo and scope_id. - - --------- - Changelog - --------- - - 0.1.7 - ----- - - * Fixed Python 3 compatibility in the examples, thanks to Tristan Stenner and Josef Schlehofer - * Exposed network interface indexes in Adapter.index, thanks to Dmitry Tantsur - * Added the license file to distributions on PyPI, thanks to Tom???? Chv??tal - * Fixed Illumos/Solaris compatibility based on a patch proposed by Jorge Schrauwen - * Set up universal wheels, ifaddr will have both source and wheel distributions on PyPI from now on - - ------------ - Alternatives - ------------ - - Alastair Houghton develops `netifaces <https://pypi.python.org/pypi/netifaces>`_ - which can do everything this library can, and more. The only drawback is that it needs - to be compiled, which can make the installation difficult. - Keywords: network interfaces,network adapters,network addresses,IP addresses Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: System :: Networking Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +License-File: LICENSE.txt + +ifaddr - Enumerate network interfaces/adapters and their IP addresses +===================================================================== + +.. image:: https://github.com/pydron/ifaddr/workflows/CI/badge.svg + :target: https://github.com/pydron/ifaddr/actions?query=workflow%3ACI+branch%3Amaster + +.. image:: https://img.shields.io/pypi/v/ifaddr.svg + :target: https://pypi.python.org/pypi/ifaddr + +.. image:: https://codecov.io/gh/pydron/ifaddr/branch/master/graph/badge.svg + :target: https://codecov.io/gh/pydron/ifaddr + +`ifaddr` is a small Python library that allows you to find all the Ethernet and +IP addresses of the computer. It is tested on **Linux**, **OS X**, and +**Windows**. Other BSD derivatives like **OpenBSD**, **FreeBSD**, and +**NetBSD** should work too, but I haven't personally tested those. +**Solaris/Illumos** should also work. + +This library is open source and released under the MIT License. It works +with Python 3.7+. + +You can install it with `pip install ifaddr`. It doesn't need to +compile anything, so there shouldn't be any surprises. Even on Windows. + +Project links: + +* `ifaddr GitHub page <https://github.com/smurn/ifaddr>`_ +* `ifaddr documentation (although there isn't much to document) <http://pythonhosted.org/ifaddr/>`_ +* `ifaddr on PyPI <https://pypi.org/project/ifaddr/>`_ + + +---------------------- +Let's get going! +---------------------- + +.. code-block:: python + + import ifaddr + + adapters = ifaddr.get_adapters() + + for adapter in adapters: + print("IPs of network adapter " + adapter.nice_name) + for ip in adapter.ips: + print(" %s/%s" % (ip.ip, ip.network_prefix)) + +This will print:: + + IPs of network adapter H5321 gw Mobile Broadband Driver + IP ('fe80::9:ebdf:30ab:39a3', 0L, 17L)/64 + IP 169.254.57.163/16 + IPs of network adapter Intel(R) Centrino(R) Advanced-N 6205 + IP ('fe80::481f:3c9d:c3f6:93f8', 0L, 12L)/64 + IP 192.168.0.51/24 + IPs of network adapter Intel(R) 82579LM Gigabit Network Connection + IP ('fe80::85cd:e07e:4f7a:6aa6', 0L, 11L)/64 + IP 192.168.0.53/24 + IPs of network adapter Software Loopback Interface 1 + IP ('::1', 0L, 0L)/128 + IP 127.0.0.1/8 + +You get both IPv4 and IPv6 addresses. The later complete with +flowinfo and scope_id. + +If you wish to include network interfaces that do not have a configured IP +addresss, pass the `include_unconfigured` parameter to `get_adapters()`. +Adapters with no configured IP addresses will have an zero-length `ips` +property. For example: + +.. code-block:: python + + import ifaddr + + adapters = ifaddr.get_adapters(include_unconfigured=True) + + for adapter in adapters: + print("IPs of network adapter " + adapter.nice_name) + if adapter.ips: + for ip in adapter.ips: + print(" %s/%s" % (ip.ip, ip.network_prefix)) + else: + print(" No IPs configured") + + +--------- +Changelog +--------- + +0.2.0 +----- + +* Added an option to include IP-less adapters, thanks to memory +* Fixed a bug where an interface's name was `bytes`, not `str`, on Windows +* Added an implementation of `netifaces.interfaces()` (available through + `ifaddr.netifaces.interfaces()`) +* Added type hints + +Backwards incompatible/breaking changes: + +* Dropped Python 3.6 support + +0.1.7 +----- + +* Fixed Python 3 compatibility in the examples, thanks to Tristan Stenner and Josef Schlehofer +* Exposed network interface indexes in Adapter.index, thanks to Dmitry Tantsur +* Added the license file to distributions on PyPI, thanks to Tom???? Chv??tal +* Fixed Illumos/Solaris compatibility based on a patch proposed by Jorge Schrauwen +* Set up universal wheels, ifaddr will have both source and wheel distributions on PyPI from now on + +------------ +Alternatives +------------ + +Alastair Houghton develops `netifaces <https://pypi.python.org/pypi/netifaces>`_ +which can do everything this library can, and more. The only drawback is that it needs +to be compiled, which can make the installation difficult. + +As of ifaddr 0.2.0 we implement the equivalent of `netifaces.interfaces()`. It's available through +`ifaddr.netifaces.interfaces()`. + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/README.rst new/ifaddr-0.2.0/README.rst --- old/ifaddr-0.1.7/README.rst 2020-06-06 20:55:48.000000000 +0200 +++ new/ifaddr-0.2.0/README.rst 2022-06-15 23:28:04.000000000 +0200 @@ -10,14 +10,14 @@ .. image:: https://codecov.io/gh/pydron/ifaddr/branch/master/graph/badge.svg :target: https://codecov.io/gh/pydron/ifaddr -`ifaddr` is a small Python library that allows you to find all the +`ifaddr` is a small Python library that allows you to find all the Ethernet and IP addresses of the computer. It is tested on **Linux**, **OS X**, and **Windows**. Other BSD derivatives like **OpenBSD**, **FreeBSD**, and **NetBSD** should work too, but I haven't personally tested those. **Solaris/Illumos** should also work. This library is open source and released under the MIT License. It works -with Python 2.7 and 3.5+. +with Python 3.7+. You can install it with `pip install ifaddr`. It doesn't need to compile anything, so there shouldn't be any surprises. Even on Windows. @@ -62,10 +62,43 @@ You get both IPv4 and IPv6 addresses. The later complete with flowinfo and scope_id. +If you wish to include network interfaces that do not have a configured IP +addresss, pass the `include_unconfigured` parameter to `get_adapters()`. +Adapters with no configured IP addresses will have an zero-length `ips` +property. For example: + +.. code-block:: python + + import ifaddr + + adapters = ifaddr.get_adapters(include_unconfigured=True) + + for adapter in adapters: + print("IPs of network adapter " + adapter.nice_name) + if adapter.ips: + for ip in adapter.ips: + print(" %s/%s" % (ip.ip, ip.network_prefix)) + else: + print(" No IPs configured") + + --------- Changelog --------- +0.2.0 +----- + +* Added an option to include IP-less adapters, thanks to memory +* Fixed a bug where an interface's name was `bytes`, not `str`, on Windows +* Added an implementation of `netifaces.interfaces()` (available through + `ifaddr.netifaces.interfaces()`) +* Added type hints + +Backwards incompatible/breaking changes: + +* Dropped Python 3.6 support + 0.1.7 ----- @@ -82,3 +115,6 @@ Alastair Houghton develops `netifaces <https://pypi.python.org/pypi/netifaces>`_ which can do everything this library can, and more. The only drawback is that it needs to be compiled, which can make the installation difficult. + +As of ifaddr 0.2.0 we implement the equivalent of `netifaces.interfaces()`. It's available through +`ifaddr.netifaces.interfaces()`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/ifaddr/_posix.py new/ifaddr-0.2.0/ifaddr/_posix.py --- old/ifaddr-0.1.7/ifaddr/_posix.py 2020-06-06 09:29:19.000000000 +0200 +++ new/ifaddr-0.2.0/ifaddr/_posix.py 2022-06-15 23:08:21.000000000 +0200 @@ -19,27 +19,35 @@ # IN THE SOFTWARE. -import sys import os import ctypes.util import ipaddress import collections import socket +from typing import Iterable, Optional + import ifaddr._shared as shared -#from ifaddr._shared import sockaddr, Interface, sockaddr_to_ip, ipv6_prefixlength + +# from ifaddr._shared import sockaddr, Interface, sockaddr_to_ip, ipv6_prefixlength + class ifaddrs(ctypes.Structure): pass -ifaddrs._fields_ = [('ifa_next', ctypes.POINTER(ifaddrs)), - ('ifa_name', ctypes.c_char_p), - ('ifa_flags', ctypes.c_uint), - ('ifa_addr', ctypes.POINTER(shared.sockaddr)), - ('ifa_netmask', ctypes.POINTER(shared.sockaddr))] -libc = ctypes.CDLL(ctypes.util.find_library("socket" if os.uname()[0] == "SunOS" else "c"), use_errno=True) -def get_adapters(): +ifaddrs._fields_ = [ + ('ifa_next', ctypes.POINTER(ifaddrs)), + ('ifa_name', ctypes.c_char_p), + ('ifa_flags', ctypes.c_uint), + ('ifa_addr', ctypes.POINTER(shared.sockaddr)), + ('ifa_netmask', ctypes.POINTER(shared.sockaddr)), +] + +libc = ctypes.CDLL(ctypes.util.find_library("socket" if os.uname()[0] == "SunOS" else "c"), use_errno=True) # type: ignore + + +def get_adapters(include_unconfigured: bool = False) -> Iterable[shared.Adapter]: addr0 = addr = ctypes.POINTER(ifaddrs)() retval = libc.getifaddrs(ctypes.byref(addr)) @@ -49,41 +57,38 @@ ips = collections.OrderedDict() - def add_ip(adapter_name, ip): - if not adapter_name in ips: + def add_ip(adapter_name: str, ip: Optional[shared.IP]) -> None: + if adapter_name not in ips: + index = None # type: Optional[int] try: - index = socket.if_nametoindex(adapter_name) + # Mypy errors on this when the Windows CI runs: + # error: Module has no attribute "if_nametoindex" + index = socket.if_nametoindex(adapter_name) # type: ignore except (OSError, AttributeError): - index = None - ips[adapter_name] = shared.Adapter(adapter_name, adapter_name, [], - index=index) - ips[adapter_name].ips.append(ip) - + pass + ips[adapter_name] = shared.Adapter(adapter_name, adapter_name, [], index=index) + if ip is not None: + ips[adapter_name].ips.append(ip) while addr: - name = addr[0].ifa_name - if sys.version_info[0] > 2: - name = name.decode(encoding='UTF-8') - ip = shared.sockaddr_to_ip(addr[0].ifa_addr) - if ip: + name = addr[0].ifa_name.decode(encoding='UTF-8') + ip_addr = shared.sockaddr_to_ip(addr[0].ifa_addr) + if ip_addr: if addr[0].ifa_netmask and not addr[0].ifa_netmask[0].sa_familiy: addr[0].ifa_netmask[0].sa_familiy = addr[0].ifa_addr[0].sa_familiy netmask = shared.sockaddr_to_ip(addr[0].ifa_netmask) if isinstance(netmask, tuple): - netmask = netmask[0] - if sys.version_info[0] > 2: - netmaskStr = str(netmask) - else: - netmaskStr = unicode(netmask) + netmaskStr = str(netmask[0]) prefixlen = shared.ipv6_prefixlength(ipaddress.IPv6Address(netmaskStr)) else: - if sys.version_info[0] > 2: - netmaskStr = str('0.0.0.0/' + netmask) - else: - netmaskStr = unicode('0.0.0.0/' + netmask) + assert netmask is not None, f'sockaddr_to_ip({addr[0].ifa_netmask}) returned None' + netmaskStr = str('0.0.0.0/' + netmask) prefixlen = ipaddress.IPv4Network(netmaskStr).prefixlen - ip = shared.IP(ip, prefixlen, name) + ip = shared.IP(ip_addr, prefixlen, name) add_ip(name, ip) + else: + if include_unconfigured: + add_ip(name, None) addr = addr[0].ifa_next libc.freeifaddrs(addr0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/ifaddr/_shared.py new/ifaddr-0.2.0/ifaddr/_shared.py --- old/ifaddr-0.1.7/ifaddr/_shared.py 2020-06-06 09:29:19.000000000 +0200 +++ new/ifaddr-0.2.0/ifaddr/_shared.py 2022-06-15 23:08:21.000000000 +0200 @@ -24,6 +24,9 @@ import ipaddress import platform +from typing import List, Optional, Tuple, Union + + class Adapter(object): """ Represents a network interface device controller (NIC), such as a @@ -35,7 +38,7 @@ a IPv4 and an IPv6 IP address. """ - def __init__(self, name, nice_name, ips, index=None): + def __init__(self, name: str, nice_name: str, ips: List['IP'], index: Optional[int] = None) -> None: #: Unique name that identifies the adapter in the system. #: On Linux this is of the form of `eth0` or `eth0:1`, on @@ -55,21 +58,25 @@ #: Adapter index as used by some API (e.g. IPv6 multicast group join). self.index = index - def __repr__(self): + def __repr__(self) -> str: return "Adapter(name={name}, nice_name={nice_name}, ips={ips}, index={index})".format( - name = repr(self.name), - nice_name = repr(self.nice_name), - ips = repr(self.ips), - index=repr(self.index) + name=repr(self.name), nice_name=repr(self.nice_name), ips=repr(self.ips), index=repr(self.index) ) +# Type of an IPv4 address (a string in "xxx.xxx.xxx.xxx" format) +_IPv4Address = str + +# Type of an IPv6 address (a three-tuple `(ip, flowinfo, scope_id)`) +_IPv6Address = Tuple[str, int, int] + + class IP(object): """ Represents an IP address of an adapter. """ - def __init__(self, ip, network_prefix, nice_name): + def __init__(self, ip: Union[_IPv4Address, _IPv6Address], network_prefix: int, nice_name: str) -> None: #: IP address. For IPv4 addresses this is a string in #: "xxx.xxx.xxx.xxx" format. For IPv6 addresses this @@ -90,7 +97,7 @@ self.nice_name = nice_name @property - def is_IPv4(self): + def is_IPv4(self) -> bool: """ Returns `True` if this IP is an IPv4 address and `False` if it is an IPv6 address. @@ -98,19 +105,16 @@ return not isinstance(self.ip, tuple) @property - def is_IPv6(self): + def is_IPv6(self) -> bool: """ Returns `True` if this IP is an IPv6 address and `False` if it is an IPv4 address. """ return isinstance(self.ip, tuple) - - def __repr__(self): + def __repr__(self) -> str: return "IP(ip={ip}, network_prefix={network_prefix}, nice_name={nice_name})".format( - ip = repr(self.ip), - network_prefix = repr(self.network_prefix), - nice_name = repr(self.nice_name) + ip=repr(self.ip), network_prefix=repr(self.network_prefix), nice_name=repr(self.nice_name) ) @@ -122,46 +126,55 @@ # both structures equally. class sockaddr(ctypes.Structure): - _fields_= [('sa_len', ctypes.c_uint8), - ('sa_familiy', ctypes.c_uint8), - ('sa_data', ctypes.c_uint8 * 14)] + _fields_ = [ + ('sa_len', ctypes.c_uint8), + ('sa_familiy', ctypes.c_uint8), + ('sa_data', ctypes.c_uint8 * 14), + ] class sockaddr_in(ctypes.Structure): - _fields_= [('sa_len', ctypes.c_uint8), - ('sa_familiy', ctypes.c_uint8), - ('sin_port', ctypes.c_uint16), - ('sin_addr', ctypes.c_uint8 * 4), - ('sin_zero', ctypes.c_uint8 * 8)] + _fields_ = [ + ('sa_len', ctypes.c_uint8), + ('sa_familiy', ctypes.c_uint8), + ('sin_port', ctypes.c_uint16), + ('sin_addr', ctypes.c_uint8 * 4), + ('sin_zero', ctypes.c_uint8 * 8), + ] class sockaddr_in6(ctypes.Structure): - _fields_= [('sa_len', ctypes.c_uint8), - ('sa_familiy', ctypes.c_uint8), - ('sin6_port', ctypes.c_uint16), - ('sin6_flowinfo', ctypes.c_uint32), - ('sin6_addr', ctypes.c_uint8 * 16), - ('sin6_scope_id', ctypes.c_uint32)] + _fields_ = [ + ('sa_len', ctypes.c_uint8), + ('sa_familiy', ctypes.c_uint8), + ('sin6_port', ctypes.c_uint16), + ('sin6_flowinfo', ctypes.c_uint32), + ('sin6_addr', ctypes.c_uint8 * 16), + ('sin6_scope_id', ctypes.c_uint32), + ] else: - class sockaddr(ctypes.Structure): - _fields_= [('sa_familiy', ctypes.c_uint16), - ('sa_data', ctypes.c_uint8 * 14)] + class sockaddr(ctypes.Structure): # type: ignore + _fields_ = [('sa_familiy', ctypes.c_uint16), ('sa_data', ctypes.c_uint8 * 14)] - class sockaddr_in(ctypes.Structure): - _fields_= [('sin_familiy', ctypes.c_uint16), - ('sin_port', ctypes.c_uint16), - ('sin_addr', ctypes.c_uint8 * 4), - ('sin_zero', ctypes.c_uint8 * 8)] - - class sockaddr_in6(ctypes.Structure): - _fields_= [('sin6_familiy', ctypes.c_uint16), - ('sin6_port', ctypes.c_uint16), - ('sin6_flowinfo', ctypes.c_uint32), - ('sin6_addr', ctypes.c_uint8 * 16), - ('sin6_scope_id', ctypes.c_uint32)] + class sockaddr_in(ctypes.Structure): # type: ignore + _fields_ = [ + ('sin_familiy', ctypes.c_uint16), + ('sin_port', ctypes.c_uint16), + ('sin_addr', ctypes.c_uint8 * 4), + ('sin_zero', ctypes.c_uint8 * 8), + ] + + class sockaddr_in6(ctypes.Structure): # type: ignore + _fields_ = [ + ('sin6_familiy', ctypes.c_uint16), + ('sin6_port', ctypes.c_uint16), + ('sin6_flowinfo', ctypes.c_uint32), + ('sin6_addr', ctypes.c_uint8 * 16), + ('sin6_scope_id', ctypes.c_uint32), + ] -def sockaddr_to_ip(sockaddr_ptr): +def sockaddr_to_ip(sockaddr_ptr: 'ctypes.pointer[sockaddr]') -> Optional[Union[_IPv4Address, _IPv6Address]]: if sockaddr_ptr: if sockaddr_ptr[0].sa_familiy == socket.AF_INET: ipv4 = ctypes.cast(sockaddr_ptr, ctypes.POINTER(sockaddr_in)) @@ -174,11 +187,11 @@ ippacked = bytes(bytearray(ipv6[0].sin6_addr)) ip = str(ipaddress.ip_address(ippacked)) scope_id = ipv6[0].sin6_scope_id - return(ip, flowinfo, scope_id) + return (ip, flowinfo, scope_id) return None -def ipv6_prefixlength(address): +def ipv6_prefixlength(address: ipaddress.IPv6Address) -> int: prefix_length = 0 for i in range(address.max_prefixlen): if int(address) >> i & 1: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/ifaddr/_win32.py new/ifaddr-0.2.0/ifaddr/_win32.py --- old/ifaddr-0.1.7/ifaddr/_win32.py 2020-06-04 22:51:16.000000000 +0200 +++ new/ifaddr-0.2.0/ifaddr/_win32.py 2022-06-15 23:08:21.000000000 +0200 @@ -21,10 +21,11 @@ import ctypes from ctypes import wintypes +from typing import Iterable, List import ifaddr._shared as shared -NO_ERROR=0 +NO_ERROR = 0 ERROR_BUFFER_OVERFLOW = 111 MAX_ADAPTER_NAME_LENGTH = 256 MAX_ADAPTER_DESCRIPTION_LENGTH = 128 @@ -32,50 +33,57 @@ AF_UNSPEC = 0 - class SOCKET_ADDRESS(ctypes.Structure): - _fields_ = [('lpSockaddr', ctypes.POINTER(shared.sockaddr)), - ('iSockaddrLength', wintypes.INT)] + _fields_ = [('lpSockaddr', ctypes.POINTER(shared.sockaddr)), ('iSockaddrLength', wintypes.INT)] + class IP_ADAPTER_UNICAST_ADDRESS(ctypes.Structure): pass -IP_ADAPTER_UNICAST_ADDRESS._fields_ = \ - [('Length', wintypes.ULONG), - ('Flags', wintypes.DWORD), - ('Next', ctypes.POINTER(IP_ADAPTER_UNICAST_ADDRESS)), - ('Address', SOCKET_ADDRESS), - ('PrefixOrigin', ctypes.c_uint), - ('SuffixOrigin', ctypes.c_uint), - ('DadState', ctypes.c_uint), - ('ValidLifetime', wintypes.ULONG), - ('PreferredLifetime', wintypes.ULONG), - ('LeaseLifetime', wintypes.ULONG), - ('OnLinkPrefixLength', ctypes.c_uint8), - ] -class IP_ADAPTER_ADDRESSES(ctypes.Structure): - pass -IP_ADAPTER_ADDRESSES._fields_ = [('Length', wintypes.ULONG), - ('IfIndex', wintypes.DWORD), - ('Next', ctypes.POINTER(IP_ADAPTER_ADDRESSES)), - ('AdapterName', ctypes.c_char_p), - ('FirstUnicastAddress', ctypes.POINTER(IP_ADAPTER_UNICAST_ADDRESS)), - ('FirstAnycastAddress', ctypes.POINTER(None)), - ('FirstMulticastAddress', ctypes.POINTER(None)), - ('FirstDnsServerAddress', ctypes.POINTER(None)), - ('DnsSuffix', ctypes.c_wchar_p), - ('Description', ctypes.c_wchar_p), - ('FriendlyName', ctypes.c_wchar_p) - ] +IP_ADAPTER_UNICAST_ADDRESS._fields_ = [ + ('Length', wintypes.ULONG), + ('Flags', wintypes.DWORD), + ('Next', ctypes.POINTER(IP_ADAPTER_UNICAST_ADDRESS)), + ('Address', SOCKET_ADDRESS), + ('PrefixOrigin', ctypes.c_uint), + ('SuffixOrigin', ctypes.c_uint), + ('DadState', ctypes.c_uint), + ('ValidLifetime', wintypes.ULONG), + ('PreferredLifetime', wintypes.ULONG), + ('LeaseLifetime', wintypes.ULONG), + ('OnLinkPrefixLength', ctypes.c_uint8), +] -iphlpapi = ctypes.windll.LoadLibrary("Iphlpapi") +class IP_ADAPTER_ADDRESSES(ctypes.Structure): + pass -def enumerate_interfaces_of_adapter(nice_name, address): + +IP_ADAPTER_ADDRESSES._fields_ = [ + ('Length', wintypes.ULONG), + ('IfIndex', wintypes.DWORD), + ('Next', ctypes.POINTER(IP_ADAPTER_ADDRESSES)), + ('AdapterName', ctypes.c_char_p), + ('FirstUnicastAddress', ctypes.POINTER(IP_ADAPTER_UNICAST_ADDRESS)), + ('FirstAnycastAddress', ctypes.c_void_p), + ('FirstMulticastAddress', ctypes.c_void_p), + ('FirstDnsServerAddress', ctypes.c_void_p), + ('DnsSuffix', ctypes.c_wchar_p), + ('Description', ctypes.c_wchar_p), + ('FriendlyName', ctypes.c_wchar_p), +] + + +iphlpapi = ctypes.windll.LoadLibrary("Iphlpapi") # type: ignore + + +def enumerate_interfaces_of_adapter( + nice_name: str, address: IP_ADAPTER_UNICAST_ADDRESS +) -> Iterable[shared.IP]: # Iterate through linked list and fill list - addresses = [] + addresses = [] # type: List[IP_ADAPTER_UNICAST_ADDRESS] while True: addresses.append(address) if not address.Next: @@ -84,28 +92,31 @@ for address in addresses: ip = shared.sockaddr_to_ip(address.Address.lpSockaddr) + assert ip is not None, f'sockaddr_to_ip({address.Address.lpSockaddr}) returned None' network_prefix = address.OnLinkPrefixLength yield shared.IP(ip, network_prefix, nice_name) -def get_adapters(): +def get_adapters(include_unconfigured: bool = False) -> Iterable[shared.Adapter]: # Call GetAdaptersAddresses() with error and buffer size handling - addressbuffersize = wintypes.ULONG(15*1024) + addressbuffersize = wintypes.ULONG(15 * 1024) retval = ERROR_BUFFER_OVERFLOW while retval == ERROR_BUFFER_OVERFLOW: addressbuffer = ctypes.create_string_buffer(addressbuffersize.value) - retval = iphlpapi.GetAdaptersAddresses(wintypes.ULONG(AF_UNSPEC), - wintypes.ULONG(0), - None, - ctypes.byref(addressbuffer), - ctypes.byref(addressbuffersize)) + retval = iphlpapi.GetAdaptersAddresses( + wintypes.ULONG(AF_UNSPEC), + wintypes.ULONG(0), + None, + ctypes.byref(addressbuffer), + ctypes.byref(addressbuffersize), + ) if retval != NO_ERROR: - raise ctypes.WinError() + raise ctypes.WinError() # type: ignore # Iterate through adapters fill array - address_infos = [] + address_infos = [] # type: List[IP_ADAPTER_ADDRESSES] address_info = IP_ADAPTER_ADDRESSES.from_buffer(addressbuffer) while True: address_infos.append(address_info) @@ -113,19 +124,22 @@ break address_info = address_info.Next[0] - # Iterate through unicast addresses - result = [] + result = [] # type: List[shared.Adapter] for adapter_info in address_infos: - name = adapter_info.AdapterName + # We don't expect non-ascii characters here, so encoding shouldn't matter + name = adapter_info.AdapterName.decode() nice_name = adapter_info.Description index = adapter_info.IfIndex if adapter_info.FirstUnicastAddress: - ips = enumerate_interfaces_of_adapter(adapter_info.FriendlyName, adapter_info.FirstUnicastAddress[0]) + ips = enumerate_interfaces_of_adapter( + adapter_info.FriendlyName, adapter_info.FirstUnicastAddress[0] + ) ips = list(ips) - result.append(shared.Adapter(name, nice_name, ips, - index=index)) + result.append(shared.Adapter(name, nice_name, ips, index=index)) + elif include_unconfigured: + result.append(shared.Adapter(name, nice_name, [], index=index)) return result diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/ifaddr/netifaces.py new/ifaddr-0.2.0/ifaddr/netifaces.py --- old/ifaddr-0.1.7/ifaddr/netifaces.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ifaddr-0.2.0/ifaddr/netifaces.py 2022-06-15 20:35:56.000000000 +0200 @@ -0,0 +1,10 @@ +# netifaces compatibility layer + +import ifaddr + +from typing import List + + +def interfaces() -> List[str]: + adapters = ifaddr.get_adapters(include_unconfigured=True) + return [a.name for a in adapters] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/ifaddr/test_ifaddr.py new/ifaddr-0.2.0/ifaddr/test_ifaddr.py --- old/ifaddr-0.1.7/ifaddr/test_ifaddr.py 2020-06-06 20:24:33.000000000 +0200 +++ new/ifaddr-0.2.0/ifaddr/test_ifaddr.py 2022-06-15 22:44:01.000000000 +0200 @@ -1,7 +1,20 @@ # Copyright (C) 2015 Stefan C. Mueller import unittest + +import pytest + import ifaddr +import ifaddr.netifaces + + +try: + import netifaces +except ImportError: + skip_netifaces = True +else: + skip_netifaces = False + class TestIfaddr(unittest.TestCase): """ @@ -12,7 +25,7 @@ a sanity check for the moment. """ - def test_get_adapters_contains_localhost(self): + def test_get_adapters_contains_localhost(self) -> None: found = False adapters = ifaddr.get_adapters() @@ -22,3 +35,14 @@ found = True self.assertTrue(found, "No adapter has IP 127.0.0.1: %s" % str(adapters)) + + [email protected](skip_netifaces, reason='netifaces not installed') +def test_netifaces_compatibility() -> None: + interfaces = ifaddr.netifaces.interfaces() + assert interfaces == netifaces.interfaces() + # TODO: implement those as well + # for interface in interfaces: + # print(interface) + # assert ifaddr.netifaces.ifaddresses(interface) == netifaces.ifaddresses(interface) + # assert ifaddr.netifaces.gateways() == netifaces.gateways() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/ifaddr.egg-info/PKG-INFO new/ifaddr-0.2.0/ifaddr.egg-info/PKG-INFO --- old/ifaddr-0.1.7/ifaddr.egg-info/PKG-INFO 2020-06-06 21:13:24.000000000 +0200 +++ new/ifaddr-0.2.0/ifaddr.egg-info/PKG-INFO 2022-06-15 23:40:17.000000000 +0200 @@ -1,106 +1,143 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: ifaddr -Version: 0.1.7 +Version: 0.2.0 Summary: Cross-platform network interface and IP address enumeration library Home-page: https://github.com/pydron/ifaddr Author: Stefan C. Mueller Author-email: [email protected] License: MIT -Description: ifaddr - Enumerate network interfaces/adapters and their IP addresses - ===================================================================== - - .. image:: https://github.com/pydron/ifaddr/workflows/CI/badge.svg - :target: https://github.com/pydron/ifaddr/actions?query=workflow%3ACI+branch%3Amaster - - .. image:: https://img.shields.io/pypi/v/ifaddr.svg - :target: https://pypi.python.org/pypi/ifaddr - - .. image:: https://codecov.io/gh/pydron/ifaddr/branch/master/graph/badge.svg - :target: https://codecov.io/gh/pydron/ifaddr - - `ifaddr` is a small Python library that allows you to find all the - IP addresses of the computer. It is tested on **Linux**, **OS X**, and - **Windows**. Other BSD derivatives like **OpenBSD**, **FreeBSD**, and - **NetBSD** should work too, but I haven't personally tested those. - **Solaris/Illumos** should also work. - - This library is open source and released under the MIT License. It works - with Python 2.7 and 3.5+. - - You can install it with `pip install ifaddr`. It doesn't need to - compile anything, so there shouldn't be any surprises. Even on Windows. - - Project links: - - * `ifaddr GitHub page <https://github.com/smurn/ifaddr>`_ - * `ifaddr documentation (although there isn't much to document) <http://pythonhosted.org/ifaddr/>`_ - * `ifaddr on PyPI <https://pypi.org/project/ifaddr/>`_ - - - ---------------------- - Let's get going! - ---------------------- - - .. code-block:: python - - import ifaddr - - adapters = ifaddr.get_adapters() - - for adapter in adapters: - print("IPs of network adapter " + adapter.nice_name) - for ip in adapter.ips: - print(" %s/%s" % (ip.ip, ip.network_prefix)) - - This will print:: - - IPs of network adapter H5321 gw Mobile Broadband Driver - IP ('fe80::9:ebdf:30ab:39a3', 0L, 17L)/64 - IP 169.254.57.163/16 - IPs of network adapter Intel(R) Centrino(R) Advanced-N 6205 - IP ('fe80::481f:3c9d:c3f6:93f8', 0L, 12L)/64 - IP 192.168.0.51/24 - IPs of network adapter Intel(R) 82579LM Gigabit Network Connection - IP ('fe80::85cd:e07e:4f7a:6aa6', 0L, 11L)/64 - IP 192.168.0.53/24 - IPs of network adapter Software Loopback Interface 1 - IP ('::1', 0L, 0L)/128 - IP 127.0.0.1/8 - - You get both IPv4 and IPv6 addresses. The later complete with - flowinfo and scope_id. - - --------- - Changelog - --------- - - 0.1.7 - ----- - - * Fixed Python 3 compatibility in the examples, thanks to Tristan Stenner and Josef Schlehofer - * Exposed network interface indexes in Adapter.index, thanks to Dmitry Tantsur - * Added the license file to distributions on PyPI, thanks to Tom???? Chv??tal - * Fixed Illumos/Solaris compatibility based on a patch proposed by Jorge Schrauwen - * Set up universal wheels, ifaddr will have both source and wheel distributions on PyPI from now on - - ------------ - Alternatives - ------------ - - Alastair Houghton develops `netifaces <https://pypi.python.org/pypi/netifaces>`_ - which can do everything this library can, and more. The only drawback is that it needs - to be compiled, which can make the installation difficult. - Keywords: network interfaces,network adapters,network addresses,IP addresses Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: System :: Networking Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +License-File: LICENSE.txt + +ifaddr - Enumerate network interfaces/adapters and their IP addresses +===================================================================== + +.. image:: https://github.com/pydron/ifaddr/workflows/CI/badge.svg + :target: https://github.com/pydron/ifaddr/actions?query=workflow%3ACI+branch%3Amaster + +.. image:: https://img.shields.io/pypi/v/ifaddr.svg + :target: https://pypi.python.org/pypi/ifaddr + +.. image:: https://codecov.io/gh/pydron/ifaddr/branch/master/graph/badge.svg + :target: https://codecov.io/gh/pydron/ifaddr + +`ifaddr` is a small Python library that allows you to find all the Ethernet and +IP addresses of the computer. It is tested on **Linux**, **OS X**, and +**Windows**. Other BSD derivatives like **OpenBSD**, **FreeBSD**, and +**NetBSD** should work too, but I haven't personally tested those. +**Solaris/Illumos** should also work. + +This library is open source and released under the MIT License. It works +with Python 3.7+. + +You can install it with `pip install ifaddr`. It doesn't need to +compile anything, so there shouldn't be any surprises. Even on Windows. + +Project links: + +* `ifaddr GitHub page <https://github.com/smurn/ifaddr>`_ +* `ifaddr documentation (although there isn't much to document) <http://pythonhosted.org/ifaddr/>`_ +* `ifaddr on PyPI <https://pypi.org/project/ifaddr/>`_ + + +---------------------- +Let's get going! +---------------------- + +.. code-block:: python + + import ifaddr + + adapters = ifaddr.get_adapters() + + for adapter in adapters: + print("IPs of network adapter " + adapter.nice_name) + for ip in adapter.ips: + print(" %s/%s" % (ip.ip, ip.network_prefix)) + +This will print:: + + IPs of network adapter H5321 gw Mobile Broadband Driver + IP ('fe80::9:ebdf:30ab:39a3', 0L, 17L)/64 + IP 169.254.57.163/16 + IPs of network adapter Intel(R) Centrino(R) Advanced-N 6205 + IP ('fe80::481f:3c9d:c3f6:93f8', 0L, 12L)/64 + IP 192.168.0.51/24 + IPs of network adapter Intel(R) 82579LM Gigabit Network Connection + IP ('fe80::85cd:e07e:4f7a:6aa6', 0L, 11L)/64 + IP 192.168.0.53/24 + IPs of network adapter Software Loopback Interface 1 + IP ('::1', 0L, 0L)/128 + IP 127.0.0.1/8 + +You get both IPv4 and IPv6 addresses. The later complete with +flowinfo and scope_id. + +If you wish to include network interfaces that do not have a configured IP +addresss, pass the `include_unconfigured` parameter to `get_adapters()`. +Adapters with no configured IP addresses will have an zero-length `ips` +property. For example: + +.. code-block:: python + + import ifaddr + + adapters = ifaddr.get_adapters(include_unconfigured=True) + + for adapter in adapters: + print("IPs of network adapter " + adapter.nice_name) + if adapter.ips: + for ip in adapter.ips: + print(" %s/%s" % (ip.ip, ip.network_prefix)) + else: + print(" No IPs configured") + + +--------- +Changelog +--------- + +0.2.0 +----- + +* Added an option to include IP-less adapters, thanks to memory +* Fixed a bug where an interface's name was `bytes`, not `str`, on Windows +* Added an implementation of `netifaces.interfaces()` (available through + `ifaddr.netifaces.interfaces()`) +* Added type hints + +Backwards incompatible/breaking changes: + +* Dropped Python 3.6 support + +0.1.7 +----- + +* Fixed Python 3 compatibility in the examples, thanks to Tristan Stenner and Josef Schlehofer +* Exposed network interface indexes in Adapter.index, thanks to Dmitry Tantsur +* Added the license file to distributions on PyPI, thanks to Tom???? Chv??tal +* Fixed Illumos/Solaris compatibility based on a patch proposed by Jorge Schrauwen +* Set up universal wheels, ifaddr will have both source and wheel distributions on PyPI from now on + +------------ +Alternatives +------------ + +Alastair Houghton develops `netifaces <https://pypi.python.org/pypi/netifaces>`_ +which can do everything this library can, and more. The only drawback is that it needs +to be compiled, which can make the installation difficult. + +As of ifaddr 0.2.0 we implement the equivalent of `netifaces.interfaces()`. It's available through +`ifaddr.netifaces.interfaces()`. + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/ifaddr.egg-info/SOURCES.txt new/ifaddr-0.2.0/ifaddr.egg-info/SOURCES.txt --- old/ifaddr-0.1.7/ifaddr.egg-info/SOURCES.txt 2020-06-06 21:13:24.000000000 +0200 +++ new/ifaddr-0.2.0/ifaddr.egg-info/SOURCES.txt 2022-06-15 23:40:17.000000000 +0200 @@ -1,15 +1,17 @@ LICENSE.txt MANIFEST.in README.rst +pyproject.toml setup.cfg setup.py ifaddr/__init__.py ifaddr/_posix.py ifaddr/_shared.py ifaddr/_win32.py +ifaddr/netifaces.py +ifaddr/py.typed ifaddr/test_ifaddr.py ifaddr.egg-info/PKG-INFO ifaddr.egg-info/SOURCES.txt ifaddr.egg-info/dependency_links.txt -ifaddr.egg-info/requires.txt ifaddr.egg-info/top_level.txt \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/ifaddr.egg-info/requires.txt new/ifaddr-0.2.0/ifaddr.egg-info/requires.txt --- old/ifaddr-0.1.7/ifaddr.egg-info/requires.txt 2020-06-06 21:13:24.000000000 +0200 +++ new/ifaddr-0.2.0/ifaddr.egg-info/requires.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +0,0 @@ - -[:python_version < "3.3"] -ipaddress diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/pyproject.toml new/ifaddr-0.2.0/pyproject.toml --- old/ifaddr-0.1.7/pyproject.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/ifaddr-0.2.0/pyproject.toml 2022-06-15 23:08:21.000000000 +0200 @@ -0,0 +1,4 @@ +[tool.black] +line-length = 110 +target_version = ['py37', 'py38', 'py39', 'py310'] +skip_string_normalization = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/setup.cfg new/ifaddr-0.2.0/setup.cfg --- old/ifaddr-0.1.7/setup.cfg 2020-06-06 21:13:24.047562600 +0200 +++ new/ifaddr-0.2.0/setup.cfg 2022-06-15 23:40:17.928228400 +0200 @@ -3,9 +3,6 @@ build-dir = build/doc all_files = 1 -[wheel] -universal = True - [egg_info] tag_build = tag_date = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ifaddr-0.1.7/setup.py new/ifaddr-0.2.0/setup.py --- old/ifaddr-0.1.7/setup.py 2020-06-06 20:56:10.000000000 +0200 +++ new/ifaddr-0.2.0/setup.py 2022-06-15 23:28:13.000000000 +0200 @@ -28,28 +28,26 @@ long_description = "" setup( - name = 'ifaddr', - version = '0.1.7', + name='ifaddr', + version='0.2.0', description='Cross-platform network interface and IP address enumeration library', long_description=long_description, author='Stefan C. Mueller', author_email='[email protected]', url='https://github.com/pydron/ifaddr', - packages = find_packages(), + packages=find_packages(), + package_data={'ifaddr': ['py.typed']}, license='MIT', - install_requires = ['ipaddress;python_version<"3.3"'], classifiers=[ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Topic :: System :: Networking', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', ], keywords=['network interfaces', 'network adapters', 'network addresses', 'IP addresses'], )
