--- a/appobject.py Mon Aug 03 14:14:07 2009 +0200
+++ b/appobject.py Mon Aug 03 15:16:47 2009 +0200
@@ -1,4 +1,6 @@
-"""Base class for dynamically loaded objects manipulated in the web interface
+"""Base class for dynamically loaded objects accessible through the vregistry.
+
+You'll also find some convenience classes to build selectors.
:organization: Logilab
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -7,20 +9,23 @@
"""
__docformat__ = "restructuredtext en"
+import types
+from logging import getLogger
from datetime import datetime, timedelta, time
from logilab.common.decorators import classproperty
from logilab.common.deprecation import deprecated
+from logilab.common.logging_ext import set_log_methods
from rql.nodes import VariableRef, SubQuery
from rql.stmts import Union, Select
from cubicweb import Unauthorized, NoSelectableObject
-from cubicweb.vregistry import VObject, AndSelector
-from cubicweb.selectors import yes
from cubicweb.utils import UStringIO, ustrftime, strptime, todate, todatetime
ONESECOND = timedelta(0, 1, 0)
+CACHE_REGISTRY = {}
+
class Cache(dict):
def __init__(self):
@@ -29,14 +34,200 @@
self.cache_creation_date = _now
self.latest_cache_lookup = _now
-CACHE_REGISTRY = {}
+
+# selector base classes and operations ########################################
+
+def objectify_selector(selector_func):
+ """convenience decorator for simple selectors where a class definition
+ would be overkill::
+
+ @objectify_selector
+ def yes(cls, *args, **kwargs):
+ return 1
+
+ """
+ return type(selector_func.__name__, (Selector,),
+ {'__call__': lambda self, *args, **kwargs: selector_func(*args, **kwargs)})
+
+
+def _instantiate_selector(selector):
+ """ensures `selector` is a `Selector` instance
+
+ NOTE: This should only be used locally in build___select__()
+ XXX: then, why not do it ??
+ """
+ if isinstance(selector, types.FunctionType):
+ return objectify_selector(selector)()
+ if isinstance(selector, type) and issubclass(selector, Selector):
+ return selector()
+ return selector
+
+
+class Selector(object):
+ """base class for selector classes providing implementation
+ for operators ``&`` and ``|``
+
+ This class is only here to give access to binary operators, the
+ selector logic itself should be implemented in the __call__ method
+
+
+ a selector is called to help choosing the correct object for a
+ particular context by returning a score (`int`) telling how well
+ the class given as first argument apply to the given context.
+
+ 0 score means that the class doesn't apply.
+ """
+
+ @property
+ def func_name(self):
+ # backward compatibility
+ return self.__class__.__name__
+
+ def search_selector(self, selector):
+ """search for the given selector or selector instance in the selectors
+ tree. Return it of None if not found
+ """
+ if self is selector:
+ return self
+ 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)
+ def __rand__(self, other):
+ return AndSelector(other, self)
+
+ def __or__(self, other):
+ return OrSelector(self, other)
+ def __ror__(self, other):
+ return OrSelector(other, self)
+
+ def __invert__(self):
+ return NotSelector(self)
+
+ # XXX (function | function) or (function & function) not managed yet
+
+ def __call__(self, cls, *args, **kwargs):
+ return NotImplementedError("selector %s must implement its logic "
+ "in its __call__ method" % self.__class__)
+
+
+class MultiSelector(Selector):
+ """base class for compound selector classes"""
+
+ 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):
+ """deal with selector instanciation when necessary and merge
+ multi-selectors if possible:
-class AppObject(VObject):
- """This is the base class for CubicWeb application objects
- which are selected according to a request and result set.
+ AndSelector(AndSelector(sel1, sel2), AndSelector(sel3, sel4))
+ ==> AndSelector(sel1, sel2, sel3, sel4)
+ """
+ merged_selectors = []
+ for selector in selectors:
+ try:
+ selector = _instantiate_selector(selector)
+ except:
+ pass
+ #assert isinstance(selector, Selector), selector
+ if isinstance(selector, cls):
+ merged_selectors += selector.selectors
+ else:
+ merged_selectors.append(selector)
+ return merged_selectors
+
+ def search_selector(self, selector):
+ """search for the given selector or selector instance in the selectors
+ tree. Return it of None if not found
+ """
+ for childselector in self.selectors:
+ if childselector is selector:
+ return childselector
+ found = childselector.search_selector(selector)
+ if found is not None:
+ return found
+ return None
+
+
+class AndSelector(MultiSelector):
+ """and-chained selectors (formerly known as chainall)"""
+ def __call__(self, cls, *args, **kwargs):
+ score = 0
+ for selector in self.selectors:
+ partscore = selector(cls, *args, **kwargs)
+ if not partscore:
+ return 0
+ score += partscore
+ return score
+
- Classes are kept in the vregistry and instantiation is done at selection
- time.
+class OrSelector(MultiSelector):
+ """or-chained selectors (formerly known as chainfirst)"""
+ def __call__(self, cls, *args, **kwargs):
+ for selector in self.selectors:
+ partscore = selector(cls, *args, **kwargs)
+ if partscore:
+ return partscore
+ return 0
+
+class NotSelector(Selector):
+ """negation selector"""
+ def __init__(self, selector):
+ self.selector = selector
+
+ def __call__(self, cls, *args, **kwargs):
+ score = self.selector(cls, *args, **kwargs)
+ return int(not score)
+
+ def __str__(self):
+ return 'NOT(%s)' % super(NotSelector, self).__str__()
+
+
+class yes(Selector):
+ """return arbitrary score
+
+ default score of 0.5 so any other selector take precedence
+ """
+ def __init__(self, score=0.5):
+ self.score = score
+
+ def __call__(self, *args, **kwargs):
+ return self.score
+
+
+# the base class for all appobjects ############################################
+
+class AppObject(object):
+ """This is the base class for CubicWeb application objects which are
+ selected according to a context (usually at least a request and a result
+ set).
+
+ Concrete application objects classes are designed to be loaded by the
+ vregistry and should be accessed through it, not by direct instantiation.
+
+ The following attributes should be set on concret appobject classes:
+ :__registry__:
+ name of the registry for this object (string like 'views',
+ 'templates'...)
+ :id:
+ object's identifier in the registry (string like 'main',
+ 'primary', 'folder_box')
+ :__select__:
+ class'selector
+
+ Moreover, the `__abstract__` attribute may be set to True to indicate
+ that a appobject is abstract and should not be registered.
At registration time, the following attributes are set on the class:
:vreg:
@@ -46,20 +237,64 @@
:config:
the instance's configuration
- At instantiation time, the following attributes are set on the instance:
+ At selection time, the following attributes are set on the instance:
:req:
current request
:rset:
- result set on which the object is applied
+ context result set or None
+ :row:
+ if a result set is set and the context is about a particular cell in the
+ result set, and not the result set as a whole, specify the row number we
+ are interested in, else None
+ :col:
+ if a result set is set and the context is about a particular cell in the
+ result set, and not the result set as a whole, specify the col number we
+ are interested in, else None
"""
+ __registry__ = None
+ id = None
__select__ = yes()
@classmethod
- def registered(cls, reg):
- super(AppObject, cls).registered(reg)
- cls.vreg = reg.vreg
- cls.schema = reg.schema
- cls.config = reg.config
+ def classid(cls):
+ """returns a unique identifier for the appobject"""
+ return '%s.%s' % (cls.__module__, cls.__name__)
+
+ # XXX bw compat code
+ @classmethod
+ def build___select__(cls):
+ for klass in cls.mro():
+ if klass.__name__ == 'AppObject':
+ continue # the bw compat __selector__ is there
+ klassdict = klass.__dict__
+ if ('__select__' in klassdict and '__selectors__' in klassdict
+ and '__selgenerated__' not in klassdict):
+ raise TypeError("__select__ and __selectors__ can't be used together on class %s" % cls)
+ if '__selectors__' in klassdict and '__selgenerated__' not in klassdict:
+ cls.__selgenerated__ = True
+ # case where __selectors__ is defined locally (but __select__
+ # is in a parent class)
+ selectors = klassdict['__selectors__']
+ if len(selectors) == 1:
+ # micro optimization: don't bother with AndSelector if there's
+ # only one selector
+ select = _instantiate_selector(selectors[0])
+ else:
+ select = AndSelector(*selectors)
+ cls.__select__ = select
+
+ @classmethod
+ def registered(cls, registry):
+ """called by the registry when the appobject has been registered.
+
+ It must return the object that will be actually registered (this may be
+ the right hook to create an instance for example). By default the
+ appobject is returned without any transformation.
+ """
+ cls.build___select__()
+ cls.vreg = registry.vreg
+ cls.schema = registry.schema
+ cls.config = registry.config
cls.register_properties()
return cls
@@ -69,9 +304,13 @@
@classmethod
def selected(cls, *args, **kwargs):
- """by default web app objects are usually instantiated on
- selection according to a request, a result set, and optional
- row and col
+ """called by the registry when the appobject has been selected.
+
+ It must return the object that will be actually returned by the .select
+ method (this may be the right hook to create an instance for
+ example). By default the selected object is called using the given args
+ and kwargs and the resulting value (usually a class instance) is
+ returned without any transformation.
"""
return cls(*args, **kwargs)
@@ -340,3 +579,5 @@
first = rql.split(' ', 1)[0].lower()
if first in ('insert', 'set', 'delete'):
raise Unauthorized(self.req._('only select queries are authorized'))
+
+set_log_methods(AppObject, getLogger('cubicweb.appobject'))
--- a/common/test/unittest_migration.py Mon Aug 03 14:14:07 2009 +0200
+++ b/common/test/unittest_migration.py Mon Aug 03 15:16:47 2009 +0200
@@ -37,8 +37,8 @@
self.config = MigrTestConfig('data')
from yams.schema import Schema
self.config.load_schema = lambda expand_cubes=False: Schema('test')
- self.config.__class__.cubicweb_vobject_path = frozenset()
- self.config.__class__.cube_vobject_path = frozenset()
+ self.config.__class__.cubicweb_appobject_path = frozenset()
+ self.config.__class__.cube_appobject_path = frozenset()
def test_filter_scripts_base(self):
self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,3,0), (2,4,0)),
--- a/cwconfig.py Mon Aug 03 14:14:07 2009 +0200
+++ b/cwconfig.py Mon Aug 03 15:16:47 2009 +0200
@@ -142,7 +142,7 @@
name = None
# log messages format (see logging module documentation for available keys)
log_format = '%(asctime)s - (%(name)s) %(levelname)s: %(message)s'
- # nor remove vobjects based on unused interface
+ # nor remove appobjects based on unused interface
cleanup_interface_sobjects = True
if os.environ.get('APYCOT_ROOT'):
@@ -419,8 +419,8 @@
except Exception, ex:
cls.warning("can't init cube %s: %s", cube, ex)
- cubicweb_vobject_path = set(['entities'])
- cube_vobject_path = set(['entities'])
+ cubicweb_appobject_path = set(['entities'])
+ cube_appobject_path = set(['entities'])
@classmethod
def build_vregistry_path(cls, templpath, evobjpath=None, tvobjpath=None):
@@ -430,13 +430,13 @@
:param evobjpath:
optional list of sub-directories (or files without the .py ext) of
the cubicweb library that should be tested and added to the output list
- if they exists. If not give, default to `cubicweb_vobject_path` class
+ if they exists. If not give, default to `cubicweb_appobject_path` class
attribute.
:param tvobjpath:
optional list of sub-directories (or files without the .py ext) of
directories given in `templpath` that should be tested and added to
the output list if they exists. If not give, default to
- `cube_vobject_path` class attribute.
+ `cube_appobject_path` class attribute.
"""
vregpath = cls.build_vregistry_cubicweb_path(evobjpath)
vregpath += cls.build_vregistry_cube_path(templpath, tvobjpath)
@@ -446,7 +446,7 @@
def build_vregistry_cubicweb_path(cls, evobjpath=None):
vregpath = []
if evobjpath is None:
- evobjpath = cls.cubicweb_vobject_path
+ evobjpath = cls.cubicweb_appobject_path
for subdir in evobjpath:
path = join(CW_SOFTWARE_ROOT, subdir)
if exists(path):
@@ -457,7 +457,7 @@
def build_vregistry_cube_path(cls, templpath, tvobjpath=None):
vregpath = []
if tvobjpath is None:
- tvobjpath = cls.cube_vobject_path
+ tvobjpath = cls.cube_appobject_path
for directory in templpath:
for subdir in tvobjpath:
path = join(directory, subdir)
--- a/cwvreg.py Mon Aug 03 14:14:07 2009 +0200
+++ b/cwvreg.py Mon Aug 03 15:16:47 2009 +0200
@@ -34,7 +34,7 @@
if impl:
return sorted(impl.expected_ifaces)
except AttributeError:
- pass # old-style vobject classes with no accepts_interfaces
+ pass # old-style appobject classes with no accepts_interfaces
except:
print 'bad selector %s on %s' % (obj.__select__, obj)
raise
@@ -308,7 +308,7 @@
# we may want to keep interface dependent objects (e.g.for i18n
# catalog generation)
if self.config.cleanup_interface_sobjects:
- # remove vobjects that don't support any available interface
+ # remove appobjects that don't support any available interface
implemented_interfaces = set()
if 'Any' in self.get('etypes', ()):
for etype in self.schema.entities():
@@ -323,7 +323,7 @@
or iface
for iface in ifaces)
if not ('Any' in ifaces or ifaces & implemented_interfaces):
- self.debug('kicking vobject %s (no implemented '
+ self.debug('kicking appobject %s (no implemented '
'interface among %s)', obj, ifaces)
self.unregister(obj)
# clear needs_iface so we don't try to remove some not-anymore-in
--- a/dbapi.py Mon Aug 03 14:14:07 2009 +0200
+++ b/dbapi.py Mon Aug 03 15:16:47 2009 +0200
@@ -17,6 +17,7 @@
from logilab.common.logging_ext import set_log_methods
from logilab.common.decorators import monkeypatch
+from logilab.common.deprecation import deprecated
from cubicweb import ETYPE_NAME_MAP, ConnectionError, RequestSessionMixIn
from cubicweb import cwvreg, cwconfig
@@ -29,10 +30,10 @@
except KeyError:
return ''
-def _fix_cls_attrs(reg, vobject):
- vobject.vreg = reg.vreg
- vobject.schema = reg.schema
- vobject.config = reg.config
+def _fix_cls_attrs(reg, appobject):
+ appobject.vreg = reg.vreg
+ appobject.schema = reg.schema
+ appobject.config = reg.config
def multiple_connections_fix():
"""some monkey patching necessary when an application has to deal with
@@ -43,15 +44,15 @@
defaultcls = cwvreg.VRegistry.REGISTRY_FACTORY[None]
orig_select_best = defaultcls.orig_select_best = defaultcls.select_best
@monkeypatch(defaultcls)
- def select_best(self, vobjects, *args, **kwargs):
+ def select_best(self, appobjects, *args, **kwargs):
"""return an instance of the most specific object according
to parameters
raise NoSelectableObject if no object apply
"""
- for vobjectcls in vobjects:
- _fix_cls_attrs(self, vobjectcls)
- selected = orig_select_best(self, vobjects, *args, **kwargs)
+ for appobjectcls in appobjects:
+ _fix_cls_attrs(self, appobjectcls)
+ selected = orig_select_best(self, appobjects, *args, **kwargs)
# redo the same thing on the instance so it won't use equivalent class
# attributes (which may change)
_fix_cls_attrs(self, selected)
@@ -448,7 +449,7 @@
raise ProgrammingError('Closed connection')
return self._repo.get_schema()
- def load_vobjects(self, cubes=_MARKER, subpath=None, expand=True,
+ def load_appobjects(self, cubes=_MARKER, subpath=None, expand=True,
force_reload=None):
config = self.vreg.config
if cubes is _MARKER:
@@ -481,11 +482,13 @@
if self._repo.config.instance_hooks:
hm.register_hooks(config.load_hooks(self.vreg))
+ load_vobjects = deprecated()(load_appobjects)
+
def use_web_compatible_requests(self, baseurl, sitetitle=None):
"""monkey patch DBAPIRequest to fake a cw.web.request, so you should
able to call html views using rset from a simple dbapi connection.
- You should call `load_vobjects` at some point to register those views.
+ You should call `load_appobjects` at some point to register those views.
"""
from cubicweb.web.request import CubicWebRequestBase as cwrb
DBAPIRequest.build_ajax_replace_url = cwrb.build_ajax_replace_url.im_func
--- a/devtools/__init__.py Mon Aug 03 14:14:07 2009 +0200
+++ b/devtools/__init__.py Mon Aug 03 15:16:47 2009 +0200
@@ -142,8 +142,8 @@
class BaseApptestConfiguration(TestServerConfiguration, TwistedConfiguration):
repo_method = 'inmemory'
options = merge_options(TestServerConfiguration.options + TwistedConfiguration.options)
- cubicweb_vobject_path = TestServerConfiguration.cubicweb_vobject_path | TwistedConfiguration.cubicweb_vobject_path
- cube_vobject_path = TestServerConfiguration.cube_vobject_path | TwistedConfiguration.cube_vobject_path
+ cubicweb_appobject_path = TestServerConfiguration.cubicweb_appobject_path | TwistedConfiguration.cubicweb_appobject_path
+ cube_appobject_path = TestServerConfiguration.cube_appobject_path | TwistedConfiguration.cube_appobject_path
def available_languages(self, *args):
return ('en', 'fr', 'de')
--- a/devtools/devctl.py Mon Aug 03 14:14:07 2009 +0200
+++ b/devtools/devctl.py Mon Aug 03 15:16:47 2009 +0200
@@ -29,8 +29,8 @@
class DevCubeConfiguration(ServerConfiguration, WebConfiguration):
"""dummy config to get full library schema and entities"""
creating = True
- cubicweb_vobject_path = ServerConfiguration.cubicweb_vobject_path | WebConfiguration.cubicweb_vobject_path
- cube_vobject_path = ServerConfiguration.cube_vobject_path | WebConfiguration.cube_vobject_path
+ cubicweb_appobject_path = ServerConfiguration.cubicweb_appobject_path | WebConfiguration.cubicweb_appobject_path
+ cube_appobject_path = ServerConfiguration.cube_appobject_path | WebConfiguration.cube_appobject_path
def __init__(self, cube):
super(DevCubeConfiguration, self).__init__(cube)
--- a/devtools/testlib.py Mon Aug 03 14:14:07 2009 +0200
+++ b/devtools/testlib.py Mon Aug 03 15:16:47 2009 +0200
@@ -377,9 +377,9 @@
rset2 = rset.limit(limit=1, offset=row)
yield rset2
-def not_selected(vreg, vobject):
+def not_selected(vreg, appobject):
try:
- vreg._selected[vobject.__class__] -= 1
+ vreg._selected[appobject.__class__] -= 1
except (KeyError, AttributeError):
pass
@@ -405,7 +405,7 @@
for regname, reg in testclass._env.vreg.iteritems():
if regname in skipregs:
continue
- for vobjects in reg.itervalues():
- for vobject in vobjects:
- if not reg._selected.get(vobject):
- print 'not tested', regname, vobject
+ for appobjects in reg.itervalues():
+ for appobject in appobjects:
+ if not reg._selected.get(appobject):
+ print 'not tested', regname, appobject
--- a/entities/test/unittest_base.py Mon Aug 03 14:14:07 2009 +0200
+++ b/entities/test/unittest_base.py Mon Aug 03 15:16:47 2009 +0200
@@ -266,7 +266,7 @@
class MyUser(CWUser):
__implements__ = (IMileStone,)
self.vreg._loadedmods[__name__] = {}
- self.vreg.register_vobject_class(MyUser)
+ self.vreg.register_appobject_class(MyUser)
self.failUnless(implements(CWUser, IWorkflowable))
self.failUnless(implements(MyUser, IMileStone))
self.failUnless(implements(MyUser, IWorkflowable))
@@ -290,7 +290,7 @@
for etype in ('Company', 'Division', 'SubDivision'):
class Foo(AnyEntity):
id = etype
- self.vreg.register_vobject_class(Foo)
+ self.vreg.register_appobject_class(Foo)
eclass = self.select_eclass('SubDivision')
if etype == 'SubDivision':
self.failUnless(eclass is Foo)
--- a/etwist/server.py Mon Aug 03 14:14:07 2009 +0200
+++ b/etwist/server.py Mon Aug 03 15:16:47 2009 +0200
@@ -330,7 +330,7 @@
def _gc_debug():
import gc
from pprint import pprint
- from cubicweb.vregistry import VObject
+ from cubicweb.appobject import AppObject
gc.collect()
count = 0
acount = 0
@@ -338,7 +338,7 @@
for obj in gc.get_objects():
if isinstance(obj, CubicWebTwistedRequestAdapter):
count += 1
- elif isinstance(obj, VObject):
+ elif isinstance(obj, AppObject):
acount += 1
else:
try:
--- a/etwist/twconfig.py Mon Aug 03 14:14:07 2009 +0200
+++ b/etwist/twconfig.py Mon Aug 03 15:16:47 2009 +0200
@@ -87,8 +87,8 @@
options = merge_options(TwistedConfiguration.options
+ ServerConfiguration.options)
- cubicweb_vobject_path = TwistedConfiguration.cubicweb_vobject_path | ServerConfiguration.cubicweb_vobject_path
- cube_vobject_path = TwistedConfiguration.cube_vobject_path | ServerConfiguration.cube_vobject_path
+ cubicweb_appobject_path = TwistedConfiguration.cubicweb_appobject_path | ServerConfiguration.cubicweb_appobject_path
+ cube_appobject_path = TwistedConfiguration.cube_appobject_path | ServerConfiguration.cube_appobject_path
def pyro_enabled(self):
"""tell if pyro is activated for the in memory repository"""
return self['pyro-server']
--- a/goa/goaconfig.py Mon Aug 03 14:14:07 2009 +0200
+++ b/goa/goaconfig.py Mon Aug 03 15:16:47 2009 +0200
@@ -81,9 +81,9 @@
options = [(optname, optdict) for optname, optdict in options
if not optname in UNSUPPORTED_OPTIONS]
- cubicweb_vobject_path = WebConfiguration.cubicweb_vobject_path | ServerConfiguration.cubicweb_vobject_path
- cubicweb_vobject_path = list(cubicweb_vobject_path) + ['goa/appobjects']
- cube_vobject_path = WebConfiguration.cube_vobject_path | ServerConfiguration.cube_vobject_path
+ cubicweb_appobject_path = WebConfiguration.cubicweb_appobject_path | ServerConfiguration.cubicweb_appobject_path
+ cubicweb_appobject_path = list(cubicweb_appobject_path) + ['goa/appobjects']
+ cube_appobject_path = WebConfiguration.cube_appobject_path | ServerConfiguration.cube_appobject_path
# use file system schema
bootstrap_schema = read_instance_schema = False
--- a/goa/goavreg.py Mon Aug 03 14:14:07 2009 +0200
+++ b/goa/goavreg.py Mon Aug 03 15:16:47 2009 +0200
@@ -59,7 +59,7 @@
self.load_module(obj)
def _auto_load(self, path, loadschema, cube=None):
- vobjpath = self.config.cube_vobject_path
+ vobjpath = self.config.cube_appobject_path
for filename in listdir(path):
if filename[-3:] == '.py' and filename[:-3] in vobjpath:
self._import(_pkg_name(cube, filename[:-3]))
--- a/selectors.py Mon Aug 03 14:14:07 2009 +0200
+++ b/selectors.py Mon Aug 03 15:16:47 2009 +0200
@@ -53,8 +53,8 @@
from cubicweb import (Unauthorized, NoSelectableObject, NotAnEntity,
role, typed_eid)
-from cubicweb.vregistry import (NoSelectableObject, Selector,
- chainall, objectify_selector)
+# even if not used, let yes here so it's importable through this module
+from cubicweb.appobject import Selector, objectify_selector, yes
from cubicweb.cwconfig import CubicWebConfiguration
from cubicweb.schema import split_expression
@@ -274,17 +274,6 @@
# very basic selectors ########################################################
-class yes(Selector):
- """return arbitrary score
-
- default score of 0.5 so any other selector take precedence
- """
- def __init__(self, score=0.5):
- self.score = score
-
- def __call__(self, *args, **kwargs):
- return self.score
-
@objectify_selector
@lltrace
def none_rset(cls, req, rset=None, **kwargs):
@@ -975,6 +964,7 @@
# XXX DEPRECATED ##############################################################
+from cubicweb.vregistry import chainall
yes_selector = deprecated()(yes)
norset_selector = deprecated()(none_rset)
@@ -1040,7 +1030,7 @@
accept_selector = deprecated()(accept)
accept_one = deprecated()(chainall(one_line_rset, accept,
- name='accept_one'))
+ name='accept_one'))
accept_one_selector = deprecated()(accept_one)
--- a/server/serverconfig.py Mon Aug 03 14:14:07 2009 +0200
+++ b/server/serverconfig.py Mon Aug 03 15:16:47 2009 +0200
@@ -85,8 +85,8 @@
SCHEMAS_LIB_DIR = '/usr/share/cubicweb/schemas/'
BACKUP_DIR = '/var/lib/cubicweb/backup/'
- cubicweb_vobject_path = CubicWebConfiguration.cubicweb_vobject_path | set(['sobjects'])
- cube_vobject_path = CubicWebConfiguration.cube_vobject_path | set(['sobjects', 'hooks'])
+ cubicweb_appobject_path = CubicWebConfiguration.cubicweb_appobject_path | set(['sobjects'])
+ cube_appobject_path = CubicWebConfiguration.cube_appobject_path | set(['sobjects', 'hooks'])
options = merge_options((
# ctl configuration
--- a/test/unittest_rtags.py Mon Aug 03 14:14:07 2009 +0200
+++ b/test/unittest_rtags.py Mon Aug 03 15:16:47 2009 +0200
@@ -39,7 +39,7 @@
# __rtags__ = {
# ('evaluee', 'Note', 'subject') : set(('inlineview',)),
# }
-# self.vreg.register_vobject_class(Personne2)
+# self.vreg.register_appobject_class(Personne2)
# rtags = Personne2.rtags
# self.assertEquals(rtags.rtag('evaluee', 'Note', 'subject'), set(('inlineview', 'link')))
# self.assertEquals(rtags.is_inlined('evaluee', 'Note', 'subject'), True)
--- a/test/unittest_selectors.py Mon Aug 03 14:14:07 2009 +0200
+++ b/test/unittest_selectors.py Mon Aug 03 15:16:47 2009 +0200
@@ -9,7 +9,7 @@
from logilab.common.testlib import TestCase, unittest_main
from cubicweb.devtools.testlib import EnvBasedTC
-from cubicweb.vregistry import Selector, AndSelector, OrSelector
+from cubicweb.appobject import Selector, AndSelector, OrSelector
from cubicweb.selectors import implements, match_user_groups
from cubicweb.interfaces import IDownloadable
from cubicweb.web import action
@@ -91,7 +91,7 @@
class ImplementsSelectorTC(EnvBasedTC):
def test_etype_priority(self):
req = self.request()
- cls = self.vreg.etype_class('File')
+ cls = self.vreg['etypes'].etype_class('File')
anyscore = implements('Any').score_class(cls, req)
idownscore = implements(IDownloadable).score_class(cls, req)
self.failUnless(idownscore > anyscore, (idownscore, anyscore))
@@ -99,7 +99,7 @@
self.failUnless(filescore > idownscore, (filescore, idownscore))
def test_etype_inheritance_no_yams_inheritance(self):
- cls = self.vreg.etype_class('Personne')
+ cls = self.vreg['etypes'].etype_class('Personne')
self.failIf(implements('Societe').score_class(cls, self.request()))
@@ -111,7 +111,7 @@
category = 'foo'
__select__ = match_user_groups('owners')
self.vreg._loadedmods[__name__] = {}
- self.vreg.register_vobject_class(SomeAction)
+ self.vreg.register_appobject_class(SomeAction)
self.failUnless(SomeAction in self.vreg['actions']['yo'], self.vreg['actions'])
try:
# login as a simple user
--- a/test/unittest_vregistry.py Mon Aug 03 14:14:07 2009 +0200
+++ b/test/unittest_vregistry.py Mon Aug 03 15:16:47 2009 +0200
@@ -10,7 +10,7 @@
from os.path import join
from cubicweb import CW_SOFTWARE_ROOT as BASE
-from cubicweb.vregistry import VObject
+from cubicweb.appobject import AppObject
from cubicweb.cwvreg import CubicWebVRegistry, UnknownProperty
from cubicweb.devtools import TestServerConfiguration
from cubicweb.interfaces import IMileStone
@@ -43,7 +43,7 @@
def test___selectors__compat(self):
myselector1 = lambda *args: 1
myselector2 = lambda *args: 1
- class AnAppObject(VObject):
+ class AnAppObject(AppObject):
__selectors__ = (myselector1, myselector2)
AnAppObject.build___select__()
self.assertEquals(AnAppObject.__select__(AnAppObject), 2)
@@ -53,7 +53,7 @@
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):
+ def test_load_subinterface_based_appobjects(self):
self.vreg.reset()
self.vreg.register_objects([join(BASE, 'web', 'views', 'iprogress.py')])
# check progressbar was kicked
@@ -62,7 +62,7 @@
__implements__ = (IMileStone,)
self.vreg.reset()
self.vreg._loadedmods[__name__] = {}
- self.vreg.register_vobject_class(MyCard)
+ self.vreg.register_appobject_class(MyCard)
self.vreg.register_objects([join(BASE, 'entities', '__init__.py'),
join(BASE, 'web', 'views', 'iprogress.py')])
# check progressbar isn't kicked
--- a/utils.py Mon Aug 03 14:14:07 2009 +0200
+++ b/utils.py Mon Aug 03 15:16:47 2009 +0200
@@ -321,7 +321,7 @@
class AcceptMixIn(object):
- """Mixin class for vobjects defining the 'accepts' attribute describing
+ """Mixin class for appobjects defining the 'accepts' attribute describing
a set of supported entity type (Any by default).
"""
# XXX deprecated, no more necessary
--- a/vregistry.py Mon Aug 03 14:14:07 2009 +0200
+++ b/vregistry.py Mon Aug 03 15:16:47 2009 +0200
@@ -5,13 +5,13 @@
according to a context
* to interact with the vregistry, objects should inherit from the
- VObject abstract class
+ AppObject abstract class
* the selection procedure has been generalized by delegating to a
- selector, which is responsible to score the vobject according to the
+ selector, which is responsible to score the appobject according to the
current state (req, rset, row, col). At the end of the selection, if
- a vobject class has been found, an instance of this class is
- returned. The selector is instantiated at vobject registration
+ a appobject class has been found, an instance of this class is
+ returned. The selector is instantiated at appobject registration
:organization: Logilab
@@ -22,17 +22,18 @@
__docformat__ = "restructuredtext en"
import sys
-import types
from os import listdir, stat
from os.path import dirname, join, realpath, split, isdir, exists
from logging import getLogger
from warnings import warn
-from logilab.common.deprecation import deprecated
+from logilab.common.deprecation import deprecated, class_moved
+from logilab.common.logging_ext import set_log_methods
-from cubicweb import CW_SOFTWARE_ROOT, set_log_methods
+from cubicweb import CW_SOFTWARE_ROOT
from cubicweb import (RegistryNotFound, ObjectNotFound, NoSelectableObject,
RegistryOutOfDate)
+from cubicweb.appobject import AppObject
# XXX depending on cubicweb.web is ugly, we should deal with uicfg
# reset with a good old event / callback system
@@ -60,80 +61,6 @@
return _toload
-class VObject(object):
- """visual object, use to be handled somehow by the visual components
- registry.
-
- The following attributes should be set on concret vobject subclasses:
-
- :__registry__:
- name of the registry for this object (string like 'views',
- 'templates'...)
- :id:
- object's identifier in the registry (string like 'main',
- 'primary', 'folder_box')
- :__select__:
- class'selector
-
- Moreover, the `__abstract__` attribute may be set to True to indicate
- that a vobject is abstract and should not be registered
- """
- # necessary attributes to interact with the registry
- id = None
- __registry__ = None
- __select__ = None
-
- @classmethod
- def registered(cls, registry):
- """called by the registry when the vobject has been registered.
-
- It must return the object that will be actually registered (this
- may be the right hook to create an instance for example). By
- default the vobject is returned without any transformation.
- """
- cls.build___select__()
- return cls
-
- @classmethod
- def selected(cls, *args, **kwargs):
- """called by the registry when the vobject has been selected.
-
- It must return the object that will be actually returned by the
- .select method (this may be the right hook to create an
- instance for example). By default the selected object is
- returned without any transformation.
- """
- return cls
-
- @classmethod
- def classid(cls):
- """returns a unique identifier for the vobject"""
- return '%s.%s' % (cls.__module__, cls.__name__)
-
- # XXX bw compat code
- @classmethod
- def build___select__(cls):
- for klass in cls.mro():
- if klass.__name__ == 'AppObject':
- continue # the bw compat __selector__ is there
- klassdict = klass.__dict__
- if ('__select__' in klassdict and '__selectors__' in klassdict
- and '__selgenerated__' not in klassdict):
- raise TypeError("__select__ and __selectors__ can't be used together on class %s" % cls)
- if '__selectors__' in klassdict and '__selgenerated__' not in klassdict:
- cls.__selgenerated__ = True
- # case where __selectors__ is defined locally (but __select__
- # is in a parent class)
- selectors = klassdict['__selectors__']
- if len(selectors) == 1:
- # micro optimization: don't bother with AndSelector if there's
- # only one selector
- select = _instantiate_selector(selectors[0])
- else:
- select = AndSelector(*selectors)
- cls.__select__ = select
-
-
class Registry(dict):
def __init__(self, config):
@@ -155,16 +82,16 @@
oid = oid or obj.id
assert oid
if clear:
- vobjects = self[oid] = []
+ appobjects = self[oid] = []
else:
- vobjects = self.setdefault(oid, [])
+ appobjects = self.setdefault(oid, [])
# registered() is technically a classmethod but is not declared
# as such because we need to compose registered in some cases
- vobject = obj.registered.im_func(obj, self)
- assert not vobject in vobjects, \
- 'object %s is already registered' % vobject
- assert callable(vobject.__select__), vobject
- vobjects.append(vobject)
+ appobject = obj.registered.im_func(obj, self)
+ assert not appobject in appobjects, \
+ 'object %s is already registered' % appobject
+ assert callable(appobject.__select__), appobject
+ appobjects.append(appobject)
def register_and_replace(self, obj, replaced):
# XXXFIXME this is a duplication of unregister()
@@ -238,13 +165,13 @@
"""return an iterator on possible objects in this registry for the given
context
"""
- for vobjects in self.itervalues():
+ for appobjects in self.itervalues():
try:
- yield self.select_best(vobjects, *args, **kwargs)
+ yield self.select_best(appobjects, *args, **kwargs)
except NoSelectableObject:
continue
- def select_best(self, vobjects, *args, **kwargs):
+ def select_best(self, appobjects, *args, **kwargs):
"""return an instance of the most specific object according
to parameters
@@ -254,16 +181,16 @@
warn('only the request param can not be named when calling select',
DeprecationWarning, stacklevel=3)
score, winners = 0, []
- for vobject in vobjects:
- vobjectscore = vobject.__select__(vobject, *args, **kwargs)
- if vobjectscore > score:
- score, winners = vobjectscore, [vobject]
- elif vobjectscore > 0 and vobjectscore == score:
- winners.append(vobject)
+ for appobject in appobjects:
+ appobjectscore = appobject.__select__(appobject, *args, **kwargs)
+ if appobjectscore > score:
+ score, winners = appobjectscore, [appobject]
+ elif appobjectscore > 0 and appobjectscore == score:
+ winners.append(appobject)
if not winners:
raise NoSelectableObject('args: %s\nkwargs: %s %s'
% (args, kwargs.keys(),
- [repr(v) for v in vobjects]))
+ [repr(v) for v in appobjects]))
if len(winners) > 1:
if self.config.mode == 'installed':
self.error('select ambiguity, args: %s\nkwargs: %s %s',
@@ -272,7 +199,7 @@
raise Exception('select ambiguity, args: %s\nkwargs: %s %s'
% (args, kwargs.keys(),
[repr(v) for v in winners]))
- # return the result of the .selected method of the vobject
+ # return the result of the .selected method of the appobject
return winners[0].selected(*args, **kwargs)
@@ -379,7 +306,7 @@
vname = obj.__name__
except AttributeError:
vname = obj.__class__.__name__
- self.debug('registered vobject %s in registry %s with id %s',
+ self.debug('registered appobject %s in registry %s with id %s',
vname, registryname, oid)
self._loadedmods[obj.__module__]['%s.%s' % (obj.__module__, oid)] = obj
@@ -473,7 +400,7 @@
return
# skip non registerable object
try:
- if not issubclass(obj, VObject):
+ if not issubclass(obj, AppObject):
return
except TypeError:
return
@@ -487,20 +414,20 @@
def load_object(self, obj):
try:
- self.register_vobject_class(obj)
+ self.register_appobject_class(obj)
except Exception, ex:
if self.config.mode in ('test', 'dev'):
raise
- self.exception('vobject %s registration failed: %s', obj, ex)
+ self.exception('appobject %s registration failed: %s', obj, ex)
# old automatic registration XXX deprecated ###############################
- def register_vobject_class(self, cls):
- """handle vobject class registration
+ def register_appobject_class(self, cls):
+ """handle appobject class registration
- vobject class with __abstract__ == True in their local dictionnary or
+ appobject class with __abstract__ == True in their local dictionnary or
with a name starting starting by an underscore are not registered.
- Also a vobject class needs to have __registry__ and id attributes set
+ Also a appobject class needs to have __registry__ and id attributes set
to a non empty string to be registered.
"""
if (cls.__dict__.get('__abstract__') or cls.__name__[0] == '_'
@@ -512,170 +439,19 @@
self.register(cls)
# init logging
-set_log_methods(VObject, getLogger('cubicweb.appobject'))
set_log_methods(VRegistry, getLogger('cubicweb.vreg'))
set_log_methods(Registry, getLogger('cubicweb.registry'))
-# selector base classes and operations ########################################
-
-class Selector(object):
- """base class for selector classes providing implementation
- for operators ``&`` and ``|``
-
- This class is only here to give access to binary operators, the
- selector logic itself should be implemented in the __call__ method
-
-
- a selector is called to help choosing the correct object for a
- particular context by returning a score (`int`) telling how well
- the class given as first argument apply to the given context.
-
- 0 score means that the class doesn't apply.
- """
-
- @property
- def func_name(self):
- # backward compatibility
- return self.__class__.__name__
-
- def search_selector(self, selector):
- """search for the given selector or selector instance in the selectors
- tree. Return it of None if not found
- """
- if self is selector:
- return self
- 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)
- def __rand__(self, other):
- return AndSelector(other, self)
-
- def __or__(self, other):
- return OrSelector(self, other)
- def __ror__(self, other):
- return OrSelector(other, self)
-
- def __invert__(self):
- return NotSelector(self)
-
- # XXX (function | function) or (function & function) not managed yet
-
- def __call__(self, cls, *args, **kwargs):
- return NotImplementedError("selector %s must implement its logic "
- "in its __call__ method" % self.__class__)
-
-class MultiSelector(Selector):
- """base class for compound selector classes"""
-
- 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):
- """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:
- try:
- selector = _instantiate_selector(selector)
- except:
- pass
- #assert isinstance(selector, Selector), selector
- if isinstance(selector, cls):
- merged_selectors += selector.selectors
- else:
- merged_selectors.append(selector)
- return merged_selectors
-
- def search_selector(self, selector):
- """search for the given selector or selector instance in the selectors
- tree. Return it of None if not found
- """
- for childselector in self.selectors:
- if childselector is selector:
- return childselector
- found = childselector.search_selector(selector)
- if found is not None:
- return found
- return None
-
-
-def objectify_selector(selector_func):
- """convenience decorator for simple selectors where a class definition
- would be overkill::
-
- @objectify_selector
- def yes(cls, *args, **kwargs):
- return 1
-
- """
- return type(selector_func.__name__, (Selector,),
- {'__call__': lambda self, *args, **kwargs: selector_func(*args, **kwargs)})
-
-def _instantiate_selector(selector):
- """ensures `selector` is a `Selector` instance
-
- NOTE: This should only be used locally in build___select__()
- XXX: then, why not do it ??
- """
- if isinstance(selector, types.FunctionType):
- return objectify_selector(selector)()
- if isinstance(selector, type) and issubclass(selector, Selector):
- return selector()
- return selector
-
-
-class AndSelector(MultiSelector):
- """and-chained selectors (formerly known as chainall)"""
- def __call__(self, cls, *args, **kwargs):
- score = 0
- for selector in self.selectors:
- partscore = selector(cls, *args, **kwargs)
- if not partscore:
- return 0
- score += partscore
- return score
-
-
-class OrSelector(MultiSelector):
- """or-chained selectors (formerly known as chainfirst)"""
- def __call__(self, cls, *args, **kwargs):
- for selector in self.selectors:
- partscore = selector(cls, *args, **kwargs)
- if partscore:
- return partscore
- return 0
-
-class NotSelector(Selector):
- """negation selector"""
- def __init__(self, selector):
- self.selector = selector
-
- def __call__(self, cls, *args, **kwargs):
- score = self.selector(cls, *args, **kwargs)
- return int(not score)
-
- def __str__(self):
- return 'NOT(%s)' % super(NotSelector, self).__str__()
-
-
# XXX bw compat functions #####################################################
+from cubicweb.appobject import objectify_selector, AndSelector, OrSelector, Selector
+
+objectify_selector = deprecated('objectify_selector has been moved to appobject module')(objectify_selector)
+
+Selector = class_moved(Selector)
+
+@deprecated('use & operator (binary and)')
def chainall(*selectors, **kwargs):
"""return a selector chaining given selectors. If one of
the selectors fail, selection will fail, else the returned score
@@ -688,6 +464,7 @@
selector.__name__ = kwargs['name']
return selector
+@deprecated('use | operator (binary or)')
def chainfirst(*selectors, **kwargs):
"""return a selector chaining given selectors. If all
the selectors fail, selection will fail, else the returned score
--- a/web/test/test_views.py Mon Aug 03 14:14:07 2009 +0200
+++ b/web/test/test_views.py Mon Aug 03 15:16:47 2009 +0200
@@ -52,7 +52,7 @@
def test_js_added_only_once(self):
self.vreg._loadedmods[__name__] = {}
- self.vreg.register_vobject_class(SomeView)
+ self.vreg.register_appobject_class(SomeView)
rset = self.execute('CWUser X')
source = self.view('someview', rset).source
self.assertEquals(source.count('spam.js'), 1)
--- a/web/test/unittest_viewselector.py Mon Aug 03 14:14:07 2009 +0200
+++ b/web/test/unittest_viewselector.py Mon Aug 03 15:16:47 2009 +0200
@@ -274,7 +274,7 @@
class CWUserCreationForm(editforms.CreationFormView):
__select__ = specified_etype_implements('CWUser')
self.vreg._loadedmods[__name__] = {}
- self.vreg.register_vobject_class(CWUserCreationForm)
+ self.vreg.register_appobject_class(CWUserCreationForm)
req.form['etype'] = 'CWUser'
self.assertIsInstance(self.vreg['views'].select('creation', req, rset=rset),
CWUserCreationForm)
@@ -431,7 +431,7 @@
def setUp(self):
super(RQLActionTC, self).setUp()
self.vreg._loadedmods[__name__] = {}
- self.vreg.register_vobject_class(CWETypeRQLAction)
+ self.vreg.register_appobject_class(CWETypeRQLAction)
def tearDown(self):
super(RQLActionTC, self).tearDown()
--- a/web/views/actions.py Mon Aug 03 14:14:07 2009 +0200
+++ b/web/views/actions.py Mon Aug 03 15:16:47 2009 +0200
@@ -8,7 +8,7 @@
__docformat__ = "restructuredtext en"
_ = unicode
-from cubicweb.vregistry import objectify_selector
+from cubicweb.appobject import objectify_selector
from cubicweb.selectors import (EntitySelector,
one_line_rset, two_lines_rset, one_etype_rset, relation_possible,
nonempty_rset, non_final_entity,
--- a/web/views/basetemplates.py Mon Aug 03 14:14:07 2009 +0200
+++ b/web/views/basetemplates.py Mon Aug 03 15:16:47 2009 +0200
@@ -10,7 +10,7 @@
from logilab.mtconverter import xml_escape
-from cubicweb.vregistry import objectify_selector
+from cubicweb.appobject import objectify_selector
from cubicweb.selectors import match_kwargs
from cubicweb.view import View, MainTemplate, NOINDEX, NOFOLLOW
from cubicweb.utils import make_uid, UStringIO
--- a/web/views/facets.py Mon Aug 03 14:14:07 2009 +0200
+++ b/web/views/facets.py Mon Aug 03 15:16:47 2009 +0200
@@ -11,7 +11,7 @@
from logilab.mtconverter import xml_escape
-from cubicweb.vregistry import objectify_selector
+from cubicweb.appobject import objectify_selector
from cubicweb.selectors import (non_final_entity, two_lines_rset,
match_context_prop, yes, relation_possible)
from cubicweb.web.box import BoxTemplate
--- a/web/views/forms.py Mon Aug 03 14:14:07 2009 +0200
+++ b/web/views/forms.py Mon Aug 03 15:16:47 2009 +0200
@@ -288,7 +288,8 @@
class EntityFieldsForm(FieldsForm):
id = 'base'
- __select__ = (match_kwargs('entity') | (one_line_rset & non_final_entity()))
+ __select__ = (match_kwargs('entity')
+ | (one_line_rset() & non_final_entity()))
internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid')
domid = 'entityForm'
--- a/web/views/plots.py Mon Aug 03 14:14:07 2009 +0200
+++ b/web/views/plots.py Mon Aug 03 15:16:47 2009 +0200
@@ -16,7 +16,7 @@
from logilab.mtconverter import xml_escape
from cubicweb.utils import make_uid, UStringIO, datetime2ticks
-from cubicweb.vregistry import objectify_selector
+from cubicweb.appobject import objectify_selector
from cubicweb.web.views import baseviews
@objectify_selector
--- a/web/views/urlpublishing.py Mon Aug 03 14:14:07 2009 +0200
+++ b/web/views/urlpublishing.py Mon Aug 03 15:16:47 2009 +0200
@@ -194,9 +194,9 @@
def evaluate_path(self, req, parts):
# uri <=> req._twreq.path or req._twreq.uri
uri = req.url_unquote('/' + '/'.join(parts))
- vobjects = sorted(self.vreg['urlrewriting'].all_objects(),
- key=lambda x: x.priority, reverse=True)
- for rewritercls in vobjects:
+ evaluators = sorted(self.vreg['urlrewriting'].all_objects(),
+ key=lambda x: x.priority, reverse=True)
+ for rewritercls in evaluators:
rewriter = rewritercls()
try:
# XXX we might want to chain url rewrites
--- a/web/webconfig.py Mon Aug 03 14:14:07 2009 +0200
+++ b/web/webconfig.py Mon Aug 03 15:16:47 2009 +0200
@@ -63,8 +63,8 @@
"""the WebConfiguration is a singleton object handling instance's
configuration and preferences
"""
- cubicweb_vobject_path = CubicWebConfiguration.cubicweb_vobject_path | set(['web/views'])
- cube_vobject_path = CubicWebConfiguration.cube_vobject_path | set(['views'])
+ cubicweb_appobject_path = CubicWebConfiguration.cubicweb_appobject_path | set(['web/views'])
+ cube_appobject_path = CubicWebConfiguration.cube_appobject_path | set(['views'])
options = merge_options(CubicWebConfiguration.options + (
('anonymous-user',