Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RuntimeError: There is no current event loop in thread error with Streamlit and Asyncio #744

Open
jroakes opened this issue Nov 25, 2019 · 30 comments
Labels
area:server priority:P3 status:confirmed Bug has been confirmed by the Streamlit team type:bug Something isn't working

Comments

@jroakes
Copy link

jroakes commented Nov 25, 2019

Summary

I am running pyppeteer, which depends on asyncio, to load data which is only available in a rendered web page. I experienced an error earlier with Jupyter, which depends on tornado, and where I received an error that RuntimeError: This event loop is already running. The reason is that tornado was reworked with asyncio and there didn't seem to be a realistic workaround by the Jupyter developers. The issue was solved with this library nest_asyncio. Whether I include nest_asyncio or not, I receive the error RuntimeError: There is no current event loop in thread 'ScriptRunner.scriptThread'.

Steps to reproduce

What are the steps we should take to reproduce the bug:

  1. Initializing the RenderHTML class in the following imported file, will cause the error to be outputted as an error to the Streamlit UI.
import asyncio
import nest_asyncio
#nest_asyncio.apply()
from pyppeteer import launch

class HTMLMissing(Exception):
    '''An exception for a missing or invalid HTML.'''
    pass

class RenderHTML():

    def __init__(self, html=None):
        self.html = ""
        self.set_html(html)
        asyncio.get_event_loop().run_until_complete(self.build_page())

    async def build_page(self):
        browser = await launch()
        context = await browser.createIncognitoBrowserContext()
        self.page = await browser.newPage()

   ...other functions...

Expected behavior:

Load RenderHTML class, launch chrome, and build a new browser page.

Actual behavior:

With: nest_asyncio.apply()

RuntimeError: There is no current event loop in thread 'ScriptRunner.scriptThread'.
Traceback:

  File "c:\users\jroak\anaconda3\envs\project\lib\site-packages\streamlit\ScriptRunner.py", line 311, in _run_script
    exec(code, module.__dict__)
  File "D:\AnacondaProjects\DataProject\main.py", line 25, in <module>
    from engine import *
  File "D:\AnacondaProjects\DataProject\engine.py", line 25, in <module>
    from lib import *
  File "D:\AnacondaProjects\DataProject\lib\__init__.py", line 34, in <module>
    from .renderer import RenderHTML
  File "D:\AnacondaProjects\DataProject\lib\renderer.py", line 55, in <module>
    nest_asyncio.apply()
  File "c:\users\jroak\anaconda3\envs\project\lib\site-packages\nest_asyncio.py", line 11, in apply
    loop = loop or asyncio.get_event_loop()
  File "c:\users\jroak\anaconda3\envs\project\lib\asyncio\events.py", line 644, in get_event_loop
    % threading.current_thread().name)

Without: nest_asyncio.apply()

RuntimeError: There is no current event loop in thread 'ScriptRunner.scriptThread'.
Traceback:

  File "c:\users\jroak\anaconda3\envs\project\lib\site-packages\streamlit\ScriptRunner.py", line 311, in _run_script
    exec(code, module.__dict__)
  File "D:\AnacondaProjects\DataProject\main.py", line 94, in <module>
    main()
  File "D:\AnacondaProjects\DataProject\main.py", line 71, in main
    crawler = render_data(crawler)
  File "c:\users\jroak\anaconda3\envs\project\lib\site-packages\streamlit\caching.py", line 560, in wrapped_func
    return get_or_set_cache()
  File "c:\users\jroak\anaconda3\envs\project\lib\site-packages\streamlit\caching.py", line 540, in get_or_set_cache
    return_value = func(*args, **kwargs)
  File "D:\AnacondaProjects\DataProject\main.py", line 39, in render_data
    crawler.render()
  File "D:\AnacondaProjects\DataProject\engine.py", line 126, in render
    renderer = RenderHTML()
  File "D:\AnacondaProjects\DataProject\lib\renderer.py", line 72, in __init__
    asyncio.get_event_loop().run_until_complete(self.build_page())
  File "c:\users\jroak\anaconda3\envs\project\lib\asyncio\events.py", line 644, in get_event_loop
    % threading.current_thread().name)

