VMware ESXi and vSphere Cluster Management

Python try, except, and finally: Guaranteed Cleanup After Errors

Learn how Python try, except, else, and finally clauses control errors, handle ValueError, and guarantee cleanup after success or failure.

Why Python Needs Exception Handling

An exception is an event, often an error, that interrupts the ordinary execution of Python code. For example, converting invalid text to an integer raises an exception:

age = int("unknown")

Because the text cannot represent an integer, Python raises ValueError. If the program does not handle that exception, execution stops and Python displays a traceback.

Exception handling lets a program respond deliberately to an exceptional condition instead of stopping unexpectedly. A try block contains operations that may fail. An except clause provides an alternate response when a matching exception occurs.

The try, except, and finally Structure

A try statement can contain a try block, one or more except clauses, and an optional finally clause. Each clause ends with a colon, and its statements must be indented.

try:
    value = int(input("Enter your age: "))
except ValueError:
    print("Please enter a whole number.")
finally:
    print("Input attempt finished.")

The order is fixed: try, then any except clauses, then optional else and finally clauses. The finally clause belongs to this same try statement; it is not a separate statement.

What Each Term Means

  • Exception: An event that interrupts ordinary execution.
  • Try block: The suite containing operations that may raise an exception.
  • Except clause: A handler that runs when a matching exception is raised in the associated try block.
  • Finally clause: A post-try section intended for cleanup that runs after the try statement's work.
  • Cleanup: Work that releases resources or leaves the program in a safe state.

How finally Executes

The statements in finally are intended to run after the try statement completes. This is true when the protected operation succeeds and when a matching exception is handled by except.

Successful Execution

try:
    print("Processing completed.")
except ValueError:
    print("A value was invalid.")
finally:
    print("Cleanup is complete.")

The output is:

Processing completed.
Cleanup is complete.

No exception occurs, so the except clause is skipped. The finally clause still runs.

Handled Failure

try:
    number = int("not a number")
except ValueError:
    print("The conversion failed.")
finally:
    print("Cleanup is complete.")

The output is:

The conversion failed.
Cleanup is complete.

The conversion raises ValueError, so the matching except clause runs. After that handler finishes, finally runs as well.

Execution Order

There are two common control-flow paths:

  1. No exception: Python enters try, completes its statements, skips except, and runs finally.
  2. Matching exception: Python enters try, encounters an exception, runs the matching except handler, and then runs finally.

If an exception has no matching handler, finally is still attempted, and the original exception normally continues upward after that. Likewise, finally is attempted when control leaves a try block through return, break, or continue.

When Each try Statement Clause Runs

Situationtryexceptelsefinally

No exception in try — Runs — Skipped — Runs, if present — Runs

Matching exception handled by except — Begins, then stops at the error — Matching handler runs — Skipped — Runs

Exception with no matching handler — Begins, then stops at the error — No matching handler — Skipped — Runs, then the exception propagates

finally Compared with else

An else clause is for work that should run only when the try block succeeds without an exception. A finally clause is for work that should run regardless of success or a handled failure.

try:
    result = risky_operation()
except ExpectedError:
    handle_error()
else:
    use_result(result)
finally:
    clean_up()

In this example, use_result(result) runs only when risky_operation() succeeds. clean_up() runs after either the successful path or the handled-error path.

ClauseUse it when...

try — An operation might raise an exception.

except — A particular error needs a response.

else — Work should happen only after successful completion of try.

finally — Cleanup or another termination task must be attempted on both paths.

Example: Converting User Input

input() returns text. Calling int() attempts to convert that text to an integer. Text such as "42" works, but text such as "forty-two" raises ValueError.

try:
    age = int(input("Enter your age: "))
    print(f"Your age is {age}.")
except ValueError as error:
    print(f"Invalid age: {error}")
finally:
    print("The age input attempt is finished.")

Run with Numeric Input

Suppose the user enters 21. The flow is:

  1. input() returns the text "21".
  2. int() converts it to the integer 21.
  3. The success message inside try prints.
  4. The except clause is skipped.
  5. The final message in finally prints.
Enter your age: 21
Your age is 21.
The age input attempt is finished.

