Skip to content

Commit d7c33bb

Browse files
ckunkiArBridgeman
andauthored
Feature/#426 (#457)
* #426: Allowed configuring the python version used for coverage * Updated workflows and templates * Added user guide * Moved MINIMUM_PYTHON_VERSION to _shared.py Co-authored-by: Ariel Schulz <43442541+ArBridgeman@users.noreply.github.com>
1 parent b697249 commit d7c33bb

File tree

11 files changed

+187
-25
lines changed

11 files changed

+187
-25
lines changed

.github/workflows/report.yml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,7 @@ jobs:
2727
path: ./artifacts
2828

2929
- name: Copy Artifacts into Root Folder
30-
working-directory: ./artifacts
31-
run: |
32-
poetry run -- coverage combine --keep coverage-python3.9*/.coverage
33-
# Errors during copying are ignored because they are checked in the next step
34-
cp .coverage ../ || true
35-
cp lint-python3.9/.lint.txt ../ || true
36-
cp lint-python3.9/.lint.json ../ || true
37-
cp security-python3.9/.security.json ../ || true
30+
run: poetry run -- nox -s artifacts:copy -- artifacts
3831

3932
- name: Validate Artifacts
4033
run: poetry run -- nox -s artifacts:validate

doc/changes/unreleased.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
11
# Unreleased
2+
3+
## Summary
4+
5+
## ✨ Features
6+
7+
* #426: Allowed configuring the python version used for coverage
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Collecting Metrics
2+
==================
3+
4+
PTB allows you to collect various metrics on the quality of your project
5+
regarding Coverage, Security, and Static Code Analysis.
6+
7+
For each metric, there is a dedicated nox task, generating one or multiple
8+
files and based on a selected external Python tool.
9+
10+
+-----------------------------+-----------------------------+--------------+
11+
| Nox Task | Generated Files | Based on |
12+
+=============================+=============================+==============+
13+
| ``lint:code`` | ``lint.txt``, ``lint.json`` | ``pylint`` |
14+
+-----------------------------+-----------------------------+--------------+
15+
| ``lint:security`` | ``.security.json`` | ``bandit`` |
16+
+-----------------------------+-----------------------------+--------------+
17+
| ``test:unit -- --coverage`` | ``.coverage`` | ``coverage`` |
18+
+-----------------------------+-----------------------------+--------------+
19+
20+
The metrics are computed for each point in your build matrix, e.g. for each
21+
Python version defined in file ``noxconfig.py``:
22+
23+
.. code-block:: python
24+
25+
@dataclass(frozen=True)
26+
class Config:
27+
python_versions = ["3.9", "3.10", "3.11", "3.12", "3.13"]
28+
29+
The GitHub workflows of your project can:
30+
31+
* Use a build matrix, e.g. using different Python versions as shown above
32+
* Define multiple test sessions, e.g. for distinguishing fast vs. slow or expensive tests.
33+
34+
PTB combines the coverage data of all test sessions but using only the Python
35+
version named first in attribute ``python_versions`` of class ``Config``.

doc/user_guide/user_guide.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
customization
1313
migrating
1414
how_to_release
15+
collecting_metrics

exasol/toolbox/nox/_artifacts.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import json
22
import pathlib
33
import re
4+
import shutil
45
import sqlite3
56
import sys
7+
from collections.abc import Iterable
68
from pathlib import Path
79

810
import nox
911
from nox import Session
1012

13+
from exasol.toolbox.nox._shared import MINIMUM_PYTHON_VERSION
1114
from noxconfig import PROJECT_CONFIG
1215

1316

@@ -118,3 +121,49 @@ def _validate_coverage(path: Path) -> str:
118121
f"Invalid database, the database is missing the following tables {missing}"
119122
)
120123
return ""
124+
125+
126+
@nox.session(name="artifacts:copy", python=False)
127+
def copy_artifacts(session: Session) -> None:
128+
"""
129+
Copy artifacts to the current directory
130+
"""
131+
132+
dir = Path(session.posargs[0])
133+
suffix = _python_version_suffix()
134+
_combine_coverage(session, dir, f"coverage{suffix}*/.coverage")
135+
_copy_artifacts(
136+
dir,
137+
dir.parent,
138+
[
139+
f"lint{suffix}/.lint.txt",
140+
f"lint{suffix}/.lint.json",
141+
f"security{suffix}/.security.json",
142+
],
143+
)
144+
145+
146+
def _python_version_suffix() -> str:
147+
versions = getattr(PROJECT_CONFIG, "python_versions", None)
148+
pivot = versions[0] if versions else MINIMUM_PYTHON_VERSION
149+
return f"-python{pivot}"
150+
151+
152+
def _combine_coverage(session: Session, dir: Path, pattern: str):
153+
"""
154+
pattern: glob pattern, e.g. "*.coverage"
155+
"""
156+
if args := [f for f in dir.glob(pattern) if f.exists()]:
157+
session.run("coverage", "combine", "--keep", *sorted(args))
158+
else:
159+
print(f"Could not find any file {dir}/{pattern}", file=sys.stderr)
160+
161+
162+
def _copy_artifacts(source: Path, dest: Path, files: Iterable[str]):
163+
for file in files:
164+
path = source / file
165+
if path.exists():
166+
print(f"Copying file {path}", file=sys.stderr)
167+
shutil.copy(path, dest)
168+
else:
169+
print(f"File not found {path}", file=sys.stderr)