Is this a regression?

That is, did this use to work the way you expected in the past? IDK
yes / no

Debug info

  • Streamlit version: 0.50.2
  • Python version: 3.7.5
  • Using Conda? PipEnv? PyEnv? Pex? Conda
  • OS version: Windows 10
  • Browser version: 70.0.1 (64-bit)

Additional information

If needed, add any other context about the problem here. For exmaple, did this bug come from https://discuss.streamlit.io or another site? Link the original source here!

@jroakes jroakes added the type:bug Something isn't working label Nov 25, 2019
@monchier
Copy link
Collaborator

monchier commented Dec 4, 2019

@jroakes Can you provide a mini-repro, that is, a complete Streamlit app and command line? Streamlit uses Tornado and it is likely conflicting with you trying to get an event loop in RenderHTML constructor. In general, could you explain what you are trying to do? If you are trying to

load data which is only available in a rendered web page

by using pyppeteer, you may not have to run this part via Streamlit. A workaround could be to separate your "data ingestion" from the actual Streamlit run, so that the data ingestion does not need to be run with Streamlit. Hope I understood the problem correctly.

@tvst
Copy link
Contributor

tvst commented Jan 13, 2020

Closing this. Please reopen if you have more data to help us debug.

@tvst tvst closed this as completed Jan 13, 2020
@erlapi
Copy link

erlapi commented Apr 10, 2020

I'm running into the same problem.

My code has a button that when clicked creates an HTMLSession which goes to Google to search for some content available only when the page gets rendered.
In my case it is not possible to separate the data gathering from the script run, because it would mean unnecessarily scraping google. Wondering whether there is a way to solve this.

@Agerrr
Copy link

Agerrr commented Apr 14, 2020

I also have a very similar issue. The simplest way to reproduce it is to run a streamlit app (streamlit run <your_streamlit_app_file_name>.py) with the following code:

import nest_asyncio
nest_asyncio.apply()

This results in:

RuntimeError: There is no current event loop in thread 'ScriptRunner.scriptThread'.

Stack trace:

File "[...]/lib/python3.7/site-packages/streamlit/ScriptRunner.py", line 322, in _run_script
    exec(code, module.__dict__)
File "[...]/<your_streamlit_app_file_name>.py", line 15, in <module>
    nest_asyncio.apply()
File "[...]/lib/python3.7/site-packages/nest_asyncio.py", line 9, in apply
    loop = loop or asyncio.get_event_loop()
File "[...].pyenv/versions/3.7.3/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
    % threading.current_thread().name)

@HWiese1980
Copy link

I'm currently experiencing the same issue! Is there already a fix?

@jrhone jrhone reopened this Jul 3, 2020
@scottweitzner
Copy link

scottweitzner commented Jul 30, 2020

I'm receiving this error as well.
using streamlit v0.64.0

File "/usr/local/Miniconda3-py37_4.8.2-Linux-x86_64/envs/########/lib/python3.6/site-packages/streamlit/ScriptRunner.py", line 319, in _run_script
    exec(code, module.__dict__)

File "########.py", line 7, in <module>
    from ######## import ########

File "/usr/local/Miniconda3-py37_4.8.2-Linux-x86_64/envs/########/lib/python3.6/site-packages/########/__init__.py", line 9, in <module>
    nest_asyncio.apply()

File "/usr/local/Miniconda3-py37_4.8.2-Linux-x86_64/envs/########/lib/python3.6/site-packages/nest_asyncio.py", line 10, in apply
    loop = loop or asyncio.get_event_loop()

