diff --git a/CHANGES b/CHANGES index 297c8c947..ccce082bf 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,10 @@ Release 1.1 (in development) * #460: Allow limiting the depth of section numbers for HTML. +* #564: Add :confval:`autodoc_docstring_signature` which retrieves + the signature from the first line of the docstring, if it is + found there. + * #176: Provide ``private-members`` option for autodoc directives. * #520: Provide ``special-members`` option for autodoc directives. diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index 8026972ce..483b30fab 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -285,6 +285,19 @@ There are also new config values that you can set: .. versionadded:: 1.0 +.. confval:: autodoc_docstring_signature + + Functions imported from C modules cannot be introspected, and therefore the + signature for such functions cannot be automatically determined. However, it + is a well- convention + + If this boolean value is set to ``True`` (which is the default), autodoc will + look at the first line of the docstring for functions and methods, and if it + looks like a signature, use the line as the signature and remove it from the + docstring content. + + .. versionadded:: 1.1 + Docstring preprocessing ----------------------- diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 993f690ab..851fcf87c 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -827,7 +827,53 @@ class ClassLevelDocumenter(Documenter): return modname, parents + [base] -class FunctionDocumenter(ModuleLevelDocumenter): +class DocstringSignatureMixin(object): + """ + Mixin for FunctionDocumenter and MethodDocumenter to provide the + feature of reading the signature from the docstring. + """ + + def _find_signature(self, encoding=None): + docstrings = Documenter.get_doc(self, encoding) + if len(docstrings) != 1: + return + doclines = docstrings[0] + setattr(self, '__new_doclines', doclines) + if not doclines: + return + # match first line of docstring against signature RE + match = py_ext_sig_re.match(doclines[0]) + if not match: + return + exmod, path, base, args, retann = match.groups() + # the base name must match ours + if not self.objpath or base != self.objpath[-1]: + return + # ok, now jump over remaining empty lines and set the remaining + # lines as the new doclines + i = 1 + while i < len(doclines) and not doclines[i].strip(): + i += 1 + setattr(self, '__new_doclines', doclines[i:]) + return args, retann + + def get_doc(self, encoding=None): + lines = getattr(self, '__new_doclines', None) + if lines is not None: + return [lines] + return Documenter.get_doc(self, encoding) + + def format_signature(self): + if self.args is None and self.env.config.autodoc_docstring_signature: + # only act if a signature is not explicitly given already, and if + # the feature is enabled + result = self._find_signature() + if result is not None: + self.args, self.retann = result + return Documenter.format_signature(self) + + +class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): """ Specialized Documenter subclass for functions. """ @@ -841,8 +887,8 @@ class FunctionDocumenter(ModuleLevelDocumenter): def format_args(self): if inspect.isbuiltin(self.object) or \ inspect.ismethoddescriptor(self.object): - # can never get arguments of a C function or method - return None + # cannot introspect arguments of a C function or method + pass try: argspec = inspect.getargspec(self.object) except TypeError: @@ -1008,7 +1054,7 @@ class DataDocumenter(ModuleLevelDocumenter): pass -class MethodDocumenter(ClassLevelDocumenter): +class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): """ Specialized Documenter subclass for methods (normal, static and class). """ @@ -1235,6 +1281,7 @@ def setup(app): app.add_config_value('autoclass_content', 'class', True) app.add_config_value('autodoc_member_order', 'alphabetic', True) app.add_config_value('autodoc_default_flags', [], True) + app.add_config_value('autodoc_docstring_signature', True, True) app.add_event('autodoc-process-docstring') app.add_event('autodoc-process-signature') app.add_event('autodoc-skip-member') diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index d70fa2606..74e69a68c 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -492,6 +492,13 @@ def test_generate(): ], 'class', 'Class', member_order='bysource', all_members=True) + # test autodoc_docstring_signature + assert_result_contains( + '.. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', 'method', + 'test_autodoc.DocstringSig.meth') + assert_result_contains( + ' rest of docstring', 'method', 'test_autodoc.DocstringSig.meth') + # --- generate fodder ------------ @@ -582,3 +589,12 @@ class Outer(object): # should be documented as an alias factory = dict + + +class DocstringSig(object): + def meth(self): + """ + meth(FOO, BAR=1) -> BAZ + + rest of docstring + """