From 20b64133bad137b72b23738951b0ee7b2c04c92f Mon Sep 17 00:00:00 2001 From: ChrsBaur Date: Mon, 4 May 2026 01:18:41 +0200 Subject: [PATCH 1/2] feat: add logging example to main.py (closes #39) Replaces the bare print() stub with a logging-aware example: - With a config file: loads config and calls util.logging_setup() - Without config: falls back to logging.basicConfig(INFO) Both paths emit a logger.info message so the generated project demonstrates end-to-end logging from the very first run. --- CHANGELOG.md | 6 ++++-- pyproject.toml | 2 +- .../src/{{cookiecutter.module_name}}/main.py | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b157fba..232daf8 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.3.1] - 2026-05-04 + +### Changed +- `main.py` now includes a working logging example (closes #39). When a config file is selected, it calls `util.load_config()` and `util.logging_setup(config)` so logging is initialised from the config on startup. When no config is selected, it falls back to `logging.basicConfig(level=logging.INFO)`. Both variants log an info message so the generated project works end-to-end immediately. ## [1.3.0] - 2026-01-27 diff --git a/pyproject.toml b/pyproject.toml index cdfc728..78b9671 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "at-python-template" -version = "1.3.0" +version = "1.3.1" description = "This is the official Python Project Template of Alexander Thamm GmbH (AT)" authors = [ "Christian Baur ", diff --git a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/main.py b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/main.py index 021ce3e..9fb23c5 100644 --- a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/main.py +++ b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/main.py @@ -1,6 +1,19 @@ +import logging +{% if cookiecutter.config_file != 'none' %} +from {{ cookiecutter.module_name }} import util +{% endif %} +logger = logging.getLogger('{{ cookiecutter.module_name }}') + + def main(): + {% if cookiecutter.config_file != 'none' -%} + config = util.load_config({% if cookiecutter.config_file == 'yaml' %}'config/config.yml'{% else %}None{% endif %}) + util.logging_setup(config) + {% else -%} + logging.basicConfig(level=logging.INFO) + {% endif -%} + logger.info("Looks like you're all set up. Let's get going!") # TODO your journey starts here - print("hello :)") if __name__ == "__main__": From fa943ae3082e10d7be9f14e6ebe25b226126dbb2 Mon Sep 17 00:00:00 2001 From: ChrsBaur Date: Mon, 4 May 2026 01:23:56 +0200 Subject: [PATCH 2/2] feat: centralise logging setup so main.py is uniform across all config variants (closes #39) All three config options (yaml, hocon, none) now expose setup_logging() so main.py can call util.setup_logging() unconditionally. Added util__none.py for the no-config case (renamed to util.py by the post-gen hook); updated handle_config() to delete the unused util__* variants; updated tests. --- CHANGELOG.md | 2 +- hooks/post_gen_project.py | 9 +++++++-- tests/test_options.py | 4 ++-- .../src/{{cookiecutter.module_name}}/main.py | 10 ++-------- .../src/{{cookiecutter.module_name}}/util__hocon.py | 5 +++++ .../src/{{cookiecutter.module_name}}/util__none.py | 10 ++++++++++ .../src/{{cookiecutter.module_name}}/util__yaml.py | 5 +++++ 7 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 {{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__none.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 232daf8..fe24277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.3.1] - 2026-05-04 ### Changed -- `main.py` now includes a working logging example (closes #39). When a config file is selected, it calls `util.load_config()` and `util.logging_setup(config)` so logging is initialised from the config on startup. When no config is selected, it falls back to `logging.basicConfig(level=logging.INFO)`. Both variants log an info message so the generated project works end-to-end immediately. +- Centralised logging setup (closes #39): all config variants now expose a single `util.setup_logging()` function so `main.py` is uniform regardless of the chosen config format. `util__yaml.py` and `util__hocon.py` gain a `setup_logging()` wrapper; a new `util__none.py` provides a standalone `setup_logging()` for the no-config case (renamed to `util.py` by the post-generate hook). ## [1.3.0] - 2026-01-27 diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 39f50af..20760ba 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -76,6 +76,10 @@ 'config/prod.conf', ] +files_config_none = [ + f'{module_dir}/util__none.py', +] + files_ci_gitlab = { ".gitlab-ci.yml", } @@ -141,16 +145,17 @@ def handle_cli(): def handle_config(): config_file = '{{ cookiecutter.config_file }}' if config_file == 'yaml': - _delete_files(files_config_hocon) + _delete_files(files_config_hocon + files_config_none) shutil.rmtree(f'{module_dir}/res') _rename_files(f'src/**/*__yaml.py', '__yaml', '') elif config_file == 'hocon': - _delete_files(files_config_yaml) + _delete_files(files_config_yaml + files_config_none) _rename_files(f'src/**/*__hocon.py', '__hocon', '') else: _delete_files(files_config_hocon + files_config_yaml + ['tests/test_util.py']) os.rmdir(f'{module_dir}/res') os.rmdir('config') + _rename_files(f'src/**/*__none.py', '__none', '') def handle_formatter(): diff --git a/tests/test_options.py b/tests/test_options.py index 6f98d83..d2982b3 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -99,8 +99,8 @@ def test_config_yaml(): def test_config_none(): check_project( settings={'config_file': 'none'}, - files_non_existent=['config', 'src/{module_name}/util.py', 'tests/util.py', - 'src/{module_name}/res']) + files_existent=['src/{module_name}/util.py'], + files_non_existent=['config', 'tests/util.py', 'src/{module_name}/res']) def test_formatter_black_pip(): diff --git a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/main.py b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/main.py index 9fb23c5..0e0616c 100644 --- a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/main.py +++ b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/main.py @@ -1,17 +1,11 @@ import logging -{% if cookiecutter.config_file != 'none' %} from {{ cookiecutter.module_name }} import util -{% endif %} + logger = logging.getLogger('{{ cookiecutter.module_name }}') def main(): - {% if cookiecutter.config_file != 'none' -%} - config = util.load_config({% if cookiecutter.config_file == 'yaml' %}'config/config.yml'{% else %}None{% endif %}) - util.logging_setup(config) - {% else -%} - logging.basicConfig(level=logging.INFO) - {% endif -%} + util.setup_logging() logger.info("Looks like you're all set up. Let's get going!") # TODO your journey starts here diff --git a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__hocon.py b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__hocon.py index bfed08f..86d8fba 100644 --- a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__hocon.py +++ b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__hocon.py @@ -54,3 +54,8 @@ def logging_setup(config: ConfigTree): level = logging.NOTSET logging.basicConfig(format=fmt, level=logging.WARNING) logger.setLevel(level) + + +def setup_logging(config_path: Union[str, Path] = None): + """Load config and configure logging in one call.""" + logging_setup(load_config(config_path)) diff --git a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__none.py b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__none.py new file mode 100644 index 0000000..add9af3 --- /dev/null +++ b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__none.py @@ -0,0 +1,10 @@ +import logging + + +def setup_logging(): + """Configure basic logging for the application.""" + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s.%(msecs)03d [%(levelname)s]: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) 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..05b3992 100644 --- a/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__yaml.py +++ b/{{cookiecutter.project_slug}}/src/{{cookiecutter.module_name}}/util__yaml.py @@ -48,3 +48,8 @@ def logging_setup(config: Dict): level = logging.NOTSET logging.basicConfig(format=fmt, level=logging.WARNING) logger.setLevel(level) + + +def setup_logging(config_path: Union[str, Path] = 'config/config.yml'): + """Load config and configure logging in one call.""" + logging_setup(load_config(config_path))