how to inherit docstrings?

E

Eric Snow

Sometimes when using class inheritance, I want the overriding methods
of the subclass to get the docstring of the matching method in the
base class. You can do this with decorators (after the class
definition), with class decorators, and with metaclasses [1].

However, I was hoping for a way to do it with just function decorators
on the methods (no metaclass or class decorator). I am not sure if
this is doable. I realize now that this is exactly the reason I got
to thinking last week about objects being notified when they are bound
[2].

So, is there a way to do this with just decorators, or am I "stuck"
with the metaclass/class decorator route? (It's not all that bad :)

Thanks!

-eric


p.s. Am I missing something or can you really not change the docstring
of a class? I was thinking about the idea of inheriting class
docstrings too.


[1] http://code.activestate.com/recipes/577743-using-decorators-to-inherit-function-docstrings/
[2] http://mail.python.org/pipermail/python-ideas/2011-June/010446.html
 
E

Eric Snow

The docstring of an object (whether function or class or module) is the
object's ‘__doc__’ attribute. Access that attribute to get the
docstring; re-bind that attribute to set a different docstring.

Sorry, I should have been more clear:
.... "some doc"
....Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: attribute '__doc__' of 'type' objects is not writable

That is on 3.3.
So, it's even possible to do what you ask without decorators at all:

   class Foo(object):
       def frob(self):
           """ Frobnicate thyself. """

   class Bar(Foo):
       def frob(self):
           pass
       frob.__doc__ = Foo.frob.__doc__

Not very elegant, and involving rather too much repetition; but not
difficult.

Yeah, definitely you can do it directly for each case. However, the
inelegance, repetition, and immodularity are exactly why I am pursuing
a solution. :) (I included a link in the original message to
examples of how you can already do it with metaclasses and class
decorators too.)

I'm just looking for a way to do it with decorators in the class body
without using metaclasses or class decorators.

Thanks

