Skip to content

Commit 076da8c

Browse files
authored
Feature/621 move paths into baseconfig (#623)
* Move path-variables to BaseConfig * Switch PROJECT_CONFIG.root with PROJECT_CONFIG.root_path * Switch PROJECT_CONFIG.doc with PROJECT_CONFIG.documentation_path * Switch PROJECT_CONFIG.source with PROJECT_CONFIG.source_code_path * Make sonar_code_path to make more explicit * Add unit tests for tests:unit and tests:integration * Prepare release 4.0.0
1 parent a6c4575 commit 076da8c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+674
-364
lines changed

.github/actions/security-issues/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ runs:
3939
- name: Install Python Toolbox / Security tool
4040
shell: bash
4141
run: |
42-
pip install exasol-toolbox==3.0.0
42+
pip install exasol-toolbox==4.0.0
4343
4444
- name: Create Security Issue Report
4545
shell: bash

doc/changes/changelog.md

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

doc/changes/changes_4.0.0.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# 4.0.0 - 2025-12-09
2+
3+
## Summary
4+
5+
This major release removes `project:fix` and `project:format`
6+
and replaces them with `format:fix` and `format:check`.
7+
8+
The `BaseConfig` has been extended to handle the commonly provided paths:
9+
* `root` is now `root_path`
10+
* `source` is now covered by `project_name` and `source_code_path`, which uses `root_path` and `project_name`
11+
* `doc` is now `documentation_path`
12+
* `version_file` is now `version_filepath`
13+
14+
If your project was previously defining these values, your **before** would look like:
15+
16+
```python
17+
from __future__ import annotations
18+
19+
from pathlib import Path
20+
from typing import Iterable
21+
22+
from exasol.toolbox.config import BaseConfig
23+
24+
25+
class Config(BaseConfig):
26+
root: Path = Path(__file__).parent
27+
doc: Path = Path(__file__).parent / "doc"
28+
source: Path = Path("exasol/{{cookiecutter.package_name}}")
29+
version_file: Path = (
30+
Path(__file__).parent
31+
/ "exasol"
32+
/ "{{cookiecutter.package_name}}"
33+
/ "version.py"
34+
)
35+
plugins: Iterable[object] = ()
36+
37+
PROJECT_CONFIG = Config()
38+
```
39+
40+
With this major release, you **should modify** your project's `noxconfig.py` to look like:
41+
```python
42+
from __future__ import annotations
43+
44+
from pathlib import Path
45+
46+
from exasol.toolbox.config import BaseConfig
47+
48+
"""
49+
A class `Config` only needs to be defined if:
50+
- you have custom attributes to pass to your project-defined nox sessions
51+
- you need to override a convention in the PTB.
52+
53+
These values do NOT need to be defined if your project follows the convention
54+
expected from the PTB:
55+
- documentation_path
56+
- source_code_path
57+
- version_filepath
58+
59+
If your values differ, you can override these properties with the needed values when
60+
you define `class Config(BaseConfig)`. We highly recommend that you create an issue
61+
to remove this override in the future by aligning your project's structure with
62+
that expected by the PTB.
63+
64+
If you have additional Paths that used one of these values (i.e. `root_path`), then
65+
you can define your own property in `class Config(BaseConfig)`, which accesses the
66+
class values
67+
"""
68+
class Config(BaseConfig):
69+
custom_field: str = "custom_field"
70+
71+
# For most projects, the PROJECT_CONFIG would look like:
72+
PROJECT_CONFIG = BaseConfig(
73+
project_name="{{cookiecutter.package_name}}",
74+
root_path=Path(__file__).parent,
75+
)
76+
```
77+
78+
## Refactoring
79+
80+
* #606: Renamed nox session `project:fix` more aptly to `format:fix` and `project:format` to `format:check`
81+
* #604: Updated `BaseConfig.exasol_versions` to `("7.1.30", "8.29.13", "2025.1.8")`
82+
83+
## Feature
84+
85+
* #614: Replaced `path_filters` with `BaseConfig.add_to_excluded_python_paths` and `BaseConfig.excluded_python_paths`
86+
* #626: Replaced `plugins` with `BaseConfig.plugins_for_nox_sessions`
87+
* #621: Moved path specifications into `BaseConfig`
88+
* `root` is now `root_path`, which must be specified by the project
89+
* `source` is now covered by `project_name`, which must be specified by the project,
90+
and `source_code_path`, which uses `root_path` and `project_name`
91+
* `doc` is now `documentation_path` and no longer needs to be specified
92+
* `version_file` is now `version_filepath` and no longer needs to be specified
93+
94+
## Dependency Updates
95+
96+
### `main`
97+
* Updated dependency `bandit:1.9.1` to `1.9.2`
98+
* Updated dependency `mypy:1.18.2` to `1.19.0`
99+
* Updated dependency `pre-commit:4.4.0` to `4.5.0`
100+
* Updated dependency `pydantic:2.12.4` to `2.12.5`
101+
* Updated dependency `pylint:4.0.3` to `4.0.4`
102+
* Updated dependency `ruff:0.14.5` to `0.14.8`

doc/changes/unreleased.md

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1 @@
11
# Unreleased
2-
3-
This major release removes `project:fix` and `project:format`
4-
and replaces them with `format:fix` and `format:check`.
5-
6-
## Refactoring
7-
8-
* #606: Renamed nox session `project:fix` more aptly to `format:fix` and `project:format` to `format:check`
9-
* #604: Updated `BaseConfig.exasol_versions` to `("7.1.30", "8.29.13", "2025.1.8")`
10-
11-
## Feature
12-
13-
* #614: Replaced `path_filters` with `BaseConfig.add_to_excluded_python_paths` and `BaseConfig.excluded_python_paths`
14-
* #626: Replaced `plugins` with `BaseConfig.plugins_for_nox_sessions`

doc/user_guide/features/metrics/sonar.rst

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,15 @@ In Sonar
4444

4545
In the code
4646
"""""""""""
47-
#. Specify in the ``noxconfig.py`` the relative path to the project's source code in ``Config.source``
48-
.. code-block:: python
49-
50-
source: Path = Path("exasol/<source-directory>")
47+
#. In the ``noxconfig.py``, the relative path to the project's source code is defined with ``Config.sonar_code_path``.
5148
#. Add the following to the project's file ``pyproject.toml``
5249
.. code-block:: toml
5350
5451
[tool.sonar]
5552
projectKey = "<sonar-project-key>"
5653
host.url = "https://sonarcloud.io"
5754
organization = "exasol"
58-
exclusions = "<source-directory>/version.py,<source_directory>/<directory-to-ignore>/*"
55+
exclusions = "<source_code_directory>/version.py,<source_code_directory>/<directory-to-ignore>/*"
5956
6057
.. note::
6158
For more information, see the :ref:`General remarks <configuration_general_remarks>` section.
@@ -130,5 +127,5 @@ project from Sonar's analysis:
130127
See the `Sonar Matching Patterns`_ for more details.
131128

132129
By default, the nox session ``sonar:check`` only analyses the source code,
133-
as specified by the ``PROJECT_CONFIG.source``, so directories outside of this
130+
as specified by the ``PROJECT_CONFIG.sonar_code_path``, so directories outside of this
134131
are already excluded from being analyzed.

doc/user_guide/migrating.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ For example, if test execution isn't performed in the standard way (e.g., :code:
7474
# within the function to keep them isolated and simplify future removal or replacement.
7575
from exasol.toolbox.nox._shared import get_filtered_python_files
7676
77-
py_files = get_filtered_python_files(PROJECT_CONFIG.root)
77+
py_files = get_filtered_python_files(PROJECT_CONFIG.root_path)
7878
print("The original 'format:fix' task has been taken hostage by this overwrite")
7979
print("Files:\n{files}".format(files="\n".join(py_files))
8080

exasol/toolbox/config.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import inspect
22
from collections.abc import Callable
3+
from pathlib import Path
34
from typing import (
45
Annotated,
56
Any,
@@ -107,6 +108,8 @@ class BaseConfig(BaseModel):
107108
runs.
108109
"""
109110

111+
project_name: str = Field(description="Name of the project")
112+
root_path: Path = Field(description="Root directory of the project")
110113
python_versions: tuple[ValidVersionStr, ...] = Field(
111114
default=("3.10", "3.11", "3.12", "3.13", "3.14"),
112115
description="Python versions to use in running CI workflows",
@@ -141,6 +144,14 @@ class BaseConfig(BaseModel):
141144
)
142145
model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)
143146

