pytransitions / transitions-gui

A frontend for transitions state machines
MIT License
64 stars 7 forks source link

Error: There is no current event loop in thread 'ThreadPoolExecutor-0_1' #5

Closed parthsharma1996 closed 5 years ago

parthsharma1996 commented 5 years ago

I tried running this with my current modification of the transitions ( see this and this)

I am receiving the following error while trying to do that. Can you give me pointers as to where it might be going wrong?

Normally I would spend some time trying to figure out the error on my own, but in this case I am bit lost since I don't have a lot of experience with threads and aync functions in general.

daphne.server 248 application_checker() ERROR Exception inside application: There is no current event loop in thread 'ThreadPoolExecutor-0_1'.
  File "/home/parth/rasa_env/lib/python3.6/site-packages/channels/sessions.py", line 175, in __call__
    return await self.inner(receive, self.send)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/channels/middleware.py", line 41, in coroutine_call
    await inner_instance(receive, send)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/channels/consumer.py", line 56, in __call__
    await await_many_dispatch([receive], self.dispatch)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/channels/utils.py", line 50, in await_many_dispatch
    await dispatch(result)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/asgiref/sync.py", line 108, in __call__
    return await asyncio.wait_for(future, timeout=None)
  File "/usr/lib64/python3.6/asyncio/tasks.py", line 339, in wait_for
    return (yield from fut)
  File "/usr/lib64/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/channels/db.py", line 13, in thread_handler
    return super().thread_handler(loop, *args, **kwargs)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/asgiref/sync.py", line 123, in thread_handler
    return self.func(*args, **kwargs)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/channels/consumer.py", line 99, in dispatch
    handler(message)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/channels/generic/websocket.py", line 53, in websocket_receive
    self.receive(text_data=message["text"])
  File "/home/parth/Documents/treadwill-v2/CCBT/chatbot/consumers.py", line 68, in receive
    handle_req(json_data)
  File "/home/parth/Documents/treadwill-v2/CCBT/chatbot/consumers.py", line 93, in new_chat
    self.interface_object = interface.Interface(self.scope['user'])
  File "/home/parth/Documents/treadwill-v2/CCBT/chatbot/interface.py", line 177, in __init__
    self.cur_controller.cur_module = get_first_module(user_object)
  File "/home/parth/Documents/treadwill-v2/CCBT/chatbot/interface.py", line 85, in get_first_module
    (user_object)
  File "/home/parth/Documents/treadwill-v2/CCBT/chatbot/modules/mood_tracker/mood_tracker.py", line 386, in __init__
    auto_transitions=False)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/transitions_gui-0.0.2-py3.6.egg/transitions_gui/web.py", line 34, in __init__
    self._application = tornado.web.Application(handlers, **settings)
  File "/home/parth/rasa_env/lib/python3.6/site-packages/tornado/web.py", line 2017, in __init__
    autoreload.start()
  File "/home/parth/rasa_env/lib/python3.6/site-packages/tornado/autoreload.py", line 121, in start
    io_loop = ioloop.IOLoop.current()
  File "/home/parth/rasa_env/lib/python3.6/site-packages/tornado/ioloop.py", line 282, in current
    loop = asyncio.get_event_loop()
  File "/usr/lib64/python3.6/asyncio/events.py", line 694, in get_event_loop
    return get_event_loop_policy().get_event_loop()
  File "/usr/lib64/python3.6/asyncio/events.py", line 602, in get_event_loop
    % threading.current_thread().name)
  There is no current event loop in thread 'ThreadPoolExecutor-0_1'.
aleneum commented 5 years ago

Hi @parthsharma1996,

is WebMachine initialized in a separate thread?

parthsharma1996 commented 5 years ago

I am not sure if that is the case.

It's being used as a backend for a chatbot so the django-channels does the initialization of the machine.

However, your commit https://github.com/aleneum/transitions-gui/commit/718a07af259c9fd0d3c684cde9f67670592aec68 seems to have fixed the issue since it works after I updated to the latest version

moritzmaier commented 2 years ago

Hi, I have a somewhat similar issue and receive the error RuntimeError: There is no current event loop in thread 'Thread-4'.

I have implemented a simple statemachine that contains only automatic transitions after a timeout of 3 seconds. It runs in the terminal and shows the expected output. However, as soon as I open http://localhost:8080/?details=true in the browser, it throws an exception and the browser GUI is not updated anymore.

Here is my minimum working example:

import sys
from os.path import join, realpath, dirname
import logging
from transitions.extensions.states import Timeout, Tags, add_state_features
from transitions_gui import WebMachine

sys.path.append(join(dirname(realpath(__file__)), '..'))
logging.basicConfig(level=logging.INFO)

