diff options
author | Georg Brandl <georg@python.org> | 2014-01-19 11:15:24 +0100 |
---|---|---|
committer | Georg Brandl <georg@python.org> | 2014-01-19 11:15:24 +0100 |
commit | 6a5de08f929ef4dc58f68122dea364af4ed212e5 (patch) | |
tree | fc43faa61088ceb82e3a7e8025240d8c685111b1 /sphinx/ext/autodoc.py | |
parent | d413fd5731de449c1f69a6c8f4898fd7645fd494 (diff) | |
parent | 88a6b565ac65a33f2798b8cbc504eb147b9a9d87 (diff) | |
download | sphinx-6a5de08f929ef4dc58f68122dea364af4ed212e5.tar.gz |
Merged in saschpe/sphinx (pull request #193)
BuildDoc shouldn't fail on Unicode paths.
Diffstat (limited to 'sphinx/ext/autodoc.py')
-rw-r--r-- | sphinx/ext/autodoc.py | 84 |
1 files changed, 71 insertions, 13 deletions
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 348c072c..86837ff8 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -28,8 +28,8 @@ from sphinx.application import ExtensionError from sphinx.util.nodes import nested_parse_with_titles from sphinx.util.compat import Directive from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \ - safe_getattr, safe_repr -from sphinx.util.pycompat import base_exception, class_types + safe_getattr, safe_repr, is_builtin_class_method +from sphinx.util.pycompat import class_types from sphinx.util.docstrings import prepare_docstring @@ -70,6 +70,35 @@ class Options(dict): return None +class _MockModule(object): + """Used by autodoc_mock_imports.""" + def __init__(self, *args, **kwargs): + pass + + def __call__(self, *args, **kwargs): + return _MockModule() + + @classmethod + def __getattr__(cls, name): + if name in ('__file__', '__path__'): + return '/dev/null' + elif name[0] == name[0].upper(): + # Not very good, we assume Uppercase names are classes... + mocktype = type(name, (), {}) + mocktype.__module__ = __name__ + return mocktype + else: + return _MockModule() + +def mock_import(modname): + if '.' in modname: + pkg, _n, mods = modname.rpartition('.') + mock_import(pkg) + mod = _MockModule() + sys.modules[modname] = mod + return mod + + ALL = object() INSTANCEATTR = object() @@ -332,6 +361,9 @@ class Documenter(object): self.modname, '.'.join(self.objpath)) try: dbg('[autodoc] import %s', self.modname) + for modname in self.env.config.autodoc_mock_imports: + dbg('[autodoc] adding a mock module %s!', self.modname) + mock_import(modname) __import__(self.modname) parent = None obj = self.module = sys.modules[self.modname] @@ -450,9 +482,10 @@ class Documenter(object): # into lines if isinstance(docstring, unicode): return [prepare_docstring(docstring, ignore)] - elif docstring: + elif isinstance(docstring, str): # this will not trigger on Py3 return [prepare_docstring(force_decode(docstring, encoding), ignore)] + # ... else it is something strange, let's ignore it return [] def process_doc(self, docstrings): @@ -957,6 +990,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): try: argspec = getargspec(self.object) except TypeError: + if (is_builtin_class_method(self.object, '__new__') and + is_builtin_class_method(self.object, '__init__')): + raise TypeError('%r is a builtin class' % self.object) + # if a class should be documented as function (yay duck # typing) we try to use the constructor signature as function # signature without the first argument. @@ -1009,8 +1046,9 @@ class ClassDocumenter(ModuleLevelDocumenter): initmeth = self.get_attr(self.object, '__init__', None) # classes without __init__ method, default __init__ or # __init__ written in C? - if initmeth is None or initmeth is object.__init__ or not \ - (inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): + if initmeth is None or \ + is_builtin_class_method(self.object, '__init__') or \ + not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): return None try: argspec = getargspec(initmeth) @@ -1047,7 +1085,7 @@ class ClassDocumenter(ModuleLevelDocumenter): # add inheritance info, if wanted if not self.doc_as_attr and self.options.show_inheritance: self.add_line(u'', '<autodoc>') - if len(self.object.__bases__): + if hasattr(self.object, '__bases__') and len(self.object.__bases__): bases = [b.__module__ == '__builtin__' and u':class:`%s`' % b.__name__ or u':class:`%s.%s`' % (b.__module__, b.__name__) @@ -1066,10 +1104,21 @@ class ClassDocumenter(ModuleLevelDocumenter): # for classes, what the "docstring" is can be controlled via a # config value; the default is only the class docstring if content in ('both', 'init'): - initdocstring = self.get_attr( - self.get_attr(self.object, '__init__', None), '__doc__') + # get __init__ method document from __init__.__doc__ + if self.env.config.autodoc_docstring_signature: + # only act if the feature is enabled + init_doc = MethodDocumenter(self.directive, '__init__') + init_doc.object = self.get_attr(self.object, '__init__', None) + init_doc.objpath = ['__init__'] + init_doc._find_signature() # this effects to get_doc() result + initdocstring = '\n'.join( + ['\n'.join(l) for l in init_doc.get_doc(encoding)]) + else: + initdocstring = self.get_attr( + self.get_attr(self.object, '__init__', None), '__doc__') # for new-style classes, no __init__ means default __init__ - if initdocstring == object.__init__.__doc__: + if (initdocstring == object.__init__.__doc__ or # for pypy + initdocstring.strip() == object.__init__.__doc__): #for !pypy initdocstring = None if initdocstring: if content == 'init': @@ -1113,7 +1162,7 @@ class ExceptionDocumenter(ClassDocumenter): @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, class_types) and \ - issubclass(member, base_exception) + issubclass(member, BaseException) class DataDocumenter(ModuleLevelDocumenter): @@ -1228,7 +1277,8 @@ class AttributeDocumenter(ClassLevelDocumenter): def can_document_member(cls, member, membername, isattr, parent): isdatadesc = isdescriptor(member) and not \ isinstance(member, cls.method_types) and not \ - type(member).__name__ in ("type", "method_descriptor") + type(member).__name__ in ("type", "method_descriptor", + "instancemethod") return isdatadesc or (not isinstance(parent, ModuleDocumenter) and not inspect.isroutine(member) and not isinstance(member, class_types)) @@ -1368,8 +1418,15 @@ class AutoDirective(Directive): not negated: self.options[flag] = None # process the options with the selected documenter's option_spec - self.genopt = Options(assemble_option_dict( - self.options.items(), doc_class.option_spec)) + try: + self.genopt = Options(assemble_option_dict( + self.options.items(), doc_class.option_spec)) + except (KeyError, ValueError, TypeError), err: + # an option is either unknown or has a wrong type + msg = self.reporter.error('An option to %s is either unknown or ' + 'has an invalid value: %s' % (self.name, err), + line=self.lineno) + return [msg] # generate the output documenter = doc_class(self, self.arguments[0]) documenter.generate(more_content=self.content) @@ -1428,6 +1485,7 @@ def setup(app): 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_config_value('autodoc_mock_imports', [], True) app.add_event('autodoc-process-docstring') app.add_event('autodoc-process-signature') app.add_event('autodoc-skip-member') |