File "/usr/local/Miniconda3-py37_4.8.2-Linux-x86_64/envs/########/lib/python3.6/asyncio/events.py", line 694, in get_event_loop
    return get_event_loop_policy().get_event_loop()

File "/usr/local/Miniconda3-py37_4.8.2-Linux-x86_64/envs/########/lib/python3.6/asyncio/events.py", line 602, in get_event_loop
    % threading.current_thread().name)

@lmeyerov
Copy link

lmeyerov commented Sep 1, 2020

Same... we do a DB query (gremlinpython), which handles async etc. fine and gets back the result, but upon conn.close(), get a similar error:

streamlit    | Traceback (most recent call last):
streamlit    |   File "/apps/views/demo_neptune_01_minimal_gremlin/__init__.py", line 125, in run_filters
streamlit    |     remoteConn.close()
streamlit    |   File "/conda/envs/rapids/lib/python3.7/site-packages/gremlin_python/driver/driver_remote_connection.py", line 52, in close
streamlit    |     self._client.close()
streamlit    |   File "/conda/envs/rapids/lib/python3.7/site-packages/gremlin_python/driver/client.py", line 109, in close
streamlit    |     conn.close()
streamlit    |   File "/conda/envs/rapids/lib/python3.7/site-packages/gremlin_python/driver/connection.py", line 51, in close
streamlit    |     self._transport.close()
streamlit    |   File "/conda/envs/rapids/lib/python3.7/site-packages/gremlin_python/driver/tornado/transport.py", line 46, in close
streamlit    |     self._ws.close()
streamlit    |   File "/conda/envs/rapids/lib/python3.7/site-packages/tornado/websocket.py", line 1160, in close
streamlit    |     self.protocol.close(code, reason)
streamlit    |   File "/conda/envs/rapids/lib/python3.7/site-packages/tornado/websocket.py", line 1041, in close
streamlit    |     self._write_frame(True, 0x8, close_data)
streamlit    |   File "/conda/envs/rapids/lib/python3.7/site-packages/tornado/websocket.py", line 846, in _write_frame
streamlit    |     return self.stream.write(frame)
streamlit    |   File "/conda/envs/rapids/lib/python3.7/site-packages/tornado/iostream.py", line 583, in write
streamlit    |     future = Future()
streamlit    |   File "/conda/envs/rapids/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
streamlit    |     % threading.current_thread().name)
streamlit    | RuntimeError: There is no current event loop in thread 'ScriptRunner.scriptThread'.

cc @spmallette wrt apache/tinkerpop#1212

@lmeyerov
Copy link

lmeyerov commented Sep 2, 2020

@tvst Are you still having issues reproducing, e.g., starting nest_asyncio?

@zkillpack
Copy link

zkillpack commented Sep 3, 2020

I'm experiencing this as well, using an internal API client that's using nest_asyncio. I'm able to reproduce the error with:

import nest_asyncio
nest_asyncio.apply()

with the same error and stack trace as @Agerrr on Python 3.6.8.

I'm using this as a workaround for the time being:

import asyncio
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

@bp6725
Copy link

bp6725 commented Oct 20, 2020

same problem when importing holoviews.. nothing special, just a simple import ..

File "c:\programdata\anaconda3\lib\site-packages\streamlit\script_runner.py", line 324, in _run_script
    exec(code, module.__dict__)
File "C:\Repos\MBA_beilinson\disruption_analysis\server\main.py", line 1, in <module>
    from pipelines.plot_correlations_map import PlotCorrelationsMap
File "c:\programdata\anaconda3\lib\site-packages\pipelines\plot_correlations_map.py", line 9, in <module>
    import holoviews as hv
File "c:\programdata\anaconda3\lib\site-packages\holoviews\__init__.py", line 12, in <module>
    from .annotators import annotate                         # noqa (API import)
File "c:\programdata\anaconda3\lib\site-packages\holoviews\annotators.py", line 10, in <module>
    from panel.pane import PaneBase
