diff --git a/CHANGES b/CHANGES index 7eba3029a0d..719de31ce1a 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,8 @@ Deprecated Use ``hashlib`` instead. * #11526: Deprecate ``sphinx.testing.path``. 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 -------------- diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index 71bfad02e4d..b5662ae183c 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -22,6 +22,16 @@ The following is a list of deprecated interfaces. - Removed - 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`` - 7.2 - 9.0 diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index d0acdc77ac2..fda5f2463bc 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -18,10 +18,11 @@ from sphinx.builders import Builder from sphinx.errors import ThemeError 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.display import status_iterator 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.osutil import canon_path, ensuredir, relpath from sphinx.util.tags import Tags diff --git a/sphinx/domains/index.py b/sphinx/domains/index.py index 3715907487f..21536349ccc 100644 --- a/sphinx/domains/index.py +++ b/sphinx/domains/index.py @@ -11,8 +11,9 @@ from sphinx import addnodes from sphinx.domains import Domain 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.index_entries import split_index_msg from sphinx.util.nodes import process_index_entry from sphinx.util.typing import OptionSpec diff --git a/sphinx/environment/adapters/indexentries.py b/sphinx/environment/adapters/indexentries.py index e9b2fe7b8d4..1cbf4a2d238 100644 --- a/sphinx/environment/adapters/indexentries.py +++ b/sphinx/environment/adapters/indexentries.py @@ -11,7 +11,8 @@ from sphinx.environment import BuildEnvironment from sphinx.errors import NoUri 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__) @@ -41,20 +42,20 @@ def create_index(self, builder: Builder, group_entries: bool = True, try: if entry_type == 'single': try: - entry, sub_entry = split_into(2, 'single', value) + entry, sub_entry = _split_into(2, 'single', value) except ValueError: - entry, = split_into(1, 'single', value) + entry, = _split_into(1, 'single', value) sub_entry = '' _add_entry(entry, sub_entry, main, dic=new, link=uri, key=category_key) elif entry_type == 'pair': - first, second = split_into(2, 'pair', value) + first, second = _split_into(2, 'pair', value) _add_entry(first, second, main, dic=new, link=uri, key=category_key) _add_entry(second, first, main, dic=new, link=uri, key=category_key) 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, dic=new, link=uri, key=category_key) _add_entry(second, third + ', ' + first, main, @@ -62,11 +63,11 @@ def create_index(self, builder: Builder, group_entries: bool = True, _add_entry(third, first + ' ' + second, main, dic=new, link=uri, key=category_key) 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, dic=new, link=False, key=category_key) 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, dic=new, link=False, key=category_key) else: diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 5d20a514939..21758d3f424 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -16,7 +16,7 @@ from sphinx import addnodes, package_dir from sphinx.environment import BuildEnvironment -from sphinx.util import split_index_msg +from sphinx.util.index_entries import split_index_msg if TYPE_CHECKING: from collections.abc import Iterable diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index ceb9e241235..f6bad514321 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -18,8 +18,9 @@ from sphinx.locale import __ from sphinx.locale import init as init_locale 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.index_entries import split_index_msg from sphinx.util.nodes import ( IMAGE_TYPE_NODES, LITERAL_TYPE_NODES, diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 72f1101b1e9..105ecb38ccc 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -16,6 +16,7 @@ from sphinx.util import display as _display from sphinx.util import exceptions as _exceptions 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 osutil as _osutil from sphinx.util.console import strip_colors # NoQA: F401 @@ -220,30 +221,6 @@ def parselinenos(spec: str, total: int) -> list[int]: 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: """Import python object by qualname.""" try: @@ -300,6 +277,9 @@ def _xml_name_checker(): 'format_exception_cut_frames': (_exceptions.format_exception_cut_frames, 'sphinx.exceptions.format_exception_cut_frames'), '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, ''), 'sha1': (_sha1, ''), } diff --git a/sphinx/util/index_entries.py b/sphinx/util/index_entries.py new file mode 100644 index 00000000000..fb2d15a4d71 --- /dev/null +++ b/sphinx/util/index_entries.py @@ -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 diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index d4441987807..994e5291602 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -20,8 +20,9 @@ from sphinx.domains.std import StandardDomain from sphinx.errors import SphinxError 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.index_entries import split_index_msg from sphinx.util.nodes import clean_astext, get_prev_node from sphinx.util.template import LaTeXRenderer from sphinx.util.texescape import tex_replace_map @@ -1688,37 +1689,32 @@ def style(string: str) -> str: if ismain: m = '|spxpagem' try: + parts = tuple(map(escape, split_index_msg(type, string))) + styled = tuple(map(style, parts)) if type == 'single': try: - p1, p2 = (escape(x) for x in split_into(2, 'single', string)) - P1, P2 = style(p1), style(p2) + p1, p2 = parts + P1, P2 = styled self.body.append(fr'\index{{{p1}@{P1}!{p2}@{P2}{m}}}') except ValueError: - p = escape(split_into(1, 'single', string)[0]) - P = style(p) + p, = parts + P, = styled self.body.append(fr'\index{{{p}@{P}{m}}}') elif type == 'pair': - p1, p2 = (escape(x) for x in split_into(2, 'pair', string)) - P1, P2 = style(p1), style(p2) - self.body.append(r'\index{%s@%s!%s@%s%s}\index{%s@%s!%s@%s%s}' % - (p1, P1, p2, P2, m, p2, P2, p1, P1, m)) + p1, p2 = parts + P1, P2 = styled + self.body.append(fr'\index{{{p1}@{P1}!{p2}@{P2}{m}}}' + fr'\index{{{p2}@{P2}!{p1}@{P1}{m}}}') elif type == 'triple': - p1, p2, p3 = (escape(x) for x in split_into(3, 'triple', string)) - P1, P2, P3 = style(p1), style(p2), style(p3) + p1, p2, p3 = parts + P1, P2, P3 = styled self.body.append( - r'\index{%s@%s!%s %s@%s %s%s}' - r'\index{%s@%s!%s, %s@%s, %s%s}' - r'\index{%s@%s!%s %s@%s %s%s}' % - (p1, P1, p2, p3, P2, P3, m, - p2, P2, p3, p1, P3, P1, m, - p3, P3, p1, p2, P1, P2, m)) - 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) + fr'\index{{{p1}@{P1}!{p2} {p3}@{P2} {P3}{m}}}' + fr'\index{{{p2}@{P2}!{p3}, {p1}@{P3}, {P1}{m}}}' + fr'\index{{{p3}@{P3}!{p1} {p2}@{P1} {P2}{m}}}') + elif type in {'see', 'seealso'}: + p1, p2 = parts + P1, _P2 = styled self.body.append(fr'\index{{{p1}@{P1}|see{{{p2}}}}}') else: logger.warning(__('unknown index entry type %s found'), type)