@add_state_features(Timeout, Tags)
class CustomMachine(WebMachine):
    pass

# Defines all states with entry and exit actions
states = [{'name': 'init', 'timeout': 3, 'on_timeout': 'move_to1'},
         {'name': 'waypoint1', 'on_enter': 'moveJ1', 'on_exit': 'byebye_msg', 'timeout': 3, 'on_timeout': 'move_to2'},
         {'name': 'waypoint2', 'on_enter': 'moveJ2', 'on_exit': 'byebye_msg', 'timeout': 3, 'on_timeout': 'move_to1'}]

# Defines all transitions with trigger, source and destination
transitions = [{'trigger': 'move_to1', 'source': ['init', 'waypoint2'], 'dest':'waypoint1'},
               {'trigger': 'move_to2', 'source': 'waypoint1', 'dest':'waypoint2'}]

# Defines all actions called at entry or exit
class Actions(object):
    def byebye_msg(self):
        print('['+str(self.__class__.__name__)+'] Moving to next waypoint')
    def moveJ1(self):
        print('['+str(self.__class__.__name__)+'] Moving to waypoint 1')
    def moveJ2(self):
        print('['+str(self.__class__.__name__)+'] Moving to waypoint 2')

actions = Actions()

machine = CustomMachine(model=actions, states=states, transitions=transitions, initial='init',
                     name="Move to waypoints",
                     ignore_invalid_triggers=True,
                     auto_transitions=True)

# Automatic transition to first state (without this it did not start)
print(actions.state)
actions.move_to1()

Please note, that I had to manually trigger the first transition, i.e., on_timeout in init does not work properly.

And here is the error message that I am receiving after opening the GUI in the browser:

