Persistent, dictionary-like object ensurance This is a part of a project I'm working on. The codebase is Python 2.7 and I don't have an option to switch to Python 3. 

Code:

import shelve
def get_document_info():
    token_shelf = False

    try:
        token_shelf = shelve.open(os.path.join(current_dir, 'document_info.shelve'))
        token_mapping = token_shelf['token_mapping']

        if token_mapping:
            return token_mapping
        else:
            return False

    except Exception, e:
        pass

    finally:
        if token_shelf:
            token_shelf.close()


Shelve is a persistent, dictionary-like object. More info can be found at https://docs.python.org/2/library/shelve.html

I need to ensure that token_shelf is closed at the end. In the beginning of the function, what do I define token_shelf as? If I don't define something there, PyCharm will throw an error saying that the token_shelf in finally block does not exist. 

What's the ideal way to write this piece of code? How can it be made more elegant and Pythonic?

PS: This code is working as expected right now.
Ideally you would want to use Python's with ... as syntax, which creates a context that calls the exit method of whatever object you create. For this, the shelve.Shelf classes would need to implement the dunder methods __enter__ and __exit__ (which they don't).

At this point you basically have three options:


Inherit from them and implement it yourself:




class Shelf(shelve.Shelf):

    def __new__(cls, file_name):
        return shelve.open(file_name)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()



Or just write a simple wrapper class like this:




class Shelf(object):

    def __init__(self, file_name):
        self.file_name = file_name

    def __enter__(self):
        self.obj = shelve.open(self.file_name)
        return self.obj

    def __exit__(self, *args):
        self.obj.close()


Both can be used like this:

import os
import shelve

def get_document_info(current_dir):
    shelf_path = os.path.join(current_dir, 'document_info.shelve')
    with Shelf(shelf_path) as token_shelf:
        return token_shelf.get('token_mapping')


When you use try..except you should always guard against as specific exceptions as possible (so that unexpected exceptions can still rise to the top). In this case this would probably be a KeyError. Here I used the get method instead, which will return None if the key is not found. 

I also removed the return False, because you should check for falsiness in the calling code. In other words, if you are doing if token_mapping is False or  if token_mapping == False in the calling code, you are doing it wrong. Just have the if not token_mapping/if token_mapping there.


Use Python's contextlib.closing, which already implements something like option 2:




from contextlib import closing
import os
import shelve

def get_document_info(current_dir):
    shelf_path = os.path.join(current_dir, 'document_info.shelve')
    with closing(shelve.open(shelf_path)) as token_shelf:
        return token_shelf.get('token_mapping')