Give precedence to '__conform__()' over superclasses choosing adapter

Close #456
This commit is contained in:
Daniele Varrazzo 2017-02-05 11:54:50 +01:00
parent 9863637f30
commit 3fbff5d848
3 changed files with 25 additions and 10 deletions

2
NEWS
View File

@ -26,6 +26,8 @@ New features:
- Added `~psycopg2.extensions.quote_ident()` function (:ticket:`#359`). - Added `~psycopg2.extensions.quote_ident()` function (:ticket:`#359`).
- Added `~connection.get_dsn_parameters()` connection method (:ticket:`#364`). - Added `~connection.get_dsn_parameters()` connection method (:ticket:`#364`).
- `~cursor.callproc()` now accepts a dictionary of parameters (:ticket:`#381`). - `~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 - Using Python C API decoding functions and codecs caching for faster
unicode encoding/decoding (:ticket:`#473`). unicode encoding/decoding (:ticket:`#473`).
- `~cursor.executemany()` slowness addressed by - `~cursor.executemany()` slowness addressed by

View File

@ -153,15 +153,6 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
return adapted; 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*/ /* try to have the protocol adapt this object*/
if ((meth = PyObject_GetAttrString(proto, "__adapt__"))) { if ((meth = PyObject_GetAttrString(proto, "__adapt__"))) {
adapted = PyObject_CallFunctionObjArgs(meth, obj, NULL); adapted = PyObject_CallFunctionObjArgs(meth, obj, NULL);
@ -181,7 +172,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
PyErr_Clear(); 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__"))) { if ((meth = PyObject_GetAttrString(obj, "__conform__"))) {
adapted = PyObject_CallFunctionObjArgs(meth, proto, NULL); adapted = PyObject_CallFunctionObjArgs(meth, proto, NULL);
Py_DECREF(meth); Py_DECREF(meth);
@ -200,6 +191,15 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
PyErr_Clear(); 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 */ /* else set the right exception and return NULL */
PyOS_snprintf(buffer, 255, "can't adapt type '%s'", PyOS_snprintf(buffer, 255, "can't adapt type '%s'",
Py_TYPE(obj)->tp_name); Py_TYPE(obj)->tp_name);

View File

@ -422,6 +422,19 @@ class AdaptSubclassTest(unittest.TestCase):
finally: finally:
del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote] 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): class ByteaParserTest(unittest.TestCase):
"""Unit test for our bytea format parser.""" """Unit test for our bytea format parser."""