more selectors update tls-sprint
authorsylvain.thenault@logilab.fr
Mon, 16 Feb 2009 19:50:10 +0100
branchtls-sprint
changeset 633 087e3f1e87c8
parent 632 3a394a90b702
child 634 0badd061ce0f
more selectors update
common/selectors.py
common/view.py
sobjects/notification.py
test/unittest_rset.py
test/unittest_vregistry.py
web/views/baseforms.py
--- a/common/selectors.py	Mon Feb 16 19:20:30 2009 +0100
+++ b/common/selectors.py	Mon Feb 16 19:50:10 2009 +0100
@@ -70,7 +70,8 @@
             oid = cls.id
         ret = selector(cls, *args, **kwargs)
         if TRACED_OIDS == 'all' or oid in TRACED_OIDS:
-            SELECTOR_LOGGER.warning('selector %s returned %s for %s', selname, ret, cls)
+            #SELECTOR_LOGGER.warning('selector %s returned %s for %s', selname, ret, cls)
+            print 'selector %s returned %s for %s' % (selname, ret, cls)
         return ret
     traced.__name__ = selector.__name__
     return traced
@@ -213,9 +214,10 @@
 
 
 class match_search_state(Selector):
-    def __init__(self, *expected_states):
-        self.expected_states = expected_states
+    def __init__(self, *expected):
+        self.expected = expected
         
+    @lltrace
     def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
         """checks if the current request search state is in one of the expected states
         the wrapped class
@@ -224,12 +226,40 @@
         object to create a relation with another)
         """
         try:
-            if not req.search_state[0] in cls.search_states:
+            if not req.search_state[0] in self.expected:
                 return 0
         except AttributeError:
             return 1 # class doesn't care about search state, accept it
         return 1
 
+
+class match_form_params(match_search_state):
+    """check if parameters specified as initializer arguments are specified
+    in request form parameters
+    """
+    @lltrace
+    def __call__(self, cls, req, *args, **kwargs):
+        score = 0
+        for param in self.expected:
+            val = req.form.get(param)
+            if not val:
+                return 0
+            score += 1
+        return len(self.expected)
+
+
+class match_kwargs(match_search_state):
+    """check if parameters specified as initializer arguments are specified
+    in named parameters
+    """
+    @lltrace
+    def __call__(self, cls, req, *args, **kwargs):
+        for arg in self.expected:
+            if not arg in kwargs:
+                return 0
+        return len(self.expected)
+
+
 @lltrace
 def anonymous_user(cls, req, *args, **kwargs):
     """accept if user is anonymous"""
@@ -244,32 +274,6 @@
     return not anonymous_user(cls, req, *args, **kwargs)
 not_anonymous_selector = deprecated_function(authenticated_user)
 
-@lltrace
-def match_form_params(cls, req, *args, **kwargs):
-    """check if parameters specified by the form_params attribute on
-    the wrapped class are specified in request form parameters
-    """
-    score = 0
-    for param in cls.form_params:
-        val = req.form.get(param)
-        if not val:
-            return 0
-        score += 1
-    return score + 1
-req_form_params_selector = deprecated_function(match_form_params)
-
-@lltrace
-def match_kwargs(cls, req, *args, **kwargs):
-    """check if arguments specified by the expected_kwargs attribute on
-    the wrapped class are specified in given named parameters
-    """
-    values = []
-    for arg in cls.expected_kwargs:
-        if not arg in kwargs:
-            return 0
-    return 1
-kwargs_selector = deprecated_function(match_kwargs)
-
 # abstract selectors ##########################################################
 
 class EClassSelector(Selector):
@@ -364,19 +368,39 @@
             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
+                        score += len(eclass.e_schema.ancestors())
+                        print 'is majoration', len(eclass.e_schema.ancestors()) 
                     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 + 1
+                                score += index
+                                print 'etype majoration', index
                                 break
         return score
 
 