File "c:\programdata\anaconda3\lib\site-packages\panel\__init__.py", line 5, in <module>
    from . import layout # noqa
File "c:\programdata\anaconda3\lib\site-packages\panel\layout.py", line 24, in <module>
    from .viewable import Layoutable, Reactive
File "c:\programdata\anaconda3\lib\site-packages\panel\viewable.py", line 26, in <module>
    from .config import config, panel_extension
File "c:\programdata\anaconda3\lib\site-packages\panel\config.py", line 20, in <module>
    from .io.notebook import load_notebook
File "c:\programdata\anaconda3\lib\site-packages\panel\io\__init__.py", line 10, in <module>
    from .server import get_server, serve, unlocked # noqa
File "c:\programdata\anaconda3\lib\site-packages\panel\io\server.py", line 20, in <module>
    from tornado.wsgi import WSGIContainer
File "c:\programdata\anaconda3\lib\site-packages\tornado\wsgi.py", line 86, in <module>
    _dummy_future = Future()
File "c:\programdata\anaconda3\lib\asyncio\events.py", line 694, in get_event_loop
    return get_event_loop_policy().get_event_loop()
File "c:\programdata\anaconda3\lib\asyncio\events.py", line 602, in get_event_loop
    % threading.current_thread().name) 

@kmcgrady kmcgrady added status:confirmed Bug has been confirmed by the Streamlit team priority:P3 labels Aug 4, 2021
@krambox
Copy link

krambox commented Nov 9, 2021

Same problem with ib_insync and streamlit 1.1

from ib_insync import IB

Error-stack:

File "/Users/kai/.pyenv/versions/3.9.1/lib/python3.9/site-packages/streamlit/script_runner.py", line 354, in _run_script
    exec(code, module.__dict__)
File "/Users/kai/sandbox/trade-cli/vl_screen.py", line 6, in <module>
    from ib_insync import IB
File "/Users/kai/.pyenv/versions/3.9.1/lib/python3.9/site-packages/ib_insync/__init__.py", line 7, in <module>
    from eventkit import Event
File "/Users/kai/.pyenv/versions/3.9.1/lib/python3.9/site-packages/eventkit/__init__.py", line 4, in <module>
    from .event import Event
File "/Users/kai/.pyenv/versions/3.9.1/lib/python3.9/site-packages/eventkit/event.py", line 7, in <module>
    from .util import NO_VALUE, loop
File "/Users/kai/.pyenv/versions/3.9.1/lib/python3.9/site-packages/eventkit/util.py", line 19, in <module>
    loop = asyncio.get_event_loop()
File "/Users/kai/.pyenv/versions/3.9.1/lib/python3.9/asyncio/events.py", line 642, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'

@AlistairHaimes
Copy link

confirmed streamlit isn't working with ib_insync (0.9.70).

from ib_insync import IB
import streamlit as st

produces error-stack:

2021-12-06 10:42:49.353 Traceback (most recent call last):
  File "/Users/me/opt/miniconda3/envs/trading/lib/python3.9/site-packages/streamlit/script_runner.py", line 350, in _run_script
    exec(code, module.__dict__)
  File "/Users/me/pysystemtrade/private/streamlit_app/app.py", line 1, in <module>
    from ib_insync import IB
  File "/Users/me/opt/miniconda3/envs/trading/lib/python3.9/site-packages/ib_insync/__init__.py", line 6, in <module>
    from eventkit import Event
  File "/Users/me/opt/miniconda3/envs/trading/lib/python3.9/site-packages/eventkit/__init__.py", line 4, in <module>
    from .event import Event
  File "/Users/me/opt/miniconda3/envs/trading/lib/python3.9/site-packages/eventkit/event.py", line 7, in <module>
    from .util import NO_VALUE, main_event_loop
  File "/Users/me/opt/miniconda3/envs/trading/lib/python3.9/site-packages/eventkit/util.py", line 18, in <module>
    main_event_loop = asyncio.get_event_loop()
  File "/Users/me/opt/miniconda3/envs/trading/lib/python3.9/asyncio/events.py", line 642, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'ScriptRunner.scriptThread'.

