vregistry.py
changeset 2657 de974465d381
parent 2656 a93ae0f6c0ad
child 2658 5535857eeaa5
--- 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