diff options
Diffstat (limited to 'Lib/ipaddress.py')
-rw-r--r-- | Lib/ipaddress.py | 142 |
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. |