Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
23103c3
Move path-variables to BaseConfig
ArBridgeman Dec 1, 2025
0ed2830
Switch PROJECT_CONFIG.root with PROJECT_CONFIG.root_path
ArBridgeman Dec 1, 2025
8845fa9
Switch PROJECT_CONFIG.doc with PROJECT_CONFIG.documentation_path
ArBridgeman Dec 1, 2025
8140f46
Switch PROJECT_CONFIG.source with PROJECT_CONFIG.source_code_path
ArBridgeman Dec 1, 2025
1704aa2
Switch remainder over to BaseConfig usage
ArBridgeman Dec 1, 2025
9f49b8a
Fix tests by creating test_project_config
ArBridgeman Dec 1, 2025
0fdb113
Remove unused and unneeded additional templates for noxfconfig and no…
ArBridgeman Dec 5, 2025
f71df38
Add factory to create PROJECT_CONFIG equivalent for tests
ArBridgeman Dec 5, 2025
1aeb6c5
Fix a few more .root places to .root_path
ArBridgeman Dec 8, 2025
dc5a0dd
Fix .doc places to .documentation_path
ArBridgeman Dec 8, 2025
cc3d212
Fix .source places to .source_code_path
ArBridgeman Dec 8, 2025
c245ac8
Fix .version_file to .version_filepath
ArBridgeman Dec 8, 2025
abceb2c
Relock dependencies for urllib3
ArBridgeman Dec 8, 2025
e2e16eb
Bump other dependencies to latest
ArBridgeman Dec 8, 2025
b55266b
Fix incomplete import to include exasol.
ArBridgeman Dec 8, 2025
22b9212
Add changelog entry
ArBridgeman Dec 8, 2025
dec4607
Update user guide
ArBridgeman Dec 8, 2025
b5c4da0
Make sonar_code_path to make more explicit
ArBridgeman Dec 8, 2025
f6c029a
Move and rename _documentation_test to correct directory structure
ArBridgeman Dec 8, 2025
4b4f056
Simplify test
ArBridgeman Dec 8, 2025
b271483
Switch to testing nox session
ArBridgeman Dec 8, 2025
cedec12
Add test for link check nox session
ArBridgeman Dec 8, 2025
94063b2
Fix broken links
ArBridgeman Dec 8, 2025
03e88bc
Add test for default config check
ArBridgeman Dec 8, 2025
582e1ee
Add unit tests for tests:unit and tests:integration
ArBridgeman Dec 8, 2025
9e67764
Remove unused code
ArBridgeman Dec 8, 2025
f4eb816
Merge branch 'main' into feature/621_move_paths_into_baseconfig
ArBridgeman Dec 9, 2025
618a644
Add explicit information in the Summary of the unreleased.md
ArBridgeman Dec 9, 2025
c76203e
Merge branch 'main' into feature/621_move_paths_into_baseconfig
ArBridgeman Dec 9, 2025
8c82930
Update unreleased file as no longer need a class Config
ArBridgeman Dec 9, 2025
d185326
Update tests as in this branch BaseConfig has 2 required values, so w…
ArBridgeman Dec 9, 2025
4e73b31
Switch Config to BaseConfig for type & fix minor typos
ArBridgeman Dec 9, 2025
4c0ea95
Prepare release 4.0.0
ArBridgeman Dec 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/security-issues/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions doc/changes/changelog.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

102 changes: 102 additions & 0 deletions doc/changes/changes_4.0.0.md
Original file line number Diff line number Diff line change
@@ -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`
13 changes: 0 additions & 13 deletions doc/changes/unreleased.md
Original file line number Diff line number Diff line change
@@ -1,14 +1 @@
# Unreleased

This major release removes `project:fix` and `project:format`
and replaces them with `format:fix` and `format:check`.

## 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`
9 changes: 3 additions & 6 deletions doc/user_guide/features/metrics/sonar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,15 @@ 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/<source-directory>")
#. 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

[tool.sonar]
projectKey = "<sonar-project-key>"
host.url = "https://sonarcloud.io"
organization = "exasol"
exclusions = "<source-directory>/version.py,<source_directory>/<directory-to-ignore>/*"
exclusions = "<source_code_directory>/version.py,<source_code_directory>/<directory-to-ignore>/*"

.. note::
For more information, see the :ref:`General remarks <configuration_general_remarks>` section.
Expand Down Expand Up @@ -130,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``, 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.
2 changes: 1 addition & 1 deletion doc/user_guide/migrating.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
43 changes: 40 additions & 3 deletions exasol/toolbox/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import inspect
from collections.abc import Callable
from pathlib import Path
from typing import (
Annotated,
Any,
Expand Down Expand Up @@ -107,6 +108,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",
Expand Down Expand Up @@ -141,6 +144,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:
Expand All @@ -166,11 +177,11 @@ 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))
sorted(DEFAULT_EXCLUDED_PATHS.union(set(self.add_to_excluded_python_paths)))
)

@computed_field # type: ignore[misc]
Expand All @@ -185,3 +196,29 @@ 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 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:
"""
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"
29 changes: 16 additions & 13 deletions exasol/toolbox/nox/_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -45,16 +43,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)
Expand Down Expand Up @@ -207,7 +205,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:
Expand All @@ -224,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",
Expand All @@ -236,7 +239,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])
Expand All @@ -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)
_prepare_coverage_xml(session, PROJECT_CONFIG.sonar_code_path)
_upload_to_sonar(session, sonar_token, PROJECT_CONFIG)
6 changes: 3 additions & 3 deletions exasol/toolbox/nox/_ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down
Loading