mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-23 01:16:34 +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: fixed refcount bug.
|
||||||
|
|
||||||
|
* psycopg/microprotocols.c: use the adapter of an object superclass if
|
||||||
|
available.
|
||||||
|
|
||||||
2010-11-06 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
2010-11-06 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||||
|
|
||||||
* lib/extras.py: added NamedTupleCursor.
|
* 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:
|
* Other features and changes:
|
||||||
|
|
||||||
- `mogrify()` now supports unicode queries.
|
- `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.
|
- `errorcodes` knows a couple of new codes introduced in PostgreSQL 9.0.
|
||||||
- Dropped deprecated Psycopg "own quoting".
|
- Dropped deprecated Psycopg "own quoting".
|
||||||
- Never issue a ROLLBACK on close/GC. This behaviour was introduced as a bug
|
- 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;
|
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 */
|
/* microprotocols_adapt - adapt an object to the built-in protocol */
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
|
microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
|
||||||
{
|
{
|
||||||
PyObject *adapter, *key;
|
PyObject *adapter, *adapted, *key;
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
|
|
||||||
/* we don't check for exact type conformance as specified in PEP 246
|
/* 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);
|
adapter = PyDict_GetItem(psyco_adapters, key);
|
||||||
Py_DECREF(key);
|
Py_DECREF(key);
|
||||||
if (adapter) {
|
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;
|
return adapted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* try to have the protocol adapt this object*/
|
/* try to have the protocol adapt this object*/
|
||||||
if (PyObject_HasAttrString(proto, "__adapt__")) {
|
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;
|
if (adapted && adapted != Py_None) return adapted;
|
||||||
Py_XDECREF(adapted);
|
Py_XDECREF(adapted);
|
||||||
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
|
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 */
|
/* and finally try to have the object adapt itself */
|
||||||
if (PyObject_HasAttrString(obj, "__conform__")) {
|
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;
|
if (adapted && adapted != Py_None) return adapted;
|
||||||
Py_XDECREF(adapted);
|
Py_XDECREF(adapted);
|
||||||
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
|
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
|
||||||
|
|
|
@ -129,6 +129,35 @@ class TypesBasicTests(unittest.TestCase):
|
||||||
o2 = self.execute("select %s;", (o1,))
|
o2 = self.execute("select %s;", (o1,))
|
||||||
self.assertEqual(type(o1[0]), type(o2[0]))
|
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():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user