Override a method but inherit the docstring

Shai shai at platonix.com
Thu Jul 23 19:51:43 EDT 2009


On Jul 17, 10:52 am, Steven D'Aprano <st... at REMOVE-THIS-
cybersource.com.au> wrote:
>
> When the decorator is called, the function object is just a function
> object, not a method, so there is no concept of "what class it is
> destined for".
>

... which points to the better solution: use a descriptor. With the
doc_inherit decorator defined below, one may write

class Foo(object):
    def foo(self):
        "Frobber"
        pass

class Bar(Foo):
    @doc_inherit
    def foo(self):
        pass

and it appears to work. The code below is a little longish because we
need to do slightly different things when called for a class and for
an instance. But there's no need to repeat the parent name, no need
to look into namespaces (which, as you said, is probably messy and
fragile), and it seems pretty readable, too.

from functools import wraps

class DocInherit(object):
    """
    Docstring inheriting method descriptor

    The class itself is also used as a decorator
    """

    def __init__(self, mthd):
        self.mthd = mthd
        self.name = mthd.__name__

    def __get__(self, obj, cls):
        if obj:
            return self.get_with_inst(obj, cls)
        else:
            return self.get_no_inst(cls)

    def get_with_inst(self, obj, cls):

        overridden = getattr(super(cls, obj), self.name, None)

        @wraps(self.mthd, assigned=('__name__','__module__'))
        def f(*args, **kwargs):
            return self.mthd(obj, *args, **kwargs)

        return self.use_parent_doc(f, overridden)

    def get_no_inst(self, cls):

        for parent in cls.__mro__[1:]:
            overridden = getattr(parent, self.name, None)
            if overridden: break

        @wraps(self.mthd, assigned=('__name__','__module__'))
        def f(*args, **kwargs):
            return self.mthd(*args, **kwargs)

        return self.use_parent_doc(f, overridden)

    def use_parent_doc(self, func, source):
        if source is None:
            raise NameError, ("Can't find '%s' in parents"%self.name)
        func.__doc__ = source.__doc__
        return func

doc_inherit = DocInherit

Combining docstrings (as suggested by Jean-Paul Calderone), and
taking proper care of classmethods and staticmethods, are left
as an exercise to the reader.

Have fun,
Shai.



More information about the Python-list mailing list