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

Ctrl+C in terminal broken on windows #1972

Open
davidefortunatotecnos opened this issue May 8, 2023 · 42 comments
Open

Ctrl+C in terminal broken on windows #1972

davidefortunatotecnos opened this issue May 8, 2023 · 42 comments

Comments

@davidefortunatotecnos
Copy link

davidefortunatotecnos commented May 8, 2023

With last release of Uvicorn 0.22.0 doing CTRL+C in a windows terminal doesn't work anymore.
Everything is frozen and it is impossible to stop it. In addition the command --reload is broken too, because it doesn't restart anymore, probably because of the same issue.

Tried on Windows 11 and Powershell 7.3.4.

Downgrading to version 0.21.1 makes it works again.

Important

  • We're using Polar.sh so you can upvote and help fund this issue.
  • We receive the funding once the issue is completed & confirmed by you.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar
@lzcmian
Copy link

lzcmian commented May 9, 2023

same question

@UF-code
Copy link

UF-code commented May 10, 2023

when you launch without testing properly that will happen, also same here, in addition to that issue with "--reload" parameter over time it stucks and doesn't update api endpoints

@AshminJayson
Copy link

Also the issue of the everything being frozen is generally triggered right after watchfiles tries restarting the server/detected a change, also killing the process manually using the port number doesn't fully terminate it and does not allow for the port to be reused without a full system reboot.

@JinglingB
Copy link

Crude monkey-patching hack to use SetConsoleCtrlHandler, which actually has its handlers called unlike signal.signal's. Don't add this to anything serious:

import sys

if __name__ == "__main__":
    if sys.platform == "win32":
        # Based on Eryk Sun's code: https://stackoverflow.com/a/43095532
        import ctypes

        from signal import SIGINT, CTRL_C_EVENT

        _console_ctrl_handlers = {} # must be global / kept alive to avoid GC

        kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)

        PHANDLER_ROUTINE = ctypes.WINFUNCTYPE(
            ctypes.wintypes.BOOL,
            ctypes.wintypes.DWORD)

        def _errcheck_bool(result, func, args):
            if not result:
                raise ctypes.WinError(ctypes.get_last_error())
            return args

        kernel32.SetConsoleCtrlHandler.errcheck = _errcheck_bool
        kernel32.SetConsoleCtrlHandler.argtypes = (
            PHANDLER_ROUTINE,
            ctypes.wintypes.BOOL)

        from uvicorn.supervisors.multiprocess import Multiprocess
        uvicorn_multiprocess_startup_orig = Multiprocess.startup
        def uvicorn_multiprocess_startup(self, *args, **kwargs):
            ret = uvicorn_multiprocess_startup_orig(self, *args, **kwargs)

            def win_ctrl_handler(dwCtrlType):
                if (dwCtrlType == CTRL_C_EVENT and
                    not self.should_exit.is_set()):
                    kernel32.SetConsoleCtrlHandler(_console_ctrl_handlers[win_ctrl_handler], False)
                    self.signal_handler(SIGINT, None)
                    del _console_ctrl_handlers[win_ctrl_handler]
                    return True
                return False

            if win_ctrl_handler not in _console_ctrl_handlers:
                h = PHANDLER_ROUTINE(win_ctrl_handler)
                kernel32.SetConsoleCtrlHandler(h, True)
                _console_ctrl_handlers[win_ctrl_handler] = h

            return ret
        Multiprocess.startup = uvicorn_multiprocess_startup

    uvicorn.run("app", workers=4)

@AshminJayson
Copy link

Also the issue of the everything being frozen is generally triggered right after watchfiles tries restarting the server/detected a change, also killing the process manually using the port number doesn't fully terminate it and does not allow for the port to be reused without a full system reboot.

The current workaround which works for this is to run uvicorn from the main function of the app rather than having to run it directly as a command line command.

This ensures that the port is freed and also terminates the program correctly.

if __name__ == '__main__':
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)

@olafrv
Copy link

olafrv commented Oct 28, 2023

Does not works for me @AshminJayson .
I switched to hypercorn in the meantime.

if __name__ == '__main__':
    # https://github.com/encode/uvicorn/issues/1972
    asyncio.run(serve(app, Config()))

Sample:

PS C:\Users\olafr\test> python .\server.py
[2023-10-28 02:19:19 +0200] [13228] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
2023-10-28 02:19:19,472 loglevel=INFO   logger=hypercorn.error info() L106  Running on http://127.0.0.1:8000 (CTRL + C to quit)
PS C:\Users\olafr\test>

