uvicorn icon indicating copy to clipboard operation
uvicorn copied to clipboard

asyncio.exceptions.CancelledError with asyncio

Open wanghaisheng opened this issue 7 months ago • 3 comments

Initial Checks

  • [X] I confirm this was discussed, and the maintainers suggest I open an issue.
  • [X] I'm aware that if I created this issue without a discussion, it may be closed without a response.

Discussion Link

yes

Description

I am building a tkinter gui and in the background host a fastapi and uvicorn server for static files. here i want to exit program when hit system tray menu ,it do but a lot of error related to how i start a uvicorn server with asyncio

INFO:     Started server process [4692]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
shutdown icon
shutdown server
INFO:     Shutting down
INFO:     Shutting down
shutdown root
shutdown loop
An error occurred when calling message handler
Traceback (most recent call last):
  File "D:\program\miniconda\lib\site-packages\pystray\_win32.py", line 412, in _dispatcher
    return int(icon._message_handlers.get(
  File "D:\program\miniconda\lib\site-packages\pystray\_win32.py", line 224, in _on_notify
    descriptors[index - 1](self)
  File "D:\program\miniconda\lib\site-packages\pystray\_base.py", line 328, in inner
    callback(self)
  File "D:\program\miniconda\lib\site-packages\pystray\_base.py", line 453, in __call__
    return self._action(icon, self)
  File "D:\workspace\tiktoka\tiktoka-studio-uploader-genius\t.py", line 65, in quit_window
    loop.close()
  File "D:\program\miniconda\lib\asyncio\proactor_events.py", line 681, in close
    raise RuntimeError("Cannot close a running event loop")
RuntimeError: Cannot close a running event loop
Traceback (most recent call last):
  File "D:\workspace\tiktoka\tiktoka-studio-uploader-genius\t.py", line 128, in <module>
    loop.run_forever()
  File "D:\program\miniconda\lib\asyncio\windows_events.py", line 319, in run_forever
ERROR:    Traceback (most recent call last):
  File "D:\program\miniconda\lib\site-packages\starlette\routing.py", line 686, in lifespan
    await receive()
  File "D:\program\miniconda\lib\site-packages\uvicorn\lifespan\on.py", line 137, in receive
    return await self.receive_queue.get()
  File "D:\program\miniconda\lib\asyncio\queues.py", line 159, in get
    await getter
asyncio.exceptions.CancelledError

    assert self._self_reading_future is None
AssertionError
Exception in thread Thread-1 (start_fastapi_server):
Traceback (most recent call last):
  File "D:\program\miniconda\lib\site-packages\uvicorn\server.py", line 82, in serve
    await self.shutdown(sockets=sockets)
  File "D:\program\miniconda\lib\site-packages\uvicorn\server.py", line 275, in shutdown
    await asyncio.sleep(0.1)
  File "D:\program\miniconda\lib\asyncio\tasks.py", line 605, in sleep
    return await future
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\program\miniconda\lib\threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "D:\program\miniconda\lib\threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "D:\workspace\tiktoka\tiktoka-studio-uploader-genius\t.py", line 88, in start_fastapi_server
    loop.run_until_complete(server.serve())
  File "D:\program\miniconda\lib\asyncio\base_events.py", line 649, in run_until_complete
    return future.result()
asyncio.exceptions.CancelledError

Example Code

import sys
import threading
from fastapi import FastAPI
from fastapi.responses import FileResponse

from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from pystray import MenuItem as item
import pystray
from PIL import Image, ImageTk
import asyncio
import tkinter as tk


app = FastAPI()
# Allow all origins
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # You can replace this with specific origins if needed
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

def start(lang, root=None, async_loop=None):
    global mainwindow, canvas

    # root.resizable(width=True, height=True)
    root.iconbitmap("assets/icon.ico")
    root.title('tkinter asyncio demo')


    root.update_idletasks()



def quit_window(icon, item):
    global loop,fastapi_thread

    print('shutdown icon')

    icon.stop()



    print('shutdown server')
    server.should_exit = True
    server.force_exit = True
    asyncio.run(server.shutdown())


    try:
        tasks = asyncio.all_tasks(loop)
        for task in tasks:
            task.cancel()
    except RuntimeError as err:
        print('SIGINT or SIGTSTP raised')
        print("cleaning and exiting")
        sys.exit(1)    
    print('shutdown root')

    root.destroy()
    print('shutdown loop')

    loop.close()
    # loop.stop()
    # loop.run_until_complete(asyncio.gather(*[shutdown()]))
    # os._exit(0)

def show_window(icon, item):
    icon.stop()
    root.after(0, root.deiconify)


def withdraw_window():
    root.withdraw()
    image = Image.open("assets/icon.ico")
    menu = (item("Quit", quit_window), item("Show", show_window))
    icon = pystray.Icon("name", image, "title", menu)
    # icon.run_detached()
    icon.run()

def start_fastapi_server(loop):
    import uvicorn
    global server
    config = uvicorn.Config(app, loop=loop, host="0.0.0.0", port=8000)
    server = uvicorn.Server(config)
    loop.run_until_complete(server.serve())





def start_tkinter_app(async_loop):
    global root, settings, db, canvas, locale
    root = tk.Tk()

    locale = 'en'
    start(locale, root, async_loop)

    root.protocol('WM_DELETE_WINDOW', withdraw_window)


    root.mainloop()




if __name__ == "__main__":
    global loop,fastapi_thread
    loop=None
    if sys.platform == 'win32':
        asyncio.get_event_loop().close()
        # On Windows, the default event loop is SelectorEventLoop, which does
        # not support subprocesses. ProactorEventLoop should be used instead.
        # Source: https://docs.python.org/3/library/asyncio-subprocess.html
        loop = asyncio.ProactorEventLoop()
        asyncio.set_event_loop(loop)
    else:
        loop = asyncio.get_event_loop()

    # Start FastAPI server in a separate thread
    fastapi_thread = threading.Thread(target=start_fastapi_server,args=(
            loop,)).start()


    start_tkinter_app(loop)
    loop.run_forever()
    loop.close()


### Python, Uvicorn & OS Version

```Text
Running uvicorn 0.24.0.post1 with CPython 3.10.10 on Windows

[!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

wanghaisheng avatar Dec 01 '23 01:12 wanghaisheng