From c914884f57038528d4d9b436dd20cb17215f2e9f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Aug 2009 22:06:19 +0200 Subject: [PATCH] #229: Fix autodoc failures with members that raise errors on ``getattr()``. --- CHANGES | 3 +++ sphinx/ext/autodoc.py | 26 ++++--------------------- sphinx/util/inspect.py | 43 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 sphinx/util/inspect.py diff --git a/CHANGES b/CHANGES index 2033e8b67..4eb24cd9a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 0.6.3 (in development) ============================== +* #229: Fix autodoc failures with members that raise errors + on ``getattr()``. + * #205: When copying files, don't copy full stat info, only modification times. diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index c9a248a70..155c5711a 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -25,6 +25,7 @@ from sphinx.util import rpartition, nested_parse_with_titles, force_decode from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.application import ExtensionError from sphinx.util.compat import Directive +from sphinx.util.inspect import isdescriptor, safe_getmembers, safe_getattr from sphinx.util.docstrings import prepare_docstring @@ -194,25 +195,6 @@ def between(marker, what=None, keepempty=False): return process -def safe_getattr(obj, name, *defargs): - try: - return getattr(obj, name, *defargs) - except Exception: - # this is a catch-all for all the weird things that some modules do - # with attribute access - if defargs: - return defargs[0] - raise AttributeError - - -def isdescriptor(x): - """Check if the object is some kind of descriptor.""" - for item in '__get__', '__set__', '__delete__': - if hasattr(safe_getattr(x, item, None), '__call__'): - return True - return False - - class Documenter(object): """ A Documenter knows how to autodocument a single object type. When @@ -486,9 +468,9 @@ class Documenter(object): % (mname, self.fullname)) return False, ret elif self.options.inherited_members: - # getmembers() uses dir() which pulls in members from all + # safe_getmembers() uses dir() which pulls in members from all # base classes - return False, inspect.getmembers(self.object) + return False, safe_getmembers(self.object) else: # __dict__ contains only the members directly defined in # the class (but get them via getattr anyway, to e.g. get @@ -728,7 +710,7 @@ class ModuleDocumenter(Documenter): if not hasattr(self.object, '__all__'): # for implicit module members, check __module__ to avoid # documenting imported objects - return True, inspect.getmembers(self.object) + return True, safe_getmembers(self.object) else: memberlist = self.object.__all__ else: diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py new file mode 100644 index 000000000..9cec5deca --- /dev/null +++ b/sphinx/util/inspect.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +""" + sphinx.util.inspect + ~~~~~~~~~~~~~~~~~~~ + + Helpers for inspecting Python modules. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +def isdescriptor(x): + """Check if the object is some kind of descriptor.""" + for item in '__get__', '__set__', '__delete__': + if hasattr(safe_getattr(x, item, None), '__call__'): + return True + return False + + +def safe_getattr(obj, name, *defargs): + """A getattr() that turns all exceptions into AttributeErrors.""" + try: + return getattr(obj, name, *defargs) + except Exception: + # this is a catch-all for all the weird things that some modules do + # with attribute access + if defargs: + return defargs[0] + raise AttributeError(name) + + +def safe_getmembers(object, predicate=None): + """A version of inspect.getmembers() that uses safe_getattr().""" + results = [] + for key in dir(object): + try: + value = safe_getattr(object, key, None) + except AttributeError: + continue + if not predicate or predicate(value): + results.append((key, value)) + results.sort() + return results