Skip to content

Serialization

provide.foundation.serialization

TODO: Add module docstring.

Functions

env_dumps

env_dumps(
    obj: dict[str, str], *, quote_values: bool = True
) -> str

Serialize dictionary to .env file format string.

Parameters:

Name Type Description Default
obj dict[str, str]

Dictionary of environment variables

required
quote_values bool

Whether to quote string values

True

Returns:

Type Description
str

.env format string

Raises:

Type Description
ValidationError

If object cannot be serialized

Example

env_dumps({"KEY": "value"}) 'KEY="value"\n' env_dumps({"KEY": "value"}, quote_values=False) 'KEY=value\n'

Source code in provide/foundation/serialization/env.py
def env_dumps(obj: dict[str, str], *, quote_values: bool = True) -> str:
    """Serialize dictionary to .env file format string.

    Args:
        obj: Dictionary of environment variables
        quote_values: Whether to quote string values

    Returns:
        .env format string

    Raises:
        ValidationError: If object cannot be serialized

    Example:
        >>> env_dumps({"KEY": "value"})
        'KEY="value"\\n'
        >>> env_dumps({"KEY": "value"}, quote_values=False)
        'KEY=value\\n'

    """
    from provide.foundation.errors import ValidationError

    if not isinstance(obj, dict):
        raise ValidationError("ENV serialization requires a dictionary")

    lines: list[str] = []

    try:
        for key, value in obj.items():
            # Ensure key is valid
            if not isinstance(key, str) or not key:
                raise ValidationError(f"Invalid environment variable name: {key}")

            value_str = str(value)

            # Quote if requested and contains spaces
            if quote_values and (" " in value_str or "\t" in value_str):
                value_str = f'"{value_str}"'

            lines.append(f"{key}={value_str}")

        return "\n".join(lines) + "\n"
    except Exception as e:
        raise ValidationError(f"Cannot serialize object to ENV format: {e}") from e

env_loads

env_loads(
    s: str, *, use_cache: bool = True
) -> dict[str, str]

Deserialize .env file format string to dictionary.

Parameters:

Name Type Description Default
s str

.env format string to deserialize

required
use_cache bool

Whether to use caching for this operation

True

Returns:

Type Description
dict[str, str]

Dictionary of environment variables

Raises:

Type Description
ValidationError

If string is not valid .env format

Example

env_loads('KEY=value') {'KEY': 'value'} env_loads('KEY="value"')

Source code in provide/foundation/serialization/env.py
def env_loads(s: str, *, use_cache: bool = True) -> dict[str, str]:
    """Deserialize .env file format string to dictionary.

    Args:
        s: .env format string to deserialize
        use_cache: Whether to use caching for this operation

    Returns:
        Dictionary of environment variables

    Raises:
        ValidationError: If string is not valid .env format

    Example:
        >>> env_loads('KEY=value')
        {'KEY': 'value'}
        >>> env_loads('KEY="value"')
        {'KEY': 'value'}

    """
    from provide.foundation.errors import ValidationError

    if not isinstance(s, str):
        raise ValidationError("Input must be a string")

    # Check cache first if enabled
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "env")
        cached = get_serialization_cache().get(cache_key)
        if cached is not None:
            return cached

    result: dict[str, str] = {}

    try:
        for line_num, line in enumerate(s.splitlines(), 1):
            parsed = _parse_env_line(line, line_num)
            if parsed is not None:
                key, value = parsed
                result[key] = value
    except ValidationError:
        raise
    except Exception as e:
        raise ValidationError(f"Invalid .env format string: {e}") from e

    # Cache result
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "env")
        get_serialization_cache().set(cache_key, result)

    return result

get_cache_key

get_cache_key(content: str, format: str) -> str

Generate cache key from content and format.

Parameters:

Name Type Description Default
content str

String content to hash

required
format str

Format identifier (json, yaml, toml, etc.)

required

Returns:

Type Description
str

Cache key string

Source code in provide/foundation/serialization/cache.py
def get_cache_key(content: str, format: str) -> str:
    """Generate cache key from content and format.

    Args:
        content: String content to hash
        format: Format identifier (json, yaml, toml, etc.)

    Returns:
        Cache key string

    """
    content_hash = hashlib.sha256(content.encode()).hexdigest()[:16]
    return f"{format}:{content_hash}"

ini_dumps

ini_dumps(
    obj: dict[str, dict[str, str]],
    *,
    include_default: bool = False
) -> str

Serialize nested dictionary to INI format string.

Parameters:

