From 6dcc15eecc5d862c1f0c14a57ed149f37a862d46 2013-09-29 21:20:21 From: MinRK Date: 2013-09-29 21:20:21 Subject: [PATCH] use netifaces for faster IPython.utils.localinterfaces --- diff --git a/IPython/utils/localinterfaces.py b/IPython/utils/localinterfaces.py index e67a35f..03b63dd 100644 --- a/IPython/utils/localinterfaces.py +++ b/IPython/utils/localinterfaces.py @@ -5,6 +5,8 @@ LOCALHOST : The loopback interface, or the first interface that points to this machine. It will *almost* always be '127.0.0.1' LOCAL_IPS : A list of IP addresses, loopback first, that point to this machine. + This will include LOCALHOST, PUBLIC_IPS, and aliases for all hosts, + such as '0.0.0.0'. PUBLIC_IPS : A list of public IP addresses that point to this machine. Use these to tell remote clients where to find you. @@ -31,7 +33,7 @@ from .data import uniq_stable LOCAL_IPS = [] PUBLIC_IPS = [] -LOCALHOST = '127.0.0.1' +LOCALHOST = '' def _only_once(f): """decorator to only run a function once""" @@ -51,17 +53,45 @@ def _requires_ips(f): return f(*args, **kwargs) return ips_loaded -@_only_once -def _load_ips(): - """load the IPs that point to this machine +def _load_ips_netifaces(): + """load ip addresses with netifaces""" + import netifaces + global LOCALHOST + local_ips = [] + public_ips = [] - This function will only ever be called once. + # list of iface names, 'lo0', 'eth0', etc. + for iface in netifaces.interfaces(): + # list of ipv4 addrinfo dicts + ipv4s = netifaces.ifaddresses(iface).get(netifaces.AF_INET, []) + for entry in ipv4s: + addr = entry.get('addr') + if not addr: + continue + if not (iface.startswith('lo') or addr.startswith('127.')): + public_ips.append(addr) + elif not LOCALHOST: + LOCALHOST = addr + local_ips.append(addr) + if not LOCALHOST: + # we never found a loopback interface (can this ever happen?), assume common default + 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_gethostbyname(): + """load ip addresses with socket.gethostbyname_ex + + This can be slow. """ global LOCALHOST try: LOCAL_IPS[:] = socket.gethostbyname_ex('localhost')[2] except socket.error: - pass + # assume common default + LOCAL_IPS[:] = ['127.0.0.1'] try: hostname = socket.gethostname() @@ -82,6 +112,32 @@ def _load_ips(): LOCALHOST = LOCAL_IPS[0] +def _load_ips_dumb(): + """Fallback in case of unexpected failure""" + global LOCALHOST + LOCALHOST = '127.0.0.1' + LOCAL_IPS[:] = [LOCALHOST, '0.0.0.0', ''] + PUBLIC_IPS[:] = [] + +@_only_once +def _load_ips(): + """load the IPs that point to this machine + + 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. + """ + try: + try: + _load_ips_netifaces() + except ImportError: + _load_ips_gethostbyname() + except Exception: + # unexpected error shouldn't crash, load dumb default values instead. + _load_ips_dumb() + + @_requires_ips def local_ips(): """return the IP addresses that point to this machine"""