pytransitions / transitions

A lightweight, object-oriented finite state machine implementation in Python with many extensions
MIT License
5.49k stars 524 forks source link

Treat warnings as errors to not miss unawaited coroutines #515

Closed thedrow closed 3 years ago

thedrow commented 3 years ago

Pytest will now fail on any warning not explictly ignored. This is a good idea since unawaited coroutines issue a RuntimeWarning instead of an exception. I hope that will change one day but in the meanwhile, this is a good workaround.

thedrow commented 3 years ago

These are the tests failures I have on my machine (focal) with linuxbrew which installs the latest graphviz.

tox -e py39
GLOB sdist-make: /home/thedrow/Documents/Projects/transitions/setup.py
py39 inst-nodeps: /home/thedrow/Documents/Projects/transitions/.tox/.tmp/package/1/transitions-0.8.7.zip
py39 installed: apipkg==1.5,attrs==20.3.0,coverage==5.4,dill==0.3.3,execnet==1.8.0,graphviz==0.16,iniconfig==1.1.1,mock==4.0.3,packaging==20.9,pluggy==0.13.1,py==1.10.0,pycodestyle==2.6.0,pygraphviz==1.7,pyparsing==2.4.7,pytest==6.2.2,pytest-cov==2.11.1,pytest-forked==1.3.0,pytest-runner==5.2,pytest-xdist==2.2.0,six==1.15.0,toml==0.10.2,transitions @ file:///home/thedrow/Documents/Projects/transitions/.tox/.tmp/package/1/transitions-0.8.7.zip
py39 run-test-pre: PYTHONHASHSEED='367292511'
py39 run-test: commands[0] | pytest -nauto --doctest-modules
================================================================================================================== test session starts ===================================================================================================================
platform linux -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
cachedir: .tox/py39/.pytest_cache
rootdir: /home/thedrow/Documents/Projects/transitions, configfile: pytest.ini
plugins: cov-2.11.1, xdist-2.2.0, forked-1.3.0
gw0 [2317] / gw1 [2317] / gw2 [2317] / gw3 [2317] / gw4 [2317] / gw5 [2317] / gw6 [2317] / gw7 [2317]
...........................................F.................................................................................F.................................F.................................................................................. [ 10%]
..F............................................................................................................................................................................................................................................... [ 20%]
................................................................................................................................................................................................................................................... [ 31%]
.................................................................................................................................................................................................................................................. [ 41%]
.................................................................................................................................................................................................................................................. [ 52%]
.................................................................................................................................................................................................................................................. [ 62%]
....................................................................................................................................................................................................................................F.............. [ 73%]
..........................................................................................................F....................................................................................................................................... [ 83%]
................................................................................................................................................................................................................................................... [ 94%]
........................................................................................................................................                                                                                                           [100%]
======================================================================================================================== FAILURES ========================================================================================================================
_______________________________________________________________________________________________________ TestAsync.test_new_state_in_enter_callback _______________________________________________________________________________________________________
[gw6] linux -- Python 3.9.1 /home/thedrow/Documents/Projects/transitions/.tox/py39/bin/python

cls = <class '_pytest.runner.CallInfo'>, func = <function call_runtest_hook.<locals>.<lambda> at 0x7fdcc620cf70>, when = 'call', reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: "Callable[[], TResult]",
        when: "Literal['collect', 'setup', 'call', 'teardown']",
        reraise: Optional[
            Union[Type[BaseException], Tuple[Type[BaseException], ...]]
        ] = None,
    ) -> "CallInfo[TResult]":
        excinfo = None
        start = timing.time()
        precise_start = timing.perf_counter()
        try:
>           result: Optional[TResult] = func()

.tox/py39/lib/python3.9/site-packages/_pytest/runner.py:311: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.tox/py39/lib/python3.9/site-packages/_pytest/runner.py:255: in <lambda>
    lambda: ihook(item=item, **kwds), when=when, reraise=reraise
