Skip to content

Verify a literal value

verify_literal() checks a value against a typing.Literal type and raises an exception if the value is not allowed. Like ensure_type(), it returns the value on success, so it fits naturally into an expression.

The verify_literal() function uses TypeForm to narrow the type of the return value, so type checkers will accept the verified string as an instance of the string literal.

Basic use

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from typing import Literal, cast

from buzz import verify_literal

Species = Literal["jawa", "ewok", "pyke"]

# Vanilla python
def vanilla(val: str) -> Species:
    if val not in ("jawa", "ewok", "pyke"):
        raise Exception("Received an unexpected value!")
    return cast(Species, val)

# With py-buzz
def buzzy(val: str) -> Species:
    return verify_literal(val, Literal["jawa", "ewok", "pyke"])

A Buzz subclass method also binds the exception type:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from typing import Literal

from buzz import Buzz, verify_literal

Species = Literal["jawa", "ewok", "pyke"]


class ConfigError(Buzz):
    pass


def buzzy(val: str) -> Species:
    return ConfigError.verify_literal(val, Species)

Advanced use

Both forms return the accepted value, run do_else on success, and pass the exception being raised to do_except on an invalid value. Constructor arguments can attach context to the error:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
from typing import Literal

from buzz import Buzz, verify_literal

Species = Literal["jawa", "ewok", "pyke"]


class SpeciesError(Exception):
    def __init__(self, message: str, species_code: int, *, field: str):
        self.species_code = species_code
        self.field = field
        super().__init__(message)


def record_rejection(exc: Exception) -> None:
    print(f"Rejected unknown species: {exc}")


def record_accepted() -> None:
    print("Well known species accepted")


def parse_species(species: str) -> Species:
    return verify_literal(
        species,
        Species,
        "Unknown species",
        raise_exc_class=SpeciesError,
        raise_args=[400],
        raise_kwargs={"field": "species"},
        do_except=record_rejection,
        do_else=record_accepted,
    )


class SpeciesBuzzError(Buzz):
    def __init__(
        self,
        message: str,
        species_code: int,
        *,
        field: str,
        base_message: str | None = None,
    ):
        self.species_code = species_code
        self.field = field
        super().__init__(message, base_message=base_message)


def parse_species_with_buzz(species: str) -> Species:
    return SpeciesBuzzError.verify_literal(
        species,
        Species,
        "Unknown species",
        raise_args=[400],
        raise_kwargs={"field": "species"},
        do_except=record_rejection,
        do_else=record_accepted,
    )

For an unknown value, each form constructs its configured exception with species_code=400 and field="species", calls the rejection callback, and raises. For an allowed value, they call record_accepted and return it. A non-Literal type raises TypeError immediately. Python permits enum members in runtime Literal values, but verify_literal()'s current LiteralElement annotation excludes enums, so static type checkers may reject those annotations.