I have a self-written class-based Python decorator. As far as I can see there is a difference, when I apply the decorator to a method in a class. Usually, decorators like @classmethod
, @staticmethod
, @property
or @unique
are applied without parenthesis. These decorators expect no parameters and are (mostly?) written as function-based decorators.
So in contrast to these examples, my decorator is class-based and expects an optional parameter while applying.
My decorator:
class DocumentMemberAttribute(Attribute):
def __init__(self, value=True):
super().__init__()
self.value = value
The Attributes class (the real decorator):
class Attribute:
__AttributesMemberName__ = "__pyattr__"
def __call__(self, func):
self._AppendAttribute(func, self)
return func
@staticmethod
def _AppendAttribute(func, attribute):
# inherit attributes and append myself or create a new attributes list
if (Attribute.__AttributesMemberName__ in func.__dict__):
func.__dict__[Attribute.__AttributesMemberName__].append(attribute)
else:
func.__setattr__(Attribute.__AttributesMemberName__, [attribute])
def __str__(self):
return self.__name__
Example class:
class MyClass:
def __init__(self, message=""):
super().__init__()
self.message = message
@DocumentMemberAttribute
def Method1(self):
return "foo"
@DocumentMemberAttribute()
def Method2(self):
return "bar"
@DocumentMemberAttribute(False)
def Method3(self):
return "spam"
@DocumentMemberAttribute(True)
def Method4(self):
return "egg"
The attached information is used in a custom autodoc-skip-member
handler to decide if a method shall be documented or skipped. This is similar to the docit
extension.
So when we look at the generated documentation (Sphinx), we can see these results:
class MyClass(message="")
Method1 = <lib.SphinxExtensions.DocumentMemberAttribute object at 0x0000000004AD9E80>
Method2()
Method4()
What can we see here:
- Method1 has no parenthesis indicating a function/method, hence it's regarded a class field and Sphinx uses
__str__
(or__repr__
?) to document the initial value - Method3 is not documented as intended.
So my questions:
- Why is there a difference in the behavior?
- MUST I apply class-based attributes with parenthesis?
- How should a user know how to use these 2 kinds of decorators? (I can document it for myself, but others might remember just the name und forget to add the parenthesis, because other decorators don't require them.)
pyAttributes is a set of attributes (real attributes, no Python attributes) written by me. They behave almost like custom-attributes in .NET