Search code examples
pythonpsycopg2

`mogrify` throws "SystemError: bad argument to internal function" on passing `__slots__` based instance


I defined the below slots based class to satisfy my "mutable namedtuple with default None values" requirement:

class Row:
    __slots__ = tuple(fields)

    def __init__(self):
        for attr in self.__slots__:
            setattr(self, attr, None)
    
    def __setitem__(self, name, value):
        setattr(name, value)

    def __getitem__(self, i):
        return getattr(self, self.__slots__[i])

But when I use its instance for generating query string using cur.mogrify I get

SystemError: ../Objects/tupleobject.c:85: bad argument to internal function

Complete code:

import psycopg2 as pg2

def define_row(fields):
    class Row:
        pass # as mentioned above
    return Row


fields = ("age", "gender")
row = define_row(fields)()
row.age = 33
row.gender = "m"

rows = [row]

cur = pg2.connect(dbname="playground", user="postgres").cursor()
placeholders = "({})".format(",".join(["%s"] * len(fields)))
args_str = ",".join(
    cur.mogrify(placeholders, r).decode("utf-8") for r in rows
)  # Exception on this line
qry = f"INSERT INTO playground({','.join(fields)}) VALUES "
cur.execute(qry + args_str)

mogrify docs do not explicitly mention what the datatype of parameters should be.


Solution

  • The Row class needs to implement a __len__ method:

    class Row:
        ...
        def __len__(self):
            return len(self.__slots__)
    

    cursor.mogrify expects its second argument to be a sequence or mapping, and Python sequences are expected to define __len__.