posit-dev / positron

Positron, a next-generation data science IDE
https://positron.posit.co
Other
2.85k stars 91 forks source link

`asyncio` error when trying to run `shiny.run_app()` #4464

Open edgararuiz opened 3 months ago

edgararuiz commented 3 months ago

System details:

Apple M3 - Sonoma 14.5

Positron and OS details:

Positron Version: 2024.08.0 (Universal) build 48 Code - OSS Version: 1.91.0 Commit: ed616b36655fb24d116108bdd833f1321704315b Date: 2024-08-19T04:26:51.868Z Electron: 29.4.0 Chromium: 122.0.6261.156 Node.js: 20.9.0 V8: 12.2.281.27-electron.0 OS: Darwin arm64 23.5.0

Interpreter details:

Python 3.12

Describe the issue:

When trying to run shiny.run_app() I get the following error:

RuntimeError: asyncio.run() cannot be called from a running event loop

I have copied the full error below.

Steps to reproduce the issue:

  1. Create a simple app.py file

  2. In the Python console run:

    import shiny
    shiny.run_app()

Expected or desired behavior:

The Shiny app to run in the Viewer pane

Full error

>>> shiny.run_app()
RuntimeError: asyncio.run() cannot be called from a running event loop
Cell In[4], line 1
----> 1 shiny.run_app()
Hide Traceback
File [~/.pyenv/versions/3.12.3/lib/python3.12/site-packages/shiny/_main.py:365](vscode-file://vscode-app/Applications/Positron.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/workbench.html#), in run_app(app, host, port, autoreload_port, reload, reload_dirs, reload_includes, reload_excludes, ws_max_size, log_level, app_dir, factory, launch_browser, dev_mode, **kwargs)
    361     setup_launch_browser(log_config)
    363 maybe_setup_rsw_proxying(log_config)
--> 365 uvicorn.run(  # pyright: ignore[reportUnknownMemberType]
    366     app,
    367     host=host,
    368     port=port,
    369     ws_max_size=ws_max_size,
    370     log_level=log_level,
    371     log_config=log_config,
    372     app_dir=app_dir,
    373     factory=factory,
    374     lifespan="on",
    375     # Don't allow shiny to use uvloop!
    376     # [https://github.com/posit-dev/py-shiny/issues/1373](vscode-file://vscode-app/Applications/Positron.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/workbench.html#)
    377     loop="asyncio",
    378     **reload_args,  # pyright: ignore[reportArgumentType]
    379     **kwargs,
    380 )

File [~/.pyenv/versions/3.12.3/lib/python3.12/site-packages/uvicorn/main.py:577](vscode-file://vscode-app/Applications/Positron.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/workbench.html#), in run(app, host, port, uds, fd, loop, http, ws, ws_max_size, ws_max_queue, ws_ping_interval, ws_ping_timeout, ws_per_message_deflate, lifespan, interface, reload, reload_dirs, reload_includes, reload_excludes, reload_delay, workers, env_file, log_config, log_level, access_log, proxy_headers, server_header, date_header, forwarded_allow_ips, root_path, limit_concurrency, backlog, limit_max_requests, timeout_keep_alive, timeout_graceful_shutdown, ssl_keyfile, ssl_certfile, ssl_keyfile_password, ssl_version, ssl_cert_reqs, ssl_ca_certs, ssl_ciphers, headers, use_colors, app_dir, factory, h11_max_incomplete_event_size)
    575         Multiprocess(config, target=server.run, sockets=[sock]).run()
    576     else:
--> 577         server.run()
    578 except KeyboardInterrupt:
    579     pass  # pragma: full coverage

File [~/.pyenv/versions/3.12.3/lib/python3.12/site-packages/uvicorn/server.py:65](vscode-file://vscode-app/Applications/Positron.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/workbench.html#), in Server.run(self, sockets)
     63 def run(self, sockets: list[socket.socket] | None = None) -> None:
     64     self.config.setup_event_loop()
---> 65     return asyncio.run(self.serve(sockets=sockets))

File [~/.pyenv/versions/3.12.3/lib/python3.12/asyncio/runners.py:190](vscode-file://vscode-app/Applications/Positron.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/workbench.html#), in run(main, debug, loop_factory)
    161 """Execute the coroutine and return the result.
    162 
    163 This function runs the passed coroutine, taking care of
   (...)
    186     asyncio.run(main())
    187 """
    188 if events._get_running_loop() is not None:
    189     # fail fast with short traceback
--> 190     raise RuntimeError(
    191         "asyncio.run() cannot be called from a running event loop")
    193 with Runner(debug=debug, loop_factory=loop_factory) as runner:
    194     return runner.run(main)
isabelizimm commented 3 months ago

Ah, thanks for bringing this up! 🙌 I don't believe this is a Positron-specific bug, it is happening since asyncio by default doesn't allow for nested events, eg, starting Shiny app inside a Console process. Generally you would run shiny run ... in the Terminal instead or use the Shiny extension to run app. I don't think you would be able to run this in a Jupyter Notebook either.

Screenshot 2024-08-26 at 1 32 18 PM

On the Positron side, we might want to think how to handle apps running from the console, if we could give a nudge to use some sort of App Builder experience. There is a way to nest asyncio with the aptly named nest-asyncio, but the project has been archived on GitHub (as of 2024)/I'm not sure what other side effects would occur, so I would be hesitant to go down that path.