From fd7cf15d464736258563f4f937aa4822a2c77191 Mon Sep 17 00:00:00 2001 From: ChrsBaur Date: Mon, 4 May 2026 01:09:42 +0200 Subject: [PATCH 1/2] feat: replace PyYAML with OmegaConf for yaml config option (closes #98) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OmegaConf provides variable interpolation, config merging and runtime type safety on top of standard YAML — the same features that made HOCON attractive. The load_config() return type changes from Dict to DictConfig, which supports both dict-style and dot-notation access. Adds omegaconf to the template repo's own test environment so the generated-project tests can import it. --- poetry.lock | 29 ++++++++++++++++++- pyproject.toml | 1 + {{cookiecutter.project_slug}}/environment.yml | 2 +- {{cookiecutter.project_slug}}/pyproject.toml | 4 +-- .../requirements.txt | 4 +-- .../util__yaml.py | 28 +++++++++--------- 6 files changed, 49 insertions(+), 19 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7defd14..693aa48 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,16 @@ # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +[[package]] +name = "antlr4-python3-runtime" +version = "4.9.3" +description = "ANTLR 4.9.3 runtime for Python 3.7" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, +] + [[package]] name = "arrow" version = "1.4.0" @@ -602,6 +613,22 @@ files = [ {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"}, ] +[[package]] +name = "omegaconf" +version = "2.3.0" +description = "A flexible configuration library" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b"}, + {file = "omegaconf-2.3.0.tar.gz", hash = "sha256:d5d4b6d29955cc50ad50c46dc269bcd92c6e00f5f90d23ab5fee7bfca4ba4cc7"}, +] + +[package.dependencies] +antlr4-python3-runtime = "==4.9.*" +PyYAML = ">=5.1.0" + [[package]] name = "packaging" version = "26.0" @@ -1151,4 +1178,4 @@ test = ["pytest (>=6.0.0)", "setuptools (>=77)"] [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "eb120efe5c973068ad0dfac90f2f9fa70bd9eb3d9fc92428081ed3964608bd32" +content-hash = "bc819fb73d23c0aa974420ac72646cb931ed653f14867f2cde183cfaae93e860" diff --git a/pyproject.toml b/pyproject.toml index cdfc728..78ad52f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ pre-commit = "^4.5.1" pytest-mock = "^3.15.1" pyhocon = "^0.3.61" pyyaml = "^6.0.3" +omegaconf = "^2.3.0" typer = "^0.21.1" setuptools = "^80.10.2" requests = ">=2.32.5" diff --git a/{{cookiecutter.project_slug}}/environment.yml b/{{cookiecutter.project_slug}}/environment.yml index 62d6468..3e9b5f5 100644 --- a/{{cookiecutter.project_slug}}/environment.yml +++ b/{{cookiecutter.project_slug}}/environment.yml @@ -9,7 +9,7 @@ channels: dependencies: - python>=3.10{% if cookiecutter.config_file == 'hocon' %} - pyhocon>=0.3.61{% elif cookiecutter.config_file == 'yaml' %} - - PyYAML>=6.0.3{% endif %} + - omegaconf>=2.3.0{% endif %} - pip {% if cookiecutter.create_cli == 'yes' %} - pip: - typer==0.21.1{% endif %} diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index d9255c1..8e55eb1 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -13,7 +13,7 @@ python = "^3.10" {%- if cookiecutter.config_file == 'hocon' %} pyhocon = "^0.3.61" {%- elif cookiecutter.config_file == 'yaml' %} -PyYAML = "^6.0.3" +omegaconf = "^2.3.0" {%- endif %} {%- if cookiecutter.create_cli == 'yes' %} typer = "^0.21.1" @@ -74,7 +74,7 @@ dependencies = [ {%- if cookiecutter.config_file == 'hocon' %} "pyhocon>=0.3.59", {%- elif cookiecutter.config_file == 'yaml' %} -"PyYAML>=6.0", +"omegaconf>=2.3.0", {%- endif %} {%- if cookiecutter.create_cli == 'yes' %} "typer>=0.16.1", diff --git a/{{cookiecutter.project_slug}}/requirements.txt b/{{cookiecutter.project_slug}}/requirements.txt index a53249e..cc234bc 100644 --- a/{{cookiecutter.project_slug}}/requirements.txt +++ b/{{cookiecutter.project_slug}}/requirements.txt @@ -7,8 +7,8 @@ # HOCON configuration parser pyhocon~=0.3.61 {% elif cookiecutter.config_file == 'yaml' %} -# YAML configuration parser -PyYAML~=6.0.2 +# YAML configuration parser (with interpolation and merging support) +omegaconf~=2.3.0 {% endif %} # Dependency for Command-Line Interface (CLI) if enabled diff --git a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__yaml.py b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__yaml.py index c5a62ff..5dbd349 100644 --- a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__yaml.py +++ b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__yaml.py @@ -1,9 +1,9 @@ import logging from pathlib import Path -from typing import Any, Dict, Union +from typing import Union import importlib.resources as resources -import yaml +from omegaconf import DictConfig, OmegaConf logger = logging.getLogger('{{ cookiecutter.module_name }}') @@ -23,27 +23,29 @@ def get_resource_string(path: str, decode=True) -> Union[str, bytes]: return s.decode(errors='ignore') if decode else s -def load_config(config_file: Union[str, Path]) -> Dict[str, Any]: +def load_config(config_file: Union[str, Path]) -> DictConfig: """ - Load the config from the specified yaml file + Load the config from the specified YAML file. + + Uses OmegaConf, which supports variable interpolation (${key}) and config + merging on top of standard YAML. See https://omegaconf.readthedocs.io/ :param config_file: path of the config file to load - :return: the parsed config as dictionary + :return: the parsed config as a DictConfig (supports both dict and dot-notation access) """ - with open(config_file) as fp: - return yaml.safe_load(fp) + return OmegaConf.load(config_file) -def logging_setup(config: Dict): +def logging_setup(config: DictConfig): """ - setup logging based on the configuration + Setup logging based on the configuration. :param config: the parsed config tree """ - log_conf = config['logging'] - fmt = log_conf['format'] - if log_conf['enabled']: - level = logging._nameToLevel[log_conf['level'].upper()] + log_conf = config.logging + fmt = log_conf.format + if log_conf.enabled: + level = logging._nameToLevel[log_conf.level.upper()] else: level = logging.NOTSET logging.basicConfig(format=fmt, level=logging.WARNING) From 53e769bcdb24415c312fa36174e93566e2be284d Mon Sep 17 00:00:00 2001 From: ChrsBaur Date: Mon, 4 May 2026 01:14:54 +0200 Subject: [PATCH 2/2] chore: bump version to 1.4.0 and update CHANGELOG for #98 --- CHANGELOG.md | 6 ++++-- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b157fba..97d66eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Added -- Placeholder for future updates and new features. +## [1.4.0] - 2026-05-04 + +### Changed +- Replaced `PyYAML` with `omegaconf~=2.3.0` for the `yaml` config option (closes #98). OmegaConf is a superset of PyYAML that adds variable interpolation (`${key}`), config merging, and optional runtime type safety via Structured Configs. The `load_config()` function now returns `DictConfig` instead of `Dict`, which supports both dict-style (`config['key']`) and dot-notation (`config.key`) access. ## [1.3.0] - 2026-01-27 diff --git a/pyproject.toml b/pyproject.toml index 78ad52f..309625b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "at-python-template" -version = "1.3.0" +version = "1.4.0" description = "This is the official Python Project Template of Alexander Thamm GmbH (AT)" authors = [ "Christian Baur ",