# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1239269413 -7200 # Node ID 99dfced5673e606449f6a86268386dbce4f5c474 # Parent a4eb20f86cb018657e5fac2e3f1f4f8ae098a53d fix vobjects registration to deal with objects inter-dependancy diff -r a4eb20f86cb0 -r 99dfced5673e vregistry.py --- a/vregistry.py Wed Apr 08 20:38:34 2009 +0200 +++ b/vregistry.py Thu Apr 09 11:30:13 2009 +0200 @@ -27,7 +27,7 @@ import sys from os import listdir, stat -from os.path import dirname, join, realpath, split, isdir +from os.path import dirname, join, realpath, split, isdir, exists from logging import getLogger import types @@ -35,6 +35,23 @@ from cubicweb import RegistryNotFound, ObjectNotFound, NoSelectableObject +def _toload_info(path, _toload=None): + """return a dictionary of : and an ordered list of + (file, module name) to load + """ + from logilab.common.modutils import modpath_from_file + if _toload is None: + _toload = {}, [] + for fileordir in path: + if isdir(fileordir) and exists(join(fileordir, '__init__.py')): + subfiles = [join(fileordir, fname) for fname in listdir(fileordir)] + _toload_info(subfiles, _toload) + elif fileordir[-3:] == '.py': + modname = '.'.join(modpath_from_file(fileordir)) + _toload[0][modname] = fileordir + _toload[1].append((fileordir, modname)) + return _toload + class registerer(object): """do whatever is needed at registration time for the wrapped @@ -239,7 +256,7 @@ # 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(obj, self) - assert not vobject in vobjects + assert not vobject in vobjects, vobject assert callable(vobject.__select__), vobject vobjects.append(vobject) try: @@ -249,7 +266,7 @@ 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 + self._loadedmods[obj.__module__]['%s.%s' % (obj.__module__, oid)] = obj def unregister(self, obj, registryname=None): registryname = registryname or obj.__registry__ @@ -352,87 +369,70 @@ if webdir in sys.path: sys.path.remove(webdir) if CW_SOFTWARE_ROOT in sys.path: - sys.path.remove(CW_SOFTWARE_ROOT) + sys.path.remove(CW_SOFTWARE_ROOT) + # compute list of all modules that have to be loaded + self._toloadmods, filemods = _toload_info(path) + self._loadedmods = {} # load views from each directory in the application's path change = False - for fileordirectory in path: - if isdir(fileordirectory): - if self.read_directory(fileordirectory, force_reload): - change = True - else: - directory, filename = split(fileordirectory) - if self.load_file(directory, filename, force_reload): - change = True + for filepath, modname in filemods: + if self.load_file(filepath, modname, force_reload): + change = True return change - - def read_directory(self, directory, force_reload=False): - """read a directory and register available views""" - modified_on = stat(realpath(directory))[-2] - # only read directory if it was modified - _lastmodifs = self._lastmodifs - if directory in _lastmodifs and modified_on <= _lastmodifs[directory]: + + def load_file(self, filepath, modname, force_reload=False): + """load visual objects from a python file""" + from logilab.common.modutils import load_module_from_name + if modname in self._loadedmods: + return + self._loadedmods[modname] = {} + try: + modified_on = stat(filepath)[-2] + except OSError: + # this typically happens on emacs backup files (.#foo.py) + self.warning('Unable to load %s. It is likely to be a backup file', + filepath) return False - self.info('loading directory %s', directory) - for filename in listdir(directory): - if filename[-3:] == '.py': - try: - self.load_file(directory, filename, force_reload) - except OSError: - # this typically happens on emacs backup files (.#foo.py) - self.warning('Unable to load file %s. It is likely to be a backup file', - filename) - except Exception, ex: - if self.config.mode in ('dev', 'test'): - raise - self.exception('%r while loading file %s', ex, filename) - _lastmodifs[directory] = modified_on - return True - - def load_file(self, directory, filename, force_reload=False): - """load visual objects from a python file""" - from logilab.common.modutils import load_module_from_modpath, modpath_from_file - filepath = join(directory, filename) - modified_on = stat(filepath)[-2] - modpath = modpath_from_file(join(directory, filename)) - modname = '.'.join(modpath) - unregistered = {} - _lastmodifs = self._lastmodifs - if filepath in _lastmodifs: + if filepath in self._lastmodifs: # only load file if it was modified - if modified_on <= _lastmodifs[filepath]: + if modified_on <= self._lastmodifs[filepath]: return - else: - # if it was modified, unregister all exisiting objects - # from this module, and keep track of what was unregistered - unregistered = self.unregister_module_vobjects(modname) + # if it was modified, unregister all exisiting objects + # from this module, and keep track of what was unregistered + unregistered = self.unregister_module_vobjects(modname) + else: + unregistered = None # load the module - module = load_module_from_modpath(modpath, use_sys=not force_reload) - registered = self.load_module(module) + module = load_module_from_name(modname, use_sys=not force_reload) + self.load_module(module) # if something was unregistered, we need to update places where it was # referenced if unregistered: # oldnew_mapping = {} + registered = self._loadedmods[modname] oldnew_mapping = dict((unregistered[name], registered[name]) for name in unregistered if name in registered) self.update_registered_subclasses(oldnew_mapping) - _lastmodifs[filepath] = modified_on + self._lastmodifs[filepath] = modified_on return True def load_module(self, module): - self._registered = {} + self.info('loading %s', module) if hasattr(module, 'registration_callback'): module.registration_callback(self) else: - self.info('loading %s', module) for objname, obj in vars(module).items(): if objname.startswith('_'): continue - self.load_ancestors_then_object(module.__name__, obj) - return self._registered + self._load_ancestors_then_object(module.__name__, obj) + self.debug('loaded %s', module) - def load_ancestors_then_object(self, modname, obj): - # skip imported classes - if getattr(obj, '__module__', None) != modname: + def _load_ancestors_then_object(self, modname, obj): + # imported classes + objmodname = getattr(obj, '__module__', None) + if objmodname != modname: + if objmodname in self._toloadmods: + self.load_file(self._toloadmods[objmodname], objmodname) return # skip non registerable object try: @@ -441,11 +441,11 @@ except TypeError: return objname = '%s.%s' % (modname, obj.__name__) - if objname in self._registered: + if objname in self._loadedmods[modname]: return - self._registered[objname] = obj + self._loadedmods[modname][objname] = obj for parent in obj.__bases__: - self.load_ancestors_then_object(modname, parent) + self._load_ancestors_then_object(modname, parent) self.load_object(obj) def load_object(self, obj):