standard_config.py

Standard Config. A module to load tooling config from a users project space.

Inspired from frustrations that some tools have a tool.ini, .tool.ini, setup.cfg, or pyproject.toml. Some allow for global configs, some don't. Some properly follow the users home directory, others end up in a weird temp directory. Windows home directory is only more confusing. Some will even respect the users $XDG_HOME directory.

This file is for any project that can be configured in plain text such as ini or toml and not requiring a .py file. Just name your tool and let users put config where it makes sense to them, no need to figure out resolution order.

Usage:

from standard_config import load

# Retrieve any overrides from the user
overrides = {'setting': True}
config = load('my_tool', overrides)

Resolution Order

Tool Specific Ini files

Ini file formats must include a <tool> key.

[my_tool]
setting = True

pyproject.toml

Toml files must include a tool.<tool> key

[tool.my_tool]
setting = True

setup.cfg

setup.cfg files must include a tool:<tool> key

[tool:my_tool]
setting = True

global files to consider

local files to consider

_get_global_path_specs function

Generate a list of standard pathspecs for global config files.

Args: tool (str): name of the tool to configure

_get_global_path_specs source
def _get_global_path_specs(tool: str) -> path_spec_type:
    """
    Generate a list of standard pathspecs for global config files.

    Args:
        tool (str): name of the tool to configure
    """
    try:
        home = Path(os.environ["XDG_HOME"])
    except KeyError:
        home = Path.home()

    return [
        {"path_specs": home / f"{tool}.ini", "ac_parser": "ini", "keys": [tool]},
        {"path_specs": home / f".{tool}", "ac_parser": "ini", "keys": [tool]},
        {"path_specs": home / f".{tool}.ini", "ac_parser": "ini", "keys": [tool]},
        {
            "path_specs": home / ".config" / f"{tool}.ini",
            "ac_parser": "ini",
            "keys": [tool],
        },
        {
            "path_specs": home / ".config" / f".{tool}",
            "ac_parser": "ini",
            "keys": [tool],
        },
        {
            "path_specs": home / ".config" / f".{tool}.ini",
            "ac_parser": "ini",
            "keys": [tool],
        },
    ]

_get_local_path_specs function

Generate a list of standard pathspecs for local, project directory config files.

Args: tool (str): name of the tool to configure

_get_local_path_specs source
def _get_local_path_specs(tool: str, project_home: Union[str, Path]) -> path_spec_type:
    """
    Generate a list of standard pathspecs for local, project directory config files.

    Args:
        tool (str): name of the tool to configure
    """
    return [
        {
            "path_specs": Path(project_home) / f"{tool}.ini",
            "ac_parser": "ini",
            "keys": [tool],
        },
        {
            "path_specs": Path(project_home) / f".{tool}",
            "ac_parser": "ini",
            "keys": [tool],
        },
        {
            "path_specs": Path(project_home) / f".{tool}.ini",
            "ac_parser": "ini",
            "keys": [tool],
        },
        {
            "path_specs": Path(project_home) / f"{tool}.yml",
            "ac_parser": "yaml",
            "keys": [tool],
        },
        {
            "path_specs": Path(project_home) / f".{tool}.yml",
            "ac_parser": "yaml",
            "keys": [tool],
        },
        {
            "path_specs": Path(project_home) / f"{tool}.toml",
            "ac_parser": "toml",
            "keys": [tool],
        },
        {
            "path_specs": Path(project_home) / f".{tool}.toml",
            "ac_parser": "toml",
            "keys": [tool],
        },
        {
            "path_specs": Path(project_home) / "pyproject.toml",
            "ac_parser": "toml",
            "keys": ["tool", tool],
        },
        {
            "path_specs": Path(project_home) / "setup.cfg",
            "ac_parser": "ini",
            "keys": [f"tool.{tool}"],
        },
    ]

_get_attrs function

Get nested config data from a list of keys.

specifically written for pyproject.toml which needs to get tool then <tool>

_get_attrs source
def _get_attrs(attrs: list, config: Dict) -> Dict:
    """Get nested config data from a list of keys.

    specifically written for pyproject.toml which needs to get `tool` then `<tool>`
    """
    for attr in attrs:
        config = config[attr]
    return config

_load_files function

Use anyconfig to load config files stopping at the first one that exists.

config_path_specs (list): a list of pathspecs and keys to load

_load_files source
def _load_files(config_path_specs: path_spec_type) -> Dict:
    """Use anyconfig to load config files stopping at the first one that exists.

    config_path_specs (list): a list of pathspecs and keys to load
    """
    for file in config_path_specs:
        if file["path_specs"].exists():
            config = anyconfig.load(**file)
        else:
            # ignore missing files
            continue

        try:
            return _get_attrs(file["keys"], config)
        except KeyError:
            # ignore incorrect keys
            continue

    return {}

_load_env function

Load config from environment variables.

Args: tool (str): name of the tool to configure

_load_env source
def _load_env(tool: str) -> Dict:
    """Load config from environment variables.

    Args:
        tool (str): name of the tool to configure
    """
    vars = [var for var in os.environ.keys() if var.startswith(tool.upper())]
    return {
        var.lower().strip(tool.lower()).strip("_").strip("-"): os.environ[var]
        for var in vars
    }

load function

Load tool config from standard config files.

Resolution Order

Args: tool (str): name of the tool to configure

load source
def load(tool: str, project_home: Union[Path, str] = ".", overrides: Dict = {}) -> Dict:
    """Load tool config from standard config files.

    Resolution Order

    * First global file with a tool key
    * First local file with a tool key
    * Environment variables prefixed with `TOOL`
    * Overrides

    Args:
        tool (str): name of the tool to configure
    """
    global_config = _load_files(_get_global_path_specs(tool))
    local_config = _load_files(_get_local_path_specs(tool, project_home))
    env_config = _load_env(tool)
    return {**global_config, **local_config, **env_config, **overrides}