Add `sphinx.util.index_entries` (#11528)

This commit is contained in:
Adam Turner 2023-07-28 07:41:10 +01:00 committed by GitHub
parent fca33a203d
commit c9f0e67cca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 76 additions and 59 deletions

View File

@ -16,6 +16,8 @@ Deprecated
Use ``hashlib`` instead. Use ``hashlib`` instead.
* #11526: Deprecate ``sphinx.testing.path``. * #11526: Deprecate ``sphinx.testing.path``.
Use ``os.path`` or ``pathlib`` instead. Use ``os.path`` or ``pathlib`` instead.
* #11528: Deprecate ``sphinx.util.split_index_msg`` and ``sphinx.util.split_into``.
Use ``sphinx.util.index_entries.split_index_msg`` instead.
Features added Features added
-------------- --------------

View File

@ -22,6 +22,16 @@ The following is a list of deprecated interfaces.
- Removed - Removed
- Alternatives - Alternatives
* - ``sphinx.util.split_into``
- 7.2
- 9.0
- N/A
* - ``sphinx.util.split_index_msg``
- 7.2
- 9.0
- ``sphinx.util.index_entries.split_index_msg``
* - ``sphinx.testing.path`` * - ``sphinx.testing.path``
- 7.2 - 7.2
- 9.0 - 9.0

View File

@ -18,10 +18,11 @@ from sphinx.application import Sphinx
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.errors import ThemeError from sphinx.errors import ThemeError
from sphinx.locale import __ from sphinx.locale import __
from sphinx.util import logging, split_index_msg from sphinx.util import logging
from sphinx.util.console import bold # type: ignore from sphinx.util.console import bold # type: ignore
from sphinx.util.display import status_iterator from sphinx.util.display import status_iterator
from sphinx.util.i18n import CatalogInfo, docname_to_domain from sphinx.util.i18n import CatalogInfo, docname_to_domain
from sphinx.util.index_entries import split_index_msg
from sphinx.util.nodes import extract_messages, traverse_translatable_index from sphinx.util.nodes import extract_messages, traverse_translatable_index
from sphinx.util.osutil import canon_path, ensuredir, relpath from sphinx.util.osutil import canon_path, ensuredir, relpath
from sphinx.util.tags import Tags from sphinx.util.tags import Tags

View File

@ -11,8 +11,9 @@ from docutils.parsers.rst import directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.domains import Domain from sphinx.domains import Domain
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.util import logging, split_index_msg from sphinx.util import logging
from sphinx.util.docutils import ReferenceRole, SphinxDirective from sphinx.util.docutils import ReferenceRole, SphinxDirective
from sphinx.util.index_entries import split_index_msg
from sphinx.util.nodes import process_index_entry from sphinx.util.nodes import process_index_entry
from sphinx.util.typing import OptionSpec from sphinx.util.typing import OptionSpec

View File

@ -11,7 +11,8 @@ from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.errors import NoUri from sphinx.errors import NoUri
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.util import logging, split_into from sphinx.util import logging
from sphinx.util.index_entries import _split_into
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -41,20 +42,20 @@ class IndexEntries:
try: try:
if entry_type == 'single': if entry_type == 'single':
try: try:
entry, sub_entry = split_into(2, 'single', value) entry, sub_entry = _split_into(2, 'single', value)
except ValueError: except ValueError:
entry, = split_into(1, 'single', value) entry, = _split_into(1, 'single', value)
sub_entry = '' sub_entry = ''
_add_entry(entry, sub_entry, main, _add_entry(entry, sub_entry, main,
dic=new, link=uri, key=category_key) dic=new, link=uri, key=category_key)
elif entry_type == 'pair': elif entry_type == 'pair':
first, second = split_into(2, 'pair', value) first, second = _split_into(2, 'pair', value)
_add_entry(first, second, main, _add_entry(first, second, main,
dic=new, link=uri, key=category_key) dic=new, link=uri, key=category_key)
_add_entry(second, first, main, _add_entry(second, first, main,
dic=new, link=uri, key=category_key) dic=new, link=uri, key=category_key)
elif entry_type == 'triple': elif entry_type == 'triple':
first, second, third = split_into(3, 'triple', value) first, second, third = _split_into(3, 'triple', value)
_add_entry(first, second + ' ' + third, main, _add_entry(first, second + ' ' + third, main,
dic=new, link=uri, key=category_key) dic=new, link=uri, key=category_key)
_add_entry(second, third + ', ' + first, main, _add_entry(second, third + ', ' + first, main,
@ -62,11 +63,11 @@ class IndexEntries:
_add_entry(third, first + ' ' + second, main, _add_entry(third, first + ' ' + second, main,
dic=new, link=uri, key=category_key) dic=new, link=uri, key=category_key)
elif entry_type == 'see': elif entry_type == 'see':
first, second = split_into(2, 'see', value) first, second = _split_into(2, 'see', value)
_add_entry(first, _('see %s') % second, None, _add_entry(first, _('see %s') % second, None,
dic=new, link=False, key=category_key) dic=new, link=False, key=category_key)
elif entry_type == 'seealso': elif entry_type == 'seealso':
first, second = split_into(2, 'see', value) first, second = _split_into(2, 'see', value)
_add_entry(first, _('see also %s') % second, None, _add_entry(first, _('see also %s') % second, None,
dic=new, link=False, key=category_key) dic=new, link=False, key=category_key)
else: else:

View File

@ -16,7 +16,7 @@ from docutils.nodes import Element, Node
from sphinx import addnodes, package_dir from sphinx import addnodes, package_dir
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.util import split_index_msg from sphinx.util.index_entries import split_index_msg
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Iterable from collections.abc import Iterable

View File

@ -18,8 +18,9 @@ from sphinx.errors import ConfigError
from sphinx.locale import __ from sphinx.locale import __
from sphinx.locale import init as init_locale from sphinx.locale import init as init_locale
from sphinx.transforms import SphinxTransform from sphinx.transforms import SphinxTransform
from sphinx.util import get_filetype, logging, split_index_msg from sphinx.util import get_filetype, logging
from sphinx.util.i18n import docname_to_domain from sphinx.util.i18n import docname_to_domain
from sphinx.util.index_entries import split_index_msg
from sphinx.util.nodes import ( from sphinx.util.nodes import (
IMAGE_TYPE_NODES, IMAGE_TYPE_NODES,
LITERAL_TYPE_NODES, LITERAL_TYPE_NODES,

View File

@ -16,6 +16,7 @@ from sphinx.locale import __
from sphinx.util import display as _display from sphinx.util import display as _display
from sphinx.util import exceptions as _exceptions from sphinx.util import exceptions as _exceptions
from sphinx.util import http_date as _http_date from sphinx.util import http_date as _http_date
from sphinx.util import index_entries as _index_entries
from sphinx.util import logging from sphinx.util import logging
from sphinx.util import osutil as _osutil from sphinx.util import osutil as _osutil
from sphinx.util.console import strip_colors # NoQA: F401 from sphinx.util.console import strip_colors # NoQA: F401
@ -220,30 +221,6 @@ def parselinenos(spec: str, total: int) -> list[int]:
return items return items
def split_into(n: int, type: str, value: str) -> list[str]:
"""Split an index entry into a given number of parts at semicolons."""
parts = [x.strip() for x in value.split(';', n - 1)]
if len(list(filter(None, parts))) < n:
raise ValueError(f'invalid {type} index entry {value!r}')
return parts
def split_index_msg(entry_type: str, value: str) -> list[str]:
# new entry types must be listed in util/nodes.py!
if entry_type == 'single':
try:
return split_into(2, 'single', value)
except ValueError:
return split_into(1, 'single', value)
if entry_type == 'pair':
return split_into(2, 'pair', value)
if entry_type == 'triple':
return split_into(3, 'triple', value)
if entry_type in {'see', 'seealso'}:
return split_into(2, 'see', value)
raise ValueError(f'invalid {entry_type} index entry {value!r}')
def import_object(objname: str, source: str | None = None) -> Any: def import_object(objname: str, source: str | None = None) -> Any:
"""Import python object by qualname.""" """Import python object by qualname."""
try: try:
@ -300,6 +277,9 @@ _DEPRECATED_OBJECTS = {
'format_exception_cut_frames': (_exceptions.format_exception_cut_frames, 'format_exception_cut_frames': (_exceptions.format_exception_cut_frames,
'sphinx.exceptions.format_exception_cut_frames'), 'sphinx.exceptions.format_exception_cut_frames'),
'xmlname_checker': (_xml_name_checker, 'sphinx.builders.epub3._XML_NAME_PATTERN'), 'xmlname_checker': (_xml_name_checker, 'sphinx.builders.epub3._XML_NAME_PATTERN'),
'split_index_msg': (_index_entries.split_index_msg,
'sphinx.util.index_entries.split_index_msg'),
'split_into': (_index_entries.split_index_msg, 'sphinx.util.index_entries.split_into'),
'md5': (_md5, ''), 'md5': (_md5, ''),
'sha1': (_sha1, ''), 'sha1': (_sha1, ''),
} }

View File

@ -0,0 +1,25 @@
from __future__ import annotations
def split_index_msg(entry_type: str, value: str) -> list[str]:
# new entry types must be listed in util/nodes.py!
if entry_type == 'single':
try:
return _split_into(2, 'single', value)
except ValueError:
return _split_into(1, 'single', value)
if entry_type == 'pair':
return _split_into(2, 'pair', value)
if entry_type == 'triple':
return _split_into(3, 'triple', value)
if entry_type in {'see', 'seealso'}:
return _split_into(2, 'see', value)
raise ValueError(f'invalid {entry_type} index entry {value!r}')
def _split_into(n: int, type: str, value: str) -> list[str]:
"""Split an index entry into a given number of parts at semicolons."""
parts = [x.strip() for x in value.split(';', n - 1)]
if len(list(filter(None, parts))) < n:
raise ValueError(f'invalid {type} index entry {value!r}')
return parts

View File

@ -20,8 +20,9 @@ from sphinx.domains import IndexEntry
from sphinx.domains.std import StandardDomain from sphinx.domains.std import StandardDomain
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.locale import _, __, admonitionlabels from sphinx.locale import _, __, admonitionlabels
from sphinx.util import logging, split_into, texescape from sphinx.util import logging, texescape
from sphinx.util.docutils import SphinxTranslator from sphinx.util.docutils import SphinxTranslator
from sphinx.util.index_entries import split_index_msg
from sphinx.util.nodes import clean_astext, get_prev_node from sphinx.util.nodes import clean_astext, get_prev_node
from sphinx.util.template import LaTeXRenderer from sphinx.util.template import LaTeXRenderer
from sphinx.util.texescape import tex_replace_map from sphinx.util.texescape import tex_replace_map
@ -1688,37 +1689,32 @@ class LaTeXTranslator(SphinxTranslator):
if ismain: if ismain:
m = '|spxpagem' m = '|spxpagem'
try: try:
parts = tuple(map(escape, split_index_msg(type, string)))
styled = tuple(map(style, parts))
if type == 'single': if type == 'single':
try: try:
p1, p2 = (escape(x) for x in split_into(2, 'single', string)) p1, p2 = parts
P1, P2 = style(p1), style(p2) P1, P2 = styled
self.body.append(fr'\index{{{p1}@{P1}!{p2}@{P2}{m}}}') self.body.append(fr'\index{{{p1}@{P1}!{p2}@{P2}{m}}}')
except ValueError: except ValueError:
p = escape(split_into(1, 'single', string)[0]) p, = parts
P = style(p) P, = styled
self.body.append(fr'\index{{{p}@{P}{m}}}') self.body.append(fr'\index{{{p}@{P}{m}}}')
elif type == 'pair': elif type == 'pair':
p1, p2 = (escape(x) for x in split_into(2, 'pair', string)) p1, p2 = parts
P1, P2 = style(p1), style(p2) P1, P2 = styled
self.body.append(r'\index{%s@%s!%s@%s%s}\index{%s@%s!%s@%s%s}' % self.body.append(fr'\index{{{p1}@{P1}!{p2}@{P2}{m}}}'
(p1, P1, p2, P2, m, p2, P2, p1, P1, m)) fr'\index{{{p2}@{P2}!{p1}@{P1}{m}}}')
elif type == 'triple': elif type == 'triple':
p1, p2, p3 = (escape(x) for x in split_into(3, 'triple', string)) p1, p2, p3 = parts
P1, P2, P3 = style(p1), style(p2), style(p3) P1, P2, P3 = styled
self.body.append( self.body.append(
r'\index{%s@%s!%s %s@%s %s%s}' fr'\index{{{p1}@{P1}!{p2} {p3}@{P2} {P3}{m}}}'
r'\index{%s@%s!%s, %s@%s, %s%s}' fr'\index{{{p2}@{P2}!{p3}, {p1}@{P3}, {P1}{m}}}'
r'\index{%s@%s!%s %s@%s %s%s}' % fr'\index{{{p3}@{P3}!{p1} {p2}@{P1} {P2}{m}}}')
(p1, P1, p2, p3, P2, P3, m, elif type in {'see', 'seealso'}:
p2, P2, p3, p1, P3, P1, m, p1, p2 = parts
p3, P3, p1, p2, P1, P2, m)) P1, _P2 = styled
elif type == 'see':
p1, p2 = (escape(x) for x in split_into(2, 'see', string))
P1 = style(p1)
self.body.append(fr'\index{{{p1}@{P1}|see{{{p2}}}}}')
elif type == 'seealso':
p1, p2 = (escape(x) for x in split_into(2, 'seealso', string))
P1 = style(p1)
self.body.append(fr'\index{{{p1}@{P1}|see{{{p2}}}}}') self.body.append(fr'\index{{{p1}@{P1}|see{{{p2}}}}}')
else: else:
logger.warning(__('unknown index entry type %s found'), type) logger.warning(__('unknown index entry type %s found'), type)