Skip to content

Conversation

barometz
Copy link

The DOC2xx documentation explains that

Methods with @property as its last decorator do not need to have a return section.

The "last decorator" restriction means this doesn't work for abstract methods, as in

import abc

class C(abc.ABC):
    @property
    @abc.abstractmethod
    def prop(self) -> float:
        """The property.""""

.. because @abstractmethod can't decorate @property (I think because the object returned by property() is a native one, so abstractmethod() can't tag it with new attributes?).

I couldn't find a reason for that restriction, so this PR removes it. If there is a reason, please let me know - I'll happily put some more time into making this case work.

and any(
( # noqa: PAR001
isinstance(_, ast.Name)
and hasattr(node.decorator_list[-1], 'id')
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the new test case, abstractmethod is the last element in the decorator list and it does not (at that time) have an id attribute, so this test failed despite there being a @property decorator.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given my reply below (#261 (comment)), I think we'd need to change this line into:

and hasattr(node.decorator_list[0], 'id')

@jsh9
Copy link
Owner

jsh9 commented Sep 20, 2025

Hi @barometz , thanks so much for finding this issue!

I think at the time when I implemented this, I got things backwards. @property should be the outer-most decorator, instead of the inner-most.

For example,

class Foo:
    @something
    @property
    def x(self): return "hi"

is equivalent to:

def x(self): return "hi"
x = property(x)
x = something(x)

Then, x is no longer a property object, but some other function, and thus needs to have a return section in the docstring.

When we put @property in the outer-most layer:

class Foo:
    @property
    @something
    def x(self): return "hi"

which is equivalent to:

def x(self): return "hi"
x = something(x)
x = property(x)

x is a property object, which can be waived having a return section in its docstring.

| `DOC202` | Function/method has a return section in docstring, but there are no return statements or annotations |
| `DOC203` | Return type(s) in the docstring not consistent with the return annotation |

Note on `DOC201`: Methods with `@property` as its last decorator do not need to
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to replace "its last decorator" with "its outer-most decorator (i.e., on the top)"

@barometz
Copy link
Author

Right now, has_attr(..., 'id') and _.id looks like it's checking for presence of the id attribute to avoid an AttributeError, but that's not what's happening. If the intent is to only check the outermost decorator, wouldn't it be simpler to only check the first element of decorator_list to begin with, instead of running any() over decorator_list? The function name checkMethodContainsSpecifiedDecorator() also suggests that any position is allowed.

This function is also used to check for @abstractmethod, but if only the outermost decorator is deemed relevant, @property + @abstractmethod won't be recognized as an abstract method. Both cause some return-related violations to be skipped so the tests still pass, but I can imagine there being other combinations that break.

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

Successfully merging this pull request may close these issues.

2 participants