diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f59686c5edd37927e12d31f65ac6e1159eeaab2..71cbabf2332a390090f924e777c4876d9d1ba36d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# v2.26.5 + +* added `get_current_traceback` and fixed `RunActionAfterGeneratorCompletes` and `run_when_iterator_completes` default + exception handling plus better docs + # v2.26.4 * `run_when_iterator_completes` and `RunActionAfterGeneratorCompletes` will now support exceptions diff --git a/docs/instrumentation/traceback.rst b/docs/instrumentation/traceback.rst index 1e5f34204f43b678d47643a943313087b1c5e509..274092bbca0f0140e6064333ff184b45951c8591 100644 --- a/docs/instrumentation/traceback.rst +++ b/docs/instrumentation/traceback.rst @@ -57,10 +57,12 @@ Alternatively, you can pass a `<frame>` object to Traceback, in order to seriali .. autoclass:: satella.instrumentation.GenerationPolicy :members: -There's a helper function as well +There's some helper function as well .. autofunction:: satella.instrumentation.frame_from_traceback +.. autofunction:: satella.instrumentation.get_current_traceback + Dumping all stack frames ------------------------ diff --git a/satella/__init__.py b/satella/__init__.py index 7ee03739b226b765ed82b8311718cafa0c0fe772..e7cf032b21c2d4b94779f51904be420354860ccd 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.26.4' +__version__ = '2.26.5' diff --git a/satella/coding/generators.py b/satella/coding/generators.py index b95ba6ec72aa0cf589d1befbb7f720a4d61b6286..33d20ee79c57d39c8624d14bfc1d64e7543b2c90 100644 --- a/satella/coding/generators.py +++ b/satella/coding/generators.py @@ -20,7 +20,6 @@ class RunActionAfterGeneratorCompletes(tp.Generator, metaclass=ABCMeta): :param generator: generator to watch for :param args: arguments to invoke action_to_run with :param call_despite_closed: :meth:`action_to_run` will be called even if the generator is closed - :param call_on_exception: callable/1 with exception instance if generator somehow fails :param kwargs: keyword arguments to invoke action_to_run with """ self.closed = False @@ -73,7 +72,11 @@ class RunActionAfterGeneratorCompletes(tp.Generator, metaclass=ABCMeta): """This will run when this generator completes. Override it.""" def call_on_exception(self, exc: Exception): - """This will run when this generator throws any exception. Override it.""" + """ + This will run when this generator throws any exception inside it's __next__() or send(). You can reraise + it (which is the default behavior if you do not override this). + """ + raise exc def run_when_generator_completes(gen: tp.Generator, call_on_done: tp.Callable, diff --git a/satella/coding/iterators.py b/satella/coding/iterators.py index 3532dc0614ac7c25e6149bdcff06a77627d67bf8..3dd82cf5bfceb7edf681cb6e9e941064e94a373a 100644 --- a/satella/coding/iterators.py +++ b/satella/coding/iterators.py @@ -138,19 +138,24 @@ class hint_with_length: return self.length -def run_when_iterator_completes(iterator: tp.Iterator, func_to_run: tp.Callable, do_exception=lambda e: None, +def run_when_iterator_completes(iterator: tp.Iterator, func_to_run: tp.Callable, do_exception=None, *args, **kwargs): """ Schedule a function to be called when an iterator completes. :param iterator: iterator to use - :param func_to_run: function to run afterwards - :param do_exception: a callable to call with the exception instance if generator fails at some point + :param func_to_run: function to run afterwards, but only if there were no exceptions or they were swallowed by + do_exception. + :param do_exception: a callable to call with the exception instance if generator fails at some point. Note that + if this doesn't re-raise the exception, it will be swallowed. Default behaviour is just to + re-raise it. :param args: arguments to pass to the function :param kwargs: keyword arguments to pass to the function """ try: yield from iterator except Exception as e: + if do_exception is None: + raise do_exception(e) func_to_run(*args, **kwargs) diff --git a/satella/instrumentation/__init__.py b/satella/instrumentation/__init__.py index b317921da6d28a35a371ef0222a5d2e9ab4f2792..3cc7b4350c385b534ba1d8a4be011d18418176db 100644 --- a/satella/instrumentation/__init__.py +++ b/satella/instrumentation/__init__.py @@ -1,6 +1,6 @@ from .dump_frames_on import install_dump_frames_on, dump_frames_on from .trace_back import Traceback, GenerationPolicy, StackFrame, StoredVariableValue, \ - frame_from_traceback + frame_from_traceback, get_current_traceback __all__ = ['install_dump_frames_on', 'Traceback', 'GenerationPolicy', 'StoredVariableValue', - 'StackFrame', 'frame_from_traceback', 'dump_frames_on'] + 'StackFrame', 'frame_from_traceback', 'dump_frames_on', 'get_current_traceback'] diff --git a/satella/instrumentation/trace_back/__init__.py b/satella/instrumentation/trace_back/__init__.py index 2f892b7575194a85a2859616815ad9be43d1a494..8be056789f46670c894dd2dfe7bdc5623d4b4eed 100644 --- a/satella/instrumentation/trace_back/__init__.py +++ b/satella/instrumentation/trace_back/__init__.py @@ -1,5 +1,5 @@ from .classes import GenerationPolicy, StackFrame, StoredVariableValue -from .trace_back import Traceback, frame_from_traceback +from .trace_back import Traceback, frame_from_traceback, get_current_traceback __all__ = ['GenerationPolicy', 'StoredVariableValue', 'StackFrame', 'Traceback', - 'frame_from_traceback'] + 'frame_from_traceback', 'get_current_traceback'] diff --git a/satella/instrumentation/trace_back/trace_back.py b/satella/instrumentation/trace_back/trace_back.py index bd65862b76daaf8b601d73ed03618b6f6fef1300..758d5c8f7f95e951d862f2936c0967f5e537fa3a 100644 --- a/satella/instrumentation/trace_back/trace_back.py +++ b/satella/instrumentation/trace_back/trace_back.py @@ -148,3 +148,10 @@ class Traceback(JSONAble): except BaseException as e: output.write( u'*** %s: repr unavailable (due to locally raised %s)\n' % (name, repr(e))) + + +def get_current_traceback() -> str: + """ + A shorthand form for :code:`Traceback().pretty_format()`. + """ + return Traceback().pretty_format() diff --git a/tests/test_instrumentation/test_trace_back.py b/tests/test_instrumentation/test_trace_back.py index 5121649f361b52b43d6b3d88d314655844512d1b..36bf8e57d0664a4880d7b32992c7171c21aa70f2 100644 --- a/tests/test_instrumentation/test_trace_back.py +++ b/tests/test_instrumentation/test_trace_back.py @@ -3,10 +3,17 @@ import pickle import sys import unittest -from satella.instrumentation import Traceback +from satella.instrumentation import Traceback, get_current_traceback class TestTraceback(unittest.TestCase): + def test_get_current_traceback(self): + try: + a = 1 / 0 + except ZeroDivisionError: + tb = get_current_traceback() + self.assertIn('ZeroDivisionError', tb) + def test_no_exc(self): tb = Traceback() byte = io.BytesIO()