merge stable
authorAlexandre Fayolle <alexandre.fayolle@logilab.fr>
Mon, 08 Feb 2010 10:07:52 +0100
branchstable
changeset 4502 1422589c35a0
parent 4501 71ba2d0f34f2 (current diff)
parent 4489 63128e8b9af9 (diff)
child 4504 f1d505f979b3
merge
--- a/appobject.py	Fri Feb 05 17:57:51 2010 +0100
+++ b/appobject.py	Mon Feb 08 10:07:52 2010 +0100
@@ -295,7 +295,6 @@
         cls.vreg = registry.vreg
         cls.schema = registry.schema
         cls.config = registry.config
-        cls.register_properties()
         return cls
 
     @classmethod
--- a/cwvreg.py	Fri Feb 05 17:57:51 2010 +0100
+++ b/cwvreg.py	Mon Feb 08 10:07:52 2010 +0100
@@ -63,6 +63,7 @@
         # registry content
         for appobjects in self.itervalues():
             for appobject in appobjects:
+                # XXX kill vreg_initialization_completed
                 appobject.vreg_initialization_completed()
 
     def render(self, __oid, req, __fallback_oid=None, rset=None, initargs=None,
@@ -376,9 +377,12 @@
                           check=self.config.cleanup_interface_sobjects)
 
     def initialization_completed(self):
-        for regname, reg in self.items():
+        for regname, reg in self.iteritems():
             self.debug('available in registry %s: %s', regname, sorted(reg))
             reg.initialization_completed()
+            for appobjects in reg.itervalues():
+                for appobjectcls in appobjects:
+                    appobjectcls.register_properties()
         # we may want to keep interface dependent objects (e.g.for i18n
         # catalog generation)
         if self.config.cleanup_interface_sobjects:
