diff --git a/CHANGES b/CHANGES index 06f732b8c..d31973321 100644 --- a/CHANGES +++ b/CHANGES @@ -62,6 +62,7 @@ Features added sidebar * #7484: html theme: Avoid clashes between sidebar and other blocks * #7476: html theme: Relbar breadcrumb should contain current page +* #7506: html theme: A canonical URL is not escaped Bugs fixed ---------- @@ -69,7 +70,7 @@ Bugs fixed Testing -------- -Release 3.0.2 (in development) +Release 3.0.3 (in development) ============================== Dependencies @@ -84,6 +85,18 @@ Deprecated Features added -------------- +Bugs fixed +---------- + +Testing +-------- + +Release 3.0.2 (released Apr 19, 2020) +===================================== + +Features added +-------------- + * C, parse attributes and add :confval:`c_id_attributes` and :confval:`c_paren_attributes` to support user-defined attributes. @@ -91,11 +104,14 @@ Bugs fixed ---------- * #7461: py domain: fails with IndexError for empty tuple in type annotation +* #7510: py domain: keyword-only arguments are documented as having a default of + None +* #7418: std domain: :rst:role:`term` role could not match case-insensitively * #7461: autodoc: empty tuple in type annotation is not shown correctly +* #7479: autodoc: Sphinx builds has been slower since 3.0.0 on mocking * C++, fix spacing issue in east-const declarations. - -Testing --------- +* #7414: LaTeX: Xindy language options were incorrect +* sphinx crashes with ImportError on python3.5.1 Release 3.0.1 (released Apr 11, 2020) ===================================== diff --git a/doc/conf.py b/doc/conf.py index 681fdc449..f62e02a34 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -26,7 +26,7 @@ modindex_common_prefix = ['sphinx.'] html_static_path = ['_static'] html_sidebars = {'index': ['indexsidebar.html', 'searchbox.html']} html_additional_pages = {'index': 'index.html'} -html_use_opensearch = 'http://sphinx-doc.org' +html_use_opensearch = 'https://www.sphinx-doc.org/en/master' html_baseurl = 'https://www.sphinx-doc.org/en/master/' htmlhelp_basename = 'Sphinxdoc' diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 3f4dab272..574ede346 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -52,6 +52,7 @@ if __version__.endswith('+'): __version__ = __version__[:-1] # remove '+' for PEP-440 version spec. try: ret = subprocess.run(['git', 'show', '-s', '--pretty=format:%h'], + cwd=package_dir, stdout=PIPE, stderr=PIPE, encoding='ascii') if ret.stdout: __display_version__ += '/' + ret.stdout.strip() diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 390a5d9c7..183708520 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -54,13 +54,13 @@ XINDY_LANG_OPTIONS = { 'hr': '-L croatian -C utf8 ', 'cs': '-L czech -C utf8 ', 'da': '-L danish -C utf8 ', - 'nl': '-L dutch -C ij-as-ij-utf8 ', + 'nl': '-L dutch-ij-as-ij -C utf8 ', 'en': '-L english -C utf8 ', 'eo': '-L esperanto -C utf8 ', 'et': '-L estonian -C utf8 ', 'fi': '-L finnish -C utf8 ', 'fr': '-L french -C utf8 ', - 'de': '-L german -C din5007-utf8 ', + 'de': '-L german-din5007 -C utf8 ', 'is': '-L icelandic -C utf8 ', 'it': '-L italian -C utf8 ', 'la': '-L latin -C utf8 ', @@ -73,9 +73,9 @@ XINDY_LANG_OPTIONS = { 'pl': '-L polish -C utf8 ', 'pt': '-L portuguese -C utf8 ', 'ro': '-L romanian -C utf8 ', - 'sk': '-L slovak -C small-utf8 ', # there is also slovak-large + 'sk': '-L slovak-small -C utf8 ', # there is also slovak-large 'sl': '-L slovenian -C utf8 ', - 'es': '-L spanish -C modern-utf8 ', # there is also spanish-traditional + 'es': '-L spanish-modern -C utf8 ', # there is also spanish-traditional 'sv': '-L swedish -C utf8 ', 'tr': '-L turkish -C utf8 ', 'hsb': '-L upper-sorbian -C utf8 ', @@ -86,7 +86,7 @@ XINDY_LANG_OPTIONS = { 'be': '-L belarusian -C utf8 ', 'bg': '-L bulgarian -C utf8 ', 'mk': '-L macedonian -C utf8 ', - 'mn': '-L mongolian -C cyrillic-utf8 ', + 'mn': '-L mongolian-cyrillic -C utf8 ', 'ru': '-L russian -C utf8 ', 'sr': '-L serbian -C utf8 ', 'sh-cyrl': '-L serbian -C utf8 ', @@ -96,7 +96,7 @@ XINDY_LANG_OPTIONS = { # can work only with xelatex/lualatex, not supported by texindy+pdflatex 'el': '-L greek -C utf8 ', # FIXME, not compatible with [:2] slice but does Sphinx support Greek ? - 'el-polyton': '-L greek -C polytonic-utf8 ', + 'el-polyton': '-L greek-polytonic -C utf8 ', } XINDY_CYRILLIC_SCRIPTS = [ diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py index a1b024c09..3a6e6aa0c 100644 --- a/sphinx/deprecation.py +++ b/sphinx/deprecation.py @@ -29,13 +29,13 @@ class RemovedInSphinx60Warning(PendingDeprecationWarning): RemovedInNextVersionWarning = RemovedInSphinx40Warning -def deprecated_alias(modname: str, objects: Dict, warning: Type[Warning]) -> None: +def deprecated_alias(modname: str, objects: Dict, warning: "Type[Warning]") -> None: module = import_module(modname) sys.modules[modname] = _ModuleWrapper(module, modname, objects, warning) # type: ignore class _ModuleWrapper: - def __init__(self, module: Any, modname: str, objects: Dict, warning: Type[Warning] + def __init__(self, module: Any, modname: str, objects: Dict, warning: "Type[Warning]" ) -> None: self._module = module self._modname = modname @@ -55,7 +55,7 @@ class _ModuleWrapper: class DeprecatedDict(dict): """A deprecated dict which warns on each access.""" - def __init__(self, data: Dict, message: str, warning: Type[Warning]) -> None: + def __init__(self, data: Dict, message: str, warning: "Type[Warning]") -> None: self.message = message self.warning = warning super().__init__(data) diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index cae250b2a..8663b97b2 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -787,6 +787,8 @@ class StandardDomain(Domain): RemovedInSphinx40Warning) domain = env.get_domain('citation') return domain.resolve_xref(env, fromdocname, builder, typ, target, node, contnode) + elif typ == 'term': + resolver = self._resolve_term_xref else: resolver = self._resolve_obj_xref @@ -921,6 +923,28 @@ class StandardDomain(Domain): return make_refnode(builder, fromdocname, docname, labelid, contnode) + def _resolve_term_xref(self, env: "BuildEnvironment", fromdocname: str, + builder: "Builder", typ: str, target: str, + node: pending_xref, contnode: Element) -> Element: + result = self._resolve_obj_xref(env, fromdocname, builder, typ, + target, node, contnode) + if result: + return result + else: + for objtype, term in self.objects: + if objtype == 'term' and term.lower() == target.lower(): + docname, labelid = self.objects[objtype, term] + logger.warning(__('term %s not found in case sensitive match.' + 'made a reference to %s instead.'), + target, term, location=node, type='ref', subtype='term') + break + else: + docname, labelid = '', '' + if not docname: + return None + return make_refnode(builder, fromdocname, docname, + labelid, contnode) + def _resolve_obj_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> Element: diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index a0d1f6e32..4202bd51b 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -577,7 +577,10 @@ class Documenter: isprivate = membername.startswith('_') keep = False - if want_all and membername.startswith('__') and \ + if getattr(member, '__sphinx_mock__', False): + # mocked module or object + keep = False + elif want_all and membername.startswith('__') and \ membername.endswith('__') and len(membername) > 4: # special __methods__ if self.options.special_members is ALL: diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py index 25f50d27e..98a3a3a96 100644 --- a/sphinx/ext/autodoc/mock.py +++ b/sphinx/ext/autodoc/mock.py @@ -25,6 +25,7 @@ class _MockObject: """Used by autodoc_mock_imports.""" __display_name__ = '_MockObject' + __sphinx_mock__ = True def __new__(cls, *args: Any, **kwargs: Any) -> Any: if len(args) == 3 and isinstance(args[1], tuple): @@ -78,6 +79,7 @@ def _make_subclass(name: str, module: str, superclass: Any = _MockObject, class _MockModule(ModuleType): """Used by autodoc_mock_imports.""" __file__ = os.devnull + __sphinx_mock__ = True def __init__(self, name: str) -> None: super().__init__(name) diff --git a/sphinx/io.py b/sphinx/io.py index c8dba1fed..fc3544cb6 100644 --- a/sphinx/io.py +++ b/sphinx/io.py @@ -79,7 +79,7 @@ class SphinxBaseReader(standalone.Reader): self._app = app # hold application object only for compatibility self._env = app.env - def get_transforms(self) -> List[Type[Transform]]: + def get_transforms(self) -> List["Type[Transform]"]: transforms = super().get_transforms() + self.transforms # remove transforms which is not needed for Sphinx diff --git a/sphinx/roles.py b/sphinx/roles.py index 3a925056a..922f2e02c 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -76,7 +76,7 @@ class XRefRole(ReferenceRole): innernodeclass = nodes.literal # type: Type[TextElement] def __init__(self, fix_parens: bool = False, lowercase: bool = False, - nodeclass: Type[Element] = None, innernodeclass: Type[TextElement] = None, + nodeclass: "Type[Element]" = None, innernodeclass: "Type[TextElement]" = None, warn_dangling: bool = False) -> None: self.fix_parens = fix_parens self.lowercase = lowercase diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html index f5961e389..d10f183e1 100644 --- a/sphinx/themes/basic/layout.html +++ b/sphinx/themes/basic/layout.html @@ -132,7 +132,7 @@ {{- script() }} {%- endblock %} {%- if pageurl %} - + {%- endif %} {%- if use_opensearch %} inspect.Signature: annotation=annotation)) for i, arg in enumerate(args.kwonlyargs): - default = ast_unparse(args.kw_defaults[i]) + default = ast_unparse(args.kw_defaults[i]) or Parameter.empty annotation = ast_unparse(arg.annotation) or Parameter.empty params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default, annotation=annotation)) diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index e330ba634..d9d61db14 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -344,9 +344,7 @@ def test_pyfunction_signature_full_py38(app): doctree = restructuredtext.parse(app, text) assert_node(doctree[1][0][1], [desc_parameterlist, ([desc_parameter, nodes.inline, "*"], - [desc_parameter, ([desc_sig_name, "a"], - [desc_sig_operator, "="], - [nodes.inline, "None"])])]) + [desc_parameter, desc_sig_name, "a"])]) # case: separator in the middle text = ".. py:function:: hello(a, /, b, *, c)" @@ -356,9 +354,7 @@ def test_pyfunction_signature_full_py38(app): [desc_parameter, desc_sig_operator, "/"], [desc_parameter, desc_sig_name, "b"], [desc_parameter, desc_sig_operator, "*"], - [desc_parameter, ([desc_sig_name, "c"], - [desc_sig_operator, "="], - [nodes.inline, "None"])])]) + [desc_parameter, desc_sig_name, "c"])]) # case: separator in the middle (2) text = ".. py:function:: hello(a, /, *, b)" @@ -367,9 +363,7 @@ def test_pyfunction_signature_full_py38(app): [desc_parameterlist, ([desc_parameter, desc_sig_name, "a"], [desc_parameter, desc_sig_operator, "/"], [desc_parameter, desc_sig_operator, "*"], - [desc_parameter, ([desc_sig_name, "b"], - [desc_sig_operator, "="], - [nodes.inline, "None"])])]) + [desc_parameter, desc_sig_name, "b"])]) # case: separator at tail text = ".. py:function:: hello(a, /)" diff --git a/tests/test_domain_std.py b/tests/test_domain_std.py index c16bd861d..66253b89a 100644 --- a/tests/test_domain_std.py +++ b/tests/test_domain_std.py @@ -140,12 +140,23 @@ def test_glossary(app): [nodes.definition, nodes.paragraph, "description"]) # index - objects = list(app.env.get_domain("std").get_objects()) + domain = app.env.get_domain("std") + objects = list(domain.get_objects()) assert ("term1", "term1", "term", "index", "term-term1", -1) in objects assert ("TERM2", "TERM2", "term", "index", "term-TERM2", -1) in objects assert ("term3", "term3", "term", "index", "term-term3", -1) in objects assert ("term4", "term4", "term", "index", "term-term4", -1) in objects + # term reference (case sensitive) + refnode = domain.resolve_xref(app.env, 'index', app.builder, 'term', 'term1', + pending_xref(), nodes.paragraph()) + assert_node(refnode, nodes.reference, refid="term-term1") + + # term reference (case insensitive) + refnode = domain.resolve_xref(app.env, 'index', app.builder, 'term', 'term2', + pending_xref(), nodes.paragraph()) + assert_node(refnode, nodes.reference, refid="term-TERM2") + def test_glossary_warning(app, status, warning): # empty line between terms diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py index 627d20f54..ff1074702 100644 --- a/tests/test_util_inspect.py +++ b/tests/test_util_inspect.py @@ -317,6 +317,15 @@ def test_signature_from_str_complex_annotations(): assert sig.return_annotation == 'Callable[[int, int], int]' +def test_signature_from_str_kwonly_args(): + sig = inspect.signature_from_str('(a, *, b)') + assert list(sig.parameters.keys()) == ['a', 'b'] + assert sig.parameters['a'].kind == Parameter.POSITIONAL_OR_KEYWORD + assert sig.parameters['a'].default == Parameter.empty + assert sig.parameters['b'].kind == Parameter.KEYWORD_ONLY + assert sig.parameters['b'].default == Parameter.empty + + @pytest.mark.skipif(sys.version_info < (3, 8), reason='python-3.8 or above is required') def test_signature_from_str_positionaly_only_args():