mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-26 10:53:44 +03:00
Make Error and subclasses picklable
Useful for multiprocessing interaction. Closes ticket #90.
This commit is contained in:
parent
28f1013c2a
commit
43daba38e7
2
NEWS
2
NEWS
|
@ -5,6 +5,8 @@ What's new in psycopg 2.4.5
|
||||||
(ticket #84).
|
(ticket #84).
|
||||||
- Use lo_creat() instead of lo_create() when possible for better
|
- Use lo_creat() instead of lo_create() when possible for better
|
||||||
interaction with pgpool-II (ticket #88).
|
interaction with pgpool-II (ticket #88).
|
||||||
|
- Error and its subclasses are picklable, useful for multiprocessing
|
||||||
|
interaction (ticket #90).
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.4.4
|
What's new in psycopg 2.4.4
|
||||||
|
|
|
@ -380,7 +380,59 @@ static struct {
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
|
||||||
|
/* Error.__reduce_ex__
|
||||||
|
*
|
||||||
|
* The method is required to make exceptions picklable: set the cursor
|
||||||
|
* attribute to None. Only working from Py 2.5: previous versions
|
||||||
|
* would require implementing __getstate__, and as of 2012 it's a little
|
||||||
|
* bit too late to care. */
|
||||||
|
static PyObject *
|
||||||
|
psyco_error_reduce_ex(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *proto = NULL;
|
||||||
|
PyObject *super = NULL;
|
||||||
|
PyObject *tuple = NULL;
|
||||||
|
PyObject *dict = NULL;
|
||||||
|
PyObject *rv = NULL;
|
||||||
|
|
||||||
|
/* tuple = Exception.__reduce_ex__(self, proto) */
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &proto)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (!(super = PyObject_GetAttrString(PyExc_Exception, "__reduce_ex__"))) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (!(tuple = PyObject_CallFunctionObjArgs(super, self, proto, NULL))) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tuple[2]['cursor'] = None
|
||||||
|
*
|
||||||
|
* If these checks fail, we can still return a valid object. Pickle
|
||||||
|
* will likely fail downstream, but there's nothing else we can do here */
|
||||||
|
if (!PyTuple_Check(tuple)) { goto exit; }
|
||||||
|
if (3 > PyTuple_GET_SIZE(tuple)) { goto exit; }
|
||||||
|
dict = PyTuple_GET_ITEM(tuple, 2); /* borrowed */
|
||||||
|
if (!PyDict_Check(dict)) { goto exit; }
|
||||||
|
|
||||||
|
/* Modify the tuple inplace and return it */
|
||||||
|
if (0 != PyDict_SetItemString(dict, "cursor", Py_None)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
rv = tuple;
|
||||||
|
tuple = NULL;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_XDECREF(tuple);
|
||||||
|
Py_XDECREF(super);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
psyco_errors_init(void)
|
psyco_errors_init(void)
|
||||||
{
|
{
|
||||||
/* the names of the exceptions here reflect the oranization of the
|
/* the names of the exceptions here reflect the oranization of the
|
||||||
|
@ -391,6 +443,11 @@ psyco_errors_init(void)
|
||||||
PyObject *dict;
|
PyObject *dict;
|
||||||
PyObject *base;
|
PyObject *base;
|
||||||
PyObject *str;
|
PyObject *str;
|
||||||
|
PyObject *descr;
|
||||||
|
int rv = -1;
|
||||||
|
|
||||||
|
static PyMethodDef psyco_error_reduce_ex_def =
|
||||||
|
{"__reduce_ex__", psyco_error_reduce_ex, METH_VARARGS, "pickle helper"};
|
||||||
|
|
||||||
for (i=0; exctable[i].name; i++) {
|
for (i=0; exctable[i].name; i++) {
|
||||||
dict = PyDict_New();
|
dict = PyDict_New();
|
||||||
|
@ -420,6 +477,22 @@ psyco_errors_init(void)
|
||||||
PyObject_SetAttrString(Error, "pgerror", Py_None);
|
PyObject_SetAttrString(Error, "pgerror", Py_None);
|
||||||
PyObject_SetAttrString(Error, "pgcode", Py_None);
|
PyObject_SetAttrString(Error, "pgcode", Py_None);
|
||||||
PyObject_SetAttrString(Error, "cursor", Py_None);
|
PyObject_SetAttrString(Error, "cursor", Py_None);
|
||||||
|
|
||||||
|
/* install __reduce_ex__ on Error to make all the subclasses picklable */
|
||||||
|
if (!(descr = PyDescr_NewMethod((PyTypeObject *)Error,
|
||||||
|
&psyco_error_reduce_ex_def))) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (0 != PyObject_SetAttrString(Error,
|
||||||
|
psyco_error_reduce_ex_def.ml_name, descr)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
Py_DECREF(descr);
|
||||||
|
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -869,7 +942,7 @@ INIT_MODULE(_psycopg)(void)
|
||||||
psyco_adapters_init(dict);
|
psyco_adapters_init(dict);
|
||||||
|
|
||||||
/* create a standard set of exceptions and add them to the module's dict */
|
/* create a standard set of exceptions and add them to the module's dict */
|
||||||
psyco_errors_init();
|
if (0 != psyco_errors_init()) { goto exit; }
|
||||||
psyco_errors_fill(dict);
|
psyco_errors_fill(dict);
|
||||||
|
|
||||||
/* Solve win32 build issue about non-constant initializer element */
|
/* Solve win32 build issue about non-constant initializer element */
|
||||||
|
|
|
@ -22,7 +22,8 @@
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
# License for more details.
|
# License for more details.
|
||||||
|
|
||||||
from testutils import unittest
|
from testutils import unittest, skip_before_python
|
||||||
|
from testconfig import dsn
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
|
||||||
|
@ -127,6 +128,38 @@ class ConnectTestCase(unittest.TestCase):
|
||||||
self.assertEqual(self.args[0], r"dbname='\\every thing\''")
|
self.assertEqual(self.args[0], r"dbname='\\every thing\''")
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionsTestCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.conn = psycopg2.connect(dsn)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.conn.close()
|
||||||
|
|
||||||
|
def test_attributes(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
try: cur.execute("select * from nonexist")
|
||||||
|
except psycopg2.Error, e: pass
|
||||||
|
|
||||||
|
self.assertEqual(e.pgcode, '42P01')
|
||||||
|
self.assert_(e.pgerror)
|
||||||
|
self.assert_(e.cursor is cur)
|
||||||
|
|
||||||
|
@skip_before_python(2, 5)
|
||||||
|
def test_pickle(self):
|
||||||
|
import pickle
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
try:
|
||||||
|
cur.execute("select * from nonexist")
|
||||||
|
except psycopg2.Error, e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
e1 = pickle.loads(pickle.dumps(e))
|
||||||
|
|
||||||
|
self.assertEqual(e.pgerror, e1.pgerror)
|
||||||
|
self.assertEqual(e.pgcode, e1.pgcode)
|
||||||
|
self.assert_(e1.cursor is None)
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user