Name Type Description Default
obj dict[str, dict[str, str]]

Nested dictionary (sections -> key-value pairs)

required
include_default bool

Whether to include DEFAULT section

False

Returns:

Type Description
str

INI format string

Raises:

Type Description
ValidationError

If object cannot be serialized

Example

ini_dumps({"section": {"key": "value"}}) '[section]\nkey = value\n\n'

Source code in provide/foundation/serialization/ini.py
def ini_dumps(obj: dict[str, dict[str, str]], *, include_default: bool = False) -> str:
    """Serialize nested dictionary to INI format string.

    Args:
        obj: Nested dictionary (sections -> key-value pairs)
        include_default: Whether to include DEFAULT section

    Returns:
        INI format string

    Raises:
        ValidationError: If object cannot be serialized

    Example:
        >>> ini_dumps({"section": {"key": "value"}})
        '[section]\\nkey = value\\n\\n'

    """
    from provide.foundation.errors import ValidationError

    if not isinstance(obj, dict):
        raise ValidationError("INI serialization requires a dictionary")

    parser = ConfigParser()

    try:
        for section_name, section_data in obj.items():
            if section_name == "DEFAULT" and not include_default:
                continue

            if not isinstance(section_data, dict):
                raise ValidationError(f"Section '{section_name}' must be a dictionary")

            if section_name != "DEFAULT":
                parser.add_section(section_name)

            for key, value in section_data.items():
                parser.set(section_name, key, str(value))

        # Write to string
        output = StringIO()
        parser.write(output)
        return output.getvalue()
    except Exception as e:
        raise ValidationError(f"Cannot serialize object to INI: {e}") from e

ini_loads

ini_loads(
    s: str, *, use_cache: bool = True
) -> dict[str, dict[str, str]]

Deserialize INI format string to nested dictionary.

Parameters:

Name Type Description Default
s str

INI format string to deserialize

required
use_cache bool

Whether to use caching for this operation

True

Returns:

Type Description
dict[str, dict[str, str]]

Nested dictionary (sections -> key-value pairs)

Raises:

Type Description
ValidationError

If string is not valid INI format

Example

ini_loads('[section]\nkey = value') {'section': {'key': 'value'}}

Source code in provide/foundation/serialization/ini.py
def ini_loads(s: str, *, use_cache: bool = True) -> dict[str, dict[str, str]]:
    """Deserialize INI format string to nested dictionary.

    Args:
        s: INI format string to deserialize
        use_cache: Whether to use caching for this operation

    Returns:
        Nested dictionary (sections -> key-value pairs)

    Raises:
        ValidationError: If string is not valid INI format

    Example:
        >>> ini_loads('[section]\\nkey = value')
        {'section': {'key': 'value'}}

    """
    from provide.foundation.errors import ValidationError

    if not isinstance(s, str):
        raise ValidationError("Input must be a string")

    # Check cache first if enabled
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "ini")
        cached = get_serialization_cache().get(cache_key)
        if cached is not None:
            return cached

    parser = ConfigParser()

    try:
        parser.read_string(s)
    except Exception as e:
        raise ValidationError(f"Invalid INI string: {e}") from e

    # Convert to dictionary
    result: dict[str, dict[str, str]] = {}

    for section in parser.sections():
        result[section] = dict(parser.items(section))

    # Include DEFAULT section if present
    if parser.defaults():
        result["DEFAULT"] = dict(parser.defaults())

    # Cache result
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "ini")
        get_serialization_cache().set(cache_key, result)

    return result

json_dumps

json_dumps(
    obj: Any,
    *,
    ensure_ascii: bool = False,
    indent: int | None = None,
    sort_keys: bool = False,
    default: Any = None
) -> str

Serialize object to JSON string with Foundation tracking.

Parameters:

Name Type Description Default
obj Any

Object to serialize

required
ensure_ascii bool

If True, non-ASCII characters are escaped

False
indent int | None

Number of spaces for indentation (None for compact)

None
sort_keys bool

Whether to sort dictionary keys

False
default Any

Function called for objects that can't be serialized

None

Returns:

Type Description
str

JSON string representation

Raises:

Type Description
ValidationError

If object cannot be serialized

Example

json_dumps({"key": "value"}) '{"key": "value"}' json_dumps({"b": 1, "a": 2}, sort_keys=True, indent=2) '{\n "a": 2,\n "b": 1\n}'

