Resource Management with Context Managers
Context managers in Python provide a clean and reliable way to manage resources like files, network connections, or database cursors. They ensure that resources are properly acquired and released, even in the presence of exceptions, promoting robust and maintainable code. This challenge will guide you through creating your own context managers to handle resource allocation and deallocation effectively.
Problem Description
You are tasked with creating two custom context managers: FileWrapper and DatabaseConnection.
FileWrapper: This context manager should wrap a file object. When entered, it should print "Opening file...". When exited (either normally or due to an exception), it should print "Closing file..." and then close the file.
DatabaseConnection: This context manager simulates a database connection. When entered, it should print "Connecting to database...". When exited, it should print "Closing database connection..." and then call a disconnect() method (which you'll define within the class). The disconnect() method should simply print "Database connection closed.".
You must implement both context managers using the __enter__ and __exit__ methods. The __enter__ method should return the resource (file object or the DatabaseConnection instance itself). The __exit__ method should handle the cleanup and exception handling.
Key Requirements:
- Both context managers must handle exceptions gracefully. If an exception occurs within the
withblock, the__exit__method should still be called to ensure proper cleanup. - The
DatabaseConnectioncontext manager should demonstrate the use of the__exit__method's arguments (exc_type,exc_val,exc_tb) to potentially log or handle exceptions. For this challenge, simply print the exception type and value if an exception occurred. - The context managers should be reusable and not rely on global state.
Expected Behavior:
When used with a with statement, the context managers should execute the appropriate "opening" and "closing" messages, and perform the necessary cleanup actions. Exceptions within the with block should be caught and handled by the __exit__ method.
Examples
Example 1: FileWrapper
Input:
with FileWrapper("my_file.txt", "w") as f:
f.write("Hello, world!")
raise ValueError("Simulated error")
Output:
Opening file...
Closing file...
ValueError: Simulated error
Explanation: The file is opened, "Hello, world!" is written, a ValueError is raised, the __exit__ method is called, printing "Closing file...", and the exception is re-raised.
Example 2: DatabaseConnection
Input:
with DatabaseConnection() as db:
print("Inside database context")
raise TypeError("Database error")
Output:
Connecting to database...
Inside database context
Closing database connection...
Database connection closed.
TypeError: Database error
Explanation: The database connection is established, "Inside database context" is printed, a TypeError is raised, the __exit__ method is called, printing "Closing database connection..." and "Database connection closed.", and the exception is re-raised.
Example 3: No Exception
Input:
with FileWrapper("my_file.txt", "w") as f:
f.write("Hello, world!")
Output:
Opening file...
Closing file...
Explanation: The file is opened, "Hello, world!" is written, the __exit__ method is called, printing "Closing file...", and no exception is raised.
Constraints
- The
FileWrappershould accept a filename and mode as arguments to its constructor. - The
DatabaseConnectionshould not accept any arguments to its constructor. - The
__exit__method inDatabaseConnectionmust handle the exception arguments (exc_type,exc_val,exc_tb) by printing the exception type and value if an exception occurred. - All file operations must be performed within the
withblock. - The
disconnect()method inDatabaseConnectionshould simply print "Database connection closed.".
Notes
- Remember that the
__exit__method receives three arguments:exc_type,exc_val, andexc_tb.exc_typeis the exception type (orNoneif no exception occurred),exc_valis the exception instance, andexc_tbis the traceback object. - Consider how to ensure that the file is properly closed even if an exception occurs during file writing.
- Think about the purpose of context managers and how they contribute to cleaner and more reliable code.
- The
__enter__method should return a value that can be assigned to a variable in thewithstatement. In this case, it's the file object or theDatabaseConnectioninstance.