vregistry.py
branchtls-sprint
changeset 1433 091ac3ba5d51
parent 1432 2c3711d4570b
child 1641 2c80b09d8d86
equal deleted inserted replaced
1432:2c3711d4570b 1433:091ac3ba5d51
     4   objects and provide a convenient api access to those objects
     4   objects and provide a convenient api access to those objects
     5   according to a context
     5   according to a context
     6 
     6 
     7 * to interact with the vregistry, object should inherit from the
     7 * to interact with the vregistry, object should inherit from the
     8   VObject abstract class
     8   VObject abstract class
     9   
     9 
    10 * the selection procedure has been generalized by delegating to a
    10 * the selection procedure has been generalized by delegating to a
    11   selector, which is responsible to score the vobject according to the
    11   selector, which is responsible to score the vobject according to the
    12   current state (req, rset, row, col). At the end of the selection, if
    12   current state (req, rset, row, col). At the end of the selection, if
    13   a vobject class has been found, an instance of this class is
    13   a vobject class has been found, an instance of this class is
    14   returned. The selector is instantiated at vobject registration
    14   returned. The selector is instantiated at vobject registration
    51 class VObject(object):
    51 class VObject(object):
    52     """visual object, use to be handled somehow by the visual components
    52     """visual object, use to be handled somehow by the visual components
    53     registry.
    53     registry.
    54 
    54 
    55     The following attributes should be set on concret vobject subclasses:
    55     The following attributes should be set on concret vobject subclasses:
    56     
    56 
    57     :__registry__:
    57     :__registry__:
    58       name of the registry for this object (string like 'views',
    58       name of the registry for this object (string like 'views',
    59       'templates'...)
    59       'templates'...)
    60     :id:
    60     :id:
    61       object's identifier in the registry (string like 'main',
    61       object's identifier in the registry (string like 'main',
    62       'primary', 'folder_box')
    62       'primary', 'folder_box')
    63     :__select__:
    63     :__select__:
    64       class'selector
    64       class'selector
    65       
    65 
    66     Moreover, the `__abstract__` attribute may be set to True to indicate
    66     Moreover, the `__abstract__` attribute may be set to True to indicate
    67     that a vobject is abstract and should not be registered
    67     that a vobject is abstract and should not be registered
    68     """
    68     """
    69     # necessary attributes to interact with the registry
    69     # necessary attributes to interact with the registry
    70     id = None
    70     id = None
    83         return cls
    83         return cls
    84 
    84 
    85     @classmethod
    85     @classmethod
    86     def selected(cls, *args, **kwargs):
    86     def selected(cls, *args, **kwargs):
    87         """called by the registry when the vobject has been selected.
    87         """called by the registry when the vobject has been selected.
    88         
    88 
    89         It must return the  object that will be actually returned by the
    89         It must return the  object that will be actually returned by the
    90         .select method (this may be the right hook to create an
    90         .select method (this may be the right hook to create an
    91         instance for example). By default the selected object is
    91         instance for example). By default the selected object is
    92         returned without any transformation.
    92         returned without any transformation.
    93         """
    93         """
   125 class VRegistry(object):
   125 class VRegistry(object):
   126     """class responsible to register, propose and select the various
   126     """class responsible to register, propose and select the various
   127     elements used to build the web interface. Currently, we have templates,
   127     elements used to build the web interface. Currently, we have templates,
   128     views, actions and components.
   128     views, actions and components.
   129     """
   129     """
   130     
   130 
   131     def __init__(self, config):#, cache_size=1000):
   131     def __init__(self, config):#, cache_size=1000):
   132         self.config = config
   132         self.config = config
   133         # dictionnary of registry (themself dictionnary) by name
   133         # dictionnary of registry (themself dictionnary) by name
   134         self._registries = {}
   134         self._registries = {}
   135         self._lastmodifs = {}
   135         self._lastmodifs = {}
   197                 oid = obj.id
   197                 oid = obj.id
   198             except AttributeError:
   198             except AttributeError:
   199                 continue
   199                 continue
   200             if oid:
   200             if oid:
   201                 self.register(obj)
   201                 self.register(obj)
   202                 
   202 
   203     def register(self, obj, registryname=None, oid=None, clear=False):
   203     def register(self, obj, registryname=None, oid=None, clear=False):
   204         """base method to add an object in the registry"""
   204         """base method to add an object in the registry"""
   205         assert not '__abstract__' in obj.__dict__
   205         assert not '__abstract__' in obj.__dict__
   206         registryname = registryname or obj.__registry__
   206         registryname = registryname or obj.__registry__
   207         oid = oid or obj.id
   207         oid = oid or obj.id
   244                     self.warning('can\'t remove %s, not in the %s registry with id %s',
   244                     self.warning('can\'t remove %s, not in the %s registry with id %s',
   245                                  removed_id, registryname, obj.id)
   245                                  removed_id, registryname, obj.id)
   246 #                 else:
   246 #                 else:
   247 #                     # if objects is empty, remove oid from registry
   247 #                     # if objects is empty, remove oid from registry
   248 #                     if not registry[obj.id]:
   248 #                     if not registry[obj.id]:
   249 #                         del regcontent[oid]                    
   249 #                         del regcontent[oid]
   250                 break
   250                 break
   251     
   251 
   252     def register_and_replace(self, obj, replaced, registryname=None):
   252     def register_and_replace(self, obj, replaced, registryname=None):
   253         if hasattr(replaced, 'classid'):
   253         if hasattr(replaced, 'classid'):
   254             replaced = replaced.classid()
   254             replaced = replaced.classid()
   255         registryname = registryname or obj.__registry__
   255         registryname = registryname or obj.__registry__
   256         registry = self.registry(registryname)
   256         registry = self.registry(registryname)
   260                 del registry[obj.id][index]
   260                 del registry[obj.id][index]
   261                 break
   261                 break
   262         self.register(obj, registryname=registryname)
   262         self.register(obj, registryname=registryname)
   263 
   263 
   264     # dynamic selection methods ###############################################
   264     # dynamic selection methods ###############################################
   265     
   265 
   266     def select(self, vobjects, *args, **kwargs):
   266     def select(self, vobjects, *args, **kwargs):
   267         """return an instance of the most specific object according
   267         """return an instance of the most specific object according
   268         to parameters
   268         to parameters
   269 
   269 
   270         raise NoSelectableObject if not object apply
   270         raise NoSelectableObject if not object apply
   289                                 % (args, kwargs.keys(),
   289                                 % (args, kwargs.keys(),
   290                                    [repr(v) for v in winners]))
   290                                    [repr(v) for v in winners]))
   291         winner = winners[0]
   291         winner = winners[0]
   292         # return the result of the .selected method of the vobject
   292         # return the result of the .selected method of the vobject
   293         return winner.selected(*args, **kwargs)
   293         return winner.selected(*args, **kwargs)
   294     
   294 
   295     def possible_objects(self, registry, *args, **kwargs):
   295     def possible_objects(self, registry, *args, **kwargs):
   296         """return an iterator on possible objects in a registry for this result set
   296         """return an iterator on possible objects in a registry for this result set
   297 
   297 
   298         actions returned are classes, not instances
   298         actions returned are classes, not instances
   299         """
   299         """
   304                 continue
   304                 continue
   305 
   305 
   306     def select_object(self, registry, cid, *args, **kwargs):
   306     def select_object(self, registry, cid, *args, **kwargs):
   307         """return the most specific component according to the resultset"""
   307         """return the most specific component according to the resultset"""
   308         return self.select(self.registry_objects(registry, cid), *args, **kwargs)
   308         return self.select(self.registry_objects(registry, cid), *args, **kwargs)
   309     
   309 
   310     # intialization methods ###################################################
   310     # intialization methods ###################################################
   311     
   311 
   312     def init_registration(self, path):
   312     def init_registration(self, path):
   313         # compute list of all modules that have to be loaded
   313         # compute list of all modules that have to be loaded
   314         self._toloadmods, filemods = _toload_info(path)
   314         self._toloadmods, filemods = _toload_info(path)
   315         self._loadedmods = {}
   315         self._loadedmods = {}
   316         return filemods
   316         return filemods
   317     
   317 
   318     def register_objects(self, path, force_reload=None):
   318     def register_objects(self, path, force_reload=None):
   319         if force_reload is None:
   319         if force_reload is None:
   320             force_reload = self.config.mode == 'dev'
   320             force_reload = self.config.mode == 'dev'
   321         elif not force_reload:
   321         elif not force_reload:
   322             # force_reload == False usually mean modules have been reloaded
   322             # force_reload == False usually mean modules have been reloaded
   339         change = False
   339         change = False
   340         for filepath, modname in filemods:
   340         for filepath, modname in filemods:
   341             if self.load_file(filepath, modname, force_reload):
   341             if self.load_file(filepath, modname, force_reload):
   342                 change = True
   342                 change = True
   343         return change
   343         return change
   344     
   344 
   345     def load_file(self, filepath, modname, force_reload=False):
   345     def load_file(self, filepath, modname, force_reload=False):
   346         """load visual objects from a python file"""
   346         """load visual objects from a python file"""
   347         from logilab.common.modutils import load_module_from_name
   347         from logilab.common.modutils import load_module_from_name
   348         if modname in self._loadedmods:
   348         if modname in self._loadedmods:
   349             return
   349             return
   366             unregistered = None
   366             unregistered = None
   367         # load the module
   367         # load the module
   368         module = load_module_from_name(modname, use_sys=not force_reload)
   368         module = load_module_from_name(modname, use_sys=not force_reload)
   369         self.load_module(module)
   369         self.load_module(module)
   370         # if something was unregistered, we need to update places where it was
   370         # if something was unregistered, we need to update places where it was
   371         # referenced 
   371         # referenced
   372         if unregistered:
   372         if unregistered:
   373             # oldnew_mapping = {}
   373             # oldnew_mapping = {}
   374             registered = self._loadedmods[modname]
   374             registered = self._loadedmods[modname]
   375             oldnew_mapping = dict((unregistered[name], registered[name])
   375             oldnew_mapping = dict((unregistered[name], registered[name])
   376                                   for name in unregistered if name in registered)
   376                                   for name in unregistered if name in registered)
   386             for objname, obj in vars(module).items():
   386             for objname, obj in vars(module).items():
   387                 if objname.startswith('_'):
   387                 if objname.startswith('_'):
   388                     continue
   388                     continue
   389                 self._load_ancestors_then_object(module.__name__, obj)
   389                 self._load_ancestors_then_object(module.__name__, obj)
   390         self.debug('loaded %s', module)
   390         self.debug('loaded %s', module)
   391     
   391 
   392     def _load_ancestors_then_object(self, modname, obj):
   392     def _load_ancestors_then_object(self, modname, obj):
   393         # imported classes
   393         # imported classes
   394         objmodname = getattr(obj, '__module__', None)
   394         objmodname = getattr(obj, '__module__', None)
   395         if objmodname != modname:
   395         if objmodname != modname:
   396             if objmodname in self._toloadmods:
   396             if objmodname in self._toloadmods:
   407             return
   407             return
   408         self._loadedmods[modname][objname] = obj
   408         self._loadedmods[modname][objname] = obj
   409         for parent in obj.__bases__:
   409         for parent in obj.__bases__:
   410             self._load_ancestors_then_object(modname, parent)
   410             self._load_ancestors_then_object(modname, parent)
   411         self.load_object(obj)
   411         self.load_object(obj)
   412             
   412 
   413     def load_object(self, obj):
   413     def load_object(self, obj):
   414         try:
   414         try:
   415             self.register_vobject_class(obj)
   415             self.register_vobject_class(obj)
   416         except Exception, ex:
   416         except Exception, ex:
   417             if self.config.mode in ('test', 'dev'):
   417             if self.config.mode in ('test', 'dev'):
   418                 raise
   418                 raise
   419             self.exception('vobject %s registration failed: %s', obj, ex)
   419             self.exception('vobject %s registration failed: %s', obj, ex)
   420         
   420 
   421     # old automatic registration XXX deprecated ###############################
   421     # old automatic registration XXX deprecated ###############################
   422     
   422 
   423     def register_vobject_class(self, cls):
   423     def register_vobject_class(self, cls):
   424         """handle vobject class registration
   424         """handle vobject class registration
   425         
   425 
   426         vobject class with __abstract__ == True in their local dictionnary or
   426         vobject class with __abstract__ == True in their local dictionnary or
   427         with a name starting starting by an underscore are not registered.
   427         with a name starting starting by an underscore are not registered.
   428         Also a vobject class needs to have __registry__ and id attributes set
   428         Also a vobject class needs to have __registry__ and id attributes set
   429         to a non empty string to be registered.
   429         to a non empty string to be registered.
   430         """
   430         """
   433             return
   433             return
   434         regname = cls.__registry__
   434         regname = cls.__registry__
   435         if '%s.%s' % (regname, cls.id) in self.config['disable-appobjects']:
   435         if '%s.%s' % (regname, cls.id) in self.config['disable-appobjects']:
   436             return
   436             return
   437         self.register(cls)
   437         self.register(cls)
   438             
   438 
   439     def unregister_module_vobjects(self, modname):
   439     def unregister_module_vobjects(self, modname):
   440         """removes registered objects coming from a given module
   440         """removes registered objects coming from a given module
   441 
   441 
   442         returns a dictionnary classid/class of all classes that will need
   442         returns a dictionnary classid/class of all classes that will need
   443         to be updated after reload (i.e. vobjects referencing classes defined
   443         to be updated after reload (i.e. vobjects referencing classes defined
   495                     # update obj's baseclasses tuple (__bases__) if needed
   495                     # update obj's baseclasses tuple (__bases__) if needed
   496                     if newbases != obj.__bases__:
   496                     if newbases != obj.__bases__:
   497                         self.debug('updating %s.%s base classes',
   497                         self.debug('updating %s.%s base classes',
   498                                   obj.__module__, obj.__name__)
   498                                   obj.__module__, obj.__name__)
   499                         obj.__bases__ = newbases
   499                         obj.__bases__ = newbases
   500         
   500 
   501 # init logging 
   501 # init logging
   502 set_log_methods(VObject, getLogger('cubicweb'))
   502 set_log_methods(VObject, getLogger('cubicweb'))
   503 set_log_methods(VRegistry, getLogger('cubicweb.registry'))
   503 set_log_methods(VRegistry, getLogger('cubicweb.registry'))
   504 
   504 
   505 
   505 
   506 # selector base classes and operations ########################################
   506 # selector base classes and operations ########################################
   535             return self
   535             return self
   536         return None
   536         return None
   537 
   537 
   538     def __str__(self):
   538     def __str__(self):
   539         return self.__class__.__name__
   539         return self.__class__.__name__
   540     
   540 
   541     def __and__(self, other):
   541     def __and__(self, other):
   542         return AndSelector(self, other)
   542         return AndSelector(self, other)
   543     def __rand__(self, other):
   543     def __rand__(self, other):
   544         return AndSelector(other, self)
   544         return AndSelector(other, self)
   545 
   545 
   548     def __ror__(self, other):
   548     def __ror__(self, other):
   549         return OrSelector(other, self)
   549         return OrSelector(other, self)
   550 
   550 
   551     def __invert__(self):
   551     def __invert__(self):
   552         return NotSelector(self)
   552         return NotSelector(self)
   553     
   553 
   554     # XXX (function | function) or (function & function) not managed yet
   554     # XXX (function | function) or (function & function) not managed yet
   555 
   555 
   556     def __call__(self, cls, *args, **kwargs):
   556     def __call__(self, cls, *args, **kwargs):
   557         return NotImplementedError("selector %s must implement its logic "
   557         return NotImplementedError("selector %s must implement its logic "
   558                                    "in its __call__ method" % self.__class__)
   558                                    "in its __call__ method" % self.__class__)
   559 
   559 
   560 class MultiSelector(Selector):
   560 class MultiSelector(Selector):
   561     """base class for compound selector classes"""
   561     """base class for compound selector classes"""
   562     
   562 
   563     def __init__(self, *selectors):
   563     def __init__(self, *selectors):
   564         self.selectors = self.merge_selectors(selectors)
   564         self.selectors = self.merge_selectors(selectors)
   565 
   565 
   566     def __str__(self):
   566     def __str__(self):
   567         return '%s(%s)' % (self.__class__.__name__,
   567         return '%s(%s)' % (self.__class__.__name__,
   598             found = childselector.search_selector(selector)
   598             found = childselector.search_selector(selector)
   599             if found is not None:
   599             if found is not None:
   600                 return found
   600                 return found
   601         return None
   601         return None
   602 
   602 
   603     
   603 
   604 def objectify_selector(selector_func):
   604 def objectify_selector(selector_func):
   605     """convenience decorator for simple selectors where a class definition
   605     """convenience decorator for simple selectors where a class definition
   606     would be overkill::
   606     would be overkill::
   607 
   607 
   608         @objectify_selector
   608         @objectify_selector
   609         def yes(cls, *args, **kwargs):
   609         def yes(cls, *args, **kwargs):
   610             return 1
   610             return 1
   611         
   611 
   612     """
   612     """
   613     return type(selector_func.__name__, (Selector,),
   613     return type(selector_func.__name__, (Selector,),
   614                 {'__call__': lambda self, *args, **kwargs: selector_func(*args, **kwargs)})
   614                 {'__call__': lambda self, *args, **kwargs: selector_func(*args, **kwargs)})
   615 
   615 
   616 def _instantiate_selector(selector):
   616 def _instantiate_selector(selector):
   617     """ensures `selector` is a `Selector` instance
   617     """ensures `selector` is a `Selector` instance
   618     
   618 
   619     NOTE: This should only be used locally in build___select__()
   619     NOTE: This should only be used locally in build___select__()
   620     XXX: then, why not do it ??
   620     XXX: then, why not do it ??
   621     """
   621     """
   622     if isinstance(selector, types.FunctionType):
   622     if isinstance(selector, types.FunctionType):
   623         return objectify_selector(selector)()
   623         return objectify_selector(selector)()