+class specified_etype_implements(implements):
+    """return the "interface score" for class associated to 'etype' (expected in
+    request form or arguments)
+    """
+    
+    @lltrace
+    def __call__(cls, req, *args, **kwargs):
+        try:
+            etype = req.form['etype']
+        except KeyError:
+            try:
+                etype = kwargs['etype']
+            except KeyError:
+                return 0
+        return self.score_class(cls.vreg.etype_class(etype), req)
+
+
 class relation_possible(EClassSelector):
     """initializer takes relation name as argument and an optional role (default
       as subject) and target type (default to unspecified)
@@ -490,8 +514,8 @@
 
 
 class may_add_relation(EntitySelector):
-    """initializer a relation type and optional role (default to 'subject') as
-    argument
+    """initializer takes a relation type and optional role (default to
+    'subject') as argument
 
     if row is specified check the relation may be added to the entity at the
     given row/col (if row specified) or to every entities in the given col (if
@@ -565,7 +589,9 @@
 
 
 class has_add_permission(EClassSelector):
-    
+    """return 1 if the user may add some entity of the types found in the
+    result set (0 else)
+    """
     def score_class(self, eclass, req):
         eschema = eclass.e_schema
         if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
@@ -575,25 +601,20 @@
 
         
 class score_entity(EntitySelector):
+    """initializer takes a function as argument (which is expected to take an
+    entity as argument)
+
+    return the score returned by the function on the entity at the given row/col
+    (if row specified) or the sum of the score for every entities in the given
+    col (if row is not specified). Return 0 at the first entity scoring to zero.
+    """
     def __init__(self, scorefunc):
         self.score_entity = scorefunc
 
+    
 # XXX not so basic selectors ######################################################
 
 @lltrace
-def accept_etype(cls, req, *args, **kwargs):
-    """check etype presence in request form *and* accepts conformance"""
-    try:
-        etype = req.form['etype']
-    except KeyError:
-        try:
-            etype = kwargs['etype']
-        except KeyError:
-            return 0
-    return implements(*cls.accepts).score_class(cls.vreg.etype_class(etype), req)
-etype_form_selector = deprecated_function(accept_etype)
-
-@lltrace
 def _rql_condition(cls, req, rset, row=None, col=0, **kwargs):
     """accept single entity result set if the entity match an rql condition
     """
@@ -697,14 +718,20 @@
     return implements(*cls.accepts_interfaces)(cls, req, rset, row, col)
 _interface_selector = deprecated_function(implement_interface)
 interface_selector = deprecated_function(implement_interface)
-implement_interface = deprecated_function(implement_interface)
+implement_interface = deprecated_function(implement_interface, 'use implements')
+
+def accept_etype(cls, req, *args, **kwargs):
+    """check etype presence in request form *and* accepts conformance"""
+    return specified_etype_implements(*cls.accepts)(cls, req, *args)
+etype_form_selector = deprecated_function(accept_etype)
+accept_etype = deprecated_function(accept_etype, 'use specified_etype_implements')
 
 def searchstate_selector(cls, req, rset, row=None, col=0, **kwargs):
     return match_search_state(cls.search_states)(cls, req, rset, row, col)
 searchstate_selector = deprecated_function(searchstate_selector)
 
 def match_user_group(cls, req, rset=None, row=None, col=0, **kwargs):
-    return match_user_groups(cls.require_groups)(cls, req, rset, row, col, **kwargs)
+    return match_user_groups(*cls.require_groups)(cls, req, rset, row, col, **kwargs)
 in_group_selector = deprecated_function(match_user_group)
 match_user_group = deprecated_function(match_user_group)
 
@@ -753,3 +780,6 @@
                                             name='searchstate_accept_one_but_etype')
 searchstate_accept_one_but_etype_selector = deprecated_function(
     searchstate_accept_one_but_etype)
+
+#req_form_params_selector = deprecated_function(match_form_params) # form_params
+#kwargs_selector = deprecated_function(match_kwargs) # expected_kwargs
--- a/common/view.py	Mon Feb 16 19:20:30 2009 +0100
+++ b/common/view.py	Mon Feb 16 19:50:10 2009 +0100
@@ -8,18 +8,41 @@
 __docformat__ = "restructuredtext en"
 
 from cStringIO import StringIO
+from warnings import warn
 
 from logilab.mtconverter import html_escape
 
 from cubicweb import NotAnEntity, NoSelectableObject
 from cubicweb.common.registerers import accepts_registerer, priority_registerer
-from cubicweb.common.selectors import (chainfirst, match_user_group, accept,
-                                       nonempty_rset, empty_rset, none_rset)
+from cubicweb.common.selectors import (yes, match_user_groups, implements,
+                                       nonempty_rset, none_rset)
 from cubicweb.common.appobject import AppRsetObject, ComponentMixIn
 from cubicweb.common.utils import UStringIO, HTMLStream
 
 _ = unicode
 
+
+def require_group_compat(registered):
+    def plug_selector(cls, vreg):
+        cls = registered(cls, vreg)
+        if getattr(cls, 'require_groups', None):
+            warn('use "use match_user_groups(group1, group2)" instead of using require_groups',
+                 DeprecationWarning)
+            cls.__selectors__ += (match_user_groups(cls.require_groups),)
+        return cls
+    return classmethod(plug_selector)
+
+def accepts_compat(registered):
+    def plug_selector(cls, vreg):
+        cls = registered(cls, vreg)
+        if getattr(cls, 'accepts', None):
+            warn('use "use match_user_groups(group1, group2)" instead of using require_groups',
+                 DeprecationWarning)
+            cls.__selectors__ += (implements(*cls.accepts),)
+        return cls
+    return classmethod(plug_selector)
+
+
 # robots control
 NOINDEX = u'<meta name="ROBOTS" content="NOINDEX" />'
 NOFOLLOW = u'<meta name="ROBOTS" content="NOFOLLOW" />'
@@ -302,9 +325,11 @@
     """base class for views applying on an entity (i.e. uniform result set)
     """
     __registerer__ = accepts_registerer
-    __selectors__ = (accept,)
+    __selectors__ = (implements('Any'),)
+    registered = accepts_compat(View.registered.im_func)
+
     category = 'entityview'
-
+    
     def field(self, label, value, row=True, show_label=True, w=None, tr=True):
         """ read-only field """
         if w is None:
@@ -325,10 +350,11 @@
     to be displayed (so they can always be displayed !)
     """
     __registerer__ = priority_registerer
