close() methods don't raise errors if called on closed objects

This commit is contained in:
Daniele Varrazzo 2012-03-04 05:03:15 +00:00
parent 2cbedbee45
commit 84f2a370f6
6 changed files with 26 additions and 7 deletions

2
NEWS
View File

@ -1,6 +1,8 @@
What's new in psycopg 2.4.5 What's new in psycopg 2.4.5
--------------------------- ---------------------------
- The close() methods on connections and cursors don't raise exceptions
if called on already closed objects.
- Fixed fetchmany() with no argument in cursor subclasses - Fixed fetchmany() with no argument in cursor subclasses
(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

View File

@ -906,6 +906,10 @@ conn_poll(connectionObject *self)
void void
conn_close(connectionObject *self) conn_close(connectionObject *self)
{ {
if (self->closed) {
return;
}
/* sets this connection as closed even for other threads; also note that /* sets this connection as closed even for other threads; also note that
we need to check the value of pgconn, because we get called even when we need to check the value of pgconn, because we get called even when
the connection fails! */ the connection fails! */
@ -922,14 +926,13 @@ conn_close(connectionObject *self)
* closed only in status CONN_STATUS_READY. * closed only in status CONN_STATUS_READY.
*/ */
if (self->closed == 0) self->closed = 1;
self->closed = 1;
if (self->pgconn) { if (self->pgconn) {
PQfinish(self->pgconn); PQfinish(self->pgconn);
PQfreeCancel(self->cancel);
Dprintf("conn_close: PQfinish called");
self->pgconn = NULL; self->pgconn = NULL;
Dprintf("conn_close: PQfinish called");
PQfreeCancel(self->cancel);
self->cancel = NULL; self->cancel = NULL;
} }

View File

@ -122,8 +122,6 @@ psyco_conn_cursor(connectionObject *self, PyObject *args, PyObject *keywds)
static PyObject * static PyObject *
psyco_conn_close(connectionObject *self, PyObject *args) psyco_conn_close(connectionObject *self, PyObject *args)
{ {
EXC_IF_CONN_CLOSED(self);
Dprintf("psyco_conn_close: closing connection at %p", self); Dprintf("psyco_conn_close: closing connection at %p", self);
conn_close(self); conn_close(self);
Dprintf("psyco_conn_close: connection at %p closed", self); Dprintf("psyco_conn_close: connection at %p closed", self);

View File

@ -52,9 +52,12 @@ extern PyObject *pyPsycopgTzFixedOffsetTimezone;
static PyObject * static PyObject *
psyco_curs_close(cursorObject *self, PyObject *args) psyco_curs_close(cursorObject *self, PyObject *args)
{ {
EXC_IF_CURS_CLOSED(self);
EXC_IF_ASYNC_IN_PROGRESS(self, close); EXC_IF_ASYNC_IN_PROGRESS(self, close);
if (self->closed) {
goto exit;
}
if (self->name != NULL) { if (self->name != NULL) {
char buffer[128]; char buffer[128];
@ -66,6 +69,7 @@ psyco_curs_close(cursorObject *self, PyObject *args)
self->closed = 1; self->closed = 1;
Dprintf("psyco_curs_close: cursor at %p closed", self); Dprintf("psyco_curs_close: cursor at %p closed", self);
exit:
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }

View File

@ -48,6 +48,12 @@ class ConnectionTests(unittest.TestCase):
conn.close() conn.close()
self.assertEqual(conn.closed, True) self.assertEqual(conn.closed, True)
def test_close_idempotent(self):
conn = self.conn
conn.close()
conn.close()
self.assert_(conn.closed)
def test_cursor_closed_attribute(self): def test_cursor_closed_attribute(self):
conn = self.conn conn = self.conn
curs = conn.cursor() curs = conn.cursor()

View File

@ -37,6 +37,12 @@ class CursorTests(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.conn.close() self.conn.close()
def test_close_idempotent(self):
cur = self.conn.cursor()
cur.close()
cur.close()
self.assert_(cur.closed)
def test_empty_query(self): def test_empty_query(self):
cur = self.conn.cursor() cur = self.conn.cursor()
self.assertRaises(psycopg2.ProgrammingError, cur.execute, "") self.assertRaises(psycopg2.ProgrammingError, cur.execute, "")