I feel like I'm going to gain a reputation as the ...
# prefect-community
n
I feel like I'm going to gain a reputation as the guy who only ever asks about type safety but... I've got pyright integrated into my environment now, which is working far better with Prefect than Mypy did. It just requires one small hack, and it makes me wonder why this isn't part of the library: I had to add
py.typed
on my own. I see that there is a
py.typed
on the master branch; was there a conscious decision made not to include this marker file in 2.0? If so, why? Without the marker file, I get:
Copy code
from prefect.flows import flow

reveal_type(flow)  # Unknown
With the marker file, I get:
Copy code
from prefect.flows import flow

reveal_type(flow)  # Overload[(__fn: (**P@flow) -> R@flow, /) -> Flow[P@flow, R@flow], (*, name: str = None, version: str = None, task_runner: BaseTaskRunner = ConcurrentTaskRunner, description: str = None, timeout_seconds: int | float = None, validate_parameters: bool = True) -> (((**P@flow) -> R@flow) -> Flow[P@flow, R@flow]), (__fn: Unknown | None = None, *, name: str = None, version: str = None, task_runner: BaseTaskRunner = ConcurrentTaskRunner, description: str = None, timeout_seconds: int | float = None, validate_parameters: bool = True) -> (Flow[P, R] | (((**P) -> R) -> Flow[P, R]))]
which is much better. For example, now when I decorate a function with
@task
and use it in an
@flow
, the return type of the task is known to have a
.result()
method. When it was just an Unknown, pyright would complain that I'm accessing a method that doesn't exist. For someone who enforces fully passing mypy (now pyright) checks on every pull request, this is kind of a necessity.
z
Oh we’re not omitting that on purpose
We need to add it to the MANIFEST I guess
n
So is it fine for now for me to just
touch py.typed
To get by while developing?
a
Nice, is this typing not recognized by mypy then?
n
In mypy, presumably due to their lack of full support for [PEP612](https://peps.python.org/pep-0612/), when it tries to type the result of
Task.__call__
(i.e. type your function you’ve decorated with
@task
), it always chooses the [first overload](https://github.com/PrefectHQ/prefect/blob/orion/src/prefect/tasks.py#L231-L255),
Task[P, NoReturn]
, whose return type is
PrefectFuture[None, Sync]
. Basically, it assumes that every single user function decorated with
@task
has a return type of
None
, which causes
.result()
to always be None
Pyright supports PEP612, so for example when you have
Copy code
@task
def my_task(a: str, b: int) -> float:
     return len(a) + b + 0.5
it picks up the third overload
Task[P, T]
and returns a
PrefectFuture[T, Sync]
, which here is a
PrefectFuture[float, Sync]
. And therefore
my_task("a", 0).result()
is a
float
… since when does Slack not support markdown-formatted links? huh.