cwvreg.py
changeset 8190 2a3c1b787688
parent 7990 a673d1d9a738
child 8437 c9ab72f0645d
equal deleted inserted replaced
8189:2ee0ef069fa7 8190:2a3c1b787688
     1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     1 # copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     3 #
     3 #
     4 # This file is part of CubicWeb.
     4 # This file is part of CubicWeb.
     5 #
     5 #
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    14 # details.
    14 # details.
    15 #
    15 #
    16 # You should have received a copy of the GNU Lesser General Public License along
    16 # You should have received a copy of the GNU Lesser General Public License along
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """.. VRegistry:
    18 """.. RegistryStore:
    19 
    19 
    20 The `VRegistry`
    20 The `RegistryStore`
    21 ---------------
    21 -------------------
    22 
    22 
    23 The `VRegistry` can be seen as a two-level dictionary. It contains
    23 The `RegistryStore` can be seen as a two-level dictionary. It contains
    24 all dynamically loaded objects (subclasses of :ref:`appobject`) to
    24 all dynamically loaded objects (subclasses of :ref:`appobject`) to
    25 build a |cubicweb| application. Basically:
    25 build a |cubicweb| application. Basically:
    26 
    26 
    27 * the first level key returns a *registry*. This key corresponds to the
    27 * the first level key returns a *registry*. This key corresponds to the
    28   `__registry__` attribute of application object classes
    28   `__registry__` attribute of application object classes
    32   attribute of application object classes.
    32   attribute of application object classes.
    33 
    33 
    34 A *registry* holds a specific kind of application objects. There is
    34 A *registry* holds a specific kind of application objects. There is
    35 for instance a registry for entity classes, another for views, etc...
    35 for instance a registry for entity classes, another for views, etc...
    36 
    36 
    37 The `VRegistry` has two main responsibilities:
    37 The `RegistryStore` has two main responsibilities:
    38 
    38 
    39 - being the access point to all registries
    39 - being the access point to all registries
    40 
    40 
    41 - handling the registration process at startup time, and during automatic
    41 - handling the registration process at startup time, and during automatic
    42   reloading in debug mode.
    42   reloading in debug mode.
    74 
    74 
    75 API for objects registration
    75 API for objects registration
    76 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    76 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    77 
    77 
    78 Here are the registration methods that you can use in the `registration_callback`
    78 Here are the registration methods that you can use in the `registration_callback`
    79 to register your objects to the `VRegistry` instance given as argument (usually
    79 to register your objects to the `RegistryStore` instance given as argument (usually
    80 named `vreg`):
    80 named `vreg`):
    81 
    81 
    82 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_all
    82 .. automethod:: cubicweb.cwvreg.CWRegistryStore.register_all
    83 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_and_replace
    83 .. automethod:: cubicweb.cwvreg.CWRegistryStore.register_and_replace
    84 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register
    84 .. automethod:: cubicweb.cwvreg.CWRegistryStore.register
    85 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.unregister
    85 .. automethod:: cubicweb.cwvreg.CWRegistryStore.unregister
    86 
    86 
    87 Examples:
    87 Examples:
    88 
    88 
    89 .. sourcecode:: python
    89 .. sourcecode:: python
    90 
    90 
   191 """
   191 """
   192 
   192 
   193 __docformat__ = "restructuredtext en"
   193 __docformat__ = "restructuredtext en"
   194 _ = unicode
   194 _ = unicode
   195 
   195 
       
   196 import sys
       
   197 from os.path import join, dirname, realpath
   196 from warnings import warn
   198 from warnings import warn
   197 from datetime import datetime, date, time, timedelta
   199 from datetime import datetime, date, time, timedelta
   198 
   200 
   199 from logilab.common.decorators import cached, clear_cache
   201 from logilab.common.decorators import cached, clear_cache
   200 from logilab.common.deprecation import deprecated, class_deprecated
   202 from logilab.common.deprecation import deprecated, class_deprecated
   201 from logilab.common.modutils import cleanup_sys_modules
   203 from logilab.common.modutils import cleanup_sys_modules
       
   204 from logilab.common.registry import (
       
   205     RegistryStore, Registry, classid,
       
   206     ObjectNotFound, NoSelectableObject, RegistryNotFound)
   202 
   207 
   203 from rql import RQLHelper
   208 from rql import RQLHelper
   204 from yams.constraints import BASE_CONVERTERS
   209 from yams.constraints import BASE_CONVERTERS
   205 
   210 
   206 from cubicweb import (ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid,
   211 from cubicweb import (CW_SOFTWARE_ROOT, ETYPE_NAME_MAP, CW_EVENT_MANAGER,
   207                       ObjectNotFound, NoSelectableObject, RegistryNotFound,
   212                       Binary, UnknownProperty, UnknownEid)
   208                       CW_EVENT_MANAGER)
       
   209 from cubicweb.vregistry import VRegistry, Registry, class_regid, classid
       
   210 from cubicweb.rtags import RTAGS
   213 from cubicweb.rtags import RTAGS
       
   214 from cubicweb.predicates import (implements, appobject_selectable,
       
   215                                  _reset_is_instance_cache)
   211 
   216 
   212 def clear_rtag_objects():
   217 def clear_rtag_objects():
   213     for rtag in RTAGS:
   218     for rtag in RTAGS:
   214         rtag.clear()
   219         rtag.clear()
   215 
   220 
   216 def use_interfaces(obj):
   221 def use_interfaces(obj):
   217     """return interfaces used by the given object by searching for implements
   222     """return interfaces required by the given object by searching for
   218     selectors
   223     `implements` predicate
   219     """
   224     """
   220     from cubicweb.selectors import implements
       
   221     impl = obj.__select__.search_selector(implements)
   225     impl = obj.__select__.search_selector(implements)
   222     if impl:
   226     if impl:
   223         return sorted(impl.expected_ifaces)
   227         return sorted(impl.expected_ifaces)
   224     return ()
   228     return ()
   225 
   229 
   226 def require_appobject(obj):
   230 def require_appobject(obj):
   227     """return interfaces used by the given object by searching for implements
   231     """return appobjects required by the given object by searching for
   228     selectors
   232     `appobject_selectable` predicate
   229     """
   233     """
   230     from cubicweb.selectors import appobject_selectable
       
   231     impl = obj.__select__.search_selector(appobject_selectable)
   234     impl = obj.__select__.search_selector(appobject_selectable)
   232     if impl:
   235     if impl:
   233         return (impl.registry, impl.regids)
   236         return (impl.registry, impl.regids)
   234     return None
   237     return None
   235 
   238 
   251         return sorted([x for x in self.possible_objects(*args, **kwargs)
   254         return sorted([x for x in self.possible_objects(*args, **kwargs)
   252                        if x.cw_propval('visible')],
   255                        if x.cw_propval('visible')],
   253                       key=lambda x: x.cw_propval('order'))
   256                       key=lambda x: x.cw_propval('order'))
   254 
   257 
   255 
   258 
   256 VRegistry.REGISTRY_FACTORY[None] = CWRegistry
       
   257 
       
   258 
   259 
   259 class ETypeRegistry(CWRegistry):
   260 class ETypeRegistry(CWRegistry):
   260 
   261 
   261     def clear_caches(self):
   262     def clear_caches(self):
   262         clear_cache(self, 'etype_class')
   263         clear_cache(self, 'etype_class')
   263         clear_cache(self, 'parent_classes')
   264         clear_cache(self, 'parent_classes')
   264         from cubicweb import selectors
   265         _reset_is_instance_cache(self.vreg)
   265         selectors._reset_is_instance_cache(self.vreg)
       
   266 
   266 
   267     def initialization_completed(self):
   267     def initialization_completed(self):
   268         """on registration completed, clear etype_class internal cache
   268         """on registration completed, clear etype_class internal cache
   269         """
   269         """
   270         super(ETypeRegistry, self).initialization_completed()
   270         super(ETypeRegistry, self).initialization_completed()
   271         # clear etype cache if you don't want to run into deep weirdness
   271         # clear etype cache if you don't want to run into deep weirdness
   272         self.clear_caches()
   272         self.clear_caches()
   273 
   273 
   274     def register(self, obj, **kwargs):
   274     def register(self, obj, **kwargs):
   275         oid = kwargs.get('oid') or class_regid(obj)
   275         oid = kwargs.get('oid') or obj.__regid__
   276         if oid != 'Any' and not oid in self.schema:
   276         if oid != 'Any' and not oid in self.schema:
   277             self.error('don\'t register %s, %s type not defined in the '
   277             self.error('don\'t register %s, %s type not defined in the '
   278                        'schema', obj, oid)
   278                        'schema', obj, oid)
   279             return
   279             return
   280         kwargs['clear'] = True
   280         kwargs['clear'] = True
   352         for ttype in targettypes:
   352         for ttype in targettypes:
   353             etypecls = self.etype_class(ttype)
   353             etypecls = self.etype_class(ttype)
   354             fetchattrs_list.append(set(etypecls.fetch_attrs))
   354             fetchattrs_list.append(set(etypecls.fetch_attrs))
   355         return reduce(set.intersection, fetchattrs_list)
   355         return reduce(set.intersection, fetchattrs_list)
   356 
   356 
   357 VRegistry.REGISTRY_FACTORY['etypes'] = ETypeRegistry
       
   358 
       
   359 
   357 
   360 class ViewsRegistry(CWRegistry):
   358 class ViewsRegistry(CWRegistry):
   361 
   359 
   362     def main_template(self, req, oid='main-template', rset=None, **kwargs):
   360     def main_template(self, req, oid='main-template', rset=None, **kwargs):
   363         """display query by calling the given template (default to main),
   361         """display query by calling the given template (default to main),
   387                     yield view
   385                     yield view
   388             except Exception:
   386             except Exception:
   389                 self.exception('error while trying to select %s view for %s',
   387                 self.exception('error while trying to select %s view for %s',
   390                                vid, rset)
   388                                vid, rset)
   391 
   389 
   392 VRegistry.REGISTRY_FACTORY['views'] = ViewsRegistry
       
   393 
       
   394 
   390 
   395 class ActionsRegistry(CWRegistry):
   391 class ActionsRegistry(CWRegistry):
   396     def poss_visible_objects(self, *args, **kwargs):
   392     def poss_visible_objects(self, *args, **kwargs):
   397         """return an ordered list of possible actions"""
   393         """return an ordered list of possible actions"""
   398         return sorted(self.possible_objects(*args, **kwargs),
   394         return sorted(self.possible_objects(*args, **kwargs),
   405             actions = rset.possible_actions(**kwargs) # cached implementation
   401             actions = rset.possible_actions(**kwargs) # cached implementation
   406         result = {}
   402         result = {}
   407         for action in actions:
   403         for action in actions:
   408             result.setdefault(action.category, []).append(action)
   404             result.setdefault(action.category, []).append(action)
   409         return result
   405         return result
   410 
       
   411 VRegistry.REGISTRY_FACTORY['actions'] = ActionsRegistry
       
   412 
   406 
   413 
   407 
   414 class CtxComponentsRegistry(CWRegistry):
   408 class CtxComponentsRegistry(CWRegistry):
   415     def poss_visible_objects(self, *args, **kwargs):
   409     def poss_visible_objects(self, *args, **kwargs):
   416         """return an ordered list of possible components"""
   410         """return an ordered list of possible components"""
   443         # XXX set context for bw compat (should now be taken by comp.render())
   437         # XXX set context for bw compat (should now be taken by comp.render())
   444         for component in thisctxcomps:
   438         for component in thisctxcomps:
   445             component.cw_extra_kwargs['context'] = context
   439             component.cw_extra_kwargs['context'] = context
   446         return thisctxcomps
   440         return thisctxcomps
   447 
   441 
   448 VRegistry.REGISTRY_FACTORY['ctxcomponents'] = CtxComponentsRegistry
       
   449 
       
   450 
   442 
   451 class BwCompatCWRegistry(object):
   443 class BwCompatCWRegistry(object):
   452     def __init__(self, vreg, oldreg, redirecttoreg):
   444     def __init__(self, vreg, oldreg, redirecttoreg):
   453         self.vreg = vreg
   445         self.vreg = vreg
   454         self.oldreg = oldreg
   446         self.oldreg = oldreg
   460         return getattr(self.vreg[self.redirecto], attr)
   452         return getattr(self.vreg[self.redirecto], attr)
   461 
   453 
   462     def clear(self): pass
   454     def clear(self): pass
   463     def initialization_completed(self): pass
   455     def initialization_completed(self): pass
   464 
   456 
   465 class CubicWebVRegistry(VRegistry):
   457 
       
   458 class CWRegistryStore(RegistryStore):
   466     """Central registry for the cubicweb instance, extending the generic
   459     """Central registry for the cubicweb instance, extending the generic
   467     VRegistry with some cubicweb specific stuff.
   460     RegistryStore with some cubicweb specific stuff.
   468 
   461 
   469     This is one of the central object in cubicweb instance, coupling
   462     This is one of the central object in cubicweb instance, coupling
   470     dynamically loaded objects with the schema and the configuration objects.
   463     dynamically loaded objects with the schema and the configuration objects.
   471 
   464 
   472     It specializes the VRegistry by adding some convenience methods to access to
   465     It specializes the RegistryStore by adding some convenience methods to access to
   473     stored objects. Currently we have the following registries of objects known
   466     stored objects. Currently we have the following registries of objects known
   474     by the web instance (library may use some others additional registries):
   467     by the web instance (library may use some others additional registries):
   475 
   468 
   476     * 'etypes', entity type classes
   469     * 'etypes', entity type classes
   477 
   470 
   489     * 'formrenderers', rendering forms to html
   482     * 'formrenderers', rendering forms to html
   490 
   483 
   491     * 'controllers', primary objects to handle request publishing, directly
   484     * 'controllers', primary objects to handle request publishing, directly
   492       plugged into the application
   485       plugged into the application
   493     """
   486     """
       
   487 
       
   488     REGISTRY_FACTORY = {None: CWRegistry,
       
   489                         'etypes': ETypeRegistry,
       
   490                         'views': ViewsRegistry,
       
   491                         'actions': ActionsRegistry,
       
   492                         'ctxcomponents': CtxComponentsRegistry,
       
   493                         }
   494 
   494 
   495     def __init__(self, config, initlog=True):
   495     def __init__(self, config, initlog=True):
   496         if initlog:
   496         if initlog:
   497             # first init log service
   497             # first init log service
   498             config.init_log()
   498             config.init_log()
   499         super(CubicWebVRegistry, self).__init__(config)
   499         super(CWRegistryStore, self).__init__(config.debugmode)
       
   500         self.config = config
       
   501         # need to clean sys.path this to avoid import confusion pb (i.e.  having
       
   502         # the same module loaded as 'cubicweb.web.views' subpackage and as
       
   503         # views' or 'web.views' subpackage. This is mainly for testing purpose,
       
   504         # we should'nt need this in production environment
       
   505         for webdir in (join(dirname(realpath(__file__)), 'web'),
       
   506                        join(dirname(__file__), 'web')):
       
   507             if webdir in sys.path:
       
   508                 sys.path.remove(webdir)
       
   509         if CW_SOFTWARE_ROOT in sys.path:
       
   510             sys.path.remove(CW_SOFTWARE_ROOT)
   500         self.schema = None
   511         self.schema = None
   501         self.initialized = False
   512         self.initialized = False
   502         # XXX give force_reload (or refactor [re]loading...)
   513         # XXX give force_reload (or refactor [re]loading...)
   503         if self.config.mode != 'test':
   514         if self.config.mode != 'test':
   504             # don't clear rtags during test, this may cause breakage with
   515             # don't clear rtags during test, this may cause breakage with
   513         except RegistryNotFound:
   524         except RegistryNotFound:
   514             self[regid] = self.registry_class(regid)(self)
   525             self[regid] = self.registry_class(regid)(self)
   515             return self[regid]
   526             return self[regid]
   516 
   527 
   517     def items(self):
   528     def items(self):
   518         return [item for item in super(CubicWebVRegistry, self).items()
   529         return [item for item in super(CWRegistryStore, self).items()
   519                 if not item[0] in ('propertydefs', 'propertyvalues')]
   530                 if not item[0] in ('propertydefs', 'propertyvalues')]
   520     def iteritems(self):
   531     def iteritems(self):
   521         return (item for item in super(CubicWebVRegistry, self).iteritems()
   532         return (item for item in super(CWRegistryStore, self).iteritems()
   522                 if not item[0] in ('propertydefs', 'propertyvalues'))
   533                 if not item[0] in ('propertydefs', 'propertyvalues'))
   523 
   534 
   524     def values(self):
   535     def values(self):
   525         return [value for key, value in self.items()]
   536         return [value for key, value in self.items()]
   526     def itervalues(self):
   537     def itervalues(self):
   527         return (value for key, value in self.items())
   538         return (value for key, value in self.items())
   528 
   539 
   529     def reset(self):
   540     def reset(self):
   530         CW_EVENT_MANAGER.emit('before-registry-reset', self)
   541         CW_EVENT_MANAGER.emit('before-registry-reset', self)
   531         super(CubicWebVRegistry, self).reset()
   542         super(CWRegistryStore, self).reset()
   532         self._needs_iface = {}
   543         self._needs_iface = {}
   533         self._needs_appobject = {}
   544         self._needs_appobject = {}
   534         # two special registries, propertydefs which care all the property
   545         # two special registries, propertydefs which care all the property
   535         # definitions, and propertyvals which contains values for those
   546         # definitions, and propertyvals which contains values for those
   536         # properties
   547         # properties
   595     def register_if_interface_found(self, obj, ifaces, **kwargs):
   606     def register_if_interface_found(self, obj, ifaces, **kwargs):
   596         """register `obj` but remove it if no entity class implements one of
   607         """register `obj` but remove it if no entity class implements one of
   597         the given `ifaces` interfaces at the end of the registration process.
   608         the given `ifaces` interfaces at the end of the registration process.
   598 
   609 
   599         Extra keyword arguments are given to the
   610         Extra keyword arguments are given to the
   600         :meth:`~cubicweb.cwvreg.CubicWebVRegistry.register` function.
   611         :meth:`~cubicweb.cwvreg.CWRegistryStore.register` function.
   601         """
   612         """
   602         self.register(obj, **kwargs)
   613         self.register(obj, **kwargs)
   603         if not isinstance(ifaces,  (tuple, list)):
   614         if not isinstance(ifaces,  (tuple, list)):
   604             self._needs_iface[obj] = (ifaces,)
   615             self._needs_iface[obj] = (ifaces,)
   605         else:
   616         else:
   611         `obj.__regid__` if not specified.
   622         `obj.__regid__` if not specified.
   612 
   623 
   613         If `clear` is true, all objects with the same identifier will be
   624         If `clear` is true, all objects with the same identifier will be
   614         previously unregistered.
   625         previously unregistered.
   615         """
   626         """
   616         super(CubicWebVRegistry, self).register(obj, *args, **kwargs)
   627         super(CWRegistryStore, self).register(obj, *args, **kwargs)
   617         # XXX bw compat
   628         # XXX bw compat
   618         ifaces = use_interfaces(obj)
   629         ifaces = use_interfaces(obj)
   619         if ifaces:
   630         if ifaces:
   620             if not obj.__name__.endswith('Adapter') and \
   631             if not obj.__name__.endswith('Adapter') and \
   621                    any(iface for iface in ifaces if not isinstance(iface, basestring)):
   632                    any(iface for iface in ifaces if not isinstance(iface, basestring)):
   628             self._needs_appobject[obj] = depends_on
   639             self._needs_appobject[obj] = depends_on
   629 
   640 
   630     def register_objects(self, path):
   641     def register_objects(self, path):
   631         """overriden to give cubicweb's extrapath (eg cubes package's __path__)
   642         """overriden to give cubicweb's extrapath (eg cubes package's __path__)
   632         """
   643         """
   633         super(CubicWebVRegistry, self).register_objects(
   644         super(CWRegistryStore, self).register_objects(
   634             path, self.config.extrapath)
   645             path, self.config.extrapath)
   635 
   646 
   636     def initialization_completed(self):
   647     def initialization_completed(self):
   637         """cw specific code once vreg initialization is completed:
   648         """cw specific code once vreg initialization is completed:
   638 
   649 
   683                         break
   694                         break
   684                 else:
   695                 else:
   685                     self.debug('unregister %s (no %s object in registry %s)',
   696                     self.debug('unregister %s (no %s object in registry %s)',
   686                                classid(obj), ' or '.join(regids), regname)
   697                                classid(obj), ' or '.join(regids), regname)
   687                     self.unregister(obj)
   698                     self.unregister(obj)
   688         super(CubicWebVRegistry, self).initialization_completed()
   699         super(CWRegistryStore, self).initialization_completed()
   689         for rtag in RTAGS:
   700         for rtag in RTAGS:
   690             # don't check rtags if we don't want to cleanup_interface_sobjects
   701             # don't check rtags if we don't want to cleanup_interface_sobjects
   691             rtag.init(self.schema, check=self.config.cleanup_interface_sobjects)
   702             rtag.init(self.schema, check=self.config.cleanup_interface_sobjects)
   692 
   703 
   693     # rql parsing utilities ####################################################
   704     # rql parsing utilities ####################################################