@AlistairHaimes
Copy link

Actually, ib_insync also depends on asyncio; is there a broader incompatibility between asyncio and streamlit? That would really limit the number of applications that can use streamlit, surely?

@rterbush
Copy link

I'm also running into the same problem attempting to use ib_insync in streamlit.

I ran across the following gist which seems to give some hints but I am unable to find the magic to get this working in streamlit based on this example.

https://gist.github.com/jwsmithers/0575fc78f9d54a93988ad29300dfe4b7

@d416
Copy link

d416 commented Jan 5, 2022

I can also reproduce this error...

Primary environment is:
Streamlit 1.3.1
ib_insync 0.9.70 https://github.com/erdewit/ib_insync
Python 3.9.0

...but error also occurs using:
Streamlit 0.76.0
ib_insync 0.9.70
Python 3.6.2

Steps to reproduce:

  1. Create test.py with 2 lines..
import streamlit as st
from ib_insync import *
  1. Execute "streamlit run test.py"

  2. Observe error generated:

2022-01-05 13:12:55.786 Traceback (most recent call last):
  File "d:\programs_big\python39\lib\site-packages\streamlit\script_runner.py", line 354, in _run_script
    exec(code, module.__dict__)
  File "D:\Documents\Python_Scripts\backtrader_2021_dashboards\IBdata\stest.py", line 2, in <module>
    from ib_insync import *
  File "d:\programs_big\python39\lib\site-packages\ib_insync\__init__.py", line 6, in <module>
    from eventkit import Event
  File "d:\programs_big\python39\lib\site-packages\eventkit\__init__.py", line 4, in <module>
    from .event import Event
  File "d:\programs_big\python39\lib\site-packages\eventkit\event.py", line 7, in <module>
    from .util import NO_VALUE, main_event_loop
  File "d:\programs_big\python39\lib\site-packages\eventkit\util.py", line 18, in <module>
    main_event_loop = asyncio.get_event_loop()
  File "d:\programs_big\python39\lib\asyncio\events.py", line 642, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'ScriptRunner.scriptThread'.

Ideas anyone? How can we get these 2 asyncio-dependent packages.. in sync?

Edit 1: datotalk's workaround works! #744 (comment)
for ib_insync you'll need to also wrap the connection into a function and st.cache it, then call it at the end after everything is rendered in streamlit eg:

@st.cache
def connect():
    ib = IB() 
    ib.connect('127.0.0.1', 7497, clientId=88)

...

connect()

Edit 2: It didn't really work - the errors went away but the app doesn't work at all (seems to hang). The ib_insync docs have code recipes to make ib_insync work with threaded and non-threaded apps https://ib-insync.readthedocs.io/recipes.html I like the library, but this seems to be a lot of working around just to have it play nice. For now I've settled for using https://pypi.org/project/ib/ which works well - not as simple as ib_insync but some helper functions can bring it on par.

Hopefully someone on the streamlit team will pick this issue up and come up with a remedy, but I'm sure this will require some collab with the keeper of ib_insync @erdewit https://github.com/erdewit

@kmcgrady kmcgrady added the type:release-defect This bug is a release defect label Jan 5, 2022
@im-keiran
Copy link

im-keiran commented Apr 28, 2022

@d416 what version are you using? I tried your minimal example and got the same errors as before:

test.py

import streamlit
from ib_insync import *

@st.cache
def connect():
    ib = IB() 
    ib.connect('127.0.0.1', 7497, clientId=88)

st.write('test - before connect called')

connect()

st.write('test - after connect called')

streamlit run test.py

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501
  Network URL: http://192.168.1.98:8501

