Dynamic documentation for instances of classes

The functionality in this module allows to define specific docstrings of instances of a class, which are different from the class docstring. A typical use case is given by cached methods: the documentation of a cached method should not be the documentation of the class CachedMethod; it should be the documentation of the underlying method.

In order to use this, define a class docstring as usual. Also define a method def _instancedoc_(self) which should return the docstring of the instance self. Finally, add the decorator @instancedoc to the class.

Warning

Since the __doc__ attribute is never inherited, the decorator @instancedoc must be added to all subclasses of the class defining _instancedoc_. Doing it on the base class is not sufficient.

EXAMPLES:

sage: from sage.docs.instancedoc import instancedoc
sage: @instancedoc
....: class X(object):
....:     "Class docstring"
....:     def _instancedoc_(self):
....:         return "Instance docstring"
sage: X.__doc__
'Class docstring'
sage: X().__doc__
'Instance docstring'

For a Cython cdef class, a decorator cannot be used. Instead, call instancedoc() as a function after defining the class:

sage: cython('''
....: from sage.docs.instancedoc import instancedoc
....: cdef class Y:
....:     "Class docstring"
....:     def _instancedoc_(self):
....:         return "Instance docstring"
....: instancedoc(Y)
....: ''')
sage: Y.__doc__
'File:...\nClass docstring'
sage: Y().__doc__
'Instance docstring'

One can still add a custom __doc__ attribute on a particular instance:

sage: obj = X()
sage: obj.__doc__ = "Very special doc"
sage: print(obj.__doc__)
Very special doc

This normally does not work on extension types:

sage: Y().__doc__ = "Very special doc"
Traceback (most recent call last):
...
AttributeError: attribute '__doc__' of 'Y' objects is not writable

This is an example involving a metaclass, where the instances are classes. In this case, the _instancedoc_ from the metaclass is only used if the instance of the metaclass (the class) does not have a docstring:

sage: @instancedoc
....: class Meta(type):
....:     "Metaclass doc"
....:     def _instancedoc_(self):
....:         return "Docstring for {}".format(self)
sage: class T(metaclass=Meta):
....:     pass
sage: print(T.__doc__)
Docstring for <class '__main__.T'>
sage: class U(metaclass=Meta):
....:     "Special doc for U"
sage: print(U.__doc__)
Special doc for U
class sage.docs.instancedoc.InstanceDocDescriptor

Bases: object

Descriptor for dynamic documentation, to be installed as the __doc__ attribute.

INPUT:

  • classdoc – (string) class documentation

  • instancedoc – (method) documentation for an instance

  • attr – (string, default __doc__) attribute name to use for custom docstring on the instance.

EXAMPLES:

sage: from sage.docs.instancedoc import InstanceDocDescriptor
sage: def instancedoc(self):
....:     return "Instance doc"
sage: docattr = InstanceDocDescriptor("Class doc", instancedoc)
sage: class Z(object):
....:     __doc__ = InstanceDocDescriptor("Class doc", instancedoc)
sage: Z.__doc__
'Class doc'
sage: Z().__doc__
'Instance doc'

We can still override the __doc__ attribute of the instance:

sage: obj = Z()
sage: obj.__doc__ = "Custom doc"
sage: obj.__doc__
'Custom doc'
sage: del obj.__doc__
sage: obj.__doc__
'Instance doc'
sage.docs.instancedoc.instancedoc(cls)

Add support for _instancedoc_ to the class cls.

Typically, this will be used as decorator.

INPUT:

  • cls – a new-style class

OUTPUT: cls

Warning

instancedoc mutates the given class. So you are not supposed to use it as newcls = instancedoc(cls) because that would mutate cls (and newcls would be the same object as cls)