@humrochagf
Copy link
Contributor

@olafrv can you check if the changes that are in this MR works on your side?

#2059

@olafrv
Copy link

olafrv commented Oct 28, 2023

@humrochagf , tested, nothing changes, but thanks to your PR I nailed down the problem.

I'm on latests Windows 11 23H2 builtin Poweshell, VSCode 1.83.1, Python 3.12.0 (tags/v3.12.0:0fb18b0).

The signal is indeed received (CTRL+C), since the shutdown starts, both in 0.23.2 and your #2059 .

However, the shutdown process hangs forever, giving the impression of not working, you only see:
INFO: Shutting down

It does not matter if you pass or not the --reload and/or --reload-delay, so you end up doing:
taskkill /f /im "uvicorn.exe"

I found that the server.wait_closed() never returns (or timeouts after minutes):

I guess the socket.close() is never closed until timeout?:

sock.close()

My dirty workaround:

      for server in self.servers:
            # await server.wait_closed()
            pass

I also saw Python has an open asyncio.Server.wait_closed() appears to hang indefinitely in 3.12.0a7 but I didn't check how it is related to the server.py logic in depth.

So you can merge #2059 but won't solve the(my) problem.

It is reproducible even with this helloworld.py but it gets more evident on more complex apps:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"Hello": "World"}

@humrochagf
Copy link
Contributor

Alright, thanks for taking a look at it, I'll run some tests in python 3.12 to see if I can reproduce it 🙂

@jcheng5
Copy link
Contributor

jcheng5 commented Oct 30, 2023

There's a discussion on this issue here, along with a proposed fix: #2122

@tonnydourado
Copy link

tonnydourado commented Nov 30, 2023

By the way, I'm having the same problem for a while, on Windows 10, both Python 3.9 and 3.11. Don't remember the version I was using with 3.9, but with 3.11 is 0.24.0.post1.

Downgrading to 0.21.1 seems to improve the reload behavior, but CTRL+C still doesn't work, with or without reload.

By the way, this is somewhat intermittent. It's broken most of the time, but sometimes it just works normally, just often enough to make me doubt my sanity, competence, and the nature of reality itself.

@frankhuurman
Copy link

frankhuurman commented Dec 11, 2023

Not entirely sure if this is the same issue but on Windows 10 I'm also having issues since a python 3.12 upgrade with the NiceGUI framework(that uses uvicorn under the hood for filewatching/serving).

Any time I edit & save a file while uvicorn is running, it seems to shut down some process and not properly pick up on file changes plus it's impossible to Ctrl+C out of the application unless I close the browser tab.

zauberzeug/nicegui#2138

Python 3.11.x works fine, Python 3.12+ seems to break the filewatcher(unsure if the actual bug is in Uvicorn or in the asyncio/python 3.12/Windows combo).
On Mac OS/Linux it works fine with Py 3.12, it's just Windows that's seemingly picky.

@Mason-Chou
Copy link

I've been using this project for on Windows for quite a while and finally decided to spend some time tonight to look into why this annoying issue is happening.

There has been some great discussions over possible fixes to this issue, but it seems like it comes down to the parent process, supervisors.multiprocess.Multiprocess, being unable to receive any signals from pressing Ctrl+C.

Why is it the case? The Server subprocesses is able to receive the signals fine. Seems to be an open bug with CPython threading.Event.wait() on Windows.

@humrochagf seems to be on the right path with #2059 by changing the behavior to use a timeout to unblock the parent process and have it receive all the handled signals. Seems to be buggy still though.

The fix I've done was replacing the self.should_exit.wait() with a simple time.sleep() loop in supervisors.multiprocess.Multiprocess

    def run(self) -> None:
        self.startup()
        while not self.should_exit.is_set():
            time.sleep(.1)
        self.shutdown()

Seems to work with my build:

  • Windows 11
  • Python 3.12
  • uvicorn master branch

I'm sure it'll work for older versions. --reload works for me but still has some annoying CancelledError.

@humrochagf
Copy link
Contributor

Hello, now that we have #2183 landing on version 0.30.0 the new process manager seems to solve this issue.

If anyone that was also facing this issue is able to check with this version I guess we are good to close this one 🙂

@spikecodes
Copy link

Same issue still happening for me on v0.30.1

