summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--psycopg/microprotocols.c20
-rwxr-xr-xtests/test_types_basic.py13
3 files changed, 25 insertions, 10 deletions
diff --git a/NEWS b/NEWS
index 499656d..8fc1714 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,8 @@ New features:
- Added `~psycopg2.extensions.quote_ident()` function (:ticket:`#359`).
- Added `~connection.get_dsn_parameters()` connection method (:ticket:`#364`).
- `~cursor.callproc()` now accepts a dictionary of parameters (:ticket:`#381`).
+- Give precedence to `!__conform__()` over superclasses to choose an object
+ adapter (:ticket:`#456`).
- Using Python C API decoding functions and codecs caching for faster
unicode encoding/decoding (:ticket:`#473`).
- `~cursor.executemany()` slowness addressed by
diff --git a/psycopg/microprotocols.c b/psycopg/microprotocols.c
index 3ddcc48..0e74cee 100644
--- a/psycopg/microprotocols.c
+++ b/psycopg/microprotocols.c
@@ -153,15 +153,6 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
return adapted;
}
- /* Check if a superclass can be adapted and use the same adapter. */
- if (!(adapter = _get_superclass_adapter(obj, proto))) {
- return NULL;
- }
- if (Py_None != adapter) {
- adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
- return adapted;
- }
-
/* try to have the protocol adapt this object*/
if ((meth = PyObject_GetAttrString(proto, "__adapt__"))) {
adapted = PyObject_CallFunctionObjArgs(meth, obj, NULL);
@@ -181,7 +172,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
PyErr_Clear();
}
- /* and finally try to have the object adapt itself */
+ /* then try to have the object adapt itself */
if ((meth = PyObject_GetAttrString(obj, "__conform__"))) {
adapted = PyObject_CallFunctionObjArgs(meth, proto, NULL);
Py_DECREF(meth);
@@ -200,6 +191,15 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
PyErr_Clear();
}
+ /* Finally check if a superclass can be adapted and use the same adapter. */
+ if (!(adapter = _get_superclass_adapter(obj, proto))) {
+ return NULL;
+ }
+ if (Py_None != adapter) {
+ adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
+ return adapted;
+ }
+
/* else set the right exception and return NULL */
PyOS_snprintf(buffer, 255, "can't adapt type '%s'",
Py_TYPE(obj)->tp_name);
diff --git a/tests/test_types_basic.py b/tests/test_types_basic.py
index bee23d5..614d4fd 100755
--- a/tests/test_types_basic.py
+++ b/tests/test_types_basic.py
@@ -422,6 +422,19 @@ class AdaptSubclassTest(unittest.TestCase):
finally:
del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
+ def test_conform_subclass_precedence(self):
+
+ import psycopg2.extensions as ext
+
+ class foo(tuple):
+ def __conform__(self, proto):
+ return self
+
+ def getquoted(self):
+ return 'bar'
+
+ self.assertEqual(ext.adapt(foo((1, 2, 3))).getquoted(), 'bar')
+
class ByteaParserTest(unittest.TestCase):
"""Unit test for our bytea format parser."""