# HG changeset patch # User Sylvain Thénault # Date 1268071355 -3600 # Node ID b718626a0e60257dc27036a4798bd589e16d3604 # Parent 41a78fb4107cd8f3a30d186e679f613763fca780 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. diff -r 41a78fb4107c -r b718626a0e60 misc/migration/bootstrapmigration_repository.py --- 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): diff -r 41a78fb4107c -r b718626a0e60 schema.py --- 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) diff -r 41a78fb4107c -r b718626a0e60 server/__init__.py --- 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 script - mhandler.exec_event_script('pre%s' % event) - # execute cubes pre 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 script - mhandler.exec_event_script('post%s' % event) - # execute cubes'post 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 script + mhandler.exec_event_script('pre%s' % event) + # execute cubes pre 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 script + mhandler.exec_event_script('post%s' % event) + # execute cubes'post 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 diff -r 41a78fb4107c -r b718626a0e60 server/checkintegrity.py --- 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): diff -r 41a78fb4107c -r b718626a0e60 server/hook.py --- 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 diff -r 41a78fb4107c -r b718626a0e60 server/migractions.py --- 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""" diff -r 41a78fb4107c -r b718626a0e60 server/repository.py --- 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 diff -r 41a78fb4107c -r b718626a0e60 server/serverconfig.py --- 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 diff -r 41a78fb4107c -r b718626a0e60 server/session.py --- 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): diff -r 41a78fb4107c -r b718626a0e60 server/test/unittest_hook.py --- 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