posit-dev / py-shiny

Shiny for Python
https://shiny.posit.co/py/
MIT License
1.31k stars 78 forks source link

Python 3.12: Shiny refuses to shut down until all network connections are closed #771

Open daattali opened 1 year ago

daattali commented 1 year ago

I just installed py-shiny on a fresh Windows installation. I'm getting an issue where any time the shiny app is killed, the terminal freezes. If I run a shiny app with shiny run, then when I press ctrl+C I see messages saying that the shiny app is attempting to shut down, but the terminal gets stuck and never returns to the prompt.

If I run in --reload mode, then the moment I change a file and save it, the terminal gets to the same state.

This happens in the command line and also in VSCode.

(venv) C:\Users\Dean\Documents\R\python\shinytest>shiny run
←[32mINFO←[0m:     Started server process [←[36m16176←[0m]
←[32mINFO←[0m:     Waiting for application startup.
←[32mINFO←[0m:     Application startup complete.
←[32mINFO←[0m:     Uvicorn running on ←[1mhttp://127.0.0.1:8000←[0m (Press CTRL+C to quit)
←[32mINFO←[0m:     127.0.0.1:49687 - "←[1mGET / HTTP/1.1←[0m" ←[32m200 OK←[0m
←[32mINFO←[0m:     ('127.0.0.1', 49689) - "WebSocket /websocket/" [accepted]
←[32mINFO←[0m:     connection open
←[32mINFO←[0m:     127.0.0.1:49688 - "←[1mGET / HTTP/1.1←[0m" ←[32m200 OK←[0m
←[32mINFO←[0m:     connection closed
←[32mINFO←[0m:     ('127.0.0.1', 49695) - "WebSocket /websocket/" [accepted]
←[32mINFO←[0m:     connection open
←[32mINFO←[0m:     Shutting down
daattali commented 1 year ago

(As you can see from the logs, it would also be nice to fix https://github.com/posit-dev/py-shiny/issues/162 because this is a terrible experience on Windows, it gives me the feeling that Windows users are not meant to use py-shiny)

j-lim-sigma commented 1 year ago

Hi, I experienced the same thing using python 3.12 on MacOS. It works fine with 3.11 though, both with --reload and without.

jcheng5 commented 1 year ago

@j-lim-sigma Thank you, I'm installing 3.12 now to try to repro.

@daattali Any chance you're on Python 3.12 as well?

daattali commented 1 year ago

Yes I am indeed on 3.12. I can try to install 3.11 later if I find very simple instructions for it :)

jcheng5 commented 1 year ago

I can repro on macOS 3.12 if I still have a connected browser. Once I close the browser, the command line returns. I get the same thing if I run the command uvicorn app:app instead of shiny run.

Edit: on Windows, it stays hung even after the browser closes.

Edit 2: on Windows, the command line returns if I close the whole browser. It's like an open socket is keeping Python alive.

jcheng5 commented 1 year ago

Here is a discussion on Uvicorn and a suggested fix, no response yet: https://github.com/encode/uvicorn/discussions/2122

jcheng5 commented 1 year ago

FWIW, I tried daphne (daphne app:app) and hypercorn (hypercorn app:app) under Python 3.12 and they both served up my app fine and exited cleanly. So this appears to be specific to uvicorn.

daattali commented 1 year ago

Here is a discussion on Uvicorn

Maybe it should be upgraded to an "Issue" in oder for the uvicorn team to notice?

daattali commented 1 year ago

I can confirm that downgrading to python 3.11 fixes this

jcheng5 commented 1 year ago

This PR contains the fix. Thanks for your patience! https://github.com/encode/uvicorn/pull/2145

ME-researchgroup commented 11 months ago

I am experiencing this issue using python 3.10.4 on windows. I have to kill the terminal to stop shiny from running after hitting ctrl+c

corey-dawson commented 8 months ago

Similar issue on python 3.10.13. Sometimes the process shuts down normally, sometimes the terminal freezes. for me. Have to close the terminal and reopen a new one. this is where mine gets stuck, but only maybe half the time:

image

jcheng5 commented 8 months ago

@corey-dawson Does it happen even with the simplest Shiny app?

from shiny.express import input, render, ui

ui.input_slider("n", "N", 0, 100, 20)

@render.text
def txt():
    return f"n*2 is {input.n() * 2}"
gza-austin-elliott commented 6 months ago

Similar issue and symptoms as @corey-dawson above.

Windows 10.0.19045 Python 3.12

Fires up perfectly every time, but CTRL+C terminate does not consistently work. Occasionally requires full kill on the terminal to shut down. Something additional I haven't seen above which seems related is the terminal's outputs seem to freeze up when it fires up in this bugged mode. That is to say, I can tell when I'll have to kill the terminal rather than CTRL+C to shut down because the terminal will not display simple debug statements onstart.

Ex

# server logic

def server(input: Inputs, output: Outputs, session: Session):

    session_data = {}

    tables_trigger = reactive.Value(None) #init triggers

    data_trigger = reactive.Value(None) #init triggers

    ...[MORE CODE]...

    print("debug: export_button function defined")  # debug

In a non-bugged startup this print statement displays in my terminal and the processes properly exit on CTRL+C. In a bugged startup this print statement does not display and the processes do not exit on CTRL+C.

jcheng5 commented 6 months ago

Does it happen with the simple app I posted in my previous comment?

gza-austin-elliott commented 6 months ago

Does it happen with the simple app I posted in my previous comment?

It does not seem to. Did a fresh venv + your app and it initializes/terminates properly every time.

The app I was initially encountering this issue with was built on shiny.core. I quickly changed your example code over to shiny.core and tested and that also checked out, terminated fine with CRTL+C every time.

jcheng5 commented 6 months ago

I don’t suppose you can share your source code with us? Either publicly or privately? If not, trying to find a reproducible example you can share would be a huge help.

jcheng5 commented 6 months ago

Can you also try pip install -U uvicorn?

gza-austin-elliott commented 6 months ago

I jumped back to my original source code to prep it to share and in retesting I could not recreate the issue. Very strange. I did not restart VSC, just changed from project to project. I'll keep an eye out and see if I can recreate it and pay closer attention to potential environmental variables.

jcheng5 commented 6 months ago

@gza-austin-elliott I don't know if your Python version varies from project to project, but if so, I've noticed the Ctrl+C handling changed in Python 3.11+ in a way that would probably be highly relevant to this issue. https://docs.python.org/3/library/asyncio-runner.html#handling-keyboard-interruption

Although the way they implementated that change, it is supposed to interrupt immediately if you hit Ctrl+C twice, which sounds like wasn't working for you, so maybe not...

jcheng5 commented 6 months ago

If you still have a project where you can reproduce it, and are up for it, I'd be happy to jump on a zoom session with you and pair debug it. This is a scary problem for us to leave undiagnosed.

ME-researchgroup commented 6 months ago

I still experience this issue very sporadically. It seems to occur a lot less frequently compared to earlier versions of py-shiny. CTRL+C multiple times does not work either when it hangs (python 3.12.3).

It is very difficult to reproduce because it seemingly occurs at random. Perhaps you can find a way to test this behavior via a script to run it a lot of times..

jcheng5 commented 6 months ago

@ME-researchgroup Are you still on Python 3.10?

(On Python 3.11+) When asyncio is active, a single Ctrl+C makes a gentle suggestion to the current task that maybe it should interrupt at an opportune time. A second Ctrl+C is supposed to raise a KeyboardInterrupt on the main thread. Perhaps we should install our own Ctrl+C handler where if you do it four times in quick succession, we terminate the process. Very unsatisfying but I hate the idea of people having to go to Task Manager.

ME-researchgroup commented 6 months ago

No I am currently on python 3.12.

Having a custom KeyboardInterrupt handler is not a bad idea to clean up the long asyncio stack trace it currenly throws. Could make it feel a bit cleaner. Though I doubt that pressing Ctrl+C four times in quick succession to work around the process getting stuck is the right approach. What happens to any custom code that is supposed to run atexit if the process gets killed, for instance?

You dont actually have to go to Task Manager to kill a terminal, by the way.

ME-researchgroup commented 6 months ago

Looks like the issue could be in watchfiles @jcheng5

recording_shiny_issue.zip

jcheng5 commented 5 months ago

Oh, wait, wait. So when you hit Ctrl+C it does show KeyboardInterrupt, but the process doesn't exit?

ME-researchgroup commented 5 months ago

Yes. But I definitely remember getting stuck on the "Finished server process" message without the KeyboardInterrupt as someone mentioned above. That may not be the case anymore in a newer version of shiny or python, I havent paid much attention I must admit. I'll keep an eye out to see if it also still happens for me without the KeyboardInterrupt trace.

ME-researchgroup commented 5 months ago

My findings over the last week:

Hope this helps!

jcheng5 commented 5 months ago

I’m at a loss. Are you able to reproduce this reliably enough that a live debugging session might be productive?

ME-researchgroup commented 5 months ago

Afraid not.. are there any debugging steps you'd like me to go through when I encounter the issue?

I could also send you a message when it happens to see if you (or someone else from the team) are available for a meeting.