I have a descriptor that turns a method into a property on the class level:
class classproperty(object):
def __init__(self, getter):
self.getter = getter
self.__doc__ = getter.__doc__
def __get__(self, instance, owner):
return self.getter(owner)
Used like this:
class A(object):
@classproperty
def test(cls):
"docstring"
return "Test"
However, I now can't access the __doc__
attribute (which is logical, because accessing A.test.__doc__
will fetch the __doc__
of str
, because A.test
already returns "Test"
.
My final goal is that my docstring will appear in sphinx, so it is not feasible to retrieve the docstring in any other way than by accessing the attributes __doc__
property. I find myself wondering if this is even possible in any way.
I know that property
solves this issue by returning the class if called without an instance. However, it should be obvious that this collides with my goal.
I am starting to fear that this is not possible in Python.
Note: I am willing to pull any stunt in classproperty
as long as it is stable (i.e. not setting __doc__
on the returned value). However, it is not feasible to put any burden on the user of classproperty
(i.e. they should only use the decorator and be done with it).
Indeed, test
is a property returning a string. You'd have to subclass str
and give that a __doc__
attribute:
class docstring_str(str):
def __new__(cls, v, __doc__=''):
s = super(docstring_str, cls).__new__(cls, v)
s.__doc__ = __doc__
return s
Demo:
>>> class docstring_str(str):
... def __new__(cls, v, __doc__=''):
... s = super(docstring_str, cls).__new__(cls, v)
... s.__doc__ = __doc__
... return s
...
>>> s = docstring_str('Test', 'docstring')
>>> s
'Test'
>>> s.__doc__
'docstring'
Use as:
class A(object):
@classproperty
def test(cls):
return docstring_str("Test", "docstring')
Because str
objects are immutable, you cannot set the __doc__
attribute in a decorator. You'd have to return a proxy object instead that fully wraps the actual return value except for the __doc__
attribute. This gets to be complex and ugly fast.
The alternative is to put a regular property
on the metaclass; the class's class:
class MetaClass(type):
@property
def test(cls):
"docstring"
return "Test"
class A(object):
__metaclass__ = MetaClass
Now A
has a test
property, and the docstring can be accessed as MetaClass.test.__doc__
or with type(A).test.__doc__
:
>>> A.test
'Test'
>>> type(A).test
<property object at 0x10757d158>
>>> type(A).test.__doc__
'docstring'
If you jump through a few hoops, it can be retrieved, but not directly through the property itself like A.test.__doc__
because of the way descriptors work.
class classproperty(object):
def __init__(self, getter):
self.getter = getter
def __get__(self, instance, owner):
if instance is None: # instance attribute accessed on class?
return self
return self.getter(owner)
class A(object):
@classproperty
def test(cls):
"test's docstring"
return "Test"
def docstring(cls, methodname):
return getattr(cls, methodname).getter.__doc__
print docstring(A, 'test') # -> test's docstring