@davidefortunatotecnos
Copy link
Author

Hello, I just tried with version 0.30.1 and i confirm that the issue is still happening, it is not fixed

@maxfischer2781
Copy link
Contributor

Is this dependent on the OS version or shell/terminal used, perhaps? That would explain why it works for some people but not for others.

@davidefortunatotecnos
Copy link
Author

@maxfischer2781 i provide some additional information about OS and shell, that are changed a bit since the first post i made due to some Windows updates, but are pretty similar.

Today i tried with these versions:
OS: Windows 11 23H2 Build 22631.3672
Powershell 7.4.2

If i downgrade to Uvicorn version 0.21.1 everything works fine, but every version i tried after the 0.21.1 always had the issue

@Danipulok
Copy link

Danipulok commented Jun 4, 2024

Encountered the same issue on Win10 with uvicorn 0.27.0.post1.
When I then tested it with 0.21.2, 0.22.2 and 0.27.0.post1 again, everything worked okay (tested simple code changes).

It seems this bug is pretty rare and maybe happens only if something specific in code changes? Or even env files, can't be sure.

My command:

ENV_FILE := $(PWD)/.local.env
.PHONY: main
main:
    uvicorn project.app:create_app --factory --reload --port=8100 --env-file=$(ENV_FILE)

PS: using PyCharm 2024.1.2 if important. Also enabled feature new terminal. But since this issues doesn't always happen, I doubt it's related to PyCharm (at least).

@Nochnikov
Copy link

Nochnikov commented Jun 19, 2024

The same problem with uvicorn 0.30.1 on Win10.

The frequency of the is alarming. I just reopened IDE and everything work fine.

@SomaLily
Copy link

SomaLily commented Jul 5, 2024

My Linux get a similar problem , when Crtl+C in the envirenment with py:12.4 and uvi:0.30.1, it raise a "KeyboardInterrupt()" ; but when I back to the env with py:12.0 and uvi:0.23.2 , the Ctrl+C shutdown gracefully without any raise.

@YuriFontella
Copy link

this still happens with version 0.30.1 with windows 11 powershell 7.4.3

poetry shell
uvicorn main:app --reload

when a file is modified everything freezes in this part:
WARNING: WatchFiles detected changes in 'main.py'. Reloading...

ctrl + c does not work
you need to kill all python processes

@Abeautifulsnow
Copy link

uvicorn0.30.1 also has this problem. The Windows terminal consistently experiences issues, freezing whether you attempt to stop it with Ctrl+C or there's a file change. So, I switched to wsl and everything is fine now.

@kk7188048
Copy link

So there is no way for stopping Uvicorn if its reloaded?

@Kludex
Copy link
Member

Kludex commented Jul 26, 2024

If someone with windows gives me a step by step on how to install Python and reproduce this on a windows 11 machine, I can debug it. 👍

Preferably with an editor that I can modify uvicorn internals.

@tugofpeas
Copy link

tugofpeas commented Aug 2, 2024

It's not limited to windows 11, windows 10 is affected as well. To replicate, just run with the --reload option. It may work correctly a few times - in my case I can 'CTRL + C' after the first run; any subsequent runs or simply after awhile it will not respond to 'CTRL + C'. To exit you have to have a second terminal open to kill python processes, in order to kill the uvicorn process.

@prochor666

This comment was marked as spam.

@Edfrost321
Copy link

Very frustrating and it seems like this has been an issue for some time. Is there any update on attempts to fix this?
The --reload is a fantastic feature, but as it stands windows development is even longer than non --reload as I have to kill the python process after every file save and restart.

@Kludex
Copy link
Member

Kludex commented Aug 11, 2024

Well @Edfrost321... Let me explain it.

It seems this issue is not reproducible in every Windows machine. You can just read this issue to notice that. I'm actually writing this comment from a Windows machine that can't reproduce it.

On the other hand, the ones that can reproduce the issue are only complaining and expecting the maintainer to solve this.
And... I'm the only maintainer. I can't reproduce it. How do you expect me to solve it?

Anyone really interested, should open a pull request, or... I'm also open to give my address for people interested in sending me a Windows machine with this issue as well. Maybe you can ask Microsoft or JetBrains (#2000).

@YuriFontella
Copy link

YuriFontella commented Aug 20, 2024

On my Windows system there are two programs, "Terminal" which I define as the main profile, Powershell, and "Powershell" itself.

"Terminal" has much more interesting settings than using "Powershell" directly, but in my case it has this problem of not reloading.

I still think it could be a graphical problem, when I click on it and give it focus, the reloading continues.

image

If I do more than one update, it generates this error.

image

ERROR:    Traceback (most recent call last):
  File "C:\Users\yurif\AppData\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\yurif\AppData\Local\Programs\Python\Python312\Lib\asyncio\base_events.py", line 687, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\yurif\AppData\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 194, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Users\yurif\AppData\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 123, in run
    raise KeyboardInterrupt()
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\yurif\AppData\Local\pypoetry\Cache\virtualenvs\auth-1Lj04NOI-py3.12\Lib\site-packages\litestar\_asgi\asgi_router.py", line 181, in lifespan
    await receive()
  File "C:\Users\yurif\AppData\Local\pypoetry\Cache\virtualenvs\auth-1Lj04NOI-py3.12\Lib\site-packages\uvicorn\lifespan\on.py", line 137, in receive
    return await self.receive_queue.get()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\yurif\AppData\Local\Programs\Python\Python312\Lib\asyncio\queues.py", line 158, in get
    await getter
asyncio.exceptions.CancelledError

and the ctrl+c problem still happens
uvicorn 0.30.6
python 3.12.5

@johnlowe-123
Copy link

Have discovered something that might be helpful here. I was having exactly this issue, very consistently. The failure to reload, the locking up of the console etc...

The issue is completely resolved when I disable default logging:

uvicorn app:app --reload --log-level critical

If I leave logging on, issue returns. If I turn it off, issue goes away. Hope that is helpful in tracking down the bug.

@q349980363
Copy link

I'm having the same glitch as well

@YuriFontella
Copy link

YuriFontella commented Oct 11, 2024

I have this same problem using vscode and poetry, I temporarily solved my problem by using watchfiles directly and everything worked perfectly

watchfiles "uvicorn app:app --port 7000" .

windows 11
python 3.12.7
powershell 7.4.5
uvicorn 0.31.1

@Danipulok
Copy link

@YuriFontella thank you so much!
Seems to work like a charm
And kinda seems to me that it's faster then using uvicorn's --reload
But maybe that's just placebo

Anyway, have been using for an hour and everything work perfect

@YuriFontella
Copy link

YuriFontella commented Oct 17, 2024

@Danipulok I advise everyone to use the watchfiles filter, I use pycharm and it has a strategy of backing up files when saving, which ends up causing unnecessary reloads

watchfiles --filter python "uvicorn app:app --port 7000" .

and I also had infinite reloads using the Litestar file-based stores module, I only solved it with the filter

@Danipulok
Copy link

That's really nice
Thanks a lot again, @YuriFontella!

@chris-avens
Copy link

I have this same problem using vscode and poetry, I temporarily solved my problem by using watchfiles directly and everything worked perfectly

watchfiles --filter python "uvicorn app:app --port 7000" .

windows 11 python 3.12.7 powershell 7.4.5 uvicorn 0.31.1

where exactly should i put this script? tried running it on a different terminal (because the terminal where i ran uvicorn does not respond to anything). it did not work

@mathenymi
Copy link

mathenymi commented Oct 25, 2024

This issue is not limited to windows. Seeing this on MacOS-14.4.1 as well. Python3.12.7. Ctrl-C does not stop the server and it either just hangs forever or wont stop background tasks.

@Kludex
Copy link
Member

Kludex commented Oct 25, 2024

This issue is not limited to windows. Seeing this on MacOS-14.4.1 as well. Python3.12.7. Ctrl-C does not stop the server and it either just hangs forever or wont stop background tasks.

It's not the same issue.

@vinhkhangphung
Copy link

Give thanks to @YuriFontella. Goated solution tbh.

@netvoip
Copy link

netvoip commented Dec 2, 2024

I have the same issue.
Windows 11, Windows Terminal + PowerShell, Python 3.11, uvicorn 0.32.1.
Ctlr-C stops working not immediately but after some time (or after some starts and stops). It seems like python process at some time starts to live on its own because even if I kill the whole Windows Terminal, python with uvicorn stays in processes.
Neither watchfiles nor __name__ == '__main__' check helped. Reload works well in the meantime.

@Kludex
Copy link
Member

Kludex commented Dec 13, 2024

Can people test this branch: #2532 to see if it solves the issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.