merge tls-sprint
authorsylvain.thenault@logilab.fr
Wed, 18 Feb 2009 13:44:35 +0100
branchtls-sprint
changeset 784 33db07c66789
parent 783 45d816326626 (diff)
parent 782 01801a10c567 (current diff)
child 785 e4f9fa3c891a
merge
selectors.py
--- a/common/registerers.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/common/registerers.py	Wed Feb 18 13:44:35 2009 +0100
@@ -12,19 +12,7 @@
 
 from cubicweb.vregistry import registerer, yes_registerer
 from cubicweb.selectors import implements
-
-def _accepts_interfaces(obj):
-    try:
-        return sorted(obj.accepts_interfaces)
-    except AttributeError:
-        try:
-            impl = obj.__select__.search_selector(implements)
-            if impl:
-                return sorted(impl.expected_ifaces)
-        except AttributeError:
-            pass # old-style vobject classes with no accepts_interfaces
-        return ()
-
+from cubicweb.cwvreg import use_interfaces
 
 class priority_registerer(registerer):
     """systematically kick previous registered class and register the
@@ -75,13 +63,13 @@
     def do_it_yourself(self, registered):
         # if object is accepting interface, we have register it now and
         # remove it later if no object is implementing accepted interfaces
-        if _accepts_interfaces(self.vobject):
+        if use_interfaces(self.vobject):
             return self.vobject
         self.remove_equivalents(registered)
         return self.vobject
     
     def equivalent(self, other):
-        if _accepts_interfaces(self.vobject) != _accepts_interfaces(other):
+        if use_interfaces(self.vobject) != use_interfaces(other):
             return False
         try:
             newaccepts = list(other.accepts)
--- a/cwvreg.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/cwvreg.py	Wed Feb 18 13:44:35 2009 +0100
@@ -18,11 +18,25 @@
 
 _ = unicode
 
-class DummyCursorError(Exception): pass
-class RaiseCursor:
-    @classmethod
-    def execute(cls, rql, args=None, eid_key=None):
-        raise DummyCursorError()
+def use_interfaces(obj):
+    from cubicweb.selectors import implements
+    try:
+        # XXX deprecated
+        return sorted(obj.accepts_interfaces) 
+    except AttributeError:
+        try:
+            impl = obj.__select__.search_selector(implements)
+            if impl:
+                return sorted(impl.expected_ifaces)
+        except AttributeError:
+            pass # old-style vobject classes with no accepts_interfaces
+        return ()
+
+def expand_parent_classes(iface):
+    res = [iface]
+    for parent in iface.__bases__:
+        res += expand_parent_classes(parent)
+    return res
 
 
 class CubicWebRegistry(VRegistry):
@@ -91,7 +105,7 @@
             kwargs['clear'] = True
         super(CubicWebRegistry, self).register(obj, **kwargs)
         # XXX bw compat
-        ifaces = getattr(obj, 'accepts_interfaces', None)
+        ifaces = use_interfaces(obj)
         if ifaces:
             self._needs_iface[obj] = frozenset(ifaces)
         
@@ -108,12 +122,15 @@
             interfaces = set()
             for classes in self.get('etypes', {}).values():
                 for cls in classes:
-                    interfaces.update(cls.__implements__)
+                    for iface in cls.__implements__:
+                        interfaces.update(expand_parent_classes(iface))
             for obj, ifaces in self._needs_iface.items():
                 if not ifaces & interfaces:
                     self.debug('kicking vobject %s (unsupported interface)', obj)
                     self.unregister(obj)
-
+            
+        
+    
     def eid_rset(self, cursor, eid, etype=None):
         """return a result set for the given eid without doing actual query
         (we have the eid, we can suppose it exists and user has access to the
--- a/devtools/htmlparser.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/devtools/htmlparser.py	Wed Feb 18 13:44:35 2009 +0100
@@ -6,7 +6,7 @@
 from lxml import etree
 from lxml.builder import E
 
-from cubicweb.common.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE, CW_XHTML_EXTENSIONS
+from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE, CW_XHTML_EXTENSIONS
 
 STRICT_DOCTYPE = str(STRICT_DOCTYPE % CW_XHTML_EXTENSIONS).strip()
 TRANSITIONAL_DOCTYPE = str(TRANSITIONAL_DOCTYPE % CW_XHTML_EXTENSIONS).strip()
--- a/selectors.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/selectors.py	Wed Feb 18 13:44:35 2009 +0100
@@ -54,7 +54,6 @@
 from cubicweb import Unauthorized, NoSelectableObject, role
 from cubicweb.vregistry import (NoSelectableObject, Selector,
                                 chainall, chainfirst, objectify_selector)
-from cubicweb.cwvreg import DummyCursorError
 from cubicweb.cwconfig import CubicWebConfiguration
 from cubicweb.schema import split_expression
 
@@ -70,7 +69,7 @@
         # /!\ lltrace decorates pure function or __call__ method, this
         #     means argument order may be different
         if isinstance(cls, Selector):
-            selname = cls.__class__.__name__
+            selname = str(cls)
             vobj = args[0]
         else:
             selname = selector.__name__
@@ -135,8 +134,9 @@
       - `once_is_enough` is False, in which case if score_class return 0, 0 is
         returned
     """
-    def __init__(self, once_is_enough=False):
+    def __init__(self, once_is_enough=False, sumscores=True):
         self.once_is_enough = once_is_enough
+        self.sumscores = sumscores
     
     @lltrace
     def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
@@ -157,7 +157,7 @@
             etype = rset.description[row][col]
             if etype is not None:
                 score = self.score(cls, req, etype)
-        return score and (score + 1)
+        return score
 
     def score(self, cls, req, etype):
         if etype in BASE_TYPES:
@@ -204,7 +204,7 @@
             etype = rset.description[row][col]
             if etype is not None: # outer join
                 score = self.score(req, rset, row, col)
-        return score and (score + 1)
+        return score
 
     def score(self, req, rset, row, col):
         try:
@@ -395,6 +395,10 @@
     def __init__(self, *expected):
         assert expected, self
         self.expected = frozenset(expected)
+
+    def __str__(self):
+        return '%s(%s)' % (self.__class__.__name__,
+                           ','.join(sorted(str(s) for s in self.expected)))
         
     @lltrace
     def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
@@ -511,6 +515,10 @@
         super(implements, self).__init__()
         self.expected_ifaces = expected_ifaces
 
+    def __str__(self):
+        return '%s(%s)' % (self.__class__.__name__,
+                           ','.join(str(s) for s in self.expected_ifaces))
+    
     def score_class(self, eclass, req):
         score = 0
         for iface in self.expected_ifaces:
@@ -518,21 +526,23 @@
                 # entity type
                 iface = eclass.vreg.etype_class(iface)
             if implements_iface(eclass, iface):
-                score += 1
                 if getattr(iface, '__registry__', None) == 'etypes':
-                    score += 1
                     # adjust score if the interface is an entity class
                     if iface is eclass:
-                        score += (len(eclass.e_schema.ancestors()) + 1)
-#                        print 'is majoration', len(eclass.e_schema.ancestors()) 
-                    else:
+                        score += len(eclass.e_schema.ancestors()) + 4
+                    else: 
                         parents = [e.type for e in eclass.e_schema.ancestors()]
                         for index, etype in enumerate(reversed(parents)):
                             basecls = eclass.vreg.etype_class(etype)
                             if iface is basecls:
-                                score += index
-#                                print 'etype majoration', index
+                                score += index + 3
                                 break
+                        else: # Any
+                            score += 1
+                else:
+                    # implenting an interface takes precedence other special Any
+                    # interface
+                    score += 2
         return score
 
 
--- a/sobjects/notification.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/sobjects/notification.py	Wed Feb 18 13:44:35 2009 +0100
@@ -19,7 +19,7 @@
 from logilab.common.deprecation import class_renamed
 
 from cubicweb import RegistryException
-from cubicweb.selectors import implements
+from cubicweb.selectors import implements, yes
 from cubicweb.common.view import EntityView, Component
 from cubicweb.common.mail import format_mail
 
@@ -36,7 +36,7 @@
     email addresses specified in the configuration are used
     """
     id = 'recipients_finder'
-    __select__ = implements('Any')
+    __select__ = yes()
     user_rql = ('Any X,E,A WHERE X is EUser, X in_state S, S name "activated",'
                 'X primary_email E, E address A')
     
--- a/test/unittest_selectors.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/test/unittest_selectors.py	Wed Feb 18 13:44:35 2009 +0100
@@ -8,6 +8,9 @@
 from logilab.common.testlib import TestCase, unittest_main
 
 from cubicweb.vregistry import Selector, AndSelector, OrSelector
+from cubicweb.selectors import implements
+
+from cubicweb.interfaces import IDownloadable
 
 class _1_(Selector):
     def __call__(self, *args, **kwargs):
@@ -74,7 +77,26 @@
         self.assertEquals(len(selector.selectors), 2)
         self.assertEquals(selector(None), 2)
 
+    def test_search_selectors(self):
+        sel = implements('something')
+        self.assertIs(sel.search_selector(implements), sel)
+        csel = AndSelector(sel, Selector())
+        self.assertIs(csel.search_selector(implements), sel)
+        csel = AndSelector(Selector(), sel)
+        self.assertIs(csel.search_selector(implements), sel)
+        
+from cubicweb.devtools.testlib import EnvBasedTC
 
+class ImplementsSelectorTC(EnvBasedTC):
+    def test_etype_priority(self):
+        req = self.request()
+        cls = self.vreg.etype_class('File')
+        anyscore = implements('Any').score_class(cls, req)
+        idownscore = implements(IDownloadable).score_class(cls, req)
+        self.failUnless(idownscore > anyscore, (idownscore, anyscore))
+        filescore = implements('File').score_class(cls, req)
+        self.failUnless(filescore > idownscore, (filescore, idownscore))
+    
 if __name__ == '__main__':
     unittest_main()
 
--- a/test/unittest_vregistry.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/test/unittest_vregistry.py	Wed Feb 18 13:44:35 2009 +0100
@@ -5,7 +5,9 @@
 from cubicweb import CW_SOFTWARE_ROOT as BASE
 from cubicweb.vregistry import VObject
 from cubicweb.cwvreg import CubicWebRegistry, UnknownProperty
-from cubicweb.cwconfig import CubicWebConfiguration
+from cubicweb.devtools import TestServerConfiguration
+from cubicweb.entities.lib import Card
+from cubicweb.interfaces import IMileStone
 
 class YesSchema:
     def __contains__(self, something):
@@ -14,9 +16,10 @@
 class VRegistryTC(TestCase):
 
     def setUp(self):
-        config = CubicWebConfiguration('data')
+        config = TestServerConfiguration('data')
         self.vreg = CubicWebRegistry(config)
-        self.vreg.schema = YesSchema()
+        config.bootstrap_cubes()
+        self.vreg.schema = config.load_schema()
 
     def test_load(self):
         self.vreg.load_file(join(BASE, 'web', 'views'), 'euser.py')
@@ -45,6 +48,19 @@
         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_vobjects(self):
+        self.vreg.reset()
+        self.vreg.register_objects([join(BASE, 'web', 'views', 'iprogress.py')])
+        # check progressbar was kicked
+        self.failIf(self.vreg['views'].get('progressbar'))
+        class MyCard(Card):
+            __implements__ = (IMileStone,)
+        self.vreg.reset()
+        self.vreg.register_vobject_class(MyCard)
+        self.vreg.register_objects([join(BASE, 'web', 'views', 'iprogress.py')])
+        # check progressbar isn't kicked
+        self.assertEquals(len(self.vreg['views']['progressbar']), 1)
         
 
 if __name__ == '__main__':
--- a/view.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/view.py	Wed Feb 18 13:44:35 2009 +0100
@@ -12,7 +12,7 @@
 from logilab.mtconverter import html_escape
 
 from cubicweb import NotAnEntity, NoSelectableObject
-from cubicweb.selectors import (yes, match_user_groups, implements,
+from cubicweb.selectors import (yes, match_user_groups, non_final_entity,
                                 nonempty_rset, none_rset)
 from cubicweb.selectors import require_group_compat, accepts_compat
 from cubicweb.appobject import AppRsetObject
@@ -322,7 +322,7 @@
     """
     # XXX deprecate
     __registerer__ = accepts_registerer
-    __select__ = implements('Any')
+    __select__ = non_final_entity()
     registered = accepts_compat(View.registered)
 
     category = 'entityview'
@@ -355,7 +355,7 @@
     """base class for entity views which may also be applied to None
     result set (usually a default rql is provided by the view class)
     """
-    __select__ = none_rset() | implements('Any')
+    __select__ = none_rset() | non_final_entity()
 
     default_rql = None
 
--- a/vregistry.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/vregistry.py	Wed Feb 18 13:44:35 2009 +0100
@@ -140,8 +140,7 @@
                 # only one selector
                 select = _instantiate_selector(selectors[0])
             else:
-                select = AndSelector(*[_instantiate_selector(selector)
-                                       for selector in selectors])
+                select = AndSelector(*selectors)
             cls.__select__ = select
 
 
@@ -579,6 +578,9 @@
         if isinstance(selector, type) and isinstance(self, selector):
             return self
         return None
+
+    def __str__(self):
+        return self.__class__.__name__
     
     def __and__(self, other):
         return AndSelector(self, other)
@@ -602,15 +604,22 @@
     def __init__(self, *selectors):
         self.selectors = self.merge_selectors(selectors)
 
+    def __str__(self):
+        return '%s(%s)' % (self.__class__.__name__,
+                           ','.join(str(s) for s in self.selectors))
+
     @classmethod
     def merge_selectors(cls, selectors):
-        """merge selectors when possible :
+        """deal with selector instanciation when necessary and merge
+        multi-selectors if possible:
 
         AndSelector(AndSelector(sel1, sel2), AndSelector(sel3, sel4))
         ==> AndSelector(sel1, sel2, sel3, sel4)
         """
         merged_selectors = []
         for selector in selectors:
+            selector = _instantiate_selector(selector)
+            assert isinstance(selector, Selector), selector
             if isinstance(selector, cls):
                 merged_selectors += selector.selectors
             else:
@@ -622,12 +631,11 @@
         tree. Return it of None if not found
         """
         for childselector in self.selectors:
-            try:
-                if childselector.use_selector(selector):
-                    return childselector
-            except AttributeError: # simple function
-                if childselector is selector:
-                    return childselector
+            if childselector is selector:
+                return childselector
+            found = childselector.search_selector(selector)
+            if found is not None:
+                return found
         return None
 
     
@@ -686,8 +694,7 @@
     """
     assert selectors
     # XXX do we need to create the AndSelector here, a tuple might be enough
-    selector = AndSelector(*[_instantiate_selector(selector)
-                             for selector in selectors])
+    selector = AndSelector(*selectors)
     if 'name' in kwargs:
         selector.__name__ = kwargs['name']
     return selector
@@ -698,8 +705,7 @@
     will be the first non-zero selector score
     """
     assert selectors
-    selector = OrSelector(*[_instantiate_selector(selector)
-                            for selector in selectors])
+    selector = OrSelector(*selectors)
     if 'name' in kwargs:
         selector.__name__ = kwargs['name']
     return selector
--- a/web/test/unittest_viewselector.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/web/test/unittest_viewselector.py	Wed Feb 18 13:44:35 2009 +0100
@@ -1,25 +1,21 @@
 # -*- coding: iso-8859-1 -*-
 """XXX rename, split, reorganize this
 """
+from __future__ import with_statement
 
-import os.path as osp
-
-from logilab.common.testlib import TestCase, unittest_main
+from logilab.common.testlib import unittest_main
 from cubicweb.devtools.apptest import EnvBasedTC
 
 
 from cubicweb import CW_SOFTWARE_ROOT as BASE, Binary
-from cubicweb.selectors import match_user_groups, implements, rql_condition
+from cubicweb.selectors import match_user_groups, implements, rql_condition, traced_selection
 
 from cubicweb.web._exceptions import NoSelectableObject
 from cubicweb.web.action import Action
 from cubicweb.web.views import (baseviews, tableview, baseforms, calendar, 
                                 management, embedding, actions, startup, 
-                                euser, schemaentities, xbel, vcard,
-                                treeview, idownloadable, wdoc, debug)
-from cubicweb.entities.lib import Card
-from cubicweb.interfaces import IMileStone
-from cubicweb.web.views import owl
+                                euser, schemaentities, xbel, vcard, owl,
+                                treeview, idownloadable, wdoc, debug, eproperties)
 
 USERACTIONS = [('myprefs', actions.UserPreferencesAction),
                ('myinfos', actions.UserInfoAction),
@@ -71,24 +67,17 @@
         self.assertListEqual(self.pviews(req, None),
                              [('changelog', wdoc.ChangeLogView),
                               ('debug', debug.DebugView),
-                              ('epropertiesform', management.EpropertiesForm),
+                              ('epropertiesform', eproperties.EPropertiesForm),
                               ('index', startup.IndexView),
                               ('info', management.ProcessInformationView),
                               ('manage', startup.ManageView),
                               ('owl', owl.OWLView),
                               ('schema', startup.SchemaView),
-                              ('systemepropertiesform', management.SystemEpropertiesForm)])
+                              ('systemepropertiesform', eproperties.SystemEPropertiesForm)])
         # no entity but etype
         rset, req = self.env.get_rset_and_req('Any X WHERE X eid 999999')
         self.assertListEqual(self.pviews(req, rset),
-                             [#('changelog', wdoc.ChangeLogView),
-                              #('epropertiesform', management.EpropertiesForm),
-                              #('index', startup.IndexView),
-                              #('info', management.ProcessInformationView),
-                              #('manage', startup.ManageView),
-                              #('schema', startup.SchemaView),
-                              #('systemepropertiesform', management.SystemEpropertiesForm)
-                                 ])
+                             [])
         # one entity
         rset, req = self.env.get_rset_and_req('EGroup X WHERE X name "managers"')
         self.assertListEqual(self.pviews(req, rset),
@@ -228,19 +217,6 @@
                               'moreactions': [('delete', actions.DeleteAction),
                                               ('copy', actions.CopyAction)],
                               })
-
-    def test_load_subinterface_based_vojects(self):
-        self.vreg._lastmodifs = {} # clear cache
-        self.vreg.register_objects([osp.join(BASE, 'web', 'views', 'iprogress.py')])
-        # check progressbar was kicked
-        self.failIf('progressbar' in self.vreg['views'])
-        class MyCard(Card):
-            __implements__ = (IMileStone,)
-        self.vreg.register_vobject_class(MyCard)
-        self.vreg._lastmodifs = {} # clear cache
-        self.vreg.register_objects([osp.join(BASE, 'web', 'views', 'iprogress.py')])
-        # check progressbar isn't kicked
-        self.assertEquals(len(self.vreg['views']['progressbar']), 1)
         
 
     def test_select_creation_form(self):
@@ -434,8 +410,6 @@
             del self.vreg[SomeAction.__registry__][SomeAction.id]
 
 
-from cubicweb.web.action import Action
-
 class EETypeRQLAction(Action):
     id = 'testaction'
     __select__ = implements('EEType') & rql_condition('X name "EEType"')
@@ -459,7 +433,8 @@
                               'mainactions': [('edit', actions.ModifyAction)],
                               'moreactions': [('delete', actions.DeleteAction),
                                               ('copy', actions.CopyAction),
-                                              ('testaction', EETypeRQLAction)],
+                                              ('testaction', EETypeRQLAction),
+                                              ('managepermission', actions.ManagePermissionsAction)],
                               })
         rset, req = self.env.get_rset_and_req('EEType X WHERE X name "ERType"')
         self.assertDictEqual(self.pactions(req, rset),
@@ -467,7 +442,8 @@
                               'siteactions': SITEACTIONS,
                               'mainactions': [('edit', actions.ModifyAction)],
                               'moreactions': [('delete', actions.DeleteAction),
-                                              ('copy', actions.CopyAction)],
+                                              ('copy', actions.CopyAction),
+                                              ('managepermission', actions.ManagePermissionsAction)],
                               })
         
 
--- a/web/views/actions.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/web/views/actions.py	Wed Feb 18 13:44:35 2009 +0100
@@ -129,7 +129,7 @@
 # generic secondary actions ###################################################
 
 class ManagePermissionsAction(Action):
-    id = 'addpermission'
+    id = 'managepermission'
     __select__ = match_user_groups('managers') 
 
     title = _('manage permissions')
--- a/web/views/baseforms.py	Wed Feb 18 13:36:28 2009 +0100
+++ b/web/views/baseforms.py	Wed Feb 18 13:44:35 2009 +0100
@@ -15,9 +15,9 @@
 from logilab.common.decorators import cached
 
 from cubicweb.interfaces import IWorkflowable
-from cubicweb.selectors import (specified_etype_implements,
-                                match_kwargs, match_form_params, 
-                                one_line_rset, implements)
+from cubicweb.selectors import (specified_etype_implements, implements,
+                                match_kwargs, match_form_params, one_line_rset,
+                                non_final_entity)
 from cubicweb.utils import make_uid
 from cubicweb.view import EntityView
 from cubicweb.common.uilib import cut
@@ -217,7 +217,7 @@
     being connected
     """    
     id = 'edition'
-    __select__ = one_line_rset() & implements('Any')
+    __select__ = one_line_rset() & non_final_entity()
 
     title = _('edition')
     controller = 'edit'
@@ -676,8 +676,7 @@
 
 class InlineEntityEditionForm(InlineFormMixIn, EditionForm):
     id = 'inline-edition'
-    __select__ = (implements('Any')
-                  & match_kwargs('ptype', 'peid', 'rtype'))
+    __select__ = non_final_entity() & match_kwargs('ptype', 'peid', 'rtype')
     
     EDITION_BODY = u'''\
 <div onclick="restoreInlinedEntity('%(parenteid)s', '%(rtype)s', '%(eid)s')" id="div-%(parenteid)s-%(rtype)s-%(eid)s" class="inlinedform">