Inheriting methods' docstrings in Python

后端 未结 5 409
无人共我
无人共我 2020-12-02 16:28

I have an OO hierarchy with docstrings that take as much maintenance as the code itself. E.g.,

class Swallow(object):
    def airspeed(self):
        \"\"\"R         


        
5条回答
  •  再見小時候
    2020-12-02 17:30

    This is a variation on Paul McGuire's DocStringInheritor metaclass.

    1. It inherits a parent member's docstring if the child member's docstring is empty.
    2. It inherits a parent class docstring if the child class docstring is empty.
    3. It can inherit the docstring from any class in any of the base classes's MROs, just like regular attribute inheritance.
    4. Unlike with a class decorator, the metaclass is inherited, so you only need to set the metaclass once in some top-level base class, and docstring inheritance will occur throughout your OOP hierarchy.

    import unittest
    import sys
    
    class DocStringInheritor(type):
        """
        A variation on
        http://groups.google.com/group/comp.lang.python/msg/26f7b4fcb4d66c95
        by Paul McGuire
        """
        def __new__(meta, name, bases, clsdict):
            if not('__doc__' in clsdict and clsdict['__doc__']):
                for mro_cls in (mro_cls for base in bases for mro_cls in base.mro()):
                    doc=mro_cls.__doc__
                    if doc:
                        clsdict['__doc__']=doc
                        break
            for attr, attribute in clsdict.items():
                if not attribute.__doc__:
                    for mro_cls in (mro_cls for base in bases for mro_cls in base.mro()
                                    if hasattr(mro_cls, attr)):
                        doc=getattr(getattr(mro_cls,attr),'__doc__')
                        if doc:
                            if isinstance(attribute, property):
                                clsdict[attr] = property(attribute.fget, attribute.fset, 
                                                         attribute.fdel, doc)
                            else:
                                attribute.__doc__ = doc
                            break
            return type.__new__(meta, name, bases, clsdict)
    
    
    
    class Test(unittest.TestCase):
    
        def test_null(self):
            class Foo(object):
    
                def frobnicate(self): pass
    
            class Bar(Foo, metaclass=DocStringInheritor):
                pass
    
            self.assertEqual(Bar.__doc__, object.__doc__)
            self.assertEqual(Bar().__doc__, object.__doc__)
            self.assertEqual(Bar.frobnicate.__doc__, None)
    
        def test_inherit_from_parent(self):
            class Foo(object):
                'Foo'
    
                def frobnicate(self):
                    'Frobnicate this gonk.'
            class Bar(Foo, metaclass=DocStringInheritor):
                pass
            self.assertEqual(Foo.__doc__, 'Foo')
            self.assertEqual(Foo().__doc__, 'Foo')
            self.assertEqual(Bar.__doc__, 'Foo')
            self.assertEqual(Bar().__doc__, 'Foo')
            self.assertEqual(Bar.frobnicate.__doc__, 'Frobnicate this gonk.')
    
        def test_inherit_from_mro(self):
            class Foo(object):
                'Foo'
    
                def frobnicate(self):
                    'Frobnicate this gonk.'
            class Bar(Foo):
                pass
    
            class Baz(Bar, metaclass=DocStringInheritor):
                pass
    
            self.assertEqual(Baz.__doc__, 'Foo')
            self.assertEqual(Baz().__doc__, 'Foo')
            self.assertEqual(Baz.frobnicate.__doc__, 'Frobnicate this gonk.')
    
        def test_inherit_metaclass_(self):
            class Foo(object):
                'Foo'
    
                def frobnicate(self):
                    'Frobnicate this gonk.'
            class Bar(Foo, metaclass=DocStringInheritor):
                pass
    
            class Baz(Bar):
                pass
            self.assertEqual(Baz.__doc__, 'Foo')
            self.assertEqual(Baz().__doc__, 'Foo')
            self.assertEqual(Baz.frobnicate.__doc__, 'Frobnicate this gonk.')
    
        def test_property(self):
            class Foo(object):
                @property
                def frobnicate(self): 
                    'Frobnicate this gonk.'
            class Bar(Foo, metaclass=DocStringInheritor):
                @property
                def frobnicate(self): pass
    
            self.assertEqual(Bar.frobnicate.__doc__, 'Frobnicate this gonk.')
    
    
    if __name__ == '__main__':
        sys.argv.insert(1, '--verbose')
        unittest.main(argv=sys.argv)
    

提交回复
热议问题