exasol/toolbox/nox/_shared.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
DEFAULT_PATH_FILTERS = {"dist", ".eggs", "venv", ".poetry"}
2121
DOCS_OUTPUT_DIR = ".html-documentation"
2222

23+
MINIMUM_PYTHON_VERSION = "3.9"
24+
2325

2426
class Mode(Enum):
2527
Fix = auto()

exasol/toolbox/templates/github/workflows/report.yml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,7 @@ jobs:
2727
path: ./artifacts
2828

2929
- name: Copy Artifacts into Root Folder
30-
working-directory: ./artifacts
31-
run: |
32-
poetry run -- coverage combine --keep coverage-python3.9*/.coverage
33-
# Errors during copying are ignored because they are checked in the next step
34-
cp .coverage ../ || true
35-
cp lint-python3.9/.lint.txt ../ || true
36-
cp lint-python3.9/.lint.json ../ || true
37-
cp security-python3.9/.security.json ../ || true
30+
run: poetry run -- nox -s artifacts:copy -- artifacts
3831

3932
- name: Validate Artifacts
4033
run: poetry run -- nox -s artifacts:validate

test/unit/artifacts_test.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import contextlib
2+
import re
3+
from dataclasses import dataclass
4+
from inspect import cleandoc
5+
from pathlib import Path
6+
from unittest.mock import (
7+
Mock,
8+
call,
9+
patch,
10+
)
11+
12+
import pytest
13+
14+
from exasol.toolbox.nox._artifacts import copy_artifacts
15+
16+
17+
@contextlib.contextmanager
18+
def mock_session(path: Path, python_version: str, *files: str):
19+
with patch("exasol.toolbox.nox._artifacts.PROJECT_CONFIG") as config:
20+
config.python_versions = [python_version]
21+
for rel in files:
22+
file = path / rel
23+
file.parent.mkdir(parents=True, exist_ok=True)
24+
file.write_text(rel)
25+
yield Mock(posargs=[str(path)])
26+
27+
28+
def test_missing_files(tmp_path, capsys):
29+
with mock_session(tmp_path, "9.9") as session:
30+
copy_artifacts(session)
31+
captured = capsys.readouterr()
32+
assert re.match(
33+
cleandoc(
34+
f"""
35+
Could not find any file .*/coverage-python9.9\\*/.coverage
36+
File not found .*/lint-python9.9/.lint.txt
37+
File not found .*/lint-python9.9/.lint.json
38+
File not found .*/security-python9.9/.security.json
39+
"""
40+
),
41+
captured.err,
42+
)
43+
44+
45+
@dataclass
46+
class endswith:
47+
"""
48+
Assert that the str representation of the argument ends with the
49+
specified suffix.
50+
"""
51+
52+
suffix: str
53+
54+
def __eq__(self, actual):
55+
return str(actual).endswith(self.suffix)
56+
57+
58+
def test_all_files(tmp_path, capsys):
59+
with mock_session(
60+
tmp_path / "artifacts",
61+
"9.9",
62+
"coverage-python9.9-fast/.coverage",
63+
"coverage-python9.9-slow/.coverage",
64+
"lint-python9.9/.lint.txt",
65+
"lint-python9.9/.lint.json",
66+
"security-python9.9/.security.json",
67+
) as session:
68+
copy_artifacts(session)
69+
70+
captured = capsys.readouterr()
71+
assert session.run.call_args == call(
72+
"coverage",
73+
"combine",
74+
"--keep",
75+
endswith("coverage-python9.9-fast/.coverage"),
76+
endswith("coverage-python9.9-slow/.coverage"),
77+
)
78+
assert re.match(
79+
cleandoc(
80+
f"""
81+
Copying file .*/lint-python9.9/.lint.txt
82+
Copying file .*/lint-python9.9/.lint.json
83+
Copying file .*/security-python9.9/.security.json
84+
"""
85+
),
86+
captured.err,
87+
)
88+
for f in [".lint.txt", ".lint.json", ".security.json"]:
89+
assert (tmp_path / f).exists()

test/unit/nox/_package_version_test.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
write_version_module,
99
)
1010
from exasol.toolbox.util.version import Version
11-
from noxconfig import (
12-
Config,
13-
)
11+
from noxconfig import Config
1412

1513
DEFAULT_VERSION = Version(major=0, minor=1, patch=0)
1614
ALTERNATE_VERSION = Version(major=0, minor=2, patch=0)

test/unit/util/dependencies/licenses_test.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
_packages_from_json,
88
packages_to_markdown,
99
)
10-
from exasol.toolbox.util.dependencies.poetry_dependencies import (
11-
PoetryGroup,
12-
)
10+
from exasol.toolbox.util.dependencies.poetry_dependencies import PoetryGroup
1311
from exasol.toolbox.util.dependencies.shared_models import Package
1412

1513
MAIN_GROUP = PoetryGroup(name="main", toml_section="project.dependencies")

0 commit comments

Comments
 (0)