Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #7273 > unrolled thread

how to inherit docstrings?

Started byEric Snow <ericsnowcurrently@gmail.com>
First post2011-06-09 00:22 -0600
Last post2011-06-10 17:19 +0000
Articles 13 — 4 participants

Back to article view | Back to comp.lang.python


Contents

  how to inherit docstrings? Eric Snow <ericsnowcurrently@gmail.com> - 2011-06-09 00:22 -0600
    Re: how to inherit docstrings? Ben Finney <ben+python@benfinney.id.au> - 2011-06-09 16:37 +1000
      Re: how to inherit docstrings? Eric Snow <ericsnowcurrently@gmail.com> - 2011-06-09 01:13 -0600
        Re: how to inherit docstrings? Ben Finney <ben+python@benfinney.id.au> - 2011-06-09 17:44 +1000
          Re: how to inherit docstrings? Duncan Booth <duncan.booth@invalid.invalid> - 2011-06-09 11:23 +0000
          Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-09 14:56 +0000
            Re: how to inherit docstrings? Ben Finney <ben+python@benfinney.id.au> - 2011-06-10 07:33 +1000
              Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-10 09:25 +0000
    Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-10 16:47 +0000
      Re: how to inherit docstrings? Eric Snow <ericsnowcurrently@gmail.com> - 2011-06-10 11:01 -0600
        Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-10 17:27 +0000
          Re: how to inherit docstrings? Eric Snow <ericsnowcurrently@gmail.com> - 2011-06-10 12:48 -0600
      Re: how to inherit docstrings? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-06-10 17:19 +0000

#7273 — how to inherit docstrings?

FromEric Snow <ericsnowcurrently@gmail.com>
Date2011-06-09 00:22 -0600
Subjecthow to inherit docstrings?
Message-ID<mailman.42.1307600576.11593.python-list@python.org>
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

[toc] | [next] | [standalone]


#7274

FromBen Finney <ben+python@benfinney.id.au>
Date2011-06-09 16:37 +1000
Message-ID<87k4cvy0gc.fsf@benfinney.id.au>
In reply to#7273
Eric Snow <ericsnowcurrently@gmail.com> writes:

> 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.

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.

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.

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

[toc] | [prev] | [next] | [standalone]


#7278

FromEric Snow <ericsnowcurrently@gmail.com>
Date2011-06-09 01:13 -0600
Message-ID<mailman.44.1307603589.11593.python-list@python.org>
In reply to#7274
On Thu, Jun 9, 2011 at 12:37 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
> Eric Snow <ericsnowcurrently@gmail.com> writes:
>
>> 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.
>
> 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:

>>> class X:
...     "some doc"
...
>>> X.__doc__
'some doc'
>>> X.__doc__ = "another 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 God than |
>  `\         we are free to adopt unjustified beliefs about science or |
> _o__)              history […].” —Sam Harris, _The End of Faith_, 2004 |
> Ben Finney
> --
> http://mail.python.org/mailman/listinfo/python-list
>

[toc] | [prev] | [next] | [standalone]


#7280

FromBen Finney <ben+python@benfinney.id.au>
Date2011-06-09 17:44 +1000
Message-ID<87fwnjxxcv.fsf@benfinney.id.au>
In reply to#7278
Eric Snow <ericsnowcurrently@gmail.com> writes:

> AttributeError: attribute '__doc__' of 'type' objects is not writable
>
> That is on 3.3.

Well, that sucks :-(

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

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

Yes, that'd be nice. Do you have a specification in mind for how it
would work? Perhaps time to start a thread on the ‘python-ideas’ forum.

-- 
 \       “Following fashion and the status quo is easy. Thinking about |
  `\        your users' lives and creating something practical is much |
_o__)                                harder.” —Ryan Singer, 2008-07-09 |
Ben Finney

[toc] | [prev] | [next] | [standalone]


#7286

FromDuncan Booth <duncan.booth@invalid.invalid>
Date2011-06-09 11:23 +0000
Message-ID<Xns9EFF7DF666E20duncanbooth@127.0.0.1>
In reply to#7280
Ben Finney <ben+python@benfinney.id.au> wrote:

