Re: flexi adaption/casting scheme

From: Daniele Varrazzo <daniele(dot)varrazzo(at)gmail(dot)com>
To: Ronan Dunklau <rdunklau(at)gmail(dot)com>
Cc: psycopg(at)postgresql(dot)org, Tobias Oberstein <tobias(dot)oberstein(at)gmail(dot)com>
Subject: Re: flexi adaption/casting scheme
Date: 2012-09-21 16:04:01
Message-ID: CA+mi_8bU6y5rEj2k9NfoXqKmbBzQp=f0Um7nVMxuy4X1RQCxTg@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: psycopg

On Fri, Sep 21, 2012 at 2:47 PM, Ronan Dunklau <rdunklau(at)gmail(dot)com> wrote:
>> 1.3
>> composite type => plain Python dict with key/value pairs only
>> for all attributes in the composite type that have non-NULL values
>
> It would be great to have an API to customize the class to instantiate with a
> composite type.
>
> I tried an implementation in the attached patch.
>
> The idea would be to add an optional "ctor" argument, which would be used in
> place of the namedtuple argument. Additionally, an adapter can be
> automatically registered to perform the reverse conversion.

Thank you, I like the idea of customizing the composite caster. But I
don't like very much the proposed way. Oddly enough, just yesterday
I've changed the _ctor: it used to take *args instead of an iterable
of args but I've just recently discovered namedtuple._make which has
the same signature of the basic tuple and can be used to avoid
unpacking. You patch puts it back to "self._ctor = lambda *args:
tuple(args)" and then some.

Your patch passes the arguments to the ctor in the most generic way
(as **kwargs) in order to work with any possible function, but this is
overkilling for the basic use with tuple/namedtuple. And still
wouldn't be enough: you can't make an OrderedDict out of it for
instance, as **kwargs randomizes the order.

I propose the attached diff instead. It makes the CompositeCaster easy
to subclass and exposes part of the machinery used by
register_composite() in order to make the class registrable without
adding new arguments to register_composite() too, which would only
deal with the basic case. Customization is performed overriding the
method "make" which takes the attributes read from the db in input.
Attribute names can be obtained from "self".

For example, to cast composite to dictionaries one can subclass it as:

class DictComposite(psycopg2.extras.CompositeCaster):
def make(self, attrs):
return dict(zip(self.attnames, attrs))

Which would be used as:

# from psql: CREATE TYPE card AS (value int, suit text);

c = DictComposite.from_db('card', cnn)
c.register()

cur.execute("select (8, 'hearts')::card")
cur.fetchone()[0]
{'suit': 'hearts', 'value': 8}

Seems nice, doesn't it?

Tobias: as expected it works ok with composite types, as the original
CompositeCaster does:

# from psql: CREATE TYPE card_back AS (face card, back text);

c2 = DictComposite.from_db('card_back', cnn)
c2.register()

cur.execute("select ((8, 'hearts'), 'blue')::card_back")
cur.fetchone()[0]
{'back': 'blue', 'face': {'suit': 'hearts', 'value': 8}}

if it doesn't work for you, you are probably doing something wrong.

-- Daniele

Attachment Content-Type Size
cust_composite2.patch application/octet-stream 2.1 KB

In response to

Responses

Browse psycopg by date

  From Date Subject
Next Message Ronan Dunklau 2012-09-21 16:42:39 Re: flexi adaption/casting scheme
Previous Message Tobias Oberstein 2012-09-21 15:13:38 Re: flexi adaption/casting scheme