Commit b012dac6 authored by Sylvain Thénault's avatar Sylvain Thénault
Browse files

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
......@@ -268,6 +268,7 @@ class AppObject(object):
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):
......
......@@ -58,9 +58,6 @@ class CWRegistry(Registry):
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 @@ class CubicWebVRegistry(VRegistry):
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):
for regname, reg in self.items():
reg.initialization_completed()
"""cw specific code once vreg initialization is 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 @@ class CubicWebVRegistry(VRegistry):
# 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 @@ class CubicWebVRegistry(VRegistry):
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
......
......@@ -8,6 +8,8 @@ validity
"""
__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_ADD = set(('owned_by', 'created_by',
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 @@ class _CheckConstraintsOp(hook.LateOperation):
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:
......
......@@ -193,8 +193,8 @@ class Hook(AppObject):
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)
......
......@@ -484,7 +484,8 @@ class Session(RequestSessionBase):
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()
......
from cubicweb.web.views import xmlrss
xmlrss.RSSIconBox.visible = True
......@@ -72,15 +72,15 @@ class CubicWebConfigurationTC(TestCase):
# 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
......
......@@ -112,6 +112,7 @@ class MatchUserGroupsTC(CubicWebTC):
__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
......
......@@ -13,6 +13,7 @@ from cubicweb import CW_SOFTWARE_ROOT as BASE
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 @@ class VRegistryTC(TestCase):
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 @@ class VRegistryTC(TestCase):
# 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()
......@@ -84,6 +84,11 @@ class Registry(dict):
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 @@ class Registry(dict):
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 @@ class VRegistry(dict):
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
......
......@@ -232,12 +232,11 @@ class CubicWebPublisher(object):
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())
......
......@@ -101,7 +101,6 @@ class RQLBoxTemplate(BoxTemplate):
according to application schema and display according to connected
user's rights) and rql attributes
"""
#XXX __selectors__ = BoxTemplate.__selectors__ + (etype_rtype_selector,)
rql = None
......
......@@ -163,13 +163,13 @@ class ManagePermissionsAction(action.Action):
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')
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment