From 9ffa1f4b596ec70f1227c7b91db0d4440828c088 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Thu, 23 Feb 2012 23:35:05 +0000 Subject: [PATCH 1/4] Fixed inet test that wasn't testing anything --- tests/test_types_extras.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_types_extras.py b/tests/test_types_extras.py index 29a935ee..156a2159 100755 --- a/tests/test_types_extras.py +++ b/tests/test_types_extras.py @@ -82,9 +82,9 @@ class TypesExtrasTests(unittest.TestCase): def testINET(self): psycopg2.extras.register_inet() - i = "192.168.1.0/24"; + i = psycopg2.extras.Inet("192.168.1.0/24") s = self.execute("SELECT %s AS foo", (i,)) - self.failUnless(i == s) + self.failUnless(i.addr == s.addr) # must survive NULL cast to inet s = self.execute("SELECT NULL::inet AS foo") self.failUnless(s is None) From 36b6c80ed1b3bcea78b52cd88050a905927c35b3 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Thu, 23 Feb 2012 23:51:28 +0000 Subject: [PATCH 2/4] register_uuid takes more iterables types as oids argument Also added docs for the function parameters. --- lib/extras.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/extras.py b/lib/extras.py index 870b5ca7..738af7d2 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -455,14 +455,21 @@ class UUID_adapter(object): __str__ = getquoted def register_uuid(oids=None, conn_or_curs=None): - """Create the UUID type and an uuid.UUID adapter.""" + """Create the UUID type and an uuid.UUID adapter. + + :param oids: oid for the PostgreSQL :sql:`uuid` type, or 2-items sequence + with oids of the type and the array. If not specified, use PostgreSQL + standard oids. + :param conn_or_curs: where to register the typecaster. If not specified, + register it globally. + """ import uuid if not oids: oid1 = 2950 oid2 = 2951 - elif type(oids) == list: + elif isinstance(oids, (list, tuple)): oid1, oid2 = oids else: oid1 = oids From 0c337a20290a766c13c0c0674c2ab079be5f6768 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Thu, 23 Feb 2012 23:56:55 +0000 Subject: [PATCH 3/4] Added support for inet array --- NEWS | 1 + doc/src/extras.rst | 3 ++- lib/extras.py | 31 +++++++++++++++++++++++++------ tests/test_types_extras.py | 9 +++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index 5149cf74..cfb88d9b 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ What's new in psycopg 2.4.5 to Menno Smits (tickets #94, #95). - Fixed 'rownumber' during iteration on cursor subclasses. Regression introduced in 2.4.4 (ticket #100). + - Added support for 'inet' arrays. What's new in psycopg 2.4.4 diff --git a/doc/src/extras.rst b/doc/src/extras.rst index 710e9b5c..1e0f12f2 100644 --- a/doc/src/extras.rst +++ b/doc/src/extras.rst @@ -250,6 +250,7 @@ UUID data type ^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 2.0.9 +.. versionchanged:: 2.4.5 added inet array support. .. doctest:: @@ -264,7 +265,7 @@ UUID data type '192.168.0.1/24' -.. autofunction:: register_inet() +.. autofunction:: register_inet .. autoclass:: Inet diff --git a/lib/extras.py b/lib/extras.py index 738af7d2..f53561d2 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -498,13 +498,13 @@ class Inet(object): """ def __init__(self, addr): self.addr = addr - + def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.addr) def prepare(self, conn): self._conn = conn - + def getquoted(self): obj = _A(self.addr) if hasattr(obj, 'prepare'): @@ -517,13 +517,32 @@ class Inet(object): def __str__(self): return str(self.addr) - + def register_inet(oid=None, conn_or_curs=None): - """Create the INET type and an Inet adapter.""" - if not oid: oid = 869 - _ext.INET = _ext.new_type((oid, ), "INET", + """Create the INET type and an Inet adapter. + + :param oid: oid for the PostgreSQL :sql:`inet` type, or 2-items sequence + with oids of the type and the array. If not specified, use PostgreSQL + standard oids. + :param conn_or_curs: where to register the typecaster. If not specified, + register it globally. + """ + if not oid: + oid1 = 869 + oid2 = 1041 + elif isinstance(oid, (list, tuple)): + oid1, oid2 = oid + else: + oid1 = oid + oid2 = 1041 + + _ext.INET = _ext.new_type((oid1, ), "INET", lambda data, cursor: data and Inet(data) or None) + _ext.INETARRAY = _ext.new_array_type((oid2, ), "INETARRAY", _ext.INET) + _ext.register_type(_ext.INET, conn_or_curs) + _ext.register_type(_ext.INETARRAY, conn_or_curs) + return _ext.INET diff --git a/tests/test_types_extras.py b/tests/test_types_extras.py index 156a2159..36dd77ed 100755 --- a/tests/test_types_extras.py +++ b/tests/test_types_extras.py @@ -89,6 +89,15 @@ class TypesExtrasTests(unittest.TestCase): s = self.execute("SELECT NULL::inet AS foo") self.failUnless(s is None) + def testINETARRAY(self): + psycopg2.extras.register_inet() + i = psycopg2.extras.Inet("192.168.1.0/24") + s = self.execute("SELECT %s AS foo", ([i],)) + self.failUnless(i.addr == s[0].addr) + # must survive NULL cast to inet + s = self.execute("SELECT NULL::inet[] AS foo") + self.failUnless(s is None) + def test_inet_conform(self): from psycopg2.extras import Inet i = Inet("192.168.1.0/24") From a165f861270994da0435e42e568f8e4327bd8251 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Fri, 24 Feb 2012 00:33:28 +0000 Subject: [PATCH 4/4] Added docs about how to create a generic array typecaster --- doc/src/extensions.rst | 14 ++++++++++++++ doc/src/faq.rst | 7 +++++++ doc/src/usage.rst | 10 +++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/src/extensions.rst b/doc/src/extensions.rst index 1ad88eda..820468dd 100644 --- a/doc/src/extensions.rst +++ b/doc/src/extensions.rst @@ -325,6 +325,20 @@ details. .. versionadded:: 2.4.3 + .. _cast-array-unknown: + + .. note:: + + The function can be used to create a generic array typecaster, + returning a list of strings: just use the `~psycopg2.STRING` as base + typecaster. For instance, if you want to receive from the database an + array of :sql:`macaddr`, each address represented by string, you can + use:: + + psycopg2.extensions.register_type( + psycopg2.extensions.new_array_type( + (1040,), 'MACADDR[]', psycopg2.STRING)) + .. function:: register_type(obj [, scope]) diff --git a/doc/src/faq.rst b/doc/src/faq.rst index 3296cd84..8afc478f 100644 --- a/doc/src/faq.rst +++ b/doc/src/faq.rst @@ -109,6 +109,13 @@ Transferring binary data from PostgreSQL 9.0 doesn't work. .. __: http://www.postgresql.org/docs/9.0/static/datatype-binary.html .. __: http://www.postgresql.org/docs/9.0/static/runtime-config-client.html#GUC-BYTEA-OUTPUT +Arrays of *TYPE* are not casted to list. + Arrays are only casted to list when their oid is known, and an array + typecaster is registered for them. If there is no typecaster, the array is + returned unparsed from PostgreSQL (e.g. ``{a,b,c}``). It is easy to create + a generic arrays typecaster, returning a list of array: an example is + provided in the `~psycopg2.extensions.new_array_type()` documentation. + Best practices -------------- diff --git a/doc/src/usage.rst b/doc/src/usage.rst index d480adef..a1b51de7 100644 --- a/doc/src/usage.rst +++ b/doc/src/usage.rst @@ -294,7 +294,7 @@ the SQL string that would be sent to the database. `bytea_output`__ configuration parameter to ``escape``, either in the server configuration file or in the client session (using a query such as ``SET bytea_output TO escape;``) before receiving binary data. - + .. __: http://www.postgresql.org/docs/9.0/static/datatype-binary.html .. __: http://www.postgresql.org/docs/9.0/static/runtime-config-client.html#GUC-BYTEA-OUTPUT @@ -334,6 +334,14 @@ the SQL string that would be sent to the database. >>> cur.mogrify("SELECT %s;", ([10, 20, 30], )) 'SELECT ARRAY[10, 20, 30];' + .. note:: + + Reading back from PostgreSQL, arrays are converted to list of Python + objects as expected, but only if the types are known one. Arrays of + unknown types are returned as represented by the database (e.g. + ``{a,b,c}``). You can easily create a typecaster for :ref:`array of + unknown types `. + .. _adapt-tuple: .. index::