147+
@computed_field # type: ignore[misc]
148+
@property
149+
def documentation_path(self) -> Path:
150+
"""
151+
Path where the documentation for the project is stored.
152+
"""
153+
return self.root_path / "doc"
154+
144155
@computed_field # type: ignore[misc]
145156
@property
146157
def minimum_python_version(self) -> str:
@@ -166,11 +177,11 @@ def excluded_python_paths(self) -> tuple[str, ...]:
166177
- lint:security
167178
- lint:typing
168179
where it is desired to restrict which Python files are considered within the
169-
PROJECT_CONFIG.source path, like excluding `dist`, `.eggs`. As such, this
170-
property is used to exclude such undesired paths.
180+
PROJECT_CONFIG.source_code_path path, like excluding `dist`, `.eggs`. As such,
181+
this property is used to exclude such undesired paths.
171182
"""
172183
return tuple(
173-
DEFAULT_EXCLUDED_PATHS.union(set(self.add_to_excluded_python_paths))
184+
sorted(DEFAULT_EXCLUDED_PATHS.union(set(self.add_to_excluded_python_paths)))
174185
)
175186

176187
@computed_field # type: ignore[misc]
@@ -185,3 +196,29 @@ def pyupgrade_argument(self) -> tuple[str, ...]:
185196
version_parts = self.minimum_python_version.split(".")[:2]
186197
version_number = "".join(version_parts)
187198
return (f"--py{version_number}-plus",)
199+
200+
@computed_field # type: ignore[misc]
201+
@property
202+
def sonar_code_path(self) -> Path:
203+
"""
204+
Relative path needed in nox session `sonar:check` to create the coverage XML
205+
"""
206+
return self.source_code_path.relative_to(self.root_path)
207+
208+
@computed_field # type: ignore[misc]
209+
@property
210+
def source_code_path(self) -> Path:
211+
"""
212+
Path to the source code of the project.
213+
"""
214+
return self.root_path / "exasol" / self.project_name
215+
216+
@computed_field # type: ignore[misc]
217+
@property
218+
def version_filepath(self) -> Path:
219+
"""
220+
Path to the ``version.py`` file included in the project. This is an
221+
autogenerated file which contains the version of the code. It is maintained by
222+
the nox sessions ``version:check`` and ``release:prepare``.
223+
"""
224+
return self.source_code_path / "version.py"

exasol/toolbox/nox/_artifacts.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@
1111
import nox
1212
from nox import Session
1313

14+
from exasol.toolbox.config import BaseConfig
1415
from exasol.toolbox.nox._shared import check_for_config_attribute
15-
from noxconfig import (
16-
PROJECT_CONFIG,
17-
Config,
18-
)
16+
from noxconfig import PROJECT_CONFIG
1917

2018
COVERAGE_DB = ".coverage"
2119
COVERAGE_XML = "ci-coverage.xml"
@@ -45,16 +43,16 @@
4543
@nox.session(name="artifacts:validate", python=False)
4644
def check_artifacts(session: Session) -> None:
4745
"""Validate that all project artifacts are available and consistent"""
48-
all_files = {f.name for f in PROJECT_CONFIG.root.iterdir() if f.is_file()}
46+
all_files = {f.name for f in PROJECT_CONFIG.root_path.iterdir() if f.is_file()}
4947
if missing_files := (ALL_LINT_FILES - all_files):
5048
print(f"files not available: {missing_files}", file=sys.stderr)
5149
sys.exit(1)
5250

5351
all_is_valid_checks = [
54-
_is_valid_lint_txt(Path(PROJECT_CONFIG.root, LINT_TXT)),
55-
_is_valid_lint_json(Path(PROJECT_CONFIG.root, LINT_JSON)),
56-
_is_valid_security_json(Path(PROJECT_CONFIG.root, SECURITY_JSON)),
57-
_is_valid_coverage(Path(PROJECT_CONFIG.root, COVERAGE_DB)),
52+
_is_valid_lint_txt(Path(PROJECT_CONFIG.root_path, LINT_TXT)),
53+
_is_valid_lint_json(Path(PROJECT_CONFIG.root_path, LINT_JSON)),
54+
_is_valid_security_json(Path(PROJECT_CONFIG.root_path, SECURITY_JSON)),
55+
_is_valid_coverage(Path(PROJECT_CONFIG.root_path, COVERAGE_DB)),
5856
]
5957
if not all(all_is_valid_checks):
6058
sys.exit(1)
@@ -207,7 +205,10 @@ def _prepare_coverage_xml(
207205
COVERAGE_XML,
208206
"--include",
209207
f"{source}/*",
210-
"--fail-under=0",
208+
"--fail-under",
209+
"0",
210+
"--data-file",
211+
".coverage",
211212
]
212213
output = subprocess.run(command, capture_output=True, text=True, cwd=cwd) # nosec
213214
if output.returncode != 0:
@@ -224,7 +225,9 @@ def _prepare_coverage_xml(
224225
session.error(output.returncode, output.stdout, output.stderr)
225226

226227

227-
def _upload_to_sonar(session: Session, sonar_token: str | None, config: Config) -> None:
228+
def _upload_to_sonar(
229+
session: Session, sonar_token: str | None, config: BaseConfig
230+
) -> None:
228231
command = [
229232
"pysonar",
230233
"--sonar-token",
@@ -236,7 +239,7 @@ def _upload_to_sonar(session: Session, sonar_token: str | None, config: Config)
236239
"--sonar-python-version",
237240
",".join(config.python_versions),
238241
"--sonar-sources",
239-
config.source,
242+
config.source_code_path,
240243
]
241244
if Path(COVERAGE_XML).exists():
242245
command.extend(["--sonar-python-coverage-report-paths", COVERAGE_XML])
@@ -247,5 +250,5 @@ def _upload_to_sonar(session: Session, sonar_token: str | None, config: Config)
247250
def upload_artifacts_to_sonar(session: Session) -> None:
248251
"""Upload artifacts to sonar for analysis"""
249252
sonar_token = os.getenv("SONAR_TOKEN")
250-
_prepare_coverage_xml(session, PROJECT_CONFIG.source)
253+
_prepare_coverage_xml(session, PROJECT_CONFIG.sonar_code_path)
251254
_upload_to_sonar(session, sonar_token, PROJECT_CONFIG)

exasol/toolbox/nox/_ci.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@
44
import nox
55
from nox import Session
66

7+
from exasol.toolbox.config import BaseConfig
78
from exasol.toolbox.nox._shared import check_for_config_attribute
89
from noxconfig import (
910
PROJECT_CONFIG,
10-
Config,
1111
)
1212

1313
_log = logging.getLogger(__name__)
1414

1515

16-
def _python_matrix(config: Config):
16+
def _python_matrix(config: BaseConfig):
1717
check_for_config_attribute(config=config, attribute="python_versions")
1818
return {"python-version": config.python_versions}
1919

2020

21-
def _exasol_matrix(config: Config):
21+
def _exasol_matrix(config: BaseConfig):
2222
check_for_config_attribute(config=config, attribute="exasol_versions")
2323
return {"exasol-version": config.exasol_versions}
2424

0 commit comments

Comments
 (0)