Ojasa Mirai

Ojasa Mirai

Python

Loading...

Learning Level

🟒 BeginnerπŸ”΅ Advanced
Exceptions OverviewException TypesTry-Except BlocksRaising ExceptionsCustom ExceptionsMultiple ExceptionsFinally & CleanupDebugging TechniquesLogging Best Practices
Python/Error Handling/Finally And Cleanup

🧹 Finally & Cleanup β€” Ensuring Resources Are Properly Released

Learn how to guarantee cleanup code runs, even when exceptions occur.


πŸ“Œ The Finally Block

The `finally` block runs no matter whatβ€”exception or not:

# Finally always runs
def example():
    try:
        print("In try block")
        result = 10 / 0
    except ZeroDivisionError:
        print("In except block")
    finally:
        print("In finally block - ALWAYS runs!")

example()

# Output:
# In try block
# In except block
# In finally block - ALWAYS runs!

# Even if you return
def early_return():
    try:
        print("Trying...")
        return "Success!"
    finally:
        print("Finally block runs before return!")

print(early_return())

# Output:
# Trying...
# Finally block runs before return!
# Success!

πŸ” Guaranteed Resource Cleanup

Finally is critical for releasing resources:

# Without finally - file might not close if exception occurs
def bad_read_file(filename):
    file = open(filename)
    try:
        content = file.read()
        return content
    except FileNotFoundError:
        print("File not found!")
        return None
    # BUG: If exception occurs, file never closes!

# With finally - file ALWAYS closes
def good_read_file(filename):
    file = open(filename)
    try:
        content = file.read()
        return content
    except FileNotFoundError:
        print("File not found!")
        return None
    finally:
        file.close()
        print("File closed")

# Best: context manager (preferred)
def best_read_file(filename):
    try:
        with open(filename) as file:
            content = file.read()
            return content
    except FileNotFoundError:
        print("File not found!")
        return None
    # File automatically closes

πŸ“Š Common Resource Cleanup Patterns

# Pattern 1: Database connections
def query_database():
    connection = None
    try:
        connection = connect_to_db()
        result = connection.execute("SELECT * FROM users")
        return result
    except ConnectionError:
        print("Database connection failed")
        return None
    finally:
        if connection:
            connection.close()
            print("Database connection closed")

# Pattern 2: File operations
def process_large_file(filename):
    file = None
    try:
        file = open(filename)
        lines = file.readlines()
        return process_lines(lines)
    except FileNotFoundError:
        print("File not found")
        return None
    except ValueError:
        print("Invalid data in file")
        return None
    finally:
        if file:
            file.close()

# Pattern 3: Lock/unlock resources
def critical_operation():
    lock = acquire_lock()
    try:
        print("Performing critical operation...")
        do_something()
    except Exception as e:
        print(f"Operation failed: {e}")
        raise  # Re-raise after logging
    finally:
        lock.release()
        print("Lock released")

🎯 Context Managers: The Better Way

Context managers automatically handle setup and cleanup:

# Using 'with' statement for automatic cleanup
def read_file(filename):
    try:
        with open(filename) as file:
            return file.read()
    except FileNotFoundError:
        print(f"Cannot find {filename}")
        return None
    # File automatically closes here

# Process multiple files
def process_files(filenames):
    for filename in filenames:
        try:
            with open(filename) as f:
                content = f.read()
                print(f"Processing {filename}...")
        except FileNotFoundError:
            print(f"Skipping {filename} - not found")
        # File closes even if exception occurs

# Multiple context managers
def copy_file(source, destination):
    try:
        with open(source) as src, open(destination, 'w') as dst:
            content = src.read()
            dst.write(content)
            print(f"Copied {source} to {destination}")
    except FileNotFoundError:
        print(f"Source file not found")
    except IOError:
        print(f"Cannot write to destination")
    # Both files close automatically

πŸ”„ Try-Except-Finally-Else

All together in one block:

# Complete pattern
def process_data(filename):
    file = None
    try:
        file = open(filename)
        data = json.load(file)
    except FileNotFoundError:
        print(f"File {filename} not found")
    except json.JSONDecodeError:
        print(f"Invalid JSON in {filename}")
    else:
        # Only runs if no exception
        print(f"Successfully loaded data from {filename}")
        return data
    finally:
        # Always runs
        if file:
            file.close()
        print("Cleanup complete")

