mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #7684 from tk0miya/7683_allowed_exceptions
Add allowed_exceptions parameter to Sphinx.emit() (refs: #7683)
This commit is contained in:
commit
da88a8234d
2
CHANGES
2
CHANGES
@ -73,6 +73,8 @@ Features added
|
||||
:rst:dir:`py:exception:` and :rst:dir:`py:method:` directives
|
||||
* #7596: py domain: Change a type annotation for variables to a hyperlink
|
||||
* #7582: napoleon: a type for attribute are represented like type annotation
|
||||
* #7683: Add ``allowed_exceptions`` parameter to ``Sphinx.emit()`` to allow
|
||||
handlers to raise specified exceptions
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
@ -436,22 +436,32 @@ class Sphinx:
|
||||
logger.debug('[app] disconnecting event: [id=%s]', listener_id)
|
||||
self.events.disconnect(listener_id)
|
||||
|
||||
def emit(self, event: str, *args: Any) -> List:
|
||||
def emit(self, event: str, *args: Any,
|
||||
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> List:
|
||||
"""Emit *event* and pass *arguments* to the callback functions.
|
||||
|
||||
Return the return values of all callbacks as a list. Do not emit core
|
||||
Sphinx events in extensions!
|
||||
"""
|
||||
return self.events.emit(event, *args)
|
||||
|
||||
def emit_firstresult(self, event: str, *args: Any) -> Any:
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
Added *allowed_exceptions* to specify path-through exceptions
|
||||
"""
|
||||
return self.events.emit(event, *args, allowed_exceptions=allowed_exceptions)
|
||||
|
||||
def emit_firstresult(self, event: str, *args: Any,
|
||||
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> Any:
|
||||
"""Emit *event* and pass *arguments* to the callback functions.
|
||||
|
||||
Return the result of the first callback that doesn't return ``None``.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
Added *allowed_exceptions* to specify path-through exceptions
|
||||
"""
|
||||
return self.events.emit_firstresult(event, *args)
|
||||
return self.events.emit_firstresult(event, *args,
|
||||
allowed_exceptions=allowed_exceptions)
|
||||
|
||||
# registering addon parts
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
from operator import attrgetter
|
||||
from typing import Any, Callable, Dict, List, NamedTuple
|
||||
from typing import Any, Callable, Dict, List, NamedTuple, Tuple
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.errors import ExtensionError, SphinxError
|
||||
@ -22,6 +22,7 @@ from sphinx.util import logging
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Type # for python3.5.1
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
|
||||
@ -88,7 +89,8 @@ class EventManager:
|
||||
if listener.id == listener_id:
|
||||
listeners.remove(listener)
|
||||
|
||||
def emit(self, name: str, *args: Any) -> List:
|
||||
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])
|
||||
@ -106,6 +108,9 @@ class EventManager:
|
||||
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:
|
||||
@ -113,12 +118,13 @@ class EventManager:
|
||||
(listener.handler, name)) from exc
|
||||
return results
|
||||
|
||||
def emit_firstresult(self, name: str, *args: Any) -> Any:
|
||||
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):
|
||||
for result in self.emit(name, *args, allowed_exceptions=allowed_exceptions):
|
||||
if result is not None:
|
||||
return result
|
||||
return None
|
||||
|
@ -8,6 +8,9 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.errors import ExtensionError
|
||||
from sphinx.events import EventManager
|
||||
|
||||
|
||||
@ -22,3 +25,19 @@ def test_event_priority():
|
||||
|
||||
events.emit('builder-inited')
|
||||
assert result == [3, 1, 2, 5, 4]
|
||||
|
||||
|
||||
def test_event_allowed_exceptions():
|
||||
def raise_error(app):
|
||||
raise RuntimeError
|
||||
|
||||
events = EventManager(object()) # pass an dummy object as an app
|
||||
events.connect('builder-inited', raise_error, priority=500)
|
||||
|
||||
# all errors are conveted to ExtensionError
|
||||
with pytest.raises(ExtensionError):
|
||||
events.emit('builder-inited')
|
||||
|
||||
# Allow RuntimeError (pass-through)
|
||||
with pytest.raises(RuntimeError):
|
||||
events.emit('builder-inited', allowed_exceptions=(RuntimeError,))
|
||||
|
Loading…
Reference in New Issue
Block a user