Source code for project_config.serializers.python

"""Object serializing for Python scripts namespaces."""

from __future__ import annotations

import contextlib
import re
from typing import TYPE_CHECKING, Any, cast


if TYPE_CHECKING:
    from project_config.compat import TypeAlias

    Namespace: TypeAlias = dict[str, Any]

DEFAULT_NAMESPACE: Namespace = {}


[docs]def loads( string: str, namespace: Namespace = DEFAULT_NAMESPACE, ) -> Namespace: """Execute a Python file and exposes their namespace as a dictionary. The logic is based in Sphinx's configuration file loader: https://github.com/sphinx-doc/sphinx/blob/4d7558e9/sphinx/config.py#L332 Args: string (str): Python script. namespace (dict): Namespace to update. Returns: dict: Global namespace of the Python script as an object. """ exec(compile(string, "utf-8", "exec"), namespace) # noqa: DUO105,DUO110 with contextlib.suppress(KeyError): del namespace["__builtins__"] # we don't care about builtins return namespace
[docs]def _pyobject_to_string(value: Any) -> str: if isinstance(value, str): escaped_value = re.sub(r"([\"])", r"\\\1", value) return f'"{escaped_value}"' if isinstance(value, bool): return "True" if value else "False" if isinstance(value, type): value = value.__name__ elif isinstance(value, dict): if len(str(value)) > 60: # noqa: PLR2004 newline = "\n " delimiter = "\n" else: newline = " " delimiter = "" result = ( "{" + (newline if newline != " " else "") + f",{newline}".join( f'"{key}": {str(_pyobject_to_string(item))}' for key, item in value.items() ) ) if newline != " ": result += "," result += delimiter + "}" return result elif isinstance(value, list): if len(str(value)) > 60: # noqa: PLR2004 newline = "\n " delimiter = "\n" else: newline = " " delimiter = "" result = ( "[" + (newline if newline != " " else "") + f",{newline}".join( str(_pyobject_to_string(item)) for item in value ) ) if newline != " ": result += "," result += delimiter + "]" return result return cast(str, value)
[docs]def dumps(obj: Any) -> str: """Converts a namespace to a Python script. Args: obj (dict): Namespace to convert. Returns: str: Python script. """ result = "" for key, value in obj.items(): result += f"{key} = {_pyobject_to_string(value)}\n" return result