from typing import List, NamedTuple, Optional from .net_ipv4_parse_ipv4 import ipv4_value from .net_ipv6_parse_ipv6 import ipv6_groups def is_cidr(text: str) -> bool: """True for a well-formed IPv4 (prefix 0-32) or IPv6 (prefix 0-128) block; never raises.""" return cidr_block(text) is not None # Exported for the other functions of this group; not part of its contract. class CidrBlock(NamedTuple): version: int bits: List[int] prefix: int def cidr_block(text: object) -> Optional[CidrBlock]: """The block, or None when the text is not address/prefix with a strict decimal prefix.""" if not isinstance(text, str) or text.count("/") != 1: return None address, digits = text.split("/") if len(digits) < 1 or len(digits) > 3: return None for ch in digits: if not ("0" <= ch <= "9"): return None if len(digits) > 1 and digits[0] == "0": return None prefix = int(digits) v4 = ipv4_value(address) if v4 is not None: return CidrBlock(4, [v4], prefix) if prefix <= 32 else None v6 = ipv6_groups(address) if v6 is not None: return CidrBlock(6, v6, prefix) if prefix <= 128 else None return None