views:

345

answers:

5

The problem: I have a class which contains a template method "execute" which calls another method "_execute". Subclasses are supposed to overwrite "_execute" to implement some specific functionality. This functionality should naturally be documented in the docstring of "_execute". I also expect users to create their own subclasses to extend the library. However, another user dealing with such a subclass should only use "execute", so he won't see the correct docstring if he uses "help(...)" in the interpreter.

Therefore it would be nice to modify the base class in such a way that in a subclass the docstring of "execute" is automatically replaced with that of "_execute". Any ideas how this might be done?

I was thinking of metaclasses to do this, to make this completely transparent to the user. I guess this approach applies to many places where the template pattern is used in Python.

Thanks!

A: 
John Montgomery
The thing is that I want the users to write subclasses without writing any boilerplate code for the docstring manipulation. Any idea how to handle this with metaclasses in a better way than my ugly solution mentioned above?
nikow
+2  A: 

Is there a reason you can't override the base class's execute function directly?

class Base(object):
    def execute(self):
        ...

class Derived(Base):
    def execute(self):
        """Docstring for derived class"""
        Base.execute(self)
        ...stuff specific to Derived...

If you don't want to do the above:

Method objects don't support writing to the __doc__ attribute, so you have to change __doc__ in the actual function object. Since you don't want to override the one in the base class, you'd have to give each subclass its own copy of execute:

class Derived(Base):
    def execute(self):
        return Base.execute(self)

    class _execute(self):
        """Docstring for subclass"""
        ...

    execute.__doc__= _execute.__doc__

but this is similar to a roundabout way of redefining execute...

dF
The motivation is that users write their own subclasses. I now clarified this in the original question.
nikow
+4  A: 

Well, if you don't mind copying the original method in the subclass, you can use the following technique.

import new

def copyfunc(func):
    return new.function(func.func_code, func.func_globals, func.func_name,
                        func.func_defaults, func.func_closure)

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        for key in attrs.keys():
            if key[0] == '_':
                skey = key[1:]
                for base in bases:
                    original = getattr(base, skey, None)
                    if original is not None:
                        copy = copyfunc(original)
                        copy.__doc__ = attrs[key].__doc__
                        attrs[skey] = copy
                        break
        return type.__new__(meta, name, bases, attrs)

class Class(object):
    __metaclass__ = Metaclass
    def execute(self):
        '''original doc-string'''
        return self._execute()

class Subclass(Class):
    def _execute(self):
        '''sub-class doc-string'''
        pass
Sylvain Defresne
Many thanks. I think this is what I was looking, since it is completelty transparent for the user writing subclasses (as far as I am aware of).
nikow
A: 

I agree that the simplest, most Pythonic way of approaching this is to simply redefine execute in your subclasses and have it call the execute method of the base class:

class Sub(Base):
    def execute(self):
        """New docstring goes here"""
        return Base.execute(self)

This is very little code to accomplish what you want; the only downside is that you must repeat this code in every subclass that extends Base. However, this is a small price to pay for the behavior you want.

If you want a sloppy and verbose way of making sure that the docstring for execute is dynamically generated, you can use the descriptor protocol, which would be significantly less code than the other proposals here. This is annoying because you can't just set a descriptor on an existing function, which means that execute must be written as a separate class with a __call__ method.

Here's the code to do this, but keep in mind that my above example is much simpler and more Pythonic:

class Executor(object):
    __doc__ = property(lambda self: self.inst._execute.__doc__)

    def __call__(self):
        return self.inst._execute()

class Base(object):
    execute = Executor()

class Sub(Base):
    def __init__(self):
        self.execute.inst = self

    def _execute(self):
        """Actually does something!"""
        return "Hello World!"

spam = Sub()
print spam.execute.__doc__  # prints "Actually does something!"
help(spam)                  # the execute method says "Actually does something!"
Eli Courtwright
A: 

Look at the functools.wraps() decorator; it does all of this, but I don't know offhand if you can get it to run in the right context