2022-04-28 08:23:26.730 Traceback (most recent call last):
  File "/Users/keiran/miniforge3/envs/p38/lib/python3.8/site-packages/streamlit/script_runner.py", line 430, in _run_script
    exec(code, module.__dict__)
  File "/Users/keiran/planarity/planarity_trading/apps/test.py", line 2, in <module>
    from ib_insync import *
  File "/Users/keiran/miniforge3/envs/p38/lib/python3.8/site-packages/ib_insync/__init__.py", line 6, in <module>
    from eventkit import Event
  File "/Users/keiran/miniforge3/envs/p38/lib/python3.8/site-packages/eventkit/__init__.py", line 4, in <module>
    from .event import Event
  File "/Users/keiran/miniforge3/envs/p38/lib/python3.8/site-packages/eventkit/event.py", line 7, in <module>
    from .util import NO_VALUE, main_event_loop
  File "/Users/keiran/miniforge3/envs/p38/lib/python3.8/site-packages/eventkit/util.py", line 18, in <module>
    main_event_loop = asyncio.get_event_loop()
  File "/Users/keiran/miniforge3/envs/p38/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 'ScriptRunner.scriptThread'.

@alexespencer
Copy link

I'm getting this same error when trying to use HTMLSession() from requests_html. The application works fine as a simple python program, but adding in import streamlit as st is enough to break it.

@d416
Copy link

d416 commented May 18, 2022

@im-keiran
I ended up with further asyncio issues after posting the above, and so abandoned trying to use ib_insync with Streamlit. The standard IB python library works well enough https://pypi.org/project/ib/ and is what I decided to opt for instead.

ib_insync is certainly easier to use, and I'm sure someone could get Streamlit to work with it with a great deal of debugging, but this was beyond the scope of my own personal project.

@joseberlines
Copy link

Same problem here.
We use in ceretain part of our code:
import nest_asyncio
nest_asyncio.apply()

And that creates the problems mentioned above in the following lines of nest_asyncio

File "/opt/conda/lib/python3.9/site-packages/..... .... name.py", line 12, in
nest_asyncio.apply()
File "/opt/conda/lib/python3.9/site-packages/nest_asyncio.py", line 16, in apply
loop = loop or asyncio.get_event_loop()
File "/opt/conda/lib/python3.9/site-packages/nest_asyncio.py", line 45, in _get_event_loop
loop = events.get_event_loop_policy().get_event_loop()
File "/opt/conda/lib/python3.9/asyncio/events.py", line 642, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'

@joseberlines
Copy link

joseberlines commented Jun 21, 2022

At least is it know wh there is this conflict streamlit vs asyncio?

@tomgallagher
Copy link

tomgallagher commented Jul 13, 2022

I'm getting the same error with coiled

image

@eokwaro
Copy link

eokwaro commented Jul 19, 2022

same error here

image

@0xmanny
Copy link

0xmanny commented Jan 27, 2023

I ran into the same issue while using asyncio. Solved by implementing a helper module from this stack overflow reply.

@Anton-Filimoncev
Copy link

Anton-Filimoncev commented Mar 25, 2023

Add this code before import ib_insync:

import asyncio

def get_or_create_eventloop():
    try:
        return asyncio.get_event_loop()
    except RuntimeError as ex:
        if "There is no current event loop in thread" in str(ex):
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            return asyncio.get_event_loop()

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

@Franky1
Copy link

Franky1 commented Mar 31, 2023

@Anton-Filimoncev
I assume the last two lines should be (otherwise the function would have no effect?):

import asyncio

def get_or_create_eventloop():
    try:
        return asyncio.get_event_loop()
    except RuntimeError as ex:
        if "There is no current event loop in thread" in str(ex):
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            return asyncio.get_event_loop()

loop = get_or_create_eventloop()
asyncio.set_event_loop(loop)

@Franky1
Copy link

Franky1 commented Mar 31, 2023

