mirror of
https://github.com/python/cpython.git
synced 2025-12-04 00:30:19 +00:00
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
This commit is contained in:
parent
d9baa8592c
commit
730f67f2fa
3 changed files with 691 additions and 337 deletions
554
Lib/ipaddress.py
554
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,102 +1162,6 @@ class _BaseV4:
|
|||
def version(self):
|
||||
return self._version
|
||||
|
||||
@property
|
||||
def is_reserved(self):
|
||||
"""Test if the address is otherwise IETF reserved.
|
||||
|
||||
Returns:
|
||||
A boolean, True if the address is within the
|
||||
reserved IPv4 Network range.
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
@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 1918.
|
||||
|
||||
"""
|
||||
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))
|
||||
|
||||
@property
|
||||
def is_multicast(self):
|
||||
"""Test if the address is reserved for multicast use.
|
||||
|
||||
Returns:
|
||||
A boolean, True if the address is multicast.
|
||||
See RFC 3171 for details.
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
@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 5735 3.
|
||||
|
||||
"""
|
||||
unspecified_address = IPv4Address('0.0.0.0')
|
||||
if isinstance(self, _BaseAddress):
|
||||
return self == unspecified_address
|
||||
return (self.network_address == self.broadcast_address ==
|
||||
unspecified_address)
|
||||
|
||||
@property
|
||||
def is_loopback(self):
|
||||
"""Test if the address is a loopback address.
|
||||
|
||||
Returns:
|
||||
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)
|
||||
|
||||
@property
|
||||
def is_link_local(self):
|
||||
"""Test if the address is reserved for link-local.
|
||||
|
||||
Returns:
|
||||
A boolean, True if the address is link-local per RFC 3927.
|
||||
|
||||
"""
|
||||
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):
|
||||
|
||||
|
|
@ -1236,6 +1208,79 @@ class IPv4Address(_BaseV4, _BaseAddress):
|
|||
"""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.
|
||||
|
||||
Returns:
|
||||
A boolean, True if the address is within the
|
||||
reserved IPv4 Network range.
|
||||
|
||||
"""
|
||||
reserved_network = IPv4Network('240.0.0.0/4')
|
||||
return self in reserved_network
|
||||
|
||||
@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 1918.
|
||||
|
||||
"""
|
||||
private_10 = IPv4Network('10.0.0.0/8')
|
||||
private_172 = IPv4Network('172.16.0.0/12')
|
||||
private_192 = IPv4Network('192.168.0.0/16')
|
||||
return (self in private_10 or
|
||||
self in private_172 or
|
||||
self in private_192)
|
||||
|
||||
@property
|
||||
def is_multicast(self):
|
||||
"""Test if the address is reserved for multicast use.
|
||||
|
||||
Returns:
|
||||
A boolean, True if the address is multicast.
|
||||
See RFC 3171 for details.
|
||||
|
||||
"""
|
||||
multicast_network = IPv4Network('224.0.0.0/4')
|
||||
return self in multicast_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 5735 3.
|
||||
|
||||
"""
|
||||
unspecified_address = IPv4Address('0.0.0.0')
|
||||
return self == unspecified_address
|
||||
|
||||
@property
|
||||
def is_loopback(self):
|
||||
"""Test if the address is a loopback address.
|
||||
|
||||
Returns:
|
||||
A boolean, True if the address is a loopback per RFC 3330.
|
||||
|
||||
"""
|
||||
loopback_network = IPv4Network('127.0.0.0/8')
|
||||
return self in loopback_network
|
||||
|
||||
@property
|
||||
def is_link_local(self):
|
||||
"""Test if the address is reserved for link-local.
|
||||
|
||||
Returns:
|
||||
A boolean, True if the address is link-local per RFC 3927.
|
||||
|
||||
"""
|
||||
linklocal_network = IPv4Network('169.254.0.0/16')
|
||||
return self in linklocal_network
|
||||
|
||||
|
||||
class IPv4Interface(IPv4Address):
|
||||
|
||||
|
|
@ -1674,162 +1719,6 @@ class _BaseV6:
|
|||
def version(self):
|
||||
return self._version
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
reserved_networks = [IPv6Network('::/8'), IPv6Network('100::/8'),
|
||||
IPv6Network('200::/7'), IPv6Network('400::/6'),
|
||||
IPv6Network('800::/5'), IPv6Network('1000::/4'),
|
||||
IPv6Network('4000::/3'), IPv6Network('6000::/3'),
|
||||
IPv6Network('8000::/3'), IPv6Network('A000::/3'),
|
||||
IPv6Network('C000::/3'), IPv6Network('E000::/4'),
|
||||
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)
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
@property
|
||||
def ipv4_mapped(self):
|
||||
"""Return the IPv4 mapped address.
|
||||
|
||||
Returns:
|
||||
If the IPv6 address is a v4 mapped address, return the
|
||||
IPv4 mapped address. Return None otherwise.
|
||||
|
||||
"""
|
||||
if (self._ip >> 32) != 0xFFFF:
|
||||
return None
|
||||
return IPv4Address(self._ip & 0xFFFFFFFF)
|
||||
|
||||
@property
|
||||
def teredo(self):
|
||||
"""Tuple of embedded teredo IPs.
|
||||
|
||||
Returns:
|
||||
Tuple of the (server, client) IPs or None if the address
|
||||
doesn't appear to be a teredo address (doesn't start with
|
||||
2001::/32)
|
||||
|
||||
"""
|
||||
if (self._ip >> 96) != 0x20010000:
|
||||
return None
|
||||
return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF),
|
||||
IPv4Address(~self._ip & 0xFFFFFFFF))
|
||||
|
||||
@property
|
||||
def sixtofour(self):
|
||||
"""Return the IPv4 6to4 embedded address.
|
||||
|
||||
Returns:
|
||||
The IPv4 6to4-embedded address if present or None if the
|
||||
address doesn't appear to contain a 6to4 embedded address.
|
||||
|
||||
"""
|
||||
if (self._ip >> 112) != 0x2002:
|
||||
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):
|
||||
|
||||
|
|
@ -1878,6 +1767,138 @@ class IPv6Address(_BaseV6, _BaseAddress):
|
|||
"""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.
|
||||
|
||||
Returns:
|
||||
A boolean, True if the address is a multicast address.
|
||||
See RFC 2373 2.7 for details.
|
||||
|
||||
"""
|
||||
multicast_network = IPv6Network('ff00::/8')
|
||||
return self in multicast_network
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
reserved_networks = [IPv6Network('::/8'), IPv6Network('100::/8'),
|
||||
IPv6Network('200::/7'), IPv6Network('400::/6'),
|
||||
IPv6Network('800::/5'), IPv6Network('1000::/4'),
|
||||
IPv6Network('4000::/3'), IPv6Network('6000::/3'),
|
||||
IPv6Network('8000::/3'), IPv6Network('A000::/3'),
|
||||
IPv6Network('C000::/3'), IPv6Network('E000::/4'),
|
||||
IPv6Network('F000::/5'), IPv6Network('F800::/6'),
|
||||
IPv6Network('FE00::/9')]
|
||||
|
||||
return any(self in x for x in reserved_networks)
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
linklocal_network = IPv6Network('fe80::/10')
|
||||
return self in linklocal_network
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
sitelocal_network = IPv6Network('fec0::/10')
|
||||
return self in sitelocal_network
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
private_network = IPv6Network('fc00::/7')
|
||||
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):
|
||||
"""Return the IPv4 mapped address.
|
||||
|
||||
Returns:
|
||||
If the IPv6 address is a v4 mapped address, return the
|
||||
IPv4 mapped address. Return None otherwise.
|
||||
|
||||
"""
|
||||
if (self._ip >> 32) != 0xFFFF:
|
||||
return None
|
||||
return IPv4Address(self._ip & 0xFFFFFFFF)
|
||||
|
||||
@property
|
||||
def teredo(self):
|
||||
"""Tuple of embedded teredo IPs.
|
||||
|
||||
Returns:
|
||||
Tuple of the (server, client) IPs or None if the address
|
||||
doesn't appear to be a teredo address (doesn't start with
|
||||
2001::/32)
|
||||
|
||||
"""
|
||||
if (self._ip >> 96) != 0x20010000:
|
||||
return None
|
||||
return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF),
|
||||
IPv4Address(~self._ip & 0xFFFFFFFF))
|
||||
|
||||
@property
|
||||
def sixtofour(self):
|
||||
"""Return the IPv4 6to4 embedded address.
|
||||
|
||||
Returns:
|
||||
The IPv4 6to4-embedded address if present or None if the
|
||||
address doesn't appear to contain a 6to4 embedded address.
|
||||
|
||||
"""
|
||||
if (self._ip >> 112) != 0x2002:
|
||||
return None
|
||||
return IPv4Address((self._ip >> 80) & 0xFFFFFFFF)
|
||||
|
||||
|
||||
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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue