Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sphinx.util.index_entries #11528

Merged
merged 7 commits into from Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -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
--------------
Expand Down
10 changes: 10 additions & 0 deletions doc/extdev/deprecated.rst
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion sphinx/builders/gettext.py
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion sphinx/domains/index.py
Expand Up @@ -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

Expand Down
15 changes: 8 additions & 7 deletions sphinx/environment/adapters/indexentries.py
Expand Up @@ -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__)

Expand Down Expand Up @@ -41,32 +42,32 @@ 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,
dic=new, link=uri, key=category_key)
_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:
Expand Down
2 changes: 1 addition & 1 deletion sphinx/search/__init__.py
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion sphinx/transforms/i18n.py
Expand Up @@ -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,
Expand Down
28 changes: 4 additions & 24 deletions sphinx/util/__init__.py
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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, ''),
}
Expand Down
25 changes: 25 additions & 0 deletions 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
44 changes: 20 additions & 24 deletions sphinx/writers/latex.py
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down