Skip to content

Commit 2c35dad

Browse files
authored
Refactoring/590 drop python 3.9 add 3.14 (#591)
* Drop support for Python 3.9 and add support for Python 3.14 * Remove unneeded code comment as done differently now * Remove defaults and add exception so that users switch to BaseConfig where exasol_versions & python_versions controlled This will only work in cases where these have not been set for a project & are used. * Remove defaults and instead use BaseConfig-inherited values * Add pyupgrade_argument to BaseConfig and use in pyupgrade command * Run project:fix on files with pyupgrade change * Relock dependencies from update * Try to pull python-version from matrix * Make uniform the workflow format * Switch create_major_version_tags to also be required to encourage BaseConfig * Add test for new check_for_config_attribute function * Updated changelog * Fix failing test due to mock missing value * Prepare release 2.0.0 * Fix failing critieria -> minimum_python_version was altering input value, leading to issues with artifact retrieval
1 parent d6a7971 commit 2c35dad

Some content is hidden

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

43 files changed

+420
-638
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==1.13.0
42+
pip install exasol-toolbox==2.0.0
4343
4444
- name: Create Security Issue Report
4545
shell: bash

.github/workflows/slow-checks.yml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ jobs:
4343

4444

4545
verify-poetry-installation:
46+
needs: [ build-matrix ]
4647
# This Job verifies if pipx installation is successful on each of the
4748
# selected GitHub Runners.
4849
strategy:
@@ -54,12 +55,8 @@ jobs:
5455
- int-linux-x64-4core-gpu-t4-ubuntu24.04-1
5556
- int-linux-x64-4core-ubuntu24.04-1
5657
- int-linux-x64-2core-ubuntu24.04-1
57-
python-version:
58-
- "3.10"
59-
- "3.11"
60-
- "3.12"
61-
- "3.13"
62-
name: Install Pipx on ${{ matrix.runner }} with Python "${{ matrix.python-version }}"
58+
python-version: ${{ fromJson(needs.build-matrix.outputs.matrix).python-version }}
59+
name: Install Pipx on ${{ matrix.runner }} (Python-${{ matrix.python-version }})
6360
runs-on:
6461
labels: ${{ matrix.runner }}
6562

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Your one-stop solution for managing all standard tasks and core workflows of you
4747
🔌️ Prerequisites
4848
-----------------
4949

50-
- `Python <https://www.python.org/>`__ >= 3.9
50+
- `Python <https://www.python.org/>`__ >= 3.10
5151

5252
💾 Installation
5353
---------------

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_2.0.0.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# 2.0.0 - 2025-11-04
2+
3+
With this release, all projects using the PTB must use has their project `Config` inherit
4+
from `BaseConfig` (introduced in [1.10.0](https://exasol.github.io/python-toolbox/main/changes/changes_1.10.0.html)). Otherwise, the workflows using these
5+
attributes will raise an exception indicating that this action is needed.
6+
7+
As Python 3.9 reached its EOL on 2025-10-31, the PTB no longer supports Python 3.9,
8+
and it has added support for 3.14. For project's that were still using Python 3.9,
9+
it is anticipated that there will be larger formatting change due to the arguments
10+
to `pyupgrade` changing.
11+
12+
## Refactoring
13+
14+
* #590:
15+
* Dropped support for Python 3.9 and added support for Python 3.14
16+
* Enforced that the `PROJECT_CONFIG` defined in `noxconfig.py` must be derived from `BaseConfig`.
17+
* Replaced `MINIMUM_PYTHON_VERSION` which acted as a back-up value for the nox session `artifacts:copy`
18+
with `BaseConfig.minimum_python_version_`
19+
* Replaced `_PYTHON_VERSIONS` which acted as a back-up value for the nox sessions `matrix:python` and `matrix:all`
20+
with `BaseConfig.python_versions_`
21+
* Replaced `__EXASOL_VERSIONS` which acted as a back-up value for the nox sessions `matrix:exasol` and `matrix:all`
22+
with `BaseConfig.python_versions_`
23+
* Moved `pyupgrade_args` from being defined per PROJECT_CONFIG to a calculated property
24+
`BaseConfig.pyupgrade_argument_`
25+
26+
## Dependency Updates
27+
28+
### `main`
29+
* Updated dependency `pysonar:1.2.0.2419` to `1.2.1.3951`
30+
* Updated dependency `shibuya:2025.10.21` to `2025.11.4`

doc/user_guide/dependencies.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ Dependencies
44
Core dependencies
55
+++++++++++++++++
66

7-
- Python >= 3.9
7+
- Python >= 3.10
88
- poetry >= 2.1.2
99
- `poetry export <https://github.com/python-poetry/poetry-plugin-export>`__

doc/user_guide/features/metrics/collecting_metrics.rst

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,7 @@ files and based on a selected external Python tool.
3131
+------------------------------------+-----------------------------+--------------+
3232

3333
These metrics are computed for each element in your build matrix, e.g. for each
34-
Python version defined in the file ``noxconfig.py``:
35-
36-
.. code-block:: python
37-
38-
@dataclass(frozen=True)
39-
class Config:
40-
python_versions = ["3.9", "3.10", "3.11", "3.12", "3.13"]
34+
Python version defined in the `PROJECT_CONFIG` of the ``noxconfig.py`` file.
4135

4236
The GitHub workflows of your project can:
4337

exasol/toolbox/config.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,7 @@ class BaseConfig(BaseModel):
3737
"""
3838

3939
python_versions: tuple[ValidVersionStr, ...] = Field(
40-
default=(
41-
"3.9",
42-
"3.10",
43-
"3.11",
44-
"3.12",
45-
"3.13",
46-
),
40+
default=("3.10", "3.11", "3.12", "3.13", "3.14"),
4741
description="Python versions to use in running CI workflows",
4842
)
4943

@@ -59,11 +53,27 @@ class BaseConfig(BaseModel):
5953

6054
@computed_field # type: ignore[misc]
6155
@property
62-
def min_py_version(self) -> str:
56+
def minimum_python_version(self) -> str:
6357
"""
6458
Minimum Python version declared from the `python_versions` list
6559
6660
This is used in specific testing scenarios where it would be either
6761
costly to run the tests for all `python_versions` or we need a single metric.
6862
"""
69-
return str(min([Version.from_string(v) for v in self.python_versions]))
63+
versioned = [Version.from_string(v) for v in self.python_versions]
64+
min_version = min(versioned)
65+
index_min_version = versioned.index(min_version)
66+
return self.python_versions[index_min_version]
67+
68+
@computed_field # type: ignore[misc]
69+
@property
70+
def pyupgrade_argument(self) -> tuple[str, ...]:
71+
"""
72+
Default argument to :func:`exasol.toolbox._format._pyupgrade`.
73+
74+
It uses the minimum Python version to ensure compatibility with all supported
75+
versions of a project.
76+
"""
77+
version_parts = self.minimum_python_version.split(".")[:2]
78+
version_number = "".join(version_parts)
79+
return (f"--py{version_number}-plus",)

exasol/toolbox/metrics.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import subprocess
55
import sys
66
from collections import defaultdict
7+
from collections.abc import Callable
78
from dataclasses import (
89
asdict,
910
dataclass,
@@ -18,7 +19,6 @@
1819
from tempfile import TemporaryDirectory
1920
from typing import (
2021
Any,
21-
Callable,
2222
Dict,
2323
List,
2424
Optional,
@@ -101,7 +101,7 @@ class Report:
101101
technical_debt: Rating
102102

103103

104-
def total_coverage(file: Union[str, Path]) -> float:
104+
def total_coverage(file: str | Path) -> float:
105105
with TemporaryDirectory() as tmpdir:
106106
tmp_dir = Path(tmpdir)
107107
report = tmp_dir / "coverage.json"
@@ -133,8 +133,8 @@ def total_coverage(file: Union[str, Path]) -> float:
133133
return total
134134

135135

136-
def _static_code_analysis(file: Union[str, Path]) -> Rating:
137-
def pylint(f: Union[str, Path]) -> Rating:
136+
def _static_code_analysis(file: str | Path) -> Rating:
137+
def pylint(f: str | Path) -> Rating:
138138
expr = re.compile(r"^Your code has been rated at (\d+.\d+)/.*", re.MULTILINE)
139139
with open(f, encoding="utf-8") as results:
140140
data = results.read()
@@ -154,15 +154,15 @@ def pylint(f: Union[str, Path]) -> Rating:
154154
return pylint_score
155155

156156

157-
def maintainability(file: Union[str, Path]) -> Rating:
157+
def maintainability(file: str | Path) -> Rating:
158158
return _static_code_analysis(file)
159159

160160

161161
def reliability() -> Rating:
162162
return Rating.NotAvailable
163163

164164

165-
def security(file: Union[str, Path]) -> Rating:
165+
def security(file: str | Path) -> Rating:
166166
with open(file) as json_file:
167167
security_lint = json.load(json_file)
168168
return Rating.bandit_rating(_bandit_scoring(security_lint["results"]))
@@ -198,10 +198,10 @@ def technical_debt() -> Rating:
198198

199199
def create_report(
200200
commit: str,
201-
date: Optional[datetime.datetime] = None,
202-
coverage_report: Union[str, Path] = ".coverage",
203-
pylint_report: Union[str, Path] = ".lint.txt",
204-
bandit_report: Union[str, Path] = ".security.json",
201+
date: datetime.datetime | None = None,
202+
coverage_report: str | Path = ".coverage",
203+
pylint_report: str | Path = ".lint.txt",
204+
bandit_report: str | Path = ".security.json",
205205
) -> Report:
206206
return Report(
207207
commit=commit,
@@ -254,7 +254,7 @@ def _rating_color(value: Rating) -> str:
254254

255255
@color.register(float)
256256
@color.register(int)
257-
def _coverage_color(value: Union[float, int]) -> str:
257+
def _coverage_color(value: float | int) -> str:
258258
if 0 <= value < 20:
259259
return _rating_color(Rating.F)
260260
elif 20 <= value < 50:

exasol/toolbox/nox/_artifacts.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import nox
1313
from nox import Session
1414

15-
from exasol.toolbox.nox._shared import MINIMUM_PYTHON_VERSION
15+
from exasol.toolbox.nox._shared import check_for_config_attribute
1616
from noxconfig import (
1717
PROJECT_CONFIG,
1818
Config,
@@ -159,8 +159,8 @@ def copy_artifacts(session: Session) -> None:
159159

160160

161161
def _python_version_suffix() -> str:
162-
versions = getattr(PROJECT_CONFIG, "python_versions", None)
163-
pivot = versions[0] if versions else MINIMUM_PYTHON_VERSION
162+
check_for_config_attribute(PROJECT_CONFIG, "minimum_python_version")
163+
pivot = PROJECT_CONFIG.minimum_python_version
164164
return f"-python{pivot}"
165165

166166

@@ -185,7 +185,7 @@ def _copy_artifacts(source: Path, dest: Path, files: Iterable[str]):
185185

186186

187187
def _prepare_coverage_xml(
188-
session: Session, source: Path, cwd: Optional[Path] = None
188+
session: Session, source: Path, cwd: Path | None = None
189189
) -> None:
190190
"""
191191
Prepare the coverage XML for input into Sonar
@@ -225,9 +225,7 @@ def _prepare_coverage_xml(
225225
session.error(output.returncode, output.stdout, output.stderr)
226226

227227

228-
def _upload_to_sonar(
229-
session: Session, sonar_token: Optional[str], config: Config
230-
) -> None:
228+
def _upload_to_sonar(session: Session, sonar_token: str | None, config: Config) -> None:
231229
command = [
232230
"pysonar",
233231
"--sonar-token",

0 commit comments

Comments
 (0)