IPAddress = Union[IPv4Address, IPv6Address]
+class IPAddressEM(BaseValueType):
+ """
+ IP address with exclamation mark suffix, e.g. '127.0.0.1!'.
+ """
+
+ _value: str
+ _addr: Union[ipaddress.IPv4Address, ipaddress.IPv6Address]
+
+ def __init__(self, source_value: Any, object_path: str = "/") -> None:
+ super().__init__(source_value)
+ if isinstance(source_value, str):
+ if source_value.endswith("!"):
+ addr, suff = source_value.split("!", 1)
+ if suff != "":
+ raise ValueError(f"suffix '{suff}' found after '!'.")
+ else:
+ raise ValueError("string does not end with '!'.")
+ try:
+ self._addr: Union[ipaddress.IPv4Address, ipaddress.IPv6Address] = ipaddress.ip_address(addr)
+ self._value = source_value
+ except ValueError as e:
+ raise ValueError("failed to parse IP address.") from e
+ else:
+ raise ValueError(
+ "Unexpected value for a IPv6 address."
+ f" Expected string, got '{source_value}' with type '{type(source_value)}'",
+ object_path,
+ )
+
+ def to_std(self) -> str:
+ return self._value
+
+ def __str__(self) -> str:
+ return self._value
+
+ def __int__(self) -> int:
+ raise ValueError("Can't convert to an integer")
+
+ def __repr__(self) -> str:
+ return f'{type(self).__name__}("{self._value}")'
+
+ def __eq__(self, o: object) -> bool:
+ """
+ Two instances of IPAddressEM are equal when they represent same string.
+ """
+ return isinstance(o, IPAddressEM) and o._value == self._value
+
+ def serialize(self) -> Any:
+ return self._value
+
+ @classmethod
+ def json_schema(cls: Type["IPAddressEM"]) -> Dict[Any, Any]:
+ return {
+ "type": "string",
+ }
+
+
class IPNetwork(BaseValueType):
_value: Union[ipaddress.IPv4Network, ipaddress.IPv6Network]