-    __selectors__ = (match_user_group, none_rset)
-    require_groups = ()
+    __selectors__ = (none_rset,)
+    registered = require_group_compat(View.registered.im_func)
+    
     category = 'startupview'
-
+    
     def url(self):
         """return the url associated with this view. We can omit rql here"""
         return self.build_url('view', vid=self.id)
@@ -347,7 +373,7 @@
     result set (usually a default rql is provided by the view class)
     """
     __registerer__ = accepts_registerer
-    __selectors__ = (chainfirst(none_rset, accept),)
+    __selectors__ = ((none_rset | implements('Any')),)
 
     default_rql = None
 
@@ -404,7 +430,7 @@
             labels.append(label)
         return labels
 
-
+    
 # concrete template base classes ##############################################
 
 class Template(View):
@@ -413,9 +439,9 @@
     """
     __registry__ = 'templates'
     __registerer__ = priority_registerer
-    __selectors__ = (match_user_group,)
+    __selectors__ = (yes,)
 
-    require_groups = ()
+    registered = require_group_compat(View.registered.im_func)
 
     def template(self, oid, **kwargs):
         """shortcut to self.registry.render method on the templates registry"""
--- a/sobjects/notification.py	Mon Feb 16 19:20:30 2009 +0100
+++ b/sobjects/notification.py	Mon Feb 16 19:50:10 2009 +0100
@@ -1,7 +1,7 @@
 """some hooks and views to handle notification on entity's changes
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -21,7 +21,7 @@
 from cubicweb.common.view import EntityView
 from cubicweb.common.appobject import Component
 from cubicweb.common.registerers import accepts_registerer
-from cubicweb.common.selectors import accept
+from cubicweb.common.selectors import implements
 from cubicweb.common.mail import format_mail
 
 from cubicweb.server.pool import PreCommitOperation
@@ -38,8 +38,7 @@
     """
     id = 'recipients_finder'
     __registerer__ = accepts_registerer
-    __selectors__ = (accept,)
-    accepts = ('Any',)
+    __selectors__ = (implements('Any'),)
     user_rql = ('Any X,E,A WHERE X is EUser, X in_state S, S name "activated",'
                 'X primary_email E, E address A')
     
@@ -299,7 +298,7 @@
 
 class CardAddedView(NormalizedTextView):
     """get notified from new cards"""
-    accepts = ('Card',)
+    __selectors__ = (implements('Card'),)
     content_attr = 'synopsis'
     
 
--- a/test/unittest_rset.py	Mon Feb 16 19:20:30 2009 +0100
+++ b/test/unittest_rset.py	Mon Feb 16 19:50:10 2009 +0100
@@ -227,7 +227,8 @@
         self.assertEquals(e.col, 0)
         self.assertEquals(e['title'], 'zou')
         self.assertRaises(KeyError, e.__getitem__, 'path')