.tox/py39/lib/python3.9/site-packages/pluggy/hooks.py:286: in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
.tox/py39/lib/python3.9/site-packages/pluggy/manager.py:93: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
.tox/py39/lib/python3.9/site-packages/pluggy/manager.py:84: in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
.tox/py39/lib/python3.9/site-packages/_pytest/unraisableexception.py:88: in pytest_runtest_call
    yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def unraisable_exception_runtest_hook() -> Generator[None, None, None]:
        with catch_unraisable_exception() as cm:
            yield
            if cm.unraisable:
                if cm.unraisable.err_msg is not None:
                    err_msg = cm.unraisable.err_msg
                else:
                    err_msg = "Exception ignored in"
                msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
                msg += "".join(
                    traceback.format_exception(
                        cm.unraisable.exc_type,
                        cm.unraisable.exc_value,
                        cm.unraisable.exc_traceback,
                    )
                )
>               warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E               pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object AsyncEvent.trigger at 0x7fdcc5385640>
E               
E               Traceback (most recent call last):
E                 File "/home/linuxbrew/.linuxbrew/opt/python@3.9/lib/python3.9/warnings.py", line 506, in _warn_unawaited_coroutine
E                   warn(msg, category=RuntimeWarning, stacklevel=2, source=coro)
E               RuntimeWarning: coroutine 'AsyncEvent.trigger' was never awaited

.tox/py39/lib/python3.9/site-packages/_pytest/unraisableexception.py:78: PytestUnraisableExceptionWarning
_________________________________________________________________________________________________ TestHierarchicalAsync.test_new_state_in_enter_callback _________________________________________________________________________________________________
[gw3] linux -- Python 3.9.1 /home/thedrow/Documents/Projects/transitions/.tox/py39/bin/python

cls = <class '_pytest.runner.CallInfo'>, func = <function call_runtest_hook.<locals>.<lambda> at 0x7f9883c05f70>, when = 'call', reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: "Callable[[], TResult]",
        when: "Literal['collect', 'setup', 'call', 'teardown']",
        reraise: Optional[
            Union[Type[BaseException], Tuple[Type[BaseException], ...]]
        ] = None,
    ) -> "CallInfo[TResult]":
        excinfo = None
        start = timing.time()
        precise_start = timing.perf_counter()
        try:
>           result: Optional[TResult] = func()

.tox/py39/lib/python3.9/site-packages/_pytest/runner.py:311: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.tox/py39/lib/python3.9/site-packages/_pytest/runner.py:255: in <lambda>
    lambda: ihook(item=item, **kwds), when=when, reraise=reraise
.tox/py39/lib/python3.9/site-packages/pluggy/hooks.py:286: in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
.tox/py39/lib/python3.9/site-packages/pluggy/manager.py:93: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
.tox/py39/lib/python3.9/site-packages/pluggy/manager.py:84: in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
.tox/py39/lib/python3.9/site-packages/_pytest/unraisableexception.py:88: in pytest_runtest_call
    yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def unraisable_exception_runtest_hook() -> Generator[None, None, None]:
        with catch_unraisable_exception() as cm:
            yield
            if cm.unraisable:
                if cm.unraisable.err_msg is not None:
                    err_msg = cm.unraisable.err_msg
                else:
                    err_msg = "Exception ignored in"
                msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
                msg += "".join(
                    traceback.format_exception(
                        cm.unraisable.exc_type,
                        cm.unraisable.exc_value,
                        cm.unraisable.exc_traceback,
                    )
                )
>               warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E               pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object HierarchicalAsyncMachine.trigger_event at 0x7f9882d7e140>
E               
E               Traceback (most recent call last):
E                 File "/home/linuxbrew/.linuxbrew/opt/python@3.9/lib/python3.9/warnings.py", line 506, in _warn_unawaited_coroutine
E                   warn(msg, category=RuntimeWarning, stacklevel=2, source=coro)
E               RuntimeWarning: coroutine 'HierarchicalAsyncMachine.trigger_event' was never awaited

.tox/py39/lib/python3.9/site-packages/_pytest/unraisableexception.py:78: PytestUnraisableExceptionWarning
_____________________________________________________________________________________________ AsyncHierarchicalGraphMachine.test_new_state_in_enter_callback _____________________________________________________________________________________________
[gw3] linux -- Python 3.9.1 /home/thedrow/Documents/Projects/transitions/.tox/py39/bin/python

