From f47f2353d38a23c152c8d4f8cafaa59c2af58a03 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 31 Jan 2018 20:43:54 +0900 Subject: [PATCH 01/11] quickstart: epub builder is enabled by default --- CHANGES | 2 ++ sphinx/cmd/quickstart.py | 8 -------- sphinx/templates/quickstart/conf.py_t | 2 -- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index b83f1925f..2b0389ae1 100644 --- a/CHANGES +++ b/CHANGES @@ -55,6 +55,7 @@ Deprecated ---------- * :confval:`source_parsers` is deprecated +* quickstart: ``--epub`` option becomes default, so it is deprecated * Drop function based directive support. For now, Sphinx only supports class based directives. * ``sphinx.util.docutils.directive_helper()`` is deprecated @@ -186,6 +187,7 @@ Features added * #5140: linkcheck: Add better Accept header to HTTP client * #4614: sphinx-build: Add ``--keep-going`` option to show all warnings * Add :rst:role:`math:numref` role to refer equations (Same as :rst:role:`eq`) +* quickstart: epub builder is enabled by default Bugs fixed ---------- diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py index 14217d1a7..6b003c926 100644 --- a/sphinx/cmd/quickstart.py +++ b/sphinx/cmd/quickstart.py @@ -74,7 +74,6 @@ DEFAULTS = { 'language': None, 'suffix': '.rst', 'master': 'index', - 'epub': False, 'makefile': True, 'batchfile': True, } @@ -246,7 +245,6 @@ def ask_user(d): * language: document language * suffix: source file suffix * master: master document name - * epub: use epub (bool) * extensions: extensions to use (list) * makefile: make Makefile * batchfile: make command file @@ -347,12 +345,6 @@ document is a custom template, you can also set this to another filename.''')) d['master'] = do_prompt(__('Please enter a new file name, or rename the ' 'existing file and press Enter'), d['master']) - if 'epub' not in d: - print(__(''' -Sphinx can also add configuration for epub output:''')) - d['epub'] = do_prompt(__('Do you want to use the epub builder (y/n)'), - 'n', boolean) - if 'extensions' not in d: print(__('Indicate which of the following Sphinx extensions should be ' 'enabled:')) diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index cc8073a82..37eef3589 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -165,7 +165,6 @@ texinfo_documents = [ author, '{{ project_fn }}', 'One line description of project.', 'Miscellaneous'), ] -{%- if epub %} # -- Options for Epub output ------------------------------------------------- @@ -187,7 +186,6 @@ epub_copyright = copyright # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] -{%- endif %} {%- if extensions %} From dc4f8d4836fdfc41fc39a1dd7c662eeb401d4945 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 31 Jan 2018 20:43:55 +0900 Subject: [PATCH 02/11] conf.py: Drop epub_author, epub_publisher and epub_copyright The default values of them are same as conf.py's. So it is not needed to override the setting. --- sphinx/templates/quickstart/conf.py_t | 3 --- tests/test_quickstart.py | 1 - 2 files changed, 4 deletions(-) diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index 37eef3589..79c78ae93 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -171,9 +171,6 @@ texinfo_documents = [ # Bibliographic Dublin Core info. epub_title = project -epub_author = author -epub_publisher = author -epub_copyright = copyright # The unique identifier of the text. This can be a ISBN number # or the project homepage. diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index fccd3d66b..64c38457d 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -197,7 +197,6 @@ def test_quickstart_all_answers(tempdir): assert ns['latex_documents'] == [ ('contents', 'STASI.tex', u'STASI™ Documentation', u'Wolfgang Schäuble \\& G\'Beckstein', 'manual')] - assert ns['epub_author'] == u'Wolfgang Schäuble & G\'Beckstein' assert ns['man_pages'] == [ ('contents', 'stasi', u'STASI™ Documentation', [u'Wolfgang Schäuble & G\'Beckstein'], 1)] From de73ee5902be1c1741f7ff46763c3db1ba3059c4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 31 Jul 2018 20:14:18 +0900 Subject: [PATCH 03/11] refactor: test_autodoc --- tests/test_autodoc.py | 375 ++++++++++++++++++++++++++---------------- 1 file changed, 236 insertions(+), 139 deletions(-) diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 168002c52..ac7abaa83 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -19,7 +19,7 @@ from docutils.statemachine import ViewList from six import PY3 from sphinx.ext.autodoc import ( - AutoDirective, ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL + AutoDirective, ModuleLevelDocumenter, cut_lines, between, ALL ) from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options from sphinx.testing.util import SphinxTestApp, Struct # NOQA @@ -28,6 +28,11 @@ from sphinx.util.docutils import LoggingReporter app = None +if PY3: + ROGER_METHOD = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)' +else: + ROGER_METHOD = ' .. py:classmethod:: Class.roger(a, e=5, f=6)' + def do_autodoc(app, objtype, name, options={}): doccls = app.registry.documenters[objtype] @@ -617,73 +622,12 @@ def test_attrgetter_using(): @pytest.mark.usefixtures('setup_test') def test_generate(): - logging.setup(app, app._status, app._warning) - - def assert_warns(warn_str, objtype, name, **kw): - inst = app.registry.documenters[objtype](directive, name) - inst.generate(**kw) - assert len(directive.result) == 0, directive.result - - assert warn_str in app._warning.getvalue() - app._warning.truncate(0) - - def assert_works(objtype, name, **kw): - inst = app.registry.documenters[objtype](directive, name) - inst.generate(**kw) - assert directive.result - # print '\n'.join(directive.result) - assert app._warning.getvalue() == '' - del directive.result[:] - - def assert_processes(items, objtype, name, **kw): - del processed_docstrings[:] - del processed_signatures[:] - assert_works(objtype, name, **kw) - assert set(processed_docstrings) | set(processed_signatures) == set(items) - def assert_result_contains(item, objtype, name, **kw): inst = app.registry.documenters[objtype](directive, name) inst.generate(**kw) - # print '\n'.join(directive.result) - assert app._warning.getvalue() == '' assert item in directive.result del directive.result[:] - def assert_order(items, objtype, name, member_order, **kw): - inst = app.registry.documenters[objtype](directive, name) - inst.options.member_order = member_order - inst.generate(**kw) - assert app._warning.getvalue() == '' - items = list(reversed(items)) - lineiter = iter(directive.result) - # for line in directive.result: - # if line.strip(): - # print repr(line) - while items: - item = items.pop() - for line in lineiter: - if line == item: - break - else: # ran out of items! - assert False, ('item %r not found in result or not in the ' - ' correct order' % item) - del directive.result[:] - - options.members = [] - - # no module found? - assert_warns("import for autodocumenting 'foobar'", - 'function', 'foobar', more_content=None) - # importing - assert_warns("failed to import module 'test_foobar'", - 'module', 'test_foobar', more_content=None) - # attributes missing - assert_warns("failed to import function 'foobar' from module 'util'", - 'function', 'util.foobar', more_content=None) - # method missing - assert_warns("failed to import method 'Class.foobar' from module 'target';", - 'method', 'target.Class.foobar', more_content=None) - # test auto and given content mixing directive.env.ref_context['py:module'] = 'target' assert_result_contains(' Function.', 'method', 'Class.meth') @@ -694,80 +638,238 @@ def test_generate(): assert_result_contains(' Content.', 'method', 'Class.meth', more_content=add_content) - # test check_module - inst = FunctionDocumenter(directive, 'add_documenter') - inst.generate(check_module=True) - assert len(directive.result) == 0 - # assert that exceptions can be documented - assert_works('exception', 'target.CustomEx', all_members=True) - assert_works('exception', 'target.CustomEx') +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_exception(app): + actual = do_autodoc(app, 'exception', 'target.CustomEx') + assert list(actual) == [ + '', + '.. py:exception:: CustomEx', + ' :module: target', + '', + ' My custom exception.', + ' ' + ] - # test diverse inclusion settings for members - should = [('class', 'target.Class')] - assert_processes(should, 'class', 'Class') - should.extend([('method', 'target.Class.meth')]) - options.members = ['meth'] - options.exclude_members = set(['excludemeth']) - assert_processes(should, 'class', 'Class') - should.extend([('attribute', 'target.Class.prop'), - ('attribute', 'target.Class.descr'), - ('attribute', 'target.Class.attr'), - ('attribute', 'target.Class.docattr'), - ('attribute', 'target.Class.udocattr'), - ('attribute', 'target.Class.mdocattr'), - ('attribute', 'target.Class.inst_attr_comment'), - ('attribute', 'target.Class.inst_attr_inline'), - ('attribute', 'target.Class.inst_attr_string'), - ('method', 'target.Class.moore'), - ]) - options.members = ALL - assert_processes(should, 'class', 'Class') - options.undoc_members = True - should.extend((('attribute', 'target.Class.skipattr'), - ('method', 'target.Class.undocmeth'), - ('method', 'target.Class.roger'))) - assert_processes(should, 'class', 'Class') - options.inherited_members = True - should.append(('method', 'target.Class.inheritedmeth')) - should.append(('method', 'target.Class.inheritedclassmeth')) - should.append(('method', 'target.Class.inheritedstaticmeth')) - assert_processes(should, 'class', 'Class') - # test special members - options.special_members = ['__special1__'] - should.append(('method', 'target.Class.__special1__')) - assert_processes(should, 'class', 'Class') - options.special_members = ALL - should.append(('method', 'target.Class.__special2__')) - assert_processes(should, 'class', 'Class') - options.special_members = False +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_warnings(app, warning): + app.env.temp_data['docname'] = 'dummy' - options.members = [] - # test module flags - assert_result_contains('.. py:module:: target', - 'module', 'target') - options.synopsis = 'Synopsis' - assert_result_contains(' :synopsis: Synopsis', 'module', 'target') - options.deprecated = True - assert_result_contains(' :deprecated:', 'module', 'target') - options.platform = 'Platform' - assert_result_contains(' :platform: Platform', 'module', 'target') - # test if __all__ is respected for modules - options.members = ALL - assert_result_contains('.. py:class:: Class(arg)', 'module', 'target') - try: - assert_result_contains('.. py:exception:: CustomEx', - 'module', 'target') - except AssertionError: - pass - else: - assert False, 'documented CustomEx which is not in __all__' + # can't import module + do_autodoc(app, 'module', 'unknown') + assert "failed to import module 'unknown'" in warning.getvalue() - # test ignore-module-all - options.ignore_module_all = True - assert_result_contains('.. py:class:: Class(arg)', 'module', 'target') - assert_result_contains('.. py:exception:: CustomEx', 'module', 'target') + # missing function + do_autodoc(app, 'function', 'unknown') + assert "import for autodocumenting 'unknown'" in warning.getvalue() + + do_autodoc(app, 'function', 'target.unknown') + assert "failed to import function 'unknown' from module 'target'" in warning.getvalue() + + # missing method + do_autodoc(app, 'method', 'target.Class.unknown') + assert "failed to import method 'Class.unknown' from module 'target'" in warning.getvalue() + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_attributes(app): + options = {"synopsis": 'Synopsis', + "platform": "Platform", + "deprecated": None} + actual = do_autodoc(app, 'module', 'target', options) + assert list(actual) == [ + '', + '.. py:module:: target', + ' :synopsis: Synopsis', + ' :platform: Platform', + ' :deprecated:', + '' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_members(app): + # default (no-members) + actual = do_autodoc(app, 'class', 'target.Base') + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base', + ] + + # default ALL-members + options = {"members": None} + actual = do_autodoc(app, 'class', 'target.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base', + ' .. py:classmethod:: Base.inheritedclassmeth()', + ' .. py:method:: Base.inheritedmeth()', + ' .. py:staticmethod:: Base.inheritedstaticmeth(cls)' + ] + + # default specific-members + options = {"members": "inheritedmeth,inheritedstaticmeth"} + actual = do_autodoc(app, 'class', 'target.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base', + ' .. py:method:: Base.inheritedmeth()', + ' .. py:staticmethod:: Base.inheritedstaticmeth(cls)' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_exclude_members(app): + options = {"members": None, + "exclude-members": "inheritedmeth,inheritedstaticmeth"} + actual = do_autodoc(app, 'class', 'target.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base', + ' .. py:classmethod:: Base.inheritedclassmeth()' + ] + + # members vs exclude-members + options = {"members": "inheritedmeth", + "exclude-members": "inheritedmeth"} + actual = do_autodoc(app, 'class', 'target.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base', + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_undoc_members(app): + options = {"members": None, + "undoc-members": None} + actual = do_autodoc(app, 'class', 'target.Class', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Class(arg)', + ' .. py:attribute:: Class.attr', + ' .. py:attribute:: Class.descr', + ' .. py:attribute:: Class.docattr', + ' .. py:method:: Class.excludemeth()', + ' .. py:attribute:: Class.inst_attr_comment', + ' .. py:attribute:: Class.inst_attr_inline', + ' .. py:attribute:: Class.inst_attr_string', + ' .. py:attribute:: Class.mdocattr', + ' .. py:method:: Class.meth()', + ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:attribute:: Class.prop', + ROGER_METHOD, + ' .. py:attribute:: Class.skipattr', + ' .. py:method:: Class.skipmeth()', + ' .. py:attribute:: Class.udocattr', + ' .. py:method:: Class.undocmeth()' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_inherited_members(app): + options = {"members": None, + "inherited-members": None} + actual = do_autodoc(app, 'class', 'target.Class', options) + assert list(filter(lambda l: 'method::' in l, actual)) == [ + ' .. py:method:: Class.excludemeth()', + ' .. py:classmethod:: Class.inheritedclassmeth()', + ' .. py:method:: Class.inheritedmeth()', + ' .. py:staticmethod:: Class.inheritedstaticmeth(cls)', + ' .. py:method:: Class.meth()', + ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.skipmeth()' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_imported_members(app): + options = {"members": None, + "imported-members": None, + "ignore-module-all": None} + actual = do_autodoc(app, 'module', 'target', options) + assert '.. py:function:: add_documenter(cls)' in actual + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_special_members(app): + # all special methods + options = {"members": None, + "undoc-members": None, + "special-members": None} + actual = do_autodoc(app, 'class', 'target.Class', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Class(arg)', + ' .. py:method:: Class.__init__(arg)', + ' .. py:attribute:: Class.__module__', + ' .. py:method:: Class.__special1__()', + ' .. py:method:: Class.__special2__()', + ' .. py:attribute:: Class.attr', + ' .. py:attribute:: Class.descr', + ' .. py:attribute:: Class.docattr', + ' .. py:method:: Class.excludemeth()', + ' .. py:attribute:: Class.inst_attr_comment', + ' .. py:attribute:: Class.inst_attr_inline', + ' .. py:attribute:: Class.inst_attr_string', + ' .. py:attribute:: Class.mdocattr', + ' .. py:method:: Class.meth()', + ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:attribute:: Class.prop', + ROGER_METHOD, + ' .. py:attribute:: Class.skipattr', + ' .. py:method:: Class.skipmeth()', + ' .. py:attribute:: Class.udocattr', + ' .. py:method:: Class.undocmeth()' + ] + + # specific special methods + options = {"members": None, + "undoc-members": None, + "special-members": "__init__,__special1__"} + actual = do_autodoc(app, 'class', 'target.Class', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Class(arg)', + ' .. py:method:: Class.__init__(arg)', + ' .. py:method:: Class.__special1__()', + ' .. py:attribute:: Class.attr', + ' .. py:attribute:: Class.descr', + ' .. py:attribute:: Class.docattr', + ' .. py:method:: Class.excludemeth()', + ' .. py:attribute:: Class.inst_attr_comment', + ' .. py:attribute:: Class.inst_attr_inline', + ' .. py:attribute:: Class.inst_attr_string', + ' .. py:attribute:: Class.mdocattr', + ' .. py:method:: Class.meth()', + ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:attribute:: Class.prop', + ROGER_METHOD, + ' .. py:attribute:: Class.skipattr', + ' .. py:method:: Class.skipmeth()', + ' .. py:attribute:: Class.udocattr', + ' .. py:method:: Class.undocmeth()' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_ignore_module_all(app): + # default (no-ignore-module-all) + options = {"members": None} + actual = do_autodoc(app, 'module', 'target', options) + assert list(filter(lambda l: 'class::' in l, actual)) == [ + '.. py:class:: Class(arg)', + ] + + # ignore-module-all + options = {"members": None, + "ignore-module-all": None} + actual = do_autodoc(app, 'module', 'target', options) + assert list(filter(lambda l: 'class::' in l, actual)) == [ + '.. py:class:: Class(arg)', + '.. py:class:: CustomDataDescriptor(doc)', + '.. py:class:: CustomDataDescriptor2(doc)', + '.. py:class:: CustomDataDescriptorMeta', + '.. py:class:: CustomDict', + '.. py:class:: EnumCls', + '.. py:class:: InstAttCls()', + '.. py:class:: Outer', + ' .. py:class:: Outer.Inner', + '.. py:class:: StrRepr' + ] @pytest.mark.sphinx('html', testroot='ext-autodoc') @@ -889,11 +991,6 @@ def test_autodoc_c_module(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_member_order(app): - if PY3: - roger_method = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)' - else: - roger_method = ' .. py:classmethod:: Class.roger(a, e=5, f=6)' - # case member-order='bysource' options = {"members": None, 'member-order': 'bysource', @@ -913,7 +1010,7 @@ def test_autodoc_member_order(app): ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', - roger_method, + ROGER_METHOD, ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.inst_attr_inline', ' .. py:attribute:: Class.inst_attr_comment', @@ -932,7 +1029,7 @@ def test_autodoc_member_order(app): ' .. py:method:: Class.excludemeth()', ' .. py:method:: Class.meth()', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', - roger_method, + ROGER_METHOD, ' .. py:method:: Class.skipmeth()', ' .. py:method:: Class.undocmeth()', ' .. py:attribute:: Class._private_inst_attr', @@ -967,7 +1064,7 @@ def test_autodoc_member_order(app): ' .. py:method:: Class.meth()', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - roger_method, + ROGER_METHOD, ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', From 7e546c0e0b6389a9fb0454da2f37e1f17f71db85 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 1 Aug 2018 00:29:27 +0900 Subject: [PATCH 04/11] refactor: Remove duplicated testcase --- tests/test_autodoc.py | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index ac7abaa83..d3e69766f 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -517,43 +517,6 @@ def test_docstring_processing(): app.disconnect(lid) -@pytest.mark.usefixtures('setup_test') -def test_docstring_property_processing(): - def genarate_docstring(objtype, name, **kw): - del processed_docstrings[:] - del processed_signatures[:] - inst = app.registry.documenters[objtype](directive, name) - inst.generate(**kw) - results = list(directive.result) - docstrings = inst.get_doc()[0] - del directive.result[:] - return results, docstrings - - directive.env.config.autodoc_docstring_signature = False - results, docstrings = \ - genarate_docstring('attribute', 'target.DocstringSig.prop1') - assert '.. py:attribute:: DocstringSig.prop1' in results - assert 'First line of docstring' in docstrings - assert 'DocstringSig.prop1(self)' in docstrings - results, docstrings = \ - genarate_docstring('attribute', 'target.DocstringSig.prop2') - assert '.. py:attribute:: DocstringSig.prop2' in results - assert 'First line of docstring' in docstrings - assert 'Second line of docstring' in docstrings - - directive.env.config.autodoc_docstring_signature = True - results, docstrings = \ - genarate_docstring('attribute', 'target.DocstringSig.prop1') - assert '.. py:attribute:: DocstringSig.prop1' in results - assert 'First line of docstring' in docstrings - assert 'DocstringSig.prop1(self)' not in docstrings - results, docstrings = \ - genarate_docstring('attribute', 'target.DocstringSig.prop2') - assert '.. py:attribute:: DocstringSig.prop2' in results - assert 'First line of docstring' in docstrings - assert 'Second line of docstring' in docstrings - - @pytest.mark.usefixtures('setup_test') def test_new_documenter(): logging.setup(app, app._status, app._warning) From 684f69d9dcd41d05e216d0f5508fbc5f884f3781 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 5 Aug 2018 14:57:13 +0200 Subject: [PATCH 05/11] C++, fix some wrong type annotations --- sphinx/domains/cpp.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index aebc5d245..678cdfc3a 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -2085,7 +2085,7 @@ class ASTTrailingTypeSpecName(ASTBase): @property def name(self): - # type: () -> Any + # type: () -> ASTNestedName return self.nestedName def get_id(self, version): @@ -2379,7 +2379,7 @@ class ASTDeclSpecs(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.trailingTypeSpec.name def get_id(self, version): @@ -2487,7 +2487,7 @@ class ASTDeclaratorPtr(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.next.name @property @@ -2583,7 +2583,7 @@ class ASTDeclaratorRef(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.next.name @property @@ -2638,7 +2638,7 @@ class ASTDeclaratorParamPack(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.next.name @property @@ -2702,7 +2702,7 @@ class ASTDeclaratorMemPtr(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.next.name @property @@ -2798,7 +2798,7 @@ class ASTDeclaratorParen(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.inner.name @property @@ -2864,7 +2864,7 @@ class ASTDeclaratorNameParamQual(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.declId @property @@ -2962,9 +2962,8 @@ class ASTType(ASTBase): @property def name(self): - # type: () -> unicode - name = self.decl.name - return name + # type: () -> ASTNestedName + return self.decl.name @property def function_params(self): @@ -3054,7 +3053,7 @@ class ASTTypeWithInit(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.type.name def get_id(self, version, objectType=None, symbol=None): @@ -3122,7 +3121,7 @@ class ASTConcept(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.nestedName def get_id(self, version, objectType=None, symbol=None): @@ -3326,7 +3325,7 @@ class ASTDeclaration(ASTBase): @property def name(self): - # type: () -> unicode + # type: () -> ASTNestedName return self.declaration.name @property From 91f736b0879b34d95ce4b80bcc5f11eb59ec4df8 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 5 Aug 2018 15:08:25 +0200 Subject: [PATCH 06/11] C++, prevent nested declarations in functions. Fixes sphinx-doc/sphinx#5191 --- CHANGES | 1 + sphinx/domains/cpp.py | 33 +++++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index b83f1925f..665438a54 100644 --- a/CHANGES +++ b/CHANGES @@ -200,6 +200,7 @@ Bugs fixed * #5114: sphinx-build: Handle errors on scanning documents * epub: spine has been broken when "self" is listed on toctree (refs: #4611) * #344: autosummary does not understand docstring of module level attributes +* #5191: C++, prevent nested declarations in functions to avoid lookup problems. Testing -------- diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 678cdfc3a..7b2d3517e 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -5790,12 +5790,37 @@ class CPPObject(ObjectDescription): # type: (addnodes.desc_signature, Any, Dict) -> None ast.describe_signature(signode, 'lastIsName', self.env, options) + def run(self): + env = self.state.document.settings.env # from ObjectDescription.run + if 'cpp:parent_symbol' not in env.temp_data: + root = env.domaindata['cpp']['root_symbol'] + env.temp_data['cpp:parent_symbol'] = root + env.ref_context['cpp:parent_key'] = root.get_lookup_key() + + # The lookup keys assume that no nested scopes exists inside overloaded functions. + # (see also #5191) + # Example: + # .. cpp:function:: void f(int) + # .. cpp:function:: void f(double) + # + # .. cpp:function:: void g() + # + # :cpp:any:`boom` + # + # So we disallow any signatures inside functions. + parentSymbol = env.temp_data['cpp:parent_symbol'] + parentDecl = parentSymbol.declaration + if parentDecl is not None and parentDecl.objectType == 'function': + self.warn("C++ declarations inside functions are not supported." + + " Parent function is " + text_type(parentSymbol.get_full_nested_name())) + name = _make_phony_error_name() + symbol = parentSymbol.add_name(name) + env.temp_data['cpp:last_symbol'] = symbol + return [] + return ObjectDescription.run(self) + def handle_signature(self, sig, signode): # type: (unicode, addnodes.desc_signature) -> Any - if 'cpp:parent_symbol' not in self.env.temp_data: - root = self.env.domaindata['cpp']['root_symbol'] - self.env.temp_data['cpp:parent_symbol'] = root - self.env.ref_context['cpp:parent_key'] = root.get_lookup_key() parentSymbol = self.env.temp_data['cpp:parent_symbol'] parser = DefinitionParser(sig, self, self.env.config) From 6092f99ffba1a477f5850048ff05f7a56a203bc5 Mon Sep 17 00:00:00 2001 From: Rok Roskar Date: Tue, 10 Jul 2018 01:06:01 +0200 Subject: [PATCH 07/11] graphviz: run dot in the document directory addresses #4033 Co-authored-by: Jiri Kuncar --- sphinx/ext/graphviz.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 11d9e54a0..cb7314596 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -238,7 +238,8 @@ def render_dot(self, code, options, format, prefix='graphviz'): if format == 'png': dot_args.extend(['-Tcmapx', '-o%s.map' % outfn]) try: - p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE) + p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE, cwd=path.dirname( + path.join(self.builder.srcdir, self.builder.current_docname))) except OSError as err: if err.errno != ENOENT: # No such file or directory raise From 5125859751d69637ae71b4a582815bb48fef2d2a Mon Sep 17 00:00:00 2001 From: Rok Roskar Date: Mon, 23 Jul 2018 16:51:02 +0200 Subject: [PATCH 08/11] added to incompatible changes --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index d7761f542..fffb5c846 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,9 @@ Dependencies Incompatible changes -------------------- +* #5156: the :py:mod:`sphinx.ext.graphviz: extension runs `dot` in the + directory of the document being built instead of in the root directory of + the documentation. * #4460: extensions which stores any data to environment should return the version of its env data structure as metadata. In detail, please see :ref:`ext-metadata`. From a59c572ec649123b112f8265cd9d72c5c4dd36b1 Mon Sep 17 00:00:00 2001 From: Rok Roskar Date: Mon, 23 Jul 2018 16:54:06 +0200 Subject: [PATCH 09/11] graphviz: make path construction more readable --- sphinx/ext/graphviz.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index cb7314596..f72c4df38 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -235,11 +235,14 @@ def render_dot(self, code, options, format, prefix='graphviz'): dot_args = [graphviz_dot] dot_args.extend(self.builder.config.graphviz_dot_args) dot_args.extend(['-T' + format, '-o' + outfn]) + + cwd = path.dirname( + path.join(self.builder.srcdir, self.builder.env.docname)) + if format == 'png': dot_args.extend(['-Tcmapx', '-o%s.map' % outfn]) try: - p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE, cwd=path.dirname( - path.join(self.builder.srcdir, self.builder.current_docname))) + p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE, cwd=cwd) except OSError as err: if err.errno != ENOENT: # No such file or directory raise From 16734bb11c2652ef92064aaa3226fb59cc555646 Mon Sep 17 00:00:00 2001 From: Rok Roskar Date: Mon, 30 Jul 2018 22:36:39 +0200 Subject: [PATCH 10/11] graphviz: add document name to graphviz node --- sphinx/ext/graphviz.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index f72c4df38..521a5662e 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -155,7 +155,10 @@ class Graphviz(SphinxDirective): line=self.lineno)] node = graphviz() node['code'] = dotcode - node['options'] = {} + node['options'] = { + 'docpath': path.splitext(self.state.document.current_source)[0], + } + if 'graphviz_dot' in self.options: node['options']['graphviz_dot'] = self.options['graphviz_dot'] if 'alt' in self.options: @@ -192,7 +195,9 @@ class GraphvizSimple(SphinxDirective): node = graphviz() node['code'] = '%s %s {\n%s\n}\n' % \ (self.name, self.arguments[0], '\n'.join(self.content)) - node['options'] = {} + node['options'] = { + 'docpath': path.splitext(self.state.document.current_source)[0], + } if 'graphviz_dot' in self.options: node['options']['graphviz_dot'] = self.options['graphviz_dot'] if 'alt' in self.options: @@ -236,8 +241,7 @@ def render_dot(self, code, options, format, prefix='graphviz'): dot_args.extend(self.builder.config.graphviz_dot_args) dot_args.extend(['-T' + format, '-o' + outfn]) - cwd = path.dirname( - path.join(self.builder.srcdir, self.builder.env.docname)) + cwd = path.dirname(options.get('docpath', self.builder.srcdir)) if format == 'png': dot_args.extend(['-Tcmapx', '-o%s.map' % outfn]) From 615b63563558375117346f628a835fda58a2f11a Mon Sep 17 00:00:00 2001 From: Rok Roskar Date: Mon, 6 Aug 2018 16:27:15 +0200 Subject: [PATCH 11/11] graphviz: rename docpath -> docname --- sphinx/ext/graphviz.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 521a5662e..3c8b3ac50 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -156,7 +156,7 @@ class Graphviz(SphinxDirective): node = graphviz() node['code'] = dotcode node['options'] = { - 'docpath': path.splitext(self.state.document.current_source)[0], + 'docname': path.splitext(self.state.document.current_source)[0], } if 'graphviz_dot' in self.options: @@ -196,7 +196,7 @@ class GraphvizSimple(SphinxDirective): node['code'] = '%s %s {\n%s\n}\n' % \ (self.name, self.arguments[0], '\n'.join(self.content)) node['options'] = { - 'docpath': path.splitext(self.state.document.current_source)[0], + 'docname': path.splitext(self.state.document.current_source)[0], } if 'graphviz_dot' in self.options: node['options']['graphviz_dot'] = self.options['graphviz_dot'] @@ -241,7 +241,8 @@ def render_dot(self, code, options, format, prefix='graphviz'): dot_args.extend(self.builder.config.graphviz_dot_args) dot_args.extend(['-T' + format, '-o' + outfn]) - cwd = path.dirname(options.get('docpath', self.builder.srcdir)) + docname = options.get('docname', 'index') + cwd = path.dirname(path.join(self.builder.srcdir, docname)) if format == 'png': dot_args.extend(['-Tcmapx', '-o%s.map' % outfn])