Repository: pytest-dev/pluggy (python)

Issue:
Remove manual kwargs-only check?
A little trivial issue.

The hookcaller `__call__` (e.g. `pm.hook.myhook(...)`) requires keyword-only arguments. This is currently checked manually:

https://github.com/pytest-dev/pluggy/blob/172ca9f59e8f1029ec734647b38ce4e2c4d7dba2/src/pluggy/_hooks.py#L384-L386

and provides a nice exception message (though it could be nicer...).

An alternative is to make the function itself kwargs-only:

```py
    def __call__(self, **kwargs: object) -> Any:
```

then Python itself checks it. This provides a worse exception message: `_HookCaller.__call__() takes 1 positional argument but 2 were given` (it's still possible to find the line which has the problem from the stacktrace).

On the other hand, the Python kwargs-only approach has the better typing (i.e. mypy will be able to catch bad usages), and removes the slight overhead of the check (likely not significant though).

I'm inclined to make the change but wonder if others agree.

Pull request description:
Remove manual kwargs-only check in call
Fix #350.

Patch:
diff --git a/src/pluggy/_hooks.py b/src/pluggy/_hooks.py
index d38cece..8316115 100644
--- a/src/pluggy/_hooks.py
+++ b/src/pluggy/_hooks.py
@@ -381,9 +381,7 @@ class _HookCaller:
                     )
                     break
 
-    def __call__(self, *args: object, **kwargs: object) -> Any:
-        if args:
-            raise TypeError("hook calling supports only keyword arguments")
+    def __call__(self, **kwargs: object) -> Any:
         assert (
             not self.is_historic()
         ), "Cannot directly call a historic hook - use call_historic instead."


Test patch:
diff --git a/testing/test_invocations.py b/testing/test_invocations.py
index 03f53b5..0c31642 100644
--- a/testing/test_invocations.py
+++ b/testing/test_invocations.py
@@ -33,10 +33,10 @@ def test_only_kwargs(pm: PluginManager) -> None:
 
     pm.add_hookspecs(Api)
     with pytest.raises(TypeError) as exc:
-        pm.hook.hello(3)
+        pm.hook.hello(3)  # type: ignore[call-arg]
 
-    comprehensible = "hook calling supports only keyword arguments"
-    assert comprehensible in str(exc.value)
+    message = "__call__() takes 1 positional argument but 2 were given"
+    assert message in str(exc.value)
 
 
 def test_opt_in_args(pm: PluginManager) -> None: