Code Review Stack Exchange is a question and answer site for peer programmer code reviews. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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))
share|improve this question
1  
Welcome to Code Review! Please include a description at the top before your code, briefly explaining what your code does. Without that, not only it's harder to review, but the summary of your question on the front page shows up as "..." instead of something interesting. See also this meta post to make the most out of asking questions here. – janos Sep 12 '15 at 11:16
    
What do you mean by 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... – user45891 Sep 12 '15 at 15:01
    
It required an understanding of what was going on under the hood to get it working. For example, the code linked above would not work under Python 3 as is because of the different treatment of bytes in python3. In this case, ifname[:15] on line 10 had to be changed to str.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. – Jean Nassar Sep 12 '15 at 15:33

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.

share|improve this answer
2  
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. – Jean Nassar Sep 12 '15 at 13:58
    
@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. – SuperBiasedMan Sep 12 '15 at 14:08
1  
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 of Returns 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 a less-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. – Jean Nassar Sep 12 '15 at 14:54

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.