From ea45dd4fe28da6ea2eec03e324ee26b314561a52 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Thu, 30 Apr 2026 10:34:28 +0200 Subject: [PATCH] refactor(pytest_plugin): migrate session state to pytest.Stash --- .../+pytest-stash-migration.housekeeping.md | 1 + infrahub_sdk/pytest_plugin/_stash.py | 15 +++++++++++++++ infrahub_sdk/pytest_plugin/items/base.py | 5 ++--- infrahub_sdk/pytest_plugin/items/check.py | 3 ++- .../pytest_plugin/items/graphql_query.py | 5 +++-- .../pytest_plugin/items/jinja2_transform.py | 7 ++++--- .../pytest_plugin/items/python_transform.py | 5 +++-- infrahub_sdk/pytest_plugin/loader.py | 4 +++- infrahub_sdk/pytest_plugin/plugin.py | 18 ++++++++++-------- pyproject.toml | 2 +- uv.lock | 2 +- 11 files changed, 45 insertions(+), 22 deletions(-) create mode 100644 changelog/+pytest-stash-migration.housekeeping.md create mode 100644 infrahub_sdk/pytest_plugin/_stash.py diff --git a/changelog/+pytest-stash-migration.housekeeping.md b/changelog/+pytest-stash-migration.housekeeping.md new file mode 100644 index 00000000..53775bd6 --- /dev/null +++ b/changelog/+pytest-stash-migration.housekeeping.md @@ -0,0 +1 @@ +Migrate the pytest plugin's session state to `pytest.Stash`. Internal change with no impact on external test files; removes inline `# type: ignore[attr-defined]` comments from `infrahub_sdk/pytest_plugin/`. Adds a `pytest>=7.0` floor to the `all` extra to make the requirement explicit. diff --git a/infrahub_sdk/pytest_plugin/_stash.py b/infrahub_sdk/pytest_plugin/_stash.py new file mode 100644 index 00000000..617536a5 --- /dev/null +++ b/infrahub_sdk/pytest_plugin/_stash.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest + +if TYPE_CHECKING: + from .. import InfrahubClientSync + from ..schema.repository import InfrahubRepositoryConfig + + +INFRAHUB_CLIENT_KEY: pytest.StashKey[InfrahubClientSync] = pytest.StashKey() +INFRAHUB_CONFIG_PATH_KEY: pytest.StashKey[Path] = pytest.StashKey() +INFRAHUB_REPO_CONFIG_KEY: pytest.StashKey[InfrahubRepositoryConfig] = pytest.StashKey() diff --git a/infrahub_sdk/pytest_plugin/items/base.py b/infrahub_sdk/pytest_plugin/items/base.py index ae08f036..b2e48079 100644 --- a/infrahub_sdk/pytest_plugin/items/base.py +++ b/infrahub_sdk/pytest_plugin/items/base.py @@ -7,6 +7,7 @@ import pytest import ujson +from .._stash import INFRAHUB_CONFIG_PATH_KEY from ..exceptions import InvalidResourceConfigError from ..models import InfrahubInputOutputTest @@ -14,8 +15,6 @@ from ...schema.repository import InfrahubRepositoryConfigElement from ..models import InfrahubTest -_infrahub_config_path_attribute = "infrahub_config_path" - class InfrahubItem(pytest.Item): def __init__( @@ -78,7 +77,7 @@ def repository_base(self) -> str: This will be an absolute path if --infrahub-config-path is an absolute path as happens when tests are started from within Infrahub server. """ - config_path: Path = getattr(self.session, _infrahub_config_path_attribute) + config_path = self.session.stash[INFRAHUB_CONFIG_PATH_KEY] if config_path.is_absolute(): return str(config_path.parent) diff --git a/infrahub_sdk/pytest_plugin/items/check.py b/infrahub_sdk/pytest_plugin/items/check.py index 8f68b271..db6b6682 100644 --- a/infrahub_sdk/pytest_plugin/items/check.py +++ b/infrahub_sdk/pytest_plugin/items/check.py @@ -7,6 +7,7 @@ import ujson from httpx import HTTPStatusError +from .._stash import INFRAHUB_CLIENT_KEY from ..exceptions import CheckDefinitionError, CheckResultError from ..models import InfrahubTestExpectedResult from .base import InfrahubItem @@ -83,7 +84,7 @@ def runtest(self) -> None: class InfrahubCheckIntegrationItem(InfrahubCheckItem): def runtest(self) -> None: - input_data = self.session.infrahub_client.query_gql_query( # type: ignore[attr-defined] + input_data = self.session.stash[INFRAHUB_CLIENT_KEY].query_gql_query( self.check_instance.query, variables=self.test.spec.get_variables_data(), # type: ignore[union-attr] ) diff --git a/infrahub_sdk/pytest_plugin/items/graphql_query.py b/infrahub_sdk/pytest_plugin/items/graphql_query.py index bced5542..759db252 100644 --- a/infrahub_sdk/pytest_plugin/items/graphql_query.py +++ b/infrahub_sdk/pytest_plugin/items/graphql_query.py @@ -6,6 +6,7 @@ from httpx import HTTPStatusError from ...analyzer import GraphQLQueryAnalyzer +from .._stash import INFRAHUB_CLIENT_KEY, INFRAHUB_CONFIG_PATH_KEY from ..exceptions import OutputMatchError from ..models import InfrahubTestExpectedResult from .base import InfrahubItem @@ -20,7 +21,7 @@ def validate_resource_config(self) -> None: return def execute_query(self) -> Any: - return self.session.infrahub_client.query_gql_query( # type: ignore[attr-defined] + return self.session.stash[INFRAHUB_CLIENT_KEY].query_gql_query( self.test.spec.query, # type: ignore[union-attr] variables=self.test.spec.get_variables_data(), # type: ignore[union-attr] ) @@ -47,7 +48,7 @@ def repr_failure(self, excinfo: pytest.ExceptionInfo, style: str | None = None) class InfrahubGraphQLQuerySmokeItem(InfrahubGraphQLQueryItem): def runtest(self) -> None: - query = (self.session.infrahub_config_path.parent / self.test.spec.path).read_text() # type: ignore[attr-defined,union-attr] + query = (self.session.stash[INFRAHUB_CONFIG_PATH_KEY].parent / self.test.spec.path).read_text() # type: ignore[union-attr] GraphQLQueryAnalyzer(query) diff --git a/infrahub_sdk/pytest_plugin/items/jinja2_transform.py b/infrahub_sdk/pytest_plugin/items/jinja2_transform.py index fe54fd71..a2f03407 100644 --- a/infrahub_sdk/pytest_plugin/items/jinja2_transform.py +++ b/infrahub_sdk/pytest_plugin/items/jinja2_transform.py @@ -11,6 +11,7 @@ from ...template import Jinja2Template from ...template.exceptions import JinjaTemplateError +from .._stash import INFRAHUB_CLIENT_KEY, INFRAHUB_CONFIG_PATH_KEY from ..exceptions import OutputMatchError from ..models import InfrahubInputOutputTest, InfrahubTestExpectedResult from .base import InfrahubItem @@ -23,7 +24,7 @@ class InfrahubJinja2Item(InfrahubItem): def _get_jinja2(self) -> Jinja2Template: return Jinja2Template( template=Path(self.resource_config.template_path), # type: ignore[attr-defined] - template_directory=Path(self.session.infrahub_config_path.parent), # type: ignore[attr-defined] + template_directory=Path(self.session.stash[INFRAHUB_CONFIG_PATH_KEY].parent), ) def get_jinja2_environment(self) -> jinja2.Environment: @@ -82,7 +83,7 @@ def repr_failure(self, excinfo: pytest.ExceptionInfo, style: str | None = None) class InfrahubJinja2TransformSmokeItem(InfrahubJinja2Item): def runtest(self) -> None: - file_path: Path = self.session.infrahub_config_path.parent / self.resource_config.template_path # type: ignore[attr-defined] + file_path: Path = self.session.stash[INFRAHUB_CONFIG_PATH_KEY].parent / self.resource_config.template_path # type: ignore[attr-defined] self.get_jinja2_environment().parse(file_path.read_text(encoding="utf-8"), filename=file_path.name) @@ -103,7 +104,7 @@ def repr_failure(self, excinfo: pytest.ExceptionInfo, style: str | None = None) class InfrahubJinja2TransformIntegrationItem(InfrahubJinja2Item): def runtest(self) -> None: - graphql_result = self.session.infrahub_client.query_gql_query( # type: ignore[attr-defined] + graphql_result = self.session.stash[INFRAHUB_CLIENT_KEY].query_gql_query( self.resource_config.query, # type: ignore[attr-defined] variables=self.test.spec.get_variables_data(), # type: ignore[union-attr] ) diff --git a/infrahub_sdk/pytest_plugin/items/python_transform.py b/infrahub_sdk/pytest_plugin/items/python_transform.py index f895e971..5cb17b84 100644 --- a/infrahub_sdk/pytest_plugin/items/python_transform.py +++ b/infrahub_sdk/pytest_plugin/items/python_transform.py @@ -8,6 +8,7 @@ from httpx import HTTPStatusError from ...node import InfrahubNode +from .._stash import INFRAHUB_CLIENT_KEY from ..exceptions import OutputMatchError, PythonTransformDefinitionError from ..models import InfrahubTestExpectedResult from .base import InfrahubItem @@ -40,7 +41,7 @@ def instantiate_transform(self) -> None: transform_class = self.resource_config.load_class( # type: ignore[attr-defined] import_root=self.repository_base, relative_path=relative_path ) - client = self.session.infrahub_client # type: ignore[attr-defined] + client = self.session.stash[INFRAHUB_CLIENT_KEY] # TODO: Look into seeing how a transform class may use the branch, but set as a empty string for the time being to keep current behaviour self.transform_instance = transform_class(branch="", client=client, infrahub_node=InfrahubNode) @@ -90,7 +91,7 @@ def runtest(self) -> None: class InfrahubPythonTransformIntegrationItem(InfrahubPythonTransformItem): def runtest(self) -> None: self.instantiate_transform() - input_data = self.session.infrahub_client.query_gql_query( # type: ignore[attr-defined] + input_data = self.session.stash[INFRAHUB_CLIENT_KEY].query_gql_query( self.transform_instance.query, variables=self.test.spec.get_variables_data(), # type: ignore[union-attr] ) diff --git a/infrahub_sdk/pytest_plugin/loader.py b/infrahub_sdk/pytest_plugin/loader.py index 7ed0b889..c6847580 100644 --- a/infrahub_sdk/pytest_plugin/loader.py +++ b/infrahub_sdk/pytest_plugin/loader.py @@ -7,6 +7,7 @@ import pytest import yaml +from ._stash import INFRAHUB_REPO_CONFIG_KEY from .exceptions import InvalidResourceConfigError from .items import ( InfrahubCheckIntegrationItem, @@ -59,7 +60,8 @@ def get_resource_config(self, group: InfrahubTestGroup) -> Any | None: resource_config = None if resource_config_function is not None: - func = getattr(self.session.infrahub_repo_config, resource_config_function) # type:ignore[attr-defined] + repo_config = self.session.stash[INFRAHUB_REPO_CONFIG_KEY] + func = getattr(repo_config, resource_config_function) with contextlib.suppress(KeyError): resource_config = func(group.resource_name) diff --git a/infrahub_sdk/pytest_plugin/plugin.py b/infrahub_sdk/pytest_plugin/plugin.py index 258e7f9c..7d15e0c7 100644 --- a/infrahub_sdk/pytest_plugin/plugin.py +++ b/infrahub_sdk/pytest_plugin/plugin.py @@ -7,6 +7,7 @@ from .. import InfrahubClientSync from ..utils import is_valid_url +from ._stash import INFRAHUB_CLIENT_KEY, INFRAHUB_CONFIG_PATH_KEY, INFRAHUB_REPO_CONFIG_KEY from .loader import InfrahubYamlFile from .utils import find_repository_config_file, load_repository_config @@ -62,13 +63,15 @@ def pytest_addoption(parser: pytest.Parser) -> None: def pytest_sessionstart(session: pytest.Session) -> None: - if session.config.option.infrahub_repo_config: - session.infrahub_config_path = Path(session.config.option.infrahub_repo_config) # type: ignore[attr-defined] - else: - session.infrahub_config_path = find_repository_config_file() # type: ignore[attr-defined] + config_path = ( + Path(session.config.option.infrahub_repo_config) + if session.config.option.infrahub_repo_config + else find_repository_config_file() + ) + session.stash[INFRAHUB_CONFIG_PATH_KEY] = config_path - if session.infrahub_config_path.is_file(): # type: ignore[attr-defined] - session.infrahub_repo_config = load_repository_config(repo_config_file=session.infrahub_config_path) # type: ignore[attr-defined] + if config_path.is_file(): + session.stash[INFRAHUB_REPO_CONFIG_KEY] = load_repository_config(repo_config_file=config_path) if not is_valid_url(session.config.option.infrahub_address): pytest.exit("Infrahub test instance address is not a valid URL", returncode=1) @@ -84,8 +87,7 @@ def pytest_sessionstart(session: pytest.Session) -> None: client_config["username"] = session.config.option.infrahub_username client_config["password"] = session.config.option.infrahub_password - infrahub_client = InfrahubClientSync(config=client_config) - session.infrahub_client = infrahub_client # type: ignore[attr-defined] + session.stash[INFRAHUB_CLIENT_KEY] = InfrahubClientSync(config=client_config) def pytest_collect_file(parent: pytest.Collector | pytest.Item, file_path: Path) -> InfrahubYamlFile | None: diff --git a/pyproject.toml b/pyproject.toml index 4f20b96a..d322635a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,7 @@ all = [ "numpy>=1.24.2; python_version<'3.12'", "numpy>=1.26.2; python_version>='3.12'", "pyarrow>=14", - "pytest", + "pytest>=7.0", "pyyaml>=6", "rich>=12,<14", "typer>=0.12.5", diff --git a/uv.lock b/uv.lock index 9f0a5bde..91d54f87 100644 --- a/uv.lock +++ b/uv.lock @@ -817,7 +817,7 @@ requires-dist = [ { name = "pyarrow", marker = "extra == 'ctl'", specifier = ">=14" }, { name = "pydantic", specifier = ">=2.0.0,!=2.0.1,!=2.1.0,<3.0.0" }, { name = "pydantic-settings", specifier = ">=2.0" }, - { name = "pytest", marker = "extra == 'all'" }, + { name = "pytest", marker = "extra == 'all'", specifier = ">=7.0" }, { name = "pyyaml", marker = "extra == 'all'", specifier = ">=6" }, { name = "pyyaml", marker = "extra == 'ctl'", specifier = ">=6" }, { name = "rich", marker = "extra == 'all'", specifier = ">=12,<14" },