[repository] add an ``internal_cnx`` method to replace ``internal_session``
authorPierre-Yves David <pierre-yves.david@logilab.fr>
Thu, 27 Jun 2013 14:12:00 +0200
changeset 9113 af6efc15fc90
parent 9112 c859c7cef346
child 9114 9a9d3f4bad31
[repository] add an ``internal_cnx`` method to replace ``internal_session`` Accessing the repo through a Session is deprecated. We need an easy replacement for ``internal_session``. This API change was a good occasion to stop disabling integrity hook by default. This is huge source of bug in user-code. related to #2503918
doc/4.0.rst
server/repository.py
server/session.py
server/test/unittest_session.py
--- a/doc/4.0.rst	Thu Jun 27 12:02:38 2013 +0200
+++ b/doc/4.0.rst	Thu Jun 27 14:12:00 2013 +0200
@@ -9,6 +9,37 @@
   connection is anonymous. Beware that the ``anonymous-user`` config is web
   specific. Therefore, no session may be anonymous in repository only setup.
 
+New Repository Access API
+-------------------------
+
+Connection replace Session
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A new explicite Connection object replace Session as the main repository entry
+point. Connection hold all the necessary methods to be used Server side
+(``execute``, ``commit``, ``rollback``, ``call_service``, ``entity_from_eid``,
+etc…). You obtains a new Connection object using ``session.new_cnx()``.
+Connection object need have an explicite begin and end. Use them as a context
+manager::
+
+    with session.new_cnx() as cnx:
+        self.execute('INSERT Elephant E, E name "Cabar"')
+        self.commit()
+        self.execute('INSERT Elephant E, E name "Celeste"')
+        self.commit()
+    # Once you get out of the "with" clause, the connection is closed.
+
+Using the same Connection object in multiple threads will give you access to the
+same Transaction. However, Connection object are not thread safe.
+
+``repository.internal_session`` is deprecated in favor of
+``repository.internal_cnx``. Note that internal connection are now safe by
+default. Integrity hooks are enabled except stated otherwise.
+
+Backward compact is preserved on Session. They can still be used to access the
+database for the next few version.
+
+
 API changes
 -----------
 
--- a/server/repository.py	Thu Jun 27 12:02:38 2013 +0200
+++ b/server/repository.py	Thu Jun 27 14:12:00 2013 +0200
@@ -36,6 +36,7 @@
 from os.path import join
 from datetime import datetime
 from time import time, localtime, strftime
+from contextlib import contextmanager
 
 from logilab.common.decorators import cached, clear_cache
 from logilab.common.deprecation import deprecated
@@ -945,6 +946,8 @@
                 nbclosed += 1
         return nbclosed
 
+    @deprecated("[4.0] use internal_cnx now\n"
+                "(Beware that integrity hook are now enabled by default)")
     def internal_session(self, cnxprops=None, safe=False):
         """return a dbapi like connection/cursor using internal user which have
         every rights on the repository. The `safe` argument is a boolean flag
@@ -958,6 +961,22 @@
         session.set_cnxset()
         return session
 
+    @contextmanager
+    def internal_cnx(self, safe=True):
+        """return a Connection using internal user which have
+        every rights on the repository. The `safe` argument is a boolean flag
+        telling if integrity hooks should be activated or not.
+
+        /!\ IN OPPOSITE OF THE OLDER INTERNAL_SESSION, INTERNAL CONNECTION ARE SAFE
+        /!\ BY DEFAULT.
+
+        This is to be used a context manager.
+        """
+        with InternalSession(self, safe) as session:
+            with session.new_cnx() as cnx:
+                yield cnx
+
+
     def _get_session(self, sessionid, setcnxset=False, txid=None,
                      checkshuttingdown=True):
         """return the user associated to the given session identifier"""
--- a/server/session.py	Thu Jun 27 12:02:38 2013 +0200
+++ b/server/session.py	Thu Jun 27 14:12:00 2013 +0200
@@ -664,7 +664,7 @@
 
     def set_entity_cache(self, entity):
         """Add `entity` to the connection entity cache"""
-        #XXX not using _open_only because before at creation time. _set_user
+        # XXX not using _open_only because before at creation time. _set_user
         # call this function to cache the Connection user.
         if entity.cw_etype != 'CWUser' and not self._open:
             raise ProgrammingError('Closed Connection: %s'
--- a/server/test/unittest_session.py	Thu Jun 27 12:02:38 2013 +0200
+++ b/server/test/unittest_session.py	Thu Jun 27 14:12:00 2013 +0200
@@ -76,6 +76,18 @@
             cnx.rollback()
             new_user = cnx.entity_from_eid(user.eid)
             self.assertIsNotNone(new_user.login)
+        self.assertFalse(cnx._open)
+
+    def test_internal_cnx(self):
+        with self.repo.internal_cnx() as cnx:
+            rset = cnx.execute('Any X LIMIT 1 WHERE X is CWUser')
+            self.assertEqual(1, len(rset))
+            user = rset.get_entity(0, 0)
+            user.cw_delete()
+            cnx.rollback()
+            new_user = cnx.entity_from_eid(user.eid)
+            self.assertIsNotNone(new_user.login)
+        self.assertFalse(cnx._open)