vregistry.py
changeset 5274 16461f675734
parent 5273 c4caef6f09c9
child 5421 8167de96c523
equal deleted inserted replaced
5272:f7d2df59231a 5274:16461f675734
    29 
    29 
    30 from logilab.common.deprecation import deprecated, class_moved
    30 from logilab.common.deprecation import deprecated, class_moved
    31 from logilab.common.logging_ext import set_log_methods
    31 from logilab.common.logging_ext import set_log_methods
    32 
    32 
    33 from cubicweb import CW_SOFTWARE_ROOT
    33 from cubicweb import CW_SOFTWARE_ROOT
    34 from cubicweb import (RegistryNotFound, ObjectNotFound, NoSelectableObject,
    34 from cubicweb import RegistryNotFound, ObjectNotFound, NoSelectableObject
    35                       RegistryOutOfDate)
       
    36 from cubicweb.appobject import AppObject
    35 from cubicweb.appobject import AppObject
    37 
    36 
    38 def _toload_info(path, extrapath, _toload=None):
    37 def _toload_info(path, extrapath, _toload=None):
    39     """return a dictionary of <modname>: <modpath> and an ordered list of
    38     """return a dictionary of <modname>: <modpath> and an ordered list of
    40     (file, module name) to load
    39     (file, module name) to load
   244     """
   243     """
   245 
   244 
   246     def __init__(self, config):
   245     def __init__(self, config):
   247         super(VRegistry, self).__init__()
   246         super(VRegistry, self).__init__()
   248         self.config = config
   247         self.config = config
   249 
   248         # need to clean sys.path this to avoid import confusion pb (i.e.  having
   250     def reset(self, path=None, force_reload=None):
   249         # the same module loaded as 'cubicweb.web.views' subpackage and as
       
   250         # views' or 'web.views' subpackage. This is mainly for testing purpose,
       
   251         # we should'nt need this in production environment
       
   252         for webdir in (join(dirname(realpath(__file__)), 'web'),
       
   253                        join(dirname(__file__), 'web')):
       
   254             if webdir in sys.path:
       
   255                 sys.path.remove(webdir)
       
   256         if CW_SOFTWARE_ROOT in sys.path:
       
   257             sys.path.remove(CW_SOFTWARE_ROOT)
       
   258 
       
   259     def reset(self):
   251         # don't use self.clear, we want to keep existing subdictionaries
   260         # don't use self.clear, we want to keep existing subdictionaries
   252         for subdict in self.itervalues():
   261         for subdict in self.itervalues():
   253             subdict.clear()
   262             subdict.clear()
   254         self._lastmodifs = {}
   263         self._lastmodifs = {}
   255 
   264 
   390         #     to avoid loading same module twice, especially with the
   399         #     to avoid loading same module twice, especially with the
   391         #     _load_ancestors_then_object logic but this needs to be checked
   400         #     _load_ancestors_then_object logic but this needs to be checked
   392         self._loadedmods = {}
   401         self._loadedmods = {}
   393         return filemods
   402         return filemods
   394 
   403 
   395     def register_objects(self, path, force_reload, extrapath=None):
   404     def register_objects(self, path, force_reload=False, extrapath=None):
   396         # need to clean sys.path this to avoid import confusion pb (i.e.
       
   397         # having the same module loaded as 'cubicweb.web.views' subpackage and
       
   398         # as views'  or 'web.views' subpackage
       
   399         # this is mainly for testing purpose, we should'nt need this in
       
   400         # production environment
       
   401         for webdir in (join(dirname(realpath(__file__)), 'web'),
       
   402                        join(dirname(__file__), 'web')):
       
   403             if webdir in sys.path:
       
   404                 sys.path.remove(webdir)
       
   405         if CW_SOFTWARE_ROOT in sys.path:
       
   406             sys.path.remove(CW_SOFTWARE_ROOT)
       
   407         # load views from each directory in the instance's path
   405         # load views from each directory in the instance's path
   408         filemods = self.init_registration(path, extrapath)
   406         filemods = self.init_registration(path, extrapath)
   409         change = False
       
   410         for filepath, modname in filemods:
   407         for filepath, modname in filemods:
   411             if self.load_file(filepath, modname, force_reload):
   408             self.load_file(filepath, modname, force_reload)
   412                 change = True
   409         self.initialization_completed()
   413         if change:
       
   414             self.initialization_completed()
       
   415         return change
       
   416 
   410 
   417     def initialization_completed(self):
   411     def initialization_completed(self):
   418         for regname, reg in self.iteritems():
   412         for regname, reg in self.iteritems():
   419             reg.initialization_completed()
   413             reg.initialization_completed()
       
   414 
       
   415     def _mdate(self, filepath):
       
   416         try:
       
   417             return stat(filepath)[-2]
       
   418         except OSError:
       
   419             # this typically happens on emacs backup files (.#foo.py)
       
   420             self.warning('Unable to load %s. It is likely to be a backup file',
       
   421                          filepath)
       
   422             return None
       
   423 
       
   424     def is_reload_needed(self, path):
       
   425         """return True if something module changed and the registry should be
       
   426         reloaded
       
   427         """
       
   428         lastmodifs = self._lastmodifs
       
   429         for fileordir in path:
       
   430             if isdir(fileordir) and exists(join(fileordir, '__init__.py')):
       
   431                 if self.is_reload_needed([join(fileordir, fname)
       
   432                                           for fname in listdir(fileordir)]):
       
   433                     return True
       
   434             elif fileordir[-3:] == '.py':
       
   435                 mdate = self._mdate(fileordir)
       
   436                 if mdate is None:
       
   437                     continue # backup file, see _mdate implementation
       
   438                 if fileordir not in lastmodifs or lastmodifs[fileordir] < mdate:
       
   439                     self.info('File %s changed since last visit', fileordir)
       
   440                     return True
       
   441         return False
   420 
   442 
   421     def load_file(self, filepath, modname, force_reload=False):
   443     def load_file(self, filepath, modname, force_reload=False):
   422         """load app objects from a python file"""
   444         """load app objects from a python file"""
   423         from logilab.common.modutils import load_module_from_name
   445         from logilab.common.modutils import load_module_from_name
   424         if modname in self._loadedmods:
   446         if modname in self._loadedmods:
   425             return
   447             return
   426         self._loadedmods[modname] = {}
   448         self._loadedmods[modname] = {}
   427         try:
   449         mdate = self._mdate(filepath)
   428             modified_on = stat(filepath)[-2]
   450         if mdate is None:
   429         except OSError:
   451             return # backup file, see _mdate implementation
   430             # this typically happens on emacs backup files (.#foo.py)
       
   431             self.warning('Unable to load %s. It is likely to be a backup file',
       
   432                          filepath)
       
   433             return False
       
   434         if filepath in self._lastmodifs:
       
   435             # only load file if it was modified
       
   436             if modified_on <= self._lastmodifs[filepath]:
       
   437                 return
       
   438             # if it was modified, raise RegistryOutOfDate to reload everything
       
   439             self.info('File %s changed since last visit', filepath)
       
   440             raise RegistryOutOfDate()
       
   441         # set update time before module loading, else we get some reloading
   452         # set update time before module loading, else we get some reloading
   442         # weirdness in case of syntax error or other error while importing the
   453         # weirdness in case of syntax error or other error while importing the
   443         # module
   454         # module
   444         self._lastmodifs[filepath] = modified_on
   455         self._lastmodifs[filepath] = mdate
   445         # load the module
   456         # load the module
   446         module = load_module_from_name(modname, use_sys=not force_reload)
   457         module = load_module_from_name(modname, use_sys=not force_reload)
   447         self.load_module(module)
   458         self.load_module(module)
   448         return True
       
   449 
   459 
   450     def load_module(self, module):
   460     def load_module(self, module):
   451         self.info('loading %s', module)
   461         self.info('loading %s', module)
   452         if hasattr(module, 'registration_callback'):
   462         if hasattr(module, 'registration_callback'):
   453             module.registration_callback(self)
   463             module.registration_callback(self)