Skip to content

Ahook class not compatible with passing 'firstresult=True' to a hookspec marker #35

@chevignon93

Description

@chevignon93

When passing firstresult=True to a hookspec marker, the HookCaller will only return 1 result (coroutine) instead of a list of results but asyncio.gather expect a list of "tasks" and not a single coroutine

Here's a minimally reproducible example to illustrate the issue.

import apluggy
import asyncio

hookspec = apluggy.HookspecMarker("my-project")
hookimpl = apluggy.HookimplMarker("my-project")

class Hookspec:
    @hookspec(firstresult=True)
    async def afunc(self, x, y): ...

class Plugin:
    @hookimpl
    async def afunc(self, x, y):
        return x + y

async def main():
    pm = apluggy.PluginManager("my-project")
    pm.add_hookspecs(Hookspec())
    pm.register(Plugin)
    print(await pm.ahook.afunc(x=1, y=2))

asyncio.run(main())

The code above fails with TypeError: asyncio.tasks.gather() argument after * must be an iterable, not coroutine

but changing the call function to actually check whether calling hook(*args, **kwargs) returns a list and acting accordingly if it doesn't fixes the issue.

class AHook:
    def __init__(self, pm: PluginManager_) -> None:
        self.pm = pm

    def __getattr__(self, name: str) -> Callable[..., Coroutine[Any, Any, list]]:
        async def call(*args: Any, **kwargs: Any) -> list:
            hook: HookCaller = getattr(self.pm.hook, name)
            coros: list[asyncio.Future] = hook(*args, **kwargs)
            if not isinstance(coros, Coroutine):
                return None
            if not isinstance(coros, list):  # Added an isinstance check to see whether a list or a single element is returned
                return await coros
            return await asyncio.gather(*coros)
        return call

After this small change the code works as expected:

print(await pm.ahook.afunc(x=1, y=2))
>>> 3

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions