Skip to content

Commit

Permalink
Merge branch '4.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
tk0miya committed Mar 27, 2022
2 parents 2db180d + 4221d1a commit 223b1a9
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 58 deletions.
54 changes: 26 additions & 28 deletions CHANGES
Expand Up @@ -74,7 +74,7 @@ Bugs fixed
Testing
--------

Release 4.5.0 (in development)
Release 4.5.1 (in development)
==============================

Dependencies
Expand All @@ -83,7 +83,27 @@ Dependencies
Incompatible changes
--------------------

Deprecated
----------

Features added
--------------

Bugs fixed
----------

Testing
--------

Release 4.5.0 (released Mar 28, 2022)
=====================================

Incompatible changes
--------------------

* #10112: extlinks: Disable hardcoded links detector by default
* #9993, #10177: std domain: Disallow to refer an inline target via
:rst:role:`ref` role

Deprecated
----------
Expand All @@ -103,17 +123,22 @@ Features added
* #9337: HTML theme, add option ``enable_search_shortcuts`` that enables :kbd:'/' as
a Quick search shortcut and :kbd:`Esc` shortcut that
removes search highlighting.
* #10107: i18n: Allow to suppress translation warnings by adding ``#noqa``
comment to the tail of each translation message
* #10252: C++, support attributes on classes, unions, and enums.
* #10253: :rst:dir:`pep` role now generates URLs based on peps.python.org

Bugs fixed
----------

* #9876: autodoc: Failed to document an imported class that is built from native
binary module
* #10133: autodoc: Crashed when mocked module is used for type annotation
* #10146: autodoc: :confval:`autodoc_default_options` does not support
``no-value`` option
* #9971: autodoc: TypeError is raised when the target object is annotated by
unhashable object
* #10205: extlinks: Failed to compile regexp on checking hardcoded links
* #10277: html search: Could not search short words (ex. "use")
* #9529: LaTeX: named auto numbered footnote (ex. ``[#named]``) that is referred
multiple times was rendered to a question mark
Expand All @@ -131,33 +156,6 @@ Bugs fixed
* #10122: sphinx-build: make.bat does not check the installation of sphinx-build
command before showing help

Testing
--------

Release 4.4.1 (in development)
==============================

Dependencies
------------

Incompatible changes
--------------------

Deprecated
----------

Features added
--------------

Bugs fixed
----------

* #9876: autodoc: Failed to document an imported class that is built from native
binary module

Testing
--------

Release 4.4.0 (released Jan 17, 2022)
=====================================

Expand Down
18 changes: 18 additions & 0 deletions doc/usage/advanced/intl.rst
Expand Up @@ -68,6 +68,24 @@ be translated you need to follow these instructions:
* Run your desired build.


In order to protect against mistakes, a warning is emitted if
cross-references in the translated paragraph do not match those from the
original. This can be turned off globally using the
:confval:`suppress_warnings` configuration variable. Alternatively, to
turn it off for one message only, end the message with ``#noqa`` like
this::

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
risus tortor, luctus id ultrices at. #noqa

(Write ``\#noqa`` in case you want to have "#noqa" literally in the
text. This does not apply to code blocks, where ``#noqa`` is ignored
because code blocks do not contain references anyway.)

.. versionadded:: 4.5
The ``#noqa`` mechanism.


Translating with sphinx-intl
----------------------------

Expand Down
14 changes: 10 additions & 4 deletions doc/usage/configuration.rst
Expand Up @@ -316,7 +316,11 @@ General configuration
* ``app.add_role``
* ``app.add_generic_role``
* ``app.add_source_parser``
* ``autosectionlabel.*``
* ``download.not_readable``
* ``epub.unknown_project_files``
* ``epub.duplicated_toc_entry``
* ``i18n.inconsistent_references``
* ``image.not_readable``
* ``ref.term``
* ``ref.ref``
Expand All @@ -332,11 +336,9 @@ General configuration
* ``toc.excluded``
* ``toc.not_readable``
* ``toc.secnum``
* ``epub.unknown_project_files``
* ``epub.duplicated_toc_entry``
* ``autosectionlabel.*``

You can choose from these types.
You can choose from these types. You can also give only the first
component to exclude all warnings attached to it.

Now, this option should be considered *experimental*.

Expand Down Expand Up @@ -366,6 +368,10 @@ General configuration

Added ``toc.excluded`` and ``toc.not_readable``

.. versionadded:: 4.5

Added ``i18n.inconsistent_references``

.. confval:: needs_sphinx

If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will
Expand Down
8 changes: 3 additions & 5 deletions sphinx/domains/std.py
Expand Up @@ -747,20 +747,18 @@ def process_doc(self, env: "BuildEnvironment", docname: str, document: nodes.doc
sectname = clean_astext(title)
elif node.tagname == 'rubric':
sectname = clean_astext(node)
elif node.tagname == 'target' and len(node) > 0:
# inline target (ex: blah _`blah` blah)
sectname = clean_astext(node)
elif self.is_enumerable_node(node):
sectname = self.get_numfig_title(node)
if not sectname:
continue
else:
toctree = next(node.findall(addnodes.toctree), None)
if toctree and toctree.get('caption'):
sectname = toctree.get('caption')
else:
# anonymous-only labels
continue
if sectname:
self.labels[name] = docname, labelid, sectname
self.labels[name] = docname, labelid, sectname

def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None:
self.progoptions[program, name] = (docname, labelid)
Expand Down
9 changes: 8 additions & 1 deletion sphinx/ext/extlinks.py
Expand Up @@ -18,6 +18,7 @@
"""

import re
import sys
from typing import Any, Dict, List, Tuple

from docutils import nodes, utils
Expand Down Expand Up @@ -63,7 +64,13 @@ def check_uri(self, refnode: nodes.reference) -> None:
title = refnode.astext()

for alias, (base_uri, _caption) in self.app.config.extlinks.items():
uri_pattern = re.compile(base_uri.replace('%s', '(?P<value>.+)'))
if sys.version_info < (3, 7):
# Replace a leading backslash because re.escape() inserts a backslash before %
# on python 3.6
uri_pattern = re.compile(re.escape(base_uri).replace('\\%s', '(?P<value>.+)'))
else:
uri_pattern = re.compile(re.escape(base_uri).replace('%s', '(?P<value>.+)'))

match = uri_pattern.match(uri)
if match and match.groupdict().get('value'):
# build a replacement suggestion
Expand Down
44 changes: 33 additions & 11 deletions sphinx/transforms/i18n.py
@@ -1,6 +1,7 @@
"""Docutils transforms used by Sphinx when reading documents."""

from os import path
from re import DOTALL, match
from textwrap import indent
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, TypeVar

Expand Down Expand Up @@ -74,6 +75,14 @@ def publish_msgstr(app: "Sphinx", source: str, source_path: str, source_line: in
config.rst_prolog = rst_prolog # type: ignore


def parse_noqa(source: str) -> Tuple[str, bool]:
m = match(r"(.*)(?<!\\)#\s*noqa\s*$", source, DOTALL)
if m:
return m.group(1), True
else:
return source, False


class PreserveTranslatableMessages(SphinxTransform):
"""
Preserve original translatable messages before translation
Expand Down Expand Up @@ -111,6 +120,14 @@ def apply(self, **kwargs: Any) -> None:
# phase1: replace reference ids with translated names
for node, msg in extract_messages(self.document):
msgstr = catalog.gettext(msg)

# There is no point in having #noqa on literal blocks because
# they cannot contain references. Recognizing it would just
# completely prevent escaping the #noqa. Outside of literal
# blocks, one can always write \#noqa.
if not isinstance(node, LITERAL_TYPE_NODES):
msgstr, _ = parse_noqa(msgstr)

# XXX add marker to untranslated parts
if not msgstr or msgstr == msg or not msgstr.strip():
# as-of-yet untranslated
Expand All @@ -131,6 +148,7 @@ def apply(self, **kwargs: Any) -> None:

patch = publish_msgstr(self.app, msgstr, source,
node.line, self.config, settings)
# FIXME: no warnings about inconsistent references in this part
# XXX doctest and other block markup
if not isinstance(patch, nodes.paragraph):
continue # skip for now
Expand Down Expand Up @@ -220,6 +238,11 @@ def apply(self, **kwargs: Any) -> None:
continue # skip if the node is already translated by phase1

msgstr = catalog.gettext(msg)

# See above.
if not isinstance(node, LITERAL_TYPE_NODES):
msgstr, noqa = parse_noqa(msgstr)

# XXX add marker to untranslated parts
if not msgstr or msgstr == msg: # as-of-yet untranslated
continue
Expand Down Expand Up @@ -265,7 +288,6 @@ def apply(self, **kwargs: Any) -> None:

patch = publish_msgstr(self.app, msgstr, source,
node.line, self.config, settings)

# Structural Subelements phase2
if isinstance(node, nodes.title):
# get <title> node that placed as a first child
Expand Down Expand Up @@ -295,13 +317,13 @@ def list_replace_or_append(lst: List[N], old: N, new: N) -> None:
is_autofootnote_ref = NodeMatcher(nodes.footnote_reference, auto=Any)
old_foot_refs: List[nodes.footnote_reference] = list(node.findall(is_autofootnote_ref)) # NOQA
new_foot_refs: List[nodes.footnote_reference] = list(patch.findall(is_autofootnote_ref)) # NOQA
if len(old_foot_refs) != len(new_foot_refs):
if not noqa and len(old_foot_refs) != len(new_foot_refs):
old_foot_ref_rawsources = [ref.rawsource for ref in old_foot_refs]
new_foot_ref_rawsources = [ref.rawsource for ref in new_foot_refs]
logger.warning(__('inconsistent footnote references in translated message.' +
' original: {0}, translated: {1}')
.format(old_foot_ref_rawsources, new_foot_ref_rawsources),
location=node)
location=node, type='i18n', subtype='inconsistent_references')
old_foot_namerefs: Dict[str, List[nodes.footnote_reference]] = {}
for r in old_foot_refs:
old_foot_namerefs.setdefault(r.get('refname'), []).append(r)
Expand Down Expand Up @@ -338,13 +360,13 @@ def list_replace_or_append(lst: List[N], old: N, new: N) -> None:
is_refnamed_ref = NodeMatcher(nodes.reference, refname=Any)
old_refs: List[nodes.reference] = list(node.findall(is_refnamed_ref))
new_refs: List[nodes.reference] = list(patch.findall(is_refnamed_ref))
if len(old_refs) != len(new_refs):
if not noqa and len(old_refs) != len(new_refs):
old_ref_rawsources = [ref.rawsource for ref in old_refs]
new_ref_rawsources = [ref.rawsource for ref in new_refs]
logger.warning(__('inconsistent references in translated message.' +
' original: {0}, translated: {1}')
.format(old_ref_rawsources, new_ref_rawsources),
location=node)
location=node, type='i18n', subtype='inconsistent_references')
old_ref_names = [r['refname'] for r in old_refs]
new_ref_names = [r['refname'] for r in new_refs]
orphans = list(set(old_ref_names) - set(new_ref_names))
Expand All @@ -366,13 +388,13 @@ def list_replace_or_append(lst: List[N], old: N, new: N) -> None:
old_foot_refs = list(node.findall(is_refnamed_footnote_ref))
new_foot_refs = list(patch.findall(is_refnamed_footnote_ref))
refname_ids_map: Dict[str, List[str]] = {}
if len(old_foot_refs) != len(new_foot_refs):
if not noqa and len(old_foot_refs) != len(new_foot_refs):
old_foot_ref_rawsources = [ref.rawsource for ref in old_foot_refs]
new_foot_ref_rawsources = [ref.rawsource for ref in new_foot_refs]
logger.warning(__('inconsistent footnote references in translated message.' +
' original: {0}, translated: {1}')
.format(old_foot_ref_rawsources, new_foot_ref_rawsources),
location=node)
location=node, type='i18n', subtype='inconsistent_references')
for oldf in old_foot_refs:
refname_ids_map.setdefault(oldf["refname"], []).append(oldf["ids"])
for newf in new_foot_refs:
Expand All @@ -385,13 +407,13 @@ def list_replace_or_append(lst: List[N], old: N, new: N) -> None:
old_cite_refs: List[nodes.citation_reference] = list(node.findall(is_citation_ref))
new_cite_refs: List[nodes.citation_reference] = list(patch.findall(is_citation_ref)) # NOQA
refname_ids_map = {}
if len(old_cite_refs) != len(new_cite_refs):
if not noqa and len(old_cite_refs) != len(new_cite_refs):
old_cite_ref_rawsources = [ref.rawsource for ref in old_cite_refs]
new_cite_ref_rawsources = [ref.rawsource for ref in new_cite_refs]
logger.warning(__('inconsistent citation references in translated message.' +
' original: {0}, translated: {1}')
.format(old_cite_ref_rawsources, new_cite_ref_rawsources),
location=node)
location=node, type='i18n', subtype='inconsistent_references')
for oldc in old_cite_refs:
refname_ids_map.setdefault(oldc["refname"], []).append(oldc["ids"])
for newc in new_cite_refs:
Expand All @@ -405,13 +427,13 @@ def list_replace_or_append(lst: List[N], old: N, new: N) -> None:
old_xrefs = list(node.findall(addnodes.pending_xref))
new_xrefs = list(patch.findall(addnodes.pending_xref))
xref_reftarget_map = {}
if len(old_xrefs) != len(new_xrefs):
if not noqa and len(old_xrefs) != len(new_xrefs):
old_xref_rawsources = [xref.rawsource for xref in old_xrefs]
new_xref_rawsources = [xref.rawsource for xref in new_xrefs]
logger.warning(__('inconsistent term references in translated message.' +
' original: {0}, translated: {1}')
.format(old_xref_rawsources, new_xref_rawsources),
location=node)
location=node, type='i18n', subtype='inconsistent_references')

def get_ref_key(node: addnodes.pending_xref) -> Optional[Tuple[str, str, str]]:
case = node["refdomain"], node["reftype"]
Expand Down
8 changes: 8 additions & 0 deletions tests/roots/test-intl/literalblock.txt
Expand Up @@ -49,6 +49,14 @@ code blocks
literal-block
in list

.. highlight:: none

::

test_code_for_noqa()
continued()


doctest blocks
==============

Expand Down
16 changes: 16 additions & 0 deletions tests/roots/test-intl/noqa.txt
@@ -0,0 +1,16 @@
First section
=============

Some text with a reference, :ref:`next-section`.

Another reference: :ref:`next-section`.

This should allow to test escaping ``#noqa``.

.. _next-section:

Next section
============

Some text, again referring to the section: :ref:`next-section`.

6 changes: 6 additions & 0 deletions tests/roots/test-intl/xx/LC_MESSAGES/literalblock.po
Expand Up @@ -77,6 +77,12 @@ msgid "literal-block\n"
msgstr "LITERAL-BLOCK\n"
"IN LIST"

msgid "test_code_for_noqa()\n"
"continued()"
msgstr ""
"# TRAILING noqa SHOULD NOT GET STRIPPED\n"
"# FROM THIS BLOCK. #noqa"

msgid "doctest blocks"
msgstr "DOCTEST-BLOCKS"

Expand Down

0 comments on commit 223b1a9

Please sign in to comment.