This is probabaly also a neat solution from the community forum for this kind of issues:

https://discuss.streamlit.io/t/deploy-sketch-on-streamlit/40434/2

Here the code snippet from the forum thread as reference, it deals with the sketch library using asyncio, but could be applied to other libraries as well:

import asyncio
from contextlib import contextmanager
import streamlit as st

# Create a context manager to run an event loop
@contextmanager
def setup_event_loop():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        yield loop
    finally:
        loop.close()
        asyncio.set_event_loop(None)

# Use the context manager to create an event loop
with setup_event_loop() as loop:
    import sketch

# Now you can use the 'sketch' library in your Streamlit app
st.write("The 'sketch' library has been successfully imported.")

@d416
Copy link

d416 commented Apr 5, 2023

For those who may have missed it, this solution #744 (comment) was posted by the maintainer of ib_insync

It sort of works, but not well enough for me that I've moved on from getting streamlit (1.3.1) to work with ib_insync (0.9.81).
Full code is below, but here are the results of my testing:

  1. this version of ib_insync wasn't working with TWS version 10 at all (schema errors). With TWS version 981, ib_insync was able to connect
  2. Since streamlit re-runs script from top to bottom, ib_insync tries to establish a connection on each run, but this would result in error
    Peer closed connection. clientId 1 already in use?
    so it would appear that a thread is already connected with the same client id.
import ib_insync
import streamlit as st
def get_or_create_eventloop():
    try:
        return asyncio.get_event_loop()
    except RuntimeError as ex:
        if "There is no current event loop in thread" in str(ex):
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            return asyncio.get_event_loop()

loop = get_or_create_eventloop()
asyncio.set_event_loop(loop)
from ib_insync import *

ib = IB()
ib.connect('127.0.0.1', 7497,clientId=1)
contract = Forex('EURUSD')
bars = ib.reqHistoricalData(
    contract, endDateTime='', durationStr='2 D',
    barSizeSetting='1 hour', whatToShow='MIDPOINT', useRTH=True)
df = util.df(bars)
st.dataframe(df)

@SamuelCheng123
Copy link

For those who may have missed it, this solution #744 (comment) was posted by the maintainer of ib_insync

It sort of works, but not well enough for me that I've moved on from getting streamlit (1.3.1) to work with ib_insync (0.9.81). Full code is below, but here are the results of my testing:

  1. this version of ib_insync wasn't working with TWS version 10 at all (schema errors). With TWS version 981, ib_insync was able to connect
  2. Since streamlit re-runs script from top to bottom, ib_insync tries to establish a connection on each run, but this would result in error
    Peer closed connection. clientId 1 already in use?
    so it would appear that a thread is already connected with the same client id.
import ib_insync
import streamlit as st
def get_or_create_eventloop():
    try:
        return asyncio.get_event_loop()
    except RuntimeError as ex:
        if "There is no current event loop in thread" in str(ex):
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            return asyncio.get_event_loop()

loop = get_or_create_eventloop()
asyncio.set_event_loop(loop)
from ib_insync import *

ib = IB()
ib.connect('127.0.0.1', 7497,clientId=1)
contract = Forex('EURUSD')
bars = ib.reqHistoricalData(
    contract, endDateTime='', durationStr='2 D',
    barSizeSetting='1 hour', whatToShow='MIDPOINT', useRTH=True)
df = util.df(bars)
st.dataframe(df)

To add an "initialize" button before the ib.connect('127.0.0.1', 7497, clientId=1) line to prevent repeated connections that may lead to the "clientId already in use" issue, you can do the following:

import streamlit as st
import asyncio
import time

def get_or_create_eventloop():
    try:
        return asyncio.get_event_loop()
    except RuntimeError as ex:
        if "There is no current event loop in thread" in str(ex):
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            return asyncio.get_event_loop()

loop = get_or_create_eventloop()
asyncio.set_event_loop(loop)

