dry-python / dry-python/returns

Feature Request: Let Pipe function take more than 1 positional argument.

Đang mở
#1,692 1 bình luận 3 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Python
Star
4.4k
Fork
155
Merge trung bình
2 giờ 27 phút
Pull request đã merge (30 ngày)
22

Mô tả

# Feature Request/Enhancement

I stumbled upon this post a while ago:

https://github.com/python/typing/discussions/1245?sort=top#discussioncomment-3454192

It was pretty neat, and wanted to try it out for functions that take more than 1 positional argument. Sadly, it does not seem to work.

## What's wrong

When trying to run the following code:
```python
from returns._internal.pipeline.pipe import pipe

def one_arg_only(num1: float) -> int:
return int(num1)

def two_args(num1: float, num2: int) -> int:
return int(num1 + num2)

def to_string(f: float) -> str:
return str(f)

def to_float(s: str) -> float:
return float(s)

if __name__ == "__main__":
fizz = pipe(one_arg_only, to_string, to_float)
print(fizz(1)) # Trivial example

buzz = pipe(to_string, to_float)
print(buzz(two_args(1, 2))) # This works, but is not ideal

bazz = pipe(two_args, to_string, to_float)
try:
print(bazz(1, 2)) # Too many arguments for "__call__" of "_Pipe" [call-arg]
except TypeError as e:
print(
"Cannot pass two arguments even though first function requires 2 positional arguments"
)
raise e
```

you would get the following exception:

```python
Cannot pass two arguments even though first function requires 2 positional arguments
Traceback (most recent call last):
File "/home/pratik/workplace/dry-python-returns/problem.py", line 21, in
raise e
File "/home/pratik/workplace/dry-python-returns/problem.py", line 16, in
print(fizz(1, 2))
^^^^^^^^^^
TypeError: pipe..() takes 1 positional argument but 2 were given
```

## How is that should be

Invoking bazz should not throw a `TypeError`.

## What I have tried

The following works **if and only if** the types are all correct.

```python
from functools import reduce
from typing import overload, ParamSpec, TypeVar, Callable

_P = ParamSpec("_P")
_T1 = TypeVar("_T1")
_T2 = TypeVar("_T2")
_T3 = TypeVar("_T3")

@overload
def pipe(f1: Callable[_P, _T1]) -> Callable[_P, _T1]: ...
@overload
def pipe(
f1: Callable[_P, _T1],
f2: Callable[[_T1], _T2],
) -> Callable[_P, _T2]: ...
@overload
def pipe(
f1: Callable[_P, _T1],
f2: Callable[[_T1], _T2],
f3: Callable[[_T2], _T3],
) -> Callable[_P, _T3]: ...

def pipe(*functions):
def compose2(f, g):
return lambda *args, **kwargs: g(f(*args, **kwargs))

return reduce(compose2, functions)
```

When the types are not correct, mypy throws the following error message:

```python
# Mypy error says: Cannot infer type argument 3 of "pipe"
fizz = pipe(one_arg_only, to_string, to_string)
```

I toyed around with `typing.ParamSpec` within `returns/_internal/pipeline/pipe.pyi` to see if that would do the trick, but I couldn't after a couple of hours of getting the above example to have mypy return no errors. Maybe I missed something, or I just have mind block.

Related issue: https://github.com/python/typing/discussions/1289

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.