This custom type allows use of values in the format <domain-name>[@<port>].
from .generic_types import ListOrItem
from .types import (
DomainName,
+ DomainNameOptionalPort,
EscapedStr,
EscapedStr32B,
FloatNonNegative,
"PolicyFlagEnum",
"DNSRecordTypeEnum",
"DomainName",
+ "DomainNameOptionalPort",
"EscapedStr",
"EscapedStr32B",
"FloatNonNegative",
raise ValueError(f"expected '<ip-address>[@<port>]', got '{parts}'.", object_path)
+class DomainNameOptionalPort(StrBase):
+ name: DomainName
+ port: Optional[PortNumber] = None
+
+ def __init__(self, source_value: Any, object_path: str = "/") -> None:
+ super().__init__(source_value)
+ parts = source_value.split("@")
+ if 0 < len(parts) < 3:
+ try:
+ self.name = DomainName(parts[0])
+ except ValueError as e:
+ raise ValueError(f"failed to parse domain name '{parts[0]}'.", object_path) from e
+ if len(parts) == 2:
+ self.port = PortNumber.from_str(parts[1], object_path)
+ else:
+ raise ValueError(f"expected '<domain-name>[@<port>]', got '{parts}'.", object_path)
+
+
class IPv4Address(BaseValueType):
_value: ipaddress.IPv4Address
from knot_resolver.datamodel.types import (
Dir,
DomainName,
+ DomainNameOptionalPort,
EscapedStr,
InterfaceName,
InterfaceOptionalPort,
IPAddressOptionalPort(val)
+@pytest.mark.parametrize("val", ["localhost", "localhost@5353"])
+def test_domain_name_optional_port_valid(val: str):
+ o = DomainNameOptionalPort(val)
+ assert str(o) == val
+ assert o == DomainNameOptionalPort(val)
+ assert str(o.name) == (val.split("@", 1)[0] if "@" in val else val)
+ assert o.port == (PortNumber(int(val.split("@", 1)[1])) if "@" in val else None)
+
+
+@pytest.mark.parametrize("val", ["localhost@", "@55"])
+def test_domain_name_optional_port_invalid(val: Any):
+ with raises(ValueError):
+ DomainNameOptionalPort(val)
+
+
@pytest.mark.parametrize("val", ["123.4.5.6", "192.168.0.1"])
def test_ipv4_address_valid(val: str):
o = IPv4Address(val)