Skip to content

IO

Module functions for working with configuration files.

write_config

write_config(args)

Write a configuration file to YAML.

Parameters:

Name Type Description Default
args Namespace | dict[str, Any]

A Namespace object parsed from argparse. If there are values for output_dir and filename these will be used to construct the path and filename to write the YAML file to. If not then output files are contingent on how the function is being called. If it is from layopt create_config then default_config.yaml will be written. If it is at the end of processing then config_YY-MM-DD-hhmmss.yaml will be used.

required
Source code in src/layopt/io.py
def write_config(args: Namespace | dict[str, Any] | Parameters | None) -> None:
    """
    Write a configuration file to YAML.

    Parameters
    ----------
    args : Namespace | dict[str, Any], optional
        A Namespace object parsed from argparse. If there are values for ``output_dir`` and ``filename`` these will be
        used to construct the path and filename to write the YAML file to.  If not then output files are contingent on
        how the function is being called. If it is from ``layopt create_config`` then ``default_config.yaml`` will be
        written. If it is at the end of processing then ``config_YY-MM-DD-hhmmss.yaml`` will be used.
    """
    # If args is `Namespace` then we are writing config with 'layopt create_config' subcommand
    if isinstance(args, Namespace):
        output_dir = Path("./") if args.output_dir is None else Path(args.output_dir)
        config = vars(Parameters())
        filename = "default_config.yaml" if args.filename is None else args.filename
    # Otherwise we are writing after 'layopt optimise' and config is a dictionary, this won't have a 'filename'
    # key/value pair
    # ns-rse 2026-05-11 - This will be obsolete once we fully switch to the Parameters dataclass for holding configuration
    elif isinstance(args, dict):
        output_dir = (
            Path("./") if args["output_dir"] is None else Path(args["output_dir"])
        )
        filename = f"config_{get_date_time(strftime='%Y-%m-%d-%H%M%S')}.yaml"
        config = args
    # If we have 'Parameters' then configuration is stored as a dataclass and we again don't have 'filename' key/value pair
    elif isinstance(args, Parameters):
        output_dir = Path("./") if args.output_dir is None else Path(args.output_dir)  # type: ignore[redundant-expr]
        filename = f"config_{get_date_time(strftime='%Y-%m-%d-%H%M%S')}.yaml"
        config = RootModel[Parameters](args).model_dump()
    else:
        msg = f"args is neither 'Namespace', 'dict' or 'Parameters' : {type(args)}"
        raise TypeError(msg)
    if ".yaml" not in str(filename) and ".yml" not in str(filename):
        config_path = output_dir / f"{filename}.yaml"
    else:
        config_path = output_dir / filename
    logger_msg = "A sample configuration has been written to"
    with config_path.open("w", encoding="utf-8") as f:
        try:
            f.write(f"# Config generated {get_date_time()}\n")
            f.write(f"{CONFIG_DOCUMENTATION_REFERENCE}")
            yaml_out = YAML()
            yaml_out.indent(sequence=4, offset=2)
            yaml_out.dump(dict_to_yaml(config), f)
            logger.info(f"{logger_msg} : {config_path!s}")
        except:  # noqa: E722, pylint: disable=W0702
            logger.error(f"Failed to write config to : {config_path}")

dict_to_yaml

dict_to_yaml(obj)

Clean a dictionary to basic data types for writing to YAML.

Parameters:

Name Type Description Default
obj Any

An object for converting to basic data types.

required

Returns:

Type Description
Any

obj as str, int, float, bool, list or dict.

Source code in src/layopt/io.py
def dict_to_yaml(obj: Any) -> Any:
    """
    Clean a dictionary to basic data types for writing to YAML.

    Parameters
    ----------
    obj : Any
        An object for converting to basic data types.

    Returns
    -------
    Any
        ``obj`` as ``str``, ``int``, ``float``, ``bool``, ``list`` or ``dict``.
    """
    # Recurse on dictionaries
    if isinstance(obj, dict):
        new = {}
        for k, v in obj.items():
            key = k if isinstance(k, str) else str(k)
            new[key] = dict_to_yaml(v)
        return new
    # Recurse on lists and tuples
    if isinstance(obj, (list, tuple)):
        return [dict_to_yaml(x) for x in obj]
    # Safe types return as is
    if isinstance(obj, (str, int, float, bool)) or obj is None:
        return obj
    # Convert Path -> string
    if isinstance(obj, Path):
        return str(obj)
    # Convert numpy array -> nested lists
    if isinstance(obj, np.ndarray):
        return obj.tolist()
    # Convert numpy scalar -> native Python scalar
    if isinstance(obj, np.generic):
        return obj.item()
    # If nothing else is matched use string representation
    return str(obj)

read_yaml

read_yaml(filename)

Read a YAML file.

Parameters:

Name Type Description Default
filename Union[str, Path]

YAML file to read.

required

Returns:

Type Description
Any

Dictionary of the file.

Source code in src/layopt/io.py
def read_yaml(filename: str | Path) -> Any:
    """
    Read a YAML file.

    Parameters
    ----------
    filename : Union[str, Path]
        YAML file to read.

    Returns
    -------
    Any
        Dictionary of the file.
    """
    with Path(filename).open(encoding="utf-8") as f:
        yaml = YAML(typ="safe")
        return yaml.load(f)

convert_path

convert_path(path)

Ensure path is Path object.

Parameters:

Name Type Description Default
path str | Path

Path to be converted.

required

Returns:

Type Description
Path

Pathlib object of path.

Source code in src/layopt/io.py
def convert_path(path: str | Path) -> Path:
    """
    Ensure path is Path object.

    Parameters
    ----------
    path : str | Path
        Path to be converted.

    Returns
    -------
    Path
        Pathlib object of path.
    """
    return Path().cwd() if path == "./" else Path(path).expanduser()

get_date_time

get_date_time(strftime='%Y-%m-%d %H:%M:%S')

Get the current date-time as a string for the systems current timezone.

Parameters:

Name Type Description Default
strftime str

String for formatting date-time, default is %Y-%m-%d %H:%M:%S.

'%Y-%m-%d %H:%M:%S'

Returns:

Type Description
str

Date-time as a string for systems current timezone.

Source code in src/layopt/io.py
def get_date_time(strftime: str = "%Y-%m-%d %H:%M:%S") -> str:
    """
    Get the current date-time as a string for the systems current timezone.

    Parameters
    ----------
    strftime : str
        String for formatting date-time, default is ``%Y-%m-%d %H:%M:%S``.

    Returns
    -------
    str
        Date-time as a string for systems current timezone.
    """
    return datetime.now(tz=datetime.now().astimezone().tzinfo).strftime(strftime)

dict_to_df

dict_to_df(results)

Convert a dictionary of LayOpt results to Pandas DataFrame.

Typically a set of results is a dictionary with no nesting and the resulting data frame has a single row.

Parameters:

Name Type Description Default
results dict[str, Any]

Dictionary to convert to Pandas DataFrame.

required

Returns:

Type Description
DataFrame

Data as a Pandas dictionary.

Source code in src/layopt/io.py
def dict_to_df(results: dict[float, dict[str, Any]]) -> Any:
    """
    Convert a dictionary of LayOpt results to Pandas DataFrame.

    Typically a set of results is a dictionary with no nesting and the resulting data frame has a single row.

    Parameters
    ----------
    results : dict[str, Any]
        Dictionary to convert to Pandas DataFrame.

    Returns
    -------
    pd.DataFrame
        Data as a Pandas dictionary.
    """
    return pd.DataFrame.from_dict(results, orient="index").T