It turns out you can make this work by using Microsoft's debugpy tool.
Django launches two processes when reloading is turned on (the default), one of them is the parent and the other is the subprocess which does the reloading magic.
Django differentiates these two processes by setting the environment variable RUN_MAIN
to true in the subprocess (which does the reloading). Refer to: https://github.com/django/django/blob/8a902b7ee622ada258d15fb122092c1f02b82698/django/utils/autoreload.py#L241
By tweaking manage.py
slightly, we can start a debug listener in the parent process and have it survive any reloads.
Add debugpy
to however you manage your requirements (requirements.txt
etc.)
Add the following function to initialize the debugger:
def initialize_debugger():
import debugpy
# optionally check to see what env you're running in, you probably only want this for
# local development, for example: if os.getenv("MY_ENV") == "dev":
# RUN_MAIN envvar is set by the reloader to indicate that this is the
# actual thread running Django. This code is in the parent process and
# initializes the debugger
if not os.getenv("RUN_MAIN"):
debugpy.listen(("0.0.0.0", 9999))
sys.stdout.write("Start the VS Code debugger now, waiting...\n")
debugpy.wait_for_client()
sys.stdout.write("Debugger attached, starting server...\n")
- Alter the
main
function in manage.py as follows:
def main()
# <...>
initialize_debugger() # add this
execute_from_command_line(sys.argv)
- Modify launch.json configuration in VSCode to attach to port 9999 (from above):
{
"name": "Python: Remote Attach (DebugPy)",
"type": "python",
"request": "attach",
"port": 9999,
"host": "localhost",
},
TIP: you can disable "Uncaught exceptions" because the reload itself causes a SystemExit