KeyboardInterrupt
Ctrl+C arrives as an exception — deliberately outside Exception, so catch-all error handlers cannot trap the user in a running program.
Demo
log = [] try: for i in range(5): try: if i == 1: raise ValueError('bad record') if i == 3: raise KeyboardInterrupt # stands in for Ctrl+C log.append(i) except Exception: log.append('skipped') except KeyboardInterrupt: log.append('stopped') log
The ValueError at bad_at is 'skipped' and the loop carries on — that is what except Exception is for. The KeyboardInterrupt at stop_at goes straight through the same handler and ends the loop, because KeyboardInterrupt is not an Exception. The demo raises it explicitly; in a real program the interpreter raises it at whatever line is running when you press Ctrl+C.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| *args | object | no | Normally none — Python raises it without arguments. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| args | tuple | Empty when raised by Ctrl+C. |
| __traceback__ | traceback | None | Shows the line that was executing when the interrupt arrived — useful for finding where a program hangs. |
Common patterns
import sys def main(): ... if __name__ == '__main__': try: main() except KeyboardInterrupt: print('\ncancelled', file=sys.stderr) sys.exit(130)
results = [] try: for item in items: results.append(process(item)) except KeyboardInterrupt: save(results) raise
import signal stop = False def on_sigint(signum, frame): global stop stop = True signal.signal(signal.SIGINT, on_sigint) while not stop: do_one_step()
Examples
Pitfalls
log = [] for attempt in range(3): try: raise KeyboardInterrupt # user presses Ctrl+C except: log.append('retrying') log
log = [] try: for attempt in range(3): try: raise KeyboardInterrupt # user presses Ctrl+C except Exception: log.append('retrying') except KeyboardInterrupt: log.append('stopped') log
done = [] def run(): try: for i in range(5): if i == 2: raise KeyboardInterrupt done.append(i) except KeyboardInterrupt: pass return 'finished' run()
done = [] def run(): try: for i in range(5): if i == 2: raise KeyboardInterrupt done.append(i) except KeyboardInterrupt: done.append('partial') raise return 'finished' try: run() except KeyboardInterrupt: r = done r
When to use
- Top-level except KeyboardInterrupt in a CLI to exit without a traceback
- Saving partial progress before re-raising
- Recognising it in logs: the traceback shows where the program was when interrupted
- Catch-all error handling → except Exception (it already excludes Ctrl+C)
- Long-running services → handle SIGINT/SIGTERM with signal handlers
- Continuing normally after Ctrl+C → the user asked the program to stop
Notes
FAQ
KeyboardInterrupt inherits directly from BaseException, not Exception — on purpose, so that catch-all error handlers do not prevent the user from stopping the program with Ctrl+C. Catch it explicitly with except KeyboardInterrupt, or use except BaseException if you really mean everything.