cwvreg.py
brancholdstable
changeset 4985 02b52bf9f5f8
parent 4719 aaed3f813ef8
child 4966 e968e0a7776b
equal deleted inserted replaced
4563:c25da7573ebd 4985:02b52bf9f5f8
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     7 """
     7 """
     8 __docformat__ = "restructuredtext en"
     8 __docformat__ = "restructuredtext en"
     9 _ = unicode
     9 _ = unicode
    10 
    10 
    11 from logilab.common.decorators import cached, clear_cache, monkeypatch
    11 from logilab.common.decorators import cached, clear_cache
    12 from logilab.common.deprecation import  deprecated
    12 from logilab.common.deprecation import  deprecated
    13 from logilab.common.modutils import cleanup_sys_modules
    13 from logilab.common.modutils import cleanup_sys_modules
    14 
    14 
    15 from rql import RQLHelper
    15 from rql import RQLHelper
    16 
    16 
    17 from cubicweb import (ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid,
    17 from cubicweb import (ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid,
    18                       ObjectNotFound, NoSelectableObject, RegistryNotFound,
    18                       ObjectNotFound, NoSelectableObject, RegistryNotFound,
    19                       RegistryOutOfDate, CW_EVENT_MANAGER, onevent)
    19                       RegistryOutOfDate, CW_EVENT_MANAGER, onevent)
    20 from cubicweb.utils import dump_class
    20 from cubicweb.utils import dump_class
    21 from cubicweb.vregistry import VRegistry, Registry
    21 from cubicweb.vregistry import VRegistry, Registry, class_regid
    22 from cubicweb.rtags import RTAGS
    22 from cubicweb.rtags import RTAGS
    23 
    23 
    24 
    24 
    25 @onevent('before-registry-reload')
    25 @onevent('before-registry-reload')
    26 def clear_rtag_objects():
    26 def clear_rtag_objects():
    56 
    56 
    57     @property
    57     @property
    58     def schema(self):
    58     def schema(self):
    59         return self.vreg.schema
    59         return self.vreg.schema
    60 
    60 
    61     def initialization_completed(self):
    61     @deprecated('[3.6] select object, then use obj.render()')
    62         # call vreg_initialization_completed on appobjects and print
       
    63         # registry content
       
    64         for appobjects in self.itervalues():
       
    65             for appobject in appobjects:
       
    66                 # XXX kill vreg_initialization_completed
       
    67                 appobject.vreg_initialization_completed()
       
    68 
       
    69     def render(self, __oid, req, __fallback_oid=None, rset=None, initargs=None,
    62     def render(self, __oid, req, __fallback_oid=None, rset=None, initargs=None,
    70                **kwargs):
    63                **kwargs):
    71         """Select object with the given id (`__oid`) then render it.  If the
    64         """Select object with the given id (`__oid`) then render it.  If the
    72         object isn't selectable, try to select fallback object if
    65         object isn't selectable, try to select fallback object if
    73         `__fallback_oid` is specified.
    66         `__fallback_oid` is specified.
    88             if __fallback_oid is None:
    81             if __fallback_oid is None:
    89                 raise
    82                 raise
    90             obj = self.select(__fallback_oid, req, rset=rset, **initargs)
    83             obj = self.select(__fallback_oid, req, rset=rset, **initargs)
    91         return obj.render(**kwargs)
    84         return obj.render(**kwargs)
    92 
    85 
       
    86     @deprecated('[3.6] use select_or_none and test for obj.cw_propval("visible")')
    93     def select_vobject(self, oid, *args, **kwargs):
    87     def select_vobject(self, oid, *args, **kwargs):
    94         selected = self.select_object(oid, *args, **kwargs)
    88         selected = self.select_or_none(oid, *args, **kwargs)
    95         if selected and selected.propval('visible'):
    89         if selected and selected.cw_propval('visible'):
    96             return selected
    90             return selected
    97         return None
    91         return None
    98 
    92 
    99     def possible_vobjects(self, *args, **kwargs):
    93     def poss_visible_objects(self, *args, **kwargs):
   100         """return an ordered list of possible app objects in a given registry,
    94         """return an ordered list of possible app objects in a given registry,
   101         supposing they support the 'visible' and 'order' properties (as most
    95         supposing they support the 'visible' and 'order' properties (as most
   102         visualizable objects)
    96         visualizable objects)
   103         """
    97         """
   104         return sorted([x for x in self.possible_objects(*args, **kwargs)
    98         return sorted([x for x in self.possible_objects(*args, **kwargs)
   105                        if x.propval('visible')],
    99                        if x.cw_propval('visible')],
   106                       key=lambda x: x.propval('order'))
   100                       key=lambda x: x.cw_propval('order'))
       
   101     possible_vobjects = deprecated('[3.6] use poss_visible_objects()')(poss_visible_objects)
   107 
   102 
   108 
   103 
   109 VRegistry.REGISTRY_FACTORY[None] = CWRegistry
   104 VRegistry.REGISTRY_FACTORY[None] = CWRegistry
   110 
   105 
   111 
   106 
   115         """on registration completed, clear etype_class internal cache
   110         """on registration completed, clear etype_class internal cache
   116         """
   111         """
   117         super(ETypeRegistry, self).initialization_completed()
   112         super(ETypeRegistry, self).initialization_completed()
   118         # clear etype cache if you don't want to run into deep weirdness
   113         # clear etype cache if you don't want to run into deep weirdness
   119         clear_cache(self, 'etype_class')
   114         clear_cache(self, 'etype_class')
       
   115         clear_cache(self, 'parent_classes')
   120 
   116 
   121     def register(self, obj, **kwargs):
   117     def register(self, obj, **kwargs):
   122         oid = kwargs.get('oid') or obj.id
   118         oid = kwargs.get('oid') or class_regid(obj)
   123         if oid != 'Any' and not oid in self.schema:
   119         if oid != 'Any' and not oid in self.schema:
   124             self.error('don\'t register %s, %s type not defined in the '
   120             self.error('don\'t register %s, %s type not defined in the '
   125                        'schema', obj, obj.id)
   121                        'schema', obj, oid)
   126             return
   122             return
   127         kwargs['clear'] = True
   123         kwargs['clear'] = True
   128         super(ETypeRegistry, self).register(obj, **kwargs)
   124         super(ETypeRegistry, self).register(obj, **kwargs)
   129 
   125 
   130     @cached
   126     @cached
       
   127     def parent_classes(self, etype):
       
   128         if etype == 'Any':
       
   129             return [self.etype_class('Any')]
       
   130         eschema = self.schema.eschema(etype)
       
   131         parents = [self.etype_class(e.type) for e in eschema.ancestors()]
       
   132         parents.append(self.etype_class('Any'))
       
   133         return parents
       
   134 
       
   135     @cached
   131     def etype_class(self, etype):
   136     def etype_class(self, etype):
   132         """return an entity class for the given entity type.
   137         """return an entity class for the given entity type.
   133 
   138 
   134         Try to find out a specific class for this kind of entity or default to a
   139         Try to find out a specific class for this kind of entity or default to a
   135         dump of the nearest parent class (in yams inheritance) registered.
   140         dump of the nearest parent class (in yams inheritance) registered.
   136 
   141 
   137         Fall back to 'Any' if not yams parent class found.
   142         Fall back to 'Any' if not yams parent class found.
   138         """
   143         """
   139         etype = str(etype)
   144         etype = str(etype)
   140         if etype == 'Any':
   145         if etype == 'Any':
   141             return self.select('Any', 'Any')
   146             objects = self['Any']
       
   147             assert len(objects) == 1, objects
       
   148             return objects[0]
   142         eschema = self.schema.eschema(etype)
   149         eschema = self.schema.eschema(etype)
   143         baseschemas = [eschema] + eschema.ancestors()
   150         baseschemas = [eschema] + eschema.ancestors()
   144         # browse ancestors from most specific to most generic and try to find an
   151         # browse ancestors from most specific to most generic and try to find an
   145         # associated custom entity class
   152         # associated custom entity class
   146         cls = None
       
   147         for baseschema in baseschemas:
   153         for baseschema in baseschemas:
   148             try:
   154             try:
   149                 btype = ETYPE_NAME_MAP[baseschema]
   155                 btype = ETYPE_NAME_MAP[baseschema]
   150             except KeyError:
   156             except KeyError:
   151                 btype = str(baseschema)
   157                 btype = str(baseschema)
   152             if cls is None:
   158             try:
   153                 try:
   159                 objects = self[btype]
   154                     objects = self[btype]
   160                 assert len(objects) == 1, objects
   155                     assert len(objects) == 1, objects
   161                 if btype == etype:
   156                     if btype == etype:
   162                     cls = objects[0]
   157                         cls = objects[0]
   163                 else:
   158                     else:
   164                     # recurse to ensure issubclass(etype_class('Child'),
   159                         cls = self.etype_class(btype)
   165                     #                              etype_class('Parent'))
   160                 except ObjectNotFound:
   166                     cls = self.etype_class(btype)
   161                     continue
   167                 break
   162             else:
   168             except ObjectNotFound:
   163                 # ensure parent classes are built first
   169                 pass
   164                 self.etype_class(btype)
   170         else:
   165         if cls is None:
       
   166             # no entity class for any of the ancestors, fallback to the default
   171             # no entity class for any of the ancestors, fallback to the default
   167             # one
   172             # one
   168             objects = self['Any']
   173             objects = self['Any']
   169             assert len(objects) == 1, objects
   174             assert len(objects) == 1, objects
   170             cls = objects[0]
   175             cls = objects[0]
   171         # make a copy event if cls.id == etype, else we may have pb for client
   176         # make a copy event if cls.__regid__ == etype, else we may have pb for
   172         # application using multiple connections to different repositories (eg
   177         # client application using multiple connections to different
   173         # shingouz)
   178         # repositories (eg shingouz)
   174         cls = dump_class(cls, etype)
   179         cls = dump_class(cls, etype)
   175         cls.id = etype
   180         cls.__regid__ = etype
   176         cls.__initialize__()
   181         cls.__initialize__(self.schema)
   177         return cls
   182         return cls
   178 
   183 
   179 VRegistry.REGISTRY_FACTORY['etypes'] = ETypeRegistry
   184 VRegistry.REGISTRY_FACTORY['etypes'] = ETypeRegistry
   180 
   185 
   181 
   186 
   182 class ViewsRegistry(CWRegistry):
   187 class ViewsRegistry(CWRegistry):
   183 
   188 
   184     def main_template(self, req, oid='main-template', **kwargs):
   189     def main_template(self, req, oid='main-template', rset=None, **kwargs):
   185         """display query by calling the given template (default to main),
   190         """display query by calling the given template (default to main),
   186         and returning the output as a string instead of requiring the [w]rite
   191         and returning the output as a string instead of requiring the [w]rite
   187         method as argument
   192         method as argument
   188         """
   193         """
   189         res = self.render(oid, req, **kwargs)
   194         obj = self.select(oid, req, rset=rset, **kwargs)
       
   195         res = obj.render(**kwargs)
   190         if isinstance(res, unicode):
   196         if isinstance(res, unicode):
   191             return res.encode(req.encoding)
   197             return res.encode(req.encoding)
   192         assert isinstance(res, str)
   198         assert isinstance(res, str)
   193         return res
   199         return res
   194 
   200 
   199         """
   205         """
   200         for vid, views in self.items():
   206         for vid, views in self.items():
   201             if vid[0] == '_':
   207             if vid[0] == '_':
   202                 continue
   208                 continue
   203             try:
   209             try:
   204                 view = self.select_best(views, req, rset=rset, **kwargs)
   210                 view = self._select_best(views, req, rset=rset, **kwargs)
   205                 if view.linkable():
   211                 if view.linkable():
   206                     yield view
   212                     yield view
   207             except NoSelectableObject:
   213             except NoSelectableObject:
   208                 continue
   214                 continue
   209             except Exception:
   215             except Exception:
   215 
   221 
   216 class ActionsRegistry(CWRegistry):
   222 class ActionsRegistry(CWRegistry):
   217 
   223 
   218     def possible_actions(self, req, rset=None, **kwargs):
   224     def possible_actions(self, req, rset=None, **kwargs):
   219         if rset is None:
   225         if rset is None:
   220             actions = self.possible_vobjects(req, rset=rset, **kwargs)
   226             actions = self.poss_visible_objects(req, rset=rset, **kwargs)
   221         else:
   227         else:
   222             actions = rset.possible_actions(**kwargs) # cached implementation
   228             actions = rset.possible_actions(**kwargs) # cached implementation
   223         result = {}
   229         result = {}
   224         for action in actions:
   230         for action in actions:
   225             result.setdefault(action.category, []).append(action)
   231             result.setdefault(action.category, []).append(action)
   332                 for obj in objects:
   338                 for obj in objects:
   333                     obj.schema = schema
   339                     obj.schema = schema
   334 
   340 
   335     def register_if_interface_found(self, obj, ifaces, **kwargs):
   341     def register_if_interface_found(self, obj, ifaces, **kwargs):
   336         """register an object but remove it if no entity class implements one of
   342         """register an object but remove it if no entity class implements one of
   337         the given interfaces
   343         the given interfaces at the end of the registration process
   338         """
   344         """
   339         self.register(obj, **kwargs)
   345         self.register(obj, **kwargs)
   340         if not isinstance(ifaces,  (tuple, list)):
   346         if not isinstance(ifaces,  (tuple, list)):
   341             self._needs_iface[obj] = (ifaces,)
   347             self._needs_iface[obj] = (ifaces,)
   342         else:
   348         else:
   352     def register_objects(self, path, force_reload=None):
   358     def register_objects(self, path, force_reload=None):
   353         """overriden to remove objects requiring a missing interface"""
   359         """overriden to remove objects requiring a missing interface"""
   354         if force_reload is None:
   360         if force_reload is None:
   355             force_reload = self.config.debugmode
   361             force_reload = self.config.debugmode
   356         try:
   362         try:
   357             self._register_objects(path, force_reload)
   363             super(CubicWebVRegistry, self).register_objects(
       
   364                 path, force_reload, self.config.extrapath)
   358         except RegistryOutOfDate:
   365         except RegistryOutOfDate:
   359             CW_EVENT_MANAGER.emit('before-registry-reload')
   366             CW_EVENT_MANAGER.emit('before-registry-reload')
   360             # modification detected, reset and reload
   367             # modification detected, reset and reload
   361             self.reset(path, force_reload)
   368             self.reset(path, force_reload)
   362             self._register_objects(path, force_reload)
   369             super(CubicWebVRegistry, self).register_objects(
       
   370                 path, force_reload, self.config.extrapath)
   363             CW_EVENT_MANAGER.emit('after-registry-reload')
   371             CW_EVENT_MANAGER.emit('after-registry-reload')
   364 
   372 
   365     def _register_objects(self, path, force_reload=None):
       
   366         """overriden to remove objects requiring a missing interface"""
       
   367         extrapath = {}
       
   368         for cubesdir in self.config.cubes_search_path():
       
   369             if cubesdir != self.config.CUBES_DIR:
       
   370                 extrapath[cubesdir] = 'cubes'
       
   371         if super(CubicWebVRegistry, self).register_objects(path, force_reload,
       
   372                                                           extrapath):
       
   373             self.initialization_completed()
       
   374             # don't check rtags if we don't want to cleanup_interface_sobjects
       
   375             for rtag in RTAGS:
       
   376                 rtag.init(self.schema,
       
   377                           check=self.config.cleanup_interface_sobjects)
       
   378 
       
   379     def initialization_completed(self):
   373     def initialization_completed(self):
   380         for regname, reg in self.iteritems():
   374         """cw specific code once vreg initialization is completed:
   381             self.debug('available in registry %s: %s', regname, sorted(reg))
   375 
   382             reg.initialization_completed()
   376         * remove objects requiring a missing interface, unless
   383             for appobjects in reg.itervalues():
   377           config.cleanup_interface_sobjects is false
   384                 for appobjectcls in appobjects:
   378         * init rtags
   385                     appobjectcls.register_properties()
   379         """
   386         # we may want to keep interface dependent objects (e.g.for i18n
   380         # we may want to keep interface dependent objects (e.g.for i18n
   387         # catalog generation)
   381         # catalog generation)
   388         if self.config.cleanup_interface_sobjects:
   382         if self.config.cleanup_interface_sobjects:
   389             # remove appobjects that don't support any available interface
   383             # remove appobjects that don't support any available interface
   390             implemented_interfaces = set()
   384             implemented_interfaces = set()
   407                                'interface among %s)', obj, ifaces)
   401                                'interface among %s)', obj, ifaces)
   408                     self.unregister(obj)
   402                     self.unregister(obj)
   409         # clear needs_iface so we don't try to remove some not-anymore-in
   403         # clear needs_iface so we don't try to remove some not-anymore-in
   410         # objects on automatic reloading
   404         # objects on automatic reloading
   411         self._needs_iface.clear()
   405         self._needs_iface.clear()
       
   406         super(CubicWebVRegistry, self).initialization_completed()
       
   407         for rtag in RTAGS:
       
   408             # don't check rtags if we don't want to cleanup_interface_sobjects
       
   409             rtag.init(self.schema, check=self.config.cleanup_interface_sobjects)
       
   410 
   412 
   411 
   413     # rql parsing utilities ####################################################
   412     # rql parsing utilities ####################################################
   414 
   413 
   415     @property
   414     @property
   416     @cached
   415     @cached
   467 
   466 
   468     def property_value(self, key):
   467     def property_value(self, key):
   469         try:
   468         try:
   470             return self['propertyvalues'][key]
   469             return self['propertyvalues'][key]
   471         except KeyError:
   470         except KeyError:
   472             return self['propertydefs'][key]['default']
   471             return self.property_info(key)['default']
   473 
   472 
   474     def typed_value(self, key, value):
   473     def typed_value(self, key, value):
   475         """value is an unicode string, return it correctly typed. Let potential
   474         """value is an unicode string, return it correctly typed. Let potential
   476         type error propagates.
   475         type error propagates.
   477         """
   476         """