docstring inheritance

H

HughSW

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...hread/1e4075ba10dcbdd9/?#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:
Foo(object):
... def foo(self,
x):
...
'Frobber'
... print 'Foo.foo()',
x

Correct usage for overridden method foo(), incorrect usage for
method
bar()
Bar(Foo):
...
@inherit_doc
... def foo(self,
x):
... print 'Bar.foo()',
x
...
@inherit_doc
... def
bar(self):
... print
'Bar.bar()'
'Frobber'

True
Foo.foo
<unbound method
Foo.foo>Foo().foo(10)
Foo.foo()
10Bar.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
 
H

HughSW

Grrrr, sorry about the line wrapping horror; trying again.

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:
... def foo(self, x):
... 'Frobber'
... print 'Foo.foo()', x

Correct usage for overridden method foo(), incorrect usage for
method bar()
... @inherit_doc
... def foo(self, x):
... print 'Bar.foo()', x
... @inherit_doc
... def bar(self):
... print 'Bar.bar()'
'Frobber'
True
Bar.foo() 11
Traceback (most recent call last):
...
NameError: inherit_doc cannot find method 'bar' in parents of
'__main__.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
 

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

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top