mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-11 03:26:37 +03:00
Use the adapter of an object superclass if available.
This commit is contained in:
parent
225b276de5
commit
62d3a1533b
|
@ -4,6 +4,9 @@
|
|||
|
||||
* psycopg/microprotocols.c: fixed refcount bug.
|
||||
|
||||
* psycopg/microprotocols.c: use the adapter of an object superclass if
|
||||
available.
|
||||
|
||||
2010-11-06 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||
|
||||
* lib/extras.py: added NamedTupleCursor.
|
||||
|
|
1
NEWS-2.3
1
NEWS-2.3
|
@ -14,6 +14,7 @@ psycopg 2.3 aims to expose some new features introduced in PostgreSQL 9.0.
|
|||
* Other features and changes:
|
||||
|
||||
- `mogrify()` now supports unicode queries.
|
||||
- subclasses of a type that can be adapted are adapted as the superclass.
|
||||
- `errorcodes` knows a couple of new codes introduced in PostgreSQL 9.0.
|
||||
- Dropped deprecated Psycopg "own quoting".
|
||||
- Never issue a ROLLBACK on close/GC. This behaviour was introduced as a bug
|
||||
|
|
|
@ -75,12 +75,61 @@ microprotocols_add(PyTypeObject *type, PyObject *proto, PyObject *cast)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Check if one of `obj` superclasses has an adapter for `proto`.
|
||||
*
|
||||
* If it does, return a *borrowed reference* to the adapter, else NULL.
|
||||
*/
|
||||
static PyObject *
|
||||
_get_superclass_adapter(PyObject *obj, PyObject *proto)
|
||||
{
|
||||
PyTypeObject *type;
|
||||
PyObject *mro, *st;
|
||||
PyObject *key, *adapter;
|
||||
Py_ssize_t i, ii;
|
||||
|
||||
type = (PyTypeObject *)Py_TYPE(obj);
|
||||
if (!(Py_TPFLAGS_HAVE_CLASS & type->tp_flags)) {
|
||||
/* has no mro */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Walk the mro from the most specific subclass. */
|
||||
mro = type->tp_mro;
|
||||
for (i = 1, ii = PyTuple_GET_SIZE(mro); i < ii; ++i) {
|
||||
st = PyTuple_GET_ITEM(mro, i);
|
||||
key = PyTuple_Pack(2, st, proto);
|
||||
adapter = PyDict_GetItem(psyco_adapters, key);
|
||||
Py_DECREF(key);
|
||||
|
||||
if (adapter) {
|
||||
Dprintf(
|
||||
"microprotocols_adapt: using '%s' adapter to adapt '%s'",
|
||||
((PyTypeObject *)st)->tp_name, type->tp_name);
|
||||
|
||||
/* register this adapter as good for the subclass too,
|
||||
* so that the next time it will be found in the fast path */
|
||||
|
||||
/* Well, no, maybe this is not a good idea.
|
||||
* It would become a leak in case of dynamic
|
||||
* classes generated in a loop (think namedtuples). */
|
||||
|
||||
/* key = PyTuple_Pack(2, (PyObject*)type, proto);
|
||||
* PyDict_SetItem(psyco_adapters, key, adapter);
|
||||
* Py_DECREF(key);
|
||||
*/
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* microprotocols_adapt - adapt an object to the built-in protocol */
|
||||
|
||||
PyObject *
|
||||
microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
|
||||
{
|
||||
PyObject *adapter, *key;
|
||||
PyObject *adapter, *adapted, *key;
|
||||
char buffer[256];
|
||||
|
||||
/* we don't check for exact type conformance as specified in PEP 246
|
||||
|
@ -99,13 +148,19 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
|
|||
adapter = PyDict_GetItem(psyco_adapters, key);
|
||||
Py_DECREF(key);
|
||||
if (adapter) {
|
||||
PyObject *adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
|
||||
adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
|
||||
return adapted;
|
||||
}
|
||||
|
||||
/* Check if a superclass can be adapted and use the same adapter. */
|
||||
if (NULL != (adapter = _get_superclass_adapter(obj, proto))) {
|
||||
adapted = PyObject_CallFunctionObjArgs(adapter, obj, NULL);
|
||||
return adapted;
|
||||
}
|
||||
|
||||
/* try to have the protocol adapt this object*/
|
||||
if (PyObject_HasAttrString(proto, "__adapt__")) {
|
||||
PyObject *adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj);
|
||||
adapted = PyObject_CallMethod(proto, "__adapt__", "O", obj);
|
||||
if (adapted && adapted != Py_None) return adapted;
|
||||
Py_XDECREF(adapted);
|
||||
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
|
||||
|
@ -114,7 +169,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
|
|||
|
||||
/* and finally try to have the object adapt itself */
|
||||
if (PyObject_HasAttrString(obj, "__conform__")) {
|
||||
PyObject *adapted = PyObject_CallMethod(obj, "__conform__","O", proto);
|
||||
adapted = PyObject_CallMethod(obj, "__conform__","O", proto);
|
||||
if (adapted && adapted != Py_None) return adapted;
|
||||
Py_XDECREF(adapted);
|
||||
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
|
||||
|
|
|
@ -129,6 +129,35 @@ class TypesBasicTests(unittest.TestCase):
|
|||
o2 = self.execute("select %s;", (o1,))
|
||||
self.assertEqual(type(o1[0]), type(o2[0]))
|
||||
|
||||
|
||||
class AdaptSubclassTest(unittest.TestCase):
|
||||
def test_adapt_subtype(self):
|
||||
from psycopg2.extensions import adapt
|
||||
class Sub(str): pass
|
||||
s1 = "hel'lo"
|
||||
s2 = Sub(s1)
|
||||
self.assertEqual(adapt(s1).getquoted(), adapt(s2).getquoted())
|
||||
|
||||
def test_adapt_most_specific(self):
|
||||
from psycopg2.extensions import adapt, register_adapter, AsIs
|
||||
|
||||
class A(object): pass
|
||||
class B(A): pass
|
||||
class C(B): pass
|
||||
|
||||
register_adapter(A, lambda a: AsIs("a"))
|
||||
register_adapter(B, lambda b: AsIs("b"))
|
||||
self.assertEqual('b', adapt(C()).getquoted())
|
||||
|
||||
def test_no_mro_no_joy(self):
|
||||
from psycopg2.extensions import adapt, register_adapter, AsIs
|
||||
|
||||
class A: pass
|
||||
class B(A): pass
|
||||
|
||||
register_adapter(A, lambda a: AsIs("a"))
|
||||
self.assertRaises(psycopg2.ProgrammingError, adapt, B())
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user