Skip to content

I/O

chanfig.io

JsonEncoder

Bases: JSONEncoder

JSON encoder for Config.

Source code in chanfig/io.py
Python
class JsonEncoder(JSONEncoder):
    r"""
    JSON encoder for Config.
    """

    def default(self, o: Any) -> Any:
        if hasattr(o, "__json__"):
            return o.__json__()
        if hasattr(o, "to_dict"):
            return o.to_dict()
        return super().default(o)

YamlDumper

Bases: SafeDumper

YAML Dumper for Config.

Source code in chanfig/io.py
Python
class YamlDumper(SafeDumper):  # pylint: disable=R0903
    r"""
    YAML Dumper for Config.
    """

    def increase_indent(self, flow: bool = False, indentless: bool = False):  # pylint: disable=W0235
        return super().increase_indent(flow, indentless)

YamlLoader

Bases: SafeLoader

YAML Loader for Config.

Source code in chanfig/io.py
Python
class YamlLoader(SafeLoader):
    r"""
    YAML Loader for Config.
    """

    def __init__(self, stream):
        super().__init__(stream)
        self._root = os.path.abspath(os.path.dirname(stream.name)) if hasattr(stream, "name") else os.getcwd()
        self.add_constructor("!include", self._include)
        self.add_constructor("!includes", self._includes)
        self.add_constructor("!env", self._env)

    @staticmethod
    def _include(loader: YamlLoader, node):
        relative_path = loader.construct_scalar(node)
        include_path = os.path.join(loader._root, relative_path)

        if not os.path.exists(include_path):
            raise FileNotFoundError(f"Included file not found: {include_path}")

        return load(include_path)

    @staticmethod
    def _includes(loader: YamlLoader, node):
        if not isinstance(node, SequenceNode):
            raise ConstructorError(None, None, f"!includes tag expects a sequence, got {node.id}", node.start_mark)
        files = loader.construct_sequence(node)
        return [YamlLoader._include(loader, ScalarNode("tag:yaml.org,2002:str", file)) for file in files]

    @staticmethod
    def _env(loader: YamlLoader, node):
        env_var = loader.construct_scalar(node)
        value = os.getenv(env_var)
        if value is None:
            raise ValueError(f"Environment variable '{env_var}' not set.")
        return value

save

Python
save(
    obj,
    file: File,
    method: str = None,
    *args: Any,
    **kwargs: Any
) -> None

Save FlatDict to file.

Raises:

Type Description
ValueError

If save to IO and method is not specified.

TypeError

If save to unsupported extension.

Alias:

  • save

Examples:

Python Console Session
>>> obj = {"a": 1, "b": 2, "c": 3}
>>> save(obj, "test.yaml")
>>> save(obj, "test.json")
>>> save(obj, "test.conf")
Traceback (most recent call last):
TypeError: `file='test.conf'` should be in ('json',), ('yml', 'yaml') or ('toml',), but got conf.
>>> with open("test.yaml", "w") as f:
...     save(obj, f)
Traceback (most recent call last):
ValueError: `method` must be specified when saving to IO.
Source code in chanfig/io.py
Python
def save(  # pylint: disable=W1113
    obj, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
) -> None:
    r"""
    Save `FlatDict` to file.

    Raises:
        ValueError: If save to `IO` and `method` is not specified.
        TypeError: If save to unsupported extension.

    **Alias**:

    + `save`

    Examples:
        >>> obj = {"a": 1, "b": 2, "c": 3}
        >>> save(obj, "test.yaml")
        >>> save(obj, "test.json")
        >>> save(obj, "test.conf")
        Traceback (most recent call last):
        TypeError: `file='test.conf'` should be in ('json',), ('yml', 'yaml') or ('toml',), but got conf.
        >>> with open("test.yaml", "w") as f:
        ...     save(obj, f)
        Traceback (most recent call last):
        ValueError: `method` must be specified when saving to IO.
    """
    # Import FlatDict here to avoid circular imports
    from .flat_dict import FlatDict, to_dict

    if isinstance(obj, FlatDict):
        obj.save(file, method, *args, **kwargs)
        return

    data = to_dict(obj)
    if method is None:
        if isinstance(file, IOBase):
            raise ValueError("`method` must be specified when saving to IO.")
        method = splitext(file)[-1][1:]
    extension = method.lower()
    if extension in YAML_EXTENSIONS:
        with FlatDict.open(file, mode="w") as fp:  # pylint: disable=C0103
            yaml_dump(data, fp, *args, **kwargs)
        return
    if extension in JSON_EXTENSIONS:
        with FlatDict.open(file, mode="w") as fp:  # pylint: disable=C0103
            fp.write(json_dumps(data, *args, **kwargs))
        return
    if extension in TOML_EXTENSIONS:
        with FlatDict.open(file, mode="w") as fp:  # pylint: disable=C0103
            fp.write(toml_dumps(data, *args, **kwargs))
        return
    raise TypeError(
        f"`file={file!r}` should be in {JSON_EXTENSIONS}, {YAML_EXTENSIONS} or {TOML_EXTENSIONS}, but got {extension}."
    )

toml_dumps

Python
toml_dumps(obj: Any, *args: Any, **kwargs: Any) -> str

Dump object to toml string when a writer is available.

Source code in chanfig/io.py
Python
def toml_dumps(obj: Any, *args: Any, **kwargs: Any) -> str:
    r"""
    Dump object to toml string when a writer is available.
    """

    try:
        import tomli_w
    except ImportError:
        try:
            import toml
        except ImportError as exc:
            raise TypeError("TOML dump requires 'tomli-w' or 'toml'.") from exc
        return toml.dumps(obj, *args, **kwargs)
    return tomli_w.dumps(obj, *args, **kwargs)

load

Python
load(
    file: PathStr, cls=None, *args: Any, **kwargs: Any
) -> Any

Load a file into a FlatDict.

This function simply calls cls.load, by default, cls is NestedDict.

Parameters:

Name Type Description Default

file

PathStr

The file to load.

required

cls

The class of the file to load. Defaults to NestedDict.

None

*args

Any

The arguments to pass to NestedDict.load.

()

**kwargs

Any

The keyword arguments to pass to NestedDict.load.

{}
See Also

load

Examples:

Python Console Session
1
2
3
4
5
6
7
8
>>> from chanfig import load
>>> config = load("tests/test.yaml")
>>> config
NestedDict(
  ('a'): 1
  ('b'): 2
  ('c'): 3
)
Source code in chanfig/io.py
Python
def load(file: PathStr, cls=None, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W1113
    r"""
    Load a file into a `FlatDict`.

    This function simply calls `cls.load`, by default, `cls` is `NestedDict`.

    Args:
        file: The file to load.
        cls: The class of the file to load. Defaults to `NestedDict`.
        *args: The arguments to pass to `NestedDict.load`.
        **kwargs: The keyword arguments to pass to `NestedDict.load`.

    See Also:
        [`load`][chanfig.FlatDict.load]

    Examples:
        >>> from chanfig import load
        >>> config = load("tests/test.yaml")
        >>> config
        NestedDict(
          ('a'): 1
          ('b'): 2
          ('c'): 3
        )
    """
    # Import here to avoid circular imports
    from .nested_dict import NestedDict

    if cls is None:
        cls = NestedDict

    return cls.load(file, *args, **kwargs)