You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
4.6 KiB
131 lines
4.6 KiB
4 years ago
|
"""
|
||
|
sphinx.events
|
||
|
~~~~~~~~~~~~~
|
||
|
|
||
|
Sphinx core events.
|
||
|
|
||
|
Gracefully adapted from the TextPress system by Armin.
|
||
|
|
||
|
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||
|
:license: BSD, see LICENSE for details.
|
||
|
"""
|
||
|
|
||
|
import warnings
|
||
|
from collections import defaultdict
|
||
|
from operator import attrgetter
|
||
|
from typing import Any, Callable, Dict, List, NamedTuple, Tuple
|
||
|
|
||
|
from sphinx.deprecation import RemovedInSphinx40Warning
|
||
|
from sphinx.errors import ExtensionError, SphinxError
|
||
|
from sphinx.locale import __
|
||
|
from sphinx.util import logging
|
||
|
|
||
|
if False:
|
||
|
# For type annotation
|
||
|
from typing import Type # for python3.5.1
|
||
|
|
||
|
from sphinx.application import Sphinx
|
||
|
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
EventListener = NamedTuple('EventListener', [('id', int),
|
||
|
('handler', Callable),
|
||
|
('priority', int)])
|
||
|
|
||
|
|
||
|
# List of all known core events. Maps name to arguments description.
|
||
|
core_events = {
|
||
|
'builder-inited': '',
|
||
|
'config-inited': 'config',
|
||
|
'env-get-outdated': 'env, added, changed, removed',
|
||
|
'env-get-updated': 'env',
|
||
|
'env-purge-doc': 'env, docname',
|
||
|
'env-before-read-docs': 'env, docnames',
|
||
|
'env-check-consistency': 'env',
|
||
|
'source-read': 'docname, source text',
|
||
|
'doctree-read': 'the doctree before being pickled',
|
||
|
'env-merge-info': 'env, read docnames, other env instance',
|
||
|
'missing-reference': 'env, node, contnode',
|
||
|
'warn-missing-reference': 'domain, node',
|
||
|
'doctree-resolved': 'doctree, docname',
|
||
|
'env-updated': 'env',
|
||
|
'build-finished': 'exception',
|
||
|
}
|
||
|
|
||
|
|
||
|
class EventManager:
|
||
|
"""Event manager for Sphinx."""
|
||
|
|
||
|
def __init__(self, app: "Sphinx" = None) -> None:
|
||
|
if app is None:
|
||
|
warnings.warn('app argument is required for EventManager.',
|
||
|
RemovedInSphinx40Warning)
|
||
|
self.app = app
|
||
|
self.events = core_events.copy()
|
||
|
self.listeners = defaultdict(list) # type: Dict[str, List[EventListener]]
|
||
|
self.next_listener_id = 0
|
||
|
|
||
|
def add(self, name: str) -> None:
|
||
|
"""Register a custom Sphinx event."""
|
||
|
if name in self.events:
|
||
|
raise ExtensionError(__('Event %r already present') % name)
|
||
|
self.events[name] = ''
|
||
|
|
||
|
def connect(self, name: str, callback: Callable, priority: int) -> int:
|
||
|
"""Connect a handler to specific event."""
|
||
|
if name not in self.events:
|
||
|
raise ExtensionError(__('Unknown event name: %s') % name)
|
||
|
|
||
|
listener_id = self.next_listener_id
|
||
|
self.next_listener_id += 1
|
||
|
self.listeners[name].append(EventListener(listener_id, callback, priority))
|
||
|
return listener_id
|
||
|
|
||
|
def disconnect(self, listener_id: int) -> None:
|
||
|
"""Disconnect a handler."""
|
||
|
for listeners in self.listeners.values():
|
||
|
for listener in listeners[:]:
|
||
|
if listener.id == listener_id:
|
||
|
listeners.remove(listener)
|
||
|
|
||
|
def emit(self, name: str, *args: Any,
|
||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> List:
|
||
|
"""Emit a Sphinx event."""
|
||
|
try:
|
||
|
logger.debug('[app] emitting event: %r%s', name, repr(args)[:100])
|
||
|
except Exception:
|
||
|
# not every object likes to be repr()'d (think
|
||
|
# random stuff coming via autodoc)
|
||
|
pass
|
||
|
|
||
|
results = []
|
||
|
listeners = sorted(self.listeners[name], key=attrgetter("priority"))
|
||
|
for listener in listeners:
|
||
|
try:
|
||
|
if self.app is None:
|
||
|
# for compatibility; RemovedInSphinx40Warning
|
||
|
results.append(listener.handler(*args))
|
||
|
else:
|
||
|
results.append(listener.handler(self.app, *args))
|
||
|
except allowed_exceptions:
|
||
|
# pass through the errors specified as *allowed_exceptions*
|
||
|
raise
|
||
|
except SphinxError:
|
||
|
raise
|
||
|
except Exception as exc:
|
||
|
raise ExtensionError(__("Handler %r for event %r threw an exception") %
|
||
|
(listener.handler, name), exc) from exc
|
||
|
return results
|
||
|
|
||
|
def emit_firstresult(self, name: str, *args: Any,
|
||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> Any:
|
||
|
"""Emit a Sphinx event and returns first result.
|
||
|
|
||
|
This returns the result of the first handler that doesn't return ``None``.
|
||
|
"""
|
||
|
for result in self.emit(name, *args, allowed_exceptions=allowed_exceptions):
|
||
|
if result is not None:
|
||
|
return result
|
||
|
return None
|