Index: trunk/Lib/pydoc.py =================================================================== --- trunk/Lib/pydoc.py (revision 61445) +++ trunk/Lib/pydoc.py (working copy) @@ -82,6 +82,30 @@ result = inspect.getdoc(object) or inspect.getcomments(object) return result and re.sub('^ *\n', '', rstrip(result)) or '' +def get_doc_ancestor(methodObject): + '''Return (docString, class) for a possibly 'inherited' docs. + + The documentation is for the first class in the method resolution + order with a doc string or comment for a method with the same name + as methodObject's, or ('', ) if none is found. + + ''' + name = methodObject.__name__ + imclass = methodObject.im_class + doc = getdoc(methodObject.im_func) + if not doc: + mro = inspect.getmro(imclass)[1:] + for ancestor in mro: + if name in ancestor.__dict__: + attr = getattr(ancestor, name) + if inspect.ismethod(attr): + doc = getdoc(attr) + if doc: + return (doc, ancestor) + else: # no docs to substitute if namesake not a method + break + return (doc, imclass) + def splitdoc(doc): """Split a doc string into a synopsis line (if any) and the rest.""" lines = split(strip(doc), '\n') @@ -308,6 +332,7 @@ except AttributeError: return None return module + # ---------------------------------------------------- formatter base class class Doc: @@ -594,6 +619,7 @@ if sys.platform == 'win32': import nturl2path url = nturl2path.pathname2url(path) + #url = '///' + path #.replace('\\','/') # \ or / in firefox path OK filelink = '%s' % (url, path) except TypeError: filelink = '(built-in)' @@ -848,6 +874,7 @@ anchor = (cl and cl.__name__ or '') + '-' + name note = '' skipdocs = 0 + doc = '' if inspect.ismethod(object): imclass = object.im_class if cl: @@ -859,8 +886,13 @@ object.im_self.__class__, mod) else: note = ' unbound %s method' % self.classlink(imclass,mod) + + (doc, ancestor) = get_doc_ancestor(object) + if ancestor is not imclass: + note +=', docs of inherited %s' % self.classlink(ancestor, mod) + object = object.im_func - + if name == realname: title = '%s' % (anchor, realname) else: @@ -890,7 +922,7 @@ return '
%s
\n' % decl else: doc = self.markup( - getdoc(object), self.preformat, funcs, classes, methods) + doc or getdoc(object), self.preformat, funcs, classes, methods) doc = doc and '
%s
' % doc return '
%s
%s
\n' % (decl, doc) @@ -933,7 +965,6 @@ return self.bigsection(dir, '#ffffff', '#ee77aa', contents) # -------------------------------------------- text documentation generator - class TextRepr(Repr): """Class for safely making a text representation of a Python object.""" def __init__(self): @@ -1230,6 +1261,7 @@ name = name or realname note = '' skipdocs = 0 + doc = '' if inspect.ismethod(object): imclass = object.im_class if cl: @@ -1241,8 +1273,13 @@ object.im_self.__class__, mod) else: note = ' unbound %s method' % classname(imclass,mod) + + (doc, ancestor) = get_doc_ancestor(object) + if ancestor is not imclass: + note +=', docs of inherited %s' % classname(ancestor, mod) + object = object.im_func - + if name == realname: title = self.bold(realname) else: @@ -1264,7 +1301,7 @@ if skipdocs: return decl + '\n' else: - doc = getdoc(object) or '' + doc = doc or getdoc(object) or '' return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n') def _docdescriptor(self, name, value, mod): Index: trunk/Lib/test/test_pydoc.html =================================================================== --- trunk/Lib/test/test_pydoc.html (revision 0) +++ trunk/Lib/test/test_pydoc.html (revision 0) @@ -0,0 +1,160 @@ + + +Python: module test.test_pydoc_sample + + + + +
 
+ 
test.test_pydoc_sample
index
c:\anh\python\pydev\trunk\lib\test\test_pydoc_sample.py
+

Sample module used by test_pydoc_inheritance.py.

+Docs for this module are generated, testing
+'inherited' doc strings for pydoc.
+Function names encode for the places where there are
+docs in the source code in the subclassing sequence ABC.
+AB or C indicate classes where the function with this name has docs.
+a, b or c indicate classes with a function of this name but no docs.

+

+ + + + + +
 
+Classes
       
+
__builtin__.object +
+
+
A +
+
+
B +
+
+
C +
+
+
+
+
+
+
+

+ + + + + +
 
+class A(__builtin__.object)
    Methods defined here:
+
fABc(self, x)
This is fABc's doc string in A.
+ +
fAbc(self, x)
This fAbc's doc string in A.
+ +
fAc(self, x)
This is fAc's doc string in A.
+ +
faBc(self)
+ +
fabC(self)
+ +
fabc(self)
+ +
+Data descriptors defined here:
+
__dict__
+
dictionary for instance variables (if defined)
+
+
__weakref__
+
list of weak references to the object (if defined)
+
+

