From 23103c35f82567b6785138f2b26be9d3dc004f14 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 1 Dec 2025 13:25:08 +0100 Subject: [PATCH 01/31] Move path-variables to BaseConfig --- exasol/toolbox/config.py | 29 +++++++++++++++++++ noxconfig.py | 4 ++- .../{{cookiecutter.repo_name}}/noxconfig.py | 14 +++------ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/exasol/toolbox/config.py b/exasol/toolbox/config.py index 22fdd80f5..f124739ce 100644 --- a/exasol/toolbox/config.py +++ b/exasol/toolbox/config.py @@ -1,3 +1,4 @@ +from pathlib import Path from typing import ( Annotated, ) @@ -46,6 +47,8 @@ class BaseConfig(BaseModel): runs. """ + project_name: str = Field(description="Name of the project") + root_path: Path = Field(description="Root directory of the project") python_versions: tuple[ValidVersionStr, ...] = Field( default=("3.10", "3.11", "3.12", "3.13", "3.14"), description="Python versions to use in running CI workflows", @@ -70,6 +73,14 @@ class BaseConfig(BaseModel): ) model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True) + @computed_field # type: ignore[misc] + @property + def documentation_path(self) -> Path: + """ + Path where the documentation for the project is stored. + """ + return self.root_path / "doc" + @computed_field # type: ignore[misc] @property def minimum_python_version(self) -> str: @@ -114,3 +125,21 @@ def pyupgrade_argument(self) -> tuple[str, ...]: version_parts = self.minimum_python_version.split(".")[:2] version_number = "".join(version_parts) return (f"--py{version_number}-plus",) + + @computed_field # type: ignore[misc] + @property + def source_code_path(self) -> Path: + """ + Path to the source code of the project. + """ + return self.root_path / "exasol" / self.project_name + + @computed_field # type: ignore[misc] + @property + def version_filepath(self) -> Path: + """ + Path to the ``version.py`` file included in the project. This is an + autogenerated file which contains the version of the code. It is maintained by + the nox sessions ``version:check`` and ``release:prepare``. + """ + return self.source_code_path / "version.py" diff --git a/noxconfig.py b/noxconfig.py index 3f7adc169..5cd30c49b 100644 --- a/noxconfig.py +++ b/noxconfig.py @@ -60,6 +60,8 @@ class Config(BaseConfig): PROJECT_CONFIG = Config( + root_path=Path(__file__).parent, + project_name="toolbox", add_to_excluded_python_paths=( # The cookiecutter placeholders do not work well with checks. # Instead, the format & linting are checked in the @@ -72,5 +74,5 @@ class Config(BaseConfig): create_major_version_tags=True, # The PTB does not have integration tests run with an Exasol DB, # so for running in the CI, we take the first element. - exasol_versions=(BaseConfig().exasol_versions[0],), + exasol_versions=("7.1.30",), ) diff --git a/project-template/{{cookiecutter.repo_name}}/noxconfig.py b/project-template/{{cookiecutter.repo_name}}/noxconfig.py index 301a6a5a0..ac8f907f0 100644 --- a/project-template/{{cookiecutter.repo_name}}/noxconfig.py +++ b/project-template/{{cookiecutter.repo_name}}/noxconfig.py @@ -7,15 +7,9 @@ class Config(BaseConfig): - root: Path = Path(__file__).parent - doc: Path = Path(__file__).parent / "doc" - source: Path = Path("exasol/{{cookiecutter.package_name}}") - version_file: Path = ( - Path(__file__).parent - / "exasol" - / "{{cookiecutter.package_name}}" - / "version.py" - ) plugins: Iterable[object] = () -PROJECT_CONFIG = Config() +PROJECT_CONFIG = Config( + project_name="{{cookiecutter.package_name}}", + root_path=Path(__file__).parent, +) From 0ed2830db357334d84a9af9d3cebdbd4e7f56f23 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 1 Dec 2025 13:28:59 +0100 Subject: [PATCH 02/31] Switch PROJECT_CONFIG.root with PROJECT_CONFIG.root_path --- doc/user_guide/migrating.rst | 2 +- exasol/toolbox/nox/_artifacts.py | 10 +++++----- exasol/toolbox/nox/_documentation.py | 10 +++++----- exasol/toolbox/nox/_format.py | 4 ++-- exasol/toolbox/nox/_lint.py | 10 +++++----- exasol/toolbox/nox/_metrics.py | 2 +- exasol/toolbox/nox/_package.py | 4 ++-- exasol/toolbox/nox/_release.py | 4 ++-- exasol/toolbox/nox/tasks.py | 2 +- exasol/toolbox/util/dependencies/shared_models.py | 2 +- test/integration/project-template/conftest.py | 7 ++++--- test/unit/nox/_package_check_test.py | 12 +++++++----- test/unit/util/dependencies/audit_test.py | 2 +- .../util/dependencies/poetry_dependencies_test.py | 2 +- 14 files changed, 38 insertions(+), 35 deletions(-) diff --git a/doc/user_guide/migrating.rst b/doc/user_guide/migrating.rst index 1b737b281..f0d26192e 100644 --- a/doc/user_guide/migrating.rst +++ b/doc/user_guide/migrating.rst @@ -74,7 +74,7 @@ For example, if test execution isn't performed in the standard way (e.g., :code: # within the function to keep them isolated and simplify future removal or replacement. from exasol.toolbox.nox._shared import get_filtered_python_files - py_files = get_filtered_python_files(PROJECT_CONFIG.root) + py_files = get_filtered_python_files(PROJECT_CONFIG.root_path) print("The original 'format:fix' task has been taken hostage by this overwrite") print("Files:\n{files}".format(files="\n".join(py_files)) diff --git a/exasol/toolbox/nox/_artifacts.py b/exasol/toolbox/nox/_artifacts.py index 6707483f9..7e7d34278 100644 --- a/exasol/toolbox/nox/_artifacts.py +++ b/exasol/toolbox/nox/_artifacts.py @@ -45,16 +45,16 @@ @nox.session(name="artifacts:validate", python=False) def check_artifacts(session: Session) -> None: """Validate that all project artifacts are available and consistent""" - all_files = {f.name for f in PROJECT_CONFIG.root.iterdir() if f.is_file()} + all_files = {f.name for f in PROJECT_CONFIG.root_path.iterdir() if f.is_file()} if missing_files := (ALL_LINT_FILES - all_files): print(f"files not available: {missing_files}", file=sys.stderr) sys.exit(1) all_is_valid_checks = [ - _is_valid_lint_txt(Path(PROJECT_CONFIG.root, LINT_TXT)), - _is_valid_lint_json(Path(PROJECT_CONFIG.root, LINT_JSON)), - _is_valid_security_json(Path(PROJECT_CONFIG.root, SECURITY_JSON)), - _is_valid_coverage(Path(PROJECT_CONFIG.root, COVERAGE_DB)), + _is_valid_lint_txt(Path(PROJECT_CONFIG.root_path, LINT_TXT)), + _is_valid_lint_json(Path(PROJECT_CONFIG.root_path, LINT_JSON)), + _is_valid_security_json(Path(PROJECT_CONFIG.root_path, SECURITY_JSON)), + _is_valid_coverage(Path(PROJECT_CONFIG.root_path, COVERAGE_DB)), ] if not all(all_is_valid_checks): sys.exit(1) diff --git a/exasol/toolbox/nox/_documentation.py b/exasol/toolbox/nox/_documentation.py index 822af4f1d..21b0e67c3 100644 --- a/exasol/toolbox/nox/_documentation.py +++ b/exasol/toolbox/nox/_documentation.py @@ -51,7 +51,7 @@ def _git_diff_changes_main() -> int: "--quiet", "origin/main", "--", - PROJECT_CONFIG.root / "doc/changes", + PROJECT_CONFIG.documentation_path / "changes", ], capture_output=True, ) @@ -73,7 +73,7 @@ def build_docs(session: Session) -> None: @nox.session(name="docs:open", python=False) def open_docs(session: Session) -> None: """Opens the built project documentation""" - docs_folder = PROJECT_CONFIG.root / DOCS_OUTPUT_DIR + docs_folder = PROJECT_CONFIG.root_path / DOCS_OUTPUT_DIR if not docs_folder.exists(): session.error(f"No documentation could be found. {docs_folder} is missing") index = docs_folder / "index.html" @@ -83,7 +83,7 @@ def open_docs(session: Session) -> None: @nox.session(name="docs:clean", python=False) def clean_docs(_session: Session) -> None: """Removes the documentations build folder""" - docs_folder = PROJECT_CONFIG.root / DOCS_OUTPUT_DIR + docs_folder = PROJECT_CONFIG.root_path / DOCS_OUTPUT_DIR if docs_folder.exists(): shutil.rmtree(docs_folder) @@ -146,7 +146,7 @@ def _docs_links_check(doc_config: Path, args): @nox.session(name="links:list", python=False) def docs_list_links(session: Session) -> None: """List all the links within the documentation.""" - r_code, text = _docs_list_links(PROJECT_CONFIG.doc) + r_code, text = _docs_list_links(PROJECT_CONFIG.documentation_path) print(text) if r_code != 0: session.error() @@ -164,7 +164,7 @@ def docs_links_check(session: Session) -> None: "-o", "--output", type=Path, help="path to copy the output json", default=None ) args = parser.parse_args(session.posargs) - r_code, problems = _docs_links_check(PROJECT_CONFIG.doc, args) + r_code, problems = _docs_links_check(PROJECT_CONFIG.documentation_path, args) if r_code >= 2: session.error(2) if r_code == 1 or problems != "": diff --git a/exasol/toolbox/nox/_format.py b/exasol/toolbox/nox/_format.py index 71ac4e477..0ab5f2e36 100644 --- a/exasol/toolbox/nox/_format.py +++ b/exasol/toolbox/nox/_format.py @@ -45,7 +45,7 @@ def command(*args: str) -> Iterable[str]: @nox.session(name="format:fix", python=False) def fix_format(session: Session) -> None: """Runs all automated format fixes on the code base""" - py_files = get_filtered_python_files(PROJECT_CONFIG.root) + py_files = get_filtered_python_files(PROJECT_CONFIG.root_path) _version(session, Mode.Fix) _pyupgrade(session, config=PROJECT_CONFIG, files=py_files) _ruff(session, mode=Mode.Fix, files=py_files) @@ -55,6 +55,6 @@ def fix_format(session: Session) -> None: @nox.session(name="format:check", python=False) def check_format(session: Session) -> None: """Checks the project for correct formatting""" - py_files = get_filtered_python_files(PROJECT_CONFIG.root) + py_files = get_filtered_python_files(PROJECT_CONFIG.root_path) _ruff(session, mode=Mode.Check, files=py_files) _code_format(session=session, mode=Mode.Check, files=py_files) diff --git a/exasol/toolbox/nox/_lint.py b/exasol/toolbox/nox/_lint.py index 6ae54eb5e..088523e07 100644 --- a/exasol/toolbox/nox/_lint.py +++ b/exasol/toolbox/nox/_lint.py @@ -16,8 +16,8 @@ def _pylint(session: Session, files: Iterable[str]) -> None: - json_file = PROJECT_CONFIG.root / ".lint.json" - txt_file = PROJECT_CONFIG.root / ".lint.txt" + json_file = PROJECT_CONFIG.root_path / ".lint.json" + txt_file = PROJECT_CONFIG.root_path / ".lint.txt" session.run( "pylint", @@ -50,7 +50,7 @@ def _security_lint(session: Session, files: Iterable[str]) -> None: "--format", "json", "--output", - PROJECT_CONFIG.root / ".security.json", + PROJECT_CONFIG.root_path / ".security.json", "--exit-zero", *files, ) @@ -130,7 +130,7 @@ def lint(session: Session) -> None: @nox.session(name="lint:typing", python=False) def type_check(session: Session) -> None: """Runs the type checker on the project""" - py_files = get_filtered_python_files(PROJECT_CONFIG.root) + py_files = get_filtered_python_files(PROJECT_CONFIG.root_path) _type_check(session=session, files=py_files) @@ -144,7 +144,7 @@ def security_lint(session: Session) -> None: @nox.session(name="lint:dependencies", python=False) def dependency_check(session: Session) -> None: """Checks if only valid sources of dependencies are used""" - content = Path(PROJECT_CONFIG.root, PoetryFiles.pyproject_toml).read_text() + content = Path(PROJECT_CONFIG.root_path, PoetryFiles.pyproject_toml).read_text() dependencies = Dependencies.parse(content) console = rich.console.Console() if illegal := dependencies.illegal: diff --git a/exasol/toolbox/nox/_metrics.py b/exasol/toolbox/nox/_metrics.py index b5b9355e2..052942a5d 100644 --- a/exasol/toolbox/nox/_metrics.py +++ b/exasol/toolbox/nox/_metrics.py @@ -23,7 +23,7 @@ class RequiredFile: """ def __init__(self, file: Path | str, task: str): - self.file = file if isinstance(file, Path) else PROJECT_CONFIG.root / file + self.file = file if isinstance(file, Path) else PROJECT_CONFIG.root_path / file self.task = task def __str__(self) -> str: diff --git a/exasol/toolbox/nox/_package.py b/exasol/toolbox/nox/_package.py index 12996f715..d7c7f508e 100644 --- a/exasol/toolbox/nox/_package.py +++ b/exasol/toolbox/nox/_package.py @@ -12,5 +12,5 @@ def package_check(session: Session) -> None: This has more robust checks for rst documentation than markdown. """ - session.run("poetry", "build", "--project", PROJECT_CONFIG.root) - session.run("twine", "check", PROJECT_CONFIG.root / "./dist/*") + session.run("poetry", "build", "--project", PROJECT_CONFIG.root_path) + session.run("twine", "check", PROJECT_CONFIG.root_path / "./dist/*") diff --git a/exasol/toolbox/nox/_release.py b/exasol/toolbox/nox/_release.py index cf476a512..8632a8b59 100644 --- a/exasol/toolbox/nox/_release.py +++ b/exasol/toolbox/nox/_release.py @@ -128,7 +128,7 @@ def prepare_release(session: Session) -> None: changelogs = Changelogs( changes_path=PROJECT_CONFIG.doc / "changes", - root_path=PROJECT_CONFIG.root, + root_path=PROJECT_CONFIG.root_path, version=new_version, ) changelogs.update_changelogs_for_release() @@ -143,7 +143,7 @@ def prepare_release(session: Session) -> None: return changed_files += [ - PROJECT_CONFIG.root / PoetryFiles.pyproject_toml, + PROJECT_CONFIG.root_path / PoetryFiles.pyproject_toml, PROJECT_CONFIG.version_file, ] results = pm.hook.prepare_release_add_files(session=session, config=PROJECT_CONFIG) diff --git a/exasol/toolbox/nox/tasks.py b/exasol/toolbox/nox/tasks.py index c8475056b..f3932da8a 100644 --- a/exasol/toolbox/nox/tasks.py +++ b/exasol/toolbox/nox/tasks.py @@ -34,7 +34,7 @@ def check(session: Session) -> None: """Runs all available checks on the project""" context = _context(session, coverage=True) - py_files = get_filtered_python_files(PROJECT_CONFIG.root) + py_files = get_filtered_python_files(PROJECT_CONFIG.root_path) _version(session, Mode.Check) _code_format(session, Mode.Check, py_files) _pylint(session, py_files) diff --git a/exasol/toolbox/util/dependencies/shared_models.py b/exasol/toolbox/util/dependencies/shared_models.py index fa802c81c..c928d49c4 100644 --- a/exasol/toolbox/util/dependencies/shared_models.py +++ b/exasol/toolbox/util/dependencies/shared_models.py @@ -66,7 +66,7 @@ def files(self) -> tuple[str, ...]: def poetry_files_from_latest_tag() -> Generator[Path]: """Context manager to set up a temporary directory with poetry files from the latest tag""" latest_tag = Git.get_latest_tag() - path = PROJECT_CONFIG.root.relative_to(Git.toplevel()) + path = PROJECT_CONFIG.root_path.relative_to(Git.toplevel()) with tempfile.TemporaryDirectory() as tmpdir_str: tmp_dir = Path(tmpdir_str) for file in PoetryFiles().files: diff --git a/test/integration/project-template/conftest.py b/test/integration/project-template/conftest.py index f949a40db..d174349be 100644 --- a/test/integration/project-template/conftest.py +++ b/test/integration/project-template/conftest.py @@ -18,7 +18,7 @@ def new_project(cwd): package_name = "package" subprocess.run( - ["cookiecutter", PROJECT_CONFIG.root / "project-template", "-o", cwd, "--no-input", + ["cookiecutter", PROJECT_CONFIG.root_path / "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) @@ -32,8 +32,9 @@ def poetry_install(run_command, poetry_path): # The tests want to verify the current branch of the PTB incl. its cookiecutter # template before releasing the PTB. The following command therefore modifies the # dependency to the PTB itself in the pyproject.toml file by replacing the latest - # released PTB version with the current checked-out branch in PROJECT_CONFIG.root: - run_command([poetry_path, "add", "--group", "dev", PROJECT_CONFIG.root]) + # released PTB version with the current checked-out branch in + # PROJECT_CONFIG.root_path: + run_command([poetry_path, "add", "--group", "dev", PROJECT_CONFIG.root_path]) @pytest.fixture(scope="session") diff --git a/test/unit/nox/_package_check_test.py b/test/unit/nox/_package_check_test.py index 0a3c664a6..68c9354cb 100644 --- a/test/unit/nox/_package_check_test.py +++ b/test/unit/nox/_package_check_test.py @@ -24,12 +24,14 @@ def test_raises_non_zero_exist_with_readme_error(nox_session, tmp_path): package_readme = package / "README.rst" # copy over `packages` and `include` from `pyproject.toml` to for `poetry build` - shutil.copytree(PROJECT_CONFIG.root / "exasol", package / "exasol") - shutil.copyfile(PROJECT_CONFIG.root / "README.rst", package_readme) - shutil.copytree(PROJECT_CONFIG.root / "doc/changes", package / "doc/changes") - shutil.copyfile(PROJECT_CONFIG.root / "LICENSE", package / "LICENSE") + shutil.copytree(PROJECT_CONFIG.root_path / "exasol", package / "exasol") + shutil.copyfile(PROJECT_CONFIG.root_path / "README.rst", package_readme) + shutil.copytree( + PROJECT_CONFIG.root_path / "doc/changes", package / "doc/changes" + ) + shutil.copyfile(PROJECT_CONFIG.root_path / "LICENSE", package / "LICENSE") shutil.copyfile( - PROJECT_CONFIG.root / "pyproject.toml", package / "pyproject.toml" + PROJECT_CONFIG.root_path / "pyproject.toml", package / "pyproject.toml" ) # create an error in readme.rst diff --git a/test/unit/util/dependencies/audit_test.py b/test/unit/util/dependencies/audit_test.py index ba91be6a1..bc84ff5e0 100644 --- a/test/unit/util/dependencies/audit_test.py +++ b/test/unit/util/dependencies/audit_test.py @@ -238,7 +238,7 @@ def test_with_mock(self, sample_vulnerability): "exasol.toolbox.util.dependencies.audit.audit_poetry_files", return_value=sample_vulnerability.pip_audit_json, ): - result = get_vulnerabilities(PROJECT_CONFIG.root) + result = get_vulnerabilities(PROJECT_CONFIG.root_path) # if successful, no errors & should be 1 due to mock assert isinstance(result, list) diff --git a/test/unit/util/dependencies/poetry_dependencies_test.py b/test/unit/util/dependencies/poetry_dependencies_test.py index f38f8a77a..3207473e4 100644 --- a/test/unit/util/dependencies/poetry_dependencies_test.py +++ b/test/unit/util/dependencies/poetry_dependencies_test.py @@ -134,7 +134,7 @@ def test_all_dependencies(create_poetry_project, project_path): def test_get_dependencies(): - result = get_dependencies(PROJECT_CONFIG.root) + result = get_dependencies(PROJECT_CONFIG.root_path) # if successful, no errors & should be non-empty dictionary assert isinstance(result, dict) From 8845fa9602ad5a394e913156b017d3a0f43416cd Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 1 Dec 2025 13:33:09 +0100 Subject: [PATCH 03/31] Switch PROJECT_CONFIG.doc with PROJECT_CONFIG.documentation_path --- exasol/toolbox/nox/_release.py | 2 +- noxconfig.py | 1 - test/unit/documentation_test.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/exasol/toolbox/nox/_release.py b/exasol/toolbox/nox/_release.py index 8632a8b59..afc1b7c53 100644 --- a/exasol/toolbox/nox/_release.py +++ b/exasol/toolbox/nox/_release.py @@ -127,7 +127,7 @@ def prepare_release(session: Session) -> None: _ = _update_project_version(session, new_version) changelogs = Changelogs( - changes_path=PROJECT_CONFIG.doc / "changes", + changes_path=PROJECT_CONFIG.documentation_path / "changes", root_path=PROJECT_CONFIG.root_path, version=new_version, ) diff --git a/noxconfig.py b/noxconfig.py index 5cd30c49b..dc71d9c19 100644 --- a/noxconfig.py +++ b/noxconfig.py @@ -52,7 +52,6 @@ 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") importlinter: Path = Path(__file__).parent / ".import_linter_config" version_file: Path = Path(__file__).parent / "exasol" / "toolbox" / "version.py" diff --git a/test/unit/documentation_test.py b/test/unit/documentation_test.py index eab6915b6..79aab7e31 100644 --- a/test/unit/documentation_test.py +++ b/test/unit/documentation_test.py @@ -42,7 +42,7 @@ def config(index, file, tmp_path): test_doc = tmp_path / "doc" test_doc.mkdir() (test_doc / "_static").mkdir() - shutil.copyfile(PROJECT_CONFIG.doc / "conf.py", test_doc / "conf.py") + shutil.copyfile(PROJECT_CONFIG.documentation_path / "conf.py", test_doc / "conf.py") rst_index = test_doc / "index.rst" rst_file1 = test_doc / "file.rst" rst_index.touch() From 8140f46a70dad9359f7baeb4e503e2ada7949a2f Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 1 Dec 2025 13:35:59 +0100 Subject: [PATCH 04/31] Switch PROJECT_CONFIG.source with PROJECT_CONFIG.source_code_path --- doc/user_guide/features/metrics/sonar.rst | 2 +- exasol/toolbox/config.py | 4 ++-- exasol/toolbox/nox/_artifacts.py | 2 +- exasol/toolbox/nox/_lint.py | 4 ++-- noxconfig.py | 1 - 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/user_guide/features/metrics/sonar.rst b/doc/user_guide/features/metrics/sonar.rst index 77a767c29..4a95ced71 100644 --- a/doc/user_guide/features/metrics/sonar.rst +++ b/doc/user_guide/features/metrics/sonar.rst @@ -130,5 +130,5 @@ project from Sonar's analysis: See the `Sonar Matching Patterns`_ for more details. By default, the nox session ``sonar:check`` only analyses the source code, -as specified by the ``PROJECT_CONFIG.source``, so directories outside of this +as specified by the ``PROJECT_CONFIG.source_code_path``, so directories outside of this are already excluded from being analyzed. diff --git a/exasol/toolbox/config.py b/exasol/toolbox/config.py index f124739ce..e031eb0c2 100644 --- a/exasol/toolbox/config.py +++ b/exasol/toolbox/config.py @@ -106,8 +106,8 @@ def excluded_python_paths(self) -> tuple[str, ...]: - lint:security - lint:typing where it is desired to restrict which Python files are considered within the - PROJECT_CONFIG.source path, like excluding `dist`, `.eggs`. As such, this - property is used to exclude such undesired paths. + PROJECT_CONFIG.source_code_path path, like excluding `dist`, `.eggs`. As such, + this property is used to exclude such undesired paths. """ return tuple( DEFAULT_EXCLUDED_PATHS.union(set(self.add_to_excluded_python_paths)) diff --git a/exasol/toolbox/nox/_artifacts.py b/exasol/toolbox/nox/_artifacts.py index 7e7d34278..067fdebee 100644 --- a/exasol/toolbox/nox/_artifacts.py +++ b/exasol/toolbox/nox/_artifacts.py @@ -247,5 +247,5 @@ def _upload_to_sonar(session: Session, sonar_token: str | None, config: Config) def upload_artifacts_to_sonar(session: Session) -> None: """Upload artifacts to sonar for analysis""" sonar_token = os.getenv("SONAR_TOKEN") - _prepare_coverage_xml(session, PROJECT_CONFIG.source) + _prepare_coverage_xml(session, PROJECT_CONFIG.source_code_path) _upload_to_sonar(session, sonar_token, PROJECT_CONFIG) diff --git a/exasol/toolbox/nox/_lint.py b/exasol/toolbox/nox/_lint.py index 088523e07..a601fbd63 100644 --- a/exasol/toolbox/nox/_lint.py +++ b/exasol/toolbox/nox/_lint.py @@ -123,7 +123,7 @@ def report_illegal(illegal: dict[str, list[str]], console: rich.console.Console) @nox.session(name="lint:code", python=False) def lint(session: Session) -> None: """Runs the static code analyzer on the project""" - py_files = get_filtered_python_files(PROJECT_CONFIG.root / PROJECT_CONFIG.source) + py_files = get_filtered_python_files(PROJECT_CONFIG.source_code_path) _pylint(session=session, files=py_files) @@ -137,7 +137,7 @@ def type_check(session: Session) -> None: @nox.session(name="lint:security", python=False) def security_lint(session: Session) -> None: """Runs the security linter on the project""" - py_files = get_filtered_python_files(PROJECT_CONFIG.root / PROJECT_CONFIG.source) + py_files = get_filtered_python_files(PROJECT_CONFIG.source_code_path) _security_lint(session=session, files=py_files) diff --git a/noxconfig.py b/noxconfig.py index dc71d9c19..4441fed84 100644 --- a/noxconfig.py +++ b/noxconfig.py @@ -52,7 +52,6 @@ class Config(BaseConfig): """Project specific configuration used by nox infrastructure""" root: Path = Path(__file__).parent - source: Path = Path("exasol/toolbox") importlinter: Path = Path(__file__).parent / ".import_linter_config" version_file: Path = Path(__file__).parent / "exasol" / "toolbox" / "version.py" plugins: Iterable[object] = (UpdateTemplates,) From 1704aa2ead581270f26f6debb61c72ecb0401189 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 1 Dec 2025 13:39:17 +0100 Subject: [PATCH 05/31] Switch remainder over to BaseConfig usage --- exasol/toolbox/nox/_package_version.py | 2 +- noxconfig.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/exasol/toolbox/nox/_package_version.py b/exasol/toolbox/nox/_package_version.py index a1a04d471..753c54982 100644 --- a/exasol/toolbox/nox/_package_version.py +++ b/exasol/toolbox/nox/_package_version.py @@ -71,7 +71,7 @@ def _create_parser() -> ArgumentParser: def _version_check(args: Namespace, config: Config) -> int: - version_file = config.version_file + version_file = config.version_filepath module_version = Version.from_python_module(version_file) poetry_version = Version.from_poetry() diff --git a/noxconfig.py b/noxconfig.py index 4441fed84..6ae737932 100644 --- a/noxconfig.py +++ b/noxconfig.py @@ -48,17 +48,18 @@ def prepare_release_add_files(self, session, config): # extra_data: list[str] = ["data"] +ROOT_PATH = Path(__file__).parent + + class Config(BaseConfig): """Project specific configuration used by nox infrastructure""" - root: Path = Path(__file__).parent - importlinter: Path = Path(__file__).parent / ".import_linter_config" - version_file: Path = Path(__file__).parent / "exasol" / "toolbox" / "version.py" + importlinter: Path = ROOT_PATH / ".import_linter_config" plugins: Iterable[object] = (UpdateTemplates,) PROJECT_CONFIG = Config( - root_path=Path(__file__).parent, + root_path=ROOT_PATH, project_name="toolbox", add_to_excluded_python_paths=( # The cookiecutter placeholders do not work well with checks. From 9f49b8a625b2474638f6fc0954e3100432760578 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 1 Dec 2025 13:49:50 +0100 Subject: [PATCH 06/31] Fix tests by creating test_project_config --- test/unit/conftest.py | 9 +++++++++ test/unit/nox/_shared_test.py | 4 ++-- test/unit/release_test.py | 7 ++----- 3 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 test/unit/conftest.py diff --git a/test/unit/conftest.py b/test/unit/conftest.py new file mode 100644 index 000000000..fd9a3523e --- /dev/null +++ b/test/unit/conftest.py @@ -0,0 +1,9 @@ +from pathlib import Path + +import pytest +from toolbox.config import BaseConfig + + +@pytest.fixture +def test_project_config(): + return BaseConfig(root_path=Path("."), project_name="test") diff --git a/test/unit/nox/_shared_test.py b/test/unit/nox/_shared_test.py index 8783ad399..3512842fa 100644 --- a/test/unit/nox/_shared_test.py +++ b/test/unit/nox/_shared_test.py @@ -88,5 +88,5 @@ def test_old_implementation_raises_error(self, attribute): check_for_config_attribute(PreviousConfig(), attribute=attribute) @pytest.mark.parametrize("attribute", MIGRATED_VALUES) - def test_current_implementation_passes(self, attribute): - check_for_config_attribute(BaseConfig(), attribute=attribute) + def test_current_implementation_passes(self, test_project_config, attribute): + check_for_config_attribute(test_project_config, attribute=attribute) diff --git a/test/unit/release_test.py b/test/unit/release_test.py index 17337e79b..fafa7d137 100644 --- a/test/unit/release_test.py +++ b/test/unit/release_test.py @@ -7,7 +7,6 @@ import pytest import noxconfig -from exasol.toolbox.config import BaseConfig from exasol.toolbox.nox._release import ( ReleaseError, _trigger_release, @@ -64,15 +63,13 @@ def simulate_pass(args, **kwargs): ] assert result == mock_from_poetry.return_value - def test_not_creates_major_version_tag(self, mock_from_poetry): - class DummyConfig(BaseConfig): - pass + def test_not_creates_major_version_tag(self, test_project_config, mock_from_poetry): def simulate_pass(args, **kwargs): return self._get_subprocess_run_mock(args) with patch("subprocess.run", side_effect=simulate_pass) as subprocess_mock: - result = _trigger_release(DummyConfig()) + result = _trigger_release(project_config=test_project_config) commands = [c.args[0] for c in subprocess_mock.mock_calls] assert commands == [ ("git", "remote", "show", "origin"), From 0fdb11350798a3a8fb87bef74052a35889bd42c5 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Fri, 5 Dec 2025 12:55:25 +0100 Subject: [PATCH 07/31] Remove unused and unneeded additional templates for noxfconfig and noxfile --- exasol/toolbox/templates/noxconfig.py | 32 --------------------------- exasol/toolbox/templates/noxfile.py | 7 ------ 2 files changed, 39 deletions(-) delete mode 100644 exasol/toolbox/templates/noxconfig.py delete mode 100644 exasol/toolbox/templates/noxfile.py diff --git a/exasol/toolbox/templates/noxconfig.py b/exasol/toolbox/templates/noxconfig.py deleted file mode 100644 index c2d628360..000000000 --- a/exasol/toolbox/templates/noxconfig.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import annotations - -from collections.abc import MutableMapping -from dataclasses import dataclass -from pathlib import Path -from typing import Any - -from nox import Session - - -@dataclass(frozen=True) -class Config: - root: Path = Path(__file__).parent - doc: Path = Path(__file__).parent / "doc" - version_file: Path = Path(__file__).parent / "exasol" / "toolbox" / "version.py" - - @staticmethod - def pre_integration_tests_hook( - _session: Session, _config: Config, _context: MutableMapping[str, Any] - ) -> bool: - """Implement if project specific behaviour is required""" - return True - - @staticmethod - def post_integration_tests_hook( - _session: Session, _config: Config, _context: MutableMapping[str, Any] - ) -> bool: - """Implement if project specific behaviour is required""" - return True - - -PROJECT_CONFIG = Config() diff --git a/exasol/toolbox/templates/noxfile.py b/exasol/toolbox/templates/noxfile.py deleted file mode 100644 index 40a6dc204..000000000 --- a/exasol/toolbox/templates/noxfile.py +++ /dev/null @@ -1,7 +0,0 @@ -import nox - -# imports all nox task provided by the toolbox -from exasol.toolbox.nox.tasks import * - -# default actions to be run if nothing is explicitly specified with the -s option -nox.options.sessions = ["format:fix"] From f71df38f42c851952d71d4213d771f6b07a1a0f8 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Fri, 5 Dec 2025 13:01:23 +0100 Subject: [PATCH 08/31] Add factory to create PROJECT_CONFIG equivalent for tests --- test/unit/config_test.py | 22 +++++++++----- test/unit/conftest.py | 14 ++++++--- test/unit/nox/_artifacts_test.py | 34 +++++++++++++++------ test/unit/nox/_format_test.py | 41 ++++++++++++++------------ test/unit/nox/_lint_test.py | 36 +++++++++++----------- test/unit/nox/_package_version_test.py | 15 ++++++---- test/unit/nox/_shared_test.py | 32 +++++++++++++------- test/unit/release_test.py | 6 ++-- 8 files changed, 122 insertions(+), 78 deletions(-) diff --git a/test/unit/config_test.py b/test/unit/config_test.py index 685e7c14e..d9e8d7213 100644 --- a/test/unit/config_test.py +++ b/test/unit/config_test.py @@ -10,8 +10,8 @@ class TestBaseConfig: @staticmethod - def test_works_as_defined(): - BaseConfig() + def test_works_as_defined(test_project_config_factory): + test_project_config_factory() @staticmethod @pytest.mark.parametrize( @@ -58,14 +58,16 @@ def test_expansion_validation_fails_for_invalid_version(): BaseConfigExpansion(python_versions=("1.f.0",)) -def test_minimum_python_version(): - conf = BaseConfig(python_versions=("5.5.5", "1.10", "9.9.9")) +def test_minimum_python_version(test_project_config_factory): + conf = test_project_config_factory(python_versions=("5.5.5", "1.10", "9.9.9")) assert conf.minimum_python_version == "1.10" @pytest.mark.parametrize("minimum_python_version", ["3.10", "3.10.5"]) -def test_pyupgrade_argument(minimum_python_version): - conf = BaseConfig(python_versions=("3.11", minimum_python_version, "3.12")) +def test_pyupgrade_argument(test_project_config_factory, minimum_python_version): + conf = test_project_config_factory( + python_versions=("3.11", minimum_python_version, "3.12") + ) assert conf.pyupgrade_argument == ("--py310-plus",) @@ -83,6 +85,10 @@ def test_pyupgrade_argument(minimum_python_version): ), ], ) -def test_excluded_python_paths(add_to_excluded_python_paths, expected): - conf = BaseConfig(add_to_excluded_python_paths=add_to_excluded_python_paths) +def test_excluded_python_paths( + test_project_config_factory, add_to_excluded_python_paths, expected +): + conf = test_project_config_factory( + add_to_excluded_python_paths=add_to_excluded_python_paths + ) assert sorted(conf.excluded_python_paths) == sorted(expected) diff --git a/test/unit/conftest.py b/test/unit/conftest.py index fd9a3523e..c75d2b361 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -1,9 +1,15 @@ -from pathlib import Path - import pytest from toolbox.config import BaseConfig @pytest.fixture -def test_project_config(): - return BaseConfig(root_path=Path("."), project_name="test") +def test_project_config_factory(tmp_path): + def _test_project_config(**kwargs) -> BaseConfig: + defaults = { + "root_path": tmp_path, + "project_name": "test", + } + config = {**defaults, **kwargs} + return BaseConfig(**config) + + return _test_project_config diff --git a/test/unit/nox/_artifacts_test.py b/test/unit/nox/_artifacts_test.py index dbc5db623..18bf8bec3 100644 --- a/test/unit/nox/_artifacts_test.py +++ b/test/unit/nox/_artifacts_test.py @@ -36,14 +36,14 @@ check_artifacts, copy_artifacts, ) +from noxconfig import Config @contextlib.contextmanager def mock_check_artifacts_session( - path: Path, + config: Config, ): - with patch("exasol.toolbox.nox._artifacts.PROJECT_CONFIG") as config: - config.root = path + with patch("exasol.toolbox.nox._artifacts.PROJECT_CONFIG", new=config): yield Mock() @@ -92,10 +92,18 @@ def _create_artifact_files(path: Path, existing_files: set): ) @mock.patch("exasol.toolbox.nox._artifacts._is_valid_coverage", return_value=True) def test_passes_when_as_expected( - self, mock_coverage, mock_security, mock_lint_json, mock_lint_txt, tmp_path + self, + mock_coverage, + mock_security, + mock_lint_json, + mock_lint_txt, + test_project_config_factory, + tmp_path, ): self._create_artifact_files(tmp_path, ALL_LINT_FILES) - with mock_check_artifacts_session(tmp_path) as session: + with mock_check_artifacts_session( + config=test_project_config_factory() + ) as session: check_artifacts(session) @pytest.mark.parametrize( @@ -105,18 +113,26 @@ def test_passes_when_as_expected( (pytest.param(ALL_LINT_FILES, id="all_files_missing")), ], ) - def test_fails_when_file_missing(self, tmp_path, missing_files, capsys): + def test_fails_when_file_missing( + self, test_project_config_factory, tmp_path, missing_files, capsys + ): existing_files = ALL_LINT_FILES - missing_files self._create_artifact_files(tmp_path, existing_files) - with mock_check_artifacts_session(tmp_path) as session: + with mock_check_artifacts_session( + config=test_project_config_factory() + ) as session: with pytest.raises(SystemExit): check_artifacts(session) assert f"files not available: {missing_files}" in capsys.readouterr().err - def test_fails_when_check_fails(self, tmp_path, capsys): + def test_fails_when_check_fails( + self, test_project_config_factory, tmp_path, capsys + ): self._create_artifact_files(tmp_path, ALL_LINT_FILES) - with mock_check_artifacts_session(tmp_path) as session: + with mock_check_artifacts_session( + config=test_project_config_factory() + ) as session: with pytest.raises(SystemExit): check_artifacts(session) assert "error in [" in capsys.readouterr().err diff --git a/test/unit/nox/_format_test.py b/test/unit/nox/_format_test.py index 56aa3e062..dea42d782 100644 --- a/test/unit/nox/_format_test.py +++ b/test/unit/nox/_format_test.py @@ -12,26 +12,33 @@ fix_format, ) from exasol.toolbox.nox._shared import Mode -from noxconfig import Config @pytest.fixture -def file_with_unneeded_import(tmp_path): - file_path = tmp_path / "dummy_file.py" +def config(test_project_config_factory): + return test_project_config_factory() + + +@pytest.fixture +def file_with_unneeded_import(config): + config.source_code_path.mkdir(parents=True, exist_ok=True) + file_path = config.source_code_path / "dummy_file.py" file_path.write_text("import black") return file_path @pytest.fixture -def file_with_not_ordered_import(tmp_path): - file_path = tmp_path / "dummy_file.py" +def file_with_not_ordered_import(config): + config.source_code_path.mkdir(parents=True, exist_ok=True) + file_path = config.source_code_path / "dummy_file.py" file_path.write_text("import isort\nimport black") return file_path @pytest.fixture -def file_without_blank_line(tmp_path): - file_path = tmp_path / "dummy_file.py" +def file_without_blank_line(config): + config.source_code_path.mkdir(parents=True, exist_ok=True) + file_path = config.source_code_path / "dummy_file.py" file_path.write_text("import black\nimport isort") return file_path @@ -86,10 +93,11 @@ def test_black_mode_check(nox_session, file_without_blank_line, caplog): assert file_without_blank_line.read_text() == "import black\nimport isort" -def test_pyupgrade(nox_session, tmp_path): - file_path = tmp_path / "dummy_file.py" +def test_pyupgrade(nox_session, config): + config.source_code_path.mkdir(parents=True, exist_ok=True) + file_path = config.source_code_path / "dummy_file.py" file_path.write_text("from typing import Union\nx:Union[int, str]=2") - _pyupgrade(session=nox_session, config=Config(), files=[str(file_path)]) + _pyupgrade(session=nox_session, config=config, files=[str(file_path)]) assert file_path.read_text() == "from typing import Union\nx:int | str=2" @@ -148,11 +156,9 @@ def file_with_multiple_problems(tmp_path): return file_path -def test_fix_format(nox_session, tmp_path, file_with_multiple_problems): - with patch("exasol.toolbox.nox._format.PROJECT_CONFIG") as config: +def test_fix_format(nox_session, config, file_with_multiple_problems): + with patch("exasol.toolbox.nox._format.PROJECT_CONFIG", new=config): with patch("exasol.toolbox.nox._format._version") as version: - config.root = tmp_path - config.pyupgrade_argument = ("--py310-plus",) # Simulate version is up-to-date, as version check is out of the scope of the test case version.return_value = True fix_format(nox_session) @@ -171,13 +177,10 @@ def test_fix_format(nox_session, tmp_path, file_with_multiple_problems): ) -def test_check_format( - nox_session, tmp_path, file_with_multiple_problems, caplog, capsys -): +def test_check_format(nox_session, config, file_with_multiple_problems, caplog, capsys): expected_text = file_with_multiple_problems.read_text() - with patch("exasol.toolbox.nox._format.PROJECT_CONFIG") as config: - config.root = tmp_path + with patch("exasol.toolbox.nox._format.PROJECT_CONFIG", new=config): with pytest.raises(CommandFailed): check_format(nox_session) diff --git a/test/unit/nox/_lint_test.py b/test/unit/nox/_lint_test.py index 9cd5084c4..55339253f 100644 --- a/test/unit/nox/_lint_test.py +++ b/test/unit/nox/_lint_test.py @@ -1,6 +1,5 @@ import json from inspect import cleandoc -from pathlib import Path from unittest.mock import patch import pytest @@ -14,7 +13,12 @@ @pytest.fixture -def file_with_multiple_problems(tmp_path): +def config(test_project_config_factory): + return test_project_config_factory() + + +@pytest.fixture +def file_with_multiple_problems(config): """ In this file with multiple problems, it is expected that the nox lint sessions would detect the following errors: @@ -30,8 +34,8 @@ def file_with_multiple_problems(tmp_path): * [B607:start_process_with_partial_path] Starting a process with a partial executable path * [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input. """ - - file_path = tmp_path / "dummy_file.py" + config.source_code_path.mkdir(parents=True, exist_ok=True) + file_path = config.source_code_path / "dummy_file.py" text = """ import subprocess @@ -42,15 +46,13 @@ def file_with_multiple_problems(tmp_path): return file_path -def test_lint(nox_session, tmp_path, file_with_multiple_problems): - with patch("exasol.toolbox.nox._lint.PROJECT_CONFIG") as config: - config.root = tmp_path - config.source = Path("") +def test_lint(nox_session, config, file_with_multiple_problems): + with patch("exasol.toolbox.nox._lint.PROJECT_CONFIG", new=config): with pytest.raises(CommandFailed, match="Returned code 20"): lint(session=nox_session) - json_file = tmp_path / ".lint.json" - txt_file = tmp_path / ".lint.txt" + json_file = config.root_path / ".lint.json" + txt_file = config.root_path / ".lint.txt" assert json_file.exists() assert txt_file.exists() @@ -60,10 +62,8 @@ def test_lint(nox_session, tmp_path, file_with_multiple_problems): assert {"C0114", "C0304", "W1510"}.issubset(errors) -def test_type_check(nox_session, tmp_path, file_with_multiple_problems, caplog): - with patch("exasol.toolbox.nox._lint.PROJECT_CONFIG") as config: - config.root = tmp_path - config.source = Path("") +def test_type_check(nox_session, config, file_with_multiple_problems, caplog): + with patch("exasol.toolbox.nox._lint.PROJECT_CONFIG", new=config): with pytest.raises(CommandFailed, match="Returned code 1"): type_check(session=nox_session) @@ -74,13 +74,11 @@ def test_type_check(nox_session, tmp_path, file_with_multiple_problems, caplog): ) -def test_security_lint(nox_session, tmp_path, file_with_multiple_problems): - with patch("exasol.toolbox.nox._lint.PROJECT_CONFIG") as config: - config.root = tmp_path - config.source = Path("") +def test_security_lint(nox_session, config, file_with_multiple_problems): + with patch("exasol.toolbox.nox._lint.PROJECT_CONFIG", new=config): security_lint(session=nox_session) - output_file = tmp_path / ".security.json" + output_file = config.root_path / ".security.json" assert output_file.exists() contents = output_file.read_text() diff --git a/test/unit/nox/_package_version_test.py b/test/unit/nox/_package_version_test.py index 23bdf0b47..e1091e17e 100644 --- a/test/unit/nox/_package_version_test.py +++ b/test/unit/nox/_package_version_test.py @@ -15,13 +15,16 @@ @pytest.fixture -def config(version_file) -> Config: - return Config(version_file=version_file) +def config(test_project_config_factory) -> Config: + config = test_project_config_factory() + # We need to set up the directory path so that version_file can execute + config.source_code_path.mkdir(parents=True, exist_ok=True) + return config @pytest.fixture -def version_file(tmp_path): - version_file = tmp_path / "version.py" +def version_file(config): + version_file = config.version_filepath write_version_module(version=DEFAULT_VERSION, version_file=version_file) return version_file @@ -38,7 +41,7 @@ def test_write_version_module(version_file) -> None: class TestVersionCheck: @staticmethod @mock.patch.object(Version, "from_poetry", return_value=DEFAULT_VERSION) - def test_same_value_is_successful(from_poetry, config): + def test_same_value_is_successful(from_poetry, config, version_file): Version(major=0, minor=1, patch=0) parser = _create_parser() args = parser.parse_args([]) @@ -48,7 +51,7 @@ def test_same_value_is_successful(from_poetry, config): @staticmethod @mock.patch.object(Version, "from_poetry", return_value=ALTERNATE_VERSION) - def test_different_value_is_failure(from_poetry, config): + def test_different_value_is_failure(from_poetry, config, version_file): Version(major=0, minor=1, patch=0) parser = _create_parser() args = parser.parse_args([]) diff --git a/test/unit/nox/_shared_test.py b/test/unit/nox/_shared_test.py index 3512842fa..2b31e8451 100644 --- a/test/unit/nox/_shared_test.py +++ b/test/unit/nox/_shared_test.py @@ -27,18 +27,18 @@ def excluded_python_path(): return "excluded_python_path" -@pytest.fixture(scope="session") -def directories(package_directory, excluded_python_path): - yield set(BaseConfig().excluded_python_paths).union( +@pytest.fixture +def directories(test_project_config_factory, package_directory, excluded_python_path): + yield set(test_project_config_factory().excluded_python_paths).union( {package_directory, excluded_python_path} ) -@pytest.fixture(scope="session") -def create_files(tmp_directory, directories): +@pytest.fixture +def create_files(tmp_path, directories): file_list = [] for directory in directories: - directory_path = tmp_directory / directory + directory_path = tmp_path / directory directory_path.mkdir(parents=True, exist_ok=True) file_path = directory_path / f"{directory}-dummy.py" @@ -49,12 +49,18 @@ def create_files(tmp_directory, directories): def test_get_filtered_python_files( - tmp_directory, create_files, package_directory, excluded_python_path + test_project_config_factory, + tmp_path, + create_files, + package_directory, + excluded_python_path, ): - config = BaseConfig(add_to_excluded_python_paths=(excluded_python_path,)) + config = test_project_config_factory( + add_to_excluded_python_paths=(excluded_python_path,) + ) with patch("exasol.toolbox.nox._shared.PROJECT_CONFIG", config): - actual = get_filtered_python_files(tmp_directory) + actual = get_filtered_python_files(tmp_path) assert len(actual) == 1 assert "toolbox-dummy" in actual[0] @@ -88,5 +94,9 @@ def test_old_implementation_raises_error(self, attribute): check_for_config_attribute(PreviousConfig(), attribute=attribute) @pytest.mark.parametrize("attribute", MIGRATED_VALUES) - def test_current_implementation_passes(self, test_project_config, attribute): - check_for_config_attribute(test_project_config, attribute=attribute) + def test_current_implementation_passes( + self, test_project_config_factory, attribute + ): + check_for_config_attribute( + config=test_project_config_factory(), attribute=attribute + ) diff --git a/test/unit/release_test.py b/test/unit/release_test.py index fafa7d137..242e4e31d 100644 --- a/test/unit/release_test.py +++ b/test/unit/release_test.py @@ -63,13 +63,15 @@ def simulate_pass(args, **kwargs): ] assert result == mock_from_poetry.return_value - def test_not_creates_major_version_tag(self, test_project_config, mock_from_poetry): + def test_not_creates_major_version_tag( + self, test_project_config_factory, mock_from_poetry + ): def simulate_pass(args, **kwargs): return self._get_subprocess_run_mock(args) with patch("subprocess.run", side_effect=simulate_pass) as subprocess_mock: - result = _trigger_release(project_config=test_project_config) + result = _trigger_release(project_config=test_project_config_factory()) commands = [c.args[0] for c in subprocess_mock.mock_calls] assert commands == [ ("git", "remote", "show", "origin"), From 1aeb6c54236cdcee9b43b2d11de41b0a2eb3ae94 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 11:30:10 +0100 Subject: [PATCH 09/31] Fix a few more .root places to .root_path --- exasol/toolbox/nox/_test.py | 14 ++++++++++---- test/unit/nox/_package_check_test.py | 10 +++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/exasol/toolbox/nox/_test.py b/exasol/toolbox/nox/_test.py index d954fd4c0..9081d3429 100644 --- a/exasol/toolbox/nox/_test.py +++ b/exasol/toolbox/nox/_test.py @@ -22,7 +22,13 @@ def _test_command( path: Path, config: Config, context: MutableMapping[str, Any] ) -> Iterable[str]: coverage_command = ( - ["coverage", "run", "-a", f"--rcfile={config.root / 'pyproject.toml'}", "-m"] + [ + "coverage", + "run", + "-a", + f"--rcfile={config.root_path / 'pyproject.toml'}", + "-m", + ] if context["coverage"] else [] ) @@ -33,7 +39,7 @@ def _test_command( def _unit_tests( session: Session, config: Config, context: MutableMapping[str, Any] ) -> None: - command = _test_command(config.root / "test" / "unit", config, context) + command = _test_command(config.root_path / "test" / "unit", config, context) session.run(*command) @@ -49,7 +55,7 @@ def _integration_tests( # - Catch exceptions and ensure post-hooks run before exiting # - Consider making the executed command(s) configurable via a plugin hook # (The default implementation of the hook could provide the current implementation) - command = _test_command(config.root / "test" / "integration", config, context) + command = _test_command(config.root_path / "test" / "integration", config, context) session.run(*command) # run post integration test plugins @@ -67,7 +73,7 @@ def _coverage( session: Session, config: Config, context: MutableMapping[str, Any] ) -> None: command = ["coverage", "report", "-m"] - coverage_file = config.root / ".coverage" + coverage_file = config.root_path / ".coverage" coverage_file.unlink(missing_ok=True) _unit_tests(session, config, context) _integration_tests(session, config, context) diff --git a/test/unit/nox/_package_check_test.py b/test/unit/nox/_package_check_test.py index 68c9354cb..c1a86a264 100644 --- a/test/unit/nox/_package_check_test.py +++ b/test/unit/nox/_package_check_test.py @@ -19,7 +19,9 @@ def test_works_as_expected(nox_session): package_check(nox_session) @staticmethod - def test_raises_non_zero_exist_with_readme_error(nox_session, tmp_path): + def test_raises_non_zero_exist_with_readme_error( + nox_session, test_project_config_factory, tmp_path + ): package = Path(tmp_path) package_readme = package / "README.rst" @@ -40,8 +42,10 @@ def test_raises_non_zero_exist_with_readme_error(nox_session, tmp_path): # use of the folder with errors in the nox -s package:check function with pytest.raises(CommandFailed) as e: - with patch("exasol.toolbox.nox._package.PROJECT_CONFIG") as config: - config.root = package + with patch( + "exasol.toolbox.nox._package.PROJECT_CONFIG", + new=test_project_config_factory(), + ): package_check(nox_session) # verify broken with non-zero exit status assert str(e.value) == "Returned code 1" From dc5a0dda66978d2e27ec526f4fd6122d31e4357e Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 11:34:58 +0100 Subject: [PATCH 10/31] Fix .doc places to .documentation_path --- exasol/toolbox/nox/_documentation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exasol/toolbox/nox/_documentation.py b/exasol/toolbox/nox/_documentation.py index 21b0e67c3..6ad994e40 100644 --- a/exasol/toolbox/nox/_documentation.py +++ b/exasol/toolbox/nox/_documentation.py @@ -25,7 +25,7 @@ def _build_docs(session: nox.Session, config: Config) -> None: "-W", "-b", "html", - f"{config.doc}", + f"{config.documentation_path}", DOCS_OUTPUT_DIR, ) @@ -33,7 +33,7 @@ def _build_docs(session: nox.Session, config: Config) -> None: def _build_multiversion_docs(session: nox.Session, config: Config) -> None: session.run( "sphinx-multiversion", - f"{config.doc}", + f"{config.documentation_path}", DOCS_OUTPUT_DIR, ) session.run("touch", f"{DOCS_OUTPUT_DIR}/.nojekyll") From cc3d212cfa1b49d0b4929c7f8a3e2cfba7d15def Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 11:36:49 +0100 Subject: [PATCH 11/31] Fix .source places to .source_code_path --- exasol/toolbox/nox/_artifacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exasol/toolbox/nox/_artifacts.py b/exasol/toolbox/nox/_artifacts.py index 067fdebee..ecd05f0b3 100644 --- a/exasol/toolbox/nox/_artifacts.py +++ b/exasol/toolbox/nox/_artifacts.py @@ -236,7 +236,7 @@ def _upload_to_sonar(session: Session, sonar_token: str | None, config: Config) "--sonar-python-version", ",".join(config.python_versions), "--sonar-sources", - config.source, + config.source_code_path, ] if Path(COVERAGE_XML).exists(): command.extend(["--sonar-python-coverage-report-paths", COVERAGE_XML]) From c245ac88171a3a2085073b04d28a67e819bd4ee1 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 11:38:06 +0100 Subject: [PATCH 12/31] Fix .version_file to .version_filepath --- exasol/toolbox/nox/_release.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exasol/toolbox/nox/_release.py b/exasol/toolbox/nox/_release.py index afc1b7c53..ff1ea16a9 100644 --- a/exasol/toolbox/nox/_release.py +++ b/exasol/toolbox/nox/_release.py @@ -144,7 +144,7 @@ def prepare_release(session: Session) -> None: changed_files += [ PROJECT_CONFIG.root_path / PoetryFiles.pyproject_toml, - PROJECT_CONFIG.version_file, + PROJECT_CONFIG.version_filepath, ] results = pm.hook.prepare_release_add_files(session=session, config=PROJECT_CONFIG) changed_files += [f for plugin_response in results for f in plugin_response] From abceb2c35a30beb0e62708e5153d483241b6483a Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 11:38:53 +0100 Subject: [PATCH 13/31] Relock dependencies for urllib3 --- poetry.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9dddbdb29..d9faf5b74 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3608,21 +3608,21 @@ files = [ [[package]] name = "urllib3" -version = "2.5.0" +version = "2.6.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, - {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, + {file = "urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f"}, + {file = "urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "virtualenv" From e2e16eb7991dd43b82f57b91f11aef458a61ee76 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 11:39:30 +0100 Subject: [PATCH 14/31] Bump other dependencies to latest --- poetry.lock | 217 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 152 insertions(+), 65 deletions(-) diff --git a/poetry.lock b/poetry.lock index d9faf5b74..96d019e4c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1550,6 +1550,92 @@ enabler = ["pytest-enabler (>=3.4)"] test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] type = ["pygobject-stubs", "pytest-mypy (>=1.0.1)", "shtab", "types-pywin32"] +[[package]] +name = "librt" +version = "0.7.3" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "librt-0.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2682162855a708e3270eba4b92026b93f8257c3e65278b456c77631faf0f4f7a"}, + {file = "librt-0.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:440c788f707c061d237c1e83edf6164ff19f5c0f823a3bf054e88804ebf971ec"}, + {file = "librt-0.7.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399938edbd3d78339f797d685142dd8a623dfaded023cf451033c85955e4838a"}, + {file = "librt-0.7.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1975eda520957c6e0eb52d12968dd3609ffb7eef05d4223d097893d6daf1d8a7"}, + {file = "librt-0.7.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9da128d0edf990cf0d2ca011b02cd6f639e79286774bd5b0351245cbb5a6e51"}, + {file = "librt-0.7.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19acfde38cb532a560b98f473adc741c941b7a9bc90f7294bc273d08becb58b"}, + {file = "librt-0.7.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7b4f57f7a0c65821c5441d98c47ff7c01d359b1e12328219709bdd97fdd37f90"}, + {file = "librt-0.7.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:256793988bff98040de23c57cf36e1f4c2f2dc3dcd17537cdac031d3b681db71"}, + {file = "librt-0.7.3-cp310-cp310-win32.whl", hash = "sha256:fcb72249ac4ea81a7baefcbff74df7029c3cb1cf01a711113fa052d563639c9c"}, + {file = "librt-0.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:4887c29cadbdc50640179e3861c276325ff2986791e6044f73136e6e798ff806"}, + {file = "librt-0.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:687403cced6a29590e6be6964463835315905221d797bc5c934a98750fe1a9af"}, + {file = "librt-0.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:24d70810f6e2ea853ff79338001533716b373cc0f63e2a0be5bc96129edb5fb5"}, + {file = "librt-0.7.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bf8c7735fbfc0754111f00edda35cf9e98a8d478de6c47b04eaa9cef4300eaa7"}, + {file = "librt-0.7.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32d43610dff472eab939f4d7fbdd240d1667794192690433672ae22d7af8445"}, + {file = "librt-0.7.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:adeaa886d607fb02563c1f625cf2ee58778a2567c0c109378da8f17ec3076ad7"}, + {file = "librt-0.7.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:572a24fc5958c61431da456a0ef1eeea6b4989d81eeb18b8e5f1f3077592200b"}, + {file = "librt-0.7.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6488e69d408b492e08bfb68f20c4a899a354b4386a446ecd490baff8d0862720"}, + {file = "librt-0.7.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ed028fc3d41adda916320712838aec289956c89b4f0a361ceadf83a53b4c047a"}, + {file = "librt-0.7.3-cp311-cp311-win32.whl", hash = "sha256:2cf9d73499486ce39eebbff5f42452518cc1f88d8b7ea4a711ab32962b176ee2"}, + {file = "librt-0.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:35f1609e3484a649bb80431310ddbec81114cd86648f1d9482bc72a3b86ded2e"}, + {file = "librt-0.7.3-cp311-cp311-win_arm64.whl", hash = "sha256:550fdbfbf5bba6a2960b27376ca76d6aaa2bd4b1a06c4255edd8520c306fcfc0"}, + {file = "librt-0.7.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fa9ac2e49a6bee56e47573a6786cb635e128a7b12a0dc7851090037c0d397a3"}, + {file = "librt-0.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e980cf1ed1a2420a6424e2ed884629cdead291686f1048810a817de07b5eb18"}, + {file = "librt-0.7.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e094e445c37c57e9ec612847812c301840239d34ccc5d153a982fa9814478c60"}, + {file = "librt-0.7.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aca73d70c3f553552ba9133d4a09e767dcfeee352d8d8d3eb3f77e38a3beb3ed"}, + {file = "librt-0.7.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c634a0a6db395fdaba0361aa78395597ee72c3aad651b9a307a3a7eaf5efd67e"}, + {file = "librt-0.7.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a59a69deeb458c858b8fea6acf9e2acd5d755d76cd81a655256bc65c20dfff5b"}, + {file = "librt-0.7.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d91e60ac44bbe3a77a67af4a4c13114cbe9f6d540337ce22f2c9eaf7454ca71f"}, + {file = "librt-0.7.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:703456146dc2bf430f7832fd1341adac5c893ec3c1430194fdcefba00012555c"}, + {file = "librt-0.7.3-cp312-cp312-win32.whl", hash = "sha256:b7c1239b64b70be7759554ad1a86288220bbb04d68518b527783c4ad3fb4f80b"}, + {file = "librt-0.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef59c938f72bdbc6ab52dc50f81d0637fde0f194b02d636987cea2ab30f8f55a"}, + {file = "librt-0.7.3-cp312-cp312-win_arm64.whl", hash = "sha256:ff21c554304e8226bf80c3a7754be27c6c3549a9fec563a03c06ee8f494da8fc"}, + {file = "librt-0.7.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56f2a47beda8409061bc1c865bef2d4bd9ff9255219402c0817e68ab5ad89aed"}, + {file = "librt-0.7.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14569ac5dd38cfccf0a14597a88038fb16811a6fede25c67b79c6d50fc2c8fdc"}, + {file = "librt-0.7.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6038ccbd5968325a5d6fd393cf6e00b622a8de545f0994b89dd0f748dcf3e19e"}, + {file = "librt-0.7.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d39079379a9a28e74f4d57dc6357fa310a1977b51ff12239d7271ec7e71d67f5"}, + {file = "librt-0.7.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8837d5a52a2d7aa9f4c3220a8484013aed1d8ad75240d9a75ede63709ef89055"}, + {file = "librt-0.7.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:399bbd7bcc1633c3e356ae274a1deb8781c7bf84d9c7962cc1ae0c6e87837292"}, + {file = "librt-0.7.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8d8cf653e798ee4c4e654062b633db36984a1572f68c3aa25e364a0ddfbbb910"}, + {file = "librt-0.7.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2f03484b54bf4ae80ab2e504a8d99d20d551bfe64a7ec91e218010b467d77093"}, + {file = "librt-0.7.3-cp313-cp313-win32.whl", hash = "sha256:44b3689b040df57f492e02cd4f0bacd1b42c5400e4b8048160c9d5e866de8abe"}, + {file = "librt-0.7.3-cp313-cp313-win_amd64.whl", hash = "sha256:6b407c23f16ccc36614c136251d6b32bf30de7a57f8e782378f1107be008ddb0"}, + {file = "librt-0.7.3-cp313-cp313-win_arm64.whl", hash = "sha256:abfc57cab3c53c4546aee31859ef06753bfc136c9d208129bad23e2eca39155a"}, + {file = "librt-0.7.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:120dd21d46ff875e849f1aae19346223cf15656be489242fe884036b23d39e93"}, + {file = "librt-0.7.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1617bea5ab31266e152871208502ee943cb349c224846928a1173c864261375e"}, + {file = "librt-0.7.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93b2a1f325fefa1482516ced160c8c7b4b8d53226763fa6c93d151fa25164207"}, + {file = "librt-0.7.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d4801db8354436fd3936531e7f0e4feb411f62433a6b6cb32bb416e20b529f"}, + {file = "librt-0.7.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11ad45122bbed42cfc8b0597450660126ef28fd2d9ae1a219bc5af8406f95678"}, + {file = "librt-0.7.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6b4e7bff1d76dd2b46443078519dc75df1b5e01562345f0bb740cea5266d8218"}, + {file = "librt-0.7.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:d86f94743a11873317094326456b23f8a5788bad9161fd2f0e52088c33564620"}, + {file = "librt-0.7.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:754a0d09997095ad764ccef050dd5bf26cbf457aab9effcba5890dad081d879e"}, + {file = "librt-0.7.3-cp314-cp314-win32.whl", hash = "sha256:fbd7351d43b80d9c64c3cfcb50008f786cc82cba0450e8599fdd64f264320bd3"}, + {file = "librt-0.7.3-cp314-cp314-win_amd64.whl", hash = "sha256:d376a35c6561e81d2590506804b428fc1075fcc6298fc5bb49b771534c0ba010"}, + {file = "librt-0.7.3-cp314-cp314-win_arm64.whl", hash = "sha256:cbdb3f337c88b43c3b49ca377731912c101178be91cb5071aac48faa898e6f8e"}, + {file = "librt-0.7.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9f0e0927efe87cd42ad600628e595a1a0aa1c64f6d0b55f7e6059079a428641a"}, + {file = "librt-0.7.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:020c6db391268bcc8ce75105cb572df8cb659a43fd347366aaa407c366e5117a"}, + {file = "librt-0.7.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7af7785f5edd1f418da09a8cdb9ec84b0213e23d597413e06525340bcce1ea4f"}, + {file = "librt-0.7.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ccadf260bb46a61b9c7e89e2218f6efea9f3eeaaab4e3d1f58571890e54858e"}, + {file = "librt-0.7.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9883b2d819ce83f87ba82a746c81d14ada78784db431e57cc9719179847376e"}, + {file = "librt-0.7.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:59cb0470612d21fa1efddfa0dd710756b50d9c7fb6c1236bbf8ef8529331dc70"}, + {file = "librt-0.7.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1fe603877e1865b5fd047a5e40379509a4a60204aa7aa0f72b16f7a41c3f0712"}, + {file = "librt-0.7.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5460d99ed30f043595bbdc888f542bad2caeb6226b01c33cda3ae444e8f82d42"}, + {file = "librt-0.7.3-cp314-cp314t-win32.whl", hash = "sha256:d09f677693328503c9e492e33e9601464297c01f9ebd966ea8fc5308f3069bfd"}, + {file = "librt-0.7.3-cp314-cp314t-win_amd64.whl", hash = "sha256:25711f364c64cab2c910a0247e90b51421e45dbc8910ceeb4eac97a9e132fc6f"}, + {file = "librt-0.7.3-cp314-cp314t-win_arm64.whl", hash = "sha256:a9f9b661f82693eb56beb0605156c7fca57f535704ab91837405913417d6990b"}, + {file = "librt-0.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd8551aa21df6c60baa2624fd086ae7486bdde00c44097b32e1d1b1966e365e0"}, + {file = "librt-0.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6eb9295c730e26b849ed1f4022735f36863eb46b14b6e10604c1c39b8b5efaea"}, + {file = "librt-0.7.3-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3edbf257c40d21a42615e9e332a6b10a8bacaaf58250aed8552a14a70efd0d65"}, + {file = "librt-0.7.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b29e97273bd6999e2bfe9fe3531b1f4f64effd28327bced048a33e49b99674a"}, + {file = "librt-0.7.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e40520c37926166c24d0c2e0f3bc3a5f46646c34bdf7b4ea9747c297d6ee809"}, + {file = "librt-0.7.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6bdd9adfca615903578d2060ee8a6eb1c24eaf54919ff0ddc820118e5718931b"}, + {file = "librt-0.7.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f57aca20e637750a2c18d979f7096e2c2033cc40cf7ed201494318de1182f135"}, + {file = "librt-0.7.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cad9971881e4fec00d96af7eaf4b63aa7a595696fc221808b0d3ce7ca9743258"}, + {file = "librt-0.7.3-cp39-cp39-win32.whl", hash = "sha256:170cdb8436188347af17bf9cccf3249ba581c933ed56d926497119d4cf730cec"}, + {file = "librt-0.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:b278a9248a4e3260fee3db7613772ca9ab6763a129d6d6f29555e2f9b168216d"}, + {file = "librt-0.7.3.tar.gz", hash = "sha256:3ec50cf65235ff5c02c5b747748d9222e564ad48597122a361269dd3aa808798"}, +] + [[package]] name = "license-expression" version = "30.4.4" @@ -1839,53 +1925,54 @@ files = [ [[package]] name = "mypy" -version = "1.18.2" +version = "1.19.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"}, - {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"}, - {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"}, - {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"}, - {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"}, - {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"}, - {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"}, - {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"}, - {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"}, - {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"}, - {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"}, - {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"}, - {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"}, - {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"}, - {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"}, - {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"}, - {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"}, - {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"}, - {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"}, - {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"}, - {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"}, - {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"}, - {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"}, - {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"}, - {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"}, - {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"}, - {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"}, -] - -[package.dependencies] + {file = "mypy-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6148ede033982a8c5ca1143de34c71836a09f105068aaa8b7d5edab2b053e6c8"}, + {file = "mypy-1.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a9ac09e52bb0f7fb912f5d2a783345c72441a08ef56ce3e17c1752af36340a39"}, + {file = "mypy-1.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f7254c15ab3f8ed68f8e8f5cbe88757848df793e31c36aaa4d4f9783fd08ab"}, + {file = "mypy-1.19.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318ba74f75899b0e78b847d8c50821e4c9637c79d9a59680fc1259f29338cb3e"}, + {file = "mypy-1.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf7d84f497f78b682edd407f14a7b6e1a2212b433eedb054e2081380b7395aa3"}, + {file = "mypy-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:c3385246593ac2b97f155a0e9639be906e73534630f663747c71908dfbf26134"}, + {file = "mypy-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a31e4c28e8ddb042c84c5e977e28a21195d086aaffaf08b016b78e19c9ef8106"}, + {file = "mypy-1.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34ec1ac66d31644f194b7c163d7f8b8434f1b49719d403a5d26c87fff7e913f7"}, + {file = "mypy-1.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb64b0ba5980466a0f3f9990d1c582bcab8db12e29815ecb57f1408d99b4bff7"}, + {file = "mypy-1.19.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:120cffe120cca5c23c03c77f84abc0c14c5d2e03736f6c312480020082f1994b"}, + {file = "mypy-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a500ab5c444268a70565e374fc803972bfd1f09545b13418a5174e29883dab7"}, + {file = "mypy-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:c14a98bc63fd867530e8ec82f217dae29d0550c86e70debc9667fff1ec83284e"}, + {file = "mypy-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fb3115cb8fa7c5f887c8a8d81ccdcb94cff334684980d847e5a62e926910e1d"}, + {file = "mypy-1.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3e19e3b897562276bb331074d64c076dbdd3e79213f36eed4e592272dabd760"}, + {file = "mypy-1.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9d491295825182fba01b6ffe2c6fe4e5a49dbf4e2bb4d1217b6ced3b4797bc6"}, + {file = "mypy-1.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2"}, + {file = "mypy-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431"}, + {file = "mypy-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:0e3c3d1e1d62e678c339e7ade72746a9e0325de42cd2cccc51616c7b2ed1a018"}, + {file = "mypy-1.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7686ed65dbabd24d20066f3115018d2dce030d8fa9db01aa9f0a59b6813e9f9e"}, + {file = "mypy-1.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd4a985b2e32f23bead72e2fb4bbe5d6aceee176be471243bd831d5b2644672d"}, + {file = "mypy-1.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc51a5b864f73a3a182584b1ac75c404396a17eced54341629d8bdcb644a5bba"}, + {file = "mypy-1.19.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37af5166f9475872034b56c5efdcf65ee25394e9e1d172907b84577120714364"}, + {file = "mypy-1.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:510c014b722308c9bd377993bcbf9a07d7e0692e5fa8fc70e639c1eb19fc6bee"}, + {file = "mypy-1.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:cabbee74f29aa9cd3b444ec2f1e4fa5a9d0d746ce7567a6a609e224429781f53"}, + {file = "mypy-1.19.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f2e36bed3c6d9b5f35d28b63ca4b727cb0228e480826ffc8953d1892ddc8999d"}, + {file = "mypy-1.19.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a18d8abdda14035c5718acb748faec09571432811af129bf0d9e7b2d6699bf18"}, + {file = "mypy-1.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75e60aca3723a23511948539b0d7ed514dda194bc3755eae0bfc7a6b4887aa7"}, + {file = "mypy-1.19.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f44f2ae3c58421ee05fe609160343c25f70e3967f6e32792b5a78006a9d850f"}, + {file = "mypy-1.19.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63ea6a00e4bd6822adbfc75b02ab3653a17c02c4347f5bb0cf1d5b9df3a05835"}, + {file = "mypy-1.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ad925b14a0bb99821ff6f734553294aa6a3440a8cb082fe1f5b84dfb662afb1"}, + {file = "mypy-1.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0dde5cb375cb94deff0d4b548b993bec52859d1651e073d63a1386d392a95495"}, + {file = "mypy-1.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1cf9c59398db1c68a134b0b5354a09a1e124523f00bacd68e553b8bd16ff3299"}, + {file = "mypy-1.19.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3210d87b30e6af9c8faed61be2642fcbe60ef77cec64fa1ef810a630a4cf671c"}, + {file = "mypy-1.19.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2c1101ab41d01303103ab6ef82cbbfedb81c1a060c868fa7cc013d573d37ab5"}, + {file = "mypy-1.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ea4fd21bb48f0da49e6d3b37ef6bd7e8228b9fe41bbf4d80d9364d11adbd43c"}, + {file = "mypy-1.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:16f76ff3f3fd8137aadf593cb4607d82634fca675e8211ad75c43d86033ee6c6"}, + {file = "mypy-1.19.0-py3-none-any.whl", hash = "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9"}, + {file = "mypy-1.19.0.tar.gz", hash = "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528"}, +] + +[package.dependencies] +librt = ">=0.6.2" mypy_extensions = ">=1.0.0" pathspec = ">=0.9.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -2475,14 +2562,14 @@ pygments = "*" [[package]] name = "pylint" -version = "4.0.3" +version = "4.0.4" description = "python code static checker" optional = false python-versions = ">=3.10.0" groups = ["main"] files = [ - {file = "pylint-4.0.3-py3-none-any.whl", hash = "sha256:896d09afb0e78bbf2e030cd1f3d8dc92771a51f7e46828cbc3948a89cd03433a"}, - {file = "pylint-4.0.3.tar.gz", hash = "sha256:a427fe76e0e5355e9fb9b604fd106c419cafb395886ba7f3cebebb03f30e081d"}, + {file = "pylint-4.0.4-py3-none-any.whl", hash = "sha256:63e06a37d5922555ee2c20963eb42559918c20bd2b21244e4ef426e7c43b92e0"}, + {file = "pylint-4.0.4.tar.gz", hash = "sha256:d9b71674e19b1c36d79265b5887bf8e55278cbe236c9e95d22dc82cf044fdbd2"}, ] [package.dependencies] @@ -2932,31 +3019,31 @@ files = [ [[package]] name = "ruff" -version = "0.14.6" +version = "0.14.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3"}, - {file = "ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004"}, - {file = "ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332"}, - {file = "ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef"}, - {file = "ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775"}, - {file = "ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce"}, - {file = "ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f"}, - {file = "ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d"}, - {file = "ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440"}, - {file = "ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105"}, - {file = "ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821"}, - {file = "ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55"}, - {file = "ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71"}, - {file = "ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b"}, - {file = "ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185"}, - {file = "ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85"}, - {file = "ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9"}, - {file = "ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2"}, - {file = "ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc"}, + {file = "ruff-0.14.8-py3-none-linux_armv6l.whl", hash = "sha256:ec071e9c82eca417f6111fd39f7043acb53cd3fde9b1f95bbed745962e345afb"}, + {file = "ruff-0.14.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8cdb162a7159f4ca36ce980a18c43d8f036966e7f73f866ac8f493b75e0c27e9"}, + {file = "ruff-0.14.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e2fcbefe91f9fad0916850edf0854530c15bd1926b6b779de47e9ab619ea38f"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d70721066a296f45786ec31916dc287b44040f553da21564de0ab4d45a869b"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c87e09b3cd9d126fc67a9ecd3b5b1d3ded2b9c7fce3f16e315346b9d05cfb52"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d62cb310c4fbcb9ee4ac023fe17f984ae1e12b8a4a02e3d21489f9a2a5f730c"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1af35c2d62633d4da0521178e8a2641c636d2a7153da0bac1b30cfd4ccd91344"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25add4575ffecc53d60eed3f24b1e934493631b48ebbc6ebaf9d8517924aca4b"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c943d847b7f02f7db4201a0600ea7d244d8a404fbb639b439e987edcf2baf9a"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb6e8bf7b4f627548daa1b69283dac5a296bfe9ce856703b03130732e20ddfe2"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7aaf2974f378e6b01d1e257c6948207aec6a9b5ba53fab23d0182efb887a0e4a"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e5758ca513c43ad8a4ef13f0f081f80f08008f410790f3611a21a92421ab045b"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f74f7ba163b6e85a8d81a590363bf71618847e5078d90827749bfda1d88c9cdf"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eed28f6fafcc9591994c42254f5a5c5ca40e69a30721d2ab18bb0bb3baac3ab6"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:21d48fa744c9d1cb8d71eb0a740c4dd02751a5de9db9a730a8ef75ca34cf138e"}, + {file = "ruff-0.14.8-py3-none-win32.whl", hash = "sha256:15f04cb45c051159baebb0f0037f404f1dc2f15a927418f29730f411a79bc4e7"}, + {file = "ruff-0.14.8-py3-none-win_amd64.whl", hash = "sha256:9eeb0b24242b5bbff3011409a739929f497f3fb5fe3b5698aba5e77e8c833097"}, + {file = "ruff-0.14.8-py3-none-win_arm64.whl", hash = "sha256:965a582c93c63fe715fd3e3f8aa37c4b776777203d8e1d8aa3cc0c14424a4b99"}, + {file = "ruff-0.14.8.tar.gz", hash = "sha256:774ed0dd87d6ce925e3b8496feb3a00ac564bea52b9feb551ecd17e0a23d1eed"}, ] [[package]] From b55266b772fb4d826f57b0bd9755c20be917fe44 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 11:45:49 +0100 Subject: [PATCH 15/31] Fix incomplete import to include exasol. --- test/unit/conftest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/conftest.py b/test/unit/conftest.py index c75d2b361..ba45a241e 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -1,5 +1,6 @@ import pytest -from toolbox.config import BaseConfig + +from exasol.toolbox.config import BaseConfig @pytest.fixture From 22b9212c790d8a6f176c5fcfbe7c8f292e039469 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 11:50:49 +0100 Subject: [PATCH 16/31] Add changelog entry --- doc/changes/unreleased.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index 183612f36..820b184d4 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -10,3 +10,9 @@ and replaces them with `format:fix` and `format:check`. ## Feature * #614: Replaced `path_filters` with `BaseConfig.add_to_excluded_python_paths` and `BaseConfig.excluded_python_paths` +* #621: Moved path specifications into `BaseConfig` + * `root` is now `root_path`, which must be specified by the project + * `source` is now covered by `project_name`, which must be specified by the project, + and `source_code_path`, which uses `root_path` and `project_name` + * `doc` is now `documentation_path` and no longer needs to be specified + * `version_file` is now `version_filepath` and no longer needs to be specified From dec4607b6f70128ad625293219a529aa69c7064e Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 11:53:19 +0100 Subject: [PATCH 17/31] Update user guide --- doc/user_guide/features/metrics/sonar.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/user_guide/features/metrics/sonar.rst b/doc/user_guide/features/metrics/sonar.rst index 4a95ced71..e838243e6 100644 --- a/doc/user_guide/features/metrics/sonar.rst +++ b/doc/user_guide/features/metrics/sonar.rst @@ -44,10 +44,7 @@ In Sonar In the code """"""""""" -#. Specify in the ``noxconfig.py`` the relative path to the project's source code in ``Config.source`` - .. code-block:: python - - source: Path = Path("exasol/") +#. In the ``noxconfig.py``, the relative path to the project's source code is defined with ``Config.source_code_path``. #. Add the following to the project's file ``pyproject.toml`` .. code-block:: toml @@ -55,7 +52,7 @@ In the code projectKey = "" host.url = "https://sonarcloud.io" organization = "exasol" - exclusions = "/version.py,//*" + exclusions = "/version.py,//*" .. note:: For more information, see the :ref:`General remarks ` section. From b5c4da074eac2a25c3648c8c8f1c10725452822d Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 13:08:00 +0100 Subject: [PATCH 18/31] Make sonar_code_path to make more explicit --- doc/user_guide/features/metrics/sonar.rst | 4 ++-- exasol/toolbox/config.py | 8 ++++++++ exasol/toolbox/nox/_artifacts.py | 7 +++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/doc/user_guide/features/metrics/sonar.rst b/doc/user_guide/features/metrics/sonar.rst index e838243e6..ca24ee4bb 100644 --- a/doc/user_guide/features/metrics/sonar.rst +++ b/doc/user_guide/features/metrics/sonar.rst @@ -44,7 +44,7 @@ In Sonar In the code """"""""""" -#. In the ``noxconfig.py``, the relative path to the project's source code is defined with ``Config.source_code_path``. +#. In the ``noxconfig.py``, the relative path to the project's source code is defined with ``Config.sonar_code_path``. #. Add the following to the project's file ``pyproject.toml`` .. code-block:: toml @@ -127,5 +127,5 @@ project from Sonar's analysis: See the `Sonar Matching Patterns`_ for more details. By default, the nox session ``sonar:check`` only analyses the source code, -as specified by the ``PROJECT_CONFIG.source_code_path``, so directories outside of this +as specified by the ``PROJECT_CONFIG.sonar_code_path``, so directories outside of this are already excluded from being analyzed. diff --git a/exasol/toolbox/config.py b/exasol/toolbox/config.py index e031eb0c2..2ef6e7e68 100644 --- a/exasol/toolbox/config.py +++ b/exasol/toolbox/config.py @@ -126,6 +126,14 @@ def pyupgrade_argument(self) -> tuple[str, ...]: version_number = "".join(version_parts) return (f"--py{version_number}-plus",) + @computed_field # type: ignore[misc] + @property + def sonar_code_path(self) -> Path: + """ + Relative path needed in nox session `sonar:check` to create the coverage XML + """ + return self.source_code_path.relative_to(self.root_path) + @computed_field # type: ignore[misc] @property def source_code_path(self) -> Path: diff --git a/exasol/toolbox/nox/_artifacts.py b/exasol/toolbox/nox/_artifacts.py index ecd05f0b3..9ecbf3013 100644 --- a/exasol/toolbox/nox/_artifacts.py +++ b/exasol/toolbox/nox/_artifacts.py @@ -207,7 +207,10 @@ def _prepare_coverage_xml( COVERAGE_XML, "--include", f"{source}/*", - "--fail-under=0", + "--fail-under", + "0", + "--data-file", + ".coverage", ] output = subprocess.run(command, capture_output=True, text=True, cwd=cwd) # nosec if output.returncode != 0: @@ -247,5 +250,5 @@ def _upload_to_sonar(session: Session, sonar_token: str | None, config: Config) def upload_artifacts_to_sonar(session: Session) -> None: """Upload artifacts to sonar for analysis""" sonar_token = os.getenv("SONAR_TOKEN") - _prepare_coverage_xml(session, PROJECT_CONFIG.source_code_path) + _prepare_coverage_xml(session, PROJECT_CONFIG.sonar_code_path) _upload_to_sonar(session, sonar_token, PROJECT_CONFIG) From f6c029a76cd04a62c3e66611d7c74c94f2351cb1 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 14:26:03 +0100 Subject: [PATCH 19/31] Move and rename _documentation_test to correct directory structure --- test/unit/{documentation_test.py => nox/_documentation_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/unit/{documentation_test.py => nox/_documentation_test.py} (100%) diff --git a/test/unit/documentation_test.py b/test/unit/nox/_documentation_test.py similarity index 100% rename from test/unit/documentation_test.py rename to test/unit/nox/_documentation_test.py From 4b4f056a13e53ec509d596d5c13b8112461b129d Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 14:30:59 +0100 Subject: [PATCH 20/31] Simplify test --- test/unit/nox/_documentation_test.py | 97 ++++++++++++++-------------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/test/unit/nox/_documentation_test.py b/test/unit/nox/_documentation_test.py index 79aab7e31..fa14e2c87 100644 --- a/test/unit/nox/_documentation_test.py +++ b/test/unit/nox/_documentation_test.py @@ -12,81 +12,78 @@ from noxconfig import PROJECT_CONFIG -@pytest.fixture() -def file1(): - return """ -https://examle.invalid -:ref:`Test`""" +@pytest.fixture +def index(config): + index_rst = config.documentation_path / "index.rst" + text = """ + .. _Test: + Test + ____test_docs_links -@pytest.fixture() -def index(): - return """.. _Test: + .. toctree:: + :maxdepth: 1 + :hidden: -Test -____ + file + """ + index_rst.write_text(text) -.. toctree:: - :maxdepth: 1 - :hidden: - file""" +@pytest.fixture +def config(test_project_config_factory): + config = test_project_config_factory() + # set up required file for Sphinx + doc_path = config.documentation_path + doc_path.mkdir(parents=True, exist_ok=True) + shutil.copyfile(PROJECT_CONFIG.documentation_path / "conf.py", doc_path / "conf.py") -@pytest.fixture() -def expected1(): - return """filename: file.rst:2 -> uri: https://examle.invalid""" + return config -def config(index, file, tmp_path): - test_doc = tmp_path / "doc" - test_doc.mkdir() - (test_doc / "_static").mkdir() - shutil.copyfile(PROJECT_CONFIG.documentation_path / "conf.py", test_doc / "conf.py") - rst_index = test_doc / "index.rst" - rst_file1 = test_doc / "file.rst" - rst_index.touch() - rst_file1.touch() - rst_index.write_text(index) - rst_file1.write_text(file) +@pytest.fixture +def set_up_doc_with_link(config, index): + dummy_rst = config.documentation_path / "dummy.rst" + dummy_rst.write_text("https://examle.invalid\n:ref:`Test`") -def test_docs_links(index, file1, expected1, tmp_path): - config(index, file1, tmp_path) - r_code, text = _docs_list_links(tmp_path / "doc") - assert (text == expected1) and not r_code +def test_docs_links(config, set_up_doc_with_link): + r_code, text = _docs_list_links(config.documentation_path) + + assert not r_code + assert text == """filename: dummy.rst:1 -> uri: https://examle.invalid""" @pytest.mark.parametrize( - "file2, expected2", + "file_content, expected_code, expected_message", [ - ("https://github.com/exasol/python-toolbox", (0, "")), + ("https://github.com/exasol/python-toolbox", 0, ""), ( "http://nox.thea.codes/en/stable/", - ( - 0, - "file.rst:1: [redirected with Found] http://nox.thea.codes/en/stable/ to https://nox.thea.codes/en/stable/\n", - ), + 0, + "[redirected with Found] http://nox.thea.codes/en/stable/ to https://nox.thea.codes/en/stable/\n", ), ( "https://github.com/exasol/python-toolbox/pull", - ( - 0, - "file.rst:1: [redirected permanently] https://github.com/exasol/python-toolbox/pull to https://github.com/exasol/python-toolbox/pulls\n", - ), + 0, + "[redirected permanently] https://github.com/exasol/python-toolbox/pull to https://github.com/exasol/python-toolbox/pulls\n", ), ( "https://github.com/exasol/python-toolbox/asdf", - ( - 1, - "file.rst:1: [broken] https://github.com/exasol/python-toolbox/asdf: 404 Client Error: Not Found for url: https://github.com/exasol/python-toolbox/asdf\n", - ), + 1, + "[broken] https://github.com/exasol/python-toolbox/asdf: 404 Client Error: Not Found for url: https://github.com/exasol/python-toolbox/asdf\n", ), ], ) -def test_docs_links_check(index, file2, expected2, tmp_path): - config(index, file2, tmp_path) +def test_docs_links_check(config, index, file_content, expected_code, expected_message): + dummy_rst = config.documentation_path / "dummy.rst" + dummy_rst.write_text(file_content) + args = MagicMock args.output = None - actual = _docs_links_check(tmp_path / "doc", args) - assert actual == expected2 + + code, message = _docs_links_check(config.documentation_path, args) + + assert code == expected_code + assert expected_message in message From b27148368c93f7f728d239bbfc6252cb0a2ffffe Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 15:02:31 +0100 Subject: [PATCH 21/31] Switch to testing nox session --- test/unit/nox/_documentation_test.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/test/unit/nox/_documentation_test.py b/test/unit/nox/_documentation_test.py index fa14e2c87..814729b17 100644 --- a/test/unit/nox/_documentation_test.py +++ b/test/unit/nox/_documentation_test.py @@ -1,13 +1,15 @@ import shutil from unittest.mock import ( MagicMock, + patch, ) import pytest +from nox.sessions import _SessionQuit from exasol.toolbox.nox._documentation import ( _docs_links_check, - _docs_list_links, + docs_list_links, ) from noxconfig import PROJECT_CONFIG @@ -48,11 +50,24 @@ def set_up_doc_with_link(config, index): dummy_rst.write_text("https://examle.invalid\n:ref:`Test`") -def test_docs_links(config, set_up_doc_with_link): - r_code, text = _docs_list_links(config.documentation_path) - - assert not r_code - assert text == """filename: dummy.rst:1 -> uri: https://examle.invalid""" +class TestDocsListLinks: + @staticmethod + def test_works_as_expected(nox_session, config, set_up_doc_with_link, capsys): + with patch("exasol.toolbox.nox._documentation.PROJECT_CONFIG", new=config): + docs_list_links(nox_session) + assert ( + capsys.readouterr().out + == "filename: dummy.rst:1 -> uri: https://examle.invalid\n" + ) + + @staticmethod + def test_raises_error_for_rcode_not_0(nox_session, config): + with patch("exasol.toolbox.nox._documentation.PROJECT_CONFIG", new=config): + with patch("exasol.toolbox.nox._documentation._docs_list_links") as mock: + mock.return_value = (1, "dummy_text") + + with pytest.raises(_SessionQuit): + docs_list_links(nox_session) @pytest.mark.parametrize( From cedec12b79288da5609223828e579c5bf1605157 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 15:10:59 +0100 Subject: [PATCH 22/31] Add test for link check nox session --- test/unit/nox/_documentation_test.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/unit/nox/_documentation_test.py b/test/unit/nox/_documentation_test.py index 814729b17..fcce98dc0 100644 --- a/test/unit/nox/_documentation_test.py +++ b/test/unit/nox/_documentation_test.py @@ -9,6 +9,7 @@ from exasol.toolbox.nox._documentation import ( _docs_links_check, + docs_links_check, docs_list_links, ) from noxconfig import PROJECT_CONFIG @@ -27,7 +28,7 @@ def index(config): :maxdepth: 1 :hidden: - file + dummy """ index_rst.write_text(text) @@ -102,3 +103,23 @@ def test_docs_links_check(config, index, file_content, expected_code, expected_m assert code == expected_code assert expected_message in message + + +class TestDocsLinksCheck: + @staticmethod + def test_works_as_expected_for_good_link(nox_session, config): + with patch("exasol.toolbox.nox._documentation.PROJECT_CONFIG", new=config): + with patch("exasol.toolbox.nox._documentation._docs_links_check") as mock: + mock.return_value = (0, "") + docs_links_check(nox_session) + + @staticmethod + def test_raises_exception_for_problem_returned(nox_session, config, capsys): + with patch("exasol.toolbox.nox._documentation.PROJECT_CONFIG", new=config): + with patch("exasol.toolbox.nox._documentation._docs_links_check") as mock: + mock.return_value = (0, "[broken] ....") + + with pytest.raises(_SessionQuit): + docs_links_check(nox_session) + + assert capsys.readouterr().out == "\x1b[31merrors:\n[broken] ....\n" From 94063b2ffa5599e81fdfc051f81627766ef5e2e9 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 15:16:49 +0100 Subject: [PATCH 23/31] Fix broken links --- doc/developer_guide/modules/nox_tasks.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/developer_guide/modules/nox_tasks.rst b/doc/developer_guide/modules/nox_tasks.rst index 77fe9794b..b930c8d75 100644 --- a/doc/developer_guide/modules/nox_tasks.rst +++ b/doc/developer_guide/modules/nox_tasks.rst @@ -4,12 +4,11 @@ nox_tasks lint:import (experimental) __________________________ -`Import Linter `_ +`Import Linter `_ allows you to define and enforce rules for the imports within and between Python packages. .. important:: First configure the linter in file :code:`.import_linter_config`, see - `import-linter top-level-configuration `_ - and `import-linter contract types `_ - + `import-linter top-level-configuration `_ + and `import-linter contract types `_ From 03e88bc658fed485694147a14f1c37607e77ca4c Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 16:54:45 +0100 Subject: [PATCH 24/31] Add test for default config check --- exasol/toolbox/config.py | 2 +- test/unit/config_test.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/exasol/toolbox/config.py b/exasol/toolbox/config.py index 2ef6e7e68..63cd76af8 100644 --- a/exasol/toolbox/config.py +++ b/exasol/toolbox/config.py @@ -110,7 +110,7 @@ def excluded_python_paths(self) -> tuple[str, ...]: this property is used to exclude such undesired paths. """ return tuple( - DEFAULT_EXCLUDED_PATHS.union(set(self.add_to_excluded_python_paths)) + sorted(DEFAULT_EXCLUDED_PATHS.union(set(self.add_to_excluded_python_paths))) ) @computed_field # type: ignore[misc] diff --git a/test/unit/config_test.py b/test/unit/config_test.py index d9e8d7213..705954809 100644 --- a/test/unit/config_test.py +++ b/test/unit/config_test.py @@ -1,3 +1,5 @@ +from pathlib import Path + import pytest from pydantic_core._pydantic_core import ValidationError @@ -11,7 +13,32 @@ class TestBaseConfig: @staticmethod def test_works_as_defined(test_project_config_factory): - test_project_config_factory() + config = test_project_config_factory() + + root_path = config.root_path + assert config.model_dump() == { + "add_to_excluded_python_paths": (), + "create_major_version_tags": False, + "documentation_path": root_path / "doc", + "exasol_versions": ("7.1.30", "8.29.6", "2025.1.0"), + "excluded_python_paths": ( + ".eggs", + ".html-documentation", + ".poetry", + ".sonar", + ".venv", + "dist", + "venv", + ), + "minimum_python_version": "3.10", + "project_name": "test", + "python_versions": ("3.10", "3.11", "3.12", "3.13", "3.14"), + "pyupgrade_argument": ("--py310-plus",), + "root_path": root_path, + "sonar_code_path": Path("exasol/test"), + "source_code_path": root_path / "exasol" / "test", + "version_filepath": root_path / "exasol" / "test" / "version.py", + } @staticmethod @pytest.mark.parametrize( From 582e1ee5cc87f449f3a83131e85102ed0aecb7d4 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 17:39:46 +0100 Subject: [PATCH 25/31] Add unit tests for tests:unit and tests:integration --- test/unit/nox/_test_test.py | 101 ++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 test/unit/nox/_test_test.py diff --git a/test/unit/nox/_test_test.py b/test/unit/nox/_test_test.py new file mode 100644 index 000000000..cea538df4 --- /dev/null +++ b/test/unit/nox/_test_test.py @@ -0,0 +1,101 @@ +import shutil +from unittest.mock import patch + +import pytest + +from exasol.toolbox.nox._test import ( + _test_command, + integration_tests, + unit_tests, +) +from noxconfig import PROJECT_CONFIG + + +@pytest.fixture +def default_context(): + """ + The default context comes from `toolbox/nox/_shared.py::_context` + """ + return {"db_version": "7.1.9", "coverage": False, "fwd-args": []} + + +class TestTestCommand: + @staticmethod + def test_works_for_default_arguments(test_project_config_factory, default_context): + config = test_project_config_factory() + + result = _test_command( + path=config.root_path / "dummy", config=config, context=default_context + ) + + assert result == ["pytest", "-v", f"{config.root_path}/dummy"] + + @staticmethod + def test_works_for_coverage_true(test_project_config_factory, default_context): + config = test_project_config_factory() + default_context["coverage"] = True + + result = _test_command( + path=config.root_path / "dummy", config=config, context=default_context + ) + + assert result == [ + "coverage", + "run", + "-a", + f"--rcfile={config.root_path}/pyproject.toml", + "-m", + "pytest", + "-v", + f"{config.root_path}/dummy", + ] + + @staticmethod + def test_works_for_fwd_args(test_project_config_factory, default_context): + config = test_project_config_factory() + default_context["fwd-args"] = ["--addition"] + + result = _test_command( + path=config.root_path / "dummy", config=config, context=default_context + ) + + assert result == ["pytest", "-v", f"{config.root_path}/dummy", "--addition"] + + +def test_unit_tests(nox_session, test_project_config_factory): + config = test_project_config_factory() + + shutil.copyfile( + PROJECT_CONFIG.root_path / "pyproject.toml", config.root_path / "pyproject.toml" + ) + + test_directory = config.root_path / "test" / "unit" + test_directory.mkdir(parents=True, exist_ok=True) + test_filepath = test_directory / "dummy_test.py" + test_filepath.write_text("def test_dummy():\n assert True") + + with patch("exasol.toolbox.nox._test.PROJECT_CONFIG", new=config): + unit_tests(nox_session) + + assert (test_directory / "__pycache__").exists() + + +class TestIntegrationTests: + @staticmethod + def test_works_without_hooks(nox_session, test_project_config_factory): + config = test_project_config_factory() + + shutil.copyfile( + PROJECT_CONFIG.root_path / "pyproject.toml", + config.root_path / "pyproject.toml", + ) + + test_directory = config.root_path / "test" / "integration" + test_directory.mkdir(parents=True, exist_ok=True) + test_filepath = test_directory / "dummy_test.py" + test_filepath.write_text("def test_dummy():\n assert True") + + with patch("exasol.toolbox.nox._test.PROJECT_CONFIG", new=config): + integration_tests(nox_session) + + assert (test_directory / "__pycache__").exists() From 9e677640892a6366eda711db0347df25e0a89d87 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Mon, 8 Dec 2025 17:40:21 +0100 Subject: [PATCH 26/31] Remove unused code --- exasol/toolbox/nox/_test.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/exasol/toolbox/nox/_test.py b/exasol/toolbox/nox/_test.py index 9081d3429..7beea1c42 100644 --- a/exasol/toolbox/nox/_test.py +++ b/exasol/toolbox/nox/_test.py @@ -62,13 +62,6 @@ def _integration_tests( pm.hook.post_integration_tests_hook(session=session, config=config, context=context) -def _pass( - _session: Session, _config: Config, _context: MutableMapping[str, Any] -) -> bool: - """No operation""" - return True - - def _coverage( session: Session, config: Config, context: MutableMapping[str, Any] ) -> None: From 618a64496e41acc55a167a6d92306a763668ec27 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Tue, 9 Dec 2025 10:36:16 +0100 Subject: [PATCH 27/31] Add explicit information in the Summary of the unreleased.md --- doc/changes/unreleased.md | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index 6fa82724c..580f9f917 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -1,8 +1,78 @@ # Unreleased +## Summary + This major release removes `project:fix` and `project:format` and replaces them with `format:fix` and `format:check`. +The `BaseConfig` has been extended to handle the commonly provided paths: +* `root` is now `root_path` +* `source` is now covered by `project_name` and `source_code_path`, which uses `root_path` and `project_name` +* `doc` is now `documentation_path` +* `version_file` is now `version_filepath` + +If your project was previously defining these values, your **before** would look like: + +```python +from __future__ import annotations + +from pathlib import Path +from typing import Iterable + +from exasol.toolbox.config import BaseConfig + + +class Config(BaseConfig): + root: Path = Path(__file__).parent + doc: Path = Path(__file__).parent / "doc" + source: Path = Path("exasol/{{cookiecutter.package_name}}") + version_file: Path = ( + Path(__file__).parent + / "exasol" + / "{{cookiecutter.package_name}}" + / "version.py" + ) + plugins: Iterable[object] = () + +PROJECT_CONFIG = Config() +``` + +With this major release, you **should modify** your project's `noxconfig.py` to look like: +```python +from __future__ import annotations + +from pathlib import Path +from typing import Iterable + +from exasol.toolbox.config import BaseConfig + + +class Config(BaseConfig): + plugins: Iterable[object] = () + +""" +These values do NOT need to be defined if your project follows the convention +expected from the PTB: +- documentation_path +- source_code_path +- version_filepath + +If your values differ, you can override these properties with the needed values when +you define `class Config(BaseConfig)`. We highly recommend that you create an issue +to remove this override in the future by aligning your project's structure with +that expected by the PTB. + +If you have additional Paths that used one of these values (i.e. `root_path`), then +you can define your own property in `class Config(BaseConfig)`, which accesses the +class values +""" + +PROJECT_CONFIG = Config( + project_name="{{cookiecutter.package_name}}", + root_path=Path(__file__).parent, +) +``` + ## Refactoring * #606: Renamed nox session `project:fix` more aptly to `format:fix` and `project:format` to `format:check` From 8c82930cdf23f33314c43913e41c03a64464458c Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Tue, 9 Dec 2025 13:06:02 +0100 Subject: [PATCH 28/31] Update unreleased file as no longer need a class Config --- doc/changes/unreleased.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index ff3755f27..65d330df3 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -42,15 +42,14 @@ With this major release, you **should modify** your project's `noxconfig.py` to from __future__ import annotations from pathlib import Path -from typing import Iterable from exasol.toolbox.config import BaseConfig - -class Config(BaseConfig): - plugins: Iterable[object] = () - """ +A class `Config` only needs to be defined if: +- you have custom attributes to pass to your project-defined nox sessions +- you need to override a convention in the PTB. + These values do NOT need to be defined if your project follows the convention expected from the PTB: - documentation_path @@ -66,8 +65,11 @@ If you have additional Paths that used one of these values (i.e. `root_path`), t you can define your own property in `class Config(BaseConfig)`, which accesses the class values """ +class Config(BaseConfig): + custom_field: str = "custom_field" -PROJECT_CONFIG = Config( +# For most projects, the PROJECT_CONFIG would look like: +PROJECT_CONFIG = BaseConfig( project_name="{{cookiecutter.package_name}}", root_path=Path(__file__).parent, ) From d18532615db909e8863b6e4245c78f563f8159df Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Tue, 9 Dec 2025 13:07:21 +0100 Subject: [PATCH 29/31] Update tests as in this branch BaseConfig has 2 required values, so we use test_project_config_factory instead --- test/unit/config_test.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/unit/config_test.py b/test/unit/config_test.py index 403c209bd..760c647e6 100644 --- a/test/unit/config_test.py +++ b/test/unit/config_test.py @@ -142,22 +142,26 @@ def prepare_release_update_version(self, session, config, version: Version) -> N class TestPlugins: @staticmethod - def test_works_when_empty(): - BaseConfig(plugins_for_nox_sessions=()) + def test_works_when_empty(test_project_config_factory): + test_project_config_factory(plugins_for_nox_sessions=()) @staticmethod - def test_works_for_hook(capsys): - BaseConfig(plugins_for_nox_sessions=(WithHook,)) + def test_works_for_hook(test_project_config_factory, capsys): + test_project_config_factory(plugins_for_nox_sessions=(WithHook,)) @staticmethod - def test_raises_exception_method_with_hook_not_specified(): + def test_raises_exception_method_with_hook_not_specified( + test_project_config_factory, + ): with pytest.raises(ValidationError) as ex: - BaseConfig(plugins_for_nox_sessions=(WithNotSpecifiedHook,)) + test_project_config_factory( + plugins_for_nox_sessions=(WithNotSpecifiedHook,) + ) assert "1 method(s) were decorated with `@hookimpl`, but" in str(ex.value) assert "('not_specified_anywhere',)" in str(ex.value) @staticmethod - def test_raises_exception_without_hook(): + def test_raises_exception_without_hook(test_project_config_factory): with pytest.raises(ValidationError) as ex: - BaseConfig(plugins_for_nox_sessions=(WithoutHook,)) + test_project_config_factory(plugins_for_nox_sessions=(WithoutHook,)) assert "No methods in `WithoutHook`" in str(ex.value) From 4e73b31aec267c610ab24e79799bd3280161cd52 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Tue, 9 Dec 2025 13:13:36 +0100 Subject: [PATCH 30/31] Switch Config to BaseConfig for type & fix minor typos --- exasol/toolbox/nox/_artifacts.py | 10 +++++----- exasol/toolbox/nox/_ci.py | 6 +++--- exasol/toolbox/nox/_documentation.py | 6 +++--- exasol/toolbox/nox/_format.py | 4 ++-- exasol/toolbox/nox/_package_version.py | 4 ++-- exasol/toolbox/nox/_shared.py | 4 ++-- exasol/toolbox/nox/_test.py | 10 +++++----- .../{{cookiecutter.repo_name}}/noxconfig.py | 3 --- test/unit/config_test.py | 1 + test/unit/nox/_artifacts_test.py | 6 ++---- test/unit/nox/_package_version_test.py | 4 ++-- 11 files changed, 27 insertions(+), 31 deletions(-) diff --git a/exasol/toolbox/nox/_artifacts.py b/exasol/toolbox/nox/_artifacts.py index 9ecbf3013..b4e933743 100644 --- a/exasol/toolbox/nox/_artifacts.py +++ b/exasol/toolbox/nox/_artifacts.py @@ -11,11 +11,9 @@ import nox from nox import Session +from exasol.toolbox.config import BaseConfig from exasol.toolbox.nox._shared import check_for_config_attribute -from noxconfig import ( - PROJECT_CONFIG, - Config, -) +from noxconfig import PROJECT_CONFIG COVERAGE_DB = ".coverage" COVERAGE_XML = "ci-coverage.xml" @@ -227,7 +225,9 @@ def _prepare_coverage_xml( session.error(output.returncode, output.stdout, output.stderr) -def _upload_to_sonar(session: Session, sonar_token: str | None, config: Config) -> None: +def _upload_to_sonar( + session: Session, sonar_token: str | None, config: BaseConfig +) -> None: command = [ "pysonar", "--sonar-token", diff --git a/exasol/toolbox/nox/_ci.py b/exasol/toolbox/nox/_ci.py index aab14fd35..da2f9d22d 100644 --- a/exasol/toolbox/nox/_ci.py +++ b/exasol/toolbox/nox/_ci.py @@ -4,21 +4,21 @@ import nox from nox import Session +from exasol.toolbox.config import BaseConfig from exasol.toolbox.nox._shared import check_for_config_attribute from noxconfig import ( PROJECT_CONFIG, - Config, ) _log = logging.getLogger(__name__) -def _python_matrix(config: Config): +def _python_matrix(config: BaseConfig): check_for_config_attribute(config=config, attribute="python_versions") return {"python-version": config.python_versions} -def _exasol_matrix(config: Config): +def _exasol_matrix(config: BaseConfig): check_for_config_attribute(config=config, attribute="exasol_versions") return {"exasol-version": config.exasol_versions} diff --git a/exasol/toolbox/nox/_documentation.py b/exasol/toolbox/nox/_documentation.py index 6ad994e40..4d4b106d7 100644 --- a/exasol/toolbox/nox/_documentation.py +++ b/exasol/toolbox/nox/_documentation.py @@ -12,14 +12,14 @@ import nox from nox import Session +from exasol.toolbox.config import BaseConfig from exasol.toolbox.nox._shared import DOCS_OUTPUT_DIR from noxconfig import ( PROJECT_CONFIG, - Config, ) -def _build_docs(session: nox.Session, config: Config) -> None: +def _build_docs(session: nox.Session, config: BaseConfig) -> None: session.run( "sphinx-build", "-W", @@ -30,7 +30,7 @@ def _build_docs(session: nox.Session, config: Config) -> None: ) -def _build_multiversion_docs(session: nox.Session, config: Config) -> None: +def _build_multiversion_docs(session: nox.Session, config: BaseConfig) -> None: session.run( "sphinx-multiversion", f"{config.documentation_path}", diff --git a/exasol/toolbox/nox/_format.py b/exasol/toolbox/nox/_format.py index 0ab5f2e36..94072f6dc 100644 --- a/exasol/toolbox/nox/_format.py +++ b/exasol/toolbox/nox/_format.py @@ -5,6 +5,7 @@ import nox from nox import Session +from exasol.toolbox.config import BaseConfig from exasol.toolbox.nox._shared import ( Mode, _version, @@ -13,7 +14,6 @@ ) from noxconfig import ( PROJECT_CONFIG, - Config, ) @@ -25,7 +25,7 @@ def command(*args: str) -> Iterable[str]: session.run(*command("black"), *files) -def _pyupgrade(session: Session, config: Config, files: Iterable[str]) -> None: +def _pyupgrade(session: Session, config: BaseConfig, files: Iterable[str]) -> None: check_for_config_attribute(config, "pyupgrade_argument") session.run( "pyupgrade", diff --git a/exasol/toolbox/nox/_package_version.py b/exasol/toolbox/nox/_package_version.py index 753c54982..d4710c309 100644 --- a/exasol/toolbox/nox/_package_version.py +++ b/exasol/toolbox/nox/_package_version.py @@ -9,10 +9,10 @@ import nox from nox import Session +from exasol.toolbox.config import BaseConfig from exasol.toolbox.util.version import Version from noxconfig import ( PROJECT_CONFIG, - Config, ) _SUCCESS = 0 @@ -70,7 +70,7 @@ def _create_parser() -> ArgumentParser: return parser -def _version_check(args: Namespace, config: Config) -> int: +def _version_check(args: Namespace, config: BaseConfig) -> int: version_file = config.version_filepath module_version = Version.from_python_module(version_file) diff --git a/exasol/toolbox/nox/_shared.py b/exasol/toolbox/nox/_shared.py index fb1989b81..6c05d9349 100644 --- a/exasol/toolbox/nox/_shared.py +++ b/exasol/toolbox/nox/_shared.py @@ -14,15 +14,15 @@ from nox import Session +from exasol.toolbox.config import BaseConfig from noxconfig import ( PROJECT_CONFIG, - Config, ) DOCS_OUTPUT_DIR = ".html-documentation" -def check_for_config_attribute(config: Config, attribute: str): +def check_for_config_attribute(config: BaseConfig, attribute: str): if not hasattr(config, attribute): raise AttributeError( "in the noxconfig.py file, the class Config should inherit " diff --git a/exasol/toolbox/nox/_test.py b/exasol/toolbox/nox/_test.py index 7beea1c42..10b08c0c8 100644 --- a/exasol/toolbox/nox/_test.py +++ b/exasol/toolbox/nox/_test.py @@ -10,16 +10,16 @@ import nox from nox import Session +from exasol.toolbox.config import BaseConfig from exasol.toolbox.nox._shared import _context from exasol.toolbox.nox.plugin import NoxTasks from noxconfig import ( PROJECT_CONFIG, - Config, ) def _test_command( - path: Path, config: Config, context: MutableMapping[str, Any] + path: Path, config: BaseConfig, context: MutableMapping[str, Any] ) -> Iterable[str]: coverage_command = ( [ @@ -37,14 +37,14 @@ def _test_command( def _unit_tests( - session: Session, config: Config, context: MutableMapping[str, Any] + session: Session, config: BaseConfig, context: MutableMapping[str, Any] ) -> None: command = _test_command(config.root_path / "test" / "unit", config, context) session.run(*command) def _integration_tests( - session: Session, config: Config, context: MutableMapping[str, Any] + session: Session, config: BaseConfig, context: MutableMapping[str, Any] ) -> None: pm = NoxTasks.plugin_manager(config) @@ -63,7 +63,7 @@ def _integration_tests( def _coverage( - session: Session, config: Config, context: MutableMapping[str, Any] + session: Session, config: BaseConfig, context: MutableMapping[str, Any] ) -> None: command = ["coverage", "report", "-m"] coverage_file = config.root_path / ".coverage" diff --git a/project-template/{{cookiecutter.repo_name}}/noxconfig.py b/project-template/{{cookiecutter.repo_name}}/noxconfig.py index dc0c92c12..00bc4b192 100644 --- a/project-template/{{cookiecutter.repo_name}}/noxconfig.py +++ b/project-template/{{cookiecutter.repo_name}}/noxconfig.py @@ -4,9 +4,6 @@ from exasol.toolbox.config import BaseConfig - - - PROJECT_CONFIG = BaseConfig( project_name="{{cookiecutter.package_name}}", root_path=Path(__file__).parent, diff --git a/test/unit/config_test.py b/test/unit/config_test.py index 760c647e6..6c9c0ecb2 100644 --- a/test/unit/config_test.py +++ b/test/unit/config_test.py @@ -33,6 +33,7 @@ def test_works_as_defined(test_project_config_factory): "venv", ), "minimum_python_version": "3.10", + "plugins_for_nox_sessions": (), "project_name": "test", "python_versions": ("3.10", "3.11", "3.12", "3.13", "3.14"), "pyupgrade_argument": ("--py310-plus",), diff --git a/test/unit/nox/_artifacts_test.py b/test/unit/nox/_artifacts_test.py index 18bf8bec3..28dc11cd7 100644 --- a/test/unit/nox/_artifacts_test.py +++ b/test/unit/nox/_artifacts_test.py @@ -17,6 +17,7 @@ _SessionQuit, ) +from exasol.toolbox.config import BaseConfig from exasol.toolbox.nox import _artifacts from exasol.toolbox.nox._artifacts import ( ALL_LINT_FILES, @@ -36,13 +37,10 @@ check_artifacts, copy_artifacts, ) -from noxconfig import Config @contextlib.contextmanager -def mock_check_artifacts_session( - config: Config, -): +def mock_check_artifacts_session(config: BaseConfig): with patch("exasol.toolbox.nox._artifacts.PROJECT_CONFIG", new=config): yield Mock() diff --git a/test/unit/nox/_package_version_test.py b/test/unit/nox/_package_version_test.py index e1091e17e..03f986f5d 100644 --- a/test/unit/nox/_package_version_test.py +++ b/test/unit/nox/_package_version_test.py @@ -2,20 +2,20 @@ import pytest +from exasol.toolbox.config import BaseConfig from exasol.toolbox.nox._package_version import ( _create_parser, _version_check, write_version_module, ) from exasol.toolbox.util.version import Version -from noxconfig import Config DEFAULT_VERSION = Version(major=0, minor=1, patch=0) ALTERNATE_VERSION = Version(major=0, minor=2, patch=0) @pytest.fixture -def config(test_project_config_factory) -> Config: +def config(test_project_config_factory) -> BaseConfig: config = test_project_config_factory() # We need to set up the directory path so that version_file can execute config.source_code_path.mkdir(parents=True, exist_ok=True) From 4c0ea95691a01fa3b7339db133933364666a4be2 Mon Sep 17 00:00:00 2001 From: Ariel Schulz Date: Tue, 9 Dec 2025 13:22:31 +0100 Subject: [PATCH 31/31] Prepare release 4.0.0 --- .github/actions/security-issues/action.yml | 2 +- doc/changes/changelog.md | 2 + doc/changes/changes_4.0.0.md | 102 ++++++++++++++++++ doc/changes/unreleased.md | 91 ---------------- .../github/workflows/build-and-publish.yml | 2 +- .../github/workflows/check-release-tag.yml | 2 +- .../templates/github/workflows/checks.yml | 18 ++-- .../templates/github/workflows/gh-pages.yml | 2 +- .../templates/github/workflows/matrix-all.yml | 2 +- .../github/workflows/matrix-exasol.yml | 2 +- .../github/workflows/matrix-python.yml | 2 +- .../templates/github/workflows/report.yml | 2 +- .../github/workflows/slow-checks.yml | 2 +- exasol/toolbox/version.py | 2 +- pyproject.toml | 2 +- 15 files changed, 124 insertions(+), 111 deletions(-) create mode 100644 doc/changes/changes_4.0.0.md diff --git a/.github/actions/security-issues/action.yml b/.github/actions/security-issues/action.yml index 547ec4161..e3e0fde5f 100644 --- a/.github/actions/security-issues/action.yml +++ b/.github/actions/security-issues/action.yml @@ -39,7 +39,7 @@ runs: - name: Install Python Toolbox / Security tool shell: bash run: | - pip install exasol-toolbox==3.0.0 + pip install exasol-toolbox==4.0.0 - name: Create Security Issue Report shell: bash diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md index 716656c62..dd7682b23 100644 --- a/doc/changes/changelog.md +++ b/doc/changes/changelog.md @@ -1,6 +1,7 @@ # Changelog * [unreleased](unreleased.md) +* [4.0.0](changes_4.0.0.md) * [3.0.0](changes_3.0.0.md) * [2.0.0](changes_2.0.0.md) * [1.13.0](changes_1.13.0.md) @@ -52,6 +53,7 @@ hidden: --- unreleased +changes_4.0.0 changes_3.0.0 changes_2.0.0 changes_1.13.0 diff --git a/doc/changes/changes_4.0.0.md b/doc/changes/changes_4.0.0.md new file mode 100644 index 000000000..68ca44004 --- /dev/null +++ b/doc/changes/changes_4.0.0.md @@ -0,0 +1,102 @@ +# 4.0.0 - 2025-12-09 + +## Summary + +This major release removes `project:fix` and `project:format` +and replaces them with `format:fix` and `format:check`. + +The `BaseConfig` has been extended to handle the commonly provided paths: +* `root` is now `root_path` +* `source` is now covered by `project_name` and `source_code_path`, which uses `root_path` and `project_name` +* `doc` is now `documentation_path` +* `version_file` is now `version_filepath` + +If your project was previously defining these values, your **before** would look like: + +```python +from __future__ import annotations + +from pathlib import Path +from typing import Iterable + +from exasol.toolbox.config import BaseConfig + + +class Config(BaseConfig): + root: Path = Path(__file__).parent + doc: Path = Path(__file__).parent / "doc" + source: Path = Path("exasol/{{cookiecutter.package_name}}") + version_file: Path = ( + Path(__file__).parent + / "exasol" + / "{{cookiecutter.package_name}}" + / "version.py" + ) + plugins: Iterable[object] = () + +PROJECT_CONFIG = Config() +``` + +With this major release, you **should modify** your project's `noxconfig.py` to look like: +```python +from __future__ import annotations + +from pathlib import Path + +from exasol.toolbox.config import BaseConfig + +""" +A class `Config` only needs to be defined if: +- you have custom attributes to pass to your project-defined nox sessions +- you need to override a convention in the PTB. + +These values do NOT need to be defined if your project follows the convention +expected from the PTB: +- documentation_path +- source_code_path +- version_filepath + +If your values differ, you can override these properties with the needed values when +you define `class Config(BaseConfig)`. We highly recommend that you create an issue +to remove this override in the future by aligning your project's structure with +that expected by the PTB. + +If you have additional Paths that used one of these values (i.e. `root_path`), then +you can define your own property in `class Config(BaseConfig)`, which accesses the +class values +""" +class Config(BaseConfig): + custom_field: str = "custom_field" + +# For most projects, the PROJECT_CONFIG would look like: +PROJECT_CONFIG = BaseConfig( + project_name="{{cookiecutter.package_name}}", + root_path=Path(__file__).parent, +) +``` + +## Refactoring + +* #606: Renamed nox session `project:fix` more aptly to `format:fix` and `project:format` to `format:check` +* #604: Updated `BaseConfig.exasol_versions` to `("7.1.30", "8.29.13", "2025.1.8")` + +## Feature + +* #614: Replaced `path_filters` with `BaseConfig.add_to_excluded_python_paths` and `BaseConfig.excluded_python_paths` +* #626: Replaced `plugins` with `BaseConfig.plugins_for_nox_sessions` +* #621: Moved path specifications into `BaseConfig` + * `root` is now `root_path`, which must be specified by the project + * `source` is now covered by `project_name`, which must be specified by the project, + and `source_code_path`, which uses `root_path` and `project_name` + * `doc` is now `documentation_path` and no longer needs to be specified + * `version_file` is now `version_filepath` and no longer needs to be specified + +## Dependency Updates + +### `main` +* Updated dependency `bandit:1.9.1` to `1.9.2` +* Updated dependency `mypy:1.18.2` to `1.19.0` +* Updated dependency `pre-commit:4.4.0` to `4.5.0` +* Updated dependency `pydantic:2.12.4` to `2.12.5` +* Updated dependency `pylint:4.0.3` to `4.0.4` +* Updated dependency `ruff:0.14.5` to `0.14.8` diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index 65d330df3..79e701b84 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -1,92 +1 @@ # Unreleased - -## Summary - -This major release removes `project:fix` and `project:format` -and replaces them with `format:fix` and `format:check`. - -The `BaseConfig` has been extended to handle the commonly provided paths: -* `root` is now `root_path` -* `source` is now covered by `project_name` and `source_code_path`, which uses `root_path` and `project_name` -* `doc` is now `documentation_path` -* `version_file` is now `version_filepath` - -If your project was previously defining these values, your **before** would look like: - -```python -from __future__ import annotations - -from pathlib import Path -from typing import Iterable - -from exasol.toolbox.config import BaseConfig - - -class Config(BaseConfig): - root: Path = Path(__file__).parent - doc: Path = Path(__file__).parent / "doc" - source: Path = Path("exasol/{{cookiecutter.package_name}}") - version_file: Path = ( - Path(__file__).parent - / "exasol" - / "{{cookiecutter.package_name}}" - / "version.py" - ) - plugins: Iterable[object] = () - -PROJECT_CONFIG = Config() -``` - -With this major release, you **should modify** your project's `noxconfig.py` to look like: -```python -from __future__ import annotations - -from pathlib import Path - -from exasol.toolbox.config import BaseConfig - -""" -A class `Config` only needs to be defined if: -- you have custom attributes to pass to your project-defined nox sessions -- you need to override a convention in the PTB. - -These values do NOT need to be defined if your project follows the convention -expected from the PTB: -- documentation_path -- source_code_path -- version_filepath - -If your values differ, you can override these properties with the needed values when -you define `class Config(BaseConfig)`. We highly recommend that you create an issue -to remove this override in the future by aligning your project's structure with -that expected by the PTB. - -If you have additional Paths that used one of these values (i.e. `root_path`), then -you can define your own property in `class Config(BaseConfig)`, which accesses the -class values -""" -class Config(BaseConfig): - custom_field: str = "custom_field" - -# For most projects, the PROJECT_CONFIG would look like: -PROJECT_CONFIG = BaseConfig( - project_name="{{cookiecutter.package_name}}", - root_path=Path(__file__).parent, -) -``` - -## Refactoring - -* #606: Renamed nox session `project:fix` more aptly to `format:fix` and `project:format` to `format:check` -* #604: Updated `BaseConfig.exasol_versions` to `("7.1.30", "8.29.13", "2025.1.8")` - -## Feature - -* #614: Replaced `path_filters` with `BaseConfig.add_to_excluded_python_paths` and `BaseConfig.excluded_python_paths` -* #626: Replaced `plugins` with `BaseConfig.plugins_for_nox_sessions` -* #621: Moved path specifications into `BaseConfig` - * `root` is now `root_path`, which must be specified by the project - * `source` is now covered by `project_name`, which must be specified by the project, - and `source_code_path`, which uses `root_path` and `project_name` - * `doc` is now `documentation_path` and no longer needs to be specified - * `version_file` is now `version_filepath` and no longer needs to be specified diff --git a/exasol/toolbox/templates/github/workflows/build-and-publish.yml b/exasol/toolbox/templates/github/workflows/build-and-publish.yml index 2b31c3a5b..62a678e62 100644 --- a/exasol/toolbox/templates/github/workflows/build-and-publish.yml +++ b/exasol/toolbox/templates/github/workflows/build-and-publish.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Build Artifacts run: poetry build diff --git a/exasol/toolbox/templates/github/workflows/check-release-tag.yml b/exasol/toolbox/templates/github/workflows/check-release-tag.yml index a88d32bbf..cb6585ad9 100644 --- a/exasol/toolbox/templates/github/workflows/check-release-tag.yml +++ b/exasol/toolbox/templates/github/workflows/check-release-tag.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Check Tag Version # make sure the pushed/created tag matched the project version diff --git a/exasol/toolbox/templates/github/workflows/checks.yml b/exasol/toolbox/templates/github/workflows/checks.yml index ea4ead6c2..0f767614d 100644 --- a/exasol/toolbox/templates/github/workflows/checks.yml +++ b/exasol/toolbox/templates/github/workflows/checks.yml @@ -16,7 +16,7 @@ jobs: fetch-depth: 0 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Check Version(s) run: poetry run -- nox -s version:check @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Build Documentation run: | @@ -59,7 +59,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Run changelog update check run: poetry run -- nox -s changelog:updated @@ -78,7 +78,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 with: python-version: ${{ matrix.python-version }} @@ -109,7 +109,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 with: python-version: ${{ matrix.python-version }} @@ -131,7 +131,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 with: python-version: ${{ matrix.python-version }} @@ -155,7 +155,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Run format check run: poetry run -- nox -s format:check @@ -171,7 +171,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Run Distribution Check run: poetry run -- nox -s package:check @@ -191,7 +191,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 with: python-version: ${{ matrix.python-version }} diff --git a/exasol/toolbox/templates/github/workflows/gh-pages.yml b/exasol/toolbox/templates/github/workflows/gh-pages.yml index e04f17a61..99788b3ee 100644 --- a/exasol/toolbox/templates/github/workflows/gh-pages.yml +++ b/exasol/toolbox/templates/github/workflows/gh-pages.yml @@ -17,7 +17,7 @@ jobs: fetch-depth: 0 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Build Documentation run: | diff --git a/exasol/toolbox/templates/github/workflows/matrix-all.yml b/exasol/toolbox/templates/github/workflows/matrix-all.yml index 085d72677..bfd34e602 100644 --- a/exasol/toolbox/templates/github/workflows/matrix-all.yml +++ b/exasol/toolbox/templates/github/workflows/matrix-all.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Generate matrix run: poetry run -- nox -s matrix:all diff --git a/exasol/toolbox/templates/github/workflows/matrix-exasol.yml b/exasol/toolbox/templates/github/workflows/matrix-exasol.yml index e5e5b4b9d..547b21bd8 100644 --- a/exasol/toolbox/templates/github/workflows/matrix-exasol.yml +++ b/exasol/toolbox/templates/github/workflows/matrix-exasol.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Generate matrix run: poetry run -- nox -s matrix:exasol diff --git a/exasol/toolbox/templates/github/workflows/matrix-python.yml b/exasol/toolbox/templates/github/workflows/matrix-python.yml index c1977c767..8d4fe663e 100644 --- a/exasol/toolbox/templates/github/workflows/matrix-python.yml +++ b/exasol/toolbox/templates/github/workflows/matrix-python.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Generate matrix run: poetry run -- nox -s matrix:python diff --git a/exasol/toolbox/templates/github/workflows/report.yml b/exasol/toolbox/templates/github/workflows/report.yml index 6e3699d73..8e2828ade 100644 --- a/exasol/toolbox/templates/github/workflows/report.yml +++ b/exasol/toolbox/templates/github/workflows/report.yml @@ -19,7 +19,7 @@ jobs: fetch-depth: 0 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 - name: Download Artifacts uses: actions/download-artifact@v6 diff --git a/exasol/toolbox/templates/github/workflows/slow-checks.yml b/exasol/toolbox/templates/github/workflows/slow-checks.yml index cc5cc956c..c9ba594c0 100644 --- a/exasol/toolbox/templates/github/workflows/slow-checks.yml +++ b/exasol/toolbox/templates/github/workflows/slow-checks.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Python & Poetry Environment - uses: exasol/python-toolbox/.github/actions/python-environment@v3 + uses: exasol/python-toolbox/.github/actions/python-environment@v4 with: python-version: ${{ matrix.python-version }} diff --git a/exasol/toolbox/version.py b/exasol/toolbox/version.py index 920a0196d..a6e1374f7 100644 --- a/exasol/toolbox/version.py +++ b/exasol/toolbox/version.py @@ -8,7 +8,7 @@ `poetry version X.Y.Z`. """ -MAJOR = 3 +MAJOR = 4 MINOR = 0 PATCH = 0 VERSION = f"{MAJOR}.{MINOR}.{PATCH}" diff --git a/pyproject.toml b/pyproject.toml index 4c0035fef..805387e57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "exasol-toolbox" -version = "3.0.0" +version = "4.0.0" requires-python = ">=3.10,<4.0" description = "Your one-stop solution for managing all standard tasks and core workflows of your Python project." authors = [