cwvreg.py
changeset 2650 18aec79ec3a3
parent 2613 5e19c2bb370e
child 2655 48cd71bdb5cd
--- a/cwvreg.py	Mon Aug 03 09:37:13 2009 +0200
+++ b/cwvreg.py	Mon Aug 03 10:50:57 2009 +0200
@@ -8,13 +8,14 @@
 __docformat__ = "restructuredtext en"
 _ = unicode
 
-from logilab.common.decorators import cached, clear_cache
+from logilab.common.decorators import cached, clear_cache, monkeypatch
 from logilab.common.deprecation import  deprecated
 
 from rql import RQLHelper
 
-from cubicweb import ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid
-from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject
+from cubicweb import (ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid,
+                      ObjectNotFound, NoSelectableObject, RegistryNotFound)
+from cubicweb.vregistry import VRegistry, Registry
 from cubicweb.rtags import RTAGS
 
 
@@ -39,7 +40,149 @@
         return ()
 
 
-class CubicWebRegistry(VRegistry):
+class CWRegistry(Registry):
+    def __init__(self, vreg):
+        super(CWRegistry, self).__init__(vreg.config)
+        self.vreg = vreg
+        self.schema = vreg.schema
+
+    def initialization_completed(self):
+        # call vreg_initialization_completed on appobjects and print
+        # registry content
+        for appobjects in self.itervalues():
+            for appobject in appobjects:
+                appobject.vreg_initialization_completed()
+
+    def render(self, __oid, req, __fallback_oid=None, rset=None, **kwargs):
+        """select object, or fallback object if specified and the first one
+        isn't selectable, then render it
+        """
+        try:
+            obj = self.select(__oid, req, rset=rset, **kwargs)
+        except NoSelectableObject:
+            if __fallback_oid is None:
+                raise
+            obj = self.select(__fallback_oid, req, **kwargs)
+        return obj.render(**kwargs)
+
+    def select_vobject(self, oid, *args, **kwargs):
+        selected = self.select_object(oid, *args, **kwargs)
+        if selected and selected.propval('visible'):
+            return selected
+        return None
+
+    def possible_vobjects(self, *args, **kwargs):
+        """return an ordered list of possible app objects in a given registry,
+        supposing they support the 'visible' and 'order' properties (as most
+        visualizable objects)
+        """
+        return sorted([x for x in self.possible_objects(*args, **kwargs)
+                       if x.propval('visible')],
+                      key=lambda x: x.propval('order'))
+
+
+VRegistry.REGISTRY_FACTORY[None] = CWRegistry
+
+
+class ETypeRegistry(CWRegistry):
+
+    def initialization_completed(self):
+        super(ETypeRegistry, self).initialization_completed()
+        # clear etype cache if you don't want to run into deep weirdness
+        clear_cache(self, 'etype_class')
+
+    def register(self, obj, **kwargs):
+        oid = kwargs.get('oid') or obj.id
+        if oid != 'Any' and not oid in self.schema:
+            self.error('don\'t register %s, %s type not defined in the '
+                       'schema', obj, obj.id)
+            return
+        kwargs['clear'] = True
+        super(ETypeRegistry, self).register(obj, **kwargs)
+
+    @cached
+    def etype_class(self, etype):
+        """return an entity class for the given entity type.
+        Try to find out a specific class for this kind of entity or
+        default to a dump of the class registered for 'Any'
+        """
+        etype = str(etype)
+        if etype == 'Any':
+            return self.select('Any', 'Any')
+        eschema = self.schema.eschema(etype)
+        baseschemas = [eschema] + eschema.ancestors()
+        # browse ancestors from most specific to most generic and
+        # try to find an associated custom entity class
+        for baseschema in baseschemas:
+            try:
+                btype = ETYPE_NAME_MAP[baseschema]
+            except KeyError:
+                btype = str(baseschema)
+            try:
+                cls = self.select(btype, etype)
+                break
+            except ObjectNotFound:
+                pass
+        else:
+            # no entity class for any of the ancestors, fallback to the default
+            # one
+            cls = self.select('Any', etype)
+        return cls
+
+VRegistry.REGISTRY_FACTORY['etypes'] = ETypeRegistry
+
+
+class ViewsRegistry(CWRegistry):
+
+    def main_template(self, req, oid='main-template', **kwargs):
+        """display query by calling the given template (default to main),
+        and returning the output as a string instead of requiring the [w]rite
+        method as argument
+        """
+        res = self.render(oid, req, **kwargs)
+        if isinstance(res, unicode):
+            return res.encode(req.encoding)
+        assert isinstance(res, str)
+        return res
+
+    def possible_views(self, req, rset=None, **kwargs):
+        """return an iterator on possible views for this result set
+
+        views returned are classes, not instances
+        """
+        for vid, views in self.items():
+            if vid[0] == '_':
+                continue
+            try:
+                view = self.select_best(views, req, rset=rset, **kwargs)
+                if view.linkable():
+                    yield view
+            except NoSelectableObject:
+                continue
+            except Exception:
+                self.exception('error while trying to select %s view for %s',
+                               vid, rset)
+
+VRegistry.REGISTRY_FACTORY['views'] = ViewsRegistry
+
+
+class ActionsRegistry(CWRegistry):
+
+    def possible_actions(self, req, rset=None, **kwargs):
+        if rset is None:
+            actions = self.possible_vobjects(req, rset=rset, **kwargs)
+        else:
+            actions = rset.possible_actions(**kwargs) # cached implementation
+        result = {}
+        for action in actions:
+            result.setdefault(action.category, []).append(action)
+        return result
+
+VRegistry.REGISTRY_FACTORY['actions'] = ActionsRegistry
+
+
+
+class CubicWebVRegistry(VRegistry):
     """Central registry for the cubicweb instance, extending the generic
     VRegistry with some cubicweb specific stuff.
 
@@ -65,28 +208,33 @@
         if initlog:
             # first init log service
             config.init_log(debug=debug)
-        super(CubicWebRegistry, self).__init__(config)
+        super(CubicWebVRegistry, self).__init__(config)
         self.schema = None
         self.reset()
         self.initialized = False
 
+    def setdefault(self, regid):
+        try:
+            return self[regid]
+        except RegistryNotFound:
+            self[regid] = self.registry_class(regid)(self)
+            return self[regid]
+
     def items(self):
-        return [item for item in self._registries.items()
+        return [item for item in super(CubicWebVRegistry, self).items()
                 if not item[0] in ('propertydefs', 'propertyvalues')]
 
     def values(self):
-        return [value for key, value in self._registries.items()
-                if not key in ('propertydefs', 'propertyvalues')]
+        return [value for key, value in self.items()]
 
     def reset(self):
-        self._registries = {}
-        self._lastmodifs = {}
+        super(CubicWebVRegistry, self).reset()
         self._needs_iface = {}
         # two special registries, propertydefs which care all the property
         # definitions, and propertyvals which contains values for those
         # properties
-        self._registries['propertydefs'] = {}
-        self._registries['propertyvalues'] = self.eprop_values = {}
+        self['propertydefs'] = {}
+        self['propertyvalues'] = self.eprop_values = {}
         for key, propdef in self.config.eproperty_definitions():
             self.register_property(key, **propdef)
 
@@ -107,9 +255,7 @@
         tests
         """
         self.schema = schema
-        for registry, regcontent in self._registries.items():
-            if registry in ('propertydefs', 'propertyvalues'):
-                continue
+        for registry, regcontent in self.items():
             for objects in regcontent.values():
                 for obj in objects:
                     obj.schema = schema
@@ -125,13 +271,7 @@
             self._needs_iface[obj] = ifaces
 
     def register(self, obj, **kwargs):
-        if kwargs.get('registryname', obj.__registry__) == 'etypes':
-            if obj.id != 'Any' and not obj.id in self.schema:
-                self.error('don\'t register %s, %s type not defined in the '
-                           'schema', obj, obj.id)
-                return
-            kwargs['clear'] = True
-        super(CubicWebRegistry, self).register(obj, **kwargs)
+        super(CubicWebVRegistry, self).register(obj, **kwargs)
         # XXX bw compat
         ifaces = use_interfaces(obj)
         if ifaces:
@@ -143,25 +283,18 @@
         for cubesdir in self.config.cubes_search_path():
             if cubesdir != self.config.CUBES_DIR:
                 extrapath[cubesdir] = 'cubes'
-        if super(CubicWebRegistry, self).register_objects(path, force_reload,
+        if super(CubicWebVRegistry, self).register_objects(path, force_reload,
                                                           extrapath):
             self.initialization_completed()
-            # call vreg_initialization_completed on appobjects and print
-            # registry content
-            for registry, objects in self.items():
-                self.debug('available in registry %s: %s', registry,
-                           sorted(objects))
-                for appobjects in objects.itervalues():
-                    for appobject in appobjects:
-                        appobject.vreg_initialization_completed()
             # don't check rtags if we don't want to cleanup_interface_sobjects
             for rtag in RTAGS:
                 rtag.init(self.schema,
                           check=self.config.cleanup_interface_sobjects)
 
     def initialization_completed(self):
-        # clear etype cache if you don't want to run into deep weirdness
-        clear_cache(self, 'etype_class')
+        for regname, reg in self.items():
+            self.debug('available in registry %s: %s', regname, sorted(reg))
+            reg.initialization_completed()
         # we may want to keep interface dependent objects (e.g.for i18n
         # catalog generation)
         if self.config.cleanup_interface_sobjects:
@@ -169,14 +302,14 @@
             implemented_interfaces = set()
             if 'Any' in self.get('etypes', ()):
                 for etype in self.schema.entities():
-                    cls = self.etype_class(etype)
+                    cls = self['etypes'].etype_class(etype)
                     for iface in cls.__implements__:
                         implemented_interfaces.update(iface.__mro__)
                     implemented_interfaces.update(cls.__mro__)
             for obj, ifaces in self._needs_iface.items():
                 ifaces = frozenset(isinstance(iface, basestring)
                                    and iface in self.schema
-                                   and self.etype_class(iface)
+                                   and self['etypes'].etype_class(iface)
                                    or iface
                                    for iface in ifaces)
                 if not ('Any' in ifaces or ifaces & implemented_interfaces):
@@ -187,122 +320,55 @@
         # objects on automatic reloading
         self._needs_iface.clear()
 
+    def parse(self, session, rql, args=None):
+        rqlst = self.rqlhelper.parse(rql)
+        def type_from_eid(eid, session=session):
+            return session.describe(eid)[0]
+        try:
+            self.rqlhelper.compute_solutions(rqlst, {'eid': type_from_eid}, args)
+        except UnknownEid:
+            for select in rqlst.children:
+                select.solutions = []
+        return rqlst
+
+    @property
     @cached
+    def rqlhelper(self):
+        return RQLHelper(self.schema,
+                         special_relations={'eid': 'uid', 'has_text': 'fti'})
+
+
+    @deprecated('use vreg["etypes"].etype_class(etype)')
     def etype_class(self, etype):
-        """return an entity class for the given entity type.
-        Try to find out a specific class for this kind of entity or
-        default to a dump of the class registered for 'Any'
-        """
-        etype = str(etype)
-        if etype == 'Any':
-            return self.select('etypes', 'Any', 'Any')
-        eschema = self.schema.eschema(etype)
-        baseschemas = [eschema] + eschema.ancestors()
-        # browse ancestors from most specific to most generic and
-        # try to find an associated custom entity class
-        for baseschema in baseschemas:
-            try:
-                btype = ETYPE_NAME_MAP[baseschema]
-            except KeyError:
-                btype = str(baseschema)
-            try:
-                cls = self.select('etypes', btype, etype)
-                break
-            except ObjectNotFound:
-                pass
-        else:
-            # no entity class for any of the ancestors, fallback to the default
-            # one
-            cls = self.select('etypes', 'Any', etype)
-        return cls
+        return self["etypes"].etype_class(etype)
 
-    def render(self, __oid, req, __fallback_oid=None, __registry='views',
-               rset=None, **kwargs):
-        """select object, or fallback object if specified and the first one
-        isn't selectable, then render it
-        """
-        try:
-            obj = self.select(__registry, __oid, req, rset=rset, **kwargs)
-        except NoSelectableObject:
-            if __fallback_oid is None:
-                raise
-            obj = self.select(__registry, __fallback_oid, req, rset=rset,
-                              **kwargs)
-        return obj.render(**kwargs)
-
+    @deprecated('use vreg["views"].main_template(*args, **kwargs)')
     def main_template(self, req, oid='main-template', **context):
-        """display query by calling the given template (default to main),
-        and returning the output as a string instead of requiring the [w]rite
-        method as argument
-        """
-        res = self.render(oid, req, **context)
-        if isinstance(res, unicode):
-            return res.encode(req.encoding)
-        assert isinstance(res, str)
-        return res
+        return self["views"].main_template(req, oid, **context)
 
-    def select_vobject(self, registry, oid, *args, **kwargs):
-        selected = self.select_object(registry, oid, *args, **kwargs)
-        if selected and selected.propval('visible'):
-            return selected
-        return None
-
+    @deprecated('use vreg[registry].possible_vobjects(*args, **kwargs)')
     def possible_vobjects(self, registry, *args, **kwargs):
-        """return an ordered list of possible app objects in a given registry,
-        supposing they support the 'visible' and 'order' properties (as most
-        visualizable objects)
-        """
-        return [x for x in sorted(self.possible_objects(registry, *args, **kwargs),
-                                  key=lambda x: x.propval('order'))
-                if x.propval('visible')]
+        return self[registry].possible_vobjects(*args, **kwargs)
 
+    @deprecated('use vreg["actions"].possible_actions(*args, **kwargs)')
     def possible_actions(self, req, rset=None, **kwargs):
-        if rset is None:
-            actions = self.possible_vobjects('actions', req, rset=rset, **kwargs)
-        else:
-            actions = rset.possible_actions(**kwargs) # cached implementation
-        result = {}
-        for action in actions:
-            result.setdefault(action.category, []).append(action)
-        return result
-
-    def possible_views(self, req, rset=None, **kwargs):
-        """return an iterator on possible views for this result set
-
-        views returned are classes, not instances
-        """
-        for vid, views in self.registry('views').items():
-            if vid[0] == '_':
-                continue
-            try:
-                view = self.select_best(views, req, rset=rset, **kwargs)
-                if view.linkable():
-                    yield view
-            except NoSelectableObject:
-                continue
-            except Exception:
-                self.exception('error while trying to select %s view for %s',
-                               vid, rset)
+        return self["actions"].possible_actions(req, rest=rset, **kwargs)
 
     @deprecated("use .select_object('boxes', ...)")
     def select_box(self, oid, *args, **kwargs):
-        """return the most specific view according to the result set"""
-        return self.select_object('boxes', oid, *args, **kwargs)
+        return self['boxes'].select_object(oid, *args, **kwargs)
 
     @deprecated("use .select_object('components', ...)")
     def select_component(self, cid, *args, **kwargs):
-        """return the most specific component according to the result set"""
-        return self.select_object('components', cid, *args, **kwargs)
+        return self['components'].select_object(cid, *args, **kwargs)
 
     @deprecated("use .select_object('actions', ...)")
     def select_action(self, oid, *args, **kwargs):
-        """return the most specific view according to the result set"""
-        return self.select_object('actions', oid, *args, **kwargs)
+        return self['actions'].select_object(oid, *args, **kwargs)
 
     @deprecated("use .select('views', ...)")
     def select_view(self, __vid, req, rset=None, **kwargs):
-        """return the most specific view according to the result set"""
-        return self.select('views', __vid, req, rset=rset, **kwargs)
+        return self['views'].select(__vid, req, rset=rset, **kwargs)
 
     # properties handling #####################################################
 
@@ -316,7 +382,7 @@
     def register_property(self, key, type, help, default=None, vocabulary=None,
                           sitewide=False):
         """register a given property"""
-        properties = self._registries['propertydefs']
+        properties = self['propertydefs']
         assert type in YAMS_TO_PY
         properties[key] = {'type': type, 'vocabulary': vocabulary,
                            'default': default, 'help': help,
@@ -328,7 +394,7 @@
         boolean)
         """
         try:
-            return self._registries['propertydefs'][key]
+            return self['propertydefs'][key]
         except KeyError:
             if key.startswith('system.version.'):
                 soft = key.split('.')[-1]
@@ -339,9 +405,9 @@
 
     def property_value(self, key):
         try:
-            return self._registries['propertyvalues'][key]
+            return self['propertyvalues'][key]
         except KeyError:
-            return self._registries['propertydefs'][key]['default']
+            return self['propertydefs'][key]['default']
 
     def typed_value(self, key, value):
         """value is an unicode string, return it correctly typed. Let potential
@@ -364,7 +430,7 @@
         """init the property values registry using the given set of couple (key, value)
         """
         self.initialized = True
-        values = self._registries['propertyvalues']
+        values = self['propertyvalues']
         for key, val in propvalues:
             try:
                 values[key] = self.typed_value(key, val)
@@ -375,61 +441,6 @@
                 self.warning('%s (you should probably delete that property '
                              'from the database)', ex)
 
-    def parse(self, session, rql, args=None):
-        rqlst = self.rqlhelper.parse(rql)
-        def type_from_eid(eid, session=session):
-            return session.describe(eid)[0]
-        try:
-            self.rqlhelper.compute_solutions(rqlst, {'eid': type_from_eid}, args)
-        except UnknownEid:
-            for select in rqlst.children:
-                select.solutions = []
-        return rqlst
-
-    @property
-    @cached
-    def rqlhelper(self):
-        return RQLHelper(self.schema,
-                         special_relations={'eid': 'uid', 'has_text': 'fti'})
-
-
-class MulCnxCubicWebRegistry(CubicWebRegistry):
-    """special registry to be used when an application has to deal with
-    connections to differents repository. This class add some additional wrapper
-    trying to hide buggy class attributes since classes are not designed to be
-    shared among multiple registries.
-    """
-    def etype_class(self, etype):
-        """return an entity class for the given entity type.
-        Try to find out a specific class for this kind of entity or
-        default to a dump of the class registered for 'Any'
-        """
-        usercls = super(MulCnxCubicWebRegistry, self).etype_class(etype)
-        if etype == 'Any':
-            return usercls
-        usercls.e_schema = self.schema.eschema(etype)
-        return usercls
-
-    def select_best(self, vobjects, *args, **kwargs):
-        """return an instance of the most specific object according
-        to parameters
-
-        raise NoSelectableObject if no object apply
-        """
-        for vobjectcls in vobjects:
-            self._fix_cls_attrs(vobjectcls)
-        selected = super(MulCnxCubicWebRegistry, self).select_best(
-            vobjects, *args, **kwargs)
-        # redo the same thing on the instance so it won't use equivalent class
-        # attributes (which may change)
-        self._fix_cls_attrs(selected)
-        return selected
-
-    def _fix_cls_attrs(self, vobject):
-        vobject.vreg = self
-        vobject.schema = self.schema
-        vobject.config = self.config
-
 
 from datetime import datetime, date, time, timedelta