[REF-1561] [py312] Reflex server no longer running after hot reload on Windows
I install and download reflex as the tutorial sugested it and give the reflex run command on the terminal, but when i made changes to the initial code the auto refresh load after a while but the local host 3000 can´t be found in the browser.
To Reproduce Steps to reproduce the behavior: Modify the example file:
def index() -> rx.Component:
return rx.fragment(
rx.color_mode_button(rx.color_mode_icon(), float="right"),
rx.vstack(
rx.heading("Hi my project!", font_size="2em"),
rx.box("Get started by editing ", rx.code(filename, font_size="1em")),
rx.link(
"Check out our docs!",
href=docs_url,
border="0.1em solid",
padding="0.5em",
border_radius="0.5em",
_hover={
"color": rx.color_mode_cond(
light="rgb(107,99,246)",
dark="rgb(179, 175, 255)",
)
},
),
spacing="1.5em",
font_size="2em",
padding_top="10%",
),
)
Expected behavior if you modify that after runing reflex it should refresh the compiler, it does, but it can´t acces to the local host 3000 after that.
Specifics (please complete the following information):
- Python Version: 3.12
- Reflex Version: 0.3.7
- OS: Windows
- Browser (Optional): Edge
From SyncLinear.com | REF-1561
this is the log i get when i run the reflex run --loglevel debug
ERROR: Traceback (most recent call last):
File ".\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".\Local\Programs\Python\Python312\Lib\asyncio\base_events.py", line 664, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File ".\\Python web course\.venv\Lib\site-packages\uvicorn\server.py", line 78, in serve
await self.startup(sockets=sockets)
File ".\\Python web course\.venv\Lib\site-packages\uvicorn\server.py", line 89, in startup
await self.lifespan.startup()
File ".\\Python web course\.venv\Lib\site-packages\uvicorn\lifespan\on.py", line 54, in startup
await self.startup_event.wait()
File ".\Local\Programs\Python\Python312\Lib\asyncio\locks.py", line 212, in wait
await fut
asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File ".\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File ".\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 ".\\Python web course\.venv\Lib\site-packages\starlette\routing.py", line 686, in lifespan
await receive()
File ".\\Python web course\.venv\Lib\site-packages\uvicorn\lifespan\on.py", line 137, in receive
return await self.receive_queue.get()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".\Local\Programs\Python\Python312\Lib\asyncio\queues.py", line 158, in get
await getter
asyncio.exceptions.CancelledError
Process SpawnProcess-2:
Traceback (most recent call last):
File ".\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".\Local\Programs\Python\Python312\Lib\asyncio\base_events.py", line 664, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File ".\\Python web course\.venv\Lib\site-packages\uvicorn\server.py", line 78, in serve
await self.startup(sockets=sockets)
File ".\\Python web course\.venv\Lib\site-packages\uvicorn\server.py", line 89, in startup
await self.lifespan.startup()
File ".\\Python web course\.venv\Lib\site-packages\uvicorn\lifespan\on.py", line 54, in startup
await self.startup_event.wait()
File ".\Local\Programs\Python\Python312\Lib\asyncio\locks.py", line 212, in wait
await fut
asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File ".\Local\Programs\Python\Python312\Lib\multiprocessing\process.py", line 314, in _bootstrap
self.run()
File ".\Local\Programs\Python\Python312\Lib\multiprocessing\process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File ".\\Python web course\.venv\Lib\site-packages\uvicorn\_subprocess.py", line 76, in subprocess_started
target(sockets=sockets)
File ".\\Python web course\.venv\Lib\site-packages\uvicorn\server.py", line 61, in run
return asyncio.run(self.serve(sockets=sockets))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File ".\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 123, in run
raise KeyboardInterrupt()
KeyboardInterrupt
i'm able to reproduce this issue with python 3.12 on windows 11;
it seems like the hot reload is getting stuck in npm trying to install/check packages and hanging
Confirm, this issue is affecting python 3.12 going back to at least 0.3.5;
Python 3.11 does not seem to exhibit this problem. As a workaround, use python 3.11 on windows.
Hi, yes, I recently realized that the hot reload feature of Uvicorn is encountering an error. It appears that the files aren't reloading and are getting lost. I'll try using an older version of Python. I'm experiencing issues with Uvicorn in this new Python version; it reloads automatically and not when I save the main file.
I have the same issue with Reflex 0.4.3 on Windows 10 (no WSL) with Python 3.12. Using Python 3.11 indeed fixes the problem, but would still be nice to have Reflex working on the most recent version of Python.
The reflex team would love some help on this issue. I think the problem lies in our upstream dependency, uvicorn. Uvicorn hot reload by itself seems to work in windows with py3.12, but it does not work with Reflex 😢. Some investigation and debugging will be needed, and the team doesn't have the bandwidth to take it on at the moment.
Hey I'm new to the repo but I'd love to help with this.
As far as I can tell the issue comes from the uvicorn change (https://github.com/encode/uvicorn/pull/1584/). As part of their reloading process where they shut down the server and start it again, they shifted from using multiprocessing.Process.terminate() to sending a signal.CTRL_C_EVENT using os.kill(). It seems like this Ctrl_C_Event is somehow reaching the npm frontend and causing it to stop.
I'll investigate this further and update
Update: Dived a little deeper and figured out that the issue boils down to a bug in Microsoft's GenerateConsoleCtrlEvent function. The links regarding this bug are the following:
- https://bugs.python.org/issue42962
- https://github.com/microsoft/terminal/issues/335
Summarizing from the links, os.kill(pgid, signal.CTRL_C_EVENT) is supposed to be called on process group ids and if instead it is called on pids (which is what uvicorn does, I think) it leads to a not well documented behavior where Ctrl + C is wrongfully called on all the processes that are part of the console, including the npm frontend shell.
So, unless uvicorn makes a fix at their end it'll be hard to avoid Ctrl+C from reaching the npm frontend. We could pin uvicorn to v0.21.1 in reflex which should fix the hot reload issue. And in parallel, I'll try and make a simpler reproducible version of this issue to submit in the uvicorn repo and try and fix it.
Or we could somehow try to capture the Ctrl+C event at the frontend and ignore it when it is initiated by the hot reload process.
@sid-38 Great investigation thanks for finding this. I'd be open to pinning uvicorn - my only concern would be if this strict requirement may have conflicts with other packages that people use with Reflex.
So I found an alternative that would let us use the latest uvicorn. When we are creating the python subprocess for npm, we could add the creation flag - CREATE_NEW_PROCESS_GROUP. This would make the npm frontend part of a new process group which means that even if the uvicorn bug tries to Ctrl+C everything it will not reach the frontend. Internally, when python creates a sub process with the CREATE_NEW_PROCESS_GROUP flag, it'll turn off the Ctrl+C handler in the new process which is why uvicorn won't be able to accidentally kill frontend.
This means, hot reload will no longer cause frontend to stop. However this also means that when we actually want to quit reflex, and we intentionally do Ctrl+C on the console, that will not reach the frontend either (coz frontend no longer responds to Ctrl+C). Uvicorn will see the Ctrl+C and stop, but then reflex main thread would just wait for frontend to finish which it never does. A workaround to this would be to add the code in reflex to kill frontend, once uvicorn exits. I've tested this out and it does work.
Now, to kill the frontend, reflex cannot use a CTRL_C_EVENT for the reasons mentioned above. I'm trying to see if there is a way to enable the Ctrl C handler for frontend once uvicorn ends but that leads to using ctypes.kernel and it gets complicated. Reflex could send a SIGTERM or a CTRL_BREAK _EVENT to frontend both of which causes frontend to quit. But it prints out an error log for the frontend.
So in short, there is a workaround that makes everything work with latest uvicorn but it involves sending SIGTERM or CTRL+BREAK to frontend both of which output error messages to the terminal. We can either let the error messages be, or in reflex.processes.stream_logs we can filter out the SIGTERM error code to not print out the entire error log.
Fixed in 0.4.7