Run with Nonnumeric Input

Suppose the user enters twenty-one. The flow is:

  1. input() returns text that cannot be converted to an integer.
  2. int() raises ValueError.
  3. The remaining statements in try are skipped.
  4. The except ValueError as error handler prints an error message.
  5. The finally clause prints the final message.
Enter your age: twenty-one
Invalid age: invalid literal for int() with base 10: 'twenty-one'
The age input attempt is finished.

The important observation is that the final message appears in both runs. If a message should appear only after successful conversion, put it in else, not finally.

Using finally for Cleanup

Cleanup remains necessary when normal processing fails. A program may have opened a file, established a database connection, acquired a lock, changed application state, or started a session before an exception occurred. The finally clause gives the program a place to release or restore that resource.

finally Cleanup Examples

Resource or sessionCleanup actionWhy it matters

File — Close the file — Releases the operating-system file handle and makes buffered data available.

Database connection — Close or return the connection — Prevents leaked connections and connection-pool exhaustion.

Network connection — Close the socket or session — Releases network resources and ends the session cleanly.

User session or application state — Restore state or record completion — Prevents later code from seeing a partially changed state.

File Cleanup Illustration

file = None
try:
    file = open("report.txt", "r")
    text = file.read()
    process_report(text)
except ValueError:
    print("The report contained an invalid value.")
finally:
    if file is not None:
        file.close()

Initializing file before the try block makes it possible to test whether opening the file succeeded. If opening fails, there is no file to close. In modern Python, a with statement is usually the preferred way to manage files because it performs this resource management automatically, but this example shows the role of finally.

Keep cleanup code small, reliable, and focused on releasing resources or restoring safe state. Ordinary application logic, such as displaying a successful result or continuing a business process, usually belongs in try, except, or else instead.

Catch Specific Exceptions

A bare except has no exception type:

try:
    value = int(text)
except:
    print("Something went wrong.")

A bare except catches nearly every ordinary exception. This can hide unexpected programming mistakes, making debugging difficult. Prefer a targeted handler when you know which failure is expected:

try:
    value = int(text)
except ValueError:
    print("Please provide a whole number.")

ValueError is the exception commonly raised when int() receives text that cannot be converted to an integer. Use as to capture the exception object when its message is useful for a response or log entry:

try:
    value = int(text)
except ValueError as error:
    print(f"Conversion failed: {error}")

Cautions with finally

An Exception in finally Can Mask an Earlier Exception

If cleanup itself raises a new exception, that new failure can prevent the earlier exception from being observed normally. Cleanup should therefore be dependable and, where appropriate, have its own careful error handling.

Avoid return in finally

A return statement in finally can override a return value from try or suppress an exception:

def example():
    try:
        return "try result"
    finally:
        return "finally result"

print(example())  # finally result

The return from finally wins. This behavior can also make an exception disappear from the caller's perspective. Avoid returning from finally; use it for cleanup rather than changing the function's outcome.

Troubleshooting Common Problems

“The final message appears even after an error.”

This is expected. finally is designed to run after the try statement. Move success-only work to else or keep it in the successful part of try. Keep cleanup and always-run status work in finally.

“Invalid text causes the program to stop.”

The conversion raised ValueError, but no matching handler was present. Catch ValueError around the conversion and provide a clear response.

“A broad except hides programming mistakes.”

A bare except may catch errors beyond the anticipated invalid-input case. Replace it with the expected exception class, such as ValueError, so unrelated defects remain visible.

“Cleanup fails because the resource was never created.”

An exception may occur before a file, connection, or other resource is assigned. Initialize the resource safely before try, check whether it exists in finally, or use a context manager when one is available.

“An earlier error seems to disappear.”

A new exception or a return inside finally can replace the original outcome. Avoid returns in finally and keep cleanup failures controlled so they do not mask the primary exception.

Key Points

  • Put operations that may fail in a try block.
  • Use a targeted except clause to handle an expected exception.
  • Use else for code that requires successful completion of try.
  • Use finally for cleanup or another task that should be attempted after success or handled failure.
  • finally normally runs even when control leaves the block with return, break, or continue.
  • Do not use a bare except routinely, and do not put return in finally.