27 from os.path import dirname, join, realpath, split, isdir, exists |
27 from os.path import dirname, join, realpath, split, isdir, exists |
28 from logging import getLogger |
28 from logging import getLogger |
29 from warnings import warn |
29 from warnings import warn |
30 |
30 |
31 from cubicweb import CW_SOFTWARE_ROOT, set_log_methods |
31 from cubicweb import CW_SOFTWARE_ROOT, set_log_methods |
32 from cubicweb import RegistryNotFound, ObjectNotFound, NoSelectableObject |
32 from cubicweb import (RegistryNotFound, ObjectNotFound, NoSelectableObject, |
33 |
33 RegistryOutOfDate) |
34 |
34 |
35 def _toload_info(path, extrapath, _toload=None): |
35 def _toload_info(path, extrapath, _toload=None): |
36 """return a dictionary of <modname>: <modpath> and an ordered list of |
36 """return a dictionary of <modname>: <modpath> and an ordered list of |
37 (file, module name) to load |
37 (file, module name) to load |
38 """ |
38 """ |
291 vname = vobject.__name__ |
291 vname = vobject.__name__ |
292 except AttributeError: |
292 except AttributeError: |
293 vname = vobject.__class__.__name__ |
293 vname = vobject.__class__.__name__ |
294 self.debug('registered vobject %s in registry %s with id %s', |
294 self.debug('registered vobject %s in registry %s with id %s', |
295 vname, registryname, oid) |
295 vname, registryname, oid) |
296 # automatic reloading management |
|
297 self._loadedmods[obj.__module__]['%s.%s' % (obj.__module__, oid)] = obj |
296 self._loadedmods[obj.__module__]['%s.%s' % (obj.__module__, oid)] = obj |
298 |
297 |
299 def unregister(self, obj, registryname=None): |
298 def unregister(self, obj, registryname=None): |
300 registryname = registryname or obj.__registry__ |
299 registryname = registryname or obj.__registry__ |
301 registry = self.registry(registryname) |
300 registry = self.registry(registryname) |
332 # intialization methods ################################################### |
331 # intialization methods ################################################### |
333 |
332 |
334 def init_registration(self, path, extrapath=None): |
333 def init_registration(self, path, extrapath=None): |
335 # compute list of all modules that have to be loaded |
334 # compute list of all modules that have to be loaded |
336 self._toloadmods, filemods = _toload_info(path, extrapath) |
335 self._toloadmods, filemods = _toload_info(path, extrapath) |
|
336 # XXX is _loadedmods still necessary ? It seems like it's useful |
|
337 # to avoid loading same module twice, especially with the |
|
338 # _load_ancestors_then_object logic but this needs to be checked |
337 self._loadedmods = {} |
339 self._loadedmods = {} |
338 return filemods |
340 return filemods |
339 |
341 |
340 def register_objects(self, path, force_reload=None, extrapath=None): |
342 def register_objects(self, path, force_reload=None, extrapath=None): |
341 if force_reload is None: |
343 if force_reload is None: |
363 if self.load_file(filepath, modname, force_reload): |
365 if self.load_file(filepath, modname, force_reload): |
364 change = True |
366 change = True |
365 return change |
367 return change |
366 |
368 |
367 def load_file(self, filepath, modname, force_reload=False): |
369 def load_file(self, filepath, modname, force_reload=False): |
368 """load visual objects from a python file""" |
370 """load app objects from a python file""" |
369 from logilab.common.modutils import load_module_from_name |
371 from logilab.common.modutils import load_module_from_name |
370 if modname in self._loadedmods: |
372 if modname in self._loadedmods: |
371 return |
373 return |
372 self._loadedmods[modname] = {} |
374 self._loadedmods[modname] = {} |
373 try: |
375 try: |
379 return False |
381 return False |
380 if filepath in self._lastmodifs: |
382 if filepath in self._lastmodifs: |
381 # only load file if it was modified |
383 # only load file if it was modified |
382 if modified_on <= self._lastmodifs[filepath]: |
384 if modified_on <= self._lastmodifs[filepath]: |
383 return |
385 return |
384 # if it was modified, unregister all exisiting objects |
386 # if it was modified, raise RegistryOutOfDate to reload everything |
385 # from this module, and keep track of what was unregistered |
387 self.info('File %s changed since last visit', filepath) |
386 unregistered = self.unregister_module_vobjects(modname) |
388 raise RegistryOutOfDate() |
387 else: |
|
388 unregistered = None |
|
389 # load the module |
389 # load the module |
390 module = load_module_from_name(modname, use_sys=not force_reload) |
390 module = load_module_from_name(modname, use_sys=not force_reload) |
391 self.load_module(module) |
391 self.load_module(module) |
392 # if something was unregistered, we need to update places where it was |
|
393 # referenced |
|
394 if unregistered: |
|
395 # oldnew_mapping = {} |
|
396 registered = self._loadedmods[modname] |
|
397 oldnew_mapping = dict((unregistered[name], registered[name]) |
|
398 for name in unregistered if name in registered) |
|
399 self.update_registered_subclasses(oldnew_mapping) |
|
400 self._lastmodifs[filepath] = modified_on |
392 self._lastmodifs[filepath] = modified_on |
401 return True |
393 return True |
402 |
394 |
403 def load_module(self, module): |
395 def load_module(self, module): |
404 self.info('loading %s', module) |
396 self.info('loading %s', module) |
456 regname = cls.__registry__ |
448 regname = cls.__registry__ |
457 if '%s.%s' % (regname, cls.id) in self.config['disable-appobjects']: |
449 if '%s.%s' % (regname, cls.id) in self.config['disable-appobjects']: |
458 return |
450 return |
459 self.register(cls) |
451 self.register(cls) |
460 |
452 |
461 def unregister_module_vobjects(self, modname): |
|
462 """removes registered objects coming from a given module |
|
463 |
|
464 returns a dictionnary classid/class of all classes that will need |
|
465 to be updated after reload (i.e. vobjects referencing classes defined |
|
466 in the <modname> module) |
|
467 """ |
|
468 unregistered = {} |
|
469 # browse each registered object |
|
470 for registry, objdict in self.items(): |
|
471 for oid, objects in objdict.items(): |
|
472 for obj in objects[:]: |
|
473 objname = obj.classid() |
|
474 # if the vobject is defined in this module, remove it |
|
475 if objname.startswith(modname): |
|
476 unregistered[objname] = obj |
|
477 objects.remove(obj) |
|
478 self.debug('unregistering %s in %s registry', |
|
479 objname, registry) |
|
480 # if not, check if the vobject can be found in baseclasses |
|
481 # (because we also want subclasses to be updated) |
|
482 else: |
|
483 if not isinstance(obj, type): |
|
484 obj = obj.__class__ |
|
485 for baseclass in obj.__bases__: |
|
486 if hasattr(baseclass, 'classid'): |
|
487 baseclassid = baseclass.classid() |
|
488 if baseclassid.startswith(modname): |
|
489 unregistered[baseclassid] = baseclass |
|
490 # update oid entry |
|
491 if objects: |
|
492 objdict[oid] = objects |
|
493 else: |
|
494 del objdict[oid] |
|
495 return unregistered |
|
496 |
|
497 def update_registered_subclasses(self, oldnew_mapping): |
|
498 """updates subclasses of re-registered vobjects |
|
499 |
|
500 if baseviews.PrimaryView is changed, baseviews.py will be reloaded |
|
501 automatically and the new version of PrimaryView will be registered. |
|
502 But all existing subclasses must also be notified of this change, and |
|
503 that's what this method does |
|
504 |
|
505 :param oldnew_mapping: a dict mapping old version of a class to |
|
506 the new version |
|
507 """ |
|
508 # browse each registered object |
|
509 for objdict in self.values(): |
|
510 for objects in objdict.values(): |
|
511 for obj in objects: |
|
512 if not isinstance(obj, type): |
|
513 obj = obj.__class__ |
|
514 # build new baseclasses tuple |
|
515 newbases = tuple(oldnew_mapping.get(baseclass, baseclass) |
|
516 for baseclass in obj.__bases__) |
|
517 # update obj's baseclasses tuple (__bases__) if needed |
|
518 if newbases != obj.__bases__: |
|
519 self.debug('updating %s.%s base classes', |
|
520 obj.__module__, obj.__name__) |
|
521 obj.__bases__ = newbases |
|
522 |
|
523 # init logging |
453 # init logging |
524 set_log_methods(VObject, getLogger('cubicweb')) |
454 set_log_methods(VObject, getLogger('cubicweb')) |
525 set_log_methods(VRegistry, getLogger('cubicweb.registry')) |
455 set_log_methods(VRegistry, getLogger('cubicweb.registry')) |
526 |
456 |
527 |
457 |