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')) |