# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1234810210 -3600 # Node ID 087e3f1e87c8df69f1d166ef62b287bfd51c0768 # Parent 3a394a90b7020282be09db4cbacd0b23d3e69cc9 more selectors update diff -r 3a394a90b702 -r 087e3f1e87c8 common/selectors.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 diff -r 3a394a90b702 -r 087e3f1e87c8 common/view.py --- 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'' NOFOLLOW = u'' @@ -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""" diff -r 3a394a90b702 -r 087e3f1e87c8 sobjects/notification.py --- 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' diff -r 3a394a90b702 -r 087e3f1e87c8 test/unittest_rset.py --- 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) diff -r 3a394a90b702 -r 087e3f1e87c8 test/unittest_vregistry.py --- 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) diff -r 3a394a90b702 -r 087e3f1e87c8 web/views/baseforms.py --- 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'''\
@@ -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'''\
@@ -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):