3
\$\begingroup\$

I'm working on a simple decorator that creates and manages a database cursor. The goal is to 'inject' the cursor as the first argument of the function, but not expose it to the caller.

Usage:

import psycopg2

@needsNewCursor
def findItem(cursor, item_id):
    cursor.execute('SELECT name FROM items WHERE id = %s', [item_id])
    item_result = cursor.fetchone()
    # ...
    return item

# caller only needs to know about the ID
findItem(1234)

Decorator:

def needsNewCursor(func):
    def decorator(*args, **kargs):
        print 'Creating new cursor'
        # This is slightly different in the final code, but I'm only
        # concerned about how I'm dealing with decorators, at the moment
        cursor = createDefaultCursor()
        if cursor:
            with cursor:
                # Pass the cursor in as the 1st param
                return func(cursor, *args, **kargs)

        # Give the function a chance to deal with fails
        return func(None, *args, **kargs)

    # QUESTION: Is this enough?
    decorator.__name__ = func.__name__
    return decorator

Is this a good approach?

\$\endgroup\$
5
\$\begingroup\$

No, I don't think this is a good approach. Anyone who sees the declaration def findItem(cursor, item_id): will call the function with two arguments and scratch one's head to no end when it does not work.

You could just as well have cursor = createDefaultCursor() as the first line of each function that uses a cursor. The number of lines does not increase as you can omit the @needsNewCursor line.

Even better might be to create a context manager to deal with opening and closing a cursor and start each function with a statement like with new_cursor() as cursor:. A context manager also has an opportunity to deal with exceptions raised in the with block.

Anyway, when writing decorators, instead of assigning yourself decorator.__name__ = func.__name__ you can apply the functools.wraps decorator that also copies the docstring.

One more thing: in your example needsNewCursor is a decorator. You should choose a different name for the inner function.

\$\endgroup\$
  • \$\begingroup\$ Thanks a lot, really like your ideas! :) I will switch to your approach. Really like the context manager idea! thanks \$\endgroup\$ – DiogoNeves Nov 1 '13 at 21:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.