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

Unable to use @overload with Concatenate decorator #12716

Closed
Dreamsorcerer opened this issue May 2, 2022 · 5 comments · Fixed by #15898
Closed

Unable to use @overload with Concatenate decorator #12716

Dreamsorcerer opened this issue May 2, 2022 · 5 comments · Fixed by #15898
Labels
bug mypy got something wrong topic-overloads topic-paramspec PEP 612, ParamSpec, Concatenate

Comments

@Dreamsorcerer
Copy link
Contributor

Dreamsorcerer commented May 2, 2022

Bug Report

I can't seem to figure out any way to get @overloads to work with a method being transformed by a decorator.

To Reproduce

I've attempted to reduce it to a small example:

def acquire(func: Callable[Concatenate[Any, str, _P], Awaitable[_T]]) -> Callable[Concatenate[Any, _P], Awaitable[_T]]:
    ...

@overload
async def get(self, key: bytes, default: None = ...) -> Union[bytes, _T, None]:
    ...
@overload
async def get(self, key: bytes, default: _U) -> Union[bytes, _T, _U]:
    ...
@acquire
async def get(
    self, conn: str, key: bytes, default: Optional[_U] = None
) -> Union[bytes, _T, _U, None]:
    ...

get(b"foo")

I have experimented with adding/removing conn from the overloads and adding/moving the @acquire at various locations.

Expected Behavior

The final call to work correctly with no errors in mypy.

Actual Behavior

Depending on exactly how I write it, I end up with either complaints about the definition (e.g. Overloaded function implementation does not accept all possible arguments of signature 1) or complaints about the final call (e.g. No overload variant of "get" matches argument type "bytes").

Your Environment

  • Mypy version used: 0.950
  • Python version used: 3.8
@Dreamsorcerer Dreamsorcerer added the bug mypy got something wrong label May 2, 2022
@AlexWaygood AlexWaygood added topic-overloads topic-paramspec PEP 612, ParamSpec, Concatenate labels May 2, 2022
@Dreamsorcerer
Copy link
Contributor Author

@ilevkivskyi
Copy link
Member

Note after #15898 is merged, you should be able to either:

  • Decorate each overload with @acquire as defined now (overloads should have undecorated signature obviously)
  • Or fix @acquire to preserve the name of first argument to be "self".

ilevkivskyi added a commit that referenced this issue Aug 18, 2023
Fixes #15737
Fixes #12844
Fixes #12716

My goal was to fix the `ParamSpec` issues, but it turns out decorated
overloads were not supported at all. Namely:
* Decorators on overload items were ignored, caller would see original
undecorated item types
* Overload item overlap checks were performed for original types, while
arguably we should use decorated types
* Overload items completeness w.r.t. to implementation was checked with
decorated implementation, and undecorated items

Here I add basic support using same logic as for regular decorated
functions: initially set type to `None` and defer callers until
definition is type-checked. Note this results in few more `Cannot
determine type` in case of other errors, but I think it is fine.

Note I also add special-casing for "inline" applications of generic
functions to overload arguments. This use case was mentioned few times
alongside overloads. The general fix would be tricky, and my
special-casing should cover typical use cases.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
@Dreamsorcerer
Copy link
Contributor Author

  • Decorate each overload with @acquire as defined now (overloads should have undecorated signature obviously)

  • Or fix @acquire to preserve the name of first argument to be "self".

First option works. But, I don't understand how to do the second option.

Looks like your tests do the former, so I guess I'll stick with that.

@ilevkivskyi
Copy link
Member

I meant something using callback protocols (that allow specifying argument names unlike simple Callable[] and are generally more expressive, but somewhat verbose), like this:

class WithConn(Protocol[_P, _R]):
    def __call__(s, self: Any, conn: str, *args: _P.args, **kwargs: _P.kwargs) -> _R: ...

class NoConn(Protocol[_P, _R]):
    def __call__(s, self: Any, *args: _P.args, **kwargs: _P.kwargs) -> _R: ...

def acquire(func: WithConn[_P, _T]) -> NoConn[_P, _T]:
    ...

But I just tried and it looks like it doesn't really work because of some unrelated bug with return type checks for generic overloads.

@Dreamsorcerer
Copy link
Contributor Author

Not sure if there's something I'm doing wrong, but seem to have encountered a regression that's breaking this again in 1.7: #16481

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-overloads topic-paramspec PEP 612, ParamSpec, Concatenate
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants