Skip to content

Commit cdb8d47

Browse files
committed
Add simple validation check for decorator
1 parent 252a45a commit cdb8d47

File tree

3 files changed

+47
-1
lines changed

3 files changed

+47
-1
lines changed

exasol/toolbox/config.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import inspect
12
from typing import (
23
Annotated,
34
)
@@ -10,14 +11,30 @@
1011
computed_field,
1112
)
1213

14+
from exasol.toolbox.nox.plugin import PLUGIN_ATTR_NAME
1315
from exasol.toolbox.util.version import Version
1416

1517

18+
def validate_plugin_hook(plugin_class: object):
19+
"""
20+
Checks all methods in a class for at least one specific pluggy hookimpl marker.
21+
Raises ValueError if no methods are found with the marker.
22+
"""
23+
for name, method in inspect.getmembers(plugin_class, inspect.isroutine):
24+
if hasattr(method, PLUGIN_ATTR_NAME):
25+
return True
26+
raise ValueError(
27+
f"No methods in {plugin_class.__name__} were found to be decorated"
28+
"with `@hookimpl`"
29+
)
30+
31+
1632
def valid_version_string(version_string: str) -> str:
1733
Version.from_string(version_string)
1834
return version_string
1935

2036

37+
ValidPluginHook = Annotated[type[object], AfterValidator(validate_plugin_hook)]
2138
ValidVersionStr = Annotated[str, AfterValidator(valid_version_string)]
2239

2340
DEFAULT_EXCLUDED_PATHS = {
@@ -68,7 +85,7 @@ class BaseConfig(BaseModel):
6885
`exasol.toolbox.config.DEFAULT_EXCLUDED_PATHS`.
6986
""",
7087
)
71-
plugins: tuple[object, ...] = Field(
88+
plugins: tuple[ValidPluginHook, ...] = Field(
7289
default=(),
7390
description="""
7491
This is used to provide hooks to extend one or more of the Nox tasks provided

exasol/toolbox/nox/plugin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pluggy
22

33
_PLUGIN_MARKER = "python-toolbox-nox"
4+
PLUGIN_ATTR_NAME = f"{_PLUGIN_MARKER}_impl"
45
hookspec = pluggy.HookspecMarker("python-toolbox-nox")
56
hookimpl = pluggy.HookimplMarker("python-toolbox-nox")
67

test/unit/config_test.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
BaseConfig,
77
valid_version_string,
88
)
9+
from exasol.toolbox.nox.plugin import hookimpl
10+
from exasol.toolbox.util.version import Version
911

1012

1113
class TestBaseConfig:
@@ -86,3 +88,29 @@ def test_pyupgrade_argument(minimum_python_version):
8688
def test_excluded_python_paths(add_to_excluded_python_paths, expected):
8789
conf = BaseConfig(add_to_excluded_python_paths=add_to_excluded_python_paths)
8890
assert sorted(conf.excluded_python_paths) == sorted(expected)
91+
92+
93+
class WithHook:
94+
@hookimpl
95+
def prepare_release_update_version(self, session, config, version: Version) -> None:
96+
print("This is a simple, silly hook.")
97+
98+
99+
class WithoutHook:
100+
def prepare_release_update_version(self, session, config, version: Version) -> None:
101+
print("This is not a properly prepared hook.")
102+
103+
104+
class TestPlugins:
105+
@staticmethod
106+
def test_works_when_empty():
107+
BaseConfig(plugins=())
108+
109+
@staticmethod
110+
def test_works_for_hook(capsys):
111+
BaseConfig(plugins=(WithHook,))
112+
113+
@staticmethod
114+
def test_raises_exception_without_hook():
115+
with pytest.raises(ValueError, match="with `@hookimpl`"):
116+
BaseConfig(plugins=(WithoutHook,))

0 commit comments

Comments
 (0)