diff --git a/CHANGES b/CHANGES index 07846fede87..339cec86d99 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,9 @@ Deprecated Features added -------------- +* #247: autosummary: Add :confval:`autosummary_generate_option` to overwrite + old stub file + Bugs fixed ---------- diff --git a/doc/usage/extensions/autosummary.rst b/doc/usage/extensions/autosummary.rst index 16a8cea7e7c..077c5e8439c 100644 --- a/doc/usage/extensions/autosummary.rst +++ b/doc/usage/extensions/autosummary.rst @@ -143,6 +143,16 @@ also use these config values: The new files will be placed in the directories specified in the ``:toctree:`` options of the directives. +.. confval:: autosummary_generate_option + + This value switches the behavior when autosummary detects old stub files. + + * ``'skip'`` -- Skip stub file generation (default) + * ``'warning'`` -- Emit a warning and skip generation + * ``'overwrite'`` -- Overwrite the stub file + + .. versionadded:: 2.1 + .. confval:: autosummary_mock_imports This value contains a list of modules to be mocked up. See diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 262b36cea22..ad8c7296cc4 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -68,6 +68,7 @@ import sphinx from sphinx import addnodes +from sphinx.config import ENUM from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.environment.adapters.toctree import TocTree from sphinx.ext.autodoc import get_documenters @@ -756,7 +757,8 @@ def process_generate_options(app): generate_autosummary_docs(genfiles, builder=app.builder, warn=logger.warning, info=logger.info, suffix=suffix, base_path=app.srcdir, - app=app, imported_members=imported_members) + app=app, imported_members=imported_members, + generate_option=app.config.autosummary_generate_option) def setup(app): @@ -780,6 +782,8 @@ def setup(app): app.connect('doctree-read', process_autosummary_toc) app.connect('builder-inited', process_generate_options) app.add_config_value('autosummary_generate', [], True, [bool]) + app.add_config_value('autosummary_generate_option', 'skip', False, + ENUM('skip', 'warning', 'overwrite')) app.add_config_value('autosummary_mock_imports', lambda config: config.autodoc_mock_imports, 'env') app.add_config_value('autosummary_imported_members', [], False, [bool]) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 78f0650eabf..1dc5c867134 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -196,8 +196,8 @@ def get_members(obj, types, include_public=[], imported=True): def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', warn=_simple_warn, info=_simple_info, base_path=None, builder=None, template_dir=None, - imported_members=False, app=None): - # type: (List[str], str, str, Callable, Callable, str, Builder, str, bool, Any) -> None + imported_members=False, app=None, generate_option='skip'): + # type: (List[str], str, str, Callable, Callable, str, Builder, str, bool, Any, str) -> None # NOQA showed_sources = list(sorted(sources)) if len(showed_sources) > 20: @@ -235,26 +235,38 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', warn('[autosummary] failed to import %r: %s' % (name, e)) continue - fn = os.path.join(path, name + suffix) + content = generate_autosummary_content(name, obj, parent, template, template_name, + imported_members, app) - # skip it if it exists - if os.path.isfile(fn): - continue - - new_files.append(fn) + filename = os.path.join(path, name + suffix) + if os.path.isfile(filename): + with open(filename) as f: + old_content = f.read() - with open(fn, 'w') as f: - rendered = generate_autosummary_content(name, obj, parent, - template, template_name, - imported_members, app) - f.write(rendered) + if content == old_content: + continue + else: + # content has changed + if generate_option == 'skip': + continue + elif generate_option == 'warning': + warn('[autosummary] %s has changed. Ignored.' % filename) + elif generate_option == 'overwrite': + with open(filename, 'w') as f: + f.write(content) + new_files.append(filename) + else: + with open(filename, 'w') as f: + f.write(content) + new_files.append(filename) # descend recursively to new files if new_files: generate_autosummary_docs(new_files, output_dir=output_dir, suffix=suffix, warn=warn, info=info, base_path=base_path, builder=builder, - template_dir=template_dir, app=app) + template_dir=template_dir, app=app, + generate_option=generate_option) # -- Finding documented entries in files --------------------------------------- diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index 04af9ed8509..74bb3824506 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -222,6 +222,51 @@ def test_autosummary_generate(app, status, warning): ' \n' in Foo) +@pytest.mark.sphinx('dummy', testroot='ext-autosummary', + confoverrides={'autosummary_generate_option': 'skip'}) +def test_autosummary_generate_option_skip(app_params, make_app): + args, kwargs = app_params + srcdir = kwargs.get('srcdir') + + (srcdir / 'generated').makedirs(exist_ok=True) + (srcdir / 'generated' / 'autosummary_dummy_module.rst').write_text('') + + app = make_app(*args, **kwargs) + content = (srcdir / 'generated' / 'autosummary_dummy_module.rst').text() + assert content == '' + assert 'autosummary_dummy_module.rst' not in app._warning.getvalue() + + +@pytest.mark.sphinx('dummy', testroot='ext-autosummary', + confoverrides={'autosummary_generate_option': 'warning'}) +def test_autosummary_generate_option_warning(app_params, make_app): + args, kwargs = app_params + srcdir = kwargs.get('srcdir') + + (srcdir / 'generated').makedirs(exist_ok=True) + (srcdir / 'generated' / 'autosummary_dummy_module.rst').write_text('') + + app = make_app(*args, **kwargs) + content = (srcdir / 'generated' / 'autosummary_dummy_module.rst').text() + assert content == '' + assert 'autosummary_dummy_module.rst' in app._warning.getvalue() + + +@pytest.mark.sphinx('dummy', testroot='ext-autosummary', + confoverrides={'autosummary_generate_option': 'overwrite'}) +def test_autosummary_generate_option_overwrite(app_params, make_app): + args, kwargs = app_params + srcdir = kwargs.get('srcdir') + + (srcdir / 'generated').makedirs(exist_ok=True) + (srcdir / 'generated' / 'autosummary_dummy_module.rst').write_text('') + + app = make_app(*args, **kwargs) + content = (srcdir / 'generated' / 'autosummary_dummy_module.rst').text() + assert content != '' + assert 'autosummary_dummy_module.rst' not in app._warning.getvalue() + + @pytest.mark.sphinx('latex', **default_kw) def test_autosummary_latex_table_colspec(app, status, warning): app.builder.build_all()