diff --git a/src/validators/encoding.py b/src/validators/encoding.py index c4ec2ce..7af12fc 100644 --- a/src/validators/encoding.py +++ b/src/validators/encoding.py @@ -11,6 +11,8 @@ def base16(value: str, /): """Return whether or not given value is a valid base16 encoding. + Each encoded byte requires two hexadecimal characters. + Examples: >>> base16('a3f4b2') True @@ -25,7 +27,7 @@ def base16(value: str, /): (Literal[True]): If `value` is a valid base16 encoding. (ValidationError): If `value` is an invalid base16 encoding. """ - return re.fullmatch(r"^[0-9A-Fa-f]+$", value) if value else False + return re.fullmatch(r"(?:[0-9A-Fa-f]{2})+", value) if value else False @validator diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 9728a8f..c92d04e 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -38,6 +38,12 @@ def test_returns_failed_validation_on_invalid_base16(value: str): assert isinstance(base16(value), ValidationError) +@pytest.mark.parametrize("value", ["0", "a", "ABC", "012345678", "abcdef01234567890"]) +def test_base16_rejects_incomplete_bytes(value: str): + """Reject a dangling half-byte even when every character is hexadecimal.""" + assert isinstance(base16(value), ValidationError) + + # ==> base32 <== #