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 a270c2cf7..79e701b84 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -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` diff --git a/doc/user_guide/features/metrics/sonar.rst b/doc/user_guide/features/metrics/sonar.rst index 77a767c29..ca24ee4bb 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.sonar_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. @@ -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. 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/config.py b/exasol/toolbox/config.py index c7cd0724e..4678e4c9b 100644 --- a/exasol/toolbox/config.py +++ b/exasol/toolbox/config.py @@ -1,5 +1,6 @@ import inspect from collections.abc import Callable +from pathlib import Path from typing import ( Annotated, Any, @@ -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", @@ -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: @@ -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] @@ -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" diff --git a/exasol/toolbox/nox/_artifacts.py b/exasol/toolbox/nox/_artifacts.py index 6707483f9..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" @@ -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) @@ -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: @@ -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", @@ -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]) @@ -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) 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 822af4f1d..4d4b106d7 100644 --- a/exasol/toolbox/nox/_documentation.py +++ b/exasol/toolbox/nox/_documentation.py @@ -12,28 +12,28 @@ 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", "-b", "html", - f"{config.doc}", + f"{config.documentation_path}", DOCS_OUTPUT_DIR, ) -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.doc}", + f"{config.documentation_path}", DOCS_OUTPUT_DIR, ) session.run("touch", f"{DOCS_OUTPUT_DIR}/.nojekyll") @@ -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..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", @@ -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..a601fbd63 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, ) @@ -123,28 +123,28 @@ 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) @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) @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) @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/_package_version.py b/exasol/toolbox/nox/_package_version.py index a1a04d471..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,8 +70,8 @@ def _create_parser() -> ArgumentParser: return parser -def _version_check(args: Namespace, config: Config) -> int: - version_file = config.version_file +def _version_check(args: Namespace, config: BaseConfig) -> int: + version_file = config.version_filepath module_version = Version.from_python_module(version_file) poetry_version = Version.from_poetry() diff --git a/exasol/toolbox/nox/_release.py b/exasol/toolbox/nox/_release.py index cf476a512..ff1ea16a9 100644 --- a/exasol/toolbox/nox/_release.py +++ b/exasol/toolbox/nox/_release.py @@ -127,8 +127,8 @@ def prepare_release(session: Session) -> None: _ = _update_project_version(session, new_version) changelogs = Changelogs( - changes_path=PROJECT_CONFIG.doc / "changes", - root_path=PROJECT_CONFIG.root, + changes_path=PROJECT_CONFIG.documentation_path / "changes", + root_path=PROJECT_CONFIG.root_path, version=new_version, ) changelogs.update_changelogs_for_release() @@ -143,8 +143,8 @@ def prepare_release(session: Session) -> None: return changed_files += [ - PROJECT_CONFIG.root / PoetryFiles.pyproject_toml, - PROJECT_CONFIG.version_file, + PROJECT_CONFIG.root_path / PoetryFiles.pyproject_toml, + 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] 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 d954fd4c0..10b08c0c8 100644 --- a/exasol/toolbox/nox/_test.py +++ b/exasol/toolbox/nox/_test.py @@ -10,19 +10,25 @@ 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 = ( - ["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 [] ) @@ -31,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 / "test" / "unit", config, context) + 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) @@ -49,25 +55,18 @@ 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 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] + session: Session, config: BaseConfig, 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/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/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/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"] 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/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/noxconfig.py b/noxconfig.py index 0c4453a3e..51ac4f73d 100644 --- a/noxconfig.py +++ b/noxconfig.py @@ -4,6 +4,8 @@ from pathlib import Path +from pydantic import computed_field + from exasol.toolbox.config import BaseConfig from exasol.toolbox.nox.plugin import hookimpl from exasol.toolbox.tools.replace_version import update_github_yml @@ -47,17 +49,25 @@ def prepare_release_add_files(self, session, config): # extra_data: list[str] = ["data"] -class Config(BaseConfig): - """Project specific configuration used by nox infrastructure""" +ROOT_PATH = Path(__file__).parent + - 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" +class Config(BaseConfig): + @computed_field # type: ignore[misc] + @property + def importlinter(self) -> Path: + """ + Path for the import lint configuration file. + This is an experimental feature that requires further scrutiny. This will + be done in: + https://github.com/exasol/python-toolbox/issues/628 + """ + return self.root_path / ".import_linter_config" PROJECT_CONFIG = Config( + root_path=ROOT_PATH, + 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 @@ -70,6 +80,6 @@ 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",), plugins_for_nox_sessions=(UpdateTemplates,), ) diff --git a/project-template/{{cookiecutter.repo_name}}/noxconfig.py b/project-template/{{cookiecutter.repo_name}}/noxconfig.py index adef5bcb2..00bc4b192 100644 --- a/project-template/{{cookiecutter.repo_name}}/noxconfig.py +++ b/project-template/{{cookiecutter.repo_name}}/noxconfig.py @@ -1,20 +1,10 @@ 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" - ) - -PROJECT_CONFIG = Config() +PROJECT_CONFIG = BaseConfig( + project_name="{{cookiecutter.package_name}}", + root_path=Path(__file__).parent, +) 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 = [ 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/config_test.py b/test/unit/config_test.py index 064d5d806..6c9c0ecb2 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 @@ -12,8 +14,34 @@ class TestBaseConfig: @staticmethod - def test_works_as_defined(): - BaseConfig() + def test_works_as_defined(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.13", "2025.1.8"), + "excluded_python_paths": ( + ".eggs", + ".html-documentation", + ".poetry", + ".sonar", + ".venv", + "dist", + "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",), + "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( @@ -60,14 +88,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",) @@ -85,8 +115,12 @@ 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) @@ -109,22 +143,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) diff --git a/test/unit/conftest.py b/test/unit/conftest.py new file mode 100644 index 000000000..ba45a241e --- /dev/null +++ b/test/unit/conftest.py @@ -0,0 +1,16 @@ +import pytest + +from exasol.toolbox.config import BaseConfig + + +@pytest.fixture +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/documentation_test.py b/test/unit/documentation_test.py deleted file mode 100644 index eab6915b6..000000000 --- a/test/unit/documentation_test.py +++ /dev/null @@ -1,92 +0,0 @@ -import shutil -from unittest.mock import ( - MagicMock, -) - -import pytest - -from exasol.toolbox.nox._documentation import ( - _docs_links_check, - _docs_list_links, -) -from noxconfig import PROJECT_CONFIG - - -@pytest.fixture() -def file1(): - return """ -https://examle.invalid -:ref:`Test`""" - - -@pytest.fixture() -def index(): - return """.. _Test: - -Test -____ - -.. toctree:: - :maxdepth: 1 - :hidden: - - file""" - - -@pytest.fixture() -def expected1(): - return """filename: file.rst:2 -> uri: https://examle.invalid""" - - -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") - 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) - - -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 - - -@pytest.mark.parametrize( - "file2, expected2", - [ - ("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", - ), - ), - ( - "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", - ), - ), - ( - "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", - ), - ), - ], -) -def test_docs_links_check(index, file2, expected2, tmp_path): - config(index, file2, tmp_path) - args = MagicMock - args.output = None - actual = _docs_links_check(tmp_path / "doc", args) - assert actual == expected2 diff --git a/test/unit/nox/_artifacts_test.py b/test/unit/nox/_artifacts_test.py index dbc5db623..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, @@ -39,11 +40,8 @@ @contextlib.contextmanager -def mock_check_artifacts_session( - path: Path, -): - with patch("exasol.toolbox.nox._artifacts.PROJECT_CONFIG") as config: - config.root = path +def mock_check_artifacts_session(config: BaseConfig): + with patch("exasol.toolbox.nox._artifacts.PROJECT_CONFIG", new=config): yield Mock() @@ -92,10 +90,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 +111,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/_documentation_test.py b/test/unit/nox/_documentation_test.py new file mode 100644 index 000000000..fcce98dc0 --- /dev/null +++ b/test/unit/nox/_documentation_test.py @@ -0,0 +1,125 @@ +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_links_check, + docs_list_links, +) +from noxconfig import PROJECT_CONFIG + + +@pytest.fixture +def index(config): + index_rst = config.documentation_path / "index.rst" + text = """ + .. _Test: + + Test + ____test_docs_links + + .. toctree:: + :maxdepth: 1 + :hidden: + + dummy + """ + index_rst.write_text(text) + + +@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") + + return config + + +@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`") + + +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( + "file_content, expected_code, expected_message", + [ + ("https://github.com/exasol/python-toolbox", 0, ""), + ( + "http://nox.thea.codes/en/stable/", + 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, + "[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, + "[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(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 + + code, message = _docs_links_check(config.documentation_path, args) + + 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" 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_check_test.py b/test/unit/nox/_package_check_test.py index 0a3c664a6..c1a86a264 100644 --- a/test/unit/nox/_package_check_test.py +++ b/test/unit/nox/_package_check_test.py @@ -19,17 +19,21 @@ 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" # 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 @@ -38,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" diff --git a/test/unit/nox/_package_version_test.py b/test/unit/nox/_package_version_test.py index 23bdf0b47..03f986f5d 100644 --- a/test/unit/nox/_package_version_test.py +++ b/test/unit/nox/_package_version_test.py @@ -2,26 +2,29 @@ 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(version_file) -> Config: - return Config(version_file=version_file) +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) + 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 8783ad399..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, attribute): - check_for_config_attribute(BaseConfig(), 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/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() diff --git a/test/unit/release_test.py b/test/unit/release_test.py index 17337e79b..242e4e31d 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,15 @@ 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_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(DummyConfig()) + 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"), 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)