# Better with context manager
def process_data(filename):
    try:
        with open(filename) as file:
            data = json.load(file)
    except FileNotFoundError:
        print(f"File {filename} not found")
    except json.JSONDecodeError:
        print(f"Invalid JSON in {filename}")
    else:
        print(f"Successfully loaded data")
        return data
    # File closes automatically

🎨 Practical Cleanup Examples

# Example 1: Database transaction
def transfer_money(from_account, to_account, amount):
    connection = None
    try:
        connection = connect_to_database()
        connection.begin_transaction()

        # Debit from_account
        connection.execute(
            "UPDATE accounts SET balance = balance - ? WHERE id = ?",
            (amount, from_account)
        )

        # Credit to_account
        connection.execute(
            "UPDATE accounts SET balance = balance + ? WHERE id = ?",
            (amount, to_account)
        )

        connection.commit()
        print(f"Transfer of ${amount} successful")

    except InsufficientFundsError:
        print("Insufficient funds")
        if connection:
            connection.rollback()
    except DatabaseError as e:
        print(f"Database error: {e}")
        if connection:
            connection.rollback()
    finally:
        if connection:
            connection.close()
            print("Database connection closed")

# Example 2: File cleanup with logging
def process_csv_file(filename):
    input_file = None
    output_file = None

    try:
        input_file = open(filename)
        output_file = open(filename.replace(".csv", "_processed.csv"), "w")

        reader = csv.reader(input_file)
        writer = csv.writer(output_file)

        for row in reader:
            processed_row = [x.strip() for x in row]
            writer.writerow(processed_row)

        print(f"Successfully processed {filename}")

    except FileNotFoundError:
        print(f"File not found: {filename}")
    except csv.Error as e:
        print(f"CSV parsing error: {e}")
    finally:
        if input_file:
            input_file.close()
        if output_file:
            output_file.close()
        print("Files closed")

# Example 3: Temp file cleanup
import tempfile
import os

def create_report(data):
    temp_file = None
    try:
        # Create temporary file
        temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
        temp_path = temp_file.name

        # Write data
        json.dump(data, temp_file)
        temp_file.close()

        # Process the file
        process_report(temp_path)

    except Exception as e:
        print(f"Error creating report: {e}")
    finally:
        # Always delete temporary file
        if temp_file:
            try:
                os.unlink(temp_path)
                print(f"Temp file deleted: {temp_path}")
            except OSError:
                pass

# Example 4: Lock/resource tracking
class ResourceLock:
    def __init__(self, name):
        self.name = name
        self.locked = False

    def acquire(self):
        self.locked = True
        print(f"Lock acquired: {self.name}")

    def release(self):
        self.locked = False
        print(f"Lock released: {self.name}")

def protected_operation():
    lock = ResourceLock("database")
    try:
        lock.acquire()
        print("Performing protected operation...")
        # Could raise exception here
        critical_code()
    except Exception as e:
        print(f"Operation failed: {e}")
    finally:
        lock.release()

⚠️ Finally Block Exceptions

# If finally raises exception, it overwrites try/except exceptions
def problematic_finally():
    try:
        print("Trying...")
        raise ValueError("Original error")
    except ValueError as e:
        print(f"Caught: {e}")
    finally:
        print("In finally...")
        # This exception OVERWRITES the original!
        raise RuntimeError("Finally error")

# Avoid this! Use safe cleanup:
def good_finally():
    try:
        print("Trying...")
        raise ValueError("Original error")
    except ValueError as e:
        print(f"Caught: {e}")
    finally:
        print("In finally...")
        try:
            cleanup_code()
        except:
            print("Cleanup failed but original error takes priority")

# Better: don't raise in finally at all
def best_finally():
    try:
        print("Trying...")
        raise ValueError("Original error")
    except ValueError as e:
        print(f"Caught: {e}")
    finally:
        try:
            cleanup_code()
        except:
            pass  # Log but don't raise

🎯 Key Takeaways

  • βœ… `finally` block always runs, even with exceptions
  • βœ… Use for guaranteed resource cleanup
  • βœ… Context managers (`with`) are preferred over finally
  • βœ… Multiple `with` statements can stack
  • βœ… `else` block runs only if no exception occurred
  • βœ… Never raise exceptions in finally block
  • βœ… Always close files, connections, and locks


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep β€’ Build Real β€’ Verify Skills β€’ Launch Forward

Courses

PythonFastapiReactJSCloud

Β© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn