# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1234885378 -3600 # Node ID 1305da1ce3f9730eb94de47a4defb4acc73cc8ac # Parent 04154a2b238d9e2f52e96f670c4dc14862bb8e33 reorganize a bit, some cleanup and fixes diff -r 04154a2b238d -r 1305da1ce3f9 vregistry.py --- a/vregistry.py Tue Feb 17 16:41:46 2009 +0100 +++ b/vregistry.py Tue Feb 17 16:42:58 2009 +0100 @@ -205,149 +205,6 @@ def __contains__(self, key): return key in self._registries - - ########## - def register(self, obj, registryname=None): - registryname = registryname or obj.__registry__ - registry = self._registries.setdefault(registryname, {}) - registry.setdefault(obj.id, []).append(obj) - # XXX automatic reloading management - self._registered['%s.%s' % (obj.__module__, obj.id)] = obj - - def register_if_interface_found(self, obj, registryname, iface): - registry = self._registries.setdefault(registryname, {}) - for etype in self.registry_object('etypes'): - if implements(etype, iface): - registry.setdefault(obj.id, []).append(obj) - # XXX automatic reloading management - self._registered['%s.%s' % (obj.__module__, obj.id)] = obj - break - - def unregister(self, obj, registryname=None): - registryname = registryname or obj.__registry__ - registry = self.registry(registryname) - removed_id = obj.classid() - for registered in registry[obj.id]: - # use classid() to compare classes because vreg will probably - # have its own version of the class, loaded through execfile - if registered.classid() == removed_id: - # XXX automatic reloading management - registry[obj.id].remove(registered) - break - - def register_and_replace(self, obj, replaced, registryname=None): - registryname = registryname or obj.__registry__ - registry = self.registry(registryname) - registered_objs = registry[obj.id] - for index, registered in enumerate(registered_objs): - if registered.classid() == replaced: - registry[obj.id][index] = obj - self._registered['%s.%s' % (obj.__module__, obj.id)] = obj - ########## - - def register_vobject_class(self, cls, _kicked=set()): - """handle vobject class registration - - vobject class with __abstract__ == True in their local dictionnary or - with a name starting starting by an underscore are not registered. - Also a vobject class needs to have __registry__ and id attributes set - to a non empty string to be registered. - - Registration is actually handled by vobject's registerer. - """ - if (cls.__dict__.get('__abstract__') or cls.__name__[0] == '_' - or not cls.__registry__ or not cls.id): - return - # while reloading a module : - # if cls was previously kicked, it means that there is a more specific - # vobject defined elsewhere re-registering cls would kick it out - if cls.classid() in _kicked: - self.debug('not re-registering %s because it was previously kicked', - cls.classid()) - else: - regname = cls.__registry__ - if cls.id in self.config['disable-%s' % regname]: - return - registry = self._registries.setdefault(regname, {}) - vobjects = registry.setdefault(cls.id, []) - registerer = cls.__registerer__(self, cls) - cls = registerer.do_it_yourself(vobjects) - #_kicked |= registerer.kicked - if cls: - # registered() is technically a classmethod but is not declared - # as such because we need to compose registered in some cases - vobject = cls.registered.im_func(cls, self) - try: - vname = vobject.__name__ - except AttributeError: - vname = vobject.__class__.__name__ - self.debug('registered vobject %s in registry %s with id %s', - vname, cls.__registry__, cls.id) - vobjects.append(vobject) - - def unregister_module_vobjects(self, modname): - """removes registered objects coming from a given module - - returns a dictionnary classid/class of all classes that will need - to be updated after reload (i.e. vobjects referencing classes defined - in the module) - """ - unregistered = {} - # browse each registered object - for registry, objdict in self.items(): - for oid, objects in objdict.items(): - for obj in objects[:]: - objname = obj.classid() - # if the vobject is defined in this module, remove it - if objname.startswith(modname): - unregistered[objname] = obj - objects.remove(obj) - self.debug('unregistering %s in %s registry', - objname, registry) - # if not, check if the vobject can be found in baseclasses - # (because we also want subclasses to be updated) - else: - if not isinstance(obj, type): - obj = obj.__class__ - for baseclass in obj.__bases__: - if hasattr(baseclass, 'classid'): - baseclassid = baseclass.classid() - if baseclassid.startswith(modname): - unregistered[baseclassid] = baseclass - # update oid entry - if objects: - objdict[oid] = objects - else: - del objdict[oid] - return unregistered - - - def update_registered_subclasses(self, oldnew_mapping): - """updates subclasses of re-registered vobjects - - if baseviews.PrimaryView is changed, baseviews.py will be reloaded - automatically and the new version of PrimaryView will be registered. - But all existing subclasses must also be notified of this change, and - that's what this method does - - :param oldnew_mapping: a dict mapping old version of a class to - the new version - """ - # browse each registered object - for objdict in self.values(): - for objects in objdict.values(): - for obj in objects: - if not isinstance(obj, type): - obj = obj.__class__ - # build new baseclasses tuple - newbases = tuple(oldnew_mapping.get(baseclass, baseclass) - for baseclass in obj.__bases__) - # update obj's baseclasses tuple (__bases__) if needed - if newbases != obj.__bases__: - self.debug('updating %s.%s base classes', - obj.__module__, obj.__name__) - obj.__bases__ = newbases - def registry(self, name): """return the registry (dictionary of class objects) associated to this name @@ -372,7 +229,69 @@ for objs in registry.values(): result += objs return result - + + def object_by_id(self, registry, cid, *args, **kwargs): + """return the most specific component according to the resultset""" + objects = self[registry][cid] + assert len(objects) == 1, objects + return objects[0].selected(*args, **kwargs) + + # methods for explicit (un)registration ################################### + + def register(self, obj, registryname=None, oid=None): + """base method to add an object in the registry""" + registryname = registryname or obj.__registry__ + oid = oid or obj.id + registry = self._registries.setdefault(registryname, {}) + vobjects = registry.setdefault(oid, []) + # registered() is technically a classmethod but is not declared + # as such because we need to compose registered in some cases + vobject = obj.registered.im_func(cls, self) + assert not vobject in vobjects + vobjects.append(vobject) + try: + vname = vobject.__name__ + except AttributeError: + vname = vobject.__class__.__name__ + self.debug('registered vobject %s in registry %s with id %s', + vname, registryname, oid) + # automatic reloading management + self._registered['%s.%s' % (obj.__module__, oid)] = obj + + def unregister(self, obj, registryname=None): + registryname = registryname or obj.__registry__ + registry = self.registry(registryname) + removed_id = obj.classid() + for registered in registry[obj.id]: + # use classid() to compare classes because vreg will probably + # have its own version of the class, loaded through execfile + if registered.classid() == removed_id: + # XXX automatic reloading management + try: + registry[obj.id].remove(registered) + except ValueError: + self.warning('can\'t remove %s, no id %s in the %s registry', + removed_id, obj.id, registryname) + except ValueError: + self.warning('can\'t remove %s, not in the %s registry with id %s', + removed_id, registryname, obj.id) +# else: +# # if objects is empty, remove oid from registry +# if not registry[obj.id]: +# del regcontent[oid] + break + + def register_and_replace(self, obj, replaced, registryname=None): + registryname = registryname or obj.__registry__ + registry = self.registry(registryname) + registered_objs = registry[obj.id] + for index, registered in enumerate(registered_objs): + if registered.classid() == replaced: + registry[obj.id][index] = obj + self._registered['%s.%s' % (obj.__module__, obj.id)] = obj + + # dynamic selection methods ############################################### + def select(self, vobjects, *args, **kwargs): """return an instance of the most specific object according to parameters @@ -414,15 +333,8 @@ def select_object(self, registry, cid, *args, **kwargs): """return the most specific component according to the resultset""" return self.select(self.registry_objects(registry, cid), *args, **kwargs) - - def object_by_id(self, registry, cid, *args, **kwargs): - """return the most specific component according to the resultset""" - objects = self[registry][cid] - assert len(objects) == 1, objects - return objects[0].selected(*args, **kwargs) # intialization methods ################################################### - def register_objects(self, path, force_reload=None): if force_reload is None: @@ -513,22 +425,18 @@ return True def load_module(self, module): + self._registered = {} if hasattr(module, 'cw_register_objects'): - self._registered = {} module.cw_register_objects(self) - registered = self._resigtered - del self._registered - return registered else: - registered = {} self.info('loading %s', module) for objname, obj in vars(module).items(): if objname.startswith('_'): continue - self.load_ancestors_then_object(module.__name__, registered, obj) - return registered + self.load_ancestors_then_object(module.__name__, obj) + return self._registered - def load_ancestors_then_object(self, modname, registered, obj): + def load_ancestors_then_object(self, modname, obj): # skip imported classes if getattr(obj, '__module__', None) != modname: return @@ -539,11 +447,11 @@ except TypeError: return objname = '%s.%s' % (modname, obj.__name__) - if objname in registered: + if objname in self._registered: return - registered[objname] = obj + self._registered[objname] = obj for parent in obj.__bases__: - self.load_ancestors_then_object(modname, registered, parent) + self.load_ancestors_then_object(modname, parent) self.load_object(obj) def load_object(self, obj): @@ -554,6 +462,93 @@ raise self.exception('vobject %s registration failed: %s', obj, ex) + # old automatic registration XXX deprecated ############################### + + def register_vobject_class(self, cls): + """handle vobject class registration + + vobject class with __abstract__ == True in their local dictionnary or + with a name starting starting by an underscore are not registered. + Also a vobject class needs to have __registry__ and id attributes set + to a non empty string to be registered. + + Registration is actually handled by vobject's registerer. + """ + if (cls.__dict__.get('__abstract__') or cls.__name__[0] == '_' + or not cls.__registry__ or not cls.id): + return + regname = cls.__registry__ + if cls.id in self.config['disable-%s' % regname]: + return + registry = self._registries.setdefault(regname, {}) + vobjects = registry.setdefault(cls.id, []) + registerer = cls.__registerer__(self, cls) + cls = registerer.do_it_yourself(vobjects) + if cls: + self.register(cls) + + def unregister_module_vobjects(self, modname): + """removes registered objects coming from a given module + + returns a dictionnary classid/class of all classes that will need + to be updated after reload (i.e. vobjects referencing classes defined + in the module) + """ + unregistered = {} + # browse each registered object + for registry, objdict in self.items(): + for oid, objects in objdict.items(): + for obj in objects[:]: + objname = obj.classid() + # if the vobject is defined in this module, remove it + if objname.startswith(modname): + unregistered[objname] = obj + objects.remove(obj) + self.debug('unregistering %s in %s registry', + objname, registry) + # if not, check if the vobject can be found in baseclasses + # (because we also want subclasses to be updated) + else: + if not isinstance(obj, type): + obj = obj.__class__ + for baseclass in obj.__bases__: + if hasattr(baseclass, 'classid'): + baseclassid = baseclass.classid() + if baseclassid.startswith(modname): + unregistered[baseclassid] = baseclass + # update oid entry + if objects: + objdict[oid] = objects + else: + del objdict[oid] + return unregistered + + def update_registered_subclasses(self, oldnew_mapping): + """updates subclasses of re-registered vobjects + + if baseviews.PrimaryView is changed, baseviews.py will be reloaded + automatically and the new version of PrimaryView will be registered. + But all existing subclasses must also be notified of this change, and + that's what this method does + + :param oldnew_mapping: a dict mapping old version of a class to + the new version + """ + # browse each registered object + for objdict in self.values(): + for objects in objdict.values(): + for obj in objects: + if not isinstance(obj, type): + obj = obj.__class__ + # build new baseclasses tuple + newbases = tuple(oldnew_mapping.get(baseclass, baseclass) + for baseclass in obj.__bases__) + # update obj's baseclasses tuple (__bases__) if needed + if newbases != obj.__bases__: + self.debug('updating %s.%s base classes', + obj.__module__, obj.__name__) + obj.__bases__ = newbases + # init logging set_log_methods(VObject, getLogger('cubicweb')) set_log_methods(VRegistry, getLogger('cubicweb.registry'))