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

[DOC403/DOC502] False positive for abstract methods #31

Closed
ghjklw opened this issue Jun 23, 2023 · 9 comments
Closed

[DOC403/DOC502] False positive for abstract methods #31

ghjklw opened this issue Jun 23, 2023 · 9 comments

Comments

@ghjklw
Copy link

ghjklw commented Jun 23, 2023

Abstract method / abstract classes should be able to define a signature/docstring containing Yields or Raises sections even though they do not contain a yield or raise statement.

For example:

from abc import ABC, abstractmethod
from collections.abc import Iterator


class AbstractClass(ABC):
    """Example abstract class."""

    @abstractmethod
    def abstract_method(self, var1: str) -> Iterator[str]:
        """Abstract method.

        Args:
            var1 (str): Variable.

        Raises:
            ValueError: Example exception

        Yields:
            str: Paths to the files and directories listed.
        """

Results in:

    11: DOC201: Method `AbstractClass.abstract_method` does not have a return section in docstring 
    11: DOC403: Method `AbstractClass.abstract_method` has a "Yields" section in the docstring, but there are no "yield" statements or a Generator return annotation  
    11: DOC502: Method `AbstractClass.abstract_method` has a "Raises" section in the docstring, but there are not "raise" statements in the body 

I also do not understand with DOC201 is triggered in this case.

NB: DOC202 is not impacted, the following code does not trigger any warning:

from abc import ABC, abstractmethod


class AbstractClass(ABC):
    """Example abstract class."""

    @abstractmethod
    def abstract_method(self, var1: str) -> str:
        """Abstract method.

        Args:
            var1 (str): Variable.

        Returns:
            str: Paths to the files and directories listed.
        """
@real-yfprojects
Copy link
Contributor

I also do not understand with DOC201 is triggered in this case.

Pydoclint wants you to document that the method returns an Iterator which is different from yielding values which makes the method return a Generator. This is linked to #15.

@ghjklw
Copy link
Author

ghjklw commented Jun 23, 2023

I also do not understand with DOC201 is triggered in this case.

Pydoclint wants you to document that the method returns an Iterator which is different from yielding values which makes the method return a Generator. This is linked to #15.

Thanks! So I guess that's one more edge-case for DOC201 😉 The rule would be something like if a method is abstract and its output is either Iterable/Iterator/Generator and it contains a yield section, then DOC201 doesn't apply?

@real-yfprojects
Copy link
Contributor

real-yfprojects commented Jun 23, 2023

But why would you want to have a yield section but not a Generator return type?

@ghjklw
Copy link
Author

ghjklw commented Jun 23, 2023

For exactly the same reason as mentioned there: #15 (comment)
There is no reason that this combination of signature/docstring would work for a concrete method, but not for an abstract method?

@jsh9
Copy link
Owner

jsh9 commented Jun 24, 2023

Hi @ghjklw:

Thank you for raising this issue. This is indeed an edge case that I didn't think of.

Let me explain the "strange" behavior between your Example 1 and Example 2.

In Example 1, pydoclint raises DOC201 and DOC403 because: it sees a Yields section in the docstring, so it's expecting a yield statement in the method body, not in the method signature.

This is because: when we see -> Iterator[something] in the signature, we can't be sure that the method is yielding stuff or returning stuff -- see my example here (#15 (comment)). Therefore, the yield statement in the method body is our only clue.

The situation is different for Example 2, where there is -> str in the signature. Since the return type annotation is not Iterator[...], we are certain that this method should return stuff rather than yield stuff.

That's why I wrote (hasReturnStmt or hasReturnAnno) in the code:

if docstringHasReturnSection and not (hasReturnStmt or hasReturnAnno):
violations.append(v202)

So I actually don't know what the best solution should be: are we OK that both -> Iterator[...] and -> Generator[...] in abstract methods can correspond to a Yields section in the docstring? (If we are OK with this, this example will fall through the cracks.)

@jsh9
Copy link
Owner

jsh9 commented Jun 27, 2023

Hi @ghjklw :

I merged PR #35. Please feel free to take a look at the code change, if you are curious.

Please note that your 1st example will still trigger violations, because if the return type annotation is Iterator[...], the linter still expects a return section, rather than a yield section.

If you have better suggestions on how the linter should handle Iterator/Generator and yields, please feel free to share them!

@ghjklw
Copy link
Author

ghjklw commented Jun 27, 2023

Thanks! I think that's a good compromise. Thinking again about this example I think @real-yfprojects has a point regarding the use of Iterator[...] in an abstract class or protocol:

  • Either I care whether it's implemented as a generator or return statement, and I should use the Generator[...] type hint
  • Or I don't care about it, and I should probably use the more generic Returns section in the docstring since there is no guarantee that the concrete implementation will actually yield something.

By the way, thanks for all the work you're doing ! It's fantastic to have such a tool 😃 I only hope I had the skills to build a VS Code extension for it 😄 !

@real-yfprojects
Copy link
Contributor

  • Either I care whether it's implemented as a generator or return statement, and I should use the Generator[...] type hint

  • Or I don't care about it, and I should probably use the more generic Returns section in the docstring since there is no guarantee that the concrete implementation will actually yield something.

That's exactly what I was thinking.

@jsh9
Copy link
Owner

jsh9 commented Jun 28, 2023

Thank you both for sharing useful feedback!

With the discussions above, I'm going to close this issue as "fixed". Please feel free to reopen it if any additional fixing is necessary.

@jsh9 jsh9 closed this as completed Jun 28, 2023
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

3 participants