Re: flexi adaption/casting scheme - Mailing list psycopg

From Daniele Varrazzo
Subject Re: flexi adaption/casting scheme
Date
Msg-id CA+mi_8bU6y5rEj2k9NfoXqKmbBzQp=f0Um7nVMxuy4X1RQCxTg@mail.gmail.com
Whole thread Raw
In response to Re: flexi adaption/casting scheme  (Ronan Dunklau <rdunklau@gmail.com>)
Responses Re: flexi adaption/casting scheme
Re: flexi adaption/casting scheme
List psycopg
On Fri, Sep 21, 2012 at 2:47 PM, Ronan Dunklau <rdunklau@gmail.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

psycopg by date:

Previous
From: Tobias Oberstein
Date:
Subject: Re: flexi adaption/casting scheme
Next
From: Ronan Dunklau
Date:
Subject: Re: flexi adaption/casting scheme