diff options
-rw-r--r-- | Doc/howto/ipaddress.rst | 48 | ||||
-rw-r--r-- | Lib/ipaddress.py | 5 | ||||
-rw-r--r-- | Lib/test/test_ipaddress.py | 6 |
3 files changed, 43 insertions, 16 deletions
diff --git a/Doc/howto/ipaddress.rst b/Doc/howto/ipaddress.rst index 4d9ca08..67cf763 100644 --- a/Doc/howto/ipaddress.rst +++ b/Doc/howto/ipaddress.rst @@ -277,23 +277,49 @@ an integer or string that the other module will accept:: 3221225985 -Exceptions raised by :mod:`ipaddress` -===================================== +Getting more detail when instance creation fails +================================================ When creating address/network/interface objects using the version-agnostic -factory functions, any errors will be reported as :exc:`ValueError`. +factory functions, any errors will be reported as :exc:`ValueError` with +a generic error message that simply says the passed in value was not +recognised as an object of that type. The lack of a specific error is +because it's necessary to know whether the value is *supposed* to be IPv4 +or IPv6 in order to provide more detail on why it has been rejected. + +To support use cases where it is useful to have access to this additional +detail, the individual class constructors actually raise the +:exc:`ValueError` subclasses :exc:`ipaddress.AddressValueError` and +:exc:`ipaddress.NetmaskValueError` to indicate exactly which part of +the definition failed to parse correctly. + +The error messages are significantly more detailed when using the +class constructors directly. For example:: + + >>> ipaddress.ip_address("192.168.0.256") + Traceback (most recent call last): + ... + ValueError: '192.168.0.256' does not appear to be an IPv4 or IPv6 address + >>> ipaddress.IPv4Address("192.168.0.256") + Traceback (most recent call last): + ... + ipaddress.AddressValueError: Octet 256 (> 255) not permitted in '192.168.0.256' -For some use cases, it desirable to know whether it is the address or the -netmask which is incorrect. To support these use cases, the class -constructors actually raise the :exc:`ValueError` subclasses -:exc:`ipaddress.AddressValueError` and :exc:`ipaddress.NetmaskValueError` -to indicate exactly which part of the definition failed to parse correctly. + >>> ipaddress.ip_network("192.168.0.1/64") + Traceback (most recent call last): + ... + ValueError: '192.168.0.1/64' does not appear to be an IPv4 or IPv6 network + >>> ipaddress.IPv4Network("192.168.0.1/64") + Traceback (most recent call last): + ... + ipaddress.NetmaskValueError: '64' is not a valid netmask -Both of the module specific exceptions have :exc:`ValueError` as their +However, both of the module specific exceptions have :exc:`ValueError` as their parent class, so if you're not concerned with the particular type of error, you can still write code like the following:: try: - ipaddress.IPv4Address(address) + network = ipaddress.IPv4Network(address) except ValueError: - print('address/netmask is invalid:', address) + print('address/netmask is invalid for IPv4:', address) + diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index bd79e2a..e788c0a5 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1048,7 +1048,7 @@ class _BaseV4: raise ValueError("Ambiguous leading zero in %r not permitted" % octet_str) if octet_int > 255: - raise ValueError("Octet %d > 255 not permitted" % octet_int) + raise ValueError("Octet %d (> 255) not permitted" % octet_int) return octet_int def _string_from_ip_int(self, ip_int): @@ -1591,7 +1591,8 @@ class _BaseV6: hextet_int = int(hextet_str, 16) if hextet_int > 0xFFFF: # This is unreachable due to the string length check above - raise ValueError("Part %d > 0xFFFF not permitted" % hextet_int) + msg = "Part 0x%X (> 0xFFFF) not permitted" + raise ValueError(msg % hextet_int) return hextet_int def _compress_hextets(self, hextets): diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 61ec0d6..2ac37e1 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -126,8 +126,8 @@ class AddressErrors_v4(ErrorReporting): def test_octet_limit(self): def assertBadOctet(addr, octet): - msg = "Octet %d > 255 not permitted in %r" - with self.assertAddressError(msg, octet, addr): + msg = "Octet %d (> 255) not permitted in %r" % (octet, addr) + with self.assertAddressError(re.escape(msg)): ipaddress.IPv4Address(addr) assertBadOctet("12345.67899.-54321.-98765", 12345) @@ -310,7 +310,7 @@ class NetmaskErrorsMixin_v4: assertBadAddress("google.com", "Expected 4 octets") assertBadAddress("10/8", "Expected 4 octets") assertBadAddress("::1.2.3.4", "Only decimal digits") - assertBadAddress("1.2.3.256", "256 > 255") + assertBadAddress("1.2.3.256", re.escape("256 (> 255)")) def test_netmask_errors(self): def assertBadNetmask(addr, netmask): |