MagicStack / uvloop

Ultra fast asyncio event loop.
Apache License 2.0
10.46k stars 550 forks source link

Update loop task factory typing with kwargs #585

Closed ordinary-jamie closed 11 months ago

ordinary-jamie commented 12 months ago

Issue: https://github.com/MagicStack/uvloop/issues/584

For PY311 the task factory will be called with the context kwarg. This is not correctly reflected in the typing for Loop.set_task_factory and Loop.get_task_factory.

This commit addresses this with a protocol. Since uvloop requires a minimum Python 3.8 version, in which typing.Protocol is available. Note, neither typing.Unpack for kwargs, (introduced in Python 3.11) or typing.ParmSpec with ParamSpec.kwargs (introduced in Python 3.10) is available for 3.8

Did not explicitly type the context parameter and used **kwargs since the Loop method allows for the legacy API without a context param for <PY311

Also see discussion about name parameter for task factory in asyncio: https://github.com/python/cpython/pull/112623#issuecomment-1837405443

Quick demonstration of typing (see mypy Callback Protocols)

# test.py
import asyncio
from typing import Any, Generator, Protocol, TypeVar

_T = TypeVar("_T")

class TaskFactoryCallable(Protocol):
    def __call__(
        self, loop: asyncio.AbstractEventLoop, coro: Generator[Any, None, _T], **kwargs
    ) -> asyncio.Future[_T]:
        ...

def set_task_factory(factory: TaskFactoryCallable) -> None:
    ...

def task_factory_valid(
    loop: asyncio.AbstractEventLoop, coro: Generator[Any, None, _T], **kwargs
) -> asyncio.Future[_T]:
    return asyncio.Future()

def task_factory_invalid(
    loop: asyncio.AbstractEventLoop, coro: Generator[Any, None, _T]
) -> asyncio.Future[_T]:
    return asyncio.Future()

# Passes mypy
set_task_factory(task_factory_valid)

# Fails mypy
set_task_factory(task_factory_invalid)
mypy test.py 
# test.py:35: error: Argument 1 to "set_task_factory" has incompatible type "Callable[[AbstractEventLoop, Generator[Any, None, _T]], Future[_T]]"; expected "TaskFactoryCallable"  [arg-type]
# Found 1 error in 1 file (checked 1 source file)