Source code in provide/foundation/serialization/json.py
def json_dumps(
    obj: Any,
    *,
    ensure_ascii: bool = False,
    indent: int | None = None,
    sort_keys: bool = False,
    default: Any = None,
) -> str:
    """Serialize object to JSON string with Foundation tracking.

    Args:
        obj: Object to serialize
        ensure_ascii: If True, non-ASCII characters are escaped
        indent: Number of spaces for indentation (None for compact)
        sort_keys: Whether to sort dictionary keys
        default: Function called for objects that can't be serialized

    Returns:
        JSON string representation

    Raises:
        ValidationError: If object cannot be serialized

    Example:
        >>> json_dumps({"key": "value"})
        '{"key": "value"}'
        >>> json_dumps({"b": 1, "a": 2}, sort_keys=True, indent=2)
        '{\\n  "a": 2,\\n  "b": 1\\n}'

    """
    from provide.foundation.errors import ValidationError

    try:
        return json.dumps(obj, ensure_ascii=ensure_ascii, indent=indent, sort_keys=sort_keys, default=default)
    except (TypeError, ValueError) as e:
        raise ValidationError(f"Cannot serialize object to JSON: {e}") from e

json_loads

json_loads(s: str, *, use_cache: bool = True) -> Any

Deserialize JSON string to Python object with Foundation tracking.

Parameters:

Name Type Description Default
s str

JSON string to deserialize

required
use_cache bool

Whether to use caching for this operation

True

Returns:

Type Description
Any

Deserialized Python object

Raises:

Type Description
ValidationError

If string is not valid JSON

Example

json_loads('{"key": "value"}') {'key': 'value'} json_loads('[1, 2, 3]') [1, 2, 3]

Source code in provide/foundation/serialization/json.py
def json_loads(s: str, *, use_cache: bool = True) -> Any:
    """Deserialize JSON string to Python object with Foundation tracking.

    Args:
        s: JSON string to deserialize
        use_cache: Whether to use caching for this operation

    Returns:
        Deserialized Python object

    Raises:
        ValidationError: If string is not valid JSON

    Example:
        >>> json_loads('{"key": "value"}')
        {'key': 'value'}
        >>> json_loads('[1, 2, 3]')
        [1, 2, 3]

    """
    from provide.foundation.errors import ValidationError

    if not isinstance(s, str):
        raise ValidationError("Input must be a string")

    # Check cache first if enabled
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "json")
        cached = get_serialization_cache().get(cache_key)
        if cached is not None:
            return cached

    try:
        result = json.loads(s)
    except json.JSONDecodeError as e:
        raise ValidationError(f"Invalid JSON string: {e}") from e

    # Cache result
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "json")
        get_serialization_cache().set(cache_key, result)

    return result

toml_dumps

toml_dumps(obj: dict[str, Any]) -> str

Serialize dictionary to TOML string.

Parameters:

Name Type Description Default
obj dict[str, Any]

Dictionary to serialize (TOML requires dict at top level)

required

Returns:

Type Description
str

TOML string representation

Raises:

Type Description
ValidationError

If object cannot be serialized

ImportError

If tomli-w is not installed

Example

toml_dumps({"key": "value"}) 'key = "value"\n'

Source code in provide/foundation/serialization/toml.py
def toml_dumps(obj: dict[str, Any]) -> str:
    """Serialize dictionary to TOML string.

    Args:
        obj: Dictionary to serialize (TOML requires dict at top level)

    Returns:
        TOML string representation

    Raises:
        ValidationError: If object cannot be serialized
        ImportError: If tomli-w is not installed

    Example:
        >>> toml_dumps({"key": "value"})
        'key = "value"\\n'

    """
    from provide.foundation.errors import ValidationError

    try:
        import tomli_w
    except ImportError as e:
        raise ImportError("tomli-w is required for TOML write operations") from e

    if not isinstance(obj, dict):
        raise ValidationError("TOML serialization requires a dictionary at the top level")

    try:
        return tomli_w.dumps(obj)
    except Exception as e:
        raise ValidationError(f"Cannot serialize object to TOML: {e}") from e

toml_loads

toml_loads(
    s: str, *, use_cache: bool = True
) -> dict[str, Any]

Deserialize TOML string to Python dictionary.

Parameters:

Name Type Description Default
s str

TOML string to deserialize

required
use_cache bool

Whether to use caching for this operation

True

Returns:

Type Description
dict[str, Any]

Deserialized Python dictionary

Raises:

Type Description
ValidationError

If string is not valid TOML

Example

toml_loads('key = "value"')

Source code in provide/foundation/serialization/toml.py
def toml_loads(s: str, *, use_cache: bool = True) -> dict[str, Any]:
    """Deserialize TOML string to Python dictionary.

    Args:
        s: TOML string to deserialize
        use_cache: Whether to use caching for this operation

    Returns:
        Deserialized Python dictionary

    Raises:
        ValidationError: If string is not valid TOML

    Example:
        >>> toml_loads('key = "value"')
        {'key': 'value'}

    """
    from provide.foundation.errors import ValidationError

    if not isinstance(s, str):
        raise ValidationError("Input must be a string")

    # Check cache first if enabled
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "toml")
        cached = get_serialization_cache().get(cache_key)
        if cached is not None:
            return cached

    try:
        result = tomllib.loads(s)
    except Exception as e:
        raise ValidationError(f"Invalid TOML string: {e}") from e

    # Cache result
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "toml")
        get_serialization_cache().set(cache_key, result)

    return result

yaml_dumps

yaml_dumps(
    obj: Any,
    *,
    default_flow_style: bool = False,
    allow_unicode: bool = True,
    sort_keys: bool = False
) -> str

Serialize object to YAML string.

Parameters:

Name Type Description Default
obj Any

Object to serialize

required
default_flow_style bool

Use flow style (JSON-like) instead of block style

False
allow_unicode bool

If True, allow unicode characters

True
sort_keys bool

Whether to sort dictionary keys

False

Returns:

Type Description
str

YAML string representation

Raises:

Type Description
ValidationError

If object cannot be serialized

ImportError

If PyYAML is not installed

Example

yaml_dumps({"key": "value"}) 'key: value\n'

Source code in provide/foundation/serialization/yaml.py
def yaml_dumps(
    obj: Any,
    *,
    default_flow_style: bool = False,
    allow_unicode: bool = True,
    sort_keys: bool = False,
) -> str:
    """Serialize object to YAML string.

    Args:
        obj: Object to serialize
        default_flow_style: Use flow style (JSON-like) instead of block style
        allow_unicode: If True, allow unicode characters
        sort_keys: Whether to sort dictionary keys

    Returns:
        YAML string representation

    Raises:
        ValidationError: If object cannot be serialized
        ImportError: If PyYAML is not installed

    Example:
        >>> yaml_dumps({"key": "value"})
        'key: value\\n'

    """
    try:
        import yaml
    except ImportError as e:
        raise ImportError("PyYAML is required for YAML operations") from e

    from provide.foundation.errors import ValidationError

    try:
        return yaml.dump(
            obj,
            default_flow_style=default_flow_style,
            allow_unicode=allow_unicode,
            sort_keys=sort_keys,
        )
    except Exception as e:
        raise ValidationError(f"Cannot serialize object to YAML: {e}") from e

yaml_loads

yaml_loads(s: str, *, use_cache: bool = True) -> Any

Deserialize YAML string to Python object.

Parameters:

Name Type Description Default
s str

YAML string to deserialize

required
use_cache bool

Whether to use caching for this operation

True

Returns:

Type Description
Any

Deserialized Python object

Raises:

Type Description
ValidationError

If string is not valid YAML

ImportError

If PyYAML is not installed

Example

yaml_loads('key: value') {'key': 'value'} yaml_loads('[1, 2, 3]') [1, 2, 3]

Source code in provide/foundation/serialization/yaml.py
def yaml_loads(s: str, *, use_cache: bool = True) -> Any:
    """Deserialize YAML string to Python object.

    Args:
        s: YAML string to deserialize
        use_cache: Whether to use caching for this operation

    Returns:
        Deserialized Python object

    Raises:
        ValidationError: If string is not valid YAML
        ImportError: If PyYAML is not installed

    Example:
        >>> yaml_loads('key: value')
        {'key': 'value'}
        >>> yaml_loads('[1, 2, 3]')
        [1, 2, 3]

    """
    from provide.foundation.errors import ValidationError

    if not isinstance(s, str):
        raise ValidationError("Input must be a string")

    try:
        import yaml
    except ImportError as e:
        raise ImportError("PyYAML is required for YAML operations") from e

    # Check cache first if enabled
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "yaml")
        cached = get_serialization_cache().get(cache_key)
        if cached is not None:
            return cached

    try:
        result = yaml.safe_load(s)
    except yaml.YAMLError as e:
        raise ValidationError(f"Invalid YAML string: {e}") from e

    # Cache result
    if use_cache and get_cache_enabled():
        cache_key = get_cache_key(s, "yaml")
        get_serialization_cache().set(cache_key, result)

    return result