-eric
--
 \     “We are no more free to believe whatever we want about Godthan |
 `\         we are free to adopt unjustified beliefs about science or |
_o__)              history […].” —Sam Harris, _The End of Faith_, 2004 |
Ben Finney
 
S

Steven D'Aprano

Well, that sucks :-(

Where can we see the discussion of that change before it was
implemented?

It goes back to Python 2.2, when new style classes were first introduced.


[steve@sylar ~]$ python2.2
Python 2.2.3 (#1, Aug 12 2010, 01:08:27)
[GCC 4.1.2 20070925 (Red Hat 4.1.2-27)] on linux2
Type "help", "copyright", "credits" or "license" for more information..... "foo"
....Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: attribute '__doc__' of 'type' objects is not writable


It's an unnecessary restriction, as far as I'm concerned, but an old one.
 
S

Steven D'Aprano

Well, it's incompatible with the Python compiler I keep in my head. Have
these developers no consideration for backward-thinking-compatibility?

+1 QOTW
 
S

Steven D'Aprano

Sometimes when using class inheritance, I want the overriding methods of
the subclass to get the docstring of the matching method in the base
class. You can do this with decorators (after the class definition),
with class decorators, and with metaclasses [1].


Here's some Python 3 code that uses a factory function as a metaclass to
inherit docstrings. Give the class a docstring of an empty string, and it
will be inherited from the first superclass found with a non-empty
docstring.



def InheritableDocstring(name, bases, dict):
mro = None
docstring = dict.get('__doc__')
if docstring == '':
# Search the MRO for the first non-empty docstring. We let Python
# do all the hard work of calculating the MRO.
mro = type('K', bases, {}).__mro__[1:] # Exclude the class K.
# Also exclude object.
assert mro[-1] == object
mro = mro[:-1]
for cls in mro:
if cls.__doc__:
docstring = cls.__doc__
break
else:
docstring = None
dict['__doc__'] = docstring
assert dict.get('__doc__') != ''
# Create the class we want, and return it.
cls = type(name, bases, dict)
if mro:
assert cls.__mro__ == (cls,) + mro + (object,)
return cls



class A(metaclass=InheritableDocstring):
pass

class B(A, metaclass=InheritableDocstring):
''

class C(B, metaclass=InheritableDocstring):
'A docstring.'

class D(B, metaclass=InheritableDocstring):
pass

class E(D, C, metaclass=InheritableDocstring):
''

class F(E, metaclass=InheritableDocstring):
''

assert all(cls.__doc__ is None for cls in (A, B, D))
assert all(cls.__doc__ == 'A docstring.' for cls in (C, E, F))
 
E

Eric Snow

Here's some Python 3 code that uses a factory function as a metaclass to
inherit docstrings. Give the class a docstring of an empty string, and it
will be inherited from the first superclass found with a non-empty
docstring.

Yeah, the idea of an empty docstring to trigger docstring inheritance
really appeals to me. Nice example. Incidently, aren't metaclasses
always inherited, as opposed to class decorators (which are never)?

-eric
def InheritableDocstring(name, bases, dict):
   mro = None
   docstring = dict.get('__doc__')
   if docstring == '':
       # Search the MRO for the first non-empty docstring. We letPython
       # do all the hard work of calculating the MRO.
       mro = type('K', bases, {}).__mro__[1:]  # Exclude the class K.
       # Also exclude object.
       assert mro[-1] == object
       mro = mro[:-1]
       for cls in mro:
           if cls.__doc__:
               docstring = cls.__doc__
               break
       else:
           docstring = None
       dict['__doc__'] = docstring
   assert dict.get('__doc__') != ''
   # Create the class we want, and return it.
   cls = type(name, bases, dict)
   if mro:
       assert cls.__mro__ == (cls,) + mro + (object,)
   return cls



class A(metaclass=InheritableDocstring):
   pass

class B(A, metaclass=InheritableDocstring):
   ''

class C(B, metaclass=InheritableDocstring):
   'A docstring.'

class D(B, metaclass=InheritableDocstring):
   pass

class E(D, C, metaclass=InheritableDocstring):
   ''

class F(E, metaclass=InheritableDocstring):
   ''

assert all(cls.__doc__ is None for cls in (A, B, D))
assert all(cls.__doc__ == 'A docstring.' for cls in (C, E, F))
 
S

Steven D'Aprano

Sometimes when using class inheritance, I want the overriding methods
of the subclass to get the docstring of the matching method in the base
class. You can do this with decorators (after the class definition),
with class decorators, and with metaclasses [1].


Here's some Python 3 code that uses a factory function as a metaclass to
inherit docstrings. Give the class a docstring of an empty string, and
it will be inherited from the first superclass found with a non-empty
docstring.
[...]


And here's a version using a more conventional metaclass. Extending this
to work on methods is left as an exercise.


class MetaDocstring(type):
@staticmethod
def get_mro(bases):
return type('K', bases, {}).__mro__[1:-1]
@staticmethod
def get_docstring(mro):
for k in mro:
if k.__doc__:
return k.__doc__
def __new__(cls, name, bases, dict):
mro = None
docstring = dict.get('__doc__')
if docstring == '':
mro = cls.get_mro(bases)
dict['__doc__'] = cls.get_docstring(mro)
assert dict.get('__doc__') != ''
# Create the class we want, and return it.
K = super().__new__(cls, name, bases, dict)
if mro:
assert K.__mro__ == (K,) + mro + (object,)
return K


class U(metaclass=MetaDocstring):
pass

class V(U):
''

class W(V):
'A docstring.'

class X(V):
pass

class Y(X, W):
''

class Z(Y):
''

assert all(type(cls) is MetaDocstring for cls in (U, V, W, X, Y, Z))
assert all(cls.__doc__ is None for cls in (U, V, X))
assert all(cls.__doc__ == 'A docstring.' for cls in (W, Y, Z))
 
S

Steven D'Aprano

Yeah, the idea of an empty docstring to trigger docstring inheritance
really appeals to me. Nice example. Incidently, aren't metaclasses
always inherited, as opposed to class decorators (which are never)?

Metaclasses are inherited, but the example I give uses a factory function
as a metaclass: it manipulates the docstring inside the dict, then
returns an ordinary class. That makes it just a fancy class decorator
using metaclass syntax.

The type of each class A, B, ... F is just type, which means that when
you subclass each class you don't get any magic metaclass behaviour
unless you explicitly set the metaclass directly.

That is:

assert type(A) is type

succeeds, so class B(A) doesn't do anything special unless you explicitly
set the metaclass.

I followed up with a second example using a conventional metaclass, that
is, where the type of each class is *not* type. In that case, the magic
behaviour is inherited and there's no need to explicitly set the
metaclass except for the first time.

(Whew. Talking about metaclasses is hard work.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top