Suppose I have two classes (A
& B
) of which the second is derived from the first. Further, I hide some of the properties implemented in A by writing new implementations in B. However, the docstring I wrote for A is still valid for B and I'm lazy -- I don't want to copy paste everything.
Please note that a key issue here is my targets are properties, for class methods in general there are solutions posted.
In code, minimal example of classes could look like this:
In [1]: class A(object):
...: @property
...: def a(self):
...: "The a:ness of it"
...: return True
...:
In [2]: class B(A):
...: @property
...: def a(self):
...: return False
...:
What I basically would want is to have a way to make the following happen:
In [8]: B.a.__doc__
Out[8]: 'The a:ness of it'
In reality the docstring of B.a is empty and writing to B.a.__doc__
is impossible as it raises a TypeError
.
As far as I have gotten is the following hack of a solution:
from inspect import getmro
def inheritDocFromA(f):
"""This decorator will copy the docstring from ``A`` for the
matching function ``f`` if such exists and no docstring has been added
manually.
"""
if f.__doc__ is None:
fname = f.__name__
for c in getmro(A):
if hasattr(c, fname):
d = getattr(c, fname).__doc__
if d is not None:
f.__doc__ = d
break
return f
Which does indeed work, but is dead ugly as A
is hard-coded into the decorator-function as I have found
no way to know what class f
is attached to when passed to the decorator:
In [15]: class C(A):
....: @property
....: @inheritDocFromA
....: def a(self):
....: return False
....:
In [16]: C.a.__doc__
Out[16]: 'The a:ness of it'
Question: Is it possible to construct a general solution for decorator applied docstrings on class properties without hard-coding in the inheritance into the decorator function?
I've also tried decorating the classes but then I hit the wall with the properties docstrings being write-protected.
And finally, I'd like the solution to work for both Python 2.7 and 3.4 if possible.
It is possible to write a decorator that returns the proper __doc__
when accessed through class — after all, __get__
receives the type and can go through its MRO, find the appropriate __doc__
, and set it on itself (or on a proxy created and returned for that purpose). But it is much simpler to work around the issue of __doc__
not being writable.
It turns out that since property
is implemented as a type, making the __doc__
of its instances writable is as simple as inheriting from it:
class property_writable_doc(property):
pass
Then your idea of using a class decorator to inherit the __doc__
of properties can work:
def inherit_doc_class(cls):
for name, obj in cls.__dict__.iteritems():
if isinstance(obj, property_writable_doc) and obj.__doc__ is None:
for t in cls.__mro__:
if name in t.__dict__ and t.__dict__[name].__doc__ is not None:
obj.__doc__ = t.__dict__[name].__doc__
break
return cls
class A(object):
@property
def a(self):
"The a:ness of it"
return True
@inherit_doc_class
class B(A):
@property_writable_doc
def a(self):
return False
@inherit_doc_class
class C(A):
@property_writable_doc
def a(self):
"The C:ness of it"
return False