backport stable branch and some vreg cleanups:
* move initialization_completed from cwvreg to base VRegistry class
allowing simplification of CWVregistry
* cleanup initialization process: __registered__ is now called after
initialization completed, by the relevant registry.
* fix/remove deprecated tests
--- a/appobject.py Sat Feb 06 08:45:14 2010 +0100
+++ b/appobject.py Mon Feb 08 11:08:55 2010 +0100
@@ -268,6 +268,7 @@
pdef['default'] = getattr(cls, propid, pdef['default'])
pdef['sitewide'] = getattr(cls, 'site_wide', pdef.get('sitewide'))
registry.vreg.register_property(cls._cwpropkey(propid), **pdef)
+ assert callable(cls.__select__), obj
return cls
def __init__(self, req, **extra):
--- a/cwvreg.py Sat Feb 06 08:45:14 2010 +0100
+++ b/cwvreg.py Mon Feb 08 11:08:55 2010 +0100
@@ -58,9 +58,6 @@
def schema(self):
return self.vreg.schema
- def initialization_completed(self):
- pass
-
@deprecated('[3.6] select object, then use obj.render()')
def render(self, __oid, req, __fallback_oid=None, rset=None, initargs=None,
**kwargs):
@@ -363,27 +360,23 @@
if force_reload is None:
force_reload = self.config.debugmode
try:
- self._register_objects(path, force_reload)
+ super(CubicWebVRegistry, self).register_objects(
+ path, force_reload, self.config.extrapath)
except RegistryOutOfDate:
CW_EVENT_MANAGER.emit('before-registry-reload')
# modification detected, reset and reload
self.reset(path, force_reload)
- self._register_objects(path, force_reload)
+ super(CubicWebVRegistry, self).register_objects(
+ path, force_reload, self.config.extrapath)
CW_EVENT_MANAGER.emit('after-registry-reload')
- def _register_objects(self, path, force_reload=None):
- """overriden to remove objects requiring a missing interface"""
- if super(CubicWebVRegistry, self).register_objects(path, force_reload,
- self.config.extrapath):
- self.initialization_completed()
- # don't check rtags if we don't want to cleanup_interface_sobjects
- for rtag in RTAGS:
- rtag.init(self.schema,
- check=self.config.cleanup_interface_sobjects)
+ def initialization_completed(self):
+ """cw specific code once vreg initialization is completed:
- def initialization_completed(self):
- for regname, reg in self.items():
- reg.initialization_completed()
+ * remove objects requiring a missing interface, unless
+ config.cleanup_interface_sobjects is false
+ * init rtags
+ """
# we may want to keep interface dependent objects (e.g.for i18n
# catalog generation)
if self.config.cleanup_interface_sobjects:
@@ -410,6 +403,11 @@
# clear needs_iface so we don't try to remove some not-anymore-in
# objects on automatic reloading
self._needs_iface.clear()
+ super(CubicWebVRegistry, self).initialization_completed()
+ for rtag in RTAGS:
+ # don't check rtags if we don't want to cleanup_interface_sobjects
+ rtag.init(self.schema, check=self.config.cleanup_interface_sobjects)
+
# rql parsing utilities ####################################################
@@ -470,7 +468,7 @@
try:
return self['propertyvalues'][key]
except KeyError:
- return self['propertydefs'][key]['default']
+ return self.property_info(key)['default']
def typed_value(self, key, value):
"""value is an unicode string, return it correctly typed. Let potential
--- a/hooks/integrity.py Sat Feb 06 08:45:14 2010 +0100
+++ b/hooks/integrity.py Mon Feb 08 11:08:55 2010 +0100
@@ -8,6 +8,8 @@
"""
__docformat__ = "restructuredtext en"
+from threading import Lock
+
from cubicweb import ValidationError
from cubicweb.schema import RQLConstraint, RQLUniqueConstraint
from cubicweb.selectors import implements
@@ -22,6 +24,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
+
+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
+ _ReleaseUniqueConstraintsOperation(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
+
+class _ReleaseUniqueConstraintsOperation(hook.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)
+
class _CheckRequiredRelationOperation(hook.LateOperation):
"""checking relation cardinality has to be done after commit in
@@ -126,6 +163,12 @@
if self.session.deleted_in_transaction(eidto):
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/hook.py Sat Feb 06 08:45:14 2010 +0100
+++ b/server/hook.py Mon Feb 08 11:08:55 2010 +0100
@@ -193,8 +193,8 @@
return str(id(cls))
@classmethod
- def __registered__(cls, vreg):
- super(Hook, cls).__registered__(vreg)
+ def __registered__(cls, reg):
+ super(Hook, cls).__registered__(reg)
if getattr(cls, 'accepts', None):
warn('[3.6] %s.%s: accepts is deprecated, define proper __select__'
% (cls.__module__, cls.__name__), DeprecationWarning)
--- a/server/session.py Sat Feb 06 08:45:14 2010 +0100
+++ b/server/session.py Mon Feb 08 11:08:55 2010 +0100
@@ -484,7 +484,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 11:08:55 2010 +0100
@@ -0,0 +1,2 @@
+from cubicweb.web.views import xmlrss
+xmlrss.RSSIconBox.visible = True
--- a/test/unittest_cwconfig.py Sat Feb 06 08:45:14 2010 +0100
+++ b/test/unittest_cwconfig.py Mon Feb 08 11:08:55 2010 +0100
@@ -72,15 +72,15 @@
# self.assertRaises(KeyError, vcconf.__getitem__, 'CRM')
def test_expand_cubes(self):
- self.assertEquals(self.config.expand_cubes(('email', 'eblog')),
- ['email', 'eblog', 'file'])
+ self.assertEquals(self.config.expand_cubes(('email', 'blog')),
+ ['email', 'blog', 'file'])
def test_vregistry_path(self):
self.assertEquals([unabsolutize(p) for p in self.config.vregistry_path()],
['entities', 'web/views', 'sobjects', 'hooks',
'file/entities.py', 'file/views', 'file/hooks.py',
'email/entities.py', 'email/views', 'email/hooks.py',
- 'test/data/entities.py'])
+ 'test/data/entities.py', 'test/data/views.py'])
def test_cubes_path(self):
# make sure we don't import the email cube, but the stdlib email package
--- a/test/unittest_selectors.py Sat Feb 06 08:45:14 2010 +0100
+++ b/test/unittest_selectors.py Mon Feb 08 11:08:55 2010 +0100
@@ -112,6 +112,7 @@
__select__ = match_user_groups('owners')
self.vreg._loadedmods[__name__] = {}
self.vreg.register_appobject_class(SomeAction)
+ SomeAction.__registered__(self.vreg['actions'])
self.failUnless(SomeAction in self.vreg['actions']['yo'], self.vreg['actions'])
try:
# login as a simple user
--- a/test/unittest_vregistry.py Sat Feb 06 08:45:14 2010 +0100
+++ b/test/unittest_vregistry.py Mon Feb 08 11:08:55 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.testlib import CubicWebTC
from cubicweb.interfaces import IMileStone
from cubes.card.entities import Card
@@ -40,10 +41,6 @@
self.vreg.initialization_completed()
self.assertEquals(len(self.vreg['views']['primary']), 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')
def test_load_subinterface_based_appobjects(self):
self.vreg.reset()
@@ -60,6 +57,18 @@
# 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(CubicWebTC):
+
+ def test_property_default_overriding(self):
+ # see data/views.py
+ from cubicweb.web.views.xmlrss import RSSIconBox
+ self.assertEquals(self.vreg.property_info(RSSIconBox._cwpropkey('visible'))['default'], True)
if __name__ == '__main__':
unittest_main()
--- a/vregistry.py Sat Feb 06 08:45:14 2010 +0100
+++ b/vregistry.py Mon Feb 08 11:08:55 2010 +0100
@@ -84,6 +84,11 @@
except KeyError:
raise ObjectNotFound(name), None, sys.exc_info()[-1]
+ def initialization_completed(self):
+ for appobjects in self.itervalues():
+ for appobjectcls in appobjects:
+ appobjectcls.__registered__(self)
+
def register(self, obj, oid=None, clear=False):
"""base method to add an object in the registry"""
assert not '__abstract__' in obj.__dict__
@@ -93,11 +98,9 @@
appobjects = self[oid] = []
else:
appobjects = self.setdefault(oid, [])
- appobject = obj.__registered__(self)
- assert not appobject in appobjects, \
- 'object %s is already registered' % appobject
- assert callable(appobject.__select__), appobject
- appobjects.append(appobject)
+ assert not obj in appobjects, \
+ 'object %s is already registered' % obj
+ appobjects.append(obj)
def register_and_replace(self, obj, replaced):
# XXXFIXME this is a duplication of unregister()
@@ -356,8 +359,15 @@
for filepath, modname in filemods:
if self.load_file(filepath, modname, force_reload):
change = True
+ if change:
+ self.initialization_completed()
return change
+ def initialization_completed(self):
+ for regname, reg in self.iteritems():
+ self.debug('available in registry %s: %s', regname, sorted(reg))
+ reg.initialization_completed()
+
def load_file(self, filepath, modname, force_reload=False):
"""load app objects from a python file"""
from logilab.common.modutils import load_module_from_name
--- a/web/application.py Sat Feb 06 08:45:14 2010 +0100
+++ b/web/application.py Mon Feb 08 11:08:55 2010 +0100
@@ -232,12 +232,11 @@
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)
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())
--- a/web/box.py Sat Feb 06 08:45:14 2010 +0100
+++ b/web/box.py Mon Feb 08 11:08:55 2010 +0100
@@ -101,7 +101,6 @@
according to application schema and display according to connected
user's rights) and rql attributes
"""
-#XXX __selectors__ = BoxTemplate.__selectors__ + (etype_rtype_selector,)
rql = None
--- a/web/views/actions.py Sat Feb 06 08:45:14 2010 +0100
+++ b/web/views/actions.py Mon Feb 08 11:08:55 2010 +0100
@@ -163,13 +163,13 @@
order = 15
@classmethod
- def __registered__(cls, vreg):
- if 'require_permission' in vreg.schema:
+ def __registered__(cls, reg):
+ if 'require_permission' in reg.schema:
cls.__select__ = (one_line_rset() & non_final_entity() &
(match_user_groups('managers')
| relation_possible('require_permission', 'subject', 'CWPermission',
action='add')))
- return super(ManagePermissionsAction, cls).__registered__(vreg)
+ return super(ManagePermissionsAction, cls).__registered__(reg)
def url(self):
return self.cw_rset.get_entity(self.cw_row or 0, self.cw_col or 0).absolute_url(vid='security')