vregistry.py
branchtls-sprint
changeset 665 1305da1ce3f9
parent 660 5233a9457f6b
child 668 61b2328f9ed9
equal deleted inserted replaced
664:04154a2b238d 665:1305da1ce3f9
   203         return self._registries.values()
   203         return self._registries.values()
   204 
   204 
   205     def __contains__(self, key):
   205     def __contains__(self, key):
   206         return key in self._registries
   206         return key in self._registries
   207 
   207 
   208 
       
   209     ##########
       
   210     def register(self, obj, registryname=None):
       
   211         registryname = registryname or obj.__registry__
       
   212         registry = self._registries.setdefault(registryname, {})
       
   213         registry.setdefault(obj.id, []).append(obj)
       
   214         # XXX automatic reloading management
       
   215         self._registered['%s.%s' % (obj.__module__, obj.id)] = obj
       
   216 
       
   217     def register_if_interface_found(self, obj, registryname, iface):
       
   218         registry = self._registries.setdefault(registryname, {})
       
   219         for etype in self.registry_object('etypes'):
       
   220             if implements(etype, iface):
       
   221                 registry.setdefault(obj.id, []).append(obj)
       
   222                 # XXX automatic reloading management
       
   223                 self._registered['%s.%s' % (obj.__module__, obj.id)] = obj
       
   224                 break
       
   225 
       
   226     def unregister(self, obj, registryname=None):
       
   227         registryname = registryname or obj.__registry__
       
   228         registry = self.registry(registryname)
       
   229         removed_id = obj.classid()
       
   230         for registered in registry[obj.id]:
       
   231             # use classid() to compare classes because vreg will probably
       
   232             # have its own version of the class, loaded through execfile
       
   233             if registered.classid() == removed_id:
       
   234                 # XXX automatic reloading management
       
   235                 registry[obj.id].remove(registered)
       
   236                 break
       
   237     
       
   238     def register_and_replace(self, obj, replaced, registryname=None):
       
   239         registryname = registryname or obj.__registry__
       
   240         registry = self.registry(registryname)
       
   241         registered_objs = registry[obj.id]
       
   242         for index, registered in enumerate(registered_objs):
       
   243             if registered.classid() == replaced:
       
   244                 registry[obj.id][index] = obj
       
   245                 self._registered['%s.%s' % (obj.__module__, obj.id)] = obj
       
   246     ##########
       
   247     
       
   248     def register_vobject_class(self, cls, _kicked=set()):
       
   249         """handle vobject class registration
       
   250         
       
   251         vobject class with __abstract__ == True in their local dictionnary or
       
   252         with a name starting starting by an underscore are not registered.
       
   253         Also a vobject class needs to have __registry__ and id attributes set
       
   254         to a non empty string to be registered.
       
   255 
       
   256         Registration is actually handled by vobject's registerer.
       
   257         """
       
   258         if (cls.__dict__.get('__abstract__') or cls.__name__[0] == '_'
       
   259             or not cls.__registry__ or not cls.id):
       
   260             return
       
   261         # while reloading a module :
       
   262         # if cls was previously kicked, it means that there is a more specific
       
   263         # vobject defined elsewhere re-registering cls would kick it out
       
   264         if cls.classid() in _kicked:
       
   265             self.debug('not re-registering %s because it was previously kicked',
       
   266                       cls.classid())
       
   267         else:
       
   268             regname = cls.__registry__
       
   269             if cls.id in self.config['disable-%s' % regname]:
       
   270                 return
       
   271             registry = self._registries.setdefault(regname, {})
       
   272             vobjects = registry.setdefault(cls.id, [])
       
   273             registerer = cls.__registerer__(self, cls)
       
   274             cls = registerer.do_it_yourself(vobjects)
       
   275             #_kicked |= registerer.kicked
       
   276             if cls:
       
   277                 # registered() is technically a classmethod but is not declared
       
   278                 # as such because we need to compose registered in some cases
       
   279                 vobject = cls.registered.im_func(cls, self)
       
   280                 try:
       
   281                     vname = vobject.__name__
       
   282                 except AttributeError:
       
   283                     vname = vobject.__class__.__name__
       
   284                 self.debug('registered vobject %s in registry %s with id %s',
       
   285                           vname, cls.__registry__, cls.id)
       
   286                 vobjects.append(vobject)
       
   287             
       
   288     def unregister_module_vobjects(self, modname):
       
   289         """removes registered objects coming from a given module
       
   290 
       
   291         returns a dictionnary classid/class of all classes that will need
       
   292         to be updated after reload (i.e. vobjects referencing classes defined
       
   293         in the <modname> module)
       
   294         """
       
   295         unregistered = {}
       
   296         # browse each registered object
       
   297         for registry, objdict in self.items():
       
   298             for oid, objects in objdict.items():
       
   299                 for obj in objects[:]:
       
   300                     objname = obj.classid()
       
   301                     # if the vobject is defined in this module, remove it
       
   302                     if objname.startswith(modname):
       
   303                         unregistered[objname] = obj
       
   304                         objects.remove(obj)
       
   305                         self.debug('unregistering %s in %s registry',
       
   306                                   objname, registry)
       
   307                     # if not, check if the vobject can be found in baseclasses
       
   308                     # (because we also want subclasses to be updated)
       
   309                     else:
       
   310                         if not isinstance(obj, type):
       
   311                             obj = obj.__class__
       
   312                         for baseclass in obj.__bases__:
       
   313                             if hasattr(baseclass, 'classid'):
       
   314                                 baseclassid = baseclass.classid()
       
   315                                 if baseclassid.startswith(modname):
       
   316                                     unregistered[baseclassid] = baseclass
       
   317                 # update oid entry
       
   318                 if objects:
       
   319                     objdict[oid] = objects
       
   320                 else:
       
   321                     del objdict[oid]
       
   322         return unregistered
       
   323 
       
   324 
       
   325     def update_registered_subclasses(self, oldnew_mapping):
       
   326         """updates subclasses of re-registered vobjects
       
   327 
       
   328         if baseviews.PrimaryView is changed, baseviews.py will be reloaded
       
   329         automatically and the new version of PrimaryView will be registered.
       
   330         But all existing subclasses must also be notified of this change, and
       
   331         that's what this method does
       
   332 
       
   333         :param oldnew_mapping: a dict mapping old version of a class to
       
   334                                the new version
       
   335         """
       
   336         # browse each registered object
       
   337         for objdict in self.values():
       
   338             for objects in objdict.values():
       
   339                 for obj in objects:
       
   340                     if not isinstance(obj, type):
       
   341                         obj = obj.__class__
       
   342                     # build new baseclasses tuple
       
   343                     newbases = tuple(oldnew_mapping.get(baseclass, baseclass)
       
   344                                      for baseclass in obj.__bases__)
       
   345                     # update obj's baseclasses tuple (__bases__) if needed
       
   346                     if newbases != obj.__bases__:
       
   347                         self.debug('updating %s.%s base classes',
       
   348                                   obj.__module__, obj.__name__)
       
   349                         obj.__bases__ = newbases
       
   350 
       
   351     def registry(self, name):
   208     def registry(self, name):
   352         """return the registry (dictionary of class objects) associated to
   209         """return the registry (dictionary of class objects) associated to
   353         this name
   210         this name
   354         """
   211         """
   355         try:
   212         try:
   370         else:
   227         else:
   371             result = []
   228             result = []
   372             for objs in registry.values():
   229             for objs in registry.values():
   373                 result += objs
   230                 result += objs
   374             return result
   231             return result
   375         
   232 
       
   233     def object_by_id(self, registry, cid, *args, **kwargs):
       
   234         """return the most specific component according to the resultset"""
       
   235         objects = self[registry][cid]
       
   236         assert len(objects) == 1, objects
       
   237         return objects[0].selected(*args, **kwargs)
       
   238 
       
   239     # methods for explicit (un)registration ###################################
       
   240     
       
   241     def register(self, obj, registryname=None, oid=None):
       
   242         """base method to add an object in the registry"""
       
   243         registryname = registryname or obj.__registry__
       
   244         oid = oid or obj.id
       
   245         registry = self._registries.setdefault(registryname, {})
       
   246         vobjects = registry.setdefault(oid, [])
       
   247         # registered() is technically a classmethod but is not declared
       
   248         # as such because we need to compose registered in some cases
       
   249         vobject = obj.registered.im_func(cls, self)
       
   250         assert not vobject in vobjects
       
   251         vobjects.append(vobject)
       
   252         try:
       
   253             vname = vobject.__name__
       
   254         except AttributeError:
       
   255             vname = vobject.__class__.__name__
       
   256         self.debug('registered vobject %s in registry %s with id %s',
       
   257                    vname, registryname, oid)
       
   258         # automatic reloading management
       
   259         self._registered['%s.%s' % (obj.__module__, oid)] = obj
       
   260 
       
   261     def unregister(self, obj, registryname=None):
       
   262         registryname = registryname or obj.__registry__
       
   263         registry = self.registry(registryname)
       
   264         removed_id = obj.classid()
       
   265         for registered in registry[obj.id]:
       
   266             # use classid() to compare classes because vreg will probably
       
   267             # have its own version of the class, loaded through execfile
       
   268             if registered.classid() == removed_id:
       
   269                 # XXX automatic reloading management
       
   270                 try:
       
   271                     registry[obj.id].remove(registered)
       
   272                 except ValueError:
       
   273                     self.warning('can\'t remove %s, no id %s in the %s registry',
       
   274                                  removed_id, obj.id, registryname)
       
   275                 except ValueError:
       
   276                     self.warning('can\'t remove %s, not in the %s registry with id %s',
       
   277                                  removed_id, registryname, obj.id)
       
   278 #                 else:
       
   279 #                     # if objects is empty, remove oid from registry
       
   280 #                     if not registry[obj.id]:
       
   281 #                         del regcontent[oid]                    
       
   282                 break
       
   283     
       
   284     def register_and_replace(self, obj, replaced, registryname=None):
       
   285         registryname = registryname or obj.__registry__
       
   286         registry = self.registry(registryname)
       
   287         registered_objs = registry[obj.id]
       
   288         for index, registered in enumerate(registered_objs):
       
   289             if registered.classid() == replaced:
       
   290                 registry[obj.id][index] = obj
       
   291                 self._registered['%s.%s' % (obj.__module__, obj.id)] = obj                
       
   292 
       
   293     # dynamic selection methods ###############################################
       
   294     
   376     def select(self, vobjects, *args, **kwargs):
   295     def select(self, vobjects, *args, **kwargs):
   377         """return an instance of the most specific object according
   296         """return an instance of the most specific object according
   378         to parameters
   297         to parameters
   379 
   298 
   380         raise NoSelectableObject if not object apply
   299         raise NoSelectableObject if not object apply
   412                 continue
   331                 continue
   413 
   332 
   414     def select_object(self, registry, cid, *args, **kwargs):
   333     def select_object(self, registry, cid, *args, **kwargs):
   415         """return the most specific component according to the resultset"""
   334         """return the most specific component according to the resultset"""
   416         return self.select(self.registry_objects(registry, cid), *args, **kwargs)
   335         return self.select(self.registry_objects(registry, cid), *args, **kwargs)
   417 
       
   418     def object_by_id(self, registry, cid, *args, **kwargs):
       
   419         """return the most specific component according to the resultset"""
       
   420         objects = self[registry][cid]
       
   421         assert len(objects) == 1, objects
       
   422         return objects[0].selected(*args, **kwargs)
       
   423     
   336     
   424     # intialization methods ###################################################
   337     # intialization methods ###################################################
   425 
       
   426     
   338     
   427     def register_objects(self, path, force_reload=None):
   339     def register_objects(self, path, force_reload=None):
   428         if force_reload is None:
   340         if force_reload is None:
   429             force_reload = self.config.mode == 'dev'
   341             force_reload = self.config.mode == 'dev'
   430         elif not force_reload:
   342         elif not force_reload:
   511             self.update_registered_subclasses(oldnew_mapping)
   423             self.update_registered_subclasses(oldnew_mapping)
   512         _lastmodifs[filepath] = modified_on
   424         _lastmodifs[filepath] = modified_on
   513         return True
   425         return True
   514 
   426 
   515     def load_module(self, module):
   427     def load_module(self, module):
       
   428         self._registered = {}
   516         if hasattr(module, 'cw_register_objects'):
   429         if hasattr(module, 'cw_register_objects'):
   517             self._registered = {}
       
   518             module.cw_register_objects(self)
   430             module.cw_register_objects(self)
   519             registered = self._resigtered
       
   520             del self._registered
       
   521             return registered
       
   522         else:
   431         else:
   523             registered = {}
       
   524             self.info('loading %s', module)
   432             self.info('loading %s', module)
   525             for objname, obj in vars(module).items():
   433             for objname, obj in vars(module).items():
   526                 if objname.startswith('_'):
   434                 if objname.startswith('_'):
   527                     continue
   435                     continue
   528                 self.load_ancestors_then_object(module.__name__, registered, obj)
   436                 self.load_ancestors_then_object(module.__name__, obj)
   529             return registered
   437         return self._registered
   530     
   438     
   531     def load_ancestors_then_object(self, modname, registered, obj):
   439     def load_ancestors_then_object(self, modname, obj):
   532         # skip imported classes
   440         # skip imported classes
   533         if getattr(obj, '__module__', None) != modname:
   441         if getattr(obj, '__module__', None) != modname:
   534             return
   442             return
   535         # skip non registerable object
   443         # skip non registerable object
   536         try:
   444         try:
   537             if not issubclass(obj, VObject):
   445             if not issubclass(obj, VObject):
   538                 return
   446                 return
   539         except TypeError:
   447         except TypeError:
   540             return
   448             return
   541         objname = '%s.%s' % (modname, obj.__name__)
   449         objname = '%s.%s' % (modname, obj.__name__)
   542         if objname in registered:
   450         if objname in self._registered:
   543             return
   451             return
   544         registered[objname] = obj
   452         self._registered[objname] = obj
   545         for parent in obj.__bases__:
   453         for parent in obj.__bases__:
   546             self.load_ancestors_then_object(modname, registered, parent)
   454             self.load_ancestors_then_object(modname, parent)
   547         self.load_object(obj)
   455         self.load_object(obj)
   548             
   456             
   549     def load_object(self, obj):
   457     def load_object(self, obj):
   550         try:
   458         try:
   551             self.register_vobject_class(obj)
   459             self.register_vobject_class(obj)
   552         except Exception, ex:
   460         except Exception, ex:
   553             if self.config.mode in ('test', 'dev'):
   461             if self.config.mode in ('test', 'dev'):
   554                 raise
   462                 raise
   555             self.exception('vobject %s registration failed: %s', obj, ex)
   463             self.exception('vobject %s registration failed: %s', obj, ex)
       
   464         
       
   465     # old automatic registration XXX deprecated ###############################
       
   466     
       
   467     def register_vobject_class(self, cls):
       
   468         """handle vobject class registration
       
   469         
       
   470         vobject class with __abstract__ == True in their local dictionnary or
       
   471         with a name starting starting by an underscore are not registered.
       
   472         Also a vobject class needs to have __registry__ and id attributes set
       
   473         to a non empty string to be registered.
       
   474 
       
   475         Registration is actually handled by vobject's registerer.
       
   476         """
       
   477         if (cls.__dict__.get('__abstract__') or cls.__name__[0] == '_'
       
   478             or not cls.__registry__ or not cls.id):
       
   479             return
       
   480         regname = cls.__registry__
       
   481         if cls.id in self.config['disable-%s' % regname]:
       
   482             return
       
   483         registry = self._registries.setdefault(regname, {})
       
   484         vobjects = registry.setdefault(cls.id, [])
       
   485         registerer = cls.__registerer__(self, cls)
       
   486         cls = registerer.do_it_yourself(vobjects)
       
   487         if cls:
       
   488             self.register(cls)
       
   489             
       
   490     def unregister_module_vobjects(self, modname):
       
   491         """removes registered objects coming from a given module
       
   492 
       
   493         returns a dictionnary classid/class of all classes that will need
       
   494         to be updated after reload (i.e. vobjects referencing classes defined
       
   495         in the <modname> module)
       
   496         """
       
   497         unregistered = {}
       
   498         # browse each registered object
       
   499         for registry, objdict in self.items():
       
   500             for oid, objects in objdict.items():
       
   501                 for obj in objects[:]:
       
   502                     objname = obj.classid()
       
   503                     # if the vobject is defined in this module, remove it
       
   504                     if objname.startswith(modname):
       
   505                         unregistered[objname] = obj
       
   506                         objects.remove(obj)
       
   507                         self.debug('unregistering %s in %s registry',
       
   508                                   objname, registry)
       
   509                     # if not, check if the vobject can be found in baseclasses
       
   510                     # (because we also want subclasses to be updated)
       
   511                     else:
       
   512                         if not isinstance(obj, type):
       
   513                             obj = obj.__class__
       
   514                         for baseclass in obj.__bases__:
       
   515                             if hasattr(baseclass, 'classid'):
       
   516                                 baseclassid = baseclass.classid()
       
   517                                 if baseclassid.startswith(modname):
       
   518                                     unregistered[baseclassid] = baseclass
       
   519                 # update oid entry
       
   520                 if objects:
       
   521                     objdict[oid] = objects
       
   522                 else:
       
   523                     del objdict[oid]
       
   524         return unregistered
       
   525 
       
   526     def update_registered_subclasses(self, oldnew_mapping):
       
   527         """updates subclasses of re-registered vobjects
       
   528 
       
   529         if baseviews.PrimaryView is changed, baseviews.py will be reloaded
       
   530         automatically and the new version of PrimaryView will be registered.
       
   531         But all existing subclasses must also be notified of this change, and
       
   532         that's what this method does
       
   533 
       
   534         :param oldnew_mapping: a dict mapping old version of a class to
       
   535                                the new version
       
   536         """
       
   537         # browse each registered object
       
   538         for objdict in self.values():
       
   539             for objects in objdict.values():
       
   540                 for obj in objects:
       
   541                     if not isinstance(obj, type):
       
   542                         obj = obj.__class__
       
   543                     # build new baseclasses tuple
       
   544                     newbases = tuple(oldnew_mapping.get(baseclass, baseclass)
       
   545                                      for baseclass in obj.__bases__)
       
   546                     # update obj's baseclasses tuple (__bases__) if needed
       
   547                     if newbases != obj.__bases__:
       
   548                         self.debug('updating %s.%s base classes',
       
   549                                   obj.__module__, obj.__name__)
       
   550                         obj.__bases__ = newbases
   556         
   551         
   557 # init logging 
   552 # init logging 
   558 set_log_methods(VObject, getLogger('cubicweb'))
   553 set_log_methods(VObject, getLogger('cubicweb'))
   559 set_log_methods(VRegistry, getLogger('cubicweb.registry'))
   554 set_log_methods(VRegistry, getLogger('cubicweb.registry'))
   560 set_log_methods(registerer, getLogger('cubicweb.registration'))
   555 set_log_methods(registerer, getLogger('cubicweb.registration'))