cls = <class '_pytest.runner.CallInfo'>, func = <function call_runtest_hook.<locals>.<lambda> at 0x7f9882b25ca0>, when = 'call', reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: "Callable[[], TResult]",
        when: "Literal['collect', 'setup', 'call', 'teardown']",
        reraise: Optional[
            Union[Type[BaseException], Tuple[Type[BaseException], ...]]
        ] = None,
    ) -> "CallInfo[TResult]":
        excinfo = None
        start = timing.time()
        precise_start = timing.perf_counter()
        try:
>           result: Optional[TResult] = func()

.tox/py39/lib/python3.9/site-packages/_pytest/runner.py:311: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.tox/py39/lib/python3.9/site-packages/_pytest/runner.py:255: in <lambda>
    lambda: ihook(item=item, **kwds), when=when, reraise=reraise
.tox/py39/lib/python3.9/site-packages/pluggy/hooks.py:286: in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
.tox/py39/lib/python3.9/site-packages/pluggy/manager.py:93: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
.tox/py39/lib/python3.9/site-packages/pluggy/manager.py:84: in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
.tox/py39/lib/python3.9/site-packages/_pytest/unraisableexception.py:88: in pytest_runtest_call
    yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def unraisable_exception_runtest_hook() -> Generator[None, None, None]:
        with catch_unraisable_exception() as cm:
            yield
            if cm.unraisable:
                if cm.unraisable.err_msg is not None:
                    err_msg = cm.unraisable.err_msg
                else:
                    err_msg = "Exception ignored in"
                msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
                msg += "".join(
                    traceback.format_exception(
                        cm.unraisable.exc_type,
                        cm.unraisable.exc_value,
                        cm.unraisable.exc_traceback,
                    )
                )
>               warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E               pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object HierarchicalAsyncMachine.trigger_event at 0x7f9882aadf40>
E               
E               Traceback (most recent call last):
E                 File "/home/linuxbrew/.linuxbrew/opt/python@3.9/lib/python3.9/warnings.py", line 506, in _warn_unawaited_coroutine
E                   warn(msg, category=RuntimeWarning, stacklevel=2, source=coro)
E               RuntimeWarning: coroutine 'HierarchicalAsyncMachine.trigger_event' was never awaited

.tox/py39/lib/python3.9/site-packages/_pytest/unraisableexception.py:78: PytestUnraisableExceptionWarning
___________________________________________________________________________________________________ AsyncGraphMachine.test_new_state_in_enter_callback ___________________________________________________________________________________________________
[gw4] linux -- Python 3.9.1 /home/thedrow/Documents/Projects/transitions/.tox/py39/bin/python

cls = <class '_pytest.runner.CallInfo'>, func = <function call_runtest_hook.<locals>.<lambda> at 0x7ff22f24f040>, when = 'call', reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: "Callable[[], TResult]",
        when: "Literal['collect', 'setup', 'call', 'teardown']",
        reraise: Optional[
            Union[Type[BaseException], Tuple[Type[BaseException], ...]]
        ] = None,
    ) -> "CallInfo[TResult]":
        excinfo = None
        start = timing.time()
        precise_start = timing.perf_counter()
        try:
>           result: Optional[TResult] = func()

.tox/py39/lib/python3.9/site-packages/_pytest/runner.py:311: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.tox/py39/lib/python3.9/site-packages/_pytest/runner.py:255: in <lambda>
    lambda: ihook(item=item, **kwds), when=when, reraise=reraise
.tox/py39/lib/python3.9/site-packages/pluggy/hooks.py:286: in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
.tox/py39/lib/python3.9/site-packages/pluggy/manager.py:93: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
.tox/py39/lib/python3.9/site-packages/pluggy/manager.py:84: in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
.tox/py39/lib/python3.9/site-packages/_pytest/unraisableexception.py:88: in pytest_runtest_call
    yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def unraisable_exception_runtest_hook() -> Generator[None, None, None]:
        with catch_unraisable_exception() as cm:
            yield
            if cm.unraisable:
                if cm.unraisable.err_msg is not None:
                    err_msg = cm.unraisable.err_msg
                else:
                    err_msg = "Exception ignored in"
                msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
                msg += "".join(
                    traceback.format_exception(
                        cm.unraisable.exc_type,
                        cm.unraisable.exc_value,
                        cm.unraisable.exc_traceback,
                    )
                )
