[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
--- 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)