> Eric Snow <ericsnowcurrently@gmail.com> writes:
> 
>> AttributeError: attribute '__doc__' of 'type' objects is not writable
>>
>> That is on 3.3.
> 
> Well, that sucks :-(
> 
> Where can we see the discussion of that change before it was
> implemented?
> 

Change? What change?

C:\Python27>python
Python 2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit 
(Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.
>>> class C(object):
...    "Hello world"
...
>>> C.__doc__ = "whatever"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: attribute '__doc__' of 'type' objects is not writable
>>>


Or even:
Python 2.3.5 (#1, Oct 13 2005, 09:17:23)
[GCC 3.2.3 20030502 (Red Hat Linux 3.2.3-52)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class C(object):
...    "Hello world"
...
>>> C.__doc__ = "whatever"
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: attribute '__doc__' of 'type' objects is not writable
>>>


-- 
Duncan Booth http://kupuguy.blogspot.com

[toc] | [prev] | [next] | [standalone]


#7303

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-06-09 14:56 +0000
Message-ID<4df0df26$0$29977$c3e8da3$5496439d@news.astraweb.com>
In reply to#7280
On Thu, 09 Jun 2011 17:44:32 +1000, Ben Finney wrote:

> Eric Snow <ericsnowcurrently@gmail.com> writes:
> 
>> AttributeError: attribute '__doc__' of 'type' objects is not writable
>>
>> That is on 3.3.
> 
> 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.
>>> class K(object):
...     "foo"
...
>>> K.__doc__ = 'bar'
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.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#7321

FromBen Finney <ben+python@benfinney.id.au>
Date2011-06-10 07:33 +1000
Message-ID<87y61awuz5.fsf@benfinney.id.au>
In reply to#7303
Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes:

> On Thu, 09 Jun 2011 17:44:32 +1000, Ben Finney wrote:
>
> > Eric Snow <ericsnowcurrently@gmail.com> writes:
> > 
> >> AttributeError: attribute '__doc__' of 'type' objects is not writable
> >>
> >> That is on 3.3.
> > 
> > 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.
[…]

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

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

-- 
 \          “The fact that I have no remedy for all the sorrows of the |
  `\     world is no reason for my accepting yours. It simply supports |
_o__)  the strong probability that yours is a fake.” —Henry L. Mencken |
Ben Finney

[toc] | [prev] | [next] | [standalone]


#7364

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-06-10 09:25 +0000
Message-ID<4df1e300$0$29977$c3e8da3$5496439d@news.astraweb.com>
In reply to#7321
On Fri, 10 Jun 2011 07:33:34 +1000, Ben Finney wrote:

> Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes:

>> It's an unnecessary restriction, as far as I'm concerned, but an old
>> one.
> 
> Well, it's incompatible with the Python compiler I keep in my head. Have
> these developers no consideration for backward-thinking-compatibility?

+1 QOTW


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#7382

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-06-10 16:47 +0000
Message-ID<4df24a87$0$30002$c3e8da3$5496439d@news.astraweb.com>
In reply to#7273
On Thu, 09 Jun 2011 00:22:54 -0600, Eric Snow wrote:

> 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))



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#7385

FromEric Snow <ericsnowcurrently@gmail.com>
Date2011-06-10 11:01 -0600
Message-ID<mailman.86.1307725303.11593.python-list@python.org>
In reply to#7382
On Fri, Jun 10, 2011 at 10:47 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> 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 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))
>
>
>
> --
> Steven
> --
> http://mail.python.org/mailman/listinfo/python-list
>

[toc] | [prev] | [next] | [standalone]


#7388

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-06-10 17:27 +0000
Message-ID<4df25403$0$30002$c3e8da3$5496439d@news.astraweb.com>
In reply to#7385
On Fri, 10 Jun 2011 11:01:41 -0600, Eric Snow wrote:

> On Fri, Jun 10, 2011 at 10:47 AM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> 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)?

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.)


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#7394

FromEric Snow <ericsnowcurrently@gmail.com>
Date2011-06-10 12:48 -0600
Message-ID<mailman.94.1307731741.11593.python-list@python.org>
In reply to#7388
FYI, I started this topic up on python-ideas, as it seemed valid
enough from the responses I've gotten here [1].

-eric

[1] http://mail.python.org/pipermail/python-ideas/2011-June/010473.html

[toc] | [prev] | [next] | [standalone]


#7386

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-06-10 17:19 +0000
Message-ID<4df25210$0$30002$c3e8da3$5496439d@news.astraweb.com>
In reply to#7382
On Fri, 10 Jun 2011 16:47:03 +0000, Steven D'Aprano wrote:

> On Thu, 09 Jun 2011 00:22:54 -0600, Eric Snow wrote:
> 
>> 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))


-- 
Steven

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web