1from __future__ import annotations
2
3import ipaddress
4
5from plain.exceptions import ValidationError
6
7
8def clean_ipv6_address(
9 ip_str: str,
10 unpack_ipv4: bool = False,
11 error_message: str = "This is not a valid IPv6 address.",
12) -> str:
13 """
14 Clean an IPv6 address string.
15
16 Raise ValidationError if the address is invalid.
17
18 Replace the longest continuous zero-sequence with "::", remove leading
19 zeroes, and make sure all hextets are lowercase.
20
21 Args:
22 ip_str: A valid IPv6 address.
23 unpack_ipv4: if an IPv4-mapped address is found,
24 return the plain IPv4 address (default=False).
25 error_message: An error message used in the ValidationError.
26
27 Return a compressed IPv6 address or the same value.
28 """
29 try:
30 addr = ipaddress.IPv6Address(int(ipaddress.IPv6Address(ip_str)))
31 except ValueError:
32 raise ValidationError(error_message, code="invalid")
33
34 if unpack_ipv4 and addr.ipv4_mapped:
35 return str(addr.ipv4_mapped)
36 elif addr.ipv4_mapped:
37 return f"::ffff:{str(addr.ipv4_mapped)}"
38
39 return str(addr)
40
41
42def is_valid_ipv6_address(ip_str: str) -> bool:
43 """
44 Return whether or not the `ip_str` string is a valid IPv6 address.
45 """
46 try:
47 ipaddress.IPv6Address(ip_str)
48 except ValueError:
49 return False
50 return True