--- 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">