# Initialization
if st.button('Fetch Kline'):
    from ib_insync import *
    st.session_state['ib'] = IB()

    # Check if a connection is already established
    print('isConnected', st.session_state['ib'].isConnected())
    if not st.session_state['ib'].isConnected():
        st.session_state['ib'].connect(
            '127.0.0.1', 
            7497, 
            clientId=1,
            timeout=0)

    contract = Forex('EURUSD')
    bars = st.session_state['ib'].reqHistoricalData(
        contract, endDateTime='', durationStr='1 D',
        barSizeSetting='1 hour', whatToShow='MIDPOINT', useRTH=True)
    df = util.df(bars)
    st.dataframe(df)


@dreamless2871
Copy link

dreamless2871 commented Mar 26, 2024

For those who may have missed it, this solution #744 (comment) was posted by the maintainer of ib_insync

It sort of works, but not well enough for me that I've moved on from getting streamlit (1.3.1) to work with ib_insync (0.9.81). Full code is below, but here are the results of my testing:

  1. this version of ib_insync wasn't working with TWS version 10 at all (schema errors). With TWS version 981, ib_insync was able to connect
  2. Since streamlit re-runs script from top to bottom, ib_insync tries to establish a connection on each run, but this would result in error
    Peer closed connection. clientId 1 already in use?
    so it would appear that a thread is already connected with the same client id.
import ib_insync
import streamlit as st
def get_or_create_eventloop():
    try:
        return asyncio.get_event_loop()
    except RuntimeError as ex:
        if "There is no current event loop in thread" in str(ex):
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            return asyncio.get_event_loop()

loop = get_or_create_eventloop()
asyncio.set_event_loop(loop)
from ib_insync import *

ib = IB()
ib.connect('127.0.0.1', 7497,clientId=1)
contract = Forex('EURUSD')
bars = ib.reqHistoricalData(
    contract, endDateTime='', durationStr='2 D',
    barSizeSetting='1 hour', whatToShow='MIDPOINT', useRTH=True)
df = util.df(bars)
st.dataframe(df)

This solution is not working anymore, is there any update on this?
Another approach we can try?

@thorin-schiffer
Copy link

thorin-schiffer commented Apr 12, 2024

Having same problems, streamlit should support async data sources out of the box in my not very humble opinion.
If the developers read this, I would like to point the following:

  • asyncio interface is also important for snowflake, so usability or applicability of that should be a no brainer
  • all modern databases have async interfaces and they will only expand with time
  • streamlit is based on asyncio itself, so at least properly exposing the original tornado's event loop would also not require to much work for you

Known problems for now:

  • when using asyncio.run() -> RuntimeError: Event loop is closed if the requests follow concurrently in the tornado eventlets
  • every eventlet with own event loop -> tasks are getting to the wrong event loop
  • cached event loop for all eventlets through session state -> best for now, gets event loop is closed when multile sessions are acessing the app simultaneously

So with that said, very ugly workaround that I found working best for now:

@st.cache_resource(show_spinner=False)
def get_event_loop():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    return loop


if not st.session_state.get("event_loop"):
    st.session_state["event_loop"] = get_event_loop()

then pass that event loop wherever your data is coming from, here is motor example:

async def setup_db(force=False):
    if force or not st.session_state.get("db_client"):
        with st.spinner("Loading..."):
            client = AsyncIOMotorClient(
                os.environ["MONGODB_URI"],
                tls=MONGODB_TLS,
                io_loop=st.session_state["event_loop"],
            )
            st.session_state["db_client"] = await init_database(client=client)
            return client

init_database is a call to beanie's init database

I understand that making the entire framework async would require too much work and would prb confuse the ML/AI crowd too much, but streamlit is such a gorgeous framework, the only place it fails in misery is the async

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:server priority:P3 status:confirmed Bug has been confirmed by the Streamlit team type:bug Something isn't working
Projects
None yet
Development

No branches or pull requests