From a8f65e9d3c8b3ffb16b0ad5004b420a60d1c6a60 Mon Sep 17 00:00:00 2001 From: guibog Date: Wed, 23 Oct 2013 16:36:45 +0800 Subject: [PATCH] autodoc extension: add autodoc_mock_imports config value --- doc/ext/autodoc.rst | 10 +++++++++ sphinx/ext/autodoc.py | 32 +++++++++++++++++++++++++++ tests/root/autodoc.txt | 2 ++ tests/root/autodoc_missing_imports.py | 9 ++++++++ tests/root/conf.py | 7 ++++++ 5 files changed, 60 insertions(+) create mode 100644 tests/root/autodoc_missing_imports.py diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index c92fe0c4e..c37328f54 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -195,6 +195,10 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, .. versionadded:: 1.2 + * Add a list of modules in the :confval:`autodoc_mock_imports` to prevent + import errors to halt the building process when some external dependencies + are not importable at build time. + .. rst:directive:: autofunction autodata @@ -335,6 +339,12 @@ There are also new config values that you can set: .. versionadded:: 1.1 +.. confval:: autodoc_mock_imports + + This value contains a list of modules to be mocked up. This is useful when + some external dependencies are not met at build time and break the building + process. + Docstring preprocessing ----------------------- diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 348c072c4..a556edc8f 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -70,6 +70,26 @@ class Options(dict): return None +class _MockModule(object): + 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() + + ALL = object() INSTANCEATTR = object() @@ -332,6 +352,8 @@ class Documenter(object): self.modname, '.'.join(self.objpath)) try: dbg('[autodoc] import %s', self.modname) + for modname in self.env.config.autodoc_mock_imports: + self._mock_import(modname) __import__(self.modname) parent = None obj = self.module = sys.modules[self.modname] @@ -361,6 +383,15 @@ class Documenter(object): self.env.note_reread() return False + def _mock_import(self, modname): + if '.' in modname: + pkg, _n, mods = modname.rpartition('.') + self._mock_import(pkg) + mod = _MockModule() + sys.modules[modname] = mod + return mod + + def get_real_modname(self): """Get the real module name of an object to document. @@ -1428,6 +1459,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') diff --git a/tests/root/autodoc.txt b/tests/root/autodoc.txt index d4b3404c4..aa0dffba1 100644 --- a/tests/root/autodoc.txt +++ b/tests/root/autodoc.txt @@ -45,3 +45,5 @@ Just testing a few autodoc possibilities... :members: ca1, ia1 Specific members (2 total) + +.. automodule:: autodoc_missing_imports diff --git a/tests/root/autodoc_missing_imports.py b/tests/root/autodoc_missing_imports.py new file mode 100644 index 000000000..7a7173452 --- /dev/null +++ b/tests/root/autodoc_missing_imports.py @@ -0,0 +1,9 @@ + +import missing_module +from missing_module import missing_name +import missing_package1.missing_module1 +from missing_package2 import missing_module2 +from missing_package3.missing_module3 import missing_name + +class TestAutodoc(object): + """TestAutodoc docstring.""" diff --git a/tests/root/conf.py b/tests/root/conf.py index 8025ba33c..215cebf9a 100644 --- a/tests/root/conf.py +++ b/tests/root/conf.py @@ -71,6 +71,13 @@ autosummary_generate = ['autosummary'] extlinks = {'issue': ('http://bugs.python.org/issue%s', 'issue '), 'pyurl': ('http://python.org/%s', None)} +autodoc_mock_imports = [ + 'missing_module', + 'missing_package1.missing_module1', + 'missing_package2.missing_module2', + 'missing_package3.missing_module3', +] + # modify tags from conf.py tags.add('confpytag')