Repository: Pylons/pyramid_zodbconn

Issue:
Use the transaction manager defined by the request.
https://github.com/Pylons/pyramid_zodbconn/blob/2bce04378e5d98e68cb49b3e4be47fe9e15a6ce1/pyramid_zodbconn/__init__.py#L49

This should be `primary_db.open(request.tm)` to  support the request's transaction manager from pyramid_tm. Currently the pyramid scaffold is broken without this and it'd be better to improve this than to revert the scaffold - however I cannot cut releases of this package.

Discussion:

Patch:
diff --git a/CHANGES.txt b/CHANGES.txt
index 52d8686..2d15203 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,12 @@
 0.8 (unreleased)
 ----------------
 
+- Open primary database using ``request.tm``, if present, as the transaction
+  manager.  If not present, fall back to the default / global transaction
+  manager.  Compatibility with ``pyramid_tm >= `0.11`, which allowed the
+  user to specify an explicit per-request transaction factory.
+  https://github.com/Pylons/pyramid_zodbconn/issues/6.
+
 - Add support for Python 3.5 and 3.6.
 
 - Drop support for Python 2.6, 3.2, and 3.3.
diff --git a/pyramid_zodbconn/__init__.py b/pyramid_zodbconn/__init__.py
index 4e96b6f..9c89ac3 100644
--- a/pyramid_zodbconn/__init__.py
+++ b/pyramid_zodbconn/__init__.py
@@ -11,7 +11,7 @@ from .compat import text_
 def get_connection(request, dbname=None):
     """
     ``request`` must be a Pyramid request object.
-    
+
     When called with no ``dbname`` argument or a ``dbname`` argument of
     ``None``, return a connection to the primary datbase (the database set
     up as ``zodbconn.uri`` in the current configuration).
@@ -46,7 +46,8 @@ def get_connection(request, dbname=None):
             raise ConfigurationError(
                 'No zodbconn.uri defined in Pyramid settings')
 
-        primary_conn = primary_db.open()
+        tm = getattr(request, 'tm', None)
+        primary_conn = primary_db.open(transaction_manager=tm)
 
         registry.notify(ZODBConnectionOpened(primary_conn, request))
 
@@ -62,7 +63,7 @@ def get_connection(request, dbname=None):
 
     if dbname is None:
         return primary_conn
-    
+
     try:
         conn = primary_conn.get_connection(dbname)
     except KeyError:


Test patch:
diff --git a/pyramid_zodbconn/tests/test_init.py b/pyramid_zodbconn/tests/test_init.py
index 901fdeb..2fb0538 100644
--- a/pyramid_zodbconn/tests/test_init.py
+++ b/pyramid_zodbconn/tests/test_init.py
@@ -16,7 +16,7 @@ class Test_get_connection(unittest.TestCase):
         request = self._makeRequest()
         del request.registry._zodb_databases
         self.assertRaises(ConfigurationError, self._callFUT, request)
-    
+
     def test_without_zodb_database(self):
         from pyramid.exceptions import ConfigurationError
         request = self._makeRequest()
@@ -27,7 +27,7 @@ class Test_get_connection(unittest.TestCase):
         from pyramid.exceptions import ConfigurationError
         request = self._makeRequest()
         self.assertRaises(ConfigurationError, self._callFUT, request, 'wont')
-        
+
     def test_primary_conn_already_exists(self):
         request = self._makeRequest()
         dummy_conn = DummyConnection()
@@ -42,12 +42,27 @@ class Test_get_connection(unittest.TestCase):
         request._primary_zodb_conn = dummy_conn
         conn = self._callFUT(request, 'secondary')
         self.assertEqual(conn, secondary)
-        
-    def test_primary_conn_new(self):
+
+    def test_primary_conn_new_wo_request_tm(self):
         request = self._makeRequest()
+        db = request.registry._zodb_databases['']
         conn = self._callFUT(request)
-        self.assertEqual(conn, 
-                         request.registry._zodb_databases[''].connection)
+        self.assertEqual(conn, db.connection)
+        self.assertEqual(db._opened_with, [None])
+        self.assertEqual(len(request.finished_callbacks), 1)
+        callback = request.finished_callbacks[0]
+        self.assertFalse(conn.closed)
+        callback(request)
+        self.assertTrue(conn.closed)
+        self.assertTrue(conn.transaction_manager.aborted)
+
+    def test_primary_conn_new_w_request_tm(self):
+        request = self._makeRequest()
+        tm = request.tm = object()
+        db = request.registry._zodb_databases['']
+        conn = self._callFUT(request)
+        self.assertEqual(conn, db.connection)
+        self.assertEqual(db._opened_with, [tm])
         self.assertEqual(len(request.finished_callbacks), 1)
         callback = request.finished_callbacks[0]
         self.assertFalse(conn.closed)
@@ -125,7 +140,7 @@ class Test_includeme(unittest.TestCase):
 
     def tearDown(self):
         testing.tearDown()
-    
+
     def _callFUT(self, config, db_from_uri=None, open=open):
         config.captured_uris = []
         self.db = DummyDB()
@@ -219,9 +234,11 @@ class Test_includeme(unittest.TestCase):
 
 class DummyDB:
     def __init__(self, connections=None):
+        self._opened_with = []
         self.databases = {'unnamed': self}
         self.connection = DummyConnection(connections)
-    def open(self):
+    def open(self, transaction_manager=None):
+        self._opened_with.append(transaction_manager)
         return self.connection
     def setActivityMonitor(self, am):
         self.am = am