Cleaning up in a context manager

In this section, we'll discuss a more complex context manager that attempts some cleanup when there are problems.

This addresses the common issue where we want to save a backup copy of a file that our application is rewriting. We want to be able to do something similar to the following:

with Updating(some_path): 
    with some_path.open('w') as target_file: 
        process(target_file) 

The intent is to have the original file renamed to some_file copy. If the context works normally, that is, no exceptions are raised, then the backup copy can be deleted or renamed to some_file old.

If the context doesn't work normally, that is, an exception is raised, we want to rename the new file to some_file error and rename the old file to some_file, putting the original file back the way it was before the exception.

We will need a context manager similar to the following:

from pathlib import Path
from typing import Optional

class Updating:

def __init__(self, target: Path) -> None:
self.target: Path = target
self.previous: Optional[Path] = None

def __enter__(self) -> None:
try:
self.previous = (
self.target.parent
/ (self.target.stem + " backup")
).with_suffix(self.target.suffix)
self.target.rename(self.previous)
except FileNotFoundError:
self.previous = None

def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType]
) -> Optional[bool]:
if exc_type is not None:
try:
self.failure = (
self.target.parent
/ (self.target.stem + " error")
).with_suffix(self.target.suffix)
self.target.rename(self.failure)
except FileNotFoundError:
pass # Never even got created.
if self.previous:
self.previous.rename(self.target)
return False

This context manager's __enter__() method will attempt to preserve any previous copy of the named file if it already exists. If it didn't exist, there's nothing to preserve. The file is preserved by simply renaming it to a name such as "file backup.ext".

The __exit__() method will be given information about any exception that occurred in the body of context. If there is no exception, nothing more needs to be done. If there is an exception, then the __exit__() method will try to preserve the output (with a suffix of "error") for debugging purposes. It will also put any previous copy of the file back in place by renaming the backup to the original name.

This is functionally equivalent to a try-except-finally block. However, it has the advantage that it separates the relevant application processing from the context management. The application processing is written in the with statement. The context issues are set aside into a separate class.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset