From 6f78f0b164f4f44a6dbb6abb50c3dc97d221e761 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 6 Oct 2020 08:39:13 +0200 Subject: [PATCH 01/16] local exclude-members option fix --- sphinx/ext/autodoc/directive.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index 9a3428f5d..f2c090503 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -84,7 +84,12 @@ def process_documenter_options(documenter: "Type[Documenter]", config: Config, o else: negated = options.pop('no-' + name, True) is None if name in config.autodoc_default_options and not negated: - options[name] = config.autodoc_default_options[name] + if name == "exclude-members": + if config.autodoc_default_options[name]: + options[name] = config.autodoc_default_options[name] \ + + options.get(name, '') + else: + options[name] = config.autodoc_default_options[name] return Options(assemble_option_dict(options.items(), documenter.option_spec)) From 0a85b4148e272a5ec91abdb53b1a710891db2899 Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Mon, 25 Jan 2021 12:54:08 +0100 Subject: [PATCH 02/16] add more extendable options and tests --- sphinx/ext/autodoc/directive.py | 12 ++- tests/test_ext_autodoc.py | 176 ++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 4 deletions(-) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index f2c090503..dfac5e0e6 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -37,6 +37,8 @@ AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members', 'ignore-module-all', 'exclude-members', 'member-order', 'imported-members'] +AUTODOC_EXTENDABLE_OPTIONS = ['members', 'special-members', 'exclude-members'] + class DummyOptionSpec(dict): """An option_spec allows any options.""" @@ -84,10 +86,12 @@ def process_documenter_options(documenter: "Type[Documenter]", config: Config, o else: negated = options.pop('no-' + name, True) is None if name in config.autodoc_default_options and not negated: - if name == "exclude-members": - if config.autodoc_default_options[name]: - options[name] = config.autodoc_default_options[name] \ - + options.get(name, '') + if name in options: + # take value from options if present or extend it with autodoc_default_options if necessary + if name in AUTODOC_EXTENDABLE_OPTIONS: + if options[name] is not None and options[name].startswith('+'): + options[name] = ','.join([config.autodoc_default_options[name], + options[name][1:]]) else: options[name] = config.autodoc_default_options[name] diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 9cb54de5b..57401133f 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -578,6 +578,36 @@ def test_autodoc_members(app): ' .. py:method:: Base.inheritedstaticmeth(cls)' ] + # ALL-members override autodoc_default_options + options = {"members": None} + app.config.autodoc_default_options["members"] = "inheritedstaticmeth" + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base()', + ' .. py:method:: Base.inheritedclassmeth()', + ' .. py:method:: Base.inheritedmeth()', + ' .. py:method:: Base.inheritedstaticmeth(cls)' + ] + + # members override autodoc_default_options + options = {"members": "inheritedmeth"} + app.config.autodoc_default_options["members"] = "inheritedstaticmeth" + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base()', + ' .. py:method:: Base.inheritedmeth()', + ] + + # members extends autodoc_default_options + options = {"members": "+inheritedmeth"} + app.config.autodoc_default_options["members"] = "inheritedstaticmeth" + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base()', + ' .. py:method:: Base.inheritedmeth()', + ' .. py:method:: Base.inheritedstaticmeth(cls)' + ] + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_exclude_members(app): @@ -597,6 +627,48 @@ def test_autodoc_exclude_members(app): '.. py:class:: Base()', ] + # exclude-members overrides autodoc_default_options + options = {"members": None, + "exclude-members": "inheritedmeth"} + app.config.autodoc_default_options["exclude-members"] = "inheritedstaticmeth" + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base()', + ' .. py:method:: Base.inheritedclassmeth()', + ' .. py:method:: Base.inheritedstaticmeth(cls)' + ] + + # exclude-members extends autodoc_default_options + options = {"members": None, + "exclude-members": "+inheritedmeth"} + app.config.autodoc_default_options["exclude-members"] = "inheritedstaticmeth" + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base()', + ' .. py:method:: Base.inheritedclassmeth()', + ] + + # no exclude-members causes use autodoc_default_options + options = {"members": None} + app.config.autodoc_default_options["exclude-members"] = "inheritedstaticmeth,inheritedmeth" + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base()', + ' .. py:method:: Base.inheritedclassmeth()', + ] + + # empty exclude-members cancels autodoc_default_options + options = {"members": None, + "exclude-members": None} + app.config.autodoc_default_options["exclude-members"] = "inheritedstaticmeth,inheritedmeth" + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base()', + ' .. py:method:: Base.inheritedclassmeth()', + ' .. py:method:: Base.inheritedmeth()', + ' .. py:method:: Base.inheritedstaticmeth(cls)' + ] + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_undoc_members(app): @@ -621,6 +693,48 @@ def test_autodoc_undoc_members(app): ' .. py:method:: Class.undocmeth()' ] + # use autodoc_default_options + options = {"members": None} + app.config.autodoc_default_options["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.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:method:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:attribute:: Class.skipattr', + ' .. py:method:: Class.skipmeth()', + ' .. py:attribute:: Class.udocattr', + ' .. py:method:: Class.undocmeth()' + ] + + # options negation work check + options = {"members": None, + "no-undoc-members": None} + app.config.autodoc_default_options["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.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:method:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.skipmeth()', + ' .. py:attribute:: Class.udocattr', + ] + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_inherited_members(app): @@ -721,6 +835,68 @@ def test_autodoc_special_members(app): ' .. py:method:: Class.undocmeth()' ] + # specific special methods from autodoc_default_options + options = {"undoc-members": None} + app.config.autodoc_default_options["special-members"] = "__special2__" + actual = do_autodoc(app, 'class', 'target.Class', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Class(arg)', + ' .. py:method:: Class.__special2__()', + ] + + # specific special methods option with autodoc_default_options + options = {"undoc-members": None, + "special-members": "__init__,__special1__"} + app.config.autodoc_default_options["special-members"] = "__special2__" + 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__()', + ] + + # specific special methods merge with autodoc_default_options + options = {"undoc-members": None, + "special-members": "+__init__,__special1__"} + app.config.autodoc_default_options["special-members"] = "__special2__" + 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:method:: Class.__special2__()', + ] + + # all special methods with autodoc_default_options + options = {"members": None, + "undoc-members": None, + "special-members": None} + app.config.autodoc_default_options["special-members"] = "__special1__" + actual = do_autodoc(app, 'class', 'target.Class', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Class(arg)', + ' .. py:attribute:: Class.__dict__', + ' .. py:method:: Class.__init__(arg)', + ' .. py:attribute:: Class.__module__', + ' .. py:method:: Class.__special1__()', + ' .. py:method:: Class.__special2__()', + ' .. py:attribute:: Class.__weakref__', + ' .. py:attribute:: Class.attr', + ' .. 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:method:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. 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): From 7348ba8d60f17c746732fb2387fa8d71cf8f4f2a Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Mon, 25 Jan 2021 13:15:13 +0100 Subject: [PATCH 03/16] add more extendable options and tests --- sphinx/ext/autodoc/directive.py | 3 ++- tests/test_ext_autodoc.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index dfac5e0e6..32adc342d 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -87,7 +87,8 @@ def process_documenter_options(documenter: "Type[Documenter]", config: Config, o negated = options.pop('no-' + name, True) is None if name in config.autodoc_default_options and not negated: if name in options: - # take value from options if present or extend it with autodoc_default_options if necessary + # take value from options if present or extend it + # with autodoc_default_options if necessary if name in AUTODOC_EXTENDABLE_OPTIONS: if options[name] is not None and options[name].startswith('+'): options[name] = ','.join([config.autodoc_default_options[name], diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 57401133f..bad8da61d 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -835,6 +835,10 @@ def test_autodoc_special_members(app): ' .. py:method:: Class.undocmeth()' ] + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_special_members_extentiable(app): + # specific special methods from autodoc_default_options options = {"undoc-members": None} app.config.autodoc_default_options["special-members"] = "__special2__" From 381e30c23b0d72662ffec2658e699d020616e06f Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Mon, 25 Jan 2021 13:24:01 +0100 Subject: [PATCH 04/16] add more extendable options and tests --- tests/test_ext_autodoc.py | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index bad8da61d..d9649e37e 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -835,10 +835,6 @@ def test_autodoc_special_members(app): ' .. py:method:: Class.undocmeth()' ] - -@pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_autodoc_special_members_extentiable(app): - # specific special methods from autodoc_default_options options = {"undoc-members": None} app.config.autodoc_default_options["special-members"] = "__special2__" @@ -871,36 +867,6 @@ def test_autodoc_special_members_extentiable(app): ' .. py:method:: Class.__special2__()', ] - # all special methods with autodoc_default_options - options = {"members": None, - "undoc-members": None, - "special-members": None} - app.config.autodoc_default_options["special-members"] = "__special1__" - actual = do_autodoc(app, 'class', 'target.Class', options) - assert list(filter(lambda l: '::' in l, actual)) == [ - '.. py:class:: Class(arg)', - ' .. py:attribute:: Class.__dict__', - ' .. py:method:: Class.__init__(arg)', - ' .. py:attribute:: Class.__module__', - ' .. py:method:: Class.__special1__()', - ' .. py:method:: Class.__special2__()', - ' .. py:attribute:: Class.__weakref__', - ' .. py:attribute:: Class.attr', - ' .. 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:method:: Class.moore(a, e, f) -> happiness', - ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', - ' .. 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): From ac5079ed851585acb754ce5fb1c8c486de60d6f6 Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Mon, 25 Jan 2021 16:07:18 +0100 Subject: [PATCH 05/16] redundant '+' has no effect --- sphinx/ext/autodoc/directive.py | 5 +++++ tests/test_ext_autodoc.py | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index 32adc342d..2c82122fc 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -93,9 +93,14 @@ def process_documenter_options(documenter: "Type[Documenter]", config: Config, o if options[name] is not None and options[name].startswith('+'): options[name] = ','.join([config.autodoc_default_options[name], options[name][1:]]) + print(options[name]) else: options[name] = config.autodoc_default_options[name] + elif isinstance(options.get(name), str) and options[name].startswith('+'): + # remove '+' from option argument if there's nothing to merge it with + options[name] = options[name][1:] + return Options(assemble_option_dict(options.items(), documenter.option_spec)) diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index d9649e37e..4c82be95b 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -627,6 +627,15 @@ def test_autodoc_exclude_members(app): '.. py:class:: Base()', ] + # + has no effect when autodoc_default_options are not present + options = {"members": None, + "exclude-members": "+inheritedmeth,inheritedstaticmeth"} + actual = do_autodoc(app, 'class', 'target.inheritance.Base', options) + assert list(filter(lambda l: '::' in l, actual)) == [ + '.. py:class:: Base()', + ' .. py:method:: Base.inheritedclassmeth()' + ] + # exclude-members overrides autodoc_default_options options = {"members": None, "exclude-members": "inheritedmeth"} From 28a37c29369c9065c3df3104c42407b6fbd48aaa Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Fri, 29 Jan 2021 10:34:05 +0100 Subject: [PATCH 06/16] local options handling --- sphinx/ext/autodoc/directive.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index 2c82122fc..5f1db2ebb 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -93,13 +93,12 @@ def process_documenter_options(documenter: "Type[Documenter]", config: Config, o if options[name] is not None and options[name].startswith('+'): options[name] = ','.join([config.autodoc_default_options[name], options[name][1:]]) - print(options[name]) else: options[name] = config.autodoc_default_options[name] elif isinstance(options.get(name), str) and options[name].startswith('+'): # remove '+' from option argument if there's nothing to merge it with - options[name] = options[name][1:] + options[name] = options[name].strip('+') return Options(assemble_option_dict(options.items(), documenter.option_spec)) From b76187a6e5b856bcfdfca2d851554d685674275b Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Fri, 29 Jan 2021 10:36:35 +0100 Subject: [PATCH 07/16] add private-members as extendable --- sphinx/ext/autodoc/directive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index 5f1db2ebb..8ac838bae 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -37,7 +37,7 @@ AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members', 'ignore-module-all', 'exclude-members', 'member-order', 'imported-members'] -AUTODOC_EXTENDABLE_OPTIONS = ['members', 'special-members', 'exclude-members'] +AUTODOC_EXTENDABLE_OPTIONS = ['members', 'private-members', 'special-members', 'exclude-members'] class DummyOptionSpec(dict): From dc277e0e18b4bed5191eb3ce875a96ae6c123768 Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Fri, 29 Jan 2021 10:41:16 +0100 Subject: [PATCH 08/16] code format --- sphinx/ext/autodoc/directive.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index 8ac838bae..47c6766e5 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -37,7 +37,8 @@ AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members', 'ignore-module-all', 'exclude-members', 'member-order', 'imported-members'] -AUTODOC_EXTENDABLE_OPTIONS = ['members', 'private-members', 'special-members', 'exclude-members'] +AUTODOC_EXTENDABLE_OPTIONS = ['members', 'private-members', 'special-members', + 'exclude-members'] class DummyOptionSpec(dict): From 8d380cadc723149f2391685c73b00b48e8a3bc0b Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Mon, 1 Feb 2021 09:46:19 +0100 Subject: [PATCH 09/16] check if autodoc_defaults are str, fix tests --- sphinx/ext/autodoc/directive.py | 6 +++--- tests/test_ext_autodoc.py | 36 +++++++++++++++---------------- tests/test_ext_autodoc_configs.py | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index 47c6766e5..c88600ff2 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -87,7 +87,7 @@ def process_documenter_options(documenter: "Type[Documenter]", config: Config, o else: negated = options.pop('no-' + name, True) is None if name in config.autodoc_default_options and not negated: - if name in options: + if name in options and isinstance(config.autodoc_default_options[name], str): # take value from options if present or extend it # with autodoc_default_options if necessary if name in AUTODOC_EXTENDABLE_OPTIONS: @@ -97,9 +97,9 @@ def process_documenter_options(documenter: "Type[Documenter]", config: Config, o else: options[name] = config.autodoc_default_options[name] - elif isinstance(options.get(name), str) and options[name].startswith('+'): + elif options.get(name) is not None: # remove '+' from option argument if there's nothing to merge it with - options[name] = options[name].strip('+') + options[name] = options[name].lstrip('+') return Options(assemble_option_dict(options.items(), documenter.option_spec)) diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 4c82be95b..e3ec11124 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -903,7 +903,7 @@ def test_autodoc_ignore_module_all(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_noindex(app): - options = {"noindex": True} + options = {"noindex": None} actual = do_autodoc(app, 'module', 'target', options) assert list(actual) == [ '', @@ -984,7 +984,7 @@ def test_autodoc_inner_class(app): '', ] - options['show-inheritance'] = True + options['show-inheritance'] = None actual = do_autodoc(app, 'class', 'target.InnerChild', options) assert list(actual) == [ '', @@ -1028,7 +1028,7 @@ def test_autodoc_staticmethod(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_descriptor(app): options = {"members": None, - "undoc-members": True} + "undoc-members": None} actual = do_autodoc(app, 'class', 'target.descriptor.Class', options) assert list(actual) == [ '', @@ -1056,7 +1056,7 @@ def test_autodoc_descriptor(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_cached_property(app): options = {"members": None, - "undoc-members": True} + "undoc-members": None} actual = do_autodoc(app, 'class', 'target.cached_property.Foo', options) assert list(actual) == [ '', @@ -1076,8 +1076,8 @@ def test_autodoc_member_order(app): # case member-order='bysource' options = {"members": None, 'member-order': 'bysource', - "undoc-members": True, - 'private-members': True} + "undoc-members": None, + 'private-members': None} actual = do_autodoc(app, 'class', 'target.Class', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Class(arg)', @@ -1101,8 +1101,8 @@ def test_autodoc_member_order(app): # case member-order='groupwise' options = {"members": None, 'member-order': 'groupwise', - "undoc-members": True, - 'private-members': True} + "undoc-members": None, + 'private-members': None} actual = do_autodoc(app, 'class', 'target.Class', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Class(arg)', @@ -1125,8 +1125,8 @@ def test_autodoc_member_order(app): # case member-order=None options = {"members": None, - "undoc-members": True, - 'private-members': True} + "undoc-members": None, + 'private-members': None} actual = do_autodoc(app, 'class', 'target.Class', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Class(arg)', @@ -1153,7 +1153,7 @@ def test_autodoc_module_member_order(app): # case member-order='bysource' options = {"members": 'foo, Bar, baz, qux, Quux, foobar', 'member-order': 'bysource', - "undoc-members": True} + "undoc-members": None} actual = do_autodoc(app, 'module', 'target.sort_by_all', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:module:: target.sort_by_all', @@ -1168,8 +1168,8 @@ def test_autodoc_module_member_order(app): # case member-order='bysource' and ignore-module-all options = {"members": 'foo, Bar, baz, qux, Quux, foobar', 'member-order': 'bysource', - "undoc-members": True, - "ignore-module-all": True} + "undoc-members": None, + "ignore-module-all": None} actual = do_autodoc(app, 'module', 'target.sort_by_all', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:module:: target.sort_by_all', @@ -1216,7 +1216,7 @@ def test_autodoc_class_scope(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_class_attributes(app): options = {"members": None, - "undoc-members": True} + "undoc-members": None} actual = do_autodoc(app, 'class', 'target.AttCls', options) assert list(actual) == [ '', @@ -1326,7 +1326,7 @@ def test_autoattribute_instance_attributes(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_slots(app): options = {"members": None, - "undoc-members": True} + "undoc-members": None} actual = do_autodoc(app, 'module', 'target.slots', options) assert list(actual) == [ '', @@ -1710,7 +1710,7 @@ def test_partialmethod_undoc_members(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_typed_instance_variables(app): options = {"members": None, - "undoc-members": True} + "undoc-members": None} actual = do_autodoc(app, 'module', 'target.typed_vars', options) assert list(actual) == [ '', @@ -1804,8 +1804,8 @@ def test_autodoc_typed_instance_variables(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_typed_inherited_instance_variables(app): options = {"members": None, - "undoc-members": True, - "inherited-members": True} + "undoc-members": None, + "inherited-members": None} actual = do_autodoc(app, 'class', 'target.typed_vars.Derived', options) assert list(actual) == [ '', diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index 7d51b7f0e..41ccb230e 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -483,7 +483,7 @@ def test_mocked_module_imports(app, warning): confoverrides={'autodoc_typehints': "signature"}) def test_autodoc_typehints_signature(app): options = {"members": None, - "undoc-members": True} + "undoc-members": None} actual = do_autodoc(app, 'module', 'target.typehints', options) assert list(actual) == [ '', @@ -549,7 +549,7 @@ def test_autodoc_typehints_signature(app): confoverrides={'autodoc_typehints': "none"}) def test_autodoc_typehints_none(app): options = {"members": None, - "undoc-members": True} + "undoc-members": None} actual = do_autodoc(app, 'module', 'target.typehints', options) assert list(actual) == [ '', From 35adeeff4111c0250c84e1568081973db30cf3fb Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Mon, 1 Feb 2021 10:28:01 +0100 Subject: [PATCH 10/16] update docs --- doc/usage/extensions/autodoc.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst index 2d8a216c0..3e5f0b1be 100644 --- a/doc/usage/extensions/autodoc.rst +++ b/doc/usage/extensions/autodoc.rst @@ -127,6 +127,17 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, .. automodule:: foo :no-undoc-members: + .. tip:: + + You can use autodoc directive options to temporarily override or + extend default options which takes list as an input. For example:: + + .. autoclass:: Noodle + :members: eat + :private-members: +_spicy, _garlickly + + .. versionchanged:: 3.5 + The default options can be overridden or extended temporarily. * Members without docstrings will be left out, unless you give the ``undoc-members`` flag option:: From fd6587d5e9426e1169f4c7ff4b502d00782864b0 Mon Sep 17 00:00:00 2001 From: pbudzyns Date: Mon, 1 Feb 2021 10:58:09 +0100 Subject: [PATCH 11/16] rebase and fix tests --- tests/test_ext_autodoc.py | 2 +- tests/test_ext_autodoc_autoclass.py | 8 ++++---- tests/test_ext_autodoc_automodule.py | 4 ++-- tests/test_ext_autodoc_configs.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 1f083c8ae..492f3f1c1 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -2395,7 +2395,7 @@ def test_name_mangling(app): @pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.') @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_hide_value(app): - options = {'members': True} + options = {'members': None} actual = do_autodoc(app, 'module', 'target.hide_value', options) assert list(actual) == [ '', diff --git a/tests/test_ext_autodoc_autoclass.py b/tests/test_ext_autodoc_autoclass.py index 488b72263..ed6442811 100644 --- a/tests/test_ext_autodoc_autoclass.py +++ b/tests/test_ext_autodoc_autoclass.py @@ -53,7 +53,7 @@ def test_classes(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_instance_variable(app): - options = {'members': True} + options = {'members': None} actual = do_autodoc(app, 'class', 'target.instance_variable.Bar', options) assert list(actual) == [ '', @@ -77,8 +77,8 @@ def test_instance_variable(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_inherited_instance_variable(app): - options = {'members': True, - 'inherited-members': True} + options = {'members': None, + 'inherited-members': None} actual = do_autodoc(app, 'class', 'target.instance_variable.Bar', options) assert list(actual) == [ '', @@ -161,7 +161,7 @@ def test_slots_attribute(app): @pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_show_inheritance_for_subclass_of_generic_type(app): - options = {'show-inheritance': True} + options = {'show-inheritance': None} actual = do_autodoc(app, 'class', 'target.classes.Quux', options) assert list(actual) == [ '', diff --git a/tests/test_ext_autodoc_automodule.py b/tests/test_ext_autodoc_automodule.py index df57724b3..3332704bb 100644 --- a/tests/test_ext_autodoc_automodule.py +++ b/tests/test_ext_autodoc_automodule.py @@ -18,7 +18,7 @@ from .test_ext_autodoc import do_autodoc @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_empty_all(app): - options = {'members': True} + options = {'members': None} actual = do_autodoc(app, 'module', 'target.empty_all', options) assert list(actual) == [ '', @@ -39,6 +39,6 @@ def test_empty_all(app): def test_subclass_of_mocked_object(app): sys.modules.pop('target', None) # unload target module to clear the module cache - options = {'members': True} + options = {'members': None} actual = do_autodoc(app, 'module', 'target.need_mocks', options) assert '.. py:class:: Inherited(*args: Any, **kwargs: Any)' in actual diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index 5b75cd972..06bf39c24 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -851,7 +851,7 @@ def test_autodoc_default_options(app): assert ' .. py:attribute:: EnumCls.val4' not in actual # with :members: = True - app.config.autodoc_default_options = {'members': True} + app.config.autodoc_default_options = {'members': None} actual = do_autodoc(app, 'class', 'target.enums.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val4' not in actual From 421c6bb4736377367628e0a50752ced1d8cde580 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 31 Jan 2021 16:10:29 +0900 Subject: [PATCH 12/16] refactor: linkcheck: Skip queuing ignored URIs To make checker-thread simpler, this checks the URI is ignored before queueing. --- sphinx/builders/linkcheck.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index c61ba5cfc..70cd302d5 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -129,6 +129,9 @@ class CheckExternalLinksBuilder(DummyBuilder): thread.start() self.workers.append(thread) + def is_ignored_uri(self, uri: str) -> bool: + return any(pat.match(uri) for pat in self.to_ignore) + @property def good(self) -> Set[str]: warnings.warn( @@ -280,21 +283,14 @@ class CheckExternalLinksBuilder(DummyBuilder): if path.exists(path.join(srcdir, uri)): return 'working', '', 0 else: - for rex in self.to_ignore: - if rex.match(uri): - return 'ignored', '', 0 - else: - self._broken[uri] = '' - return 'broken', '', 0 + self._broken[uri] = '' + return 'broken', '', 0 elif uri in self._good: return 'working', 'old', 0 elif uri in self._broken: return 'broken', self._broken[uri], 0 elif uri in self._redirected: return 'redirected', self._redirected[uri][0], self._redirected[uri][1] - for rex in self.to_ignore: - if rex.match(uri): - return 'ignored', '', 0 # need to actually check the URI for _ in range(self.config.linkcheck_retries): @@ -442,13 +438,18 @@ class CheckExternalLinksBuilder(DummyBuilder): def finish(self) -> None: logger.info('') - for hyperlink in self.hyperlinks.values(): - self.wqueue.put(hyperlink, False) - - total_links = len(self.hyperlinks) - done = 0 with open(path.join(self.outdir, 'output.txt'), 'w') as self.txt_outfile,\ open(path.join(self.outdir, 'output.json'), 'w') as self.json_outfile: + total_links = 0 + for hyperlink in self.hyperlinks.values(): + if self.is_ignored_uri(hyperlink.uri): + self.process_result((hyperlink.uri, hyperlink.docname, hyperlink.lineno, + 'ignored', '', 0)) + else: + self.wqueue.put(hyperlink, False) + total_links += 1 + + done = 0 while done < total_links: self.process_result(self.rqueue.get()) done += 1 From 39e4ea23c6c0930fa6d917361263a66f0b9b4196 Mon Sep 17 00:00:00 2001 From: jfbu Date: Thu, 4 Feb 2021 14:10:32 +0100 Subject: [PATCH 13/16] Sync with footnotehyper 1.1d This fixes a problem introduced at 1.1b which could case latex build crash in case of extra user packages. It also improves compatibility with babel + french. --- sphinx/texinputs/footnotehyper-sphinx.sty | 84 ++++++++++++++++------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/sphinx/texinputs/footnotehyper-sphinx.sty b/sphinx/texinputs/footnotehyper-sphinx.sty index dfe43cdcb..5ed0e8e6e 100644 --- a/sphinx/texinputs/footnotehyper-sphinx.sty +++ b/sphinx/texinputs/footnotehyper-sphinx.sty @@ -1,9 +1,9 @@ \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{footnotehyper-sphinx}% - [2021/01/29 v1.1c hyperref aware footnote.sty for sphinx (JFB)] + [2021/02/04 v1.1d hyperref aware footnote.sty for sphinx (JFB)] %% %% Package: footnotehyper-sphinx -%% Version: based on footnotehyper.sty 2021/01/29 v1.1c +%% Version: based on footnotehyper.sty 2021/02/04 v1.1d %% as available at https://www.ctan.org/pkg/footnotehyper %% License: the one applying to Sphinx %% @@ -17,6 +17,7 @@ %% 4. macro definition \sphinxfootnotemark, %% 5. macro definition \sphinxlongtablepatch %% 6. replaced some \undefined by \@undefined +\newif\iffootnotehyperparse\footnotehyperparsetrue \DeclareOption*{\PackageWarning{footnotehyper-sphinx}{Option `\CurrentOption' is unknown}}% \ProcessOptions\relax \newbox\FNH@notes @@ -218,38 +219,76 @@ \FNH@endfntext@fntext {\unvbox\z@}% \endgroup }% -\AtBeginDocument{% - \let\FNH@@makefntext\@makefntext - \ifx\@makefntextFB\@undefined - \expandafter\@gobble\else\expandafter\@firstofone\fi - {\ifFBFrenchFootnotes \let\FNH@@makefntext\@makefntextFB \else - \let\FNH@@makefntext\@makefntextORI\fi}% - \expandafter\FNH@check@a\FNH@@makefntext{1.2!3?4,}% - \FNH@@@1.2!3?4,\FNH@@@\relax +\let\FNH@prefntext\@empty +\let\FNH@postfntext\@empty +\AtBeginDocument{\iffootnotehyperparse\expandafter\FNH@check\fi}% +\def\FNH@safeif#1{% + \iftrue\csname if#1\endcsname\csname fi\endcsname\expandafter\@firstoftwo + \else\csname fi\endcsname\expandafter\@secondoftwo + \fi +}% +\def\FNH@check{% + \ifx\@makefntextFB\@undefined\expandafter\FNH@check@ + \else\expandafter\FNH@frenchb@ + \fi +}% +\def\FNH@frenchb@{% + \def\FNH@prefntext{% + \localleftbox{}% + \let\FBeverypar@save\FBeverypar@quote + \let\FBeverypar@quote\relax + \FNH@safeif{FB@koma}% + {\FNH@safeif{FBFrenchFootnotes}% + {\ifx\footnote\thanks + \let\@@makefnmark\@@makefnmarkTH + \@makefntextTH{} % space as in french.ldf + \else + \let\@@makefnmark\@@makefnmarkFB + \@makefntextFB{} % space as in french.ldf + \fi + }{\let\@@makefnmark\@@makefnmarkORI + \@makefntextORI{}% no space as in french.ldf + }% + }% + {\FNH@safeif{FBFrenchFootnotes}% + {\@makefntextFB{}}% + {\@makefntextORI{}}% + }% + }% + \def\FNH@postfntext{% + \let\FBeverypar@quote\FBeverypar@save + \localleftbox{\FBeveryline@quote}% + }% +}% +\def\FNH@check@{% + \expandafter\FNH@check@a\@makefntext{1.2!3?4,}% + \FNH@@@1.2!3?4,\FNH@@@\relax }% \long\def\FNH@check@a #11.2!3?4,#2\FNH@@@#3{% - \ifx\relax#3\FNH@bad@makefntext@alert + \ifx\relax#3\expandafter\FNH@checkagain@ \else - \edef\FNH@restore@{\catcode`\noexpand\@\the\catcode`\@\relax}% - \makeatletter - \ifx\@makefntextFB\@undefined - \expandafter\@gobble\else\expandafter\@firstofone\fi - {\@ifclassloaded{memoir}% - {\ifFBFrenchFootnotes\expandafter\@gobble\fi}% - {}}% - \@secondoftwo - \scantokens{\def\FNH@prefntext{#1}\def\FNH@postfntext{#2}}% - \FNH@restore@ + \def\FNH@prefntext{#1}\def\FNH@postfntext{#2}% \expandafter\FNH@check@b \fi }% +\def\FNH@checkagain@{% + \expandafter\FNH@checkagain@a + \detokenize\expandafter{\@makefntext{1.2!3?4,}}\relax\FNH@@@ +}% +\edef\FNH@temp{\noexpand\FNH@checkagain@a ##1\string{1.2!3?4,\string}}% +\expandafter\def\FNH@temp#2#3\FNH@@@{% + \ifx\relax#2% + \def\FNH@prefntext{\@makefntext{}}% + \else\FNH@bad@makefntext@alert + \fi +}% \def\FNH@check@b #1\relax{% \expandafter\expandafter\expandafter\FNH@check@c \expandafter\meaning\expandafter\FNH@prefntext \meaning\FNH@postfntext1.2!3?4,\FNH@check@c\relax }% \def\FNH@check@c #11.2!3?4,#2#3\relax{% - \ifx\FNH@check@c#2\expandafter\@gobble\fi\FNH@bad@makefntext@alert + \ifx\FNH@check@c#2\else\FNH@bad@makefntext@alert\fi }% % slight reformulation for Sphinx \def\FNH@bad@makefntext@alert{% @@ -283,7 +322,6 @@ \noexpand\if@endpe\noexpand\@endpetrue\noexpand\fi }% }% -% end of footnotehyper 2017/02/16 v0.99 % some extras for Sphinx : % \sphinxfootnotemark: usable in section titles and silently removed from TOCs. \def\sphinxfootnotemark [#1]% From 572c9a403c2c3516fdcb6eaff06448c0a743802b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 4 Feb 2021 22:27:02 +0900 Subject: [PATCH 14/16] Update CHANGES for PR #8297 --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 428df86c3..895352ff9 100644 --- a/CHANGES +++ b/CHANGES @@ -39,6 +39,8 @@ Features added * #8514: autodoc: Default values of overloaded functions are taken from actual implementation if they're ellipsis * #8775: autodoc: Support type union operator (PEP-604) in Python 3.10 or above +* #8297: autodoc: Allow to extend :confcal:`autodoc_default_options` via + directive options * #8619: html: kbd role generates customizable HTML tags for compound keys * #8634: html: Allow to change the order of JS/CSS via ``priority`` parameter for :meth:`Sphinx.add_js_file()` and :meth:`Sphinx.add_css_file()` From 50717a2cfd07d38f994d63acc23f7049d8e3626c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 4 Feb 2021 22:33:00 +0900 Subject: [PATCH 15/16] Fix typo --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 895352ff9..a4ab8bd51 100644 --- a/CHANGES +++ b/CHANGES @@ -39,7 +39,7 @@ Features added * #8514: autodoc: Default values of overloaded functions are taken from actual implementation if they're ellipsis * #8775: autodoc: Support type union operator (PEP-604) in Python 3.10 or above -* #8297: autodoc: Allow to extend :confcal:`autodoc_default_options` via +* #8297: autodoc: Allow to extend :confval:`autodoc_default_options` via directive options * #8619: html: kbd role generates customizable HTML tags for compound keys * #8634: html: Allow to change the order of JS/CSS via ``priority`` parameter From 284d9e703bf9919c34414353afd5992218518745 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 4 Feb 2021 22:35:25 +0900 Subject: [PATCH 16/16] refactor: Fix the right hand value of autodoc options should be None --- tests/test_ext_autodoc_autoclass.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_ext_autodoc_autoclass.py b/tests/test_ext_autodoc_autoclass.py index e729f220d..538b36881 100644 --- a/tests/test_ext_autodoc_autoclass.py +++ b/tests/test_ext_autodoc_autoclass.py @@ -110,7 +110,7 @@ def test_inherited_instance_variable(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_uninitialized_attributes(app): options = {"members": None, - "inherited-members": True} + "inherited-members": None} actual = do_autodoc(app, 'class', 'target.uninitialized_attributes.Derived', options) assert list(actual) == [ '', @@ -138,8 +138,8 @@ def test_uninitialized_attributes(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_undocumented_uninitialized_attributes(app): options = {"members": None, - "inherited-members": True, - "undoc-members": True} + "inherited-members": None, + "undoc-members": None} actual = do_autodoc(app, 'class', 'target.uninitialized_attributes.Derived', options) assert list(actual) == [ '',