>               warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E               pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object AsyncEvent.trigger at 0x7ff22f1697c0>
E               
E               Traceback (most recent call last):
E                 File "/home/linuxbrew/.linuxbrew/opt/python@3.9/lib/python3.9/warnings.py", line 506, in _warn_unawaited_coroutine
E                   warn(msg, category=RuntimeWarning, stacklevel=2, source=coro)
E               RuntimeWarning: coroutine 'AsyncEvent.trigger' was never awaited

.tox/py39/lib/python3.9/site-packages/_pytest/unraisableexception.py:78: PytestUnraisableExceptionWarning
________________________________________________________________________________________________________________ PygraphvizTest.test_roi _________________________________________________________________________________________________________________
[gw0] linux -- Python 3.9.1 /home/thedrow/Documents/Projects/transitions/.tox/py39/bin/python

self = <tests.test_pygraphviz.PygraphvizTest testMethod=test_roi>

    def test_roi(self):
        m = self.machine_cls(states=['A', 'B', 'C', 'D', 'E', 'F'], initial='A')
        m.add_transition('to_state_A', 'B', 'A')
        m.add_transition('to_state_C', 'B', 'C')
        m.add_transition('to_state_F', 'B', 'F')
        g1 = m.get_graph(show_roi=True)
        self.assertEqual(len(g1.edges()), 0)
        self.assertEqual(len(g1.nodes()), 1)
        m.to_B()
        g2 = m.get_graph(show_roi=True)
>       self.assertEqual(len(g2.edges()), 4)
E       AssertionError: 3 != 4

tests/test_pygraphviz.py:87: AssertionError
_____________________________________________________________________________________________________________ TestPygraphvizNested.test_roi ______________________________________________________________________________________________________________
[gw5] linux -- Python 3.9.1 /home/thedrow/Documents/Projects/transitions/.tox/py39/bin/python

self = <tests.test_pygraphviz.TestPygraphvizNested testMethod=test_roi>

    def test_roi(self):
        class Model:
            def is_fast(self, *args, **kwargs):
                return True
        model = Model()
        m = self.machine_cls(model, states=self.states, transitions=self.transitions, initial='A', title='A test',
                             use_pygraphviz=self.use_pygraphviz, show_conditions=True)
        model.walk()
        model.run()
        g1 = model.get_graph(show_roi=True)
        _, nodes, edges = self.parse_dot(g1)
>       self.assertEqual(len(edges), 4)
E       AssertionError: 0 != 4

tests/test_graphviz.py:302: AssertionError
================================================================================================================ short test summary info =================================================================================================================
FAILED tests/test_async.py::TestAsync::test_new_state_in_enter_callback - pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object AsyncEvent.trigger at 0x7fdcc5385640>
FAILED tests/test_async.py::TestHierarchicalAsync::test_new_state_in_enter_callback - pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object HierarchicalAsyncMachine.trigger_event at 0x7f9882d7e140>
FAILED tests/test_async.py::AsyncHierarchicalGraphMachine::test_new_state_in_enter_callback - pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object HierarchicalAsyncMachine.trigger_event at 0x7f9882aadf40>
FAILED tests/test_async.py::AsyncGraphMachine::test_new_state_in_enter_callback - pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object AsyncEvent.trigger at 0x7ff22f1697c0>
FAILED tests/test_pygraphviz.py::PygraphvizTest::test_roi - AssertionError: 3 != 4
FAILED tests/test_pygraphviz.py::TestPygraphvizNested::test_roi - AssertionError: 0 != 4
============================================================================================================ 6 failed, 2311 passed in 11.03s =============================================================================================================
ERROR: InvocationError for command /home/thedrow/Documents/Projects/transitions/.tox/py39/bin/pytest -nauto --doctest-modules (exited with code 1)
________________________________________________________________________________________________________________________ summary _________________________________________________________________________________________________________________________
ERROR:   py39: commands failed