move hooks activation control on session object, so we can have a per transaction control. Added a new `hooks_control` context manager for usual modification of hooks activation.
--- a/misc/migration/bootstrapmigration_repository.py Mon Mar 08 18:31:36 2010 +0100
+++ b/misc/migration/bootstrapmigration_repository.py Mon Mar 08 19:02:35 2010 +0100
@@ -32,7 +32,7 @@
session.execute = session.unsafe_execute
permsdict = ss.deserialize_ertype_permissions(session)
- config.disabled_hooks_categories.add('integrity')
+ changes = session.disable_hooks_category.add('integrity')
for rschema in repo.schema.relations():
rpermsdict = permsdict.get(rschema.eid, {})
for rdef in rschema.rdefs.values():
@@ -72,7 +72,8 @@
for action in ('read', 'add', 'delete'):
drop_relation_definition('CWRType', '%s_permission' % action, 'CWGroup', commit=False)
drop_relation_definition('CWRType', '%s_permission' % action, 'RQLExpression')
- config.disabled_hooks_categories.remove('integrity')
+ if changes:
+ session.enable_hooks_category.add(*changes)
if applcubicwebversion < (3, 4, 0) and cubicwebversion >= (3, 4, 0):
--- a/schema.py Mon Mar 08 18:31:36 2010 +0100
+++ b/schema.py Mon Mar 08 19:02:35 2010 +0100
@@ -1087,7 +1087,7 @@
if hasattr(cw, 'is_super_session'):
# cw is a server session
hasperm = cw.is_super_session or \
- not cw.vreg.config.is_hook_category_activated('integrity') or \
+ not cw.is_hooks_category_activated('integrity') or \
cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT)
else:
hasperm = cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT)
--- a/server/__init__.py Mon Mar 08 18:31:36 2010 +0100
+++ b/server/__init__.py Mon Mar 08 19:02:35 2010 +0100
@@ -8,6 +8,8 @@
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
+from __future__ import with_statement
+
__docformat__ = "restructuredtext en"
import sys
@@ -209,29 +211,26 @@
def initialize_schema(config, schema, mhandler, event='create'):
from cubicweb.server.schemaserial import serialize_schema
- # deactivate every hooks but those responsible to set metadata
- # so, NO INTEGRITY CHECKS are done, to have quicker db creation
- oldmode = config.set_hooks_mode(config.DENY_ALL)
- changes = config.enable_hook_category('metadata')
+ from cubicweb.server.session import hooks_control
+ session = mhandler.session
paths = [p for p in config.cubes_path() + [config.apphome]
if exists(join(p, 'migration'))]
- # execute cubicweb's pre<event> script
- mhandler.exec_event_script('pre%s' % event)
- # execute cubes pre<event> script if any
- for path in reversed(paths):
- mhandler.exec_event_script('pre%s' % event, path)
- # enter instance'schema into the database
- mhandler.session.set_pool()
- serialize_schema(mhandler.session, schema)
- # execute cubicweb's post<event> script
- mhandler.exec_event_script('post%s' % event)
- # execute cubes'post<event> script if any
- for path in reversed(paths):
- mhandler.exec_event_script('post%s' % event, path)
- # restore hooks config
- if changes:
- config.disable_hook_category(changes)
- config.set_hooks_mode(oldmode)
+ # deactivate every hooks but those responsible to set metadata
+ # so, NO INTEGRITY CHECKS are done, to have quicker db creation
+ with hooks_control(session, session.HOOKS_DENY_ALL, 'metadata'):
+ # execute cubicweb's pre<event> script
+ mhandler.exec_event_script('pre%s' % event)
+ # execute cubes pre<event> script if any
+ for path in reversed(paths):
+ mhandler.exec_event_script('pre%s' % event, path)
+ # enter instance'schema into the database
+ session.set_pool()
+ serialize_schema(session, schema)
+ # execute cubicweb's post<event> script
+ mhandler.exec_event_script('post%s' % event)
+ # execute cubes'post<event> script if any
+ for path in reversed(paths):
+ mhandler.exec_event_script('post%s' % event, path)
# sqlite'stored procedures have to be registered at connection opening time
--- a/server/checkintegrity.py Mon Mar 08 18:31:36 2010 +0100
+++ b/server/checkintegrity.py Mon Mar 08 19:02:35 2010 +0100
@@ -73,8 +73,6 @@
if not repo.system_source.dbhelper.has_fti_table(cursor):
print 'no text index table'
dbhelper.init_fti(cursor)
- repo.config.disabled_hooks_categories.add('metadata')
- repo.config.disabled_hooks_categories.add('integrity')
repo.system_source.do_fti = True # ensure full-text indexation is activated
etypes = set()
for eschema in schema.entities():
@@ -90,9 +88,6 @@
if withpb:
pb = ProgressBar(len(etypes) + 1)
# first monkey patch Entity.check to disable validation
- from cubicweb.entity import Entity
- _check = Entity.check
- Entity.check = lambda self, creation=False: True
# clear fti table first
session.system_sql('DELETE FROM %s' % session.repo.system_source.dbhelper.fti_table)
if withpb:
@@ -102,14 +97,9 @@
source = repo.system_source
for eschema in etypes:
for entity in session.execute('Any X WHERE X is %s' % eschema).entities():
- source.fti_unindex_entity(session, entity.eid)
source.fti_index_entity(session, entity)
if withpb:
pb.update()
- # restore Entity.check
- Entity.check = _check
- repo.config.disabled_hooks_categories.remove('metadata')
- repo.config.disabled_hooks_categories.remove('integrity')
def check_schema(schema, session, eids, fix=1):
--- a/server/hook.py Mon Mar 08 18:31:36 2010 +0100
+++ b/server/hook.py Mon Mar 08 19:02:35 2010 +0100
@@ -113,11 +113,8 @@
@lltrace
def enabled_category(cls, req, **kwargs):
if req is None:
- # server startup / shutdown event
- config = kwargs['repo'].config
- else:
- config = req.vreg.config
- return config.is_hook_activated(cls)
+ return True # XXX how to deactivate server startup / shutdown event
+ return req.is_hook_activated(cls)
@objectify_selector
@lltrace
--- a/server/migractions.py Mon Mar 08 18:31:36 2010 +0100
+++ b/server/migractions.py Mon Mar 08 19:02:35 2010 +0100
@@ -1223,12 +1223,6 @@
def rqliter(self, rql, kwargs=None, ask_confirm=True):
return ForRqlIterator(self, rql, None, ask_confirm)
- def cmd_deactivate_verification_hooks(self):
- self.config.disabled_hooks_categories.add('integrity')
-
- def cmd_reactivate_verification_hooks(self):
- self.config.disabled_hooks_categories.remove('integrity')
-
# broken db commands ######################################################
def cmd_change_attribute_type(self, etype, attr, newtype, commit=True):
@@ -1279,6 +1273,14 @@
if commit:
self.commit()
+ @deprecated("[3.7] use session.disable_hooks_category('integrity')")
+ def cmd_deactivate_verification_hooks(self):
+ self.session.disable_hooks_category('integrity')
+
+ @deprecated("[3.7] use session.enable_hooks_category('integrity')")
+ def cmd_reactivate_verification_hooks(self):
+ self.session.enable_hooks_category('integrity')
+
class ForRqlIterator:
"""specific rql iterator to make the loop skipable"""
--- a/server/repository.py Mon Mar 08 18:31:36 2010 +0100
+++ b/server/repository.py Mon Mar 08 19:02:35 2010 +0100
@@ -85,7 +85,7 @@
# sessions). Also we should imo rely on the orm to first fetch existing
# entity if any then delete it.
if session.is_internal_session \
- or not session.vreg.config.is_hook_category_activated('integrity'):
+ or not session.is_hooks_category_activated('integrity'):
return
card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality')
# one may be tented to check for neweids but this may cause more than one
@@ -988,7 +988,8 @@
if not rschema.final: # inlined relation
relations.append((attr, entity[attr]))
entity.set_defaults()
- entity.check(creation=True)
+ if session.is_hooks_category_activated('integrity'):
+ entity.check(creation=True)
source.add_entity(session, entity)
if source.uri != 'system':
extid = source.get_extid(entity)
@@ -1035,7 +1036,8 @@
print 'UPDATE entity', etype, entity.eid, \
dict(entity), edited_attributes
entity.edited_attributes = edited_attributes
- entity.check()
+ if session.is_hooks_category_activated('integrity'):
+ entity.check()
eschema = entity.e_schema
session.set_entity_cache(entity)
only_inline_rels, need_fti_update = True, False
--- a/server/serverconfig.py Mon Mar 08 18:31:36 2010 +0100
+++ b/server/serverconfig.py Mon Mar 08 19:02:35 2010 +0100
@@ -185,63 +185,6 @@
# check user's state at login time
consider_user_state = True
- # XXX hooks control stuff should probably be on the session, not on the config
-
- # hooks activation configuration
- # all hooks should be activated during normal execution
- disabled_hooks_categories = set()
- enabled_hooks_categories = set()
- ALLOW_ALL = object()
- DENY_ALL = object()
- hooks_mode = ALLOW_ALL
-
- @classmethod
- def set_hooks_mode(cls, mode):
- assert mode is cls.ALLOW_ALL or mode is cls.DENY_ALL
- oldmode = cls.hooks_mode
- cls.hooks_mode = mode
- return oldmode
-
- @classmethod
- def disable_hook_category(cls, *categories):
- changes = set()
- if cls.hooks_mode is cls.DENY_ALL:
- for category in categories:
- if category in cls.enabled_hooks_categories:
- cls.enabled_hooks_categories.remove(category)
- changes.add(category)
- else:
- for category in categories:
- if category not in cls.disabled_hooks_categories:
- cls.disabled_hooks_categories.add(category)
- changes.add(category)
- return changes
-
- @classmethod
- def enable_hook_category(cls, *categories):
- changes = set()
- if cls.hooks_mode is cls.DENY_ALL:
- for category in categories:
- if category not in cls.enabled_hooks_categories:
- cls.enabled_hooks_categories.add(category)
- changes.add(category)
- else:
- for category in categories:
- if category in cls.disabled_hooks_categories:
- cls.disabled_hooks_categories.remove(category)
- changes.add(category)
- return changes
-
- @classmethod
- def is_hook_activated(cls, hook):
- return cls.is_hook_category_activated(hook.category)
-
- @classmethod
- def is_hook_category_activated(cls, category):
- if cls.hooks_mode is cls.DENY_ALL:
- return category in cls.enabled_hooks_categories
- return category not in cls.disabled_hooks_categories
-
# should some hooks be deactivated during [pre|post]create script execution
free_wheel = False
--- a/server/session.py Mon Mar 08 18:31:36 2010 +0100
+++ b/server/session.py Mon Mar 08 19:02:35 2010 +0100
@@ -42,6 +42,37 @@
return description
+class hooks_control(object):
+ """context manager to control activated hooks categories.
+
+ If mode is session.`HOOKS_DENY_ALL`, given hooks categories will
+ be enabled.
+
+ If mode is session.`HOOKS_ALLOW_ALL`, given hooks categories will
+ be disabled.
+ """
+ def __init__(self, session, mode, *categories):
+ self.session = session
+ self.mode = mode
+ self.categories = categories
+
+ def __enter__(self):
+ self.oldmode = self.session.set_hooks_mode(self.mode)
+ if self.mode is self.session.HOOKS_DENY_ALL:
+ self.changes = self.session.enable_hooks_category(*self.categories)
+ else:
+ self.changes = self.session.disable_hooks_category(*self.categories)
+
+ def __exit__(self, exctype, exc, traceback):
+ if self.changes:
+ if self.mode is self.session.HOOKS_DENY_ALL:
+ self.session.disable_hooks_category(*self.changes)
+ else:
+ self.session.enable_hooks_category(*self.changes)
+ self.session.set_hooks_mode(self.oldmode)
+
+
+
class Session(RequestSessionBase):
"""tie session id, user, connections pool and other session data all
together
@@ -245,6 +276,78 @@
rdef = rschema.rdef(subjtype, objtype)
return rdef.get(rprop)
+ # hooks activation control #################################################
+ # all hooks should be activated during normal execution
+
+ HOOKS_ALLOW_ALL = object()
+ HOOKS_DENY_ALL = object()
+
+ @property
+ def hooks_mode(self):
+ return getattr(self._threaddata, 'hooks_mode', self.HOOKS_ALLOW_ALL)
+
+ def set_hooks_mode(self, mode):
+ assert mode is self.HOOKS_ALLOW_ALL or mode is self.HOOKS_DENY_ALL
+ oldmode = getattr(self._threaddata, 'hooks_mode', self.HOOKS_ALLOW_ALL)
+ self._threaddata.hooks_mode = mode
+ return oldmode
+
+ @property
+ def disabled_hooks_categories(self):
+ try:
+ return getattr(self._threaddata, 'disabled_hooks_cats')
+ except AttributeError:
+ cats = self._threaddata.disabled_hooks_cats = set()
+ return cats
+
+ @property
+ def enabled_hooks_categories(self):
+ try:
+ return getattr(self._threaddata, 'enabled_hooks_cats')
+ except AttributeError:
+ cats = self._threaddata.enabled_hooks_cats = set()
+ return cats
+
+ def disable_hooks_category(self, *categories):
+ changes = set()
+ if self.hooks_mode is self.HOOKS_DENY_ALL:
+ enablecats = self.enabled_hooks_categories
+ for category in categories:
+ if category in enablecats:
+ enablecats.remove(category)
+ changes.add(category)
+ else:
+ disablecats = self.disabled_hooks_categories
+ for category in categories:
+ if category not in disablecats:
+ disablecats.add(category)
+ changes.add(category)
+ return tuple(changes)
+
+ def enable_hooks_category(self, *categories):
+ changes = set()
+ if self.hooks_mode is self.HOOKS_DENY_ALL:
+ enablecats = self.enabled_hooks_categories
+ for category in categories:
+ if category not in enablecats:
+ enablecats.add(category)
+ changes.add(category)
+ else:
+ disablecats = self.disabled_hooks_categories
+ for category in categories:
+ if category in self.disabled_hooks_categories:
+ disablecats.remove(category)
+ changes.add(category)
+ return tuple(changes)
+
+ def is_hooks_category_activated(self, category):
+ if self.hooks_mode is self.HOOKS_DENY_ALL:
+ return category in self.enabled_hooks_categories
+ return category not in self.disabled_hooks_categories
+
+ def is_hook_activated(self, hook):
+ return self.is_hooks_category_activated(hook.category)
+
# connection management ###################################################
def keep_pool_mode(self, mode):
@@ -716,6 +819,21 @@
def super_session(self):
return self
+ @property
+ def hooks_mode(self):
+ return self.parent_session.hooks_mode
+ def set_hooks_mode(self, mode):
+ return self.parent_session.set_hooks_mode(mode)
+
+ @property
+ def disabled_hooks_categories(self):
+ return self.parent_session.disabled_hooks_categories
+
+ @property
+ def enabled_hooks_categories(self):
+ return self.parent_session.enabled_hooks_categories
+
+
def get_mode(self):
return self.parent_session.mode
def set_mode(self, value):
--- a/server/test/unittest_hook.py Mon Mar 08 18:31:36 2010 +0100
+++ b/server/test/unittest_hook.py Mon Mar 08 19:02:35 2010 +0100
@@ -108,13 +108,17 @@
def test_call_hook(self):
self.o.register(AddAnyHook)
- cw = mock_object(vreg=self.vreg)
- self.assertRaises(HookCalled, self.o.call_hooks, 'before_add_entity', cw)
+ dis = set()
+ cw = mock_object(vreg=self.vreg,
+ is_hook_activated=lambda x, cls: cls.category not in dis)
+ self.assertRaises(HookCalled,
+ self.o.call_hooks, 'before_add_entity', cw)
self.o.call_hooks('before_delete_entity', cw) # nothing to call
- config.disabled_hooks_categories.add('cat1')
+ dis.add('cat1')
self.o.call_hooks('before_add_entity', cw) # disabled hooks category, not called
- config.disabled_hooks_categories.remove('cat1')
- self.assertRaises(HookCalled, self.o.call_hooks, 'before_add_entity', cw)
+ dis.remove('cat1')
+ self.assertRaises(HookCalled,
+ self.o.call_hooks, 'before_add_entity', cw)
self.o.unregister(AddAnyHook)
self.o.call_hooks('before_add_entity', cw) # nothing to call