# coding: utf-8
'''This module provides 2 classes and 4 functions to,
obtain, check and convert IP and MAC addresses.
Class IP and MAC represent an IP respectively a
MAC address. Instances of IP and MAC classes
are immutable.
The IP class provides properties to check whether
the address is link-local, loopback, multicast,
a network address or network mask, private (non-
routable), TEST-NET, THIS-HOST or THIS-NET.
Functions getIPs and getMACs collect the available
IP respectively MAC addresses from the underlying
system. Both functions provide an optional argument
to exclude specific IP or MAC instances.
Functions isIP and isMAC can be used to check an
address. The return value is an IP respectively
MAC instance if the given address is valid. None
is returned otherwise.
The IPv6 format is not supported, only IPv4. For
both, see module ipaddress (Python 3 only) at
<https://docs.python.org/3/library/ipaddress.html>
Run python ipmac.py to see examples and the test
results. Use python ipmac.py -debug to include
debug output.
The core code is copied from standard Python 2.6
and 3.1 module Lib/uuid.py and then modified to:
- use immutable classes IP and MAC like UUID,
- handle any number of IP and MAC addresses,
- collect IP and MAC addresses from several
additional sources,
- locate external programs only once,
- use env and grep with ifconfig on *nixes,
- support Python versions 2.4 thru 3.5, both
32- and 64-bit.
Tested on CentOS 4 (Intel), MacOS X 10.4.11 Tiger
(Intel), MacOS X 10.3.9 Panther (PowerPC), RedHat 3
(Opteron), Solaris 10 (Opteron), Windows XP SP2 and
SP3 and Windows Server 2003 R2 with 32-bit Python
2.4, 2.5, 2.6, 3.0 and/or 3.1, on CentOS 4 and 5
(Intel) with 64-bit Python 2.4 and 2.6, on MacOS X
10.11.5 with 64-bit Python 2.7.10 and 3.5.1 and on
iOS 9.3.2 with 64-bit Pythonista 2.1 (on iPad).
This module does not support Python 2.3 and older
and has not been tested on platforms other than
the ones listed above.
'''
import os
import socket
import struct
import sys
try:
import ctypes
except ImportError:
ctypes = None
__version__ = '16.07.07' # '10.4.22'
__all__ = ('getIPs', 'IP', 'isIP',
'getMACs', 'MAC', 'isMAC')
if sys.hexversion < 0x3000000:
_byte = ord # 2.x chr to integer
_Ints = (int, long)
_Strs = basestring
else:
_byte = int # 3.x byte to integer
_Ints = int
_Strs = str # (str, unicode)?
_b08 = 1 << 8
_b32 = 1 << 32
_b48 = 1 << 48
_debugf = None # or printf
_exes = {} # executables cache
_IPsep = '.'
_MACsep = ':'
def _bytes2int(bytes, byte=_byte):
# convert bytes to int.
i = 0
for b in bytes:
i = (i << 8) + byte(b)
return i
def _hostname_ips():
# get IP addresses from hostname.
h = socket.gethostname()
try:
for a in socket.gethostbyname_ex(h)[2]:
yield isIP(a)
except AttributeError: # no gethostbyname_ex()
yield isIP(socket.gethostbyname(h))
def _which(exe, dirs):
# return full path of an executable.
p = _exes.get(exe, None)
if p is None:
for d in dirs:
p = os.path.join(d, exe)
if os.access(p, os.X_OK):
break
else:
p = '' # no such exe
_exes[exe] = p
if _debugf:
_debugf('_which(%r): %r', exe, p)
return p
if sys.platform.startswith('win'):
# On Windows prior to 2000, UuidCreate gives a UUID containing
# the MAC address. On Windows 2000 and later, UuidCreate makes
# a random UUID and UuidCreateSequential gives a UUID containing
# the MAC address. These routines are provided by the RPC runtime.
# NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last
# 6 bytes returned by UuidCreateSequential are fixed, they don't
# appear to bear any relationship to the MAC address of any network
# device on the box.
def _windll_macs():
# get the MAC address from UuidCreate*.
try:
b = ctypes.windll.rpcrt4
f = getattr(b, 'UuidCreateSequential',
getattr(b, 'UuidCreate', None))
if f:
b = ctypes.create_string_buffer(16)
if f(b) == 0:
yield MAC(_bytes2int(b.raw[-6:]))
except (AttributeError, TypeError, ValueError):
pass
def _netbios_macs():
# get MAC addresses from NetBIOS. See
# <http://support.microsoft.com/kb/118623>.
try:
import netbios
from win32wnet import Netbios
ncb = netbios.NCB()
ncb.Command = netbios.NCBENUM
ncb.Buffer = a = netbios.LANA_ENUM()
a._pack()
if Netbios(ncb) == 0:
a._unpack()
for i in range(a.length):
ncb.Reset()
ncb.Command = netbios.NCBRESET
ncb.Lana_num = _byte(a.lana[i])
if Netbios(ncb) == 0:
ncb.Reset()
ncb.Command = netbios.NCBASTAT
ncb.Lana_num = _byte(a.lana[i])
ncb.Callname = '*'.ljust(16)
ncb.Buffer = s = netbios.ADAPTER_STATUS()
if Netbios(ncb) == 0:
s._unpack()
yield isMAC(_bytes2int(s.adapter_address[:6]))
except (ImportError, AttributeError):
pass
_dirs = None # ('c:\...', ...)
def _ipconfig(tag, Class):
# get IP or MAC addresses from ipconfig /all.
global _dirs
if _dirs is None:
try: # check system directory first
f = ctypes.windll.kernel32.GetSystemDirectoryA
b = ctypes.create_string_buffer(300)
f(b, 300)
t = (b.value.decode('mbcs'),)
except (ImportError, AttributeError):
t = ()
_dirs = t + (r'c:\windows\system32',
r'c:\winnt\system32', '')
m, c = [], _which('ipconfig.exe', _dirs)
if c:
for t in os.popen(c + ' /all'):
if tag in t:
try:
t = t.split(':', 1)[1].strip()
m.append(Class(t))
except (IndexError, TypeError, ValueError):
pass
return m
def _ipconfig_ips():
# get IP addresses from ipconfig.
return _ipconfig('IP', IP)
_MACsep = '-'
def _ipconfig_macs():
# get MAC addresses from ipconfig.
return _ipconfig(_MACsep, MAC)
_all_ips = (_hostname_ips, _ipconfig_ips)
_all_macs = (_windll_macs, _netbios_macs, _ipconfig_macs)
else: # *nix
_uuid_generate_time = None
# If ctypes is available, use it to find routines for
# UUID generation (making this module thread-UNsafe!)
# Thanks to Thomas Heller for ctypes and for his help
# with its use here. See Lib/uuid.py for more.
try:
import ctypes.util
# The uuid_generate_* routines are provided by
# libuuid on at least Linux and FreeBSD and by
# libc on MacOS X.
for lib in ('uuid', 'c'):
try:
lib = ctypes.CDLL(ctypes.util.find_library(lib))
_uuid_generate_time = getattr(lib, 'uuid_generate_time',
_uuid_generate_time)
except (TypeError, AttributeError):
lib = None
del lib
except (AttributeError, ImportError):
pass
_bins = ('/bin', '/usr/bin', '')
_sbins = ('/sbin', '/usr/sbin', '')
def _run(cmd, opts, tag, offset, Class, cut_addr_=False):
# get IP or MAC addresses from cmd output.
c = '%s %s 2>/dev/null' % (cmd, opts)
e = _which('env', _bins)
if e: # LC_ALL=C gets English output
c = '%s LC_ALL=C %s' % (e, c)
e = _which('grep', _bins)
if e: # pipe to case-insensitve grep
c = '%s | %s -i %s' % (c, e, tag) # tag.strip('()')
if _debugf:
_debugf('_run: %s', c)
m = []
for t in os.popen(c):
t = t.lower().split()
try:
t = t[offset(t.index(tag))]
# cut prefix from Linux inet addr:<IP>
if cut_addr_ and t.startswith('addr:'):
t = t[5:]
m.append(Class(t))
except (IndexError, TypeError, ValueError):
pass
return m
def _ifconfig_ips():
# get IP addresses from ifconfig on MacOS ('-a'), Linux ('-a'
# or ''), Tru64 ('-av'), Solaris ('-a') but not all *nixes.
m, c = [], _which('ifconfig', _sbins)
if c:
for a in ('-a', '-av'):
m = _run(c, a, 'inet', lambda i: i+1, IP, _cut_addr)
if m:
break
return m
def _macs(cmd, opts, tag, offset):
if cmd: # get MAC addresses from cmd ...
return _run(cmd, opts, tag, offset, MAC, False)
return []
def _ifconfig_macs():
# get MAC addresses from ifconfig on MacOS ('-a'), Linux ('-a'
# or ''), Tru64 ('-av'), but not all *nixes nor Solaris.
m, c = [], _which('ifconfig', _sbins)
for a, t in (('-a', 'ether'), # MacOS
('-a', 'hwaddr'), # Linux
('-av', 'ether'),
('-av', 'hwaddr')):
m = _macs(c, a, t, lambda i: i + 1)
if m:
break
return m
def _arp_macs():
# get MAC addresses from arp on Solaris (and MacOS).
m, c = [], _which('arp', _sbins)
for a in getIPs():
# using (c, '-an', '(%s)' % a, lambda i: i+2)
# works on MacOS X, but then replace tag with
# tag.strip('()') in the _grep format above
m.extend(_macs(c, '-an', a, lambda i: -1)) # PYCHOK false
return m
def _lanscan_macs():
# this might get MAC addresses on HP-UX.
c = _which('lanscan', _sbins)
return _macs(c, '-ai', 'lan0', lambda i: 0) # PYCHOK false
def _uuid_macs():
# If the system provides UUID generator(s),
# use those to extract the MAC address.
# NOTE: the uuid's returned by iOS 9.3.2 on
# iPad/iPhone are not persistent and may or
# may not reflect the device MAC address.
try:
import uuid
u = uuid.uuid1(clock_seq=0)
yield isMAC(_bytes2int(u.bytes_le[-6:]))
except (AttributeError, ImportError):
pass
if _uuid_generate_time:
b = ctypes.create_string_buffer(16)
if _uuid_generate_time(b) == 0:
yield isMAC(_bytes2int(b.raw[-6:]))
try:
# Linux (and perhaps AIX, see <http://www.ibm.com/
# developerworks/aix/library/au-ioctl-socket.html>)
from fcntl import ioctl
# see e.g. CentOS 4 /usr/include/linux/sockios.h or
# <http://www.kernel-api.org/docs/online/1.2.13/df/
# da2/sockios_8h-source.html>
_SIOCGIFADDR = 0x8915 # PA address
_SIOCGIHWADDR = 0x8927 # hardware address
def _ioctl_(SIOCG, Class):
# see <http://code.activestate.com/recipes/439094
# -get-the-ip-address-associated-with-a-network-inter/>
m, c = [], 24 - Class.size
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for d in range(32):
try: # Linux ('eth'), MacOS ('en')
d = struct.pack('256s', 'eth' + str(d))
b = ioctl(s.fileno(), SIOCG, d)
# 32-bit IP addr is in bytes b[20:24]
# in network byte order, get IP addr
# str from socket.inet_ntoa(b[20:24]),
# 48-bit MAC addr is in bytes b[18:24]
m.append(Class(_bytes2int(b[c:24])))
except (IOError, ValueError, TypeError):
if m:
break
return m
def _ioctl_ips():
return _ioctl_(_SIOCGIFADDR, IP)
def _ioctl_macs():
return _ioctl_(_SIOCGIHWADDR, MAC)
except ImportError:
_ioctl_ips = _ioctl_macs = lambda: ()
_cut_addr = False
if sys.platform.startswith('darwin'):
_all_ips = (_hostname_ips, _ifconfig_ips)
_all_macs = (_uuid_macs, _ifconfig_macs)
elif sys.platform.startswith('iphone'):
_all_ips = (_hostname_ips,)
_all_macs = (_uuid_macs,)
elif sys.platform.startswith('linux'):
_cut_addr = True
_all_ips = (_hostname_ips, _ioctl_ips, _ifconfig_ips)
_all_macs = (_uuid_macs, _ioctl_macs, _ifconfig_macs)
elif sys.platform.startswith('sunos'):
_all_ips = (_hostname_ips, _ifconfig_ips)
_all_macs = (_uuid_macs, _arp_macs)
else: # not tested!
_all_ips = (_hostname_ips, _ifconfig_ips)
_all_macs = (_uuid_macs, _ifconfig_macs, _lanscan_macs)
# see Python recipe <http://code.activestate.com/recipes/66517
# -ip-address-conversion-functions-with-the-builtin-s/?in=user-97991>
_NET = '!L' # network byte order (big-endian)
def _ip42int(addr, *unused):
# convert a dot-quad IP addr to an _NET int
# (raises socket.error for invalid addr)
return struct.unpack(_NET, socket.inet_aton(addr))[0]
def _ip42str(ip):
# convert an int to dot-quad IP string
return socket.inet_ntoa(struct.pack(_NET, ip))
def _ip4int(ip):
# check an int IP (avoid the DeprecationWarning:
# struct integer overflow masking is deprecated)
if 0 <= ip < _b32: # IP
return _ip42int(_ip42str(ip))
raise ValueError('IP out of range: %r' % ip)
def _ip4masks(bits):
# return an IP net and host mask
if 0 < bits <= 32:
b = 1 << (32 - bits)
return (_b32 - b), (b - 1)
raise ValueError('invalid IP mask width: %r' % (bits,))
def _ip4isnet(ip, net):
# check whether an int IP is in the given net
a, m, _ = _ip4netuple(net)
return (ip & m) == a # and ip != a?
_ip4nets = {} # IP net (addr, mask) cache
# see RFCs and <http://code.google.com/p/ipaddr-py/>
_IP4NET_LINKLOCAL = '169.254.0.0/16' # RFC 3927
_IP4NET_LOOPBACK = '127.0.0.0/8' # RFC 5735
_IP4NET_MULTICAST = '224.0.0.0/4' # RFC 3171
_IP4NET_PRIVATE_A = '10.0.0.0/8' # RFC 1918
_IP4NET_PRIVATE_B = '172.16.0.0/12' # RFC 1918
_IP4NET_PRIVATE_C = '192.168.0.0/16' # RFC 1918
_IP4NET_TEST_NET = '192.0.2.0/24' # RFC 5735
_IP4NET_THIS_HOST = '0.0.0.0/32' # RFC 5735
_IP4NET_THIS_NET = '0.0.0.0/8' # RFC 5735
def _ip4isnet_(addr_mask_cast, ip):
# check network addr, mask or cast match
return ip in [_ip4netuple(n)[addr_mask_cast] for
n in (_IP4NET_LINKLOCAL,
_IP4NET_LOOPBACK,
_IP4NET_MULTICAST,
_IP4NET_PRIVATE_A,
_IP4NET_PRIVATE_B,
_IP4NET_PRIVATE_C,
_IP4NET_TEST_NET,
_IP4NET_THIS_HOST,
_IP4NET_THIS_NET)]
def _ip4net(ip):
# return containing IP net or an empty string
for n in (_IP4NET_PRIVATE_A,
_IP4NET_PRIVATE_B,
_IP4NET_PRIVATE_C,
_IP4NET_TEST_NET,
_IP4NET_LOOPBACK,
_IP4NET_LINKLOCAL,
_IP4NET_MULTICAST,
_IP4NET_THIS_HOST,
_IP4NET_THIS_NET):
if _ip4isnet(ip, n):
return n
return '' # no network
def _ip4netuple(net):
# return an IP net addr, mask and cast
t = _ip4nets.get(net, None)
if t is None:
a, b = net.split('/')
m, b = _ip4masks(int(b))
a = _ip42int(a) & m
t = a, m, (a | b)
_ip4nets[net] = t
if _debugf:
_debugf('_ip4nets[%r]: (addr 0x%08x, mask 0x%08x, cast 0x%08x)', net, *t)
return t
def _mac2int(addr, sep=_MACsep):
# convert a MAC str to an int
h = addr.split(sep)
if len(h) > 4:
i = 0
for b in h:
b = int(b, 16)
if 0 <= b < _b08:
i = (i << 8) + b
else:
break # raise
else:
if 0 < i < _b48:
return i
raise ValueError('invalid MAC address: %r' % (addr,))
def _macint(mac):
# check an int MAC
if 0 < mac < _b48:
return mac
raise ValueError('MAC out of range: %r' % (mac,))
class _Base(object):
'''Base class for IP and MAC classes.
'''
_int = 0 # value as int
def __init__(self, addr, addr2int, addrint):
try:
if isinstance(addr, self.__class__):
i = addr._int
elif isinstance(addr, _Strs):
if self.sep in addr: # PYCHOK false
i = addr2int(addr, self.sep) # PYCHOK false
elif addr[:2].lower() == '0x':
i = addrint(int(addr, 16))
else:
i = addrint(int(addr, 10))
elif isinstance(addr, _Ints):
i = addrint(addr)
else:
raise TypeError('%s invalid: %s' %
(self.__class__.__name__, type(addr)))
self.__dict__['_int'] = i # immutable
except (socket.error, ValueError):
raise ValueError('%s invalid: %r' %
(self.__class__.__name__, addr))
if _debugf:
_debugf('_Base(%r): %r', addr, self)
# see Python 2.6 and 3.1 Lib/uuid.py
def __cmp__(self, other): # 2.x
if isinstance(other, self.__class__):
return cmp(self._int, other._int)
return NotImplemented
def __eq__(self, other): # 3.x
if isinstance(other, self.__class__):
return self._int == other._int
return NotImplemented
def __ne__(self, other): # 3.x
if isinstance(other, self.__class__):
return self._int != other._int
return NotImplemented
# Q. What's the value of being able to sort UUIDs?
# A. Use them as keys in a B-Tree or similar mapping.
def __lt__(self, other): # 3.x
if isinstance(other, self.__class__):
return self._int < other._int
return NotImplemented
def __gt__(self, other): # 3.x
if isinstance(other, self.__class__):
return self._int > other._int
return NotImplemented
def __le__(self, other): # 3.x
if isinstance(other, self.__class__):
return self._int <= other._int
return NotImplemented
def __ge__(self, other): # 3.x
if isinstance(other, self.__class__):
return self._int >= other._int
return NotImplemented
def __hash__(self):
return hash(self._int)
def __hex__(self): # 2.6+
return self.HEX % (self._int,) # PYCHOK false
hex = property(__hex__, doc='This address as hexadecimal string, see the .HEX attribute.')
def __int__(self): # 2.6+
return self._int
int = property(__int__, doc='This address as an int.')
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, str(self))
def __setattr__(self, attr, value): # PYCHOK expected
raise TypeError('%s objects are immutable' % (self.__class__.__name__,))
class IP(_Base):
'''IP address class.
'''
HEX = '0x%08x' # hex format (or '%#010x')
sep = _IPsep
size = 4 # bytes
def __init__(self, addr):
'''Construct an IP instance from a dot-quad string, a hexa/decimal
string, an integer value or from another IP instance.
Raises a TypeError or ValueError for an invalid address.
'''
_Base.__init__(self, addr, _ip42int, _ip4int)
def __linklocal(self):
return _ip4isnet(self._int, _IP4NET_LINKLOCAL)
islinklocal = property(__linklocal, doc='True if an IP link-local address per RFC 3927.')
def __loopback(self):
return _ip4isnet(self._int, _IP4NET_LOOPBACK)
isloopback = property(__loopback, doc='True if an IP loopback address per RFC 5735.')
def __multicast(self):
return _ip4isnet(self._int, _IP4NET_MULTICAST)
ismulticast = property(__multicast, doc='True if an IP multicast (class D) address per RFC 3171.')
def __netcast(self):
return _ip4isnet_(2, self._int)
isnetcast = property(__netcast, doc='True if an IP network broadcast.')
def __netmask(self):
return _ip4isnet_(1, self._int)
isnetmask = property(__netmask, doc='True if an IP network mask.')
def __network(self):
return _ip4isnet_(0, self._int)
isnetwork = property(__network, doc='True if an IP network address.')
def __private(self):
return self.isprivateA or \
self.isprivateB or \
self.isprivateC
isprivate = property(__private, doc='True if a private (non-routable) IP address per RFC 1918.')
def __privateA(self):
return _ip4isnet(self._int, _IP4NET_PRIVATE_A)
isprivateA = property(__privateA, doc='True if a private (class A) IP address per RFC 1918.')
def __privateB(self):
return _ip4isnet(self._int, _IP4NET_PRIVATE_B)
isprivateB = property(__privateB, doc='True if a private (class B) IP address per RFC 1918.')
def __privateC(self):
return _ip4isnet(self._int, _IP4NET_PRIVATE_C)
isprivateC = property(__privateC, doc='True if a private (class C) IP address per RFC 1918.')
def __reserved(self):
return self.isloopback or \
self.islinklocal or \
self.ismulticast or \
self.isnetmask or \
self.isnetwork
isreserved = property(__reserved, doc='True if link-local, loopback, multicast, netmask or network.')
def __testnet(self):
return _ip4isnet(self._int, _IP4NET_TEST_NET)
istestnet = property(__testnet, doc='True if a TEST-NET (class E) IP address per RFC 5735.')
def __thishost(self):
return _ip4isnet(self._int, _IP4NET_THIS_HOST)
isthishost = property(__thishost, doc='True if a THIS-HOST IP address per RFC 5735.')
def __thisnet(self):
return _ip4isnet(self._int, _IP4NET_THIS_NET)
isthisnet = property(__thisnet, doc='True if a THIS-NET IP address per RFC 5735.')
def __net(self):
return _ip4net(self._int)
net = property(__net, doc='The containing IP network or an empty string.')
def __str__(self):
return _ip42str(self._int)
str = property(__str__, doc='This IP address as a dot-quad string.')
def __ton(self):
return self._int # is in network byte order
ton = property(__ton, doc='This IP address as an int in network byte order.')
def __toh(self):
return socket.ntohl(self._int)
toh = property(__toh, doc='This IP address as an int in host byte order.')
class MAC(_Base):
'''MAC address class.
'''
HEX = '0x%012x' # hex format (or '%#014x')
sep = _MACsep
size = 6 # bytes
def __init__(self, addr):
'''Construct a MAC instance from an address string, a hexa/decimal
string, an integer value or from another MAC instance.
Raises a TypeError or ValueError for an invalid address.
'''
_Base.__init__(self, addr, _mac2int, _macint)
def __str__(self):
h = '%012x' % self._int
return self.sep.join([h[d:d+2] for d in range(0, len(h), 2)])
str = property(__str__, doc='''This MAC address as a 12-digit hexadecimal
string, separated by the MAC.sep attribute.''')
def _gets(gens, exclude):
'''Return all IP or MAC addresses subject to exclude.
'''
x = exclude
if x is None:
x = lambda _: False # PYCHOK expected
elif not hasattr(x, '__call__'):
raise TypeError('exclude not callable: %r' % (x,))
t = []
for g in gens:
for a in g():
if _debugf:
_debugf('%s yields: %r', g.__name__, a)
if a and a not in t and not x(a):
t.append(a)
t = tuple(t)
if _debugf:
_debugf('_gets(exclude=%r): %r', exclude, t)
return t
def getIPs(exclude=lambda ip: ip.isreserved):
'''Get all unique IP addresses as a tuple of IP instances.
By default, all reserved IP addresses are excluded. Set
optional argument exclude=None to include all IP addresses
or provide a callable returning True or False to exclude
respectively include a given IP instance.
'''
return _gets(_all_ips, exclude)
def getMACs(exclude=None):
'''Get all unique MAC addresses as a tuple of MAC instances.
By default, optional argument exclude=None to include all
MAC addresses. Provide a callable returning True or False
to exclude respectively include a given MAC instance.
'''
return _gets(_all_macs, exclude)
def isIP(addr):
'''Return an IP instance for a valid IP address, None otherwise.
'''
try:
return IP(addr)
except (TypeError, ValueError):
return None
def isMAC(addr):
'''Return a MAC instance for a valid MAC address, None otherwise.
'''
try:
return MAC(addr)
except (TypeError, ValueError):
return None
if __name__ == '__main__':
_argv0 = sys.argv[0] + ' '
def printf(fmt, *args):
# Formatted print.
print(_argv0 + fmt % args)
if sys.argv[-1] in ('-debug', '-d'):
_debugf = printf
printf('%s using Python %s on %s\n', __version__,
sys.version.split()[0], sys.platform)
def test(Class, gets):
n = Class.__name__
global e, t
e = t = 0
def check(value, *expected):
global e, t
t += 1
if value not in expected:
x = ', '.join([repr(x) for x in expected])
printf('%s error: %r, expected %s',
n, value, x)
e += 1
s = gets()
printf('get%ss(): %r', n, s)
check(gets(), s)
for a in s:
# check immutable
check(a.HEX, Class.HEX)
check(a.sep, Class.sep)
check(a.size, Class.size)
try:
a.size = 0
check(a.size, TypeError.__name__)
except TypeError:
check(a.size, Class.size)
# check constructor
check(Class(a), a)
check(Class(a.hex), a)
check(Class(a.int), a)
check(Class(str(a)), a)
check(Class(str(a.int)), a)
r = repr(a)
printf('%s.str: %r', r, a.str)
printf('%s.hex: %r', r, a.hex)
printf('%s.int: %x (%%x)', r, a.int)
printf('%s.int: %d (%%d)', r, a.int)
if Class is IP:
printf('%s.ton: %x (%%x)', r, a.ton)
printf('%s.toh: %x (%%x)', r, a.toh)
printf('%s.net: %r', r, a.net)
for m in dir(a):
if m.startswith('is'):
# check is... properties
p = getattr(a, m)
printf('%s.%-12s %r', r, m + ':', p)
if m.startswith('isprivate'):
check(p, False, True)
else:
check(p, False)
# check immutability
try:
x = None
setattr(a, p, True)
except:
x = sys.exc_info()[0]
check(x, TypeError)
# check Errors
for v, X in (('text', ValueError),
(None, TypeError),
([], TypeError),
(Class, TypeError)):
try:
x = None
Class(v)
except:
x = sys.exc_info()[0]
check(x, X)
# check exclude None case
check(gets(None), gets(None))
printf('%s errors in %d tests\n', e or 'no', t)
return e
e = test(IP, getIPs) + \
test(MAC, getMACs)
printf('%s errors in total', e or 'no')
Diff to Previous Revision
--- revision 2 2010-12-20 06:50:17
+++ revision 3 2016-07-07 17:52:15
@@ -1,3 +1,5 @@
+# coding: utf-8
+
'''This module provides 2 classes and 4 functions to,
obtain, check and convert IP and MAC addresses.
@@ -13,47 +15,59 @@
Functions getIPs and getMACs collect the available
IP respectively MAC addresses from the underlying
system. Both functions provide an optional argument
- to exclude specific IP or MAC instance.
+ to exclude specific IP or MAC instances.
Functions isIP and isMAC can be used to check an
address. The return value is an IP respectively
MAC instance if the given address is valid. None
is returned otherwise.
- The IP6 format is not supported, only IP4.
-
- Run python addr.py to see examples and the test
- results. Use python addrs.py -debug to include
+ The IPv6 format is not supported, only IPv4. For
+ both, see module ipaddress (Python 3 only) at
+ <https://docs.python.org/3/library/ipaddress.html>
+
+ Run python ipmac.py to see examples and the test
+ results. Use python ipmac.py -debug to include
debug output.
The core code is copied from standard Python 2.6
and 3.1 module Lib/uuid.py and then modified to:
- - model immutable classes IP and MC like UUID
- - handle any number of IP and MAC addresses
+ - use immutable classes IP and MAC like UUID,
+ - handle any number of IP and MAC addresses,
- collect IP and MAC addresses from several
- additional sources
- - locate external programs only once
- - use env and grep with ifconfig on *nixes
- - support Python versions 2.4 thru 3.1
+ additional sources,
+ - locate external programs only once,
+ - use env and grep with ifconfig on *nixes,
+ - support Python versions 2.4 thru 3.5, both
+ 32- and 64-bit.
Tested on CentOS 4 (Intel), MacOS X 10.4.11 Tiger
(Intel), MacOS X 10.3.9 Panther (PowerPC), RedHat 3
(Opteron), Solaris 10 (Opteron), Windows XP SP2 and
SP3 and Windows Server 2003 R2 with 32-bit Python
- 2.4, 2.5, 2.6, 3.0 and/or 3.1 and on CentOS 4 and
- 5 (Intel) with 64-bit Python 2.4 and 2.6.
+ 2.4, 2.5, 2.6, 3.0 and/or 3.1, on CentOS 4 and 5
+ (Intel) with 64-bit Python 2.4 and 2.6, on MacOS X
+ 10.11.5 with 64-bit Python 2.7.10 and 3.5.1 and on
+ iOS 9.3.2 with 64-bit Pythonista 2.1 (on iPad).
This module does not support Python 2.3 and older
and has not been tested on platforms other than
the ones listed above.
'''
-__version__ = '10.4.22'
+import os
+import socket
+import struct
+import sys
+try:
+ import ctypes
+except ImportError:
+ ctypes = None
+
+__version__ = '16.07.07' # '10.4.22'
__all__ = ('getIPs', 'IP', 'isIP',
'getMACs', 'MAC', 'isMAC')
-
-import os, socket, struct, sys
if sys.hexversion < 0x3000000:
_byte = ord # 2.x chr to integer
@@ -64,47 +78,35 @@
_Ints = int
_Strs = str # (str, unicode)?
+_b08 = 1 << 8
+_b32 = 1 << 32
+_b48 = 1 << 48
+_debugf = None # or printf
+_exes = {} # executables cache
+_IPsep = '.'
+_MACsep = ':'
+
+
def _bytes2int(bytes, byte=_byte):
- # convert bytes to int.
+ # convert bytes to int.
i = 0
for b in bytes:
i = (i << 8) + byte(b)
return i
-_debugf = None # or printf
-
-
- # If ctypes is available, use it to find routines for
- # UUID generation (making this module thread-UNsafe!)
- # Thanks to Thomas Heller for ctypes and for his help
- # with its use here. See Lib/uuid.py for more.
-
-_uuid_generate_time = None
-
-try:
- import ctypes
- import ctypes.util
-
- # The uuid_generate_* routines are provided by libuuid
- # on at least Linux and FreeBSD and by libc on Mac OS X.
- for lib in ('uuid', 'c'):
- try:
- lib = ctypes.CDLL(ctypes.util.find_library(lib))
- _uuid_generate_time = getattr(lib, 'uuid_generate_time',
- _uuid_generate_time)
- except (TypeError, AttributeError):
- lib = None
- del lib
-
-except ImportError:
- ctypes = None
-except AttributeError:
- pass
-
-_exes = {} # executables cache
+
+def _hostname_ips():
+ # get IP addresses from hostname.
+ h = socket.gethostname()
+ try:
+ for a in socket.gethostbyname_ex(h)[2]:
+ yield isIP(a)
+ except AttributeError: # no gethostbyname_ex()
+ yield isIP(socket.gethostbyname(h))
+
def _which(exe, dirs):
- # return full path of an executable.
+ # return full path of an executable.
p = _exes.get(exe, None)
if p is None:
for d in dirs:
@@ -115,32 +117,24 @@
p = '' # no such exe
_exes[exe] = p
if _debugf:
- _debugf('_which(%r): %r', exe, p)
+ _debugf('_which(%r): %r', exe, p)
return p
-def _hostname_ips():
- # get IP addresses from hostname.
- h = socket.gethostname()
- try:
- for a in socket.gethostbyname_ex(h)[2]:
- yield isIP(a)
- except AttributeError: # no gethostbyname_ex()
- yield isIP(socket.gethostbyname(h))
if sys.platform.startswith('win'):
- # On Windows prior to 2000, UuidCreate gives a UUID containing
- # the MAC address. On Windows 2000 and later, UuidCreate makes
- # a random UUID and UuidCreateSequential gives a UUID containing
- # the MAC address. These routines are provided by the RPC runtime.
-
- # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last
- # 6 bytes returned by UuidCreateSequential are fixed, they don't
- # appear to bear any relationship to the MAC address of any network
- # device on the box.
+ # On Windows prior to 2000, UuidCreate gives a UUID containing
+ # the MAC address. On Windows 2000 and later, UuidCreate makes
+ # a random UUID and UuidCreateSequential gives a UUID containing
+ # the MAC address. These routines are provided by the RPC runtime.
+
+ # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last
+ # 6 bytes returned by UuidCreateSequential are fixed, they don't
+ # appear to bear any relationship to the MAC address of any network
+ # device on the box.
def _windll_macs():
- # get the MAC address from UuidCreate*.
+ # get the MAC address from UuidCreate*.
try:
b = ctypes.windll.rpcrt4
f = getattr(b, 'UuidCreateSequential',
@@ -153,8 +147,8 @@
pass
def _netbios_macs():
- # get MAC addresses from NetBIOS. See
- # <http://support.microsoft.com/kb/118623>.
+ # get MAC addresses from NetBIOS. See
+ # <http://support.microsoft.com/kb/118623>.
try:
import netbios
from win32wnet import Netbios
@@ -184,7 +178,7 @@
_dirs = None # ('c:\...', ...)
def _ipconfig(tag, Class):
- # get IP or MAC addresses from ipconfig /all.
+ # get IP or MAC addresses from ipconfig /all.
global _dirs
if _dirs is None:
try: # check system directory first
@@ -209,13 +203,13 @@
return m
def _ipconfig_ips():
- # get IP addresses from ipconfig.
+ # get IP addresses from ipconfig.
return _ipconfig('IP', IP)
_MACsep = '-'
def _ipconfig_macs():
- # get MAC addresses from ipconfig.
+ # get MAC addresses from ipconfig.
return _ipconfig(_MACsep, MAC)
_all_ips = (_hostname_ips, _ipconfig_ips)
@@ -223,13 +217,32 @@
else: # *nix
- _MACsep = ':'
+ _uuid_generate_time = None
+ # If ctypes is available, use it to find routines for
+ # UUID generation (making this module thread-UNsafe!)
+ # Thanks to Thomas Heller for ctypes and for his help
+ # with its use here. See Lib/uuid.py for more.
+ try:
+ import ctypes.util
+ # The uuid_generate_* routines are provided by
+ # libuuid on at least Linux and FreeBSD and by
+ # libc on MacOS X.
+ for lib in ('uuid', 'c'):
+ try:
+ lib = ctypes.CDLL(ctypes.util.find_library(lib))
+ _uuid_generate_time = getattr(lib, 'uuid_generate_time',
+ _uuid_generate_time)
+ except (TypeError, AttributeError):
+ lib = None
+ del lib
+ except (AttributeError, ImportError):
+ pass
_bins = ('/bin', '/usr/bin', '')
_sbins = ('/sbin', '/usr/sbin', '')
def _run(cmd, opts, tag, offset, Class, cut_addr_=False):
- # get IP or MAC addresses from cmd output.
+ # get IP or MAC addresses from cmd output.
c = '%s %s 2>/dev/null' % (cmd, opts)
e = _which('env', _bins)
@@ -241,14 +254,14 @@
c = '%s | %s -i %s' % (c, e, tag) # tag.strip('()')
if _debugf:
- _debugf('_run: %s', c)
+ _debugf('_run: %s', c)
m = []
for t in os.popen(c):
t = t.lower().split()
try:
t = t[offset(t.index(tag))]
- # cut prefix from Linux inet addr:<IP>
+ # cut prefix from Linux inet addr:<IP>
if cut_addr_ and t.startswith('addr:'):
t = t[5:]
m.append(Class(t))
@@ -257,8 +270,8 @@
return m
def _ifconfig_ips():
- # get IP addresses from ifconfig on MacOS ('-a'), Linux ('-a'
- # or ''), Tru64 ('-av'), Solaris ('-a') but not all *nixes.
+ # get IP addresses from ifconfig on MacOS ('-a'), Linux ('-a'
+ # or ''), Tru64 ('-av'), Solaris ('-a') but not all *nixes.
m, c = [], _which('ifconfig', _sbins)
if c:
for a in ('-a', '-av'):
@@ -273,69 +286,80 @@
return []
def _ifconfig_macs():
- # get MAC addresses from ifconfig on MacOS ('-a'), Linux ('-a'
- # or ''), Tru64 ('-av'), but not all *nixes nor Solaris.
+ # get MAC addresses from ifconfig on MacOS ('-a'), Linux ('-a'
+ # or ''), Tru64 ('-av'), but not all *nixes nor Solaris.
m, c = [], _which('ifconfig', _sbins)
- for a, t in (('-a', 'ether' ), # MacOS
+ for a, t in (('-a', 'ether'), # MacOS
('-a', 'hwaddr'), # Linux
- ('-av', 'ether' ),
+ ('-av', 'ether'),
('-av', 'hwaddr')):
- m = _macs(c, a, t, lambda i: i+1) #PYCHOK false
+ m = _macs(c, a, t, lambda i: i + 1)
if m:
break
return m
def _arp_macs():
- # get MAC addresses from arp on Solaris (and MacOS).
+ # get MAC addresses from arp on Solaris (and MacOS).
m, c = [], _which('arp', _sbins)
for a in getIPs():
- # using (c, '-an', '(%s)' % a, lambda i: i+2)
- # works on MacOS X, but then replace tag with
- # tag.strip('()') in the _grep format above
- m.extend(_macs(c, '-an', a, lambda i: -1)) #PYCHOK false
+ # using (c, '-an', '(%s)' % a, lambda i: i+2)
+ # works on MacOS X, but then replace tag with
+ # tag.strip('()') in the _grep format above
+ m.extend(_macs(c, '-an', a, lambda i: -1)) # PYCHOK false
return m
def _lanscan_macs():
- # this might get MAC addresses on HP-UX.
+ # this might get MAC addresses on HP-UX.
c = _which('lanscan', _sbins)
- return _macs(c, '-ai', 'lan0', lambda i: 0) #PYCHOK false
-
- # If the system provides a UUID generator,
- # use that to extract the MAC address.
- if _uuid_generate_time:
- def _uuid_macs():
+ return _macs(c, '-ai', 'lan0', lambda i: 0) # PYCHOK false
+
+ def _uuid_macs():
+ # If the system provides UUID generator(s),
+ # use those to extract the MAC address.
+
+ # NOTE: the uuid's returned by iOS 9.3.2 on
+ # iPad/iPhone are not persistent and may or
+ # may not reflect the device MAC address.
+ try:
+ import uuid
+ u = uuid.uuid1(clock_seq=0)
+ yield isMAC(_bytes2int(u.bytes_le[-6:]))
+ except (AttributeError, ImportError):
+ pass
+
+ if _uuid_generate_time:
b = ctypes.create_string_buffer(16)
if _uuid_generate_time(b) == 0:
yield isMAC(_bytes2int(b.raw[-6:]))
- else:
- _uuid_macs = lambda: ()
-
- try: # Linux (and perhaps AIX, see <http://www.ibm.com/
- # developerworks/aix/library/au-ioctl-socket.html>)
+
+ try:
+ # Linux (and perhaps AIX, see <http://www.ibm.com/
+ # developerworks/aix/library/au-ioctl-socket.html>)
from fcntl import ioctl
- # see e.g. CentOS 4 /usr/include/linux/sockios.h or
- # <http://www.kernel-api.org/docs/online/1.2.13/df/
- # da2/sockios_8h-source.html>
+ # see e.g. CentOS 4 /usr/include/linux/sockios.h or
+ # <http://www.kernel-api.org/docs/online/1.2.13/df/
+ # da2/sockios_8h-source.html>
_SIOCGIFADDR = 0x8915 # PA address
_SIOCGIHWADDR = 0x8927 # hardware address
def _ioctl_(SIOCG, Class):
- # see <http://code.activestate.com/recipes/439094
- # -get-the-ip-address-associated-with-a-network-inter/>
+ # see <http://code.activestate.com/recipes/439094
+ # -get-the-ip-address-associated-with-a-network-inter/>
m, c = [], 24 - Class.size
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for d in range(32):
try: # Linux ('eth'), MacOS ('en')
d = struct.pack('256s', 'eth' + str(d))
b = ioctl(s.fileno(), SIOCG, d)
- # 32-bit IP addr is in bytes b[20:24]
- # in network byte order, get IP addr
- # str from socket.inet_ntoa(b[20:24]),
- # 48-bit MAC addr is in bytes b[18:24]
+ # 32-bit IP addr is in bytes b[20:24]
+ # in network byte order, get IP addr
+ # str from socket.inet_ntoa(b[20:24]),
+ # 48-bit MAC addr is in bytes b[18:24]
m.append(Class(_bytes2int(b[c:24])))
except (IOError, ValueError, TypeError):
- break
+ if m:
+ break
return m
def _ioctl_ips():
@@ -351,6 +375,9 @@
if sys.platform.startswith('darwin'):
_all_ips = (_hostname_ips, _ifconfig_ips)
_all_macs = (_uuid_macs, _ifconfig_macs)
+ elif sys.platform.startswith('iphone'):
+ _all_ips = (_hostname_ips,)
+ _all_macs = (_uuid_macs,)
elif sys.platform.startswith('linux'):
_cut_addr = True
_all_ips = (_hostname_ips, _ioctl_ips, _ifconfig_ips)
@@ -362,39 +389,47 @@
_all_ips = (_hostname_ips, _ifconfig_ips)
_all_macs = (_uuid_macs, _ifconfig_macs, _lanscan_macs)
- # see Python recipe <http://code.activestate.com/recipes/66517
- # -ip-address-conversion-functions-with-the-builtin-s/?in=user-97991>
+# see Python recipe <http://code.activestate.com/recipes/66517
+# -ip-address-conversion-functions-with-the-builtin-s/?in=user-97991>
_NET = '!L' # network byte order (big-endian)
+
def _ip42int(addr, *unused):
- # convert a dot-quad IP addr to an _NET int
- # (raises socket.error for invalid addr)
+ # convert a dot-quad IP addr to an _NET int
+ # (raises socket.error for invalid addr)
return struct.unpack(_NET, socket.inet_aton(addr))[0]
+
def _ip42str(ip):
- # convert an int to dot-quad IP string
+ # convert an int to dot-quad IP string
return socket.inet_ntoa(struct.pack(_NET, ip))
+
def _ip4int(ip):
- # check an int IP (avoid the DeprecationWarning:
- # struct integer overflow masking is deprecated)
- if 0 <= ip < (1<<32): # IP
+ # check an int IP (avoid the DeprecationWarning:
+ # struct integer overflow masking is deprecated)
+ if 0 <= ip < _b32: # IP
return _ip42int(_ip42str(ip))
raise ValueError('IP out of range: %r' % ip)
+
def _ip4masks(bits):
- # return an IP net and host mask
+ # return an IP net and host mask
if 0 < bits <= 32:
b = 1 << (32 - bits)
- return ((1<<32) - b), (b - 1)
+ return (_b32 - b), (b - 1)
raise ValueError('invalid IP mask width: %r' % (bits,))
+
def _ip4isnet(ip, net):
- # check whether an int IP is in the given net
+ # check whether an int IP is in the given net
a, m, _ = _ip4netuple(net)
return (ip & m) == a # and ip != a?
- # see RFCs and <http://code.google.com/p/ipaddr-py/>
+
+_ip4nets = {} # IP net (addr, mask) cache
+
+# see RFCs and <http://code.google.com/p/ipaddr-py/>
_IP4NET_LINKLOCAL = '169.254.0.0/16' # RFC 3927
_IP4NET_LOOPBACK = '127.0.0.0/8' # RFC 5735
_IP4NET_MULTICAST = '224.0.0.0/4' # RFC 3171
@@ -405,8 +440,9 @@
_IP4NET_THIS_HOST = '0.0.0.0/32' # RFC 5735
_IP4NET_THIS_NET = '0.0.0.0/8' # RFC 5735
+
def _ip4isnet_(addr_mask_cast, ip):
- # check network addr, mask or cast match
+ # check network addr, mask or cast match
return ip in [_ip4netuple(n)[addr_mask_cast] for
n in (_IP4NET_LINKLOCAL,
_IP4NET_LOOPBACK,
@@ -418,8 +454,9 @@
_IP4NET_THIS_HOST,
_IP4NET_THIS_NET)]
+
def _ip4net(ip):
- # return containing IP net or an empty string
+ # return containing IP net or an empty string
for n in (_IP4NET_PRIVATE_A,
_IP4NET_PRIVATE_B,
_IP4NET_PRIVATE_C,
@@ -433,10 +470,9 @@
return n
return '' # no network
-_ip4nets = {} # IP net (addr, mask) cache
def _ip4netuple(net):
- # return an IP net addr, mask and cast
+ # return an IP net addr, mask and cast
t = _ip4nets.get(net, None)
if t is None:
a, b = net.split('/')
@@ -445,34 +481,36 @@
t = a, m, (a | b)
_ip4nets[net] = t
if _debugf:
- _debugf('_ip4nets[%r]: (addr 0x%08x, mask 0x%08x, cast 0x%08x)', net, *t)
+ _debugf('_ip4nets[%r]: (addr 0x%08x, mask 0x%08x, cast 0x%08x)', net, *t)
return t
+
def _mac2int(addr, sep=_MACsep):
- # convert a MAC str to an int
+ # convert a MAC str to an int
h = addr.split(sep)
if len(h) > 4:
i = 0
for b in h:
b = int(b, 16)
- if 0 <= b < (1<<8):
- i = (i << 8) | b
+ if 0 <= b < _b08:
+ i = (i << 8) + b
else:
- break
+ break # raise
else:
- if 0 < i < (1<<48):
+ if 0 < i < _b48:
return i
raise ValueError('invalid MAC address: %r' % (addr,))
+
def _macint(mac):
- # check an int MAC
- if 0 < mac < (1<<48):
+ # check an int MAC
+ if 0 < mac < _b48:
return mac
raise ValueError('MAC out of range: %r' % (mac,))
class _Base(object):
- '''Base for IP and MAC classes.
+ '''Base class for IP and MAC classes.
'''
_int = 0 # value as int
@@ -481,8 +519,8 @@
if isinstance(addr, self.__class__):
i = addr._int
elif isinstance(addr, _Strs):
- if self.sep in addr: #PYCHOK false
- i = addr2int(addr, self.sep) #PYCHOK false
+ if self.sep in addr: # PYCHOK false
+ i = addr2int(addr, self.sep) # PYCHOK false
elif addr[:2].lower() == '0x':
i = addrint(int(addr, 16))
else:
@@ -490,16 +528,16 @@
elif isinstance(addr, _Ints):
i = addrint(addr)
else:
- raise TypeError('invalid %s address: %s' %
+ raise TypeError('%s invalid: %s' %
(self.__class__.__name__, type(addr)))
self.__dict__['_int'] = i # immutable
except (socket.error, ValueError):
- raise ValueError('invalid %s address: %r' %
+ raise ValueError('%s invalid: %r' %
(self.__class__.__name__, addr))
if _debugf:
- _debugf('_Base(%r): %r', addr, self)
-
- # see Python 2.6 and 3.1 Lib/uuid.py
+ _debugf('_Base(%r): %r', addr, self)
+
+ # see Python 2.6 and 3.1 Lib/uuid.py
def __cmp__(self, other): # 2.x
if isinstance(other, self.__class__):
@@ -516,8 +554,8 @@
return self._int != other._int
return NotImplemented
- # Q. What's the value of being able to sort UUIDs?
- # A. Use them as keys in a B-Tree or similar mapping.
+ # Q. What's the value of being able to sort UUIDs?
+ # A. Use them as keys in a B-Tree or similar mapping.
def __lt__(self, other): # 3.x
if isinstance(other, self.__class__):
@@ -543,7 +581,7 @@
return hash(self._int)
def __hex__(self): # 2.6+
- return self.HEX % (self._int,) #PYCHOK false
+ return self.HEX % (self._int,) # PYCHOK false
hex = property(__hex__, doc='This address as hexadecimal string, see the .HEX attribute.')
def __int__(self): # 2.6+
@@ -553,7 +591,7 @@
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, str(self))
- def __setattr__(self, name, value): #PYCHOK expected
+ def __setattr__(self, attr, value): # PYCHOK expected
raise TypeError('%s objects are immutable' % (self.__class__.__name__,))
@@ -561,7 +599,7 @@
'''IP address class.
'''
HEX = '0x%08x' # hex format (or '%#010x')
- sep = '.'
+ sep = _IPsep
size = 4 # bytes
def __init__(self, addr):
@@ -673,25 +711,26 @@
string, separated by the MAC.sep attribute.''')
-def _get(gens, exclude):
+def _gets(gens, exclude):
'''Return all IP or MAC addresses subject to exclude.
'''
- t, x = [], exclude
+ x = exclude
if x is None:
- x = lambda _: False
+ x = lambda _: False # PYCHOK expected
elif not hasattr(x, '__call__'):
raise TypeError('exclude not callable: %r' % (x,))
+ t = []
for g in gens:
for a in g():
if _debugf:
- _debugf('%s yields: %r', g.__name__, a)
+ _debugf('%s yields: %r', g.__name__, a)
if a and a not in t and not x(a):
t.append(a)
t = tuple(t)
if _debugf:
- _debugf('_gets(exclude=%r): %r', exclude, t)
+ _debugf('_gets(exclude=%r): %r', exclude, t)
return t
@@ -700,10 +739,10 @@
By default, all reserved IP addresses are excluded. Set
optional argument exclude=None to include all IP addresses
- or provide a callable returning True or False to ex-
+ or provide a callable returning True or False to exclude
respectively include a given IP instance.
'''
- return _get(_all_ips, exclude)
+ return _gets(_all_ips, exclude)
def getMACs(exclude=None):
@@ -711,9 +750,9 @@
By default, optional argument exclude=None to include all
MAC addresses. Provide a callable returning True or False
- to ex- respectively include a given MAC instance.
+ to exclude respectively include a given MAC instance.
'''
- return _get(_all_macs, exclude)
+ return _gets(_all_macs, exclude)
def isIP(addr):
@@ -721,7 +760,7 @@
'''
try:
return IP(addr)
- except (ValueError, TypeError):
+ except (TypeError, ValueError):
return None
@@ -730,7 +769,7 @@
'''
try:
return MAC(addr)
- except (ValueError, TypeError):
+ except (TypeError, ValueError):
return None
@@ -739,11 +778,11 @@
_argv0 = sys.argv[0] + ' '
def printf(fmt, *args):
- # Formatted print.
+ # Formatted print.
print(_argv0 + fmt % args)
if sys.argv[-1] in ('-debug', '-d'):
- _debugf = printf
+ _debugf = printf
printf('%s using Python %s on %s\n', __version__,
sys.version.split()[0], sys.platform)
@@ -767,43 +806,53 @@
s = gets()
printf('get%ss(): %r', n, s)
check(gets(), s)
- a = s[0]
-
- # check constructor
- check(Class(a), a)
- check(Class(a.hex), a)
- check(Class(a.int), a)
- check(Class(str(a)), a)
- check(Class(str(a.int)), a)
-
- r = repr(a)
- printf('%s.str: %r', r, a.str)
- printf('%s.hex: %r', r, a.hex)
- printf('%s.int: %x (%%x)', r, a.int)
- printf('%s.int: %d (%%d)', r, a.int)
-
- if Class is IP:
- printf('%s.ton: %x (%%x)', r, a.ton)
- printf('%s.toh: %x (%%x)', r, a.toh)
- printf('%s.net: %r', r, a.net)
- for m in dir(a):
- if m.startswith('is'):
- # check is... properties
- p = getattr(a, m)
- printf('%s.%-12s %r', r, m + ':', p)
- if m.startswith('isprivate'):
- check(p, False, True)
- else:
- check(p, False)
- # check immutability
- try:
- x = None
- setattr(a, p, True)
- except:
- x = sys.exc_info()[0]
- check(x, TypeError)
-
- # check Errors
+
+ for a in s:
+ # check immutable
+ check(a.HEX, Class.HEX)
+ check(a.sep, Class.sep)
+ check(a.size, Class.size)
+ try:
+ a.size = 0
+ check(a.size, TypeError.__name__)
+ except TypeError:
+ check(a.size, Class.size)
+
+ # check constructor
+ check(Class(a), a)
+ check(Class(a.hex), a)
+ check(Class(a.int), a)
+ check(Class(str(a)), a)
+ check(Class(str(a.int)), a)
+
+ r = repr(a)
+ printf('%s.str: %r', r, a.str)
+ printf('%s.hex: %r', r, a.hex)
+ printf('%s.int: %x (%%x)', r, a.int)
+ printf('%s.int: %d (%%d)', r, a.int)
+
+ if Class is IP:
+ printf('%s.ton: %x (%%x)', r, a.ton)
+ printf('%s.toh: %x (%%x)', r, a.toh)
+ printf('%s.net: %r', r, a.net)
+ for m in dir(a):
+ if m.startswith('is'):
+ # check is... properties
+ p = getattr(a, m)
+ printf('%s.%-12s %r', r, m + ':', p)
+ if m.startswith('isprivate'):
+ check(p, False, True)
+ else:
+ check(p, False)
+ # check immutability
+ try:
+ x = None
+ setattr(a, p, True)
+ except:
+ x = sys.exc_info()[0]
+ check(x, TypeError)
+
+ # check Errors
for v, X in (('text', ValueError),
(None, TypeError),
([], TypeError),
@@ -815,7 +864,7 @@
x = sys.exc_info()[0]
check(x, X)
- # check exclude None case
+ # check exclude None case
check(gets(None), gets(None))
printf('%s errors in %d tests\n', e or 'no', t)