Question

I wrapped my psycopg2 connection in DB class, and one of the methods is to fetch a cursor. I also added a cursor_factory to its parameters, but when using None as default value, it raises an error.

class DB:

    ... connection code and other stuff ...

    def fetchcursor(self, query, params, in_cursor_factory=None):
        cur = self.conn.cursor(cursor_factory=in_cursor_factory)
        cur.execute(query, params)
        return cur

db = DB()
db.fetchcursor("SELECT * FROM foo", None)

The error I get is:

TypeError: 'NoneType' object is not callable

When I replace None with psycopg2.extras.DictCursor it works. I want to use None as the default, just as the documentation says:

cursor(name=None, cursor_factory=None, scrollable=None, withhold=False)
Was it helpful?

Solution

Indeed, it appears that the connection.cursor() implementation doesn't handle setting the cursor_factory argument to None very well.

The code doesn't check for factory being set to Py_None and calls it regardless. If the keyword argument isn't given connection.cursor_factory is used instead, which is, according to the source, set to extensions.cursor (e.g. psycopg2._psycopg.cursor) by default.

You can replicate this by testing for None and using connection.cursor_factory instead in that case:

def fetchcursor(self, query, params, in_cursor_factory=None):
    if in_cursor_factory is None:
        in_cursor_factory = self.conn.cursor_factory
    cur = self.conn.cursor(cursor_factory=in_cursor_factory)
    cur.execute(query, params)
    return cur

This presumes you are using psycopg version 2.5 or newer; connection.cursor_factory was added in that version.

For previous versions just use psycopg2._psycopg.cursor instead of self.conn.cursor_factory.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top