Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 40 additions & 70 deletions Lib/ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,30 @@ def _prefix_from_ip_string(cls, ip_str):
except ValueError:
cls._report_invalid_netmask(ip_str)

@classmethod
def _split_addr_prefix(cls, address):
"""Helper function to parse address of Network/Interface.

Arg:
address: Argument of Network/Interface.

Returns:
(addr, prefix) tuple.
"""
# a packed address or integer
if isinstance(address, (bytes, int)):
return address, cls._max_prefixlen

if not isinstance(address, tuple):
# Assume input argument to be string or any object representation
# which converts into a formatted IP prefix string.
address = _split_optional_netmask(address)

# Constructing from a tuple (addr, [mask])
if len(address) > 1:
return address
return address[0], cls._max_prefixlen

def __reduce__(self):
return self.__class__, (str(self),)

Expand Down Expand Up @@ -1305,32 +1329,16 @@ def is_link_local(self):
class IPv4Interface(IPv4Address):

def __init__(self, address):
if isinstance(address, (bytes, int)):
IPv4Address.__init__(self, address)
self.network = IPv4Network(self._ip)
self._prefixlen = self._max_prefixlen
return

if isinstance(address, tuple):
IPv4Address.__init__(self, address[0])
if len(address) > 1:
self._prefixlen = int(address[1])
else:
self._prefixlen = self._max_prefixlen

self.network = IPv4Network(address, strict=False)
self.netmask = self.network.netmask
self.hostmask = self.network.hostmask
return

addr = _split_optional_netmask(address)
IPv4Address.__init__(self, addr[0])
addr, mask = self._split_addr_prefix(address)

self.network = IPv4Network(address, strict=False)
IPv4Address.__init__(self, addr)
self.network = IPv4Network((addr, mask), strict=False)
self.netmask = self.network.netmask
self._prefixlen = self.network._prefixlen

self.netmask = self.network.netmask
self.hostmask = self.network.hostmask
@functools.cached_property
def hostmask(self):
return self.network.hostmask

def __str__(self):
return '%s/%d' % (self._string_from_ip_int(self._ip),
Expand Down Expand Up @@ -1435,20 +1443,7 @@ def __init__(self, address, strict=True):
ValueError: If strict is True and a network address is not
supplied.
"""
# Constructing from a packed address or integer
if isinstance(address, (int, bytes)):
addr = address
mask = self._max_prefixlen
# Constructing from a tuple (addr, [mask])
elif isinstance(address, tuple):
addr = address[0]
mask = address[1] if len(address) > 1 else self._max_prefixlen
# Assume input argument to be string or any object representation
# which converts into a formatted IP prefix string.
else:
args = _split_optional_netmask(address)
addr = self._ip_int_from_string(args[0])
mask = args[1] if len(args) == 2 else self._max_prefixlen
addr, mask = self._split_addr_prefix(address)

self.network_address = IPv4Address(addr)
self.netmask, self._prefixlen = self._make_netmask(mask)
Expand Down Expand Up @@ -1979,28 +1974,16 @@ def sixtofour(self):
class IPv6Interface(IPv6Address):

def __init__(self, address):
if isinstance(address, (bytes, int)):
IPv6Address.__init__(self, address)
self.network = IPv6Network(self._ip)
self._prefixlen = self._max_prefixlen
return
if isinstance(address, tuple):
IPv6Address.__init__(self, address[0])
if len(address) > 1:
self._prefixlen = int(address[1])
else:
self._prefixlen = self._max_prefixlen
self.network = IPv6Network(address, strict=False)
self.netmask = self.network.netmask
self.hostmask = self.network.hostmask
return
addr, mask = self._split_addr_prefix(address)

addr = _split_optional_netmask(address)
IPv6Address.__init__(self, addr[0])
self.network = IPv6Network(address, strict=False)
IPv6Address.__init__(self, addr)
self.network = IPv6Network((addr, mask), strict=False)
self.netmask = self.network.netmask
self._prefixlen = self.network._prefixlen
self.hostmask = self.network.hostmask

@functools.cached_property
def hostmask(self):
return self.network.hostmask

def __str__(self):
return '%s/%d' % (self._string_from_ip_int(self._ip),
Expand Down Expand Up @@ -2110,20 +2093,7 @@ def __init__(self, address, strict=True):
ValueError: If strict was True and a network address was not
supplied.
"""
# Constructing from a packed address or integer
if isinstance(address, (int, bytes)):
addr = address
mask = self._max_prefixlen
# Constructing from a tuple (addr, [mask])
elif isinstance(address, tuple):
addr = address[0]
mask = address[1] if len(address) > 1 else self._max_prefixlen
# Assume input argument to be string or any object representation
# which converts into a formatted IP prefix string.
else:
args = _split_optional_netmask(address)
addr = self._ip_int_from_string(args[0])
mask = args[1] if len(args) == 2 else self._max_prefixlen
addr, mask = self._split_addr_prefix(address)

self.network_address = IPv6Address(addr)
self.netmask, self._prefixlen = self._make_netmask(mask)
Expand Down
17 changes: 16 additions & 1 deletion Lib/test/test_ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,13 @@ class NetmaskTestMixin_v4(CommonTestMixin_v4):
"""Input validation on interfaces and networks is very similar"""

def test_no_mask(self):
self.assertEqual(str(self.factory('1.2.3.4')), '1.2.3.4/32')
for address in ('1.2.3.4', 0x01020304, b'\x01\x02\x03\x04'):
net = self.factory(address)
self.assertEqual(str(net), '1.2.3.4/32')
self.assertEqual(str(net.netmask), '255.255.255.255')
self.assertEqual(str(net.hostmask), '0.0.0.0')
# IPv4Network has prefixlen, but IPv4Interface doesn't.
# Should we add it to IPv4Interface too? (bpo-36392)

def test_split_netmask(self):
addr = "1.2.3.4/32/24"
Expand Down Expand Up @@ -527,6 +533,15 @@ def test_subnet_of_mixed_types(self):
class NetmaskTestMixin_v6(CommonTestMixin_v6):
"""Input validation on interfaces and networks is very similar"""

def test_no_mask(self):
for address in ('::1', 1, b'\x00'*15 + b'\x01'):
net = self.factory(address)
self.assertEqual(str(net), '::1/128')
self.assertEqual(str(net.netmask), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
self.assertEqual(str(net.hostmask), '::')
# IPv6Network has prefixlen, but IPv6Interface doesn't.
# Should we add it to IPv4Interface too? (bpo-36392)

def test_split_netmask(self):
addr = "cafe:cafe::/128/190"
with self.assertAddressError("Only one '/' permitted in %r" % addr):
Expand Down