Skip to content

Commit e3054ac

Browse files
committed
Added new_array_type() function
Allows the creation of a generic array typecaster from Python.
1 parent 6c80519 commit e3054ac

File tree

8 files changed

+75
-4
lines changed

8 files changed

+75
-4
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
What's new in psycopg 2.4.3
22
---------------------------
33

4+
- Added 'new_array_type()' function for easy creation of array
5+
typecasters.
46
- Fixed segfault in case of transaction started with connection lost
57
(and possibly other events).
68
- Rollback connections in transaction or in error before putting them

doc/src/advanced.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ read:
216216
>>> print type(point), point.x, point.y
217217
<class 'Point'> 10.2 20.3
218218

219+
A typecaster created by `!new_type()` can be also used with
220+
`~psycopg2.extensions.new_array_type()` to create a typecaster converting a
221+
PostgreSQL array into a Python list.
219222

220223

221224
.. index::

doc/src/extensions.rst

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ details.
290290
.. function:: new_type(oids, name, adapter)
291291

292292
Create a new type caster to convert from a PostgreSQL type to a Python
293-
object. The created object must be registered using
293+
object. The object created must be registered using
294294
`register_type()` to be used.
295295

296296
:param oids: tuple of OIDs of the PostgreSQL type to convert.
@@ -309,6 +309,23 @@ details.
309309
See :ref:`type-casting-from-sql-to-python` for an usage example.
310310

311311

312+
.. function:: new_array_type(oids, name, base_caster)
313+
314+
Create a new type caster to convert from a PostgreSQL array type to a list
315+
of Python object. The object created must be registered using
316+
`register_type()` to be used.
317+
318+
:param oids: tuple of OIDs of the PostgreSQL type to convert. It should
319+
probably be the oid of the array type (e.g. the ``typarray`` field in
320+
the ``pg_type`` table.
321+
:param name: the name of the new type adapter.
322+
:param base_caster: a Psycopg typecaster, e.g. created using the
323+
`new_type()` function. The caster should be able to parse a single
324+
item of the desired type.
325+
326+
.. versionadded:: 2.4.3
327+
328+
312329
.. function:: register_type(obj [, scope])
313330

314331
Register a type caster created using `new_type()`.

lib/extensions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
pass
5858

5959
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid
60-
from psycopg2._psycopg import string_types, binary_types, new_type, register_type
60+
from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type
6161
from psycopg2._psycopg import ISQLQuote, Notify
6262

6363
from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError

psycopg/psycopgmodule.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
257257
" * `conn_or_curs`: A connection, cursor or None"
258258

259259
#define typecast_from_python_doc \
260-
"new_type(oids, name, adapter) -> new type object\n\n" \
260+
"new_type(oids, name, castobj) -> new type object\n\n" \
261261
"Create a new binding object. The object can be used with the\n" \
262262
"`register_type()` function to bind PostgreSQL objects to python objects.\n\n" \
263263
":Parameters:\n" \
@@ -268,6 +268,15 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
268268
" the string representation returned by PostgreSQL (`!None` if ``NULL``)\n" \
269269
" and ``cur`` is the cursor from which data are read."
270270

271+
#define typecast_array_from_python_doc \
272+
"new_array_type(oids, name, baseobj) -> new type object\n\n" \
273+
"Create a new binding object to parse an array.\n\n" \
274+
"The object can be used with `register_type()`.\n\n" \
275+
":Parameters:\n" \
276+
" * `oids`: Tuple of ``oid`` of the PostgreSQL types to convert.\n" \
277+
" * `name`: Name for the new type\n" \
278+
" * `baseobj`: Adapter to perform type conversion of a single array item."
279+
271280
static void
272281
_psyco_register_type_set(PyObject **dict, PyObject *type)
273282
{
@@ -758,6 +767,8 @@ static PyMethodDef psycopgMethods[] = {
758767
METH_VARARGS, psyco_register_type_doc},
759768
{"new_type", (PyCFunction)typecast_from_python,
760769
METH_VARARGS|METH_KEYWORDS, typecast_from_python_doc},
770+
{"new_array_type", (PyCFunction)typecast_array_from_python,
771+
METH_VARARGS|METH_KEYWORDS, typecast_array_from_python_doc},
761772

762773
{"AsIs", (PyCFunction)psyco_AsIs,
763774
METH_VARARGS, psyco_AsIs_doc},

psycopg/typecast.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,29 @@ typecast_from_python(PyObject *self, PyObject *args, PyObject *keywds)
603603
return typecast_new(name, v, cast, base);
604604
}
605605

606+
PyObject *
607+
typecast_array_from_python(PyObject *self, PyObject *args, PyObject *keywds)
608+
{
609+
PyObject *values, *name = NULL, *base = NULL;
610+
typecastObject *obj = NULL;
611+
612+
static char *kwlist[] = {"values", "name", "baseobj", NULL};
613+
614+
if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!O!O!", kwlist,
615+
&PyTuple_Type, &values,
616+
&Text_Type, &name,
617+
&typecastType, &base)) {
618+
return NULL;
619+
}
620+
621+
if ((obj = (typecastObject *)typecast_new(name, values, NULL, base))) {
622+
obj->ccast = typecast_GENERIC_ARRAY_cast;
623+
obj->pcast = NULL;
624+
}
625+
626+
return (PyObject *)obj;
627+
}
628+
606629
PyObject *
607630
typecast_from_c(typecastObject_initlist *type, PyObject *dict)
608631
{

psycopg/typecast.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@ HIDDEN int typecast_add(PyObject *obj, PyObject *dict, int binary);
7777
/* the C callable typecastObject creator function */
7878
HIDDEN PyObject *typecast_from_c(typecastObject_initlist *type, PyObject *d);
7979

80-
/* the python callable typecast creator function */
80+
/* the python callable typecast creator functions */
8181
HIDDEN PyObject *typecast_from_python(
8282
PyObject *self, PyObject *args, PyObject *keywds);
83+
HIDDEN PyObject *typecast_array_from_python(
84+
PyObject *self, PyObject *args, PyObject *keywds);
8385

8486
/* the function used to dispatch typecasting calls */
8587
HIDDEN PyObject *typecast_cast(

tests/types_basic.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,19 @@ def testNegNumber(self):
285285
l1 = self.execute("select -%s;", (-1L,))
286286
self.assertEqual(1, l1)
287287

288+
def testGenericArray(self):
289+
def caster(s, cur):
290+
if s is None: return "nada"
291+
return int(s) * 2
292+
base = psycopg2.extensions.new_type((23,), "INT4", caster)
293+
array = psycopg2.extensions.new_array_type((1007,), "INT4ARRAY", base)
294+
295+
psycopg2.extensions.register_type(array, self.conn)
296+
a = self.execute("select '{1,2,3}'::int4[]")
297+
self.assertEqual(a, [2,4,6])
298+
a = self.execute("select '{1,2,NULL}'::int4[]")
299+
self.assertEqual(a, [2,4,'nada'])
300+
288301

289302
class AdaptSubclassTest(unittest.TestCase):
290303
def test_adapt_subtype(self):

0 commit comments

Comments
 (0)