Title: [964] trunk: Graceful exit of DB destructor on closed connection
Revision
964
Author
cito
Date
2019-01-05 08:51:23 -0500 (Sat, 05 Jan 2019)

Log Message

Graceful exit of DB destructor on closed connection

Also, in the 5.1 branch, the DB wrapper can now be closed
(without closing the underlying connection) and reopened
(reusing the same connection).

This fixed GitHub issue #11

Modified Paths

Diff

Modified: branches/5.0.x/docs/contents/changelog.rst (963 => 964)


--- branches/5.0.x/docs/contents/changelog.rst	2019-01-05 11:59:18 UTC (rev 963)
+++ branches/5.0.x/docs/contents/changelog.rst	2019-01-05 13:51:23 UTC (rev 964)
@@ -5,6 +5,8 @@
 -------------------------
 - This version officially supports the new PostgreSQL 11.
 - Fixed a bug in parsing array subscript ranges (reported by Justin Pryzby).
+- Fixed an issue when deleting a DB wrapper object with the underlying
+  connection already closed (bug report by Jacob Champion).
 
 Vesion 5.0.6 (2018-07-29)
 -------------------------

Modified: branches/5.0.x/pg.py (963 => 964)


--- branches/5.0.x/pg.py	2019-01-05 11:59:18 UTC (rev 963)
+++ branches/5.0.x/pg.py	2019-01-05 13:51:23 UTC (rev 964)
@@ -1572,9 +1572,15 @@
         except AttributeError:
             db = None
         if db:
-            db.set_cast_hook(None)
+            try:
+                db.set_cast_hook(None)
+            except TypeError:
+                pass  # probably already closed
             if self._closeable:
-                db.close()
+                try:
+                    db.close()
+                except InternalError:
+                    pass  # probably already closed
 
     # Auxiliary methods
 
@@ -1631,7 +1637,10 @@
         # Wraps shared library function so we can track state.
         if self._closeable:
             if self.db:
-                self.db.set_cast_hook(None)
+                try:
+                    self.db.set_cast_hook(None)
+                except TypeError:
+                    pass  # probably already closed
                 self.db.close()
                 self.db = None
             else:
@@ -1662,6 +1671,7 @@
             if self.db:
                 self.db.set_cast_hook(None)
                 self.db.close()
+            db.set_cast_hook(self.dbtypes.typecast)
             self.db = db
 
     def begin(self, mode=None):

Modified: branches/5.0.x/tests/test_classic_dbwrapper.py (963 => 964)


--- branches/5.0.x/tests/test_classic_dbwrapper.py	2019-01-05 11:59:18 UTC (rev 963)
+++ branches/5.0.x/tests/test_classic_dbwrapper.py	2019-01-05 13:51:23 UTC (rev 964)
@@ -374,26 +374,42 @@
 
     def testExistingConnection(self):
         db = pg.DB(self.db.db)
+        self.assertIsNotNone(db.db)
         self.assertEqual(self.db.db, db.db)
-        self.assertTrue(db.db)
         db.close()
-        self.assertTrue(db.db)
+        self.assertIsNotNone(db.db)
+        self.assertIsNotNone(self.db.db)
         db.reopen()
-        self.assertTrue(db.db)
+        self.assertIsNotNone(db.db)
+        self.assertEqual(self.db.db, db.db)
         db.close()
-        self.assertTrue(db.db)
+        self.assertIsNotNone(db.db)
         db = pg.DB(self.db)
         self.assertEqual(self.db.db, db.db)
         db = pg.DB(db=self.db.db)
         self.assertEqual(self.db.db, db.db)
 
-        class DB2:
-            pass
+    def testExistingDbApi2Connection(self):
 
-        db2 = DB2()
-        db2._cnx = self.db.db
+        class DBApi2Con:
+
+            def __init__(self, cnx):
+                self._cnx = cnx
+
+            def close(self):
+                self._cnx.close()
+
+        db2 = DBApi2Con(self.db.db)
         db = pg.DB(db2)
         self.assertEqual(self.db.db, db.db)
+        db.close()
+        self.assertIsNotNone(db.db)
+        db.reopen()
+        self.assertIsNotNone(db.db)
+        self.assertEqual(self.db.db, db.db)
+        db.close()
+        self.assertIsNotNone(db.db)
+        db2.close()
 
 
 class TestDBClass(unittest.TestCase):

Modified: trunk/docs/contents/changelog.rst (963 => 964)


--- trunk/docs/contents/changelog.rst	2019-01-05 11:59:18 UTC (rev 963)
+++ trunk/docs/contents/changelog.rst	2019-01-05 13:51:23 UTC (rev 964)
@@ -3,12 +3,15 @@
 
 Version 5.1 (2019-mm-dd)
 ------------------------
