We've been trying to find an easy way to inherit docstrings when overriding methods in subclasses, e.g. so that Sphinx does a nice job.
We used Shai's DocInherit code from this post: http://groups.google.com/group/comp.lang.python/tree/browse_frm/thread/1e4075ba10dcbdd9/f63651cd9e76df63?rnum=1&_done=%2Fgroup%2Fcomp.lang.python%2Fbrowse_frm%2Fthread%2F1e4075ba10dcbdd9%2F%3F#doc_5f5963372b951d79 of this thread http://groups.google.com/group/comp.lang.python/browse_frm/thread/1e4075ba10dcbdd9/ The DocInherit property works fine, except that all the logic for adding the docstring to the method is evaluated every single time the method is looked up. I experimented with a simple caching scheme but abandoned it. I've tried a new approach -- it uses the same property mechanism as Shai's code to delay doing anything until the class exists and the method is used -- but it does its work only once, the first time the method is looked up; it adds the docstring from the first mro parent, and then the property *replaces itself* in the class's namespace with the method that now has a docstring. It appears to work in a large project with extensive testing. My question concerns whether it is risky to replace a class property attribute to a method attribute after the class has been created, and perhaps after the class has been used? Does this kind of change violate some subtle or internal expectations of the interpreter runtime? Thanks. -Hugh Here's the code and doctests: class InheritDoc(object): """ Docstring inheriting method descriptor The class itself is used as a decorator that creates a class property for the method; the first time the property is used it installs the method's doc and then replaces itself as a class attribute with the method! Usage: >>> class Foo(object): ... def foo(self, x): ... 'Frobber' ... print 'Foo.foo()', x Correct usage for overridden method foo(), incorrect usage for method bar() >>> class Bar(Foo): ... @inherit_doc ... def foo(self, x): ... print 'Bar.foo()', x ... @inherit_doc ... def bar(self): ... print 'Bar.bar()' >>> Bar.foo.__doc__ == Bar().foo.__doc__ == Foo.foo.__doc__ == 'Frobber' True >>> Foo.foo <unbound method Foo.foo> >>> Foo().foo(10) Foo.foo() 10 >>> Bar.foo <unbound method Bar.foo> >>> Bar().foo(11) Bar.foo() 11 >>> Bar.bar Traceback (most recent call last): ... NameError: inherit_doc cannot find method 'bar' in parents of '__main__.Bar' >>> Bar().bar Traceback (most recent call last): ... NameError: inherit_doc cannot find method 'bar' in parents of '__main__.Bar' """ __slots__ = 'inherit_doc_unbound_method' def __init__(self, unbound_method): self.inherit_doc_unbound_method = unbound_method def __get__(self, obj, cls): # a self-destructing descriptor/ property: # the first and only time it's used, it fixes the method's doc and then # replaces itself with the method # find the overridden method in mro sequence, skipping the class itself method_name = self.inherit_doc_unbound_method.__name__ mro_iter = iter(cls.__mro__) mro_iter.next() for parent in mro_iter: overridden = getattr(parent, method_name, None) if overridden is not None: break if overridden is None: raise NameError('inherit_doc cannot find method %r in parents of %r' % (method_name, '%s.%s'%(cls.__module__, cls.__name__))) # XXX next steps are not threadsafe, maybe not safe at all! # set the doc self.inherit_doc_unbound_method.__doc__ = overridden.__doc__ # replace the property with the function setattr(cls, method_name, self.inherit_doc_unbound_method) # use the replacement return getattr(obj if obj is not None else cls, method_name) inherit_doc = InheritDoc -- http://mail.python.org/mailman/listinfo/python-list