lodum.toml API documentation

# lodum.toml

View Source

      1# SPDX-FileCopyrightText: 2025-present Michael R. Bernstein <zopemaven@gmail.com>
      2#
      3# SPDX-License-Identifier: Apache-2.0
      4try:
      5    import tomllib  # type: ignore[import-not-found]
      6except ImportError:
      7    try:
      8        import tomli as tomllib  # type: ignore[no-redef, import-not-found]
      9    except ImportError:
     10        tomllib = None  # type: ignore
     11
     12try:
     13    import tomli_w
     14except ImportError:
     15    tomli_w = None  # type: ignore
     16from typing import Any, Dict, Iterator, Type, TypeVar
     17
     18from .core import Loader, BaseDumper, BaseLoader
     19from .exception import DeserializationError
     20from .internal import dump, load, DEFAULT_MAX_SIZE, generate_schema
     21
     22T = TypeVar("T")
     23
     24# --- Public API ---
     25
     26
     27def dumps(obj: Any) -> str:
     28    """
     29    Encodes a Python object to a TOML string.
     30
     31    Args:
     32        obj: The object to encode. Must be lodum-enabled or a supported type.
     33
     34    Returns:
     35        A TOML string representation of the object.
     36
     37    Raises:
     38        ImportError: If tomli-w is not installed.
     39    """
     40    if tomli_w is None:
     41        raise ImportError(
     42            "tomli-w is required for TOML serialization. Install it with 'pip install lodum[toml]'."
     43        )
     44    dumper = TomlDumper()
     45    dumped_data = dump(obj, dumper)
     46    return tomli_w.dumps(dumped_data)
     47
     48
     49def loads(cls: Type[T], toml_string: str, max_size: int = DEFAULT_MAX_SIZE) -> T:
     50    """
     51    Decodes a TOML string into a Python object of the specified type.
     52
     53    Args:
     54        cls: The class to instantiate.
     55        toml_string: The TOML data to decode.
     56        max_size: Maximum allowed size of the input string in bytes.
     57
     58    Returns:
     59        An instance of cls populated with the decoded data.
     60
     61    Raises:
     62        DeserializationError: If the input is invalid or exceeds max_size.
     63        ImportError: If tomllib (or tomli) is not installed.
     64    """
     65    if len(toml_string) > max_size:
     66        raise DeserializationError(
     67            f"Input size ({len(toml_string)}) exceeds maximum allowed ({max_size})"
     68        )
     69
     70    if tomllib is None:
     71        raise ImportError(
     72            "tomllib (or tomli) is required for TOML deserialization. Install it with 'pip install lodum[toml]'."
     73        )
     74    try:
     75        data = tomllib.loads(toml_string)
     76    except Exception as e:
     77        if isinstance(e, DeserializationError):
     78            raise e
     79        raise DeserializationError(f"Failed to parse TOML: {e}")
     80    loader = TomlLoader(data)
     81    return load(cls, loader)
     82
     83
     84def schema(cls: Type[Any]) -> Dict[str, Any]:
     85    """Generates a JSON Schema for a given lodum-enabled class."""
     86    return generate_schema(cls)
     87
     88
     89# --- TOML Dumper Implementation ---
     90
     91
     92class TomlDumper(BaseDumper):
     93    def dump_bytes(self, value: bytes) -> Any:
     94        import base64
     95
     96        return base64.b64encode(value).decode("ascii")
     97
     98
     99# --- TOML Loader Implementation ---
    100
    101
    102class TomlLoader(BaseLoader):
    103    def load_list(self) -> Iterator["Loader"]:
    104        if not isinstance(self._data, list):
    105            raise DeserializationError(
    106                f"Expected list, got {type(self._data).__name__}"
    107            )
    108        return (TomlLoader(item) for item in self._data)
    109
    110    def load_dict(self) -> Iterator[tuple[str, "Loader"]]:
    111        if not isinstance(self._data, dict):
    112            raise DeserializationError(
    113                f"Expected dict, got {type(self._data).__name__}"
    114            )
    115        return ((k, TomlLoader(v)) for k, v in self._data.items())
    116
    117    def load_bytes_value(self, value: Any) -> bytes:
    118        if not isinstance(value, str):
    119            raise DeserializationError(f"Expected str, got {type(value).__name__}")
    120        import base64
    121
    122        try:
    123            return base64.b64decode(value)
    124        except Exception as e:
    125            raise DeserializationError(f"Failed to decode base64: {e}")

def dumps(obj: Any) -> str: View Source

    28def dumps(obj: Any) -> str:
    29    """
    30    Encodes a Python object to a TOML string.
    31
    32    Args:
    33        obj: The object to encode. Must be lodum-enabled or a supported type.
    34
    35    Returns:
    36        A TOML string representation of the object.
    37
    38    Raises:
    39        ImportError: If tomli-w is not installed.
    40    """
    41    if tomli_w is None:
    42        raise ImportError(
    43            "tomli-w is required for TOML serialization. Install it with 'pip install lodum[toml]'."
    44        )
    45    dumper = TomlDumper()
    46    dumped_data = dump(obj, dumper)
    47    return tomli_w.dumps(dumped_data)