-- ...
+- DB wrapper objects based on existing connections can not be closed and
+  reopened properly (but the underlying connection will not be affected).
 
 Vesion 5.0.7 (2019-mm-dd)
 -------------------------
 - This version officially supports the new PostgreSQL 11.
 - Fixed a bug in parsing array subscript ranges (reported by Justin Pryzby).
+- Fixed an issue when deleting a DB wrapper object with the underlying
+  connection already closed (bug report by Jacob Champion).
 
 Vesion 5.0.6 (2018-07-29)
 -------------------------

Modified: trunk/pg.py (963 => 964)


--- trunk/pg.py	2019-01-05 11:59:18 UTC (rev 963)
+++ trunk/pg.py	2019-01-05 13:51:23 UTC (rev 964)
@@ -1502,8 +1502,10 @@
                     pass
         if not db or not hasattr(db, 'db') or not hasattr(db, 'query'):
             db = connect(*args, **kw)
+            self._db_args = args, kw
             self._closeable = True
         else:
+            self._db_args = db
             self._closeable = False
         self.db = db
         self.dbname = db.db
@@ -1511,7 +1513,6 @@
         self._attnames = {}
         self._pkeys = {}
         self._privileges = {}
-        self._args = args, kw
         self.adapter = Adapter(self)
         self.dbtypes = DbTypes(self)
         if db.server_version < 80400:
@@ -1572,9 +1573,15 @@
         except AttributeError:
             db = None
         if db:
-            db.set_cast_hook(None)
+            try:
+                db.set_cast_hook(None)
+            except TypeError:
+                pass  # probably already closed
             if self._closeable:
-                db.close()
+                try:
+                    db.close()
+                except InternalError:
+                    pass  # probably already closed
 
     # Auxiliary methods
 
@@ -1629,13 +1636,17 @@
     def close(self):
         """Close the database connection."""
         # Wraps shared library function so we can track state.
-        if self._closeable:
-            if self.db:
-                self.db.set_cast_hook(None)
-                self.db.close()
-                self.db = None
-            else:
-                raise _int_error('Connection already closed')
+        db = self.db
+        if db:
+            try:
+                db.set_cast_hook(None)
+            except TypeError:
+                pass  # probably already closed
+            if self._closeable:
+                db.close()
+            self.db = None
+        else:
+            raise _int_error('Connection already closed')
 
     def reset(self):
         """Reset connection with current parameters.
@@ -1658,11 +1669,14 @@
         """
         # There is no such shared library function.
         if self._closeable:
-            db = connect(*self._args[0], **self._args[1])
+            db = connect(*self._db_args[0], **self._db_args[1])
             if self.db:
                 self.db.set_cast_hook(None)
                 self.db.close()
+            db.set_cast_hook(self.dbtypes.typecast)
             self.db = db
+        else:
+            self.db = self._db_args
 
     def begin(self, mode=None):
         """Begin a transaction."""

Modified: trunk/tests/test_classic_dbwrapper.py (963 => 964)


--- trunk/tests/test_classic_dbwrapper.py	2019-01-05 11:59:18 UTC (rev 963)
+++ trunk/tests/test_classic_dbwrapper.py	2019-01-05 13:51:23 UTC (rev 964)
@@ -375,26 +375,42 @@
 
     def testExistingConnection(self):
         db = pg.DB(self.db.db)
+        self.assertIsNotNone(db.db)
         self.assertEqual(self.db.db, db.db)
-        self.assertTrue(db.db)
         db.close()
-        self.assertTrue(db.db)
+        self.assertIsNone(db.db)
+        self.assertIsNotNone(self.db.db)
         db.reopen()
-        self.assertTrue(db.db)
+        self.assertIsNotNone(db.db)
+        self.assertEqual(self.db.db, db.db)
         db.close()
-        self.assertTrue(db.db)
+        self.assertIsNone(db.db)
         db = pg.DB(self.db)
         self.assertEqual(self.db.db, db.db)
         db = pg.DB(db=self.db.db)
         self.assertEqual(self.db.db, db.db)
 
-        class DB2:
-            pass
+    def testExistingDbApi2Connection(self):
 
-        db2 = DB2()
-        db2._cnx = self.db.db
+        class DBApi2Con:
+
+            def __init__(self, cnx):
+                self._cnx = cnx
+
+            def close(self):
+                self._cnx.close()
+
+        db2 = DBApi2Con(self.db.db)
         db = pg.DB(db2)
         self.assertEqual(self.db.db, db.db)
+        db.close()
+        self.assertIsNone(db.db)
+        db.reopen()
+        self.assertIsNotNone(db.db)
+        self.assertEqual(self.db.db, db.db)
+        db.close()
+        self.assertIsNone(db.db)
+        db2.close()
 
 
 class TestDBClass(unittest.TestCase):
_______________________________________________
PyGreSQL mailing list
PyGreSQL@Vex.Net
https://mail.vex.net/mailman/listinfo/pygresql

Reply via email to