Skip to content

Commit d79f302

Browse files
committed
gh-153769: Clarify the TypeError from ipaddress subnet_of()/supernet_of()
Passing something that is not a network object, such as an address, to IPv4Network.subnet_of() or supernet_of() made _is_subnet_of() reach for the network_address attribute, fail with an AttributeError, and turn that into a TypeError with a message that read like a real containment result and hid the actual cause. Check that both arguments are network objects up front and raise a clear TypeError explaining what is required, so nothing masks an internal AttributeError anymore.
1 parent 1604043 commit d79f302

4 files changed

Lines changed: 28 additions & 9 deletions

File tree

Lib/ipaddress.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,15 +1026,14 @@ def is_multicast(self):
10261026

10271027
@staticmethod
10281028
def _is_subnet_of(a, b):
1029-
try:
1030-
# Always false if one is v4 and the other is v6.
1031-
if a.version != b.version:
1032-
raise TypeError(f"{a} and {b} are not of the same version")
1033-
return (b.network_address <= a.network_address and
1034-
b.broadcast_address >= a.broadcast_address)
1035-
except AttributeError:
1036-
raise TypeError(f"Unable to test subnet containment "
1037-
f"between {a} and {b}")
1029+
if not (isinstance(a, _BaseNetwork) and isinstance(b, _BaseNetwork)):
1030+
raise TypeError(f"Unable to test subnet containment between "
1031+
f"{a} and {b}: both must be network objects")
1032+
# Always false if one is v4 and the other is v6.
1033+
if a.version != b.version:
1034+
raise TypeError(f"{a} and {b} are not of the same version")
1035+
return (b.network_address <= a.network_address and
1036+
b.broadcast_address >= a.broadcast_address)
10381037

10391038
def subnet_of(self, other):
10401039
"""Return True if this network is a subnet of other."""

Lib/test/test_ipaddress.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,20 @@ def test_subnet_of_mixed_types(self):
723723
ipaddress.IPv6Network('::1/128').subnet_of(
724724
ipaddress.IPv4Network('10.0.0.0/30'))
725725

726+
def test_subnet_of_non_network(self):
727+
# Passing something that is not a network object (e.g. an address)
728+
# must raise a clear TypeError rather than masking an internal
729+
# AttributeError (gh-153769).
730+
net = ipaddress.IPv4Network('10.0.0.0/30')
731+
for other in (ipaddress.IPv4Address('10.0.0.1'), '10.0.0.0/30', 42):
732+
for method in (net.subnet_of, net.supernet_of):
733+
with self.assertRaises(TypeError) as cm:
734+
method(other)
735+
self.assertIn('network', str(cm.exception))
736+
# The error should not be a swallowed AttributeError.
737+
self.assertNotIsInstance(cm.exception.__context__,
738+
AttributeError)
739+
726740

727741
class NetmaskTestMixin_v6(CommonTestMixin_v6):
728742
"""Input validation on interfaces and networks is very similar"""

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2008,6 +2008,7 @@ Wm. Keith van der Meulen
20082008
Eric N. Vander Weele
20092009
Andrew Vant
20102010
Atul Varma
2011+
Vyron Vasileiadis
20112012
Dmitry Vasiliev
20122013
Sebastian Ortiz Vasquez
20132014
Alexandre Vassalotti
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
:meth:`~ipaddress.IPv4Network.subnet_of` and
2+
:meth:`~ipaddress.IPv4Network.supernet_of` now raise a clear
3+
:exc:`TypeError` when passed an argument that is not a network object,
4+
such as an address, instead of a confusing error that masked an internal
5+
:exc:`AttributeError`.

0 commit comments

Comments
 (0)