diff options
author | Nick Coghlan <ncoghlan@gmail.com> | 2012-08-05 12:02:18 (GMT) |
---|---|---|
committer | Nick Coghlan <ncoghlan@gmail.com> | 2012-08-05 12:02:18 (GMT) |
commit | 730f67f2fa2d1df828a93301e4df48f1f1c41b2b (patch) | |
tree | 6ecc84e9845eea17534e26ba6d321f2395c6d56c /Lib | |
parent | d9baa8592cd26e2fd2689d3eeda6e5f7fd3e21a8 (diff) | |
download | cpython-730f67f2fa2d1df828a93301e4df48f1f1c41b2b.zip cpython-730f67f2fa2d1df828a93301e4df48f1f1c41b2b.tar.gz cpython-730f67f2fa2d1df828a93301e4df48f1f1c41b2b.tar.bz2 |
Issue 14814: Docs work showed some more cases of networks pretending to be addresses and highlighted the weird approach to implementing the 'is_whatever' properties. Impl now illustrates far more clearly that networks have a property if both their network and broadcast addresses have that property
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/ipaddress.py | 404 | ||||
-rw-r--r-- | Lib/test/test_ipaddress.py | 4 |
2 files changed, 226 insertions, 182 deletions
diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index 612236e..dfb5944 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -497,6 +497,7 @@ class _IPAddressBase(_TotalOrderingMixin): prefixlen = self._prefixlen return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen)) + class _BaseAddress(_IPAddressBase): """A generic IP object. @@ -568,9 +569,6 @@ class _BaseNetwork(_IPAddressBase): def __init__(self, address): self._cache = {} - def __int__(self): - return int(self.network_address) - def __repr__(self): return '%s(%r)' % (self.__class__.__name__, str(self)) @@ -937,6 +935,76 @@ class _BaseNetwork(_IPAddressBase): strict=False) return t.__class__('%s/%d' % (t.network_address, t.prefixlen)) + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is a multicast address. + See RFC 2373 2.7 for details. + + """ + return (self.network_address.is_multicast and + self.broadcast_address.is_multicast) + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within one of the + reserved IPv6 Network ranges. + + """ + return (self.network_address.is_reserved and + self.broadcast_address.is_reserved) + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is reserved per RFC 4291. + + """ + return (self.network_address.is_link_local and + self.broadcast_address.is_link_local) + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per RFC 4193. + + """ + return (self.network_address.is_private and + self.broadcast_address.is_private) + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return (self.network_address.is_unspecified and + self.broadcast_address.is_unspecified) + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return (self.network_address.is_loopback and + self.broadcast_address.is_loopback) + class _BaseV4: @@ -1094,6 +1162,52 @@ class _BaseV4: def version(self): return self._version + +class IPv4Address(_BaseV4, _BaseAddress): + + """Represent and manipulate single IPv4 Addresses.""" + + def __init__(self, address): + + """ + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv4Address('192.0.2.1') == IPv4Address(3221225985). + or, more generally + IPv4Address(int(IPv4Address('192.0.2.1'))) == + IPv4Address('192.0.2.1') + + Raises: + AddressValueError: If ipaddress isn't a valid IPv4 address. + + """ + _BaseAddress.__init__(self, address) + _BaseV4.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, int): + self._check_int_address(address) + self._ip = address + return + + # Constructing from a packed address + if isinstance(address, bytes): + self._check_packed_address(address, 4) + self._ip = struct.unpack('!I', address)[0] + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = str(address) + self._ip = self._ip_int_from_string(addr_str) + + @property + def packed(self): + """The binary representation of this address.""" + return v4_int_to_packed(self._ip) + @property def is_reserved(self): """Test if the address is otherwise IETF reserved. @@ -1104,10 +1218,7 @@ class _BaseV4: """ reserved_network = IPv4Network('240.0.0.0/4') - if isinstance(self, _BaseAddress): - return self in reserved_network - return (self.network_address in reserved_network and - self.broadcast_address in reserved_network) + return self in reserved_network @property def is_private(self): @@ -1120,16 +1231,9 @@ class _BaseV4: private_10 = IPv4Network('10.0.0.0/8') private_172 = IPv4Network('172.16.0.0/12') private_192 = IPv4Network('192.168.0.0/16') - if isinstance(self, _BaseAddress): - return (self in private_10 or self in private_172 or - self in private_192) - else: - return ((self.network_address in private_10 and - self.broadcast_address in private_10) or - (self.network_address in private_172 and - self.broadcast_address in private_172) or - (self.network_address in private_192 and - self.broadcast_address in private_192)) + return (self in private_10 or + self in private_172 or + self in private_192) @property def is_multicast(self): @@ -1141,10 +1245,7 @@ class _BaseV4: """ multicast_network = IPv4Network('224.0.0.0/4') - if isinstance(self, _BaseAddress): - return self in IPv4Network('224.0.0.0/4') - return (self.network_address in multicast_network and - self.broadcast_address in multicast_network) + return self in multicast_network @property def is_unspecified(self): @@ -1156,10 +1257,7 @@ class _BaseV4: """ unspecified_address = IPv4Address('0.0.0.0') - if isinstance(self, _BaseAddress): - return self == unspecified_address - return (self.network_address == self.broadcast_address == - unspecified_address) + return self == unspecified_address @property def is_loopback(self): @@ -1169,12 +1267,8 @@ class _BaseV4: A boolean, True if the address is a loopback per RFC 3330. """ - loopback_address = IPv4Network('127.0.0.0/8') - if isinstance(self, _BaseAddress): - return self in loopback_address - - return (self.network_address in loopback_address and - self.broadcast_address in loopback_address) + loopback_network = IPv4Network('127.0.0.0/8') + return self in loopback_network @property def is_link_local(self): @@ -1185,56 +1279,7 @@ class _BaseV4: """ linklocal_network = IPv4Network('169.254.0.0/16') - if isinstance(self, _BaseAddress): - return self in linklocal_network - return (self.network_address in linklocal_network and - self.broadcast_address in linklocal_network) - - -class IPv4Address(_BaseV4, _BaseAddress): - - """Represent and manipulate single IPv4 Addresses.""" - - def __init__(self, address): - - """ - Args: - address: A string or integer representing the IP - - Additionally, an integer can be passed, so - IPv4Address('192.0.2.1') == IPv4Address(3221225985). - or, more generally - IPv4Address(int(IPv4Address('192.0.2.1'))) == - IPv4Address('192.0.2.1') - - Raises: - AddressValueError: If ipaddress isn't a valid IPv4 address. - - """ - _BaseAddress.__init__(self, address) - _BaseV4.__init__(self, address) - - # Efficient constructor from integer. - if isinstance(address, int): - self._check_int_address(address) - self._ip = address - return - - # Constructing from a packed address - if isinstance(address, bytes): - self._check_packed_address(address, 4) - self._ip = struct.unpack('!I', address)[0] - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP string. - addr_str = str(address) - self._ip = self._ip_int_from_string(addr_str) - - @property - def packed(self): - """The binary representation of this address.""" - return v4_int_to_packed(self._ip) + return self in linklocal_network class IPv4Interface(IPv4Address): @@ -1674,6 +1719,54 @@ class _BaseV6: def version(self): return self._version + +class IPv6Address(_BaseV6, _BaseAddress): + + """Represent and manipulate single IPv6 Addresses.""" + + def __init__(self, address): + """Instantiate a new IPv6 address object. + + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv6Address('2001:db8::') == + IPv6Address(42540766411282592856903984951653826560) + or, more generally + IPv6Address(int(IPv6Address('2001:db8::'))) == + IPv6Address('2001:db8::') + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + + """ + _BaseAddress.__init__(self, address) + _BaseV6.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, int): + self._check_int_address(address) + self._ip = address + return + + # Constructing from a packed address + if isinstance(address, bytes): + self._check_packed_address(address, 16) + tmp = struct.unpack('!QQ', address) + self._ip = (tmp[0] << 64) | tmp[1] + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = str(address) + self._ip = self._ip_int_from_string(addr_str) + + @property + def packed(self): + """The binary representation of this address.""" + return v6_int_to_packed(self._ip) + @property def is_multicast(self): """Test if the address is reserved for multicast use. @@ -1684,10 +1777,7 @@ class _BaseV6: """ multicast_network = IPv6Network('ff00::/8') - if isinstance(self, _BaseAddress): - return self in multicast_network - return (self.network_address in multicast_network and - self.broadcast_address in multicast_network) + return self in multicast_network @property def is_reserved(self): @@ -1707,10 +1797,7 @@ class _BaseV6: IPv6Network('F000::/5'), IPv6Network('F800::/6'), IPv6Network('FE00::/9')] - if isinstance(self, _BaseAddress): - return any(self in x for x in reserved_networks) - return any(self.network_address in x and self.broadcast_address in x - for x in reserved_networks) + return any(self in x for x in reserved_networks) @property def is_link_local(self): @@ -1721,10 +1808,7 @@ class _BaseV6: """ linklocal_network = IPv6Network('fe80::/10') - if isinstance(self, _BaseAddress): - return self in linklocal_network - return (self.network_address in linklocal_network and - self.broadcast_address in linklocal_network) + return self in linklocal_network @property def is_site_local(self): @@ -1739,10 +1823,7 @@ class _BaseV6: """ sitelocal_network = IPv6Network('fec0::/10') - if isinstance(self, _BaseAddress): - return self in sitelocal_network - return (self.network_address in sitelocal_network and - self.broadcast_address in sitelocal_network) + return self in sitelocal_network @property def is_private(self): @@ -1753,10 +1834,29 @@ class _BaseV6: """ private_network = IPv6Network('fc00::/7') - if isinstance(self, _BaseAddress): - return self in private_network - return (self.network_address in private_network and - self.broadcast_address in private_network) + return self in private_network + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return self._ip == 0 + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return self._ip == 1 @property def ipv4_mapped(self): @@ -1799,85 +1899,6 @@ class _BaseV6: return None return IPv4Address((self._ip >> 80) & 0xFFFFFFFF) - @property - def is_unspecified(self): - """Test if the address is unspecified. - - Returns: - A boolean, True if this is the unspecified address as defined in - RFC 2373 2.5.2. - - """ - if isinstance(self, (IPv6Network, IPv6Interface)): - return int(self.network_address) == 0 and getattr( - self, '_prefixlen', 128) == 128 - return self._ip == 0 - - @property - def is_loopback(self): - """Test if the address is a loopback address. - - Returns: - A boolean, True if the address is a loopback address as defined in - RFC 2373 2.5.3. - - """ - if isinstance(self, IPv6Network): - return int(self) == 1 and getattr( - self, '_prefixlen', 128) == 128 - elif isinstance(self, IPv6Interface): - return int(self.network.network_address) == 1 and getattr( - self, '_prefixlen', 128) == 128 - return self._ip == 1 - - -class IPv6Address(_BaseV6, _BaseAddress): - - """Represent and manipulate single IPv6 Addresses.""" - - def __init__(self, address): - """Instantiate a new IPv6 address object. - - Args: - address: A string or integer representing the IP - - Additionally, an integer can be passed, so - IPv6Address('2001:db8::') == - IPv6Address(42540766411282592856903984951653826560) - or, more generally - IPv6Address(int(IPv6Address('2001:db8::'))) == - IPv6Address('2001:db8::') - - Raises: - AddressValueError: If address isn't a valid IPv6 address. - - """ - _BaseAddress.__init__(self, address) - _BaseV6.__init__(self, address) - - # Efficient constructor from integer. - if isinstance(address, int): - self._check_int_address(address) - self._ip = address - return - - # Constructing from a packed address - if isinstance(address, bytes): - self._check_packed_address(address, 16) - tmp = struct.unpack('!QQ', address) - self._ip = (tmp[0] << 64) | tmp[1] - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP string. - addr_str = str(address) - self._ip = self._ip_int_from_string(addr_str) - - @property - def packed(self): - """The binary representation of this address.""" - return v6_int_to_packed(self._ip) - class IPv6Interface(IPv6Address): @@ -1946,6 +1967,14 @@ class IPv6Interface(IPv6Address): return '%s/%s' % (self._string_from_ip_int(self._ip), self.hostmask) + @property + def is_unspecified(self): + return self._ip == 0 and self.network.is_unspecified + + @property + def is_loopback(self): + return self._ip == 1 and self.network.is_loopback + class IPv6Network(_BaseV6, _BaseNetwork): @@ -2054,3 +2083,18 @@ class IPv6Network(_BaseV6, _BaseNetwork): except ValueError: return False return 0 <= prefixlen <= self._max_prefixlen + + @property + def is_site_local(self): + """Test if the address is reserved for site-local. + + Note that the site-local address space has been deprecated by RFC 3879. + Use is_private to test if this address is in the space of unique local + addresses as defined by RFC 4193. + + Returns: + A boolean, True if the address is reserved per RFC 3513 2.5.6. + + """ + return (self.network_address.is_site_local and + self.broadcast_address.is_site_local) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index a44fa7f..061c866 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -647,8 +647,8 @@ class IpaddrUnitTest(unittest.TestCase): ipv4 = ipaddress.ip_network('1.2.3.4') ipv6 = ipaddress.ip_network('2001:658:22a:cafe:200:0:0:1') - self.assertEqual(ipv4, ipaddress.ip_network(int(ipv4))) - self.assertEqual(ipv6, ipaddress.ip_network(int(ipv6))) + self.assertEqual(ipv4, ipaddress.ip_network(int(ipv4.network_address))) + self.assertEqual(ipv6, ipaddress.ip_network(int(ipv6.network_address))) v6_int = 42540616829182469433547762482097946625 self.assertEqual(self.ipv6_interface._ip, |