+ + + + + +
 
+class B(A)
    
Method resolution order:
+
B
+
A
+
__builtin__.object
+
+
+Methods defined here:
+
fABc(self, x)
This is fABc's doc string in B.
+ +
fAbc(self, x), docs of inherited A
This fAbc's doc string in A.
+ +
fBc(self)
This is fBc's doc string in B.
+ +
faBc(self)
This is faBc's doc string in B.
+ +
fabC(self)
+ +
fabc(self)
+ +
fbC(self)
+ +
+Methods inherited from A:
+
fAc(self, x)
This is fAc's doc string in A.
+ +
+Data descriptors inherited from A:
+
__dict__
+
dictionary for instance variables (if defined)
+
+
__weakref__
+
list of weak references to the object (if defined)
+
+

+ + + + + +
 
+class C(B)
    
Method resolution order:
+
C
+
B
+
A
+
__builtin__.object
+
+
+Methods defined here:
+
fABc(self, x), docs of inherited B
This is fABc's doc string in B.
+ +
fAbc(self, x), docs of inherited A
This fAbc's doc string in A.
+ +
fAc(self, x), docs of inherited A
This is fAc's doc string in A.
+ +
fBc(self), docs of inherited B
This is fBc's doc string in B.
+ +
faBc(self), docs of inherited B
This is faBc's doc string in B.
+ +
fabC(self)
This is fabC's doc string in C.
+ +
fabc(self)
+ +
fbC(self)
This is fbC's doc string in C.
+ +
+Data descriptors inherited from A:
+
__dict__
+
dictionary for instance variables (if defined)
+
+
__weakref__
+
list of weak references to the object (if defined)
+
+

+ + + + + +
 
+Data
       __package__ = None
