Repository: streamlit/streamlit

Issue:
Streamlit 0.49.0 on Windows + Python 3.8 fails to execute (Tornado error)
# Summary
Streamlit fails to execute on Windows under Python 3.8 due to a bug in Tornado.  The version of Tornado pinned in streamlit 0.49 was 5.x, while the latest version of Tornado at the time of this bug was 6.0.3.  [A similar bug was found in IPython notebook.](https://stackoverflow.com/questions/58422817/jupyter-notebook-with-python-3-8-notimplementederror)

# Steps to reproduce
What are the steps we should take to reproduce the bug:
1. Setup Python 3.8 virtualenv on Windows
2. Install streamlit 0.49.0
3. streamlit hello

## Expected behavior:
Streamlit hello should run.

## Actual behavior:
Streamlit fails to execute, spitting out the following (tail end of traceback -- [see full traceback here](https://discuss.streamlit.io/t/streamlit-issue-when-calling-streamlit/724)):

```
self._handlers[sock.fileno()] = add_accept_handler(
File “c:\users\admin\appdata\local\programs\python\python38-32\lib\site-packages\tornado\netutil.py”, line 268, in add_accept_handler
io_loop.add_handler(sock, accept_handler, IOLoop.READ)
File “c:\users\admin\appdata\local\programs\python\python38-32\lib\site-packages\tornado\platform\asyncio.py”, line 79, in add_handler
self.asyncio_loop.add_reader(
File “c:\users\admin\appdata\local\programs\python\python38-32\lib\asyncio\events.py”, line 501, in add_reader
raise NotImplementedError
NotImplementedError
```

## Is this a regression?

No; Python 3.8 hasn't been officially supported in Streamlit to date.  (But it _should_ work.)

# Debug info
- Streamlit version: 0.49.0
- Python version: 3.8
- Using Conda? PipEnv? PyEnv? Pex?  any
- OS version: Windows (probably any)
- Browser version:  n/a

# Additional information

Using Python 3.7.5 is the recommended solution for now.  See https://discuss.streamlit.io/t/streamlit-issue-when-calling-streamlit/724/4



Discussion:

Resolving patch:
diff --git a/lib/streamlit/bootstrap.py b/lib/streamlit/bootstrap.py
--- a/lib/streamlit/bootstrap.py
+++ b/lib/streamlit/bootstrap.py
@@ -23,6 +23,7 @@
 from streamlit import config
 from streamlit import net_util
 from streamlit import url_util
+from streamlit import env_util
 from streamlit import util
 from streamlit.Report import Report
 from streamlit.logger import get_logger
@@ -30,7 +31,6 @@
 
 LOGGER = get_logger(__name__)
 
-
 # Wait for 1 second before opening a browser. This gives old tabs a chance to
 # reconnect.
 # This must be >= 2 * WebSocketConnection.ts#RECONNECT_WAIT_TIME_MS.
@@ -89,6 +89,43 @@ def _fix_matplotlib_crash():
             pass
 
 
+def _fix_tornado_crash():
+    """Set default asyncio policy to be compatible with Tornado 6.
+
+        Tornado 6 (at least) is not compatible with the default
+        asyncio implementation on Windows. So here we
+        pick the older SelectorEventLoopPolicy when the OS is Windows
+        if the known-incompatible default policy is in use.
+
+        This has to happen as early as possible to make it a low priority and
+        overrideable
+
+        See: https://github.com/tornadoweb/tornado/issues/2608
+
+        FIXME: if/when tornado supports the defaults in asyncio,
+        remove and bump tornado requirement for py38
+    """
+    if env_util.IS_WINDOWS and sys.version_info >= (3, 8):
+        import asyncio
+        try:
+            from asyncio import (
+                WindowsProactorEventLoopPolicy,
+                WindowsSelectorEventLoopPolicy,
+            )
+        except ImportError:
+            pass
+            # Not affected
+        else:
+            if (
+                type(asyncio.get_event_loop_policy()) is
+                    WindowsProactorEventLoopPolicy
+            ):
+                # WindowsProactorEventLoopPolicy is not compatible with
+                # Tornado 6 fallback to the pre-3.8 default of Selector
+                asyncio.set_event_loop_policy(
+                    WindowsSelectorEventLoopPolicy())
+
+
 def _fix_sys_argv(script_path, args):
     """sys.argv needs to exclude streamlit arguments and parameters
     and be set to what a user's script may expect.
@@ -170,6 +207,7 @@ def run(script_path, command_line, args):
     """
     _fix_sys_path(script_path)
     _fix_matplotlib_crash()
+    _fix_tornado_crash()
     _fix_sys_argv(script_path, args)
 
     # Install a signal handler that will shut down the ioloop


Test patch: