mirror of
https://github.com/python/cpython.git
synced 2025-12-05 00:52:25 +00:00
bpo-34788: Add support for scoped IPv6 addresses (GH-13772)
Automerge-Triggered-By: @asvetlov
This commit is contained in:
parent
be7ead62db
commit
21da76d1f1
7 changed files with 544 additions and 37 deletions
|
|
@ -217,11 +217,20 @@ write code that handles both IP versions correctly. Address objects are
|
||||||
:RFC:`4291` for details. For example,
|
:RFC:`4291` for details. For example,
|
||||||
``"0000:0000:0000:0000:0000:0abc:0007:0def"`` can be compressed to
|
``"0000:0000:0000:0000:0000:0abc:0007:0def"`` can be compressed to
|
||||||
``"::abc:7:def"``.
|
``"::abc:7:def"``.
|
||||||
|
|
||||||
|
Optionally, the string may also have a scope zone ID, expressed
|
||||||
|
with a suffix ``%scope_id``. If present, the scope ID must be non-empty,
|
||||||
|
and may not contain ``%``.
|
||||||
|
See :RFC:`4007` for details.
|
||||||
|
For example, ``fe80::1234%1`` might identify address ``fe80::1234`` on the first link of the node.
|
||||||
2. An integer that fits into 128 bits.
|
2. An integer that fits into 128 bits.
|
||||||
3. An integer packed into a :class:`bytes` object of length 16, big-endian.
|
3. An integer packed into a :class:`bytes` object of length 16, big-endian.
|
||||||
|
|
||||||
|
|
||||||
>>> ipaddress.IPv6Address('2001:db8::1000')
|
>>> ipaddress.IPv6Address('2001:db8::1000')
|
||||||
IPv6Address('2001:db8::1000')
|
IPv6Address('2001:db8::1000')
|
||||||
|
>>> ipaddress.IPv6Address('ff02::5678%1')
|
||||||
|
IPv6Address('ff02::5678%1')
|
||||||
|
|
||||||
.. attribute:: compressed
|
.. attribute:: compressed
|
||||||
|
|
||||||
|
|
@ -268,6 +277,12 @@ write code that handles both IP versions correctly. Address objects are
|
||||||
``::FFFF/96``), this property will report the embedded IPv4 address.
|
``::FFFF/96``), this property will report the embedded IPv4 address.
|
||||||
For any other address, this property will be ``None``.
|
For any other address, this property will be ``None``.
|
||||||
|
|
||||||
|
.. attribute:: scope_id
|
||||||
|
|
||||||
|
For scoped addresses as defined by :RFC:`4007`, this property identifies
|
||||||
|
the particular zone of the address's scope that the address belongs to,
|
||||||
|
as a string. When no scope zone is specified, this property will be ``None``.
|
||||||
|
|
||||||
.. attribute:: sixtofour
|
.. attribute:: sixtofour
|
||||||
|
|
||||||
For addresses that appear to be 6to4 addresses (starting with
|
For addresses that appear to be 6to4 addresses (starting with
|
||||||
|
|
@ -299,6 +314,8 @@ the :func:`str` and :func:`int` builtin functions::
|
||||||
>>> int(ipaddress.IPv6Address('::1'))
|
>>> int(ipaddress.IPv6Address('::1'))
|
||||||
1
|
1
|
||||||
|
|
||||||
|
Note that IPv6 scoped addresses are converted to integers without scope zone ID.
|
||||||
|
|
||||||
|
|
||||||
Operators
|
Operators
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
@ -311,8 +328,9 @@ IPv6).
|
||||||
Comparison operators
|
Comparison operators
|
||||||
""""""""""""""""""""
|
""""""""""""""""""""
|
||||||
|
|
||||||
Address objects can be compared with the usual set of comparison operators. Some
|
Address objects can be compared with the usual set of comparison operators.
|
||||||
examples::
|
Same IPv6 addresses with different scope zone IDs are not equal.
|
||||||
|
Some examples::
|
||||||
|
|
||||||
>>> IPv4Address('127.0.0.2') > IPv4Address('127.0.0.1')
|
>>> IPv4Address('127.0.0.2') > IPv4Address('127.0.0.1')
|
||||||
True
|
True
|
||||||
|
|
@ -320,6 +338,10 @@ examples::
|
||||||
False
|
False
|
||||||
>>> IPv4Address('127.0.0.2') != IPv4Address('127.0.0.1')
|
>>> IPv4Address('127.0.0.2') != IPv4Address('127.0.0.1')
|
||||||
True
|
True
|
||||||
|
>>> IPv6Address('fe80::1234') == IPv6Address('fe80::1234%1')
|
||||||
|
False
|
||||||
|
>>> IPv6Address('fe80::1234%1') != IPv6Address('fe80::1234%2')
|
||||||
|
True
|
||||||
|
|
||||||
|
|
||||||
Arithmetic operators
|
Arithmetic operators
|
||||||
|
|
|
||||||
|
|
@ -78,15 +78,15 @@ created. Socket addresses are represented as follows:
|
||||||
Python programs.
|
Python programs.
|
||||||
|
|
||||||
- For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo,
|
- For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo,
|
||||||
scopeid)`` is used, where *flowinfo* and *scopeid* represent the ``sin6_flowinfo``
|
scope_id)`` is used, where *flowinfo* and *scope_id* represent the ``sin6_flowinfo``
|
||||||
and ``sin6_scope_id`` members in :const:`struct sockaddr_in6` in C. For
|
and ``sin6_scope_id`` members in :const:`struct sockaddr_in6` in C. For
|
||||||
:mod:`socket` module methods, *flowinfo* and *scopeid* can be omitted just for
|
:mod:`socket` module methods, *flowinfo* and *scope_id* can be omitted just for
|
||||||
backward compatibility. Note, however, omission of *scopeid* can cause problems
|
backward compatibility. Note, however, omission of *scope_id* can cause problems
|
||||||
in manipulating scoped IPv6 addresses.
|
in manipulating scoped IPv6 addresses.
|
||||||
|
|
||||||
.. versionchanged:: 3.7
|
.. versionchanged:: 3.7
|
||||||
For multicast addresses (with *scopeid* meaningful) *address* may not contain
|
For multicast addresses (with *scope_id* meaningful) *address* may not contain
|
||||||
``%scope`` (or ``zone id``) part. This information is superfluous and may
|
``%scope_id`` (or ``zone id``) part. This information is superfluous and may
|
||||||
be safely omitted (recommended).
|
be safely omitted (recommended).
|
||||||
|
|
||||||
- :const:`AF_NETLINK` sockets are represented as pairs ``(pid, groups)``.
|
- :const:`AF_NETLINK` sockets are represented as pairs ``(pid, groups)``.
|
||||||
|
|
@ -738,7 +738,7 @@ The :mod:`socket` module also offers various network-related services:
|
||||||
:const:`AI_CANONNAME` is part of the *flags* argument; else *canonname*
|
:const:`AI_CANONNAME` is part of the *flags* argument; else *canonname*
|
||||||
will be empty. *sockaddr* is a tuple describing a socket address, whose
|
will be empty. *sockaddr* is a tuple describing a socket address, whose
|
||||||
format depends on the returned *family* (a ``(address, port)`` 2-tuple for
|
format depends on the returned *family* (a ``(address, port)`` 2-tuple for
|
||||||
:const:`AF_INET`, a ``(address, port, flow info, scope id)`` 4-tuple for
|
:const:`AF_INET`, a ``(address, port, flowinfo, scope_id)`` 4-tuple for
|
||||||
:const:`AF_INET6`), and is meant to be passed to the :meth:`socket.connect`
|
:const:`AF_INET6`), and is meant to be passed to the :meth:`socket.connect`
|
||||||
method.
|
method.
|
||||||
|
|
||||||
|
|
@ -759,7 +759,7 @@ The :mod:`socket` module also offers various network-related services:
|
||||||
|
|
||||||
.. versionchanged:: 3.7
|
.. versionchanged:: 3.7
|
||||||
for IPv6 multicast addresses, string representing an address will not
|
for IPv6 multicast addresses, string representing an address will not
|
||||||
contain ``%scope`` part.
|
contain ``%scope_id`` part.
|
||||||
|
|
||||||
.. function:: getfqdn([name])
|
.. function:: getfqdn([name])
|
||||||
|
|
||||||
|
|
@ -827,8 +827,8 @@ The :mod:`socket` module also offers various network-related services:
|
||||||
or numeric address representation in *host*. Similarly, *port* can contain a
|
or numeric address representation in *host*. Similarly, *port* can contain a
|
||||||
string port name or a numeric port number.
|
string port name or a numeric port number.
|
||||||
|
|
||||||
For IPv6 addresses, ``%scope`` is appended to the host part if *sockaddr*
|
For IPv6 addresses, ``%scope_id`` is appended to the host part if *sockaddr*
|
||||||
contains meaningful *scopeid*. Usually this happens for multicast addresses.
|
contains meaningful *scope_id*. Usually this happens for multicast addresses.
|
||||||
|
|
||||||
For more information about *flags* you can consult :manpage:`getnameinfo(3)`.
|
For more information about *flags* you can consult :manpage:`getnameinfo(3)`.
|
||||||
|
|
||||||
|
|
@ -1354,7 +1354,7 @@ to sockets.
|
||||||
|
|
||||||
.. versionchanged:: 3.7
|
.. versionchanged:: 3.7
|
||||||
For multicast IPv6 address, first item of *address* does not contain
|
For multicast IPv6 address, first item of *address* does not contain
|
||||||
``%scope`` part anymore. In order to get full IPv6 address use
|
``%scope_id`` part anymore. In order to get full IPv6 address use
|
||||||
:func:`getnameinfo`.
|
:func:`getnameinfo`.
|
||||||
|
|
||||||
.. method:: socket.recvmsg(bufsize[, ancbufsize[, flags]])
|
.. method:: socket.recvmsg(bufsize[, ancbufsize[, flags]])
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,8 @@ library/ipaddress,,:db8,>>> ipaddress.IPv6Address('2001:db8::1000')
|
||||||
library/ipaddress,,::,>>> ipaddress.IPv6Address('2001:db8::1000')
|
library/ipaddress,,::,>>> ipaddress.IPv6Address('2001:db8::1000')
|
||||||
library/ipaddress,,:db8,IPv6Address('2001:db8::1000')
|
library/ipaddress,,:db8,IPv6Address('2001:db8::1000')
|
||||||
library/ipaddress,,::,IPv6Address('2001:db8::1000')
|
library/ipaddress,,::,IPv6Address('2001:db8::1000')
|
||||||
|
library/ipaddress,,::,IPv6Address('ff02::5678%1')
|
||||||
|
library/ipaddress,,::,fe80::1234
|
||||||
library/ipaddress,,:db8,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer"
|
library/ipaddress,,:db8,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer"
|
||||||
library/ipaddress,,::,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer"
|
library/ipaddress,,::,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer"
|
||||||
library/ipaddress,,::,"""::abc:7:def"""
|
library/ipaddress,,::,"""::abc:7:def"""
|
||||||
|
|
|
||||||
|
|
|
@ -213,6 +213,15 @@ now raises :exc:`ImportError` instead of :exc:`ValueError` for invalid relative
|
||||||
import attempts.
|
import attempts.
|
||||||
(Contributed by Ngalim Siregar in :issue:`37444`.)
|
(Contributed by Ngalim Siregar in :issue:`37444`.)
|
||||||
|
|
||||||
|
ipaddress
|
||||||
|
---------
|
||||||
|
|
||||||
|
:mod:`ipaddress` now supports IPv6 Scoped Addresses (IPv6 address with suffix ``%<scope_id>``).
|
||||||
|
|
||||||
|
Scoped IPv6 addresses can be parsed using :class:`ipaddress.IPv6Address`.
|
||||||
|
If present, scope zone ID is available through the :attr:`~ipaddress.IPv6Address.scope_id` attribute.
|
||||||
|
(Contributed by Oleksandr Pavliuk in :issue:`34788`.)
|
||||||
|
|
||||||
math
|
math
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1836,6 +1836,26 @@ class _BaseV6:
|
||||||
reverse_chars = self.exploded[::-1].replace(':', '')
|
reverse_chars = self.exploded[::-1].replace(':', '')
|
||||||
return '.'.join(reverse_chars) + '.ip6.arpa'
|
return '.'.join(reverse_chars) + '.ip6.arpa'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _split_scope_id(ip_str):
|
||||||
|
"""Helper function to parse IPv6 string address with scope id.
|
||||||
|
|
||||||
|
See RFC 4007 for details.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ip_str: A string, the IPv6 address.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(addr, scope_id) tuple.
|
||||||
|
|
||||||
|
"""
|
||||||
|
addr, sep, scope_id = ip_str.partition('%')
|
||||||
|
if not sep:
|
||||||
|
scope_id = None
|
||||||
|
elif not scope_id or '%' in scope_id:
|
||||||
|
raise AddressValueError('Invalid IPv6 address: "%r"' % ip_str)
|
||||||
|
return addr, scope_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_prefixlen(self):
|
def max_prefixlen(self):
|
||||||
return self._max_prefixlen
|
return self._max_prefixlen
|
||||||
|
|
@ -1849,7 +1869,7 @@ class IPv6Address(_BaseV6, _BaseAddress):
|
||||||
|
|
||||||
"""Represent and manipulate single IPv6 Addresses."""
|
"""Represent and manipulate single IPv6 Addresses."""
|
||||||
|
|
||||||
__slots__ = ('_ip', '__weakref__')
|
__slots__ = ('_ip', '_scope_id', '__weakref__')
|
||||||
|
|
||||||
def __init__(self, address):
|
def __init__(self, address):
|
||||||
"""Instantiate a new IPv6 address object.
|
"""Instantiate a new IPv6 address object.
|
||||||
|
|
@ -1872,12 +1892,14 @@ class IPv6Address(_BaseV6, _BaseAddress):
|
||||||
if isinstance(address, int):
|
if isinstance(address, int):
|
||||||
self._check_int_address(address)
|
self._check_int_address(address)
|
||||||
self._ip = address
|
self._ip = address
|
||||||
|
self._scope_id = None
|
||||||
return
|
return
|
||||||
|
|
||||||
# Constructing from a packed address
|
# Constructing from a packed address
|
||||||
if isinstance(address, bytes):
|
if isinstance(address, bytes):
|
||||||
self._check_packed_address(address, 16)
|
self._check_packed_address(address, 16)
|
||||||
self._ip = int.from_bytes(address, 'big')
|
self._ip = int.from_bytes(address, 'big')
|
||||||
|
self._scope_id = None
|
||||||
return
|
return
|
||||||
|
|
||||||
# Assume input argument to be string or any object representation
|
# Assume input argument to be string or any object representation
|
||||||
|
|
@ -1885,8 +1907,37 @@ class IPv6Address(_BaseV6, _BaseAddress):
|
||||||
addr_str = str(address)
|
addr_str = str(address)
|
||||||
if '/' in addr_str:
|
if '/' in addr_str:
|
||||||
raise AddressValueError("Unexpected '/' in %r" % address)
|
raise AddressValueError("Unexpected '/' in %r" % address)
|
||||||
|
addr_str, self._scope_id = self._split_scope_id(addr_str)
|
||||||
|
|
||||||
self._ip = self._ip_int_from_string(addr_str)
|
self._ip = self._ip_int_from_string(addr_str)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
ip_str = super().__str__()
|
||||||
|
return ip_str + '%' + self._scope_id if self._scope_id else ip_str
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self._ip, self._scope_id))
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
address_equal = super().__eq__(other)
|
||||||
|
if address_equal is NotImplemented:
|
||||||
|
return NotImplemented
|
||||||
|
if not address_equal:
|
||||||
|
return False
|
||||||
|
return self._scope_id == getattr(other, '_scope_id', None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scope_id(self):
|
||||||
|
"""Identifier of a particular zone of the address's scope.
|
||||||
|
|
||||||
|
See RFC 4007 for details.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string identifying the zone of the address if specified, else None.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self._scope_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def packed(self):
|
def packed(self):
|
||||||
"""The binary representation of this address."""
|
"""The binary representation of this address."""
|
||||||
|
|
@ -2040,7 +2091,7 @@ class IPv6Interface(IPv6Address):
|
||||||
return self.network.hostmask
|
return self.network.hostmask
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s/%d' % (self._string_from_ip_int(self._ip),
|
return '%s/%d' % (super().__str__(),
|
||||||
self._prefixlen)
|
self._prefixlen)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,15 @@ class CommonTestMixin_v6(CommonTestMixin):
|
||||||
assertBadLength(15)
|
assertBadLength(15)
|
||||||
assertBadLength(17)
|
assertBadLength(17)
|
||||||
|
|
||||||
|
def test_blank_scope_id(self):
|
||||||
|
address = ('::1%')
|
||||||
|
with self.assertAddressError('Invalid IPv6 address: "%r"', address):
|
||||||
|
self.factory(address)
|
||||||
|
|
||||||
|
def test_invalid_scope_id_with_percent(self):
|
||||||
|
address = ('::1%scope%')
|
||||||
|
with self.assertAddressError('Invalid IPv6 address: "%r"', address):
|
||||||
|
self.factory(address)
|
||||||
|
|
||||||
class AddressTestCase_v4(BaseTestCase, CommonTestMixin_v4):
|
class AddressTestCase_v4(BaseTestCase, CommonTestMixin_v4):
|
||||||
factory = ipaddress.IPv4Address
|
factory = ipaddress.IPv4Address
|
||||||
|
|
@ -328,24 +337,30 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
|
||||||
self.assertEqual(txt, format(v6, fmt))
|
self.assertEqual(txt, format(v6, fmt))
|
||||||
|
|
||||||
def test_network_passed_as_address(self):
|
def test_network_passed_as_address(self):
|
||||||
addr = "::1/24"
|
def assertBadSplit(addr):
|
||||||
with self.assertAddressError("Unexpected '/' in %r", addr):
|
msg = "Unexpected '/' in %r"
|
||||||
|
with self.assertAddressError(msg, addr):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
assertBadSplit("::1/24")
|
||||||
|
assertBadSplit("::1%scope_id/24")
|
||||||
|
|
||||||
def test_bad_address_split_v6_not_enough_parts(self):
|
def test_bad_address_split_v6_not_enough_parts(self):
|
||||||
def assertBadSplit(addr):
|
def assertBadSplit(addr):
|
||||||
msg = "At least 3 parts expected in %r"
|
msg = "At least 3 parts expected in %r"
|
||||||
with self.assertAddressError(msg, addr):
|
with self.assertAddressError(msg, addr.split('%')[0]):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
assertBadSplit(":")
|
assertBadSplit(":")
|
||||||
assertBadSplit(":1")
|
assertBadSplit(":1")
|
||||||
assertBadSplit("FEDC:9878")
|
assertBadSplit("FEDC:9878")
|
||||||
|
assertBadSplit(":%scope")
|
||||||
|
assertBadSplit(":1%scope")
|
||||||
|
assertBadSplit("FEDC:9878%scope")
|
||||||
|
|
||||||
def test_bad_address_split_v6_too_many_colons(self):
|
def test_bad_address_split_v6_too_many_colons(self):
|
||||||
def assertBadSplit(addr):
|
def assertBadSplit(addr):
|
||||||
msg = "At most 8 colons permitted in %r"
|
msg = "At most 8 colons permitted in %r"
|
||||||
with self.assertAddressError(msg, addr):
|
with self.assertAddressError(msg, addr.split('%')[0]):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
assertBadSplit("9:8:7:6:5:4:3::2:1")
|
assertBadSplit("9:8:7:6:5:4:3::2:1")
|
||||||
|
|
@ -355,10 +370,17 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
|
||||||
# A trailing IPv4 address is two parts
|
# A trailing IPv4 address is two parts
|
||||||
assertBadSplit("10:9:8:7:6:5:4:3:42.42.42.42")
|
assertBadSplit("10:9:8:7:6:5:4:3:42.42.42.42")
|
||||||
|
|
||||||
|
assertBadSplit("9:8:7:6:5:4:3::2:1%scope")
|
||||||
|
assertBadSplit("10:9:8:7:6:5:4:3:2:1%scope")
|
||||||
|
assertBadSplit("::8:7:6:5:4:3:2:1%scope")
|
||||||
|
assertBadSplit("8:7:6:5:4:3:2:1::%scope")
|
||||||
|
# A trailing IPv4 address is two parts
|
||||||
|
assertBadSplit("10:9:8:7:6:5:4:3:42.42.42.42%scope")
|
||||||
|
|
||||||
def test_bad_address_split_v6_too_many_parts(self):
|
def test_bad_address_split_v6_too_many_parts(self):
|
||||||
def assertBadSplit(addr):
|
def assertBadSplit(addr):
|
||||||
msg = "Exactly 8 parts expected without '::' in %r"
|
msg = "Exactly 8 parts expected without '::' in %r"
|
||||||
with self.assertAddressError(msg, addr):
|
with self.assertAddressError(msg, addr.split('%')[0]):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
assertBadSplit("3ffe:0:0:0:0:0:0:0:1")
|
assertBadSplit("3ffe:0:0:0:0:0:0:0:1")
|
||||||
|
|
@ -368,18 +390,26 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
|
||||||
assertBadSplit("9:8:7:6:5:4:3:42.42.42.42")
|
assertBadSplit("9:8:7:6:5:4:3:42.42.42.42")
|
||||||
assertBadSplit("7:6:5:4:3:42.42.42.42")
|
assertBadSplit("7:6:5:4:3:42.42.42.42")
|
||||||
|
|
||||||
|
assertBadSplit("3ffe:0:0:0:0:0:0:0:1%scope")
|
||||||
|
assertBadSplit("9:8:7:6:5:4:3:2:1%scope")
|
||||||
|
assertBadSplit("7:6:5:4:3:2:1%scope")
|
||||||
|
# A trailing IPv4 address is two parts
|
||||||
|
assertBadSplit("9:8:7:6:5:4:3:42.42.42.42%scope")
|
||||||
|
assertBadSplit("7:6:5:4:3:42.42.42.42%scope")
|
||||||
|
|
||||||
def test_bad_address_split_v6_too_many_parts_with_double_colon(self):
|
def test_bad_address_split_v6_too_many_parts_with_double_colon(self):
|
||||||
def assertBadSplit(addr):
|
def assertBadSplit(addr):
|
||||||
msg = "Expected at most 7 other parts with '::' in %r"
|
msg = "Expected at most 7 other parts with '::' in %r"
|
||||||
with self.assertAddressError(msg, addr):
|
with self.assertAddressError(msg, addr.split('%')[0]):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
assertBadSplit("1:2:3:4::5:6:7:8")
|
assertBadSplit("1:2:3:4::5:6:7:8")
|
||||||
|
assertBadSplit("1:2:3:4::5:6:7:8%scope")
|
||||||
|
|
||||||
def test_bad_address_split_v6_repeated_double_colon(self):
|
def test_bad_address_split_v6_repeated_double_colon(self):
|
||||||
def assertBadSplit(addr):
|
def assertBadSplit(addr):
|
||||||
msg = "At most one '::' permitted in %r"
|
msg = "At most one '::' permitted in %r"
|
||||||
with self.assertAddressError(msg, addr):
|
with self.assertAddressError(msg, addr.split('%')[0]):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
assertBadSplit("3ffe::1::1")
|
assertBadSplit("3ffe::1::1")
|
||||||
|
|
@ -393,10 +423,21 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
|
||||||
assertBadSplit(":::")
|
assertBadSplit(":::")
|
||||||
assertBadSplit('2001:db8:::1')
|
assertBadSplit('2001:db8:::1')
|
||||||
|
|
||||||
|
assertBadSplit("3ffe::1::1%scope")
|
||||||
|
assertBadSplit("1::2::3::4:5%scope")
|
||||||
|
assertBadSplit("2001::db:::1%scope")
|
||||||
|
assertBadSplit("3ffe::1::%scope")
|
||||||
|
assertBadSplit("::3ffe::1%scope")
|
||||||
|
assertBadSplit(":3ffe::1::1%scope")
|
||||||
|
assertBadSplit("3ffe::1::1:%scope")
|
||||||
|
assertBadSplit(":3ffe::1::1:%scope")
|
||||||
|
assertBadSplit(":::%scope")
|
||||||
|
assertBadSplit('2001:db8:::1%scope')
|
||||||
|
|
||||||
def test_bad_address_split_v6_leading_colon(self):
|
def test_bad_address_split_v6_leading_colon(self):
|
||||||
def assertBadSplit(addr):
|
def assertBadSplit(addr):
|
||||||
msg = "Leading ':' only permitted as part of '::' in %r"
|
msg = "Leading ':' only permitted as part of '::' in %r"
|
||||||
with self.assertAddressError(msg, addr):
|
with self.assertAddressError(msg, addr.split('%')[0]):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
assertBadSplit(":2001:db8::1")
|
assertBadSplit(":2001:db8::1")
|
||||||
|
|
@ -404,10 +445,15 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
|
||||||
assertBadSplit(":1:2:3:4:5:6:")
|
assertBadSplit(":1:2:3:4:5:6:")
|
||||||
assertBadSplit(":6:5:4:3:2:1::")
|
assertBadSplit(":6:5:4:3:2:1::")
|
||||||
|
|
||||||
|
assertBadSplit(":2001:db8::1%scope")
|
||||||
|
assertBadSplit(":1:2:3:4:5:6:7%scope")
|
||||||
|
assertBadSplit(":1:2:3:4:5:6:%scope")
|
||||||
|
assertBadSplit(":6:5:4:3:2:1::%scope")
|
||||||
|
|
||||||
def test_bad_address_split_v6_trailing_colon(self):
|
def test_bad_address_split_v6_trailing_colon(self):
|
||||||
def assertBadSplit(addr):
|
def assertBadSplit(addr):
|
||||||
msg = "Trailing ':' only permitted as part of '::' in %r"
|
msg = "Trailing ':' only permitted as part of '::' in %r"
|
||||||
with self.assertAddressError(msg, addr):
|
with self.assertAddressError(msg, addr.split('%')[0]):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
assertBadSplit("2001:db8::1:")
|
assertBadSplit("2001:db8::1:")
|
||||||
|
|
@ -415,9 +461,14 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
|
||||||
assertBadSplit("::1.2.3.4:")
|
assertBadSplit("::1.2.3.4:")
|
||||||
assertBadSplit("::7:6:5:4:3:2:")
|
assertBadSplit("::7:6:5:4:3:2:")
|
||||||
|
|
||||||
|
assertBadSplit("2001:db8::1:%scope")
|
||||||
|
assertBadSplit("1:2:3:4:5:6:7:%scope")
|
||||||
|
assertBadSplit("::1.2.3.4:%scope")
|
||||||
|
assertBadSplit("::7:6:5:4:3:2:%scope")
|
||||||
|
|
||||||
def test_bad_v4_part_in(self):
|
def test_bad_v4_part_in(self):
|
||||||
def assertBadAddressPart(addr, v4_error):
|
def assertBadAddressPart(addr, v4_error):
|
||||||
with self.assertAddressError("%s in %r", v4_error, addr):
|
with self.assertAddressError("%s in %r", v4_error, addr.split('%')[0]):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
assertBadAddressPart("3ffe::1.net", "Expected 4 octets in '1.net'")
|
assertBadAddressPart("3ffe::1.net", "Expected 4 octets in '1.net'")
|
||||||
|
|
@ -431,9 +482,20 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
|
||||||
"Only decimal digits permitted in 'net' "
|
"Only decimal digits permitted in 'net' "
|
||||||
"in '1.1.1.net'")
|
"in '1.1.1.net'")
|
||||||
|
|
||||||
|
assertBadAddressPart("3ffe::1.net%scope", "Expected 4 octets in '1.net'")
|
||||||
|
assertBadAddressPart("3ffe::127.0.1%scope",
|
||||||
|
"Expected 4 octets in '127.0.1'")
|
||||||
|
assertBadAddressPart("::1.2.3%scope",
|
||||||
|
"Expected 4 octets in '1.2.3'")
|
||||||
|
assertBadAddressPart("::1.2.3.4.5%scope",
|
||||||
|
"Expected 4 octets in '1.2.3.4.5'")
|
||||||
|
assertBadAddressPart("3ffe::1.1.1.net%scope",
|
||||||
|
"Only decimal digits permitted in 'net' "
|
||||||
|
"in '1.1.1.net'")
|
||||||
|
|
||||||
def test_invalid_characters(self):
|
def test_invalid_characters(self):
|
||||||
def assertBadPart(addr, part):
|
def assertBadPart(addr, part):
|
||||||
msg = "Only hex digits permitted in %r in %r" % (part, addr)
|
msg = "Only hex digits permitted in %r in %r" % (part, addr.split('%')[0])
|
||||||
with self.assertAddressError(re.escape(msg)):
|
with self.assertAddressError(re.escape(msg)):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
|
|
@ -444,10 +506,17 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
|
||||||
assertBadPart("1.2.3.4::", "1.2.3.4")
|
assertBadPart("1.2.3.4::", "1.2.3.4")
|
||||||
assertBadPart('1234:axy::b', "axy")
|
assertBadPart('1234:axy::b', "axy")
|
||||||
|
|
||||||
|
assertBadPart("3ffe::goog%scope", "goog")
|
||||||
|
assertBadPart("3ffe::-0%scope", "-0")
|
||||||
|
assertBadPart("3ffe::+0%scope", "+0")
|
||||||
|
assertBadPart("3ffe::-1%scope", "-1")
|
||||||
|
assertBadPart("1.2.3.4::%scope", "1.2.3.4")
|
||||||
|
assertBadPart('1234:axy::b%scope', "axy")
|
||||||
|
|
||||||
def test_part_length(self):
|
def test_part_length(self):
|
||||||
def assertBadPart(addr, part):
|
def assertBadPart(addr, part):
|
||||||
msg = "At most 4 characters permitted in %r in %r"
|
msg = "At most 4 characters permitted in %r in %r"
|
||||||
with self.assertAddressError(msg, part, addr):
|
with self.assertAddressError(msg, part, addr.split('%')[0]):
|
||||||
ipaddress.IPv6Address(addr)
|
ipaddress.IPv6Address(addr)
|
||||||
|
|
||||||
assertBadPart("::00000", "00000")
|
assertBadPart("::00000", "00000")
|
||||||
|
|
@ -455,11 +524,17 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
|
||||||
assertBadPart("02001:db8::", "02001")
|
assertBadPart("02001:db8::", "02001")
|
||||||
assertBadPart('2001:888888::1', "888888")
|
assertBadPart('2001:888888::1', "888888")
|
||||||
|
|
||||||
|
assertBadPart("::00000%scope", "00000")
|
||||||
|
assertBadPart("3ffe::10000%scope", "10000")
|
||||||
|
assertBadPart("02001:db8::%scope", "02001")
|
||||||
|
assertBadPart('2001:888888::1%scope', "888888")
|
||||||
|
|
||||||
def test_pickle(self):
|
def test_pickle(self):
|
||||||
self.pickle_test('2001:db8::')
|
self.pickle_test('2001:db8::')
|
||||||
|
|
||||||
def test_weakref(self):
|
def test_weakref(self):
|
||||||
weakref.ref(self.factory('2001:db8::'))
|
weakref.ref(self.factory('2001:db8::'))
|
||||||
|
weakref.ref(self.factory('2001:db8::%scope'))
|
||||||
|
|
||||||
|
|
||||||
class NetmaskTestMixin_v4(CommonTestMixin_v4):
|
class NetmaskTestMixin_v4(CommonTestMixin_v4):
|
||||||
|
|
@ -617,11 +692,20 @@ class NetmaskTestMixin_v6(CommonTestMixin_v6):
|
||||||
# IPv6Network has prefixlen, but IPv6Interface doesn't.
|
# IPv6Network has prefixlen, but IPv6Interface doesn't.
|
||||||
# Should we add it to IPv4Interface too? (bpo-36392)
|
# Should we add it to IPv4Interface too? (bpo-36392)
|
||||||
|
|
||||||
|
scoped_net = self.factory('::1%scope')
|
||||||
|
self.assertEqual(str(scoped_net), '::1%scope/128')
|
||||||
|
self.assertEqual(str(scoped_net.netmask), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
|
||||||
|
self.assertEqual(str(scoped_net.hostmask), '::')
|
||||||
|
|
||||||
def test_split_netmask(self):
|
def test_split_netmask(self):
|
||||||
addr = "cafe:cafe::/128/190"
|
addr = "cafe:cafe::/128/190"
|
||||||
with self.assertAddressError("Only one '/' permitted in %r" % addr):
|
with self.assertAddressError("Only one '/' permitted in %r" % addr):
|
||||||
self.factory(addr)
|
self.factory(addr)
|
||||||
|
|
||||||
|
scoped_addr = "cafe:cafe::%scope/128/190"
|
||||||
|
with self.assertAddressError("Only one '/' permitted in %r" % scoped_addr):
|
||||||
|
self.factory(scoped_addr)
|
||||||
|
|
||||||
def test_address_errors(self):
|
def test_address_errors(self):
|
||||||
def assertBadAddress(addr, details):
|
def assertBadAddress(addr, details):
|
||||||
with self.assertAddressError(details):
|
with self.assertAddressError(details):
|
||||||
|
|
@ -634,6 +718,13 @@ class NetmaskTestMixin_v6(CommonTestMixin_v6):
|
||||||
assertBadAddress("10/8", "At least 3 parts")
|
assertBadAddress("10/8", "At least 3 parts")
|
||||||
assertBadAddress("1234:axy::b", "Only hex digits")
|
assertBadAddress("1234:axy::b", "Only hex digits")
|
||||||
|
|
||||||
|
assertBadAddress("/%scope", "Address cannot be empty")
|
||||||
|
assertBadAddress("/%scope8", "Address cannot be empty")
|
||||||
|
assertBadAddress("google.com%scope", "At least 3 parts")
|
||||||
|
assertBadAddress("1.2.3.4%scope", "At least 3 parts")
|
||||||
|
assertBadAddress("10%scope/8", "At least 3 parts")
|
||||||
|
assertBadAddress("1234:axy::b%scope", "Only hex digits")
|
||||||
|
|
||||||
def test_valid_netmask(self):
|
def test_valid_netmask(self):
|
||||||
# We only support CIDR for IPv6, because expanded netmasks are not
|
# We only support CIDR for IPv6, because expanded netmasks are not
|
||||||
# standard notation.
|
# standard notation.
|
||||||
|
|
@ -645,6 +736,14 @@ class NetmaskTestMixin_v6(CommonTestMixin_v6):
|
||||||
# Zero prefix is treated as decimal.
|
# Zero prefix is treated as decimal.
|
||||||
self.assertEqual(str(self.factory('::/0%d' % i)), net_str)
|
self.assertEqual(str(self.factory('::/0%d' % i)), net_str)
|
||||||
|
|
||||||
|
self.assertEqual(str(self.factory('2001:db8::%scope/32')), '2001:db8::%scope/32')
|
||||||
|
for i in range(0, 129):
|
||||||
|
# Generate and re-parse the CIDR format (trivial).
|
||||||
|
net_str = '::/%d' % i
|
||||||
|
self.assertEqual(str(self.factory(net_str)), net_str)
|
||||||
|
# Zero prefix is treated as decimal.
|
||||||
|
self.assertEqual(str(self.factory('::/0%d' % i)), net_str)
|
||||||
|
|
||||||
def test_netmask_errors(self):
|
def test_netmask_errors(self):
|
||||||
def assertBadNetmask(addr, netmask):
|
def assertBadNetmask(addr, netmask):
|
||||||
msg = "%r is not a valid netmask" % netmask
|
msg = "%r is not a valid netmask" % netmask
|
||||||
|
|
@ -663,6 +762,8 @@ class NetmaskTestMixin_v6(CommonTestMixin_v6):
|
||||||
assertBadNetmask("::1", "pudding")
|
assertBadNetmask("::1", "pudding")
|
||||||
assertBadNetmask("::", "::")
|
assertBadNetmask("::", "::")
|
||||||
|
|
||||||
|
assertBadNetmask("::1%scope", "pudding")
|
||||||
|
|
||||||
def test_netmask_in_tuple_errors(self):
|
def test_netmask_in_tuple_errors(self):
|
||||||
def assertBadNetmask(addr, netmask):
|
def assertBadNetmask(addr, netmask):
|
||||||
msg = "%r is not a valid netmask" % netmask
|
msg = "%r is not a valid netmask" % netmask
|
||||||
|
|
@ -670,12 +771,15 @@ class NetmaskTestMixin_v6(CommonTestMixin_v6):
|
||||||
self.factory((addr, netmask))
|
self.factory((addr, netmask))
|
||||||
assertBadNetmask("::1", -1)
|
assertBadNetmask("::1", -1)
|
||||||
assertBadNetmask("::1", 129)
|
assertBadNetmask("::1", 129)
|
||||||
|
assertBadNetmask("::1%scope", 129)
|
||||||
|
|
||||||
def test_pickle(self):
|
def test_pickle(self):
|
||||||
self.pickle_test('2001:db8::1000/124')
|
self.pickle_test('2001:db8::1000/124')
|
||||||
self.pickle_test('2001:db8::1000/127') # IPV6LENGTH - 1
|
self.pickle_test('2001:db8::1000/127') # IPV6LENGTH - 1
|
||||||
self.pickle_test('2001:db8::1000') # IPV6LENGTH
|
self.pickle_test('2001:db8::1000') # IPV6LENGTH
|
||||||
|
|
||||||
|
self.pickle_test('2001:db8::1000%scope') # IPV6LENGTH
|
||||||
|
|
||||||
|
|
||||||
class InterfaceTestCase_v6(BaseTestCase, NetmaskTestMixin_v6):
|
class InterfaceTestCase_v6(BaseTestCase, NetmaskTestMixin_v6):
|
||||||
factory = ipaddress.IPv6Interface
|
factory = ipaddress.IPv6Interface
|
||||||
|
|
@ -702,6 +806,13 @@ class NetworkTestCase_v6(BaseTestCase, NetmaskTestMixin_v6):
|
||||||
self.factory('2000:aaa::/48').subnet_of(
|
self.factory('2000:aaa::/48').subnet_of(
|
||||||
self.factory('2000:aaa::/56')))
|
self.factory('2000:aaa::/56')))
|
||||||
|
|
||||||
|
self.assertFalse(
|
||||||
|
self.factory('2000:999::%scope/56').subnet_of(
|
||||||
|
self.factory('2000:aaa::%scope/48')))
|
||||||
|
self.assertTrue(
|
||||||
|
self.factory('2000:aaa::%scope/56').subnet_of(
|
||||||
|
self.factory('2000:aaa::%scope/48')))
|
||||||
|
|
||||||
def test_supernet_of(self):
|
def test_supernet_of(self):
|
||||||
# containee left of container
|
# containee left of container
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
|
|
@ -748,13 +859,19 @@ class ComparisonTests(unittest.TestCase):
|
||||||
v6addr = ipaddress.IPv6Address(1)
|
v6addr = ipaddress.IPv6Address(1)
|
||||||
v6net = ipaddress.IPv6Network(1)
|
v6net = ipaddress.IPv6Network(1)
|
||||||
v6intf = ipaddress.IPv6Interface(1)
|
v6intf = ipaddress.IPv6Interface(1)
|
||||||
|
v6addr_scoped = ipaddress.IPv6Address('::1%scope')
|
||||||
|
v6net_scoped= ipaddress.IPv6Network('::1%scope')
|
||||||
|
v6intf_scoped= ipaddress.IPv6Interface('::1%scope')
|
||||||
|
|
||||||
v4_addresses = [v4addr, v4intf]
|
v4_addresses = [v4addr, v4intf]
|
||||||
v4_objects = v4_addresses + [v4net]
|
v4_objects = v4_addresses + [v4net]
|
||||||
v6_addresses = [v6addr, v6intf]
|
v6_addresses = [v6addr, v6intf]
|
||||||
v6_objects = v6_addresses + [v6net]
|
v6_objects = v6_addresses + [v6net]
|
||||||
|
v6_scoped_addresses = [v6addr_scoped, v6intf_scoped]
|
||||||
|
v6_scoped_objects = v6_scoped_addresses + [v6net_scoped]
|
||||||
|
|
||||||
objects = v4_objects + v6_objects
|
objects = v4_objects + v6_objects
|
||||||
|
objects_with_scoped = objects + v6_scoped_objects
|
||||||
|
|
||||||
v4addr2 = ipaddress.IPv4Address(2)
|
v4addr2 = ipaddress.IPv4Address(2)
|
||||||
v4net2 = ipaddress.IPv4Network(2)
|
v4net2 = ipaddress.IPv4Network(2)
|
||||||
|
|
@ -762,11 +879,14 @@ class ComparisonTests(unittest.TestCase):
|
||||||
v6addr2 = ipaddress.IPv6Address(2)
|
v6addr2 = ipaddress.IPv6Address(2)
|
||||||
v6net2 = ipaddress.IPv6Network(2)
|
v6net2 = ipaddress.IPv6Network(2)
|
||||||
v6intf2 = ipaddress.IPv6Interface(2)
|
v6intf2 = ipaddress.IPv6Interface(2)
|
||||||
|
v6addr2_scoped = ipaddress.IPv6Address('::2%scope')
|
||||||
|
v6net2_scoped = ipaddress.IPv6Network('::2%scope')
|
||||||
|
v6intf2_scoped = ipaddress.IPv6Interface('::2%scope')
|
||||||
|
|
||||||
def test_foreign_type_equality(self):
|
def test_foreign_type_equality(self):
|
||||||
# __eq__ should never raise TypeError directly
|
# __eq__ should never raise TypeError directly
|
||||||
other = object()
|
other = object()
|
||||||
for obj in self.objects:
|
for obj in self.objects_with_scoped:
|
||||||
self.assertNotEqual(obj, other)
|
self.assertNotEqual(obj, other)
|
||||||
self.assertFalse(obj == other)
|
self.assertFalse(obj == other)
|
||||||
self.assertEqual(obj.__eq__(other), NotImplemented)
|
self.assertEqual(obj.__eq__(other), NotImplemented)
|
||||||
|
|
@ -781,8 +901,17 @@ class ComparisonTests(unittest.TestCase):
|
||||||
continue
|
continue
|
||||||
self.assertNotEqual(lhs, rhs)
|
self.assertNotEqual(lhs, rhs)
|
||||||
|
|
||||||
|
def test_scoped_ipv6_equality(self):
|
||||||
|
for lhs, rhs in zip(self.v6_objects, self.v6_scoped_objects):
|
||||||
|
self.assertNotEqual(lhs, rhs)
|
||||||
|
|
||||||
|
def test_v4_with_v6_scoped_equality(self):
|
||||||
|
for lhs in self.v4_objects:
|
||||||
|
for rhs in self.v6_scoped_objects:
|
||||||
|
self.assertNotEqual(lhs, rhs)
|
||||||
|
|
||||||
def test_same_type_equality(self):
|
def test_same_type_equality(self):
|
||||||
for obj in self.objects:
|
for obj in self.objects_with_scoped:
|
||||||
self.assertEqual(obj, obj)
|
self.assertEqual(obj, obj)
|
||||||
self.assertLessEqual(obj, obj)
|
self.assertLessEqual(obj, obj)
|
||||||
self.assertGreaterEqual(obj, obj)
|
self.assertGreaterEqual(obj, obj)
|
||||||
|
|
@ -795,6 +924,9 @@ class ComparisonTests(unittest.TestCase):
|
||||||
(self.v6addr, self.v6addr2),
|
(self.v6addr, self.v6addr2),
|
||||||
(self.v6net, self.v6net2),
|
(self.v6net, self.v6net2),
|
||||||
(self.v6intf, self.v6intf2),
|
(self.v6intf, self.v6intf2),
|
||||||
|
(self.v6addr_scoped, self.v6addr2_scoped),
|
||||||
|
(self.v6net_scoped, self.v6net2_scoped),
|
||||||
|
(self.v6intf_scoped, self.v6intf2_scoped),
|
||||||
):
|
):
|
||||||
self.assertNotEqual(lhs, rhs)
|
self.assertNotEqual(lhs, rhs)
|
||||||
self.assertLess(lhs, rhs)
|
self.assertLess(lhs, rhs)
|
||||||
|
|
@ -809,16 +941,21 @@ class ComparisonTests(unittest.TestCase):
|
||||||
def test_containment(self):
|
def test_containment(self):
|
||||||
for obj in self.v4_addresses:
|
for obj in self.v4_addresses:
|
||||||
self.assertIn(obj, self.v4net)
|
self.assertIn(obj, self.v4net)
|
||||||
for obj in self.v6_addresses:
|
for obj in self.v6_addresses + self.v6_scoped_addresses:
|
||||||
self.assertIn(obj, self.v6net)
|
self.assertIn(obj, self.v6net)
|
||||||
for obj in self.v4_objects + [self.v6net]:
|
for obj in self.v6_addresses + self.v6_scoped_addresses:
|
||||||
|
self.assertIn(obj, self.v6net_scoped)
|
||||||
|
|
||||||
|
for obj in self.v4_objects + [self.v6net, self.v6net_scoped]:
|
||||||
self.assertNotIn(obj, self.v6net)
|
self.assertNotIn(obj, self.v6net)
|
||||||
for obj in self.v6_objects + [self.v4net]:
|
for obj in self.v4_objects + [self.v6net, self.v6net_scoped]:
|
||||||
|
self.assertNotIn(obj, self.v6net_scoped)
|
||||||
|
for obj in self.v6_objects + self.v6_scoped_objects + [self.v4net]:
|
||||||
self.assertNotIn(obj, self.v4net)
|
self.assertNotIn(obj, self.v4net)
|
||||||
|
|
||||||
def test_mixed_type_ordering(self):
|
def test_mixed_type_ordering(self):
|
||||||
for lhs in self.objects:
|
for lhs in self.objects_with_scoped:
|
||||||
for rhs in self.objects:
|
for rhs in self.objects_with_scoped:
|
||||||
if isinstance(lhs, type(rhs)) or isinstance(rhs, type(lhs)):
|
if isinstance(lhs, type(rhs)) or isinstance(rhs, type(lhs)):
|
||||||
continue
|
continue
|
||||||
self.assertRaises(TypeError, lambda: lhs < rhs)
|
self.assertRaises(TypeError, lambda: lhs < rhs)
|
||||||
|
|
@ -828,7 +965,7 @@ class ComparisonTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_foreign_type_ordering(self):
|
def test_foreign_type_ordering(self):
|
||||||
other = object()
|
other = object()
|
||||||
for obj in self.objects:
|
for obj in self.objects_with_scoped:
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
obj < other
|
obj < other
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
|
|
@ -850,14 +987,18 @@ class ComparisonTests(unittest.TestCase):
|
||||||
# with get_mixed_type_key, you can sort addresses and network.
|
# with get_mixed_type_key, you can sort addresses and network.
|
||||||
v4_ordered = [self.v4addr, self.v4net, self.v4intf]
|
v4_ordered = [self.v4addr, self.v4net, self.v4intf]
|
||||||
v6_ordered = [self.v6addr, self.v6net, self.v6intf]
|
v6_ordered = [self.v6addr, self.v6net, self.v6intf]
|
||||||
|
v6_scoped_ordered = [self.v6addr_scoped, self.v6net_scoped, self.v6intf_scoped]
|
||||||
self.assertEqual(v4_ordered,
|
self.assertEqual(v4_ordered,
|
||||||
sorted(self.v4_objects,
|
sorted(self.v4_objects,
|
||||||
key=ipaddress.get_mixed_type_key))
|
key=ipaddress.get_mixed_type_key))
|
||||||
self.assertEqual(v6_ordered,
|
self.assertEqual(v6_ordered,
|
||||||
sorted(self.v6_objects,
|
sorted(self.v6_objects,
|
||||||
key=ipaddress.get_mixed_type_key))
|
key=ipaddress.get_mixed_type_key))
|
||||||
self.assertEqual(v4_ordered + v6_ordered,
|
self.assertEqual(v6_scoped_ordered,
|
||||||
sorted(self.objects,
|
sorted(self.v6_scoped_objects,
|
||||||
|
key=ipaddress.get_mixed_type_key))
|
||||||
|
self.assertEqual(v4_ordered + v6_scoped_ordered,
|
||||||
|
sorted(self.v4_objects + self.v6_scoped_objects,
|
||||||
key=ipaddress.get_mixed_type_key))
|
key=ipaddress.get_mixed_type_key))
|
||||||
self.assertEqual(NotImplemented, ipaddress.get_mixed_type_key(object))
|
self.assertEqual(NotImplemented, ipaddress.get_mixed_type_key(object))
|
||||||
|
|
||||||
|
|
@ -867,6 +1008,8 @@ class ComparisonTests(unittest.TestCase):
|
||||||
v4net = ipaddress.ip_network('1.1.1.1')
|
v4net = ipaddress.ip_network('1.1.1.1')
|
||||||
v6addr = ipaddress.ip_address('::1')
|
v6addr = ipaddress.ip_address('::1')
|
||||||
v6net = ipaddress.ip_network('::1')
|
v6net = ipaddress.ip_network('::1')
|
||||||
|
v6addr_scoped = ipaddress.ip_address('::1%scope')
|
||||||
|
v6net_scoped = ipaddress.ip_network('::1%scope')
|
||||||
|
|
||||||
self.assertRaises(TypeError, v4addr.__lt__, v6addr)
|
self.assertRaises(TypeError, v4addr.__lt__, v6addr)
|
||||||
self.assertRaises(TypeError, v4addr.__gt__, v6addr)
|
self.assertRaises(TypeError, v4addr.__gt__, v6addr)
|
||||||
|
|
@ -878,6 +1021,16 @@ class ComparisonTests(unittest.TestCase):
|
||||||
self.assertRaises(TypeError, v6net.__lt__, v4net)
|
self.assertRaises(TypeError, v6net.__lt__, v4net)
|
||||||
self.assertRaises(TypeError, v6net.__gt__, v4net)
|
self.assertRaises(TypeError, v6net.__gt__, v4net)
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, v4addr.__lt__, v6addr_scoped)
|
||||||
|
self.assertRaises(TypeError, v4addr.__gt__, v6addr_scoped)
|
||||||
|
self.assertRaises(TypeError, v4net.__lt__, v6net_scoped)
|
||||||
|
self.assertRaises(TypeError, v4net.__gt__, v6net_scoped)
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, v6addr_scoped.__lt__, v4addr)
|
||||||
|
self.assertRaises(TypeError, v6addr_scoped.__gt__, v4addr)
|
||||||
|
self.assertRaises(TypeError, v6net_scoped.__lt__, v4net)
|
||||||
|
self.assertRaises(TypeError, v6net_scoped.__gt__, v4net)
|
||||||
|
|
||||||
|
|
||||||
class IpaddrUnitTest(unittest.TestCase):
|
class IpaddrUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
@ -891,12 +1044,19 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.ipv6_interface = ipaddress.IPv6Interface(
|
self.ipv6_interface = ipaddress.IPv6Interface(
|
||||||
'2001:658:22a:cafe:200:0:0:1/64')
|
'2001:658:22a:cafe:200:0:0:1/64')
|
||||||
self.ipv6_network = ipaddress.IPv6Network('2001:658:22a:cafe::/64')
|
self.ipv6_network = ipaddress.IPv6Network('2001:658:22a:cafe::/64')
|
||||||
|
self.ipv6_scoped_address = ipaddress.IPv6Interface(
|
||||||
|
'2001:658:22a:cafe:200:0:0:1%scope')
|
||||||
|
self.ipv6_scoped_interface = ipaddress.IPv6Interface(
|
||||||
|
'2001:658:22a:cafe:200:0:0:1%scope/64')
|
||||||
|
self.ipv6_scoped_network = ipaddress.IPv6Network('2001:658:22a:cafe::%scope/64')
|
||||||
|
|
||||||
def testRepr(self):
|
def testRepr(self):
|
||||||
self.assertEqual("IPv4Interface('1.2.3.4/32')",
|
self.assertEqual("IPv4Interface('1.2.3.4/32')",
|
||||||
repr(ipaddress.IPv4Interface('1.2.3.4')))
|
repr(ipaddress.IPv4Interface('1.2.3.4')))
|
||||||
self.assertEqual("IPv6Interface('::1/128')",
|
self.assertEqual("IPv6Interface('::1/128')",
|
||||||
repr(ipaddress.IPv6Interface('::1')))
|
repr(ipaddress.IPv6Interface('::1')))
|
||||||
|
self.assertEqual("IPv6Interface('::1%scope/128')",
|
||||||
|
repr(ipaddress.IPv6Interface('::1%scope')))
|
||||||
|
|
||||||
# issue #16531: constructing IPv4Network from an (address, mask) tuple
|
# issue #16531: constructing IPv4Network from an (address, mask) tuple
|
||||||
def testIPv4Tuple(self):
|
def testIPv4Tuple(self):
|
||||||
|
|
@ -983,6 +1143,8 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(ipaddress.IPv6Network((ip, '96')),
|
self.assertEqual(ipaddress.IPv6Network((ip, '96')),
|
||||||
net)
|
net)
|
||||||
|
|
||||||
|
ip_scoped = ipaddress.IPv6Address('2001:db8::%scope')
|
||||||
|
|
||||||
# strict=True and host bits set
|
# strict=True and host bits set
|
||||||
ip = ipaddress.IPv6Address('2001:db8::1')
|
ip = ipaddress.IPv6Address('2001:db8::1')
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
|
|
@ -1011,6 +1173,13 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
(42540766411282592856903984951653826561, '96')),
|
(42540766411282592856903984951653826561, '96')),
|
||||||
ipaddress.IPv6Interface('2001:db8::1/96'))
|
ipaddress.IPv6Interface('2001:db8::1/96'))
|
||||||
|
|
||||||
|
ip_scoped = ipaddress.IPv6Address('2001:db8::1%scope')
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
ipaddress.IPv6Network(('2001:db8::1%scope', 96))
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
ipaddress.IPv6Network((ip_scoped, 96))
|
||||||
|
# strict=False and host bits set
|
||||||
|
|
||||||
# issue57
|
# issue57
|
||||||
def testAddressIntMath(self):
|
def testAddressIntMath(self):
|
||||||
self.assertEqual(ipaddress.IPv4Address('1.1.1.1') + 255,
|
self.assertEqual(ipaddress.IPv4Address('1.1.1.1') + 255,
|
||||||
|
|
@ -1021,6 +1190,10 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
ipaddress.IPv6Address('::ffff'))
|
ipaddress.IPv6Address('::ffff'))
|
||||||
self.assertEqual(ipaddress.IPv6Address('::ffff') - (2**16 - 2),
|
self.assertEqual(ipaddress.IPv6Address('::ffff') - (2**16 - 2),
|
||||||
ipaddress.IPv6Address('::1'))
|
ipaddress.IPv6Address('::1'))
|
||||||
|
self.assertNotEqual(ipaddress.IPv6Address('::1%scope') + (2**16 - 2),
|
||||||
|
ipaddress.IPv6Address('::ffff%scope'))
|
||||||
|
self.assertNotEqual(ipaddress.IPv6Address('::ffff%scope') - (2**16 - 2),
|
||||||
|
ipaddress.IPv6Address('::1%scope'))
|
||||||
|
|
||||||
def testInvalidIntToBytes(self):
|
def testInvalidIntToBytes(self):
|
||||||
self.assertRaises(ValueError, ipaddress.v4_int_to_packed, -1)
|
self.assertRaises(ValueError, ipaddress.v4_int_to_packed, -1)
|
||||||
|
|
@ -1053,6 +1226,12 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
'2001:658:22a:cafe::')
|
'2001:658:22a:cafe::')
|
||||||
self.assertEqual(str(self.ipv6_network.hostmask),
|
self.assertEqual(str(self.ipv6_network.hostmask),
|
||||||
'::ffff:ffff:ffff:ffff')
|
'::ffff:ffff:ffff:ffff')
|
||||||
|
self.assertEqual(int(self.ipv6_scoped_network.network_address),
|
||||||
|
42540616829182469433403647294022090752)
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_network.network_address),
|
||||||
|
'2001:658:22a:cafe::%scope')
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_network.hostmask),
|
||||||
|
'::ffff:ffff:ffff:ffff')
|
||||||
|
|
||||||
def testIpFromInt(self):
|
def testIpFromInt(self):
|
||||||
self.assertEqual(self.ipv4_interface._ip,
|
self.assertEqual(self.ipv4_interface._ip,
|
||||||
|
|
@ -1060,17 +1239,23 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
|
|
||||||
ipv4 = ipaddress.ip_network('1.2.3.4')
|
ipv4 = ipaddress.ip_network('1.2.3.4')
|
||||||
ipv6 = ipaddress.ip_network('2001:658:22a:cafe:200:0:0:1')
|
ipv6 = ipaddress.ip_network('2001:658:22a:cafe:200:0:0:1')
|
||||||
|
ipv6_scoped = ipaddress.ip_network('2001:658:22a:cafe:200:0:0:1%scope')
|
||||||
self.assertEqual(ipv4, ipaddress.ip_network(int(ipv4.network_address)))
|
self.assertEqual(ipv4, ipaddress.ip_network(int(ipv4.network_address)))
|
||||||
self.assertEqual(ipv6, ipaddress.ip_network(int(ipv6.network_address)))
|
self.assertEqual(ipv6, ipaddress.ip_network(int(ipv6.network_address)))
|
||||||
|
self.assertNotEqual(ipv6_scoped, ipaddress.ip_network(int(ipv6_scoped.network_address)))
|
||||||
|
|
||||||
v6_int = 42540616829182469433547762482097946625
|
v6_int = 42540616829182469433547762482097946625
|
||||||
self.assertEqual(self.ipv6_interface._ip,
|
self.assertEqual(self.ipv6_interface._ip,
|
||||||
ipaddress.IPv6Interface(v6_int)._ip)
|
ipaddress.IPv6Interface(v6_int)._ip)
|
||||||
|
self.assertEqual(self.ipv6_scoped_interface._ip,
|
||||||
|
ipaddress.IPv6Interface(v6_int)._ip)
|
||||||
|
|
||||||
self.assertEqual(ipaddress.ip_network(self.ipv4_address._ip).version,
|
self.assertEqual(ipaddress.ip_network(self.ipv4_address._ip).version,
|
||||||
4)
|
4)
|
||||||
self.assertEqual(ipaddress.ip_network(self.ipv6_address._ip).version,
|
self.assertEqual(ipaddress.ip_network(self.ipv6_address._ip).version,
|
||||||
6)
|
6)
|
||||||
|
self.assertEqual(ipaddress.ip_network(self.ipv6_scoped_address._ip).version,
|
||||||
|
6)
|
||||||
|
|
||||||
def testIpFromPacked(self):
|
def testIpFromPacked(self):
|
||||||
address = ipaddress.ip_address
|
address = ipaddress.ip_address
|
||||||
|
|
@ -1096,6 +1281,24 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
42540616829182469433547762482097946625)
|
42540616829182469433547762482097946625)
|
||||||
self.assertEqual(str(self.ipv6_interface.ip),
|
self.assertEqual(str(self.ipv6_interface.ip),
|
||||||
'2001:658:22a:cafe:200::1')
|
'2001:658:22a:cafe:200::1')
|
||||||
|
self.assertEqual(int(self.ipv6_scoped_interface.ip),
|
||||||
|
42540616829182469433547762482097946625)
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_interface.ip),
|
||||||
|
'2001:658:22a:cafe:200::1')
|
||||||
|
|
||||||
|
def testGetScopeId(self):
|
||||||
|
self.assertEqual(self.ipv6_address.scope_id,
|
||||||
|
None)
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_address.scope_id),
|
||||||
|
'scope')
|
||||||
|
self.assertEqual(self.ipv6_interface.scope_id,
|
||||||
|
None)
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_interface.scope_id),
|
||||||
|
'scope')
|
||||||
|
self.assertEqual(self.ipv6_network.network_address.scope_id,
|
||||||
|
None)
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_network.network_address.scope_id),
|
||||||
|
'scope')
|
||||||
|
|
||||||
def testGetNetmask(self):
|
def testGetNetmask(self):
|
||||||
self.assertEqual(int(self.ipv4_network.netmask), 4294967040)
|
self.assertEqual(int(self.ipv4_network.netmask), 4294967040)
|
||||||
|
|
@ -1103,6 +1306,9 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(int(self.ipv6_network.netmask),
|
self.assertEqual(int(self.ipv6_network.netmask),
|
||||||
340282366920938463444927863358058659840)
|
340282366920938463444927863358058659840)
|
||||||
self.assertEqual(self.ipv6_network.prefixlen, 64)
|
self.assertEqual(self.ipv6_network.prefixlen, 64)
|
||||||
|
self.assertEqual(int(self.ipv6_scoped_network.netmask),
|
||||||
|
340282366920938463444927863358058659840)
|
||||||
|
self.assertEqual(self.ipv6_scoped_network.prefixlen, 64)
|
||||||
|
|
||||||
def testZeroNetmask(self):
|
def testZeroNetmask(self):
|
||||||
ipv4_zero_netmask = ipaddress.IPv4Interface('1.2.3.4/0')
|
ipv4_zero_netmask = ipaddress.IPv4Interface('1.2.3.4/0')
|
||||||
|
|
@ -1113,6 +1319,10 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(int(ipv6_zero_netmask.network.netmask), 0)
|
self.assertEqual(int(ipv6_zero_netmask.network.netmask), 0)
|
||||||
self.assertEqual(ipv6_zero_netmask._prefix_from_prefix_string('0'), 0)
|
self.assertEqual(ipv6_zero_netmask._prefix_from_prefix_string('0'), 0)
|
||||||
|
|
||||||
|
ipv6_scoped_zero_netmask = ipaddress.IPv6Interface('::1%scope/0')
|
||||||
|
self.assertEqual(int(ipv6_scoped_zero_netmask.network.netmask), 0)
|
||||||
|
self.assertEqual(ipv6_scoped_zero_netmask._prefix_from_prefix_string('0'), 0)
|
||||||
|
|
||||||
def testIPv4Net(self):
|
def testIPv4Net(self):
|
||||||
net = ipaddress.IPv4Network('127.0.0.0/0.0.0.255')
|
net = ipaddress.IPv4Network('127.0.0.0/0.0.0.255')
|
||||||
self.assertEqual(net.prefixlen, 24)
|
self.assertEqual(net.prefixlen, 24)
|
||||||
|
|
@ -1126,9 +1336,15 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(str(self.ipv6_network.broadcast_address),
|
self.assertEqual(str(self.ipv6_network.broadcast_address),
|
||||||
'2001:658:22a:cafe:ffff:ffff:ffff:ffff')
|
'2001:658:22a:cafe:ffff:ffff:ffff:ffff')
|
||||||
|
|
||||||
|
self.assertEqual(int(self.ipv6_scoped_network.broadcast_address),
|
||||||
|
42540616829182469451850391367731642367)
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_network.broadcast_address),
|
||||||
|
'2001:658:22a:cafe:ffff:ffff:ffff:ffff')
|
||||||
|
|
||||||
def testGetPrefixlen(self):
|
def testGetPrefixlen(self):
|
||||||
self.assertEqual(self.ipv4_interface.network.prefixlen, 24)
|
self.assertEqual(self.ipv4_interface.network.prefixlen, 24)
|
||||||
self.assertEqual(self.ipv6_interface.network.prefixlen, 64)
|
self.assertEqual(self.ipv6_interface.network.prefixlen, 64)
|
||||||
|
self.assertEqual(self.ipv6_scoped_interface.network.prefixlen, 64)
|
||||||
|
|
||||||
def testGetSupernet(self):
|
def testGetSupernet(self):
|
||||||
self.assertEqual(self.ipv4_network.supernet().prefixlen, 23)
|
self.assertEqual(self.ipv4_network.supernet().prefixlen, 23)
|
||||||
|
|
@ -1143,6 +1359,9 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
'2001:658:22a:cafe::')
|
'2001:658:22a:cafe::')
|
||||||
self.assertEqual(ipaddress.IPv6Interface('::0/0').network.supernet(),
|
self.assertEqual(ipaddress.IPv6Interface('::0/0').network.supernet(),
|
||||||
ipaddress.IPv6Network('::0/0'))
|
ipaddress.IPv6Network('::0/0'))
|
||||||
|
self.assertEqual(self.ipv6_scoped_network.supernet().prefixlen, 63)
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_network.supernet().network_address),
|
||||||
|
'2001:658:22a:cafe::')
|
||||||
|
|
||||||
def testGetSupernet3(self):
|
def testGetSupernet3(self):
|
||||||
self.assertEqual(self.ipv4_network.supernet(3).prefixlen, 21)
|
self.assertEqual(self.ipv4_network.supernet(3).prefixlen, 21)
|
||||||
|
|
@ -1152,6 +1371,9 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(self.ipv6_network.supernet(3).prefixlen, 61)
|
self.assertEqual(self.ipv6_network.supernet(3).prefixlen, 61)
|
||||||
self.assertEqual(str(self.ipv6_network.supernet(3).network_address),
|
self.assertEqual(str(self.ipv6_network.supernet(3).network_address),
|
||||||
'2001:658:22a:caf8::')
|
'2001:658:22a:caf8::')
|
||||||
|
self.assertEqual(self.ipv6_scoped_network.supernet(3).prefixlen, 61)
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_network.supernet(3).network_address),
|
||||||
|
'2001:658:22a:caf8::')
|
||||||
|
|
||||||
def testGetSupernet4(self):
|
def testGetSupernet4(self):
|
||||||
self.assertRaises(ValueError, self.ipv4_network.supernet,
|
self.assertRaises(ValueError, self.ipv4_network.supernet,
|
||||||
|
|
@ -1167,6 +1389,12 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
new_prefix=65)
|
new_prefix=65)
|
||||||
self.assertEqual(self.ipv6_network.supernet(prefixlen_diff=2),
|
self.assertEqual(self.ipv6_network.supernet(prefixlen_diff=2),
|
||||||
self.ipv6_network.supernet(new_prefix=62))
|
self.ipv6_network.supernet(new_prefix=62))
|
||||||
|
self.assertRaises(ValueError, self.ipv6_scoped_network.supernet,
|
||||||
|
prefixlen_diff=2, new_prefix=1)
|
||||||
|
self.assertRaises(ValueError, self.ipv6_scoped_network.supernet,
|
||||||
|
new_prefix=65)
|
||||||
|
self.assertEqual(self.ipv6_scoped_network.supernet(prefixlen_diff=2),
|
||||||
|
self.ipv6_scoped_network.supernet(new_prefix=62))
|
||||||
|
|
||||||
def testHosts(self):
|
def testHosts(self):
|
||||||
hosts = list(self.ipv4_network.hosts())
|
hosts = list(self.ipv4_network.hosts())
|
||||||
|
|
@ -1180,6 +1408,12 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::1'), hosts[0])
|
self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::1'), hosts[0])
|
||||||
self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::ff'), hosts[-1])
|
self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::ff'), hosts[-1])
|
||||||
|
|
||||||
|
ipv6_scoped_network = ipaddress.IPv6Network('2001:658:22a:cafe::%scope/120')
|
||||||
|
hosts = list(ipv6_scoped_network.hosts())
|
||||||
|
self.assertEqual(255, len(hosts))
|
||||||
|
self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::1'), hosts[0])
|
||||||
|
self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::ff'), hosts[-1])
|
||||||
|
|
||||||
# special case where only 1 bit is left for address
|
# special case where only 1 bit is left for address
|
||||||
addrs = [ipaddress.IPv4Address('2.0.0.0'),
|
addrs = [ipaddress.IPv4Address('2.0.0.0'),
|
||||||
ipaddress.IPv4Address('2.0.0.1')]
|
ipaddress.IPv4Address('2.0.0.1')]
|
||||||
|
|
@ -1198,6 +1432,14 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts()))
|
self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts()))
|
||||||
self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
|
self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
|
||||||
list(ipaddress.ip_network(tpl_args).hosts()))
|
list(ipaddress.ip_network(tpl_args).hosts()))
|
||||||
|
addrs = [ipaddress.IPv6Address('2001:658:22a:cafe::'),
|
||||||
|
ipaddress.IPv6Address('2001:658:22a:cafe::1')]
|
||||||
|
str_args = '2001:658:22a:cafe::/127'
|
||||||
|
tpl_args = ('2001:658:22a:cafe::', 127)
|
||||||
|
self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts()))
|
||||||
|
self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts()))
|
||||||
|
self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
|
||||||
|
list(ipaddress.ip_network(tpl_args).hosts()))
|
||||||
|
|
||||||
def testFancySubnetting(self):
|
def testFancySubnetting(self):
|
||||||
self.assertEqual(sorted(self.ipv4_network.subnets(prefixlen_diff=3)),
|
self.assertEqual(sorted(self.ipv4_network.subnets(prefixlen_diff=3)),
|
||||||
|
|
@ -1214,6 +1456,13 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertRaises(ValueError, list,
|
self.assertRaises(ValueError, list,
|
||||||
self.ipv6_network.subnets(prefixlen_diff=4,
|
self.ipv6_network.subnets(prefixlen_diff=4,
|
||||||
new_prefix=68))
|
new_prefix=68))
|
||||||
|
self.assertEqual(sorted(self.ipv6_scoped_network.subnets(prefixlen_diff=4)),
|
||||||
|
sorted(self.ipv6_scoped_network.subnets(new_prefix=68)))
|
||||||
|
self.assertRaises(ValueError, list,
|
||||||
|
self.ipv6_scoped_network.subnets(new_prefix=63))
|
||||||
|
self.assertRaises(ValueError, list,
|
||||||
|
self.ipv6_scoped_network.subnets(prefixlen_diff=4,
|
||||||
|
new_prefix=68))
|
||||||
|
|
||||||
def testGetSubnets(self):
|
def testGetSubnets(self):
|
||||||
self.assertEqual(list(self.ipv4_network.subnets())[0].prefixlen, 25)
|
self.assertEqual(list(self.ipv4_network.subnets())[0].prefixlen, 25)
|
||||||
|
|
@ -1225,6 +1474,7 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
'1.2.3.128')
|
'1.2.3.128')
|
||||||
|
|
||||||
self.assertEqual(list(self.ipv6_network.subnets())[0].prefixlen, 65)
|
self.assertEqual(list(self.ipv6_network.subnets())[0].prefixlen, 65)
|
||||||
|
self.assertEqual(list(self.ipv6_scoped_network.subnets())[0].prefixlen, 65)
|
||||||
|
|
||||||
def testGetSubnetForSingle32(self):
|
def testGetSubnetForSingle32(self):
|
||||||
ip = ipaddress.IPv4Network('1.2.3.4/32')
|
ip = ipaddress.IPv4Network('1.2.3.4/32')
|
||||||
|
|
@ -1240,6 +1490,12 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(subnets1, ['::1/128'])
|
self.assertEqual(subnets1, ['::1/128'])
|
||||||
self.assertEqual(subnets1, subnets2)
|
self.assertEqual(subnets1, subnets2)
|
||||||
|
|
||||||
|
ip_scoped = ipaddress.IPv6Network('::1%scope/128')
|
||||||
|
subnets1 = [str(x) for x in ip_scoped.subnets()]
|
||||||
|
subnets2 = [str(x) for x in ip_scoped.subnets(2)]
|
||||||
|
self.assertEqual(subnets1, ['::1%scope/128'])
|
||||||
|
self.assertEqual(subnets1, subnets2)
|
||||||
|
|
||||||
def testSubnet2(self):
|
def testSubnet2(self):
|
||||||
ips = [str(x) for x in self.ipv4_network.subnets(2)]
|
ips = [str(x) for x in self.ipv4_network.subnets(2)]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
@ -1283,12 +1539,18 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.ipv6_interface.network.subnets(65))
|
self.ipv6_interface.network.subnets(65))
|
||||||
self.assertRaises(ValueError, list,
|
self.assertRaises(ValueError, list,
|
||||||
self.ipv6_network.subnets(65))
|
self.ipv6_network.subnets(65))
|
||||||
|
self.assertRaises(ValueError, list,
|
||||||
|
self.ipv6_scoped_interface.network.subnets(65))
|
||||||
|
self.assertRaises(ValueError, list,
|
||||||
|
self.ipv6_scoped_network.subnets(65))
|
||||||
|
|
||||||
def testSupernetFailsForLargeCidrDiff(self):
|
def testSupernetFailsForLargeCidrDiff(self):
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
self.ipv4_interface.network.supernet, 25)
|
self.ipv4_interface.network.supernet, 25)
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
self.ipv6_interface.network.supernet, 65)
|
self.ipv6_interface.network.supernet, 65)
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
self.ipv6_scoped_interface.network.supernet, 65)
|
||||||
|
|
||||||
def testSubnetFailsForNegativeCidrDiff(self):
|
def testSubnetFailsForNegativeCidrDiff(self):
|
||||||
self.assertRaises(ValueError, list,
|
self.assertRaises(ValueError, list,
|
||||||
|
|
@ -1299,6 +1561,10 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.ipv6_interface.network.subnets(-1))
|
self.ipv6_interface.network.subnets(-1))
|
||||||
self.assertRaises(ValueError, list,
|
self.assertRaises(ValueError, list,
|
||||||
self.ipv6_network.subnets(-1))
|
self.ipv6_network.subnets(-1))
|
||||||
|
self.assertRaises(ValueError, list,
|
||||||
|
self.ipv6_scoped_interface.network.subnets(-1))
|
||||||
|
self.assertRaises(ValueError, list,
|
||||||
|
self.ipv6_scoped_network.subnets(-1))
|
||||||
|
|
||||||
def testGetNum_Addresses(self):
|
def testGetNum_Addresses(self):
|
||||||
self.assertEqual(self.ipv4_network.num_addresses, 256)
|
self.assertEqual(self.ipv4_network.num_addresses, 256)
|
||||||
|
|
@ -1311,6 +1577,11 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
9223372036854775808)
|
9223372036854775808)
|
||||||
self.assertEqual(self.ipv6_network.supernet().num_addresses,
|
self.assertEqual(self.ipv6_network.supernet().num_addresses,
|
||||||
36893488147419103232)
|
36893488147419103232)
|
||||||
|
self.assertEqual(self.ipv6_scoped_network.num_addresses, 18446744073709551616)
|
||||||
|
self.assertEqual(list(self.ipv6_scoped_network.subnets())[0].num_addresses,
|
||||||
|
9223372036854775808)
|
||||||
|
self.assertEqual(self.ipv6_scoped_network.supernet().num_addresses,
|
||||||
|
36893488147419103232)
|
||||||
|
|
||||||
def testContains(self):
|
def testContains(self):
|
||||||
self.assertIn(ipaddress.IPv4Interface('1.2.3.128/25'),
|
self.assertIn(ipaddress.IPv4Interface('1.2.3.128/25'),
|
||||||
|
|
@ -1332,6 +1603,9 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(str(self.ipv6_network[5]),
|
self.assertEqual(str(self.ipv6_network[5]),
|
||||||
'2001:658:22a:cafe::5')
|
'2001:658:22a:cafe::5')
|
||||||
self.assertRaises(IndexError, self.ipv6_network.__getitem__, 1 << 64)
|
self.assertRaises(IndexError, self.ipv6_network.__getitem__, 1 << 64)
|
||||||
|
self.assertEqual(str(self.ipv6_scoped_network[5]),
|
||||||
|
'2001:658:22a:cafe::5')
|
||||||
|
self.assertRaises(IndexError, self.ipv6_scoped_network.__getitem__, 1 << 64)
|
||||||
|
|
||||||
def testGetitem(self):
|
def testGetitem(self):
|
||||||
# http://code.google.com/p/ipaddr-py/issues/detail?id=15
|
# http://code.google.com/p/ipaddr-py/issues/detail?id=15
|
||||||
|
|
@ -1351,6 +1625,8 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
ipaddress.IPv4Interface('1.2.3.4/23'))
|
ipaddress.IPv4Interface('1.2.3.4/23'))
|
||||||
self.assertFalse(self.ipv4_interface ==
|
self.assertFalse(self.ipv4_interface ==
|
||||||
ipaddress.IPv6Interface('::1.2.3.4/24'))
|
ipaddress.IPv6Interface('::1.2.3.4/24'))
|
||||||
|
self.assertFalse(self.ipv4_interface ==
|
||||||
|
ipaddress.IPv6Interface('::1.2.3.4%scope/24'))
|
||||||
self.assertFalse(self.ipv4_interface == '')
|
self.assertFalse(self.ipv4_interface == '')
|
||||||
self.assertFalse(self.ipv4_interface == [])
|
self.assertFalse(self.ipv4_interface == [])
|
||||||
self.assertFalse(self.ipv4_interface == 2)
|
self.assertFalse(self.ipv4_interface == 2)
|
||||||
|
|
@ -1365,6 +1641,20 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertFalse(self.ipv6_interface == [])
|
self.assertFalse(self.ipv6_interface == [])
|
||||||
self.assertFalse(self.ipv6_interface == 2)
|
self.assertFalse(self.ipv6_interface == 2)
|
||||||
|
|
||||||
|
self.assertTrue(self.ipv6_scoped_interface ==
|
||||||
|
ipaddress.IPv6Interface('2001:658:22a:cafe:200::1%scope/64'))
|
||||||
|
self.assertFalse(self.ipv6_scoped_interface ==
|
||||||
|
ipaddress.IPv6Interface('2001:658:22a:cafe:200::1%scope/63'))
|
||||||
|
self.assertFalse(self.ipv6_scoped_interface ==
|
||||||
|
ipaddress.IPv6Interface('2001:658:22a:cafe:200::1/64'))
|
||||||
|
self.assertFalse(self.ipv6_scoped_interface ==
|
||||||
|
ipaddress.IPv6Interface('2001:658:22a:cafe:200::1/63'))
|
||||||
|
self.assertFalse(self.ipv6_scoped_interface ==
|
||||||
|
ipaddress.IPv4Interface('1.2.3.4/23'))
|
||||||
|
self.assertFalse(self.ipv6_scoped_interface == '')
|
||||||
|
self.assertFalse(self.ipv6_scoped_interface == [])
|
||||||
|
self.assertFalse(self.ipv6_scoped_interface == 2)
|
||||||
|
|
||||||
def testNotEqual(self):
|
def testNotEqual(self):
|
||||||
self.assertFalse(self.ipv4_interface !=
|
self.assertFalse(self.ipv4_interface !=
|
||||||
ipaddress.IPv4Interface('1.2.3.4/24'))
|
ipaddress.IPv4Interface('1.2.3.4/24'))
|
||||||
|
|
@ -1372,6 +1662,8 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
ipaddress.IPv4Interface('1.2.3.4/23'))
|
ipaddress.IPv4Interface('1.2.3.4/23'))
|
||||||
self.assertTrue(self.ipv4_interface !=
|
self.assertTrue(self.ipv4_interface !=
|
||||||
ipaddress.IPv6Interface('::1.2.3.4/24'))
|
ipaddress.IPv6Interface('::1.2.3.4/24'))
|
||||||
|
self.assertTrue(self.ipv4_interface !=
|
||||||
|
ipaddress.IPv6Interface('::1.2.3.4%scope/24'))
|
||||||
self.assertTrue(self.ipv4_interface != '')
|
self.assertTrue(self.ipv4_interface != '')
|
||||||
self.assertTrue(self.ipv4_interface != [])
|
self.assertTrue(self.ipv4_interface != [])
|
||||||
self.assertTrue(self.ipv4_interface != 2)
|
self.assertTrue(self.ipv4_interface != 2)
|
||||||
|
|
@ -1398,6 +1690,26 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertTrue(self.ipv6_address != [])
|
self.assertTrue(self.ipv6_address != [])
|
||||||
self.assertTrue(self.ipv6_address != 2)
|
self.assertTrue(self.ipv6_address != 2)
|
||||||
|
|
||||||
|
self.assertFalse(self.ipv6_scoped_interface !=
|
||||||
|
ipaddress.IPv6Interface('2001:658:22a:cafe:200::1%scope/64'))
|
||||||
|
self.assertTrue(self.ipv6_scoped_interface !=
|
||||||
|
ipaddress.IPv6Interface('2001:658:22a:cafe:200::1%scope/63'))
|
||||||
|
self.assertTrue(self.ipv6_scoped_interface !=
|
||||||
|
ipaddress.IPv6Interface('2001:658:22a:cafe:200::1/64'))
|
||||||
|
self.assertTrue(self.ipv6_scoped_interface !=
|
||||||
|
ipaddress.IPv6Interface('2001:658:22a:cafe:200::1/63'))
|
||||||
|
self.assertTrue(self.ipv6_scoped_interface !=
|
||||||
|
ipaddress.IPv4Interface('1.2.3.4/23'))
|
||||||
|
self.assertTrue(self.ipv6_scoped_interface != '')
|
||||||
|
self.assertTrue(self.ipv6_scoped_interface != [])
|
||||||
|
self.assertTrue(self.ipv6_scoped_interface != 2)
|
||||||
|
|
||||||
|
self.assertTrue(self.ipv6_scoped_address !=
|
||||||
|
ipaddress.IPv4Address('1.2.3.4'))
|
||||||
|
self.assertTrue(self.ipv6_scoped_address != '')
|
||||||
|
self.assertTrue(self.ipv6_scoped_address != [])
|
||||||
|
self.assertTrue(self.ipv6_scoped_address != 2)
|
||||||
|
|
||||||
def testSlash32Constructor(self):
|
def testSlash32Constructor(self):
|
||||||
self.assertEqual(str(ipaddress.IPv4Interface(
|
self.assertEqual(str(ipaddress.IPv4Interface(
|
||||||
'1.2.3.4/255.255.255.255')), '1.2.3.4/32')
|
'1.2.3.4/255.255.255.255')), '1.2.3.4/32')
|
||||||
|
|
@ -1405,6 +1717,8 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
def testSlash128Constructor(self):
|
def testSlash128Constructor(self):
|
||||||
self.assertEqual(str(ipaddress.IPv6Interface('::1/128')),
|
self.assertEqual(str(ipaddress.IPv6Interface('::1/128')),
|
||||||
'::1/128')
|
'::1/128')
|
||||||
|
self.assertEqual(str(ipaddress.IPv6Interface('::1%scope/128')),
|
||||||
|
'::1%scope/128')
|
||||||
|
|
||||||
def testSlash0Constructor(self):
|
def testSlash0Constructor(self):
|
||||||
self.assertEqual(str(ipaddress.IPv4Interface('1.2.3.4/0.0.0.0')),
|
self.assertEqual(str(ipaddress.IPv4Interface('1.2.3.4/0.0.0.0')),
|
||||||
|
|
@ -1476,6 +1790,13 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3])
|
collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3])
|
||||||
self.assertEqual(list(collapsed), [ip3])
|
self.assertEqual(list(collapsed), [ip3])
|
||||||
|
|
||||||
|
ip1 = ipaddress.IPv6Network('2001::%scope/100')
|
||||||
|
ip2 = ipaddress.IPv6Network('2001::%scope/120')
|
||||||
|
ip3 = ipaddress.IPv6Network('2001::%scope/96')
|
||||||
|
# test that ipv6 addresses are subsumed properly.
|
||||||
|
collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3])
|
||||||
|
self.assertEqual(list(collapsed), [ip3])
|
||||||
|
|
||||||
# the toejam test
|
# the toejam test
|
||||||
addr_tuples = [
|
addr_tuples = [
|
||||||
(ipaddress.ip_address('1.1.1.1'),
|
(ipaddress.ip_address('1.1.1.1'),
|
||||||
|
|
@ -1489,6 +1810,18 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertRaises(TypeError, ipaddress.collapse_addresses,
|
self.assertRaises(TypeError, ipaddress.collapse_addresses,
|
||||||
[ip1, ip2])
|
[ip1, ip2])
|
||||||
|
|
||||||
|
addr_tuples = [
|
||||||
|
(ipaddress.ip_address('1.1.1.1'),
|
||||||
|
ipaddress.ip_address('::1%scope')),
|
||||||
|
(ipaddress.IPv4Network('1.1.0.0/24'),
|
||||||
|
ipaddress.IPv6Network('2001::%scope/120')),
|
||||||
|
(ipaddress.IPv4Network('1.1.0.0/32'),
|
||||||
|
ipaddress.IPv6Network('2001::%scope/128')),
|
||||||
|
]
|
||||||
|
for ip1, ip2 in addr_tuples:
|
||||||
|
self.assertRaises(TypeError, ipaddress.collapse_addresses,
|
||||||
|
[ip1, ip2])
|
||||||
|
|
||||||
def testSummarizing(self):
|
def testSummarizing(self):
|
||||||
#ip = ipaddress.ip_address
|
#ip = ipaddress.ip_address
|
||||||
#ipnet = ipaddress.ip_network
|
#ipnet = ipaddress.ip_network
|
||||||
|
|
@ -1508,6 +1841,8 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
# test that a summary over ip4 & ip6 fails
|
# test that a summary over ip4 & ip6 fails
|
||||||
self.assertRaises(TypeError, list,
|
self.assertRaises(TypeError, list,
|
||||||
summarize(ip1, ipaddress.IPv6Address('::1')))
|
summarize(ip1, ipaddress.IPv6Address('::1')))
|
||||||
|
self.assertRaises(TypeError, list,
|
||||||
|
summarize(ip1, ipaddress.IPv6Address('::1%scope')))
|
||||||
# test a /24 is summarized properly
|
# test a /24 is summarized properly
|
||||||
self.assertEqual(list(summarize(ip1, ip2))[0],
|
self.assertEqual(list(summarize(ip1, ip2))[0],
|
||||||
ipaddress.ip_network('1.1.1.0/24'))
|
ipaddress.ip_network('1.1.1.0/24'))
|
||||||
|
|
@ -1533,6 +1868,17 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
[ipaddress.ip_network('1::/16'),
|
[ipaddress.ip_network('1::/16'),
|
||||||
ipaddress.ip_network('2::/128')])
|
ipaddress.ip_network('2::/128')])
|
||||||
|
|
||||||
|
ip1 = ipaddress.ip_address('1::%scope')
|
||||||
|
ip2 = ipaddress.ip_address('1:ffff:ffff:ffff:ffff:ffff:ffff:ffff%scope')
|
||||||
|
# test an IPv6 is summarized properly
|
||||||
|
self.assertEqual(list(summarize(ip1, ip2))[0],
|
||||||
|
ipaddress.ip_network('1::/16'))
|
||||||
|
# test an IPv6 range that isn't on a network byte boundary
|
||||||
|
ip2 = ipaddress.ip_address('2::%scope')
|
||||||
|
self.assertEqual(list(summarize(ip1, ip2)),
|
||||||
|
[ipaddress.ip_network('1::/16'),
|
||||||
|
ipaddress.ip_network('2::/128')])
|
||||||
|
|
||||||
# test exception raised when first is greater than last
|
# test exception raised when first is greater than last
|
||||||
self.assertRaises(ValueError, list,
|
self.assertRaises(ValueError, list,
|
||||||
summarize(ipaddress.ip_address('1.1.1.0'),
|
summarize(ipaddress.ip_address('1.1.1.0'),
|
||||||
|
|
@ -1558,6 +1904,10 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
ipaddress.ip_address('::1'))
|
ipaddress.ip_address('::1'))
|
||||||
self.assertTrue(ipaddress.ip_address('::1') <=
|
self.assertTrue(ipaddress.ip_address('::1') <=
|
||||||
ipaddress.ip_address('::2'))
|
ipaddress.ip_address('::2'))
|
||||||
|
self.assertTrue(ipaddress.ip_address('::1%scope') <=
|
||||||
|
ipaddress.ip_address('::1%scope'))
|
||||||
|
self.assertTrue(ipaddress.ip_address('::1%scope') <=
|
||||||
|
ipaddress.ip_address('::2%scope'))
|
||||||
|
|
||||||
def testInterfaceComparison(self):
|
def testInterfaceComparison(self):
|
||||||
self.assertTrue(ipaddress.ip_interface('1.1.1.1/24') ==
|
self.assertTrue(ipaddress.ip_interface('1.1.1.1/24') ==
|
||||||
|
|
@ -1590,6 +1940,52 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertTrue(ipaddress.ip_interface('::1/64') >
|
self.assertTrue(ipaddress.ip_interface('::1/64') >
|
||||||
ipaddress.ip_interface('::2/48'))
|
ipaddress.ip_interface('::2/48'))
|
||||||
|
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1%scope/64') ==
|
||||||
|
ipaddress.ip_interface('::1%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1%scope/64') <
|
||||||
|
ipaddress.ip_interface('::1%scope/80'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1%scope/64') <
|
||||||
|
ipaddress.ip_interface('::2%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::2%scope/48') <
|
||||||
|
ipaddress.ip_interface('::1%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1%scope/80') >
|
||||||
|
ipaddress.ip_interface('::1%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::2%scope/64') >
|
||||||
|
ipaddress.ip_interface('::1%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1%scope/64') >
|
||||||
|
ipaddress.ip_interface('::2%scope/48'))
|
||||||
|
|
||||||
|
|
||||||
|
self.assertFalse(ipaddress.ip_interface('::1%scope/64') ==
|
||||||
|
ipaddress.ip_interface('::1/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1%scope/64') <
|
||||||
|
ipaddress.ip_interface('::1/80'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1%scope/64') <
|
||||||
|
ipaddress.ip_interface('::2/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::2%scope/48') <
|
||||||
|
ipaddress.ip_interface('::1/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1%scope/80') >
|
||||||
|
ipaddress.ip_interface('::1/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::2%scope/64') >
|
||||||
|
ipaddress.ip_interface('::1/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1%scope/64') >
|
||||||
|
ipaddress.ip_interface('::2/48'))
|
||||||
|
|
||||||
|
self.assertFalse(ipaddress.ip_interface('::1/64') ==
|
||||||
|
ipaddress.ip_interface('::1%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1/64') <
|
||||||
|
ipaddress.ip_interface('::1%scope/80'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1/64') <
|
||||||
|
ipaddress.ip_interface('::2%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::2/48') <
|
||||||
|
ipaddress.ip_interface('::1%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1/80') >
|
||||||
|
ipaddress.ip_interface('::1%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::2/64') >
|
||||||
|
ipaddress.ip_interface('::1%scope/64'))
|
||||||
|
self.assertTrue(ipaddress.ip_interface('::1/64') >
|
||||||
|
ipaddress.ip_interface('::2%scope/48'))
|
||||||
|
|
||||||
def testNetworkComparison(self):
|
def testNetworkComparison(self):
|
||||||
# ip1 and ip2 have the same network address
|
# ip1 and ip2 have the same network address
|
||||||
ip1 = ipaddress.IPv4Network('1.1.1.0/24')
|
ip1 = ipaddress.IPv4Network('1.1.1.0/24')
|
||||||
|
|
@ -1669,6 +2065,7 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
ipaddress.ip_network('1.1.1.2'))
|
ipaddress.ip_network('1.1.1.2'))
|
||||||
self.assertFalse(ipaddress.ip_network('1.1.1.2') <=
|
self.assertFalse(ipaddress.ip_network('1.1.1.2') <=
|
||||||
ipaddress.ip_network('1.1.1.1'))
|
ipaddress.ip_network('1.1.1.1'))
|
||||||
|
|
||||||
self.assertTrue(ipaddress.ip_network('::1') <=
|
self.assertTrue(ipaddress.ip_network('::1') <=
|
||||||
ipaddress.ip_network('::1'))
|
ipaddress.ip_network('::1'))
|
||||||
self.assertTrue(ipaddress.ip_network('::1') <=
|
self.assertTrue(ipaddress.ip_network('::1') <=
|
||||||
|
|
@ -1679,6 +2076,7 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
def testStrictNetworks(self):
|
def testStrictNetworks(self):
|
||||||
self.assertRaises(ValueError, ipaddress.ip_network, '192.168.1.1/24')
|
self.assertRaises(ValueError, ipaddress.ip_network, '192.168.1.1/24')
|
||||||
self.assertRaises(ValueError, ipaddress.ip_network, '::1/120')
|
self.assertRaises(ValueError, ipaddress.ip_network, '::1/120')
|
||||||
|
self.assertRaises(ValueError, ipaddress.ip_network, '::1%scope/120')
|
||||||
|
|
||||||
def testOverlaps(self):
|
def testOverlaps(self):
|
||||||
other = ipaddress.IPv4Network('1.2.3.0/30')
|
other = ipaddress.IPv4Network('1.2.3.0/30')
|
||||||
|
|
@ -1707,13 +2105,28 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
self.assertEqual(ipaddress.ip_address('FFFF::192.0.2.1'),
|
self.assertEqual(ipaddress.ip_address('FFFF::192.0.2.1'),
|
||||||
ipaddress.ip_address('FFFF::c000:201'))
|
ipaddress.ip_address('FFFF::c000:201'))
|
||||||
|
|
||||||
|
self.assertEqual(ipaddress.ip_address('::FFFF:192.0.2.1%scope'),
|
||||||
|
ipaddress.ip_address('::FFFF:c000:201%scope'))
|
||||||
|
self.assertEqual(ipaddress.ip_address('FFFF::192.0.2.1%scope'),
|
||||||
|
ipaddress.ip_address('FFFF::c000:201%scope'))
|
||||||
|
self.assertNotEqual(ipaddress.ip_address('::FFFF:192.0.2.1%scope'),
|
||||||
|
ipaddress.ip_address('::FFFF:c000:201'))
|
||||||
|
self.assertNotEqual(ipaddress.ip_address('FFFF::192.0.2.1%scope'),
|
||||||
|
ipaddress.ip_address('FFFF::c000:201'))
|
||||||
|
self.assertNotEqual(ipaddress.ip_address('::FFFF:192.0.2.1'),
|
||||||
|
ipaddress.ip_address('::FFFF:c000:201%scope'))
|
||||||
|
self.assertNotEqual(ipaddress.ip_address('FFFF::192.0.2.1'),
|
||||||
|
ipaddress.ip_address('FFFF::c000:201%scope'))
|
||||||
|
|
||||||
def testIPVersion(self):
|
def testIPVersion(self):
|
||||||
self.assertEqual(self.ipv4_address.version, 4)
|
self.assertEqual(self.ipv4_address.version, 4)
|
||||||
self.assertEqual(self.ipv6_address.version, 6)
|
self.assertEqual(self.ipv6_address.version, 6)
|
||||||
|
self.assertEqual(self.ipv6_scoped_address.version, 6)
|
||||||
|
|
||||||
def testMaxPrefixLength(self):
|
def testMaxPrefixLength(self):
|
||||||
self.assertEqual(self.ipv4_interface.max_prefixlen, 32)
|
self.assertEqual(self.ipv4_interface.max_prefixlen, 32)
|
||||||
self.assertEqual(self.ipv6_interface.max_prefixlen, 128)
|
self.assertEqual(self.ipv6_interface.max_prefixlen, 128)
|
||||||
|
self.assertEqual(self.ipv6_scoped_interface.max_prefixlen, 128)
|
||||||
|
|
||||||
def testPacked(self):
|
def testPacked(self):
|
||||||
self.assertEqual(self.ipv4_address.packed,
|
self.assertEqual(self.ipv4_address.packed,
|
||||||
|
|
@ -1728,6 +2141,14 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
+ b'\x00' * 6)
|
+ b'\x00' * 6)
|
||||||
self.assertEqual(ipaddress.IPv6Interface('::1:0:0:0:0').packed,
|
self.assertEqual(ipaddress.IPv6Interface('::1:0:0:0:0').packed,
|
||||||
b'\x00' * 6 + b'\x00\x01' + b'\x00' * 8)
|
b'\x00' * 6 + b'\x00\x01' + b'\x00' * 8)
|
||||||
|
self.assertEqual(self.ipv6_scoped_address.packed,
|
||||||
|
b'\x20\x01\x06\x58\x02\x2a\xca\xfe'
|
||||||
|
b'\x02\x00\x00\x00\x00\x00\x00\x01')
|
||||||
|
self.assertEqual(ipaddress.IPv6Interface('ffff:2:3:4:ffff::%scope').packed,
|
||||||
|
b'\xff\xff\x00\x02\x00\x03\x00\x04\xff\xff'
|
||||||
|
+ b'\x00' * 6)
|
||||||
|
self.assertEqual(ipaddress.IPv6Interface('::1:0:0:0:0%scope').packed,
|
||||||
|
b'\x00' * 6 + b'\x00\x01' + b'\x00' * 8)
|
||||||
|
|
||||||
def testIpType(self):
|
def testIpType(self):
|
||||||
ipv4net = ipaddress.ip_network('1.2.3.4')
|
ipv4net = ipaddress.ip_network('1.2.3.4')
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add support for scoped IPv6 addresses to :mod:`ipaddress`. Patch by Oleksandr
|
||||||
|
Pavliuk.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue