I wrote this in order to get a list of network interfaces on Linux. In the past, I have used publicly available methods from, say, ActiveState, which uses ioctl and was annoying to get working under Python 3. This version simply uses regular expressions on the output of ifconfig.
The documentation style used here follows the numpy documentation guide.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Get a list of network interfaces on Linux.
This code is compatible with Python versions 2 and 3.
"""
from collections import namedtuple
import re
import subprocess
def get_interfaces(external=False, ip=False):
"""
Get a list of network interfaces on Linux.
To access the MAC address and/or the IP address, set the relevant keyword
arguments to True.
Parameters
----------
external : bool, optional
Only show external interfaces, and ignore virtual (e.g. loopback)
devices, and return their MAC addresses.
ip : bool, optional
Only show interfaces which are UP and have an IP address, and return
their IPv4 addresses.
Returns
-------
interfaces
list of str containing the interface name by default, or list of
namedtuple containing `name`, `mac`, and `ip` as requested.
Examples
--------
>>> print(get_interfaces())
['eth0', 'lo', 'wlan0']
>>> print(get_interfaces(external=True))
[Interface(name='eth0', mac='a0:b1:c2:d3:e4:f5'), Interface(name='wlan0', ma
c='f5:e4:d3:c2:b1:a0')]
>>> print(get_interfaces(ip=True))
[Interface(name='lo', ip='127.0.0.1'), Interface(name='wlan0', ip='192.168.1
1.2')]
>>> print(get_interfaces(external=True, ip=True))
[Interface(name='wlan0', mac='f5:e4:d3:c2:b1:a0', ip='192.168.11.2')]
"""
name_pattern = "^(\w+)\s"
mac_pattern = ".*?HWaddr[ ]([0-9A-Fa-f:]{17})" if external else ""
ip_pattern = ".*?\n\s+inet[ ]addr:((?:\d+\.){3}\d+)" if ip else ""
pattern = re.compile("".join((name_pattern, mac_pattern, ip_pattern)),
flags=re.MULTILINE)
ifconfig = subprocess.check_output("ifconfig").decode()
interfaces = pattern.findall(ifconfig)
if external or ip:
Interface = namedtuple("Interface", "name {mac} {ip}".format(
mac="mac" if external else "",
ip="ip" if ip else ""))
return [Interface(*interface) for interface in interfaces]
else:
return interfaces
if __name__ == "__main__":
interfaces = get_interfaces(external=True, ip=True)
for interface in interfaces:
print("{name}: {ip}".format(name=interface.name, ip=interface.ip))
2 Answers 2
That is a huge docstring, it's longer than the code itself. You should read Python's PEP about how to write good docstrings.
For a start a docstring shouldn't contain examples, you can put that as a regular comment in the script if you want. And you don't need to be so detailed and explicit about the type and optional nature of your parameters. I would take some information out of your parameters and returns sections and just fold it back into your opening section. Docstrings should be relatively concise and mostly add context and explanation.
A user can easily see the default parameters get_interfaces(external=False, ip=False)
, which reveals that they're optional and booleans. All you need to explain is what setting them will affect.
-
2\$\begingroup\$ Thanks for your comment. The documentation style I use in real life is the numpy documentation standard (example), because it is useful for scientific documentation etc. (I used to use Google's Python style guide.) But with numpy, this is common. For instance, numpy's mean has 79 lines of docstring vs 9 of code. \$\endgroup\$Jean Nassar– Jean Nassar2015年09月12日 13:58:11 +00:00Commented Sep 12, 2015 at 13:58
-
\$\begingroup\$ @JeanNassar Ah, I'm not familiar with that style, I personally stick with the PEP I linked. If you run your script with
help(get_interfaces)
it prints a big long string, which seems unwieldy to me, but it is dependent on how it'll be used. \$\endgroup\$SuperBiasedMan– SuperBiasedMan2015年09月12日 14:08:38 +00:00Commented Sep 12, 2015 at 14:08 -
1\$\begingroup\$ Ah, I see how this can be a problem. I REPL with ipython (both qtconsole and notebook). Typing
get_interfaces(
would show a hovering tooltip-style box with the signature, as well as the docstring. In this case, the docstring goes all the way to the first line ofReturns interfaces
(i.e.,or list of
). Which is good enough for most users. If I need more help, I type a question mark, and I get aless
-like window with the documentation. If you use Spyder as your IDE, it's even better because everything is formatted beautifully, including headers and syntax highlighting etc. \$\endgroup\$Jean Nassar– Jean Nassar2015年09月12日 14:54:33 +00:00Commented Sep 12, 2015 at 14:54
Parsing the output of ifconfig
is problematic: different versions have slightly different output formats, and it's hard to make code sufficiently flexible enough to handle them all. And it's impossible, in general, to also handle future output formats.
You're probably better off using ip addr
, which has a more consistent and predictable output format.
The doctest
cases in the docstring aren't very good unit tests - they might be correct for your machine at the time of writing, but they are too fragile, as they depend on environment outside your control.
annoying to get working under Python 3
? Because the IOCTL method is more efficient (after all it doesn't need to create another process), so if speed is very important... \$\endgroup\$ifname[:15]
on line 10 had to be changed tostr.encode(ifname)
to get it to work. You are correct about the speed; in this case, this function would run once at the start of two scripts when the robot was to be started, so it was not an issue. \$\endgroup\$ipconfig
is deprecated on Linux. Better use theip
command instead. \$\endgroup\$