--- a/server/hooks.py	Fri Feb 05 17:57:51 2010 +0100
+++ b/server/hooks.py	Mon Feb 08 10:07:52 2010 +0100
@@ -8,6 +8,7 @@
 """
 __docformat__ = "restructuredtext en"
 
+from threading import Lock
 from datetime import datetime
 
 from cubicweb import UnknownProperty, ValidationError, BadConnectionId
@@ -25,6 +26,41 @@
 DONT_CHECK_RTYPES_ON_DEL = set(('is', 'is_instance_of',
                                 'wf_info_for', 'from_state', 'to_state'))
 
+_UNIQUE_CONSTRAINTS_LOCK = Lock()
+_UNIQUE_CONSTRAINTS_HOLDER = None
+
+class _ReleaseUniqueConstraintsHook(Operation):
+    def commit_event(self):
+        pass
+    def postcommit_event(self):
+        _release_unique_cstr_lock(self.session)
+    def rollback_event(self):
+        _release_unique_cstr_lock(self.session)
+
+def _acquire_unique_cstr_lock(session):
+    """acquire the _UNIQUE_CONSTRAINTS_LOCK for the session.
+
+    This lock used to avoid potential integrity pb when checking
+    RQLUniqueConstraint in two different transactions, as explained in
+    http://intranet.logilab.fr/jpl/ticket/36564
+    """
+    global _UNIQUE_CONSTRAINTS_HOLDER
+    asession = session.actual_session()
+    if _UNIQUE_CONSTRAINTS_HOLDER is asession:
+        return
+    _UNIQUE_CONSTRAINTS_LOCK.acquire()
+    _UNIQUE_CONSTRAINTS_HOLDER = asession
+    # register operation responsible to release the lock on commit/rollback
+    _ReleaseUniqueConstraintsHook(asession)
+
+def _release_unique_cstr_lock(session):
+    global _UNIQUE_CONSTRAINTS_HOLDER
+    if _UNIQUE_CONSTRAINTS_HOLDER is session:
+        _UNIQUE_CONSTRAINTS_HOLDER = None
+        _UNIQUE_CONSTRAINTS_LOCK.release()
+    else:
+        assert _UNIQUE_CONSTRAINTS_HOLDER is None
+
 
 def relation_deleted(session, eidfrom, rtype, eidto):
     session.transaction_data.setdefault('pendingrelations', []).append(
@@ -216,6 +252,12 @@
         if eidto in pending:
             return
         for constraint in self.constraints:
+            # XXX
+            # * lock RQLConstraint as well?
+            # * use a constraint id to use per constraint lock and avoid
+            #   unnecessary commit serialization ?
+            if isinstance(constraint, RQLUniqueConstraint):
+                _acquire_unique_cstr_lock(self.session)
             try:
                 constraint.repo_check(self.session, eidfrom, rtype, eidto)
             except NotImplementedError:
--- a/server/session.py	Fri Feb 05 17:57:51 2010 +0100
+++ b/server/session.py	Mon Feb 08 10:07:52 2010 +0100
@@ -474,7 +474,8 @@
                 try:
                     operation.handle_event('%s_event' % trstate)
                 except:
-                    self.exception('error while %sing', trstate)
+                    self.critical('error while %sing', trstate,
+                                  exc_info=sys.exc_info())
             self.debug('%s session %s done', trstate, self.id)
         finally:
             self._touch()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/views.py	Mon Feb 08 10:07:52 2010 +0100
@@ -0,0 +1,2 @@
+from cubicweb.web.views import xmlrss
+xmlrss.RSSIconBox.visible = True
--- a/test/unittest_vregistry.py	Fri Feb 05 17:57:51 2010 +0100
+++ b/test/unittest_vregistry.py	Mon Feb 08 10:07:52 2010 +0100
@@ -13,6 +13,7 @@
 from cubicweb.appobject import AppObject
 from cubicweb.cwvreg import CubicWebVRegistry, UnknownProperty
 from cubicweb.devtools import TestServerConfiguration
+from cubicweb.devtools.apptest import EnvBasedTC
 from cubicweb.interfaces import IMileStone
 
 from cubes.card.entities import Card
@@ -31,6 +32,14 @@
         config.bootstrap_cubes()
         self.vreg.schema = config.load_schema()
 
+    def test___selectors__compat(self):
+        myselector1 = lambda *args: 1
+        myselector2 = lambda *args: 1
+        class AnAppObject(AppObject):
+            __selectors__ = (myselector1, myselector2)
+        AnAppObject.build___select__()
+        self.assertEquals(AnAppObject.__select__(AnAppObject), 2)
+
     def test_load_interface_based_vojects(self):
         self.vreg.init_registration([WEBVIEWSDIR])
         self.vreg.load_file(join(BASE, 'entities', '__init__.py'), 'cubicweb.entities.__init__')
@@ -40,19 +49,6 @@
         self.vreg.initialization_completed()
         self.assertEquals(len(self.vreg['views']['primary']), 1)
 
-    def test___selectors__compat(self):
-        myselector1 = lambda *args: 1
-        myselector2 = lambda *args: 1
-        class AnAppObject(AppObject):
-            __selectors__ = (myselector1, myselector2)
-        AnAppObject.build___select__()
-        self.assertEquals(AnAppObject.__select__(AnAppObject), 2)
-
-    def test_properties(self):
-        self.failIf('system.version.cubicweb' in self.vreg['propertydefs'])
-        self.failUnless(self.vreg.property_info('system.version.cubicweb'))
-        self.assertRaises(UnknownProperty, self.vreg.property_info, 'a.non.existent.key')
-
     def test_load_subinterface_based_appobjects(self):
         self.vreg.reset()
         self.vreg.register_objects([join(BASE, 'web', 'views', 'iprogress.py')])
@@ -68,6 +64,17 @@
         # check progressbar isn't kicked
         self.assertEquals(len(self.vreg['views']['progressbar']), 1)
 
+    def test_properties(self):
+        self.failIf('system.version.cubicweb' in self.vreg['propertydefs'])
+        self.failUnless(self.vreg.property_info('system.version.cubicweb'))
+        self.assertRaises(UnknownProperty, self.vreg.property_info, 'a.non.existent.key')
+
+class CWVregTC(EnvBasedTC):
+
+    def test_property_default_overriding(self):
+        # see data/views.py
+        from cubicweb.web.views.xmlrss import RSSIconBox
+        self.assertEquals(self.vreg.property_info(RSSIconBox.propkey('visible'))['default'], True)
 
 if __name__ == '__main__':
     unittest_main()
--- a/web/application.py	Fri Feb 05 17:57:51 2010 +0100
+++ b/web/application.py	Mon Feb 08 10:07:52 2010 +0100
@@ -226,17 +226,23 @@
     def __init__(self, config, debug=None,
                  session_handler_fact=CookieSessionHandler,
                  vreg=None):
-        super(CubicWebPublisher, self).__init__()
-        # connect to the repository and get instance's schema
+        self.info('starting web instance from %s', config.apphome)
         if vreg is None:
             vreg = cwvreg.CubicWebVRegistry(config, debug=debug)
+            need_set_schema = True
+        else:
+            # vreg is specified during test and the vreg is already properly
+            # initialized. Even, reinitializing it may cause some unwanted
+            # side effect due to unproper reloading of appobjects modules
+            need_set_schema = False
         self.vreg = vreg
-        self.info('starting web instance from %s', config.apphome)
+        # connect to the repository and get instance's schema
         self.repo = config.repository(vreg)
         if not vreg.initialized:
             self.config.init_cubes(self.repo.get_cubes())
             vreg.init_properties(self.repo.properties())
-        vreg.set_schema(self.repo.get_schema())
+        if need_set_schema:
+            vreg.set_schema(self.repo.get_schema())
         # set the correct publish method
         if config['query-log-file']:
             from threading import Lock