Encodes a Python object to a TOML string.

Args: obj: The object to encode. Must be lodum-enabled or a supported type.

Returns: A TOML string representation of the object.

Raises: ImportError: If tomli-w is not installed.

def loads(cls: Type[~T], toml_string: str, max_size: int = 10485760) -> ~T: View Source

    50def loads(cls: Type[T], toml_string: str, max_size: int = DEFAULT_MAX_SIZE) -> T:
    51    """
    52    Decodes a TOML string into a Python object of the specified type.
    53
    54    Args:
    55        cls: The class to instantiate.
    56        toml_string: The TOML data to decode.
    57        max_size: Maximum allowed size of the input string in bytes.
    58
    59    Returns:
    60        An instance of cls populated with the decoded data.
    61
    62    Raises:
    63        DeserializationError: If the input is invalid or exceeds max_size.
    64        ImportError: If tomllib (or tomli) is not installed.
    65    """
    66    if len(toml_string) > max_size:
    67        raise DeserializationError(
    68            f"Input size ({len(toml_string)}) exceeds maximum allowed ({max_size})"
    69        )
    70
    71    if tomllib is None:
    72        raise ImportError(
    73            "tomllib (or tomli) is required for TOML deserialization. Install it with 'pip install lodum[toml]'."
    74        )
    75    try:
    76        data = tomllib.loads(toml_string)
    77    except Exception as e:
    78        if isinstance(e, DeserializationError):
    79            raise e
    80        raise DeserializationError(f"Failed to parse TOML: {e}")
    81    loader = TomlLoader(data)
    82    return load(cls, loader)

Decodes a TOML string into a Python object of the specified type.

Args: cls: The class to instantiate. toml_string: The TOML data to decode. max_size: Maximum allowed size of the input string in bytes.

Returns: An instance of cls populated with the decoded data.

Raises: DeserializationError: If the input is invalid or exceeds max_size. ImportError: If tomllib (or tomli) is not installed.

def schema(cls: Type[Any]) -> Dict[str, Any]: View Source

    85def schema(cls: Type[Any]) -> Dict[str, Any]:
    86    """Generates a JSON Schema for a given lodum-enabled class."""
    87    return generate_schema(cls)

Generates a JSON Schema for a given lodum-enabled class.

class TomlDumper(lodum.core.BaseDumper): View Source

    93class TomlDumper(BaseDumper):
    94    def dump_bytes(self, value: bytes) -> Any:
    95        import base64
    96
    97        return base64.b64encode(value).decode("ascii")

Base implementation of the Dumper protocol to reduce duplication.

def dump_bytes(self, value: bytes) -> Any: View Source

    94    def dump_bytes(self, value: bytes) -> Any:
    95        import base64
    96
    97        return base64.b64encode(value).decode("ascii")

class TomlLoader(lodum.core.BaseLoader): View Source

    103class TomlLoader(BaseLoader):
    104    def load_list(self) -> Iterator["Loader"]:
    105        if not isinstance(self._data, list):
    106            raise DeserializationError(
    107                f"Expected list, got {type(self._data).__name__}"
    108            )
    109        return (TomlLoader(item) for item in self._data)
    110
    111    def load_dict(self) -> Iterator[tuple[str, "Loader"]]:
    112        if not isinstance(self._data, dict):
    113            raise DeserializationError(
    114                f"Expected dict, got {type(self._data).__name__}"
    115            )
    116        return ((k, TomlLoader(v)) for k, v in self._data.items())
    117
    118    def load_bytes_value(self, value: Any) -> bytes:
    119        if not isinstance(value, str):
    120            raise DeserializationError(f"Expected str, got {type(value).__name__}")
    121        import base64
    122
    123        try:
    124            return base64.b64decode(value)
    125        except Exception as e:
    126            raise DeserializationError(f"Failed to decode base64: {e}")

Base implementation of the Loader protocol to reduce duplication.

def load_list(self) -> Iterator[lodum.core.Loader]: View Source

    104    def load_list(self) -> Iterator["Loader"]:
    105        if not isinstance(self._data, list):
    106            raise DeserializationError(
    107                f"Expected list, got {type(self._data).__name__}"
    108            )
    109        return (TomlLoader(item) for item in self._data)

def load_dict(self) -> Iterator[tuple[str, lodum.core.Loader]]: View Source

    111    def load_dict(self) -> Iterator[tuple[str, "Loader"]]:
    112        if not isinstance(self._data, dict):
    113            raise DeserializationError(
    114                f"Expected dict, got {type(self._data).__name__}"
    115            )
    116        return ((k, TomlLoader(v)) for k, v in self._data.items())

def load_bytes_value(self, value: Any) -> bytes: View Source

    118    def load_bytes_value(self, value: Any) -> bytes:
    119        if not isinstance(value, str):
    120            raise DeserializationError(f"Expected str, got {type(value).__name__}")
    121        import base64
    122
    123        try:
    124            return base64.b64decode(value)
    125        except Exception as e:
    126            raise DeserializationError(f"Failed to decode base64: {e}")