Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add type hints #76

Open
devRMA opened this issue Mar 20, 2022 · 11 comments
Open

Add type hints #76

devRMA opened this issue Mar 20, 2022 · 11 comments

Comments

@devRMA
Copy link
Contributor

devRMA commented Mar 20, 2022

Use a static type checker called mypy. In libs, I think it's very important to have type hints, so that when users are going to use the lib, code linters can help, knowing exactly what kind of parameters to receive, and what the return type is.

The ideal solution would be to add type hints as specified by PEP 484, PEP 526, PEP 544, PEP 586, PEP 589, and PEP 591 directly on your code.

@devRMA
Copy link
Contributor Author

devRMA commented Mar 21, 2022

I even noticed that you had already added mypy to the requirements. Just need to do static typing.

Pipe/requirements.txt

Lines 3 to 4 in dd179c8

mypy==0.550
pylint==1.7.4

@JulienPalard
Copy link
Owner

Probably blocked by python/mypy#11833, as I'll need something like:

P = ParamSpec("P")
I = TypeVar("I")
O = TypeVar("O")

class Pipe(Generic[P, I, O]):
    def __init__(self, function: Callable[Concatenate[Iterable[I], P], Iterator[O]]) -> None:
        ...

as later the __call__ arguments are P. (The Iterable[I] being passed to the function via __ror__.)

But yes I agree it would be very nice to be able to write something like:

T = TypeVar("T")
take: Pipe[T, int, T]

to express than take takes an int, and yields the same type that the feeded type.

@JulienPalard
Copy link
Owner

Now blocked by mypy issue 1484 as we need a way to cleanly decribe functools.partial, to properly describe:

def __call__(self, *args, **kwargs):
    return Pipe(
        lambda iterable, *args2, **kwargs2: self.function(
            iterable, *args, *args2, **kwargs, **kwargs2
        )
    )

@devRMA
Copy link
Contributor Author

devRMA commented Oct 27, 2022

It's more complex than I thought 😳

@0xMochan
Copy link

Now blocked by mypy issue 1484 as we need a way to cleanly decribe functools.partial, to properly describe:

def __call__(self, *args, **kwargs):
    return Pipe(
        lambda iterable, *args2, **kwargs2: self.function(
            iterable, *args, *args2, **kwargs, **kwargs2
        )
    )

I'm pretty sure this is nearly impossible without being able to represent HKTs (Higher-Kinded Types) in the Python type system.

@fabiob
Copy link

fabiob commented Mar 13, 2024

@JulienPalard and @devRMA, would you share the code you write so far in a branch or fork? I would love to take a swing at it and try to help you.

@JulienPalard
Copy link
Owner

@fabiob pushed my typing branch, see the pyi file. I know it's not much and does not works.

What we really need is a clean way to type functools.partial, as it's what's pipe does a lot.

@p-98
Copy link

p-98 commented Jul 3, 2024

Seems like python/mypy#1484 was completed with python/mypy#16939.

Does the implementation provide what you need?

@JulienPalard
Copy link
Owner

I don't think so, they implemented a mypy plugin to handle the single functools.partial function, I really mean there's an actual if fullname == "functools.partial": in the implementation.

Looks like there's still no way to type annotate functools.partial equivalents.

@JulienPalard
Copy link
Owner

Currently this works:

from collections.abc import Callable
from typing import Concatenate, Generic, ParamSpec, TypeVar

P = ParamSpec('P')
T = TypeVar('T')
U = TypeVar('U')

class Pipe(Generic[U, P, T]):
    def __init__(self, function: Callable[Concatenate[U, P], T]):
        self.function = function

    def run(self, arg1: U) -> T:
        return self.function(arg1)


def simple_annotated_function(value: int) -> str:
    assert isinstance(value, int)
    return "hello"


def main() -> None:
    p = Pipe(simple_annotated_function)
    reveal_type(p)  # demo.Pipe[builtins.int, [], builtins.str]
    reveal_type(p.function) # def (builtins.int) -> builtins.str
    reveal_type(p.run) # def (arg1: builtins.int) -> builtins.str
    p.run("coucou") # Argument 1 to "run" of "Pipe" has incompatible type "str"; expected "int"


main()

But it feels like it's only 1/10 of the work, as we need some way to "substract" parameters from a paramspec.

@JulienPalard
Copy link
Owner

Starting a new discussion here: python/mypy#17481 to track this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants