diff --git a/IPython/utils/localinterfaces.py b/IPython/utils/localinterfaces.py index 03b63dd..3e828f4 100644 --- a/IPython/utils/localinterfaces.py +++ b/IPython/utils/localinterfaces.py @@ -12,7 +12,7 @@ PUBLIC_IPS : A list of public IP addresses that point to this machine. Use these to tell remote clients where to find you. """ #----------------------------------------------------------------------------- -# Copyright (C) 2010-2011 The IPython Development Team +# Copyright (C) 2010 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. @@ -22,9 +22,13 @@ PUBLIC_IPS : A list of public IP addresses that point to this machine. # Imports #----------------------------------------------------------------------------- +import os import socket from .data import uniq_stable +from .process import get_output_error_code +from .py3compat import bytes_to_str +from .warn import warn #----------------------------------------------------------------------------- # Code @@ -53,6 +57,84 @@ def _requires_ips(f): return f(*args, **kwargs) return ips_loaded +# subprocess-parsing ip finders +class NoIPAddresses(Exception): + pass + +def _populate_from_list(addrs): + """populate local and public IPs from flat list of all IPs""" + if not addrs: + raise NoIPAddresses + + global LOCALHOST + public_ips = [] + local_ips = [] + + for ip in addrs: + local_ips.append(ip) + if not ip.startswith('127.'): + public_ips.append(ip) + elif not LOCALHOST: + LOCALHOST = ip + + if not LOCALHOST: + LOCALHOST = '127.0.0.1' + local_ips.insert(0, LOCALHOST) + + local_ips.extend(['0.0.0.0', '']) + + LOCAL_IPS[:] = uniq_stable(local_ips) + PUBLIC_IPS[:] = uniq_stable(public_ips) + +def _load_ips_ifconfig(): + """load ip addresses from `ifconfig` output (posix)""" + + out, err, rc = get_output_error_code('ifconfig') + if rc: + # no ifconfig, it's usually in /sbin and /sbin is not on everyone's PATH + out, err, rc = get_output_error_code('/sbin/ifconfig') + if rc: + raise IOError("no ifconfig: %s" % err) + + lines = bytes_to_str(out).splitlines() + addrs = [] + for line in lines: + blocks = line.lower().split() + if blocks[0] == 'inet': + addrs.append(blocks[1]) + _populate_from_list(addrs) + + +def _load_ips_ip(): + """load ip addresses from `ip addr` output (Linux)""" + out, err, rc = get_output_error_code('ip addr') + if rc: + raise IOError("no ip: %s" % err) + + lines = bytes_to_str(out).splitlines() + addrs = [] + for line in lines: + blocks = line.lower().split() + if blocks[0] == 'inet': + addrs.append(blocks[1].split('/')[0]) + _populate_from_list(addrs) + + +def _load_ips_ipconfig(): + """load ip addresses from `ipconfig` output (Windows)""" + out, err, rc = get_output_error_code('ipconfig') + if rc: + raise IOError("no ipconfig: %s" % err) + + lines = bytes_to_str(out).splitlines() + addrs = ['127.0.0.1'] + for line in lines: + line = line.lower().split() + if line[:2] == ['ipv4', 'address']: + addrs.append(line.split()[-1]) + _populate_from_list(addrs) + + def _load_ips_netifaces(): """load ip addresses with netifaces""" import netifaces @@ -81,6 +163,7 @@ def _load_ips_netifaces(): LOCAL_IPS[:] = uniq_stable(local_ips) PUBLIC_IPS[:] = uniq_stable(public_ips) + def _load_ips_gethostbyname(): """load ip addresses with socket.gethostbyname_ex @@ -104,7 +187,7 @@ def _load_ips_gethostbyname(): finally: PUBLIC_IPS[:] = uniq_stable(PUBLIC_IPS) LOCAL_IPS.extend(PUBLIC_IPS) - + # include all-interface aliases: 0.0.0.0 and '' LOCAL_IPS.extend(['0.0.0.0', '']) @@ -125,17 +208,42 @@ def _load_ips(): This function will only ever be called once. - It will use netifaces to do it quickly if available, - otherwise it will fallback on socket.gethostbyname_ex, which can be slow. + It will use netifaces to do it quickly if available. + Then it will fallback on parsing the output of ifconfig / ip addr / ipconfig, as appropriate. + Finally, it will fallback on socket.gethostbyname_ex, which can be slow. """ + try: + # first priority, use netifaces try: - _load_ips_netifaces() + return _load_ips_netifaces() except ImportError: - _load_ips_gethostbyname() - except Exception: + pass + + # second priority, parse subprocess output (how reliable is this?) + + if os.name == 'nt': + try: + return _load_ips_ipconfig() + except (IOError, NoIPAddresses): + pass + else: + try: + return _load_ips_ifconfig() + except (IOError, NoIPAddresses): + pass + try: + return _load_ips_ip() + except (IOError, NoIPAddresses): + pass + + # lowest priority, use gethostbyname + + return _load_ips_gethostbyname() + except Exception as e: # unexpected error shouldn't crash, load dumb default values instead. - _load_ips_dumb() + warn("Unexpected error discovering local network interfaces: %s" % e) + _load_ips_dumb() @_requires_ips