From 89907b50db37ddaffb5a2e2e1426a3da9e17a93f Mon Sep 17 00:00:00 2001 From: Jannis Mittenzwei Date: Fri, 8 Aug 2025 12:09:00 +0200 Subject: [PATCH 1/3] Base Config --- exasol/toolbox/BaseConfig/__init__.py | 52 ++++++++++++++++++++++++ noxconfig.py | 8 +--- test/unit/BaseConfig/base_config_test.py | 40 ++++++++++++++++++ 3 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 exasol/toolbox/BaseConfig/__init__.py create mode 100644 test/unit/BaseConfig/base_config_test.py diff --git a/exasol/toolbox/BaseConfig/__init__.py b/exasol/toolbox/BaseConfig/__init__.py new file mode 100644 index 000000000..5d88df243 --- /dev/null +++ b/exasol/toolbox/BaseConfig/__init__.py @@ -0,0 +1,52 @@ +from collections.abc import Iterable +from pathlib import Path +from typing import ( + Annotated, + Optional, +) + +from pydantic import ( + BaseModel, + AfterValidator, + computed_field +) +from pydantic.dataclasses import dataclass + +from exasol.toolbox.util.version import Version +from dataclasses import dataclass + +def str_like_version_validation(versions: list[str]): + for version in versions: + Version.from_string(version) + return versions + + +class BaseConfig(BaseModel): + """ + Basic Configuration for Projects + + Attributes: + * python_versions (Iterable[str]): Iterable over all available python versions of the project [default: ("3.9", "3.10", "3.11", "3.12", "3.13")] + * min_py_version : Minimum of python_versions + * max_py_version: Maximum of python_versions + * exasol_versions: (Iterable[str]): Iterabble over all available exasol versions [default: ("7.1.9) + """ + python_versions: Annotated[ + list[str], AfterValidator(str_like_version_validation) + ] = ["3.9", "3.10", "3.11", "3.12", "3.13"] + exasol_versions: Annotated[ + list[str], AfterValidator(str_like_version_validation) + ] = ["7.1.9"] + model_config = { + "frozen": True + } + + @computed_field + @property + def min_py_version(self) -> str: + return str(min([Version.from_string(v) for v in self.python_versions])) + + @computed_field + @property + def max_py_version(self) -> str: + return str(max([Version.from_string(v) for v in self.python_versions])) \ No newline at end of file diff --git a/noxconfig.py b/noxconfig.py index 87023a0a7..6f0208afe 100644 --- a/noxconfig.py +++ b/noxconfig.py @@ -8,7 +8,7 @@ from exasol.toolbox.nox.plugin import hookimpl from exasol.toolbox.tools.replace_version import update_github_yml - +from exasol.toolbox.BaseConfig import BaseConfig class UpdateTemplates: TEMPLATE_PATH: Path = Path(__file__).parent / "exasol" / "toolbox" / "templates" @@ -37,10 +37,8 @@ def prepare_release_add_files(self, session, config): return self.template_workflows + self.actions -@dataclass(frozen=True) -class Config: +class Config(BaseConfig): """Project specific configuration used by nox infrastructure""" - root: Path = Path(__file__).parent doc: Path = Path(__file__).parent / "doc" source: Path = Path("exasol/toolbox") @@ -51,8 +49,6 @@ class Config: "project-template", "idioms", ) - python_versions: Iterable[str] = ("3.9", "3.10", "3.11", "3.12", "3.13") - exasol_versions: Iterable[str] = ("7.1.9",) plugins: Iterable[object] = (UpdateTemplates,) # need --keep-runtime-typing, as pydantic with python3.9 does not accept str | None # format, and it is not resolved with from __future__ import annotations. pyupgrade diff --git a/test/unit/BaseConfig/base_config_test.py b/test/unit/BaseConfig/base_config_test.py new file mode 100644 index 000000000..29c0c8e36 --- /dev/null +++ b/test/unit/BaseConfig/base_config_test.py @@ -0,0 +1,40 @@ +from exasol.toolbox.BaseConfig import BaseConfig, str_like_version_validation +import pytest +import pydantic + +@pytest.mark.parametrize( + "versions", + ( + ["$.2.3"], + ["1.f.1"], + ["1.1.1", "1.2.3", "2.3.4", "2.3.7", "1.1.1.1"] + ) +) +def test_str_like_version_validation(versions): + with pytest.raises(ValueError): + str_like_version_validation(versions) + + +def test_default(): + config = BaseConfig() + str_like_version_validation(config.python_versions) + str_like_version_validation(config.exasol_versions) + + +def test_new_value(): + conf = BaseConfig(python_versions=["1.21.1", "1.1.1"]) + assert conf.python_versions == ["1.21.1", "1.1.1"] + + +class BaseConfigExpansion(BaseConfig): + expansion1: str = "test1" + + +def test_expansion_validation(): + with pytest.raises(ValueError): + _ = BaseConfigExpansion(python_versions=["1.1.1", "1.1.F"]) + + +def test_min_max_py(): + conf = BaseConfig(python_versions=["1.1.1", "5.5.5", "9.9.9"]) + assert conf.min_py_version == "1.1.1" and conf.max_py_version == "9.9.9" From f45ca23ae98ddbaabb5a362d187a6ca1d2b5a544 Mon Sep 17 00:00:00 2001 From: Jannis Mittenzwei Date: Fri, 8 Aug 2025 13:43:53 +0200 Subject: [PATCH 2/3] Base Config --- exasol/toolbox/BaseConfig/__init__.py | 15 ++++++++------- noxconfig.py | 4 +++- .../{{cookiecutter.repo_name}}/noxconfig.py | 4 ++-- test/integration/project-template/conftest.py | 10 ++++++++-- test/unit/BaseConfig/base_config_test.py | 16 ++++++++-------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/exasol/toolbox/BaseConfig/__init__.py b/exasol/toolbox/BaseConfig/__init__.py index 5d88df243..bc3ddb14b 100644 --- a/exasol/toolbox/BaseConfig/__init__.py +++ b/exasol/toolbox/BaseConfig/__init__.py @@ -1,4 +1,5 @@ from collections.abc import Iterable +from dataclasses import dataclass from pathlib import Path from typing import ( Annotated, @@ -6,14 +7,15 @@ ) from pydantic import ( - BaseModel, AfterValidator, - computed_field + BaseModel, + computed_field, + ConfigDict ) from pydantic.dataclasses import dataclass from exasol.toolbox.util.version import Version -from dataclasses import dataclass + def str_like_version_validation(versions: list[str]): for version in versions: @@ -31,15 +33,14 @@ class BaseConfig(BaseModel): * max_py_version: Maximum of python_versions * exasol_versions: (Iterable[str]): Iterabble over all available exasol versions [default: ("7.1.9) """ + python_versions: Annotated[ list[str], AfterValidator(str_like_version_validation) ] = ["3.9", "3.10", "3.11", "3.12", "3.13"] exasol_versions: Annotated[ list[str], AfterValidator(str_like_version_validation) ] = ["7.1.9"] - model_config = { - "frozen": True - } + model_config = ConfigDict(frozen=True) @computed_field @property @@ -49,4 +50,4 @@ def min_py_version(self) -> str: @computed_field @property def max_py_version(self) -> str: - return str(max([Version.from_string(v) for v in self.python_versions])) \ No newline at end of file + return str(max([Version.from_string(v) for v in self.python_versions])) diff --git a/noxconfig.py b/noxconfig.py index 6f0208afe..494bfb7e7 100644 --- a/noxconfig.py +++ b/noxconfig.py @@ -6,9 +6,10 @@ from dataclasses import dataclass from pathlib import Path +from exasol.toolbox.BaseConfig import BaseConfig from exasol.toolbox.nox.plugin import hookimpl from exasol.toolbox.tools.replace_version import update_github_yml -from exasol.toolbox.BaseConfig import BaseConfig + class UpdateTemplates: TEMPLATE_PATH: Path = Path(__file__).parent / "exasol" / "toolbox" / "templates" @@ -39,6 +40,7 @@ def prepare_release_add_files(self, session, config): class Config(BaseConfig): """Project specific configuration used by nox infrastructure""" + root: Path = Path(__file__).parent doc: Path = Path(__file__).parent / "doc" source: Path = Path("exasol/toolbox") diff --git a/project-template/{{cookiecutter.repo_name}}/noxconfig.py b/project-template/{{cookiecutter.repo_name}}/noxconfig.py index c7e1f5198..f8a776644 100644 --- a/project-template/{{cookiecutter.repo_name}}/noxconfig.py +++ b/project-template/{{cookiecutter.repo_name}}/noxconfig.py @@ -3,10 +3,10 @@ from dataclasses import dataclass from pathlib import Path from typing import Iterable +from exasol.toolbox.BaseConfig import BaseConfig -@dataclass(frozen=True) -class Config: +class Config(BaseConfig): root: Path = Path(__file__).parent doc: Path = Path(__file__).parent / "doc" source: Path = Path("exasol/{{cookiecutter.package_name}}") diff --git a/test/integration/project-template/conftest.py b/test/integration/project-template/conftest.py index 9bea90ae5..02dcbd95c 100644 --- a/test/integration/project-template/conftest.py +++ b/test/integration/project-template/conftest.py @@ -11,20 +11,26 @@ def cwd(tmp_path_factory): return tmp_path_factory.mktemp("project_template_test") +@pytest.fixture(scope="session") +def config(): + return Config() + + @pytest.fixture(scope="session", autouse=True) -def new_project(cwd): +def new_project(cwd, config): project_name = "project" repo_name = "repo" package_name = "package" subprocess.run( - ["cookiecutter", Config.root / "project-template", "-o", cwd, "--no-input", + ["cookiecutter", config.root / "project-template", "-o", cwd, "--no-input", f"project_name={project_name}", f"repo_name={repo_name}", f"package_name={package_name}", ], capture_output=True, check=True) return cwd / repo_name + @pytest.fixture(scope="session", autouse=True) def poetry_install(run_command, poetry_path): run_command([poetry_path, "install"]) diff --git a/test/unit/BaseConfig/base_config_test.py b/test/unit/BaseConfig/base_config_test.py index 29c0c8e36..f75ae3052 100644 --- a/test/unit/BaseConfig/base_config_test.py +++ b/test/unit/BaseConfig/base_config_test.py @@ -1,14 +1,14 @@ -from exasol.toolbox.BaseConfig import BaseConfig, str_like_version_validation -import pytest import pydantic +import pytest + +from exasol.toolbox.BaseConfig import ( + BaseConfig, + str_like_version_validation, +) + @pytest.mark.parametrize( - "versions", - ( - ["$.2.3"], - ["1.f.1"], - ["1.1.1", "1.2.3", "2.3.4", "2.3.7", "1.1.1.1"] - ) + "versions", (["$.2.3"], ["1.f.1"], ["1.1.1", "1.2.3", "2.3.4", "2.3.7", "1.1.1.1"]) ) def test_str_like_version_validation(versions): with pytest.raises(ValueError): From 54cfb15de91c8562521fb524623bd0de75970774 Mon Sep 17 00:00:00 2001 From: Jannis Mittenzwei Date: Fri, 8 Aug 2025 14:24:04 +0200 Subject: [PATCH 3/3] fix --- exasol/toolbox/BaseConfig/__init__.py | 2 +- project-template/{{cookiecutter.repo_name}}/noxconfig.py | 3 +-- test/unit/documentation_test.py | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/exasol/toolbox/BaseConfig/__init__.py b/exasol/toolbox/BaseConfig/__init__.py index bc3ddb14b..201622176 100644 --- a/exasol/toolbox/BaseConfig/__init__.py +++ b/exasol/toolbox/BaseConfig/__init__.py @@ -9,8 +9,8 @@ from pydantic import ( AfterValidator, BaseModel, + ConfigDict, computed_field, - ConfigDict ) from pydantic.dataclasses import dataclass diff --git a/project-template/{{cookiecutter.repo_name}}/noxconfig.py b/project-template/{{cookiecutter.repo_name}}/noxconfig.py index f8a776644..3dcdae6e3 100644 --- a/project-template/{{cookiecutter.repo_name}}/noxconfig.py +++ b/project-template/{{cookiecutter.repo_name}}/noxconfig.py @@ -3,10 +3,9 @@ from dataclasses import dataclass from pathlib import Path from typing import Iterable -from exasol.toolbox.BaseConfig import BaseConfig -class Config(BaseConfig): +class Config(): root: Path = Path(__file__).parent doc: Path = Path(__file__).parent / "doc" source: Path = Path("exasol/{{cookiecutter.package_name}}") diff --git a/test/unit/documentation_test.py b/test/unit/documentation_test.py index 32ddff262..e63be68ce 100644 --- a/test/unit/documentation_test.py +++ b/test/unit/documentation_test.py @@ -11,7 +11,7 @@ _docs_links_check, _docs_list_links, ) -from noxconfig import Config +from noxconfig import PROJECT_CONFIG @pytest.fixture() @@ -44,7 +44,7 @@ def config(index, file, tmp_path): test_doc = tmp_path / "doc" test_doc.mkdir() (test_doc / "_static").mkdir() - shutil.copyfile(Config.doc / "conf.py", test_doc / "conf.py") + shutil.copyfile(PROJECT_CONFIG.doc / "conf.py", test_doc / "conf.py") rst_index = test_doc / "index.rst" rst_file1 = test_doc / "file.rst" rst_index.touch()