INFO:transitions_gui.web:Initializing tornado web application
INFO:transitions_gui.web:Starting server thread with daemon=False listening on port 8080
init
INFO:transitions.core:Move to waypoints: Finished processing state init exit callbacks.
[Actions] Moving to waypoint 1
INFO:transitions.core:Move to waypoints: Executed callback 'moveJ1'
INFO:transitions.core:Move to waypoints: Finished processing state waypoint1 enter callbacks.
[Actions] Moving to next waypoint
INFO:transitions.core:Move to waypoints: Executed callback 'byebye_msg'
INFO:transitions.core:Move to waypoints: Finished processing state waypoint1 exit callbacks.
[Actions] Moving to waypoint 2
INFO:transitions.core:Move to waypoints: Executed callback 'moveJ2'
INFO:transitions.core:Move to waypoints: Finished processing state waypoint2 enter callbacks.
INFO:transitions.extensions.states:Move to waypoints: Timeout state waypoint1 processed.
[Actions] Moving to next waypoint
INFO:transitions.core:Move to waypoints: Executed callback 'byebye_msg'
INFO:transitions.core:Move to waypoints: Finished processing state waypoint2 exit callbacks.
[Actions] Moving to waypoint 1
INFO:transitions.core:Move to waypoints: Executed callback 'moveJ1'
INFO:transitions.core:Move to waypoints: Finished processing state waypoint1 enter callbacks.
INFO:transitions.extensions.states:Move to waypoints: Timeout state waypoint2 processed.
INFO:tornado.access:200 GET /?details=true (127.0.0.1) 1.53ms
INFO:tornado.access:200 GET /static/img/pencil.svg (127.0.0.1) 2.27ms
INFO:tornado.access:101 GET /ws (127.0.0.1) 0.79ms
INFO:transitions_gui.handlers:WebSocket opened
[Actions] Moving to next waypoint
INFO:transitions.core:Move to waypoints: Executed callback 'byebye_msg'
INFO:transitions.core:Move to waypoints: Finished processing state waypoint1 exit callbacks.
[Actions] Moving to waypoint 2
INFO:transitions.core:Move to waypoints: Executed callback 'moveJ2'
INFO:transitions.core:Move to waypoints: Finished processing state waypoint2 enter callbacks.
Exception in thread Thread-4:
Traceback (most recent call last):
  File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.8/threading.py", line 1254, in run
    self.function(*self.args, **self.kwargs)
  File "/home/mmaier/.local/lib/python3.8/site-packages/transitions/extensions/states.py", line 114, in _process_timeout
    event_data.machine.callback(callback, event_data)
  File "/home/mmaier/.local/lib/python3.8/site-packages/transitions/core.py", line 1144, in callback
    func(*event_data.args, **event_data.kwargs)
  File "/home/mmaier/.local/lib/python3.8/site-packages/transitions/core.py", line 401, in trigger
    return self.machine._process(func)
  File "/home/mmaier/.local/lib/python3.8/site-packages/transitions/core.py", line 1188, in _process
    return trigger()
  File "/home/mmaier/.local/lib/python3.8/site-packages/transitions/core.py", line 426, in _trigger
    return self._process(event_data)
  File "/home/mmaier/.local/lib/python3.8/site-packages/transitions/core.py", line 435, in _process
    if trans.execute(event_data):
  File "/home/mmaier/.local/lib/python3.8/site-packages/transitions/core.py", line 276, in execute
    self._change_state(event_data)
  File "/home/mmaier/.local/lib/python3.8/site-packages/transitions_gui/web.py", line 20, in _change_state
    event_data.machine.websocket_handler.send_message({"method": "state_changed",
  File "/home/mmaier/.local/lib/python3.8/site-packages/transitions_gui/handlers/__init__.py", line 25, in send_message
    s.write_message(message, binary=False)
  File "/home/mmaier/.local/lib/python3.8/site-packages/tornado/websocket.py", line 340, in write_message
    return self.ws_connection.write_message(message, binary=binary)
  File "/home/mmaier/.local/lib/python3.8/site-packages/tornado/websocket.py", line 1096, in write_message
    fut = self._write_frame(True, opcode, message, flags=flags)
  File "/home/mmaier/.local/lib/python3.8/site-packages/tornado/websocket.py", line 1073, in _write_frame
    return self.stream.write(frame)
  File "/home/mmaier/.local/lib/python3.8/site-packages/tornado/iostream.py", line 540, in write
    future = Future()  # type: Future[None]
  File "/usr/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-4'.
[Actions] Moving to next waypoint
INFO:transitions.core:Move to waypoints: Executed callback 'byebye_msg'
INFO:transitions.core:Move to waypoints: Finished processing state waypoint2 exit callbacks.
[Actions] Moving to waypoint 1
INFO:transitions.core:Move to waypoints: Executed callback 'moveJ1'
INFO:transitions.core:Move to waypoints: Finished processing state waypoint1 enter callbacks.
Exception in thread Thread-5:
Traceback (most recent call last):

Obviously, the statemachine keeps running, but the exception is thrown everytime the timeout is exceeded and the GUI doesn't show any updates. As soon as I close the browser window, there are no more exceptions and the statemachine keeps on running.

UPDATE:

I've fixed this similar to the exception mentioned above, by adding an exception handler and creating a new event loop in web.py like this:

        try:
            event_data.machine.websocket_handler.send_message({"method": "state_changed",
                                                                "arg": {"model": model_name, "transition": transition,
                                                                "state": current_state}})
        except RuntimeError:
            import asyncio
            asyncio.set_event_loop(asyncio.new_event_loop())
            event_data.machine.websocket_handler.send_message({"method": "state_changed",
                                                                "arg": {"model": model_name, "transition": transition,
                                                                "state": current_state}})
        except ImportError:
            _LOGGER.warn("Could not initialize event loop correctly.")

Now, the statemachine runs and the browser GUI is updated properly. However, I am receiving the following error from time to time:

ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending name='Task-7' coro=<WebSocketProtocol13.write_message.<locals>.wrapper() running at /home/mmaier/.local/lib/python3.8/site-packages/tornado/websocket.py:1100>>
/usr/lib/python3.8/asyncio/base_events.py:641: RuntimeWarning: coroutine 'WebSocketProtocol13.write_message.<locals>.wrapper' was never awaited
  self._ready.clear()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending name='Task-8' coro=<WebSocketProtocol13.write_message.<locals>.wrapper() running at /home/mmaier/.local/lib/python3.8/site-packages/tornado/websocket.py:1100>>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending name='Task-9' coro=<WebSocketProtocol13.write_message.<locals>.wrapper() running at /home/mmaier/.local/lib/python3.8/site-packages/tornado/websocket.py:1100>>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending name='Task-10' coro=<WebSocketProtocol13.write_message.<locals>.wrapper() running at /home/mmaier/.local/lib/python3.8/site-packages/tornado/websocket.py:1100>>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending name='Task-11' coro=<WebSocketProtocol13.write_message.<locals>.wrapper() running at /home/mmaier/.local/lib/python3.8/site-packages/tornado/websocket.py:1100>>
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending name='Task-12' coro=<WebSocketProtocol13.write_message.<locals>.wrapper() running at /home/mmaier/.local/lib/python3.8/site-packages/tornado/websocket.py:1100>>

Note that statemachine and browser GUI keep running, even if this error pops up. For now, I can live with that, but am curious if this can be fixed as well!

enjoysmath commented 6 months ago

WTF, having same issue...