-        self.assertEquals(e.view('text'), 'zou')
+        with traced_selection():
+            self.assertEquals(e.view('text'), 'zou')
         self.assertEquals(pprelcachedict(e._related_cache), [])
         
         e = rset.get_entity(0, 1)
--- a/test/unittest_vregistry.py	Mon Feb 16 19:20:30 2009 +0100
+++ b/test/unittest_vregistry.py	Mon Feb 16 19:50:10 2009 +0100
@@ -22,7 +22,7 @@
         self.vreg.load_file(join(BASE, 'web', 'views'), 'euser.py')
         self.vreg.load_file(join(BASE, 'web', 'views'), 'baseviews.py')
         fpvc = [v for v in self.vreg.registry_objects('views', 'primary')
-               i f v.__module__ == 'cubicweb.web.views.euser'][0]
+               if v.__module__ == 'cubicweb.web.views.euser'][0]
         fpv = fpvc(None, None)
         # don't want a TypeError due to super call
         self.assertRaises(AttributeError, fpv.render_entity_attributes, None, None)
--- a/web/views/baseforms.py	Mon Feb 16 19:20:30 2009 +0100
+++ b/web/views/baseforms.py	Mon Feb 16 19:50:10 2009 +0100
@@ -17,9 +17,9 @@
 from cubicweb.interfaces import IWorkflowable
 from cubicweb.common.utils import make_uid
 from cubicweb.common.uilib import cut
-from cubicweb.common.selectors import (accept_etype, match_kwargs,
-                                    one_line_rset, implement_interface,
-                                    match_form_params, accept)
+from cubicweb.common.selectors import (specified_etype_implements,
+                                       match_kwargs, match_form_params, 
+                                       one_line_rset, implements)
 from cubicweb.common.view import EntityView
 from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param
 from cubicweb.web.controller import NAV_FORM_PARAMETERS
@@ -90,9 +90,7 @@
     id = 'statuschange'
     title = _('status change')
 
-    __selectors__ = (implement_interface, match_form_params)
-    accepts_interfaces = (IWorkflowable,)
-    form_params = ('treid',)
+    __selectors__ = (implements(IWorkflowable), match_form_params('treid'))
 
     def cell_call(self, row, col, vid='secondary'):
         entity = self.entity(row, col)
@@ -153,8 +151,7 @@
 
 class ClickAndEditForm(EntityForm):
     id = 'reledit'
-    __selectors__ = (match_kwargs, )
-    expected_kwargs = ('rtype',)
+    __selectors__ = (match_kwargs('rtype'), )
 
     #FIXME editableField class could be toggleable from userprefs
 
@@ -219,7 +216,7 @@
     dynamic default values such as the 'tomorrow' date or the user's login
     being connected
     """    
-    __selectors__ = (one_line_rset, accept)
+    __selectors__ = (one_line_rset, implements('Any'))
 
     id = 'edition'
     title = _('edition')
@@ -526,7 +523,7 @@
 
     
 class CreationForm(EditionForm):
-    __selectors__ = (accept_etype, )
+    __selectors__ = (specified_etype_implements('Any'), )
     id = 'creation'
     title = _('creation')
     
@@ -639,8 +636,8 @@
 
 class InlineEntityCreationForm(InlineFormMixIn, CreationForm):
     id = 'inline-creation'
-    __selectors__ = (match_kwargs, accept_etype)
-    expected_kwargs = ('ptype', 'peid', 'rtype')
+    __selectors__ = (match_kwargs('ptype', 'peid', 'rtype'), specified_etype_implements('Any'))
+    
     
     EDITION_BODY = u'''\
 <div id="div-%(parenteid)s-%(rtype)s-%(eid)s" class="inlinedform">
@@ -678,8 +675,7 @@
 
 class InlineEntityEditionForm(InlineFormMixIn, EditionForm):
     id = 'inline-edition'
-    __selectors__ = (accept, match_kwargs)
-    expected_kwargs = ('ptype', 'peid', 'rtype')
+    __selectors__ = (implements('Any'), 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">   
@@ -881,8 +877,7 @@
 
 class UnrelatedDivs(EntityView):
     id = 'unrelateddivs'
-    __selectors__ = (match_form_params,)
-    form_params = ('relation',)
+    __selectors__ = (match_form_params('relation',),)
 
     @property
     def limit(self):
@@ -993,7 +988,6 @@
     THIS IS A TEXT VIEW. DO NOT HTML_ESCAPE
     """
     id = 'combobox'
-    accepts = ('Any',)
     title = None
     
     def cell_call(self, row, col):