summaryrefslogtreecommitdiff
path: root/Lib/ipaddress.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/ipaddress.py')
-rw-r--r--Lib/ipaddress.py142
1 files changed, 90 insertions, 52 deletions
diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py
index ecf3f4491c..ac03c36ce0 100644
--- a/Lib/ipaddress.py
+++ b/Lib/ipaddress.py
@@ -388,40 +388,7 @@ def get_mixed_type_key(obj):
return NotImplemented
-class _TotalOrderingMixin:
- # Helper that derives the other comparison operations from
- # __lt__ and __eq__
- # We avoid functools.total_ordering because it doesn't handle
- # NotImplemented correctly yet (http://bugs.python.org/issue10042)
- def __eq__(self, other):
- raise NotImplementedError
- def __ne__(self, other):
- equal = self.__eq__(other)
- if equal is NotImplemented:
- return NotImplemented
- return not equal
- def __lt__(self, other):
- raise NotImplementedError
- def __le__(self, other):
- less = self.__lt__(other)
- if less is NotImplemented or not less:
- return self.__eq__(other)
- return less
- def __gt__(self, other):
- less = self.__lt__(other)
- if less is NotImplemented:
- return NotImplemented
- equal = self.__eq__(other)
- if equal is NotImplemented:
- return NotImplemented
- return not (less or equal)
- def __ge__(self, other):
- less = self.__lt__(other)
- if less is NotImplemented:
- return NotImplemented
- return not less
-
-class _IPAddressBase(_TotalOrderingMixin):
+class _IPAddressBase:
"""The mother class."""
@@ -472,7 +439,7 @@ class _IPAddressBase(_TotalOrderingMixin):
"""Return prefix length from the bitwise netmask.
Args:
- ip_int: An integer, the netmask in axpanded bitwise format
+ ip_int: An integer, the netmask in expanded bitwise format
Returns:
An integer, the prefix length.
@@ -554,6 +521,7 @@ class _IPAddressBase(_TotalOrderingMixin):
self._report_invalid_netmask(ip_str)
+@functools.total_ordering
class _BaseAddress(_IPAddressBase):
"""A generic IP object.
@@ -578,12 +546,11 @@ class _BaseAddress(_IPAddressBase):
return NotImplemented
def __lt__(self, other):
+ if not isinstance(other, _BaseAddress):
+ return NotImplemented
if self._version != other._version:
raise TypeError('%s and %s are not of the same version' % (
self, other))
- if not isinstance(other, _BaseAddress):
- raise TypeError('%s and %s are not of the same type' % (
- self, other))
if self._ip != other._ip:
return self._ip < other._ip
return False
@@ -613,6 +580,7 @@ class _BaseAddress(_IPAddressBase):
return (self._version, self)
+@functools.total_ordering
class _BaseNetwork(_IPAddressBase):
"""A generic IP network object.
@@ -662,12 +630,11 @@ class _BaseNetwork(_IPAddressBase):
return self._address_class(broadcast + n)
def __lt__(self, other):
+ if not isinstance(other, _BaseNetwork):
+ return NotImplemented
if self._version != other._version:
raise TypeError('%s and %s are not of the same version' % (
self, other))
- if not isinstance(other, _BaseNetwork):
- raise TypeError('%s and %s are not of the same type' % (
- self, other))
if self.network_address != other.network_address:
return self.network_address < other.network_address
if self.netmask != other.netmask:
@@ -1030,13 +997,25 @@ class _BaseNetwork(_IPAddressBase):
"""Test if this address is allocated for private networks.
Returns:
- A boolean, True if the address is reserved per RFC 4193.
+ A boolean, True if the address is reserved per
+ iana-ipv4-special-registry or iana-ipv6-special-registry.
"""
return (self.network_address.is_private and
self.broadcast_address.is_private)
@property
+ def is_global(self):
+ """Test if this address is allocated for public networks.
+
+ Returns:
+ A boolean, True if the address is not reserved per
+ iana-ipv4-special-registry or iana-ipv6-special-registry.
+
+ """
+ return not self.is_private
+
+ @property
def is_unspecified(self):
"""Test if the address is unspecified.
@@ -1276,19 +1255,30 @@ class IPv4Address(_BaseV4, _BaseAddress):
return self in reserved_network
@property
+ @functools.lru_cache()
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.
+ A boolean, True if the address is reserved per
+ iana-ipv4-special-registry.
"""
- 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)
+ return (self in IPv4Network('0.0.0.0/8') or
+ self in IPv4Network('10.0.0.0/8') or
+ self in IPv4Network('127.0.0.0/8') or
+ self in IPv4Network('169.254.0.0/16') or
+ self in IPv4Network('172.16.0.0/12') or
+ self in IPv4Network('192.0.0.0/29') or
+ self in IPv4Network('192.0.0.170/31') or
+ self in IPv4Network('192.0.2.0/24') or
+ self in IPv4Network('192.168.0.0/16') or
+ self in IPv4Network('198.18.0.0/15') or
+ self in IPv4Network('198.51.100.0/24') or
+ self in IPv4Network('203.0.113.0/24') or
+ self in IPv4Network('240.0.0.0/4') or
+ self in IPv4Network('255.255.255.255/32'))
+
@property
def is_multicast(self):
@@ -1504,6 +1494,21 @@ class IPv4Network(_BaseV4, _BaseNetwork):
if self._prefixlen == (self._max_prefixlen - 1):
self.hosts = self.__iter__
+ @property
+ @functools.lru_cache()
+ def is_global(self):
+ """Test if this address is allocated for public networks.
+
+ Returns:
+ A boolean, True if the address is not reserved per
+ iana-ipv4-special-registry.
+
+ """
+ return (not (self.network_address in IPv4Network('100.64.0.0/10') and
+ self.broadcast_address in IPv4Network('100.64.0.0/10')) and
+ not self.is_private)
+
+
class _BaseV6:
@@ -1860,15 +1865,36 @@ class IPv6Address(_BaseV6, _BaseAddress):
return self in sitelocal_network
@property
+ @functools.lru_cache()
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.
+ A boolean, True if the address is reserved per
+ iana-ipv6-special-registry.
"""
- private_network = IPv6Network('fc00::/7')
- return self in private_network
+ return (self in IPv6Network('::1/128') or
+ self in IPv6Network('::/128') or
+ self in IPv6Network('::ffff:0:0/96') or
+ self in IPv6Network('100::/64') or
+ self in IPv6Network('2001::/23') or
+ self in IPv6Network('2001:2::/48') or
+ self in IPv6Network('2001:db8::/32') or
+ self in IPv6Network('2001:10::/28') or
+ self in IPv6Network('fc00::/7') or
+ self in IPv6Network('fe80::/10'))
+
+ @property
+ def is_global(self):
+ """Test if this address is allocated for public networks.
+
+ Returns:
+ A boolean, true if the address is not reserved per
+ iana-ipv6-special-registry.
+
+ """
+ return not self.is_private
@property
def is_unspecified(self):
@@ -2096,6 +2122,18 @@ class IPv6Network(_BaseV6, _BaseNetwork):
if self._prefixlen == (self._max_prefixlen - 1):
self.hosts = self.__iter__
+ def hosts(self):
+ """Generate Iterator over usable hosts in a network.
+
+ This is like __iter__ except it doesn't return the
+ Subnet-Router anycast address.
+
+ """
+ network = int(self.network_address)
+ broadcast = int(self.broadcast_address)
+ for x in range(network + 1, broadcast + 1):
+ yield self._address_class(x)
+
@property
def is_site_local(self):
"""Test if the address is reserved for site-local.