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

Bug in PIE790 in Ruff v0.1.6 (... is meaningful in protocol methods and should not be removed) #8756

Closed
kkom opened this issue Nov 18, 2023 · 11 comments · Fixed by #8769
Closed
Assignees
Labels
bug Something isn't working

Comments

@kkom
Copy link

kkom commented Nov 18, 2023

Hey, I've got a regression in Ruff 0.1.6 to report.

PIE790 wants the ... removed, but it's necessary in this case – because foo is a protocol method:

from typing import Protocol

class Repro(Protocol):
    def foo(self) -> str:
        """docblock"""
        ...

At least according to Pyright 1.1.336, which shows this error if I allow Ruff to remove the ...:

error: Function with declared return type "str" must return value on all code paths
    "None" is incompatible with "str" (reportGeneralTypeIssues)
@zanieb
Copy link
Member

zanieb commented Nov 18, 2023

Thanks for the report!

Would you mind reporting this on mypy? This seems like a bug on their end unless there's something in the specification that says ... is required for protocol methods.

ref https://peps.python.org/pep-0544/

@kkom
Copy link
Author

kkom commented Nov 18, 2023

Ah, I didn't think of it this way! Yeah, it's possible Pyright is in the (heh) wrong here. I'll report!

@erictraut
Copy link

I'm the author of pyright, and I can say that this isn't a bug in pyright. Ruff's formatter should not remove the ellipsis in this case because it has significant meaning.

A protocol class is allowed to provide a default implementation for a method, in which case all subclasses of that protocol inherit that implementation. If the implementation of a method does not include a ... as its implementation, pyright assumes it's a default implementation and type checks it accordingly.

I'll note that black doesn't remove the ellipsis in this case, so I consider this a bug in ruff's formatter.

@kkom kkom changed the title Bug in PIE790 in Ruff v0.1.6 (... is necessary for protocol methods which return something else than None) Bug in PIE790 in Ruff v0.1.6 (... is meaningul in protocol methods and should not be removed) Nov 18, 2023
@kkom kkom changed the title Bug in PIE790 in Ruff v0.1.6 (... is meaningul in protocol methods and should not be removed) Bug in PIE790 in Ruff v0.1.6 (... is meaningful in protocol methods and should not be removed) Nov 18, 2023
@zanieb
Copy link
Member

zanieb commented Nov 18, 2023

Thanks for the context Eric!

This isn't part of Ruff's formatter — it will never change the semantics of the code.

This is a fix for rule removing unnecessary pass statements which was recently expanded to include ellipses (...). If a function has a docstring, there is no need for a pass or ... for it to be syntactically valid. Should Pyright be treating a function with no expressions in its body as the default implementation? Is that part of the protocol specification or is that just what you've implemented?

Regardless, I think we should add a special exemption to this lint rule in Ruff.

@zanieb
Copy link
Member

zanieb commented Nov 18, 2023

It looks like mypy (1.7.0) does not raise an error for this

from typing import Protocol


class Repro(Protocol):
    def foo(self) -> str:
        """docblock"""

@erictraut
Copy link

Is that part of the protocol specification or is that just what you've implemented?

The protocol spec doesn't specify this, nor do any other parts of the typing spec. Pyright's treatment of ... as a placeholder is consistent with how an ellipsis is used elsewhere in the language and typing spec.

Mypy's treatment of ... in function bodies is inconsistent. It treats ... as a placeholder in protocols but not in regular classes. Mypy's behavior has also changed over time. Pyright's behavior has been consistent and principled. I'm not inclined to change it because this would be disruptive to pyright users who rely on the current behavior.

This is a fix for rule removing unnecessary pass statements which was recently expanded to include ellipses (...).

Ah, I see. Thanks for the clarification. Yes, I agree that it would be good to add an exemption for this case. The ... is not extraneous in this situation.

@charliermarsh charliermarsh added the bug Something isn't working label Nov 18, 2023
@charliermarsh
Copy link
Member

charliermarsh commented Nov 18, 2023

Thanks @erictraut! Does this apply anywhere outside of Protocol methods? Should be an easy fix.

@erictraut
Copy link

erictraut commented Nov 18, 2023

It also applies to abstract methods (those decorated with @abc.abstractmethod) within an abstract class. Protocols and abstract classes are closely related.

Should be an easy fix.

Great to hear!

@charliermarsh
Copy link
Member

Okay great, thanks Eric!

@charliermarsh charliermarsh self-assigned this Nov 18, 2023
@charliermarsh
Copy link
Member

I can take this one since it's my regression :)

charliermarsh added a commit that referenced this issue Nov 19, 2023
## Summary

It turns out that some type checkers rely on the presence of ellipses in
`Protocol` interfaces and abstract methods, in order to differentiate
between default implementations and stubs. This PR modifies the preview
behavior of `PIE790` to avoid flagging "unnecessary" ellipses in such
cases.

Closes #8756.

## Test Plan

`cargo test`
Julian added a commit to python-jsonschema/referencing that referenced this issue Feb 5, 2024
This seems somehow caused by astral-sh/ruff#8756, which is
marked closed.

Here comes a rabbit hole to find out why this cropped up now.
@rsokl
Copy link

rsokl commented Mar 12, 2024

Does this apply anywhere outside of Protocol methods?

This also applies for stubs defined inside if TYPE_CHECKING clauses. See #10358

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants