There is no known way to properly exit applications run with IDLE "run module"
Bug report
Hello!
So this has been an issue that has probably been bugging many people for a while now. This has bitten me back when I was a newbie as well, learning python using IDLE. This issue does not personally affect me anymore since I've mostly moved on from IDLE, but for the sake of others new to the language, I would like to formalise this issue on the tracker.
Here is a pure python MRE that demonstrates what I'm saying
import atexit
import time
@atexit.register
def done():
print("Exiting application!")
time.sleep(1)
When I run this on the command-line with no flags, I see the text printed after the program runs for a second and exits. On IDLE, this just runs for a second and exits without printing anything.
So I later realised that IDLE runs programs with the -i flag. But on the commandline I can do a ctrl+z and get the interpreter to actually stop and handle atexit. This is not apparent in IDLE shell, where there does not seem to be any distinction between the "regular interactive state", and the "previous module run interactive state". And neither does ctrl+c/z/d work. A hard shell restart also does not lead to anything being printed.
I've glanced through the issue tracker with a quick search and I didn't find a duplicate of this issue, but incase I'm mistaken, my apologies; this issue can be closed.
Your environment
- CPython versions tested on: 3.10 (but from my memory, it happened on all previous versions too)
- Operating system and architecture: Windows 11 64-bit (In my understanding it also happens on other architectures and OSes)
I just realised that IDLE running stuff with the -i flag is a feature not a bug after I was pointed to at the doc for this
So I will re-word this issue a bit
On Windows Command Prompt, after ...>python -i atexit.py (for instance), ^Z...> prompt. If I hit ^C first, >>> is repeated. Then exit with ^Z is interrupted with Terminate batch job (Y/N)?. Either answer exits. If one starts python from an icon, so it is running directly in a console window, enters the code above, and hits ^Z, the print may happens, but the window normally closes too fast to tell.
IDLE uses ^D rather then ^Z to exit, even on Windows. In this sense, Shell is like python is a console window, Even if it did execute the atexit function, one might not be able to tell. To see output, one should exit the session without killing the Window, as when starting in a command line window with python -i. In IDLE, this is done by restarting either with the Shell or Run menu. The problem with then triggering atexit functions is that IDLE executes user code with exec(usercode), and C-coded atexit has no public API to trigger or access the registered functions.
In normal mode, with user code exec in a separate process, I believe the subprocess is terminated by a signal or the windows equivalent, which the atexit doc explicitly says does not trigger registered functions. The code goes through some effort to make sure that the process is really killed and does not become a zombie. I might experiment with getting the IDLE code in the user code process to directly exit, but care would be needed to assure that the process is really killed.
With python -i, SystemExit raised by user code is printed and and >>> is printed, as requested by -i. IDLE does not print the traceback, but this deviation from the REPL is maybe a mistake. In command prompt, the atexit print occurs when it is registered first.