2,279 bytes · the Python implementation · view raw
from typing import List, Optional, Sequence
from .net_ipv4_parse_ipv4 import ipv4_value
_ALLOWED = frozenset("0123456789abcdefABCDEF:.")
def parse_ipv6(text: str) -> List[int]:
"""The eight 16-bit groups of an IPv6 address written as RFC 4291 allows. Hex groups of one to four digits in either case, at most one "::" standing for one or more zero groups, and optionally a dotted IPv4 address as the last 32 bits. A zone id ("%eth0") or a prefix ("/64") is not part of an address and is refused, as is anything around it (spaces, newlines). """
groups = ipv6_groups(text)
if groups isNone:
raise ValueError('"%s" is not an IPv6 address' % (text,))
return groups
# Exported for is_ipv6 and for capabilities that parse IPv6 text without# wanting an error; not part of the group's contract.def ipv6_groups(text: object) -> Optional[List[int]]:
"""The eight groups, or None when the text is not an IPv6 address."""ifnot isinstance(text, str) or text == "":
returnNonefor ch in text:
if ch notin _ALLOWED:
returnNone
at = text.find("::")
if at < 0:
groups = _pieces(text.split(":"), True)
return groups if groups isnotNoneand len(groups) == 8elseNone
before = text[:at]
after = text[at + 2:]
if"::"in after:
returnNone
head = _pieces(before.split(":") if before else [], False)
tail = _pieces(after.split(":") if after else [], True)
if head isNoneor tail isNone:
returnNone# "::" stands for at least one group, so at most seven are written.if len(head) + len(tail) > 7:
returnNonereturn head + [0] * (8 - len(head) - len(tail)) + tail
def _pieces(parts: Sequence[str], may_end_with_ipv4: bool) -> Optional[List[int]]:
groups: List[int] = []
for i, part in enumerate(parts):
if"."in part:
ifnot may_end_with_ipv4 or i != len(parts) - 1:
returnNone
v4 = ipv4_value(part)
if v4 isNone:
returnNone
groups.extend([v4 // 65536, v4 % 65536])
continueif len(part) < 1or len(part) > 4:
returnNone
groups.append(int(part, 16))
return groups