Define `__slots__ in sphinx.builders.gettext` classes

This commit is contained in:
Adam Turner 2025-01-10 12:09:58 +00:00
parent 267fe9afa8
commit bd864d6858
2 changed files with 39 additions and 18 deletions

View File

@ -45,6 +45,12 @@ logger = logging.getLogger(__name__)
class Message: class Message:
"""An entry of translatable message.""" """An entry of translatable message."""
__slots__ = 'text', 'locations', 'uuids'
text: str
locations: list[tuple[str, int]]
uuids: list[str]
def __init__( def __init__(
self, text: str, locations: list[tuple[str, int]], uuids: list[str] self, text: str, locations: list[tuple[str, int]], uuids: list[str]
) -> None: ) -> None:
@ -52,36 +58,41 @@ class Message:
self.locations = locations self.locations = locations
self.uuids = uuids self.uuids = uuids
def __repr__(self) -> str:
return (
'Message('
f'text={self.text!r}, locations={self.locations!r}, uuids={self.uuids!r}'
')'
)
class Catalog: class Catalog:
"""Catalog of translatable messages.""" """Catalog of translatable messages."""
def __init__(self) -> None: __slots__ = ('metadata',)
self.messages: list[str] = [] # retain insertion order
def __init__(self) -> None:
# msgid -> file, line, uid # msgid -> file, line, uid
self.metadata: dict[str, list[tuple[str, int, str]]] = {} self.metadata: dict[str, list[tuple[str, int, str]]] = {}
def add(self, msg: str, origin: Element | MsgOrigin) -> None: def add(self, msg: str, origin: Element | MsgOrigin) -> None:
if not hasattr(origin, 'uid'): if not hasattr(origin, 'uid'):
# Nodes that are replicated like todo don't have a uid, # Nodes that are replicated like todo don't have a uid,
# however i18n is also unnecessary. # however translation is also unnecessary.
return return
if msg not in self.metadata: # faster lookup in hash msg_metadata = self.metadata.setdefault(msg, [])
self.messages.append(msg) line = line if (line := origin.line) is not None else -1
self.metadata[msg] = [] msg_metadata.append((origin.source or '', line, origin.uid))
line = origin.line
if line is None:
line = -1
self.metadata[msg].append((origin.source, line, origin.uid)) # type: ignore[arg-type]
def __iter__(self) -> Iterator[Message]: def __iter__(self) -> Iterator[Message]:
for message in self.messages: for message, msg_metadata in self.metadata.items():
positions = sorted({ positions = sorted(set(map(operator.itemgetter(0, 1), msg_metadata)))
(source, line) for source, line, uuid in self.metadata[message] uuids = list(map(operator.itemgetter(2), msg_metadata))
}) yield Message(text=message, locations=positions, uuids=uuids)
uuids = [uuid for source, line, uuid in self.metadata[message]]
yield Message(message, positions, uuids) @property
def messages(self) -> list[str]:
return list(self.metadata)
class MsgOrigin: class MsgOrigin:
@ -89,11 +100,20 @@ class MsgOrigin:
Origin holder for Catalog message origin. Origin holder for Catalog message origin.
""" """
__slots__ = 'source', 'line', 'uid'
source: str
line: int
uid: str
def __init__(self, source: str, line: int) -> None: def __init__(self, source: str, line: int) -> None:
self.source = source self.source = source
self.line = line self.line = line
self.uid = uuid4().hex self.uid = uuid4().hex
def __repr__(self) -> str:
return f'<MsgOrigin {self.source}:{self.line}; uid={self.uid!r}>'
class GettextRenderer(SphinxRenderer): class GettextRenderer(SphinxRenderer):
def __init__( def __init__(
@ -258,7 +278,7 @@ class MessageCatalogBuilder(I18nBuilder):
with open(template, encoding='utf-8') as f: with open(template, encoding='utf-8') as f:
context = f.read() context = f.read()
for line, _meth, msg in extract_translations(context): for line, _meth, msg in extract_translations(context):
origin = MsgOrigin(template, line) origin = MsgOrigin(source=template, line=line)
self.catalogs['sphinx'].add(msg, origin) self.catalogs['sphinx'].add(msg, origin)
except Exception as exc: except Exception as exc:
msg = f'{template}: {exc!r}' msg = f'{template}: {exc!r}'
@ -285,6 +305,7 @@ class MessageCatalogBuilder(I18nBuilder):
'display_location': self.config.gettext_location, 'display_location': self.config.gettext_location,
'display_uuid': self.config.gettext_uuid, 'display_uuid': self.config.gettext_uuid,
} }
catalog: Catalog
for textdomain, catalog in status_iterator( for textdomain, catalog in status_iterator(
self.catalogs.items(), self.catalogs.items(),
__('writing message catalogs... '), __('writing message catalogs... '),

View File

@ -65,7 +65,7 @@ logger = logging.getLogger(__name__)
class CatalogInfo: class CatalogInfo:
__slots__ = ('base_dir', 'domain', 'charset') __slots__ = 'base_dir', 'domain', 'charset'
def __init__( def __init__(
self, base_dir: str | os.PathLike[str], domain: str, charset: str self, base_dir: str | os.PathLike[str], domain: str, charset: str