最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Type annotating an inline decorated wrapped function - Stack Overflow

programmeradmin3浏览0评论

For context I am using prefect, which is a workflow orchestration engine that uses function decorates to tag functions for use. Their approach is very similar to dask in the creation of work flows.

Say I want to apply a decorator (in this case a prefect.task) to a function defined out of scope, say from an external module, I can do something like:

from prefect import task, Task
from some_module import foo

# task is the decorate, and returns an instance of Task

task_foo = task(foo)

@task
def task_bar(a: int, b: float) -> float:
    return a * b

in the case of task_bar my environment in vscode is able to resolve the function parameters to (a: int, b: float) -> float.

In the case of task_foo is it not: (...) -> None.

Is there a way to apply some typing.ParamSpec to appropriately provide the type inference?

import typing

P = typing.ParamSpec('P', bound=foo)
task_foo: Task[P] = task(foo)

I realise that ParamSpec(..., bound=foo) is not the answer here, but I am hoping it highlights the intent here.

I have not been able to spot the right thing to do here. I have found:

Which is getting very close to the problem I am describing. Playing around with ParamSpec I am falling just a little short.

Edit:

Alright, not I think I am misunderstanding something, though this does not surprise me. Doing something as simple as:

from typing import ParamSpec, TypeVar
from prefect import task, Task
from some_module import foo

P = ParamSpec('P')
R = TypeVar('R')


task_foo: Task[P,R] = task(foo)

behaves exactly I want it to. But I am left very confused where the information is bubbled up from. The Task class does have similar type P R parameters attached to its methods. Is it just a case that the types are being 'returned' and captured into the type variables P and R?

Hope this makes sense!

For context I am using prefect, which is a workflow orchestration engine that uses function decorates to tag functions for use. Their approach is very similar to dask in the creation of work flows.

Say I want to apply a decorator (in this case a prefect.task) to a function defined out of scope, say from an external module, I can do something like:

from prefect import task, Task
from some_module import foo

# task is the decorate, and returns an instance of Task

task_foo = task(foo)

@task
def task_bar(a: int, b: float) -> float:
    return a * b

in the case of task_bar my environment in vscode is able to resolve the function parameters to (a: int, b: float) -> float.

In the case of task_foo is it not: (...) -> None.

Is there a way to apply some typing.ParamSpec to appropriately provide the type inference?

import typing

P = typing.ParamSpec('P', bound=foo)
task_foo: Task[P] = task(foo)

I realise that ParamSpec(..., bound=foo) is not the answer here, but I am hoping it highlights the intent here.

I have not been able to spot the right thing to do here. I have found:

https://github/python/typing/issues/1027

Which is getting very close to the problem I am describing. Playing around with ParamSpec I am falling just a little short.

Edit:

Alright, not I think I am misunderstanding something, though this does not surprise me. Doing something as simple as:

from typing import ParamSpec, TypeVar
from prefect import task, Task
from some_module import foo

P = ParamSpec('P')
R = TypeVar('R')


task_foo: Task[P,R] = task(foo)

behaves exactly I want it to. But I am left very confused where the information is bubbled up from. The Task class does have similar type P R parameters attached to its methods. Is it just a case that the types are being 'returned' and captured into the type variables P and R?

Hope this makes sense!

Share Improve this question edited Jan 30 at 12:46 InSync 10.9k4 gold badges17 silver badges56 bronze badges asked Jan 30 at 5:54 user29426864user29426864 314 bronze badges 2
  • Is some_module.foo type-annotated? – dROOOze Commented Jan 30 at 6:35
  • Yes, it is type-annotated – user29426864 Commented Jan 30 at 8:00
Add a comment  | 

1 Answer 1

Reset to default -1

There are plenty ways how to achieve this:

1.

from typing import ParamSpec, TypeVar
from prefect import task, Task
from some_module import foo

P = ParamSpec('P')
R = TypeVar('R')

task_foo: Task[P, R] = task(foo)
from typing import Callable, ParamSpec, TypeVar
from prefect import task, Task

P = ParamSpec("P")
R = TypeVar("R")

def typed_task(fn: Callable[P, R], **task_kwargs) -> Task[P, R]:
    return task(fn, **task_kwargs)

from some_module import foo
task_foo = typed_task(foo)

reveal_type(task_foo)
from typing import Protocol, ParamSpec, TypeVar

P = ParamSpec("P")
R = TypeVar("R")

class TaskLike(Protocol[P, R]):
    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R: ...
    def submit(self, *args: P.args, **kwargs: P.kwargs) -> R: ...

def typed_task(fn: Callable[P, R]) -> TaskLike[P, R]:
    return task(fn)
from typing import cast

task_foo = cast(Task[(int, str), float], task(foo))
from typing import ParamSpec, TypeVar
from prefect import task, Task
from some_module import foo

P = ParamSpec('P')
R = TypeVar('R')

task_foo: Task[P, R] = task(foo)
发布评论

评论列表(0)

  1. 暂无评论