How to Catch KeyboardInterrupt in Python
KeyboardInterrupt is a built-in Python exception that's raised when a user presses Ctrl+C to stop a running program. By default, this immediately terminates the program. However, catching this exception lets you perform cleanup operations, save progress, close connections, or display a friendly exit message before shutting down.
This is especially important in programs that involve long-running processes, file operations, network connections, or database transactions where abrupt termination could cause data loss or corruption.
Basic Syntaxā
Catch KeyboardInterrupt using a standard try/except block:
try:
# Code that might be interrupted
pass
except KeyboardInterrupt:
# Handle the interruption gracefully
print("Program interrupted by user.")
Simple Example: User Input Loopā
A common scenario is a program that continuously accepts user input and needs a clean way to exit:
try:
while True:
user_input = input("Enter something (Ctrl+C to exit): ")
print(f"You entered: {user_input}")
except KeyboardInterrupt:
print("\nš Program terminated by user. Goodbye!")
Output (after pressing Ctrl+C):
Enter something (Ctrl+C to exit): hello
You entered: hello
Enter something (Ctrl+C to exit): ^C
š Program terminated by user. Goodbye!
Without the except KeyboardInterrupt block, pressing Ctrl+C would display an ugly traceback instead of a clean message.
Handling Long-Running Processesā
For processes that take time to complete, catching KeyboardInterrupt lets you stop gracefully and perform cleanup:
import time
def process_data():
total_steps = 10
try:
print(f"Processing {total_steps} steps. Press Ctrl+C to cancel.\n")
for i in range(1, total_steps + 1):
print(f"Step {i}/{total_steps}: processing...")
time.sleep(1) # Simulate work
print(f"Step {i}/{total_steps}: complete ā
")
print("\nš All steps completed successfully!")
except KeyboardInterrupt:
print(f"\n\nā ļø Interrupted at step {i}/{total_steps}")
print("Partial results may be available.")
process_data()
Output (pressing Ctrl+C during step 3):
Processing 10 steps. Press Ctrl+C to cancel.
Step 1/10: processing...
Step 1/10: complete ā
Step 2/10: processing...
Step 2/10: complete ā
Step 3/10: processing...
^C
ā ļø Interrupted at step 3/10
Partial results may be available.
Using finally for Guaranteed Cleanupā
The finally block runs regardless of whether an exception occurred, whether the program completes normally, is interrupted, or encounters an error:
import time
def write_report():
data = []
try:
print("Collecting data... Press Ctrl+C to stop early.\n")
for i in range(1, 11):
data.append(f"Record {i}")
print(f"Collected record {i}")
time.sleep(0.5)
except KeyboardInterrupt:
print(f"\nā ļø Collection interrupted after {len(data)} records.")
finally:
# This ALWAYS runs: even after KeyboardInterrupt
print(f"\nš Saving {len(data)} records to report.txt...")
with open("report.txt", "w") as f:
f.write("\n".join(data))
print("ā
Report saved successfully.")
write_report()
Output (pressing Ctrl+C after 4 records):
Collecting data... Press Ctrl+C to stop early.
Collected record 1
Collected record 2
Collected record 3
Collected record 4
^C
ā ļø Collection interrupted after 4 records.
š Saving 4 records to report.txt...
ā
Report saved successfully.
finally for critical cleanupThe finally block is the safest place for operations that must happen: closing files, releasing locks, saving state, or disconnecting from databases. It runs even if sys.exit() is called inside the except block.
Catching KeyboardInterrupt with Other Exceptionsā
You can handle KeyboardInterrupt alongside other exceptions, but keep them in separate except blocks to handle each appropriately:
import time
def divide_numbers():
try:
while True:
num = input("Enter a number (Ctrl+C to quit): ")
result = 100 / int(num)
print(f"100 / {num} = {result:.2f}\n")
except KeyboardInterrupt:
print("\nš Exiting calculator.")
except ValueError:
print("ā Please enter a valid integer.")
except ZeroDivisionError:
print("ā Cannot divide by zero.")
divide_numbers()
KeyboardInterrupt with a bare exceptA bare except: (without specifying the exception type) catches everything, including KeyboardInterrupt and SystemExit, which can make your program impossible to stop:
# BAD: Catches KeyboardInterrupt, making Ctrl+C useless
try:
while True:
pass
except: # Catches EVERYTHING: avoid this!
pass
# GOOD: Only catch specific exceptions
try:
while True:
pass
except Exception: # Doesn't catch KeyboardInterrupt or SystemExit
pass
Use except Exception: instead of bare except: to let KeyboardInterrupt and SystemExit propagate normally.
Nested Exception Handlingā
For complex programs, you might want different handlers at different levels:
import time
def process_item(item):
"""Process a single item: may raise various errors."""
time.sleep(0.3)
if item == 5:
raise ValueError(f"Invalid item: {item}")
print(f" Processed item {item} ā
")
def main():
items = range(1, 11)
try:
print("Starting batch processing...\n")
for item in items:
try:
process_item(item)
except ValueError as e:
print(f" ā ļø Skipping: {e}")
continue
print("\nš Batch processing complete!")
except KeyboardInterrupt:
print(f"\n\nā Batch interrupted at item {item}.")
print(f" {item - 1} items were processed successfully.")
main()
Output (pressing Ctrl+C during item 7):
Starting batch processing...
Processed item 1 ā
Processed item 2 ā
Processed item 3 ā
Processed item 4 ā
ā ļø Skipping: Invalid item: 5
Processed item 6 ā
^C
ā Batch interrupted at item 7.
6 items were processed successfully.
Practical Example: Database Connection Handlerā
import time
class DatabaseConnection:
def __init__(self, name):
self.name = name
self.connected = False
def connect(self):
print(f"š” Connected to {self.name}")
self.connected = True
def close(self):
if self.connected:
print(f"š Connection to {self.name} closed")
self.connected = False
def run_queries():
db = DatabaseConnection("production_db")
try:
db.connect()
print("Running queries... Press Ctrl+C to abort.\n")
for i in range(1, 101):
print(f"Query {i}: executing...")
time.sleep(0.5)
except KeyboardInterrupt:
print("\n\nā ļø Query execution interrupted!")
finally:
db.close()
print("ā
Cleanup complete.")
run_queries()
Output (pressing Ctrl+C):
š” Connected to production_db
Running queries... Press Ctrl+C to abort.
Query 1: executing...
Query 2: executing...
Query 3: executing...
^C
ā ļø Query execution interrupted!
š Connection to production_db closed
ā
Cleanup complete.
Quick Referenceā
| Pattern | Use Case |
|---|---|
except KeyboardInterrupt: | Catch Ctrl+C and handle gracefully |
finally: | Guaranteed cleanup (files, connections) |
except Exception: | Catch errors but let KeyboardInterrupt through |
except (KeyboardInterrupt, SystemExit): | Handle both user and system exits |
Conclusionā
Catching KeyboardInterrupt in Python is essential for building robust, user-friendly programs. Use a try/except KeyboardInterrupt block to display clean exit messages, and add a finally block when you need guaranteed cleanup, i.e. saving data, closing files, or releasing resources. Avoid bare except: clauses that accidentally trap keyboard interrupts, and keep your exception handlers specific to handle each error type appropriately. With proper interrupt handling, your programs exit gracefully regardless of how they're stopped.