Source code for calendaring_jmap._config
# SPDX-FileCopyrightText: 2026 calendaring-jmap contributors
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Standalone configuration loading for calendaring-jmap.
Resolves connection parameters in priority order:
1. Explicit keyword arguments
2. Environment variables (``JMAP_URL``, ``JMAP_USERNAME``, ``JMAP_PASSWORD``,
``JMAP_AUTH_TYPE``, ``JMAP_TIMEOUT``)
3. A YAML config file (``JMAP_CONFIG_FILE`` env var, or
``~/.config/calendaring-jmap/calendar.yaml``)
Returns ``None`` if none of the three sources yields a URL, rather than
raising. Callers decide whether "no configuration" is an error.
"""
from __future__ import annotations
import os
from pathlib import Path
from typing import Any
_CONN_KEYS = {"url", "username", "password", "auth", "auth_type", "timeout"}
_DEFAULT_CONFIG_PATH = Path.home() / ".config" / "calendaring-jmap" / "calendar.yaml"
_ENV_VARS = {
"url": "JMAP_URL",
"username": "JMAP_USERNAME",
"password": "JMAP_PASSWORD",
"auth_type": "JMAP_AUTH_TYPE",
"timeout": "JMAP_TIMEOUT",
}
def _from_environment() -> dict[str, Any]:
env: dict[str, Any] = {}
for key, var in _ENV_VARS.items():
value = os.environ.get(var)
if value:
env[key] = int(value) if key == "timeout" else value
return env
def _from_config_file(config_file: str | Path | None) -> dict[str, Any]:
path = Path(config_file) if config_file else _DEFAULT_CONFIG_PATH
if not path.is_file():
return {}
import yaml
with path.open("rb") as f:
data = yaml.safe_load(f) or {}
if not isinstance(data, dict):
return {}
return {k: v for k, v in data.items() if k in _CONN_KEYS}
[docs]
def get_connection_params(
config_file: str | Path | None = None,
**explicit_params: Any,
) -> dict[str, Any] | None:
"""Resolve JMAP connection parameters from kwargs, env vars, or a config file.
Args:
config_file: Explicit path to a YAML config file. Defaults to the
``JMAP_CONFIG_FILE`` environment variable, then
``~/.config/calendaring-jmap/calendar.yaml``.
**explicit_params: ``url``, ``username``, ``password``, ``auth``,
``auth_type``, ``timeout``. A value of ``None`` is treated as "not supplied",
not "unset": this lets a thin CLI wrapper pass every option
through unconditionally without wiping out env vars for the
ones the user left off.
Returns:
A dict of connection parameters, or ``None`` if no source yields a URL.
"""
explicit = {k: v for k, v in explicit_params.items() if k in _CONN_KEYS and v is not None}
if explicit.get("url"):
return explicit
env = _from_environment()
if env.get("url"):
return {**env, **explicit}
file_config = _from_config_file(config_file or os.environ.get("JMAP_CONFIG_FILE"))
if file_config.get("url"):
return {**file_config, **env, **explicit}
return None