+ \ No newline at end of file Index: trunk/Lib/test/test_pydoc_sample.py =================================================================== --- trunk/Lib/test/test_pydoc_sample.py (revision 0) +++ trunk/Lib/test/test_pydoc_sample.py (revision 0) @@ -0,0 +1,59 @@ +'''Sample module used by test_pydoc_inheritance.py. + +Docs for this module are generated, testing +'inherited' doc strings for pydoc. +Function names encode for the places where there are +docs in the source code in the subclassing sequence A, B, C. +A, B or C indicate classes where the function with this name has docs. +a, b or c indicate classes with a function of this name but no docs. + +''' +class A(object): + def fAbc(self, x): + '''This fAbc's doc string in A.''' + def fABc(self, x): + '''This is fABc's doc string in A.''' + def fAc(self, x): + '''This is fAc's doc string in A.''' + def fabC(self): + pass + def faBc(self): + pass + def fabc(self): + pass + + +class B(A): + def fAbc(self, x): + pass + def fABc(self, x): + '''This is fABc's doc string in B.''' + def fabC(self): + pass + def faBc(self): + '''This is faBc's doc string in B.''' + def fBc(self): + '''This is fBc's doc string in B.''' + def fbC(self): + pass + def fabc(self): + pass + + +class C(B): + def fAbc(self, x): + pass + def fABc(self, x): + pass + def fabC(self): + '''This is fabC's doc string in C.''' + def faBc(self): + pass + def fAc(self, x): + pass + def fBc(self): + pass + def fbC(self): + '''This is fbC's doc string in C.''' + def fabc(self): + pass Index: trunk/Lib/test/test_pydoc.txt =================================================================== --- trunk/Lib/test/test_pydoc.txt (revision 0) +++ trunk/Lib/test/test_pydoc.txt (revision 0) @@ -0,0 +1,132 @@ +NNAAMMEE + test.test_pydoc_sample - Sample module used by test_pydoc_inheritance.py. + +FFIILLEE + c:\anh\python\pydev\trunk\lib\test\test_pydoc_sample.py + +DDEESSCCRRIIPPTTIIOONN + Docs for this module are generated, testing + 'inherited' doc strings for pydoc. + Function names encode for the places where there are + docs in the source code in the subclassing sequence A, B, C. + A, B or C indicate classes where the function with this name has docs. + a, b or c indicate classes with a function of this name but no docs. + +CCLLAASSSSEESS + __builtin__.object + A + B + C + + class AA(__builtin__.object) + | Methods defined here: + | + | ffAABBcc(self, x) + | This is fABc's doc string in A. + | + | ffAAbbcc(self, x) + | This fAbc's doc string in A. + | + | ffAAcc(self, x) + | This is fAc's doc string in A. + | + | ffaaBBcc(self) + | + | ffaabbCC(self) + | + | ffaabbcc(self) + | + | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | ____ddiicctt____ + | dictionary for instance variables (if defined) + | + | ____wweeaakkrreeff____ + | list of weak references to the object (if defined) + + class BB(A) + | Method resolution order: + | B + | A + | __builtin__.object + | + | Methods defined here: + | + | ffAABBcc(self, x) + | This is fABc's doc string in B. + | + | ffAAbbcc(self, x), docs of inherited A + | This fAbc's doc string in A. + | + | ffBBcc(self) + | This is fBc's doc string in B. + | + | ffaaBBcc(self) + | This is faBc's doc string in B. + | + | ffaabbCC(self) + | + | ffaabbcc(self) + | + | ffbbCC(self) + | + | ---------------------------------------------------------------------- + | Methods inherited from A: + | + | ffAAcc(self, x) + | This is fAc's doc string in A. + | + | ---------------------------------------------------------------------- + | Data descriptors inherited from A: + | + | ____ddiicctt____ + | dictionary for instance variables (if defined) + | + | ____wweeaakkrreeff____ + | list of weak references to the object (if defined) + + class CC(B) + | Method resolution order: + | C + | B + | A + | __builtin__.object + | + | Methods defined here: + | + | ffAABBcc(self, x), docs of inherited B + | This is fABc's doc string in B. + | + | ffAAbbcc(self, x), docs of inherited A + | This fAbc's doc string in A. + | + | ffAAcc(self, x), docs of inherited A + | This is fAc's doc string in A. + | + | ffBBcc(self), docs of inherited B + | This is fBc's doc string in B. + | + | ffaaBBcc(self), docs of inherited B + | This is faBc's doc string in B. + | + | ffaabbCC(self) + | This is fabC's doc string in C. + | + | ffaabbcc(self) + | + | ffbbCC(self) + | This is fbC's doc string in C. + | + | ---------------------------------------------------------------------- + | Data descriptors inherited from A: + | + | ____ddiicctt____ + | dictionary for instance variables (if defined) + | + | ____wweeaakkrreeff____ + | list of weak references to the object (if defined) + +DDAATTAA + ____ppaacckkaaggee____ = None + Index: trunk/Lib/test/test_pydoc_inheritance.py =================================================================== --- trunk/Lib/test/test_pydoc_inheritance.py (revision 0) +++ trunk/Lib/test/test_pydoc_inheritance.py (revision 0) @@ -0,0 +1,85 @@ +'''Test the 'inheritance' of method docs in pydoc. + +Compare the doc text for a sample module to +the text previously generated and checked, and saved +in files test_pydoc.txt and test_pydoc.html. +The initial part of the documentation through the +local file name is stripped before comparison. + +After intentional changes to the pydoc format, +run the regenerate_data() function and manually +check the new versions of the files generated. + +''' +import pydoc +import os.path + +from test import test_pydoc_sample + +DATAFILEBASE = 'test_pydoc' +TEXT = 'txt' +HTML = 'html' + +def get_data_filename(extension): + '''Return the doc data file name with extension txt or html.''' + basedir = os.path.split(test_pydoc_sample.__file__)[0] + return os.path.join(basedir, DATAFILEBASE + '.' + extension) + +def generate_docs(extension): + '''Generate and return sample doc string with current pydoc. + + This works for both extensions 'txt' and 'html'. + + ''' + if extension == TEXT: + return pydoc.text.docmodule(test_pydoc_sample) + return pydoc.html.page(pydoc.describe(test_pydoc_sample), + pydoc.html.document(test_pydoc_sample)) + +def _str2file(s, filename): + '''Make a file containing string s.''' + outf = open(filename, 'w') + outf.write(s) + outf.close() + +def _file2str(filename): + '''Return the file's contents.''' + inf = open(filename) + s = inf.read() + inf.close() + return s + +def regenerate_data(): + '''Recreate pydoc 'inheritance' reference data. + + Call after the pydoc output format is intentionally changed. + + ''' + for extension in [TEXT, HTML]: + _str2file(generate_docs(extension), get_data_filename(extension)) + +def get_reference_docs(extension): + '''Read and return file contents with extension 'txt' or 'html'.''' + return _file2str(get_data_filename(extension)) + +def _strip_to(s, target): + '''Return the part of s from target onward.''' + return s[s.index(target) : ] + +def main(): + '''Compare pydoc 'inheritance' output to reference data. + + Test both text and html versions. The initial part of the text + is removed before comparison. This eliminates the local + file reference at the beginning of the text. + + ''' + for extension, marker in [(TEXT, pydoc.text.bold('DESCRIPTION')), + (HTML, '')]: + newData = _strip_to(generate_docs(extension), marker) + refData = _strip_to(get_reference_docs(extension), marker) + assert newData == refData + print "pydoc 'inheritance'", extension, 'verson OK' + +if __name__ == '__main__': + main()