Package logilab :: Package common :: Module registry
[frames] | no frames]

Source Code for Module logilab.common.registry

   1  # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 
   2  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr 
   3  # 
   4  # This file is part of Logilab-common. 
   5  # 
   6  # Logilab-common is free software: you can redistribute it and/or modify it 
   7  # under the terms of the GNU Lesser General Public License as published by the 
   8  # Free Software Foundation, either version 2.1 of the License, or (at your 
   9  # option) any later version. 
  10  # 
  11  # Logilab-common is distributed in the hope that it will be useful, but WITHOUT 
  12  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  13  # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
  14  # details. 
  15  # 
  16  # You should have received a copy of the GNU Lesser General Public License along 
  17  # with Logilab-common.  If not, see <http://www.gnu.org/licenses/>. 
  18  """This module provides bases for predicates dispatching (the pattern in use 
  19  here is similar to what's refered as multi-dispatch or predicate-dispatch in the 
  20  literature, though a bit different since the idea is to select across different 
  21  implementation 'e.g. classes), not to dispatch a message to a function or 
  22  method. It contains the following classes: 
  23   
  24  * :class:`RegistryStore`, the top level object which loads implementation 
  25    objects and stores them into registries. You'll usually use it to access 
  26    registries and their contained objects; 
  27   
  28  * :class:`Registry`, the base class which contains objects semantically grouped 
  29    (for instance, sharing a same API, hence the 'implementation' name). You'll 
  30    use it to select the proper implementation according to a context. Notice you 
  31    may use registries on their own without using the store. 
  32   
  33  .. Note:: 
  34   
  35    implementation objects are usually designed to be accessed through the 
  36    registry and not by direct instantiation, besides to use it as base classe. 
  37   
  38  The selection procedure is delegated to a selector, which is responsible for 
  39  scoring the object according to some context. At the end of the selection, if an 
  40  implementation has been found, an instance of this class is returned. A selector 
  41  is built from one or more predicates combined together using AND, OR, NOT 
  42  operators (actually `&`, `|` and `~`). You'll thus find some base classes to 
  43  build predicates: 
  44   
  45  * :class:`Predicate`, the abstract base predicate class 
  46   
  47  * :class:`AndPredicate`, :class:`OrPredicate`, :class:`NotPredicate`, which you 
  48    shouldn't have to use directly. You'll use `&`, `|` and '~' operators between 
  49    predicates directly 
  50   
  51  * :func:`objectify_predicate` 
  52   
  53  You'll eventually find one concrete predicate: :class:`yes` 
  54   
  55  .. autoclass:: RegistryStore 
  56  .. autoclass:: Registry 
  57   
  58  Predicates 
  59  ---------- 
  60  .. autoclass:: Predicate 
  61  .. autofunction:: objectify_predicate 
  62  .. autoclass:: yes 
  63  .. autoclass:: AndPredicate 
  64  .. autoclass:: OrPredicate 
  65  .. autoclass:: NotPredicate 
  66   
  67  Debugging 
  68  --------- 
  69  .. autoclass:: traced_selection 
  70   
  71  Exceptions 
  72  ---------- 
  73  .. autoclass:: RegistryException 
  74  .. autoclass:: RegistryNotFound 
  75  .. autoclass:: ObjectNotFound 
  76  .. autoclass:: NoSelectableObject 
  77  """ 
  78   
  79  from __future__ import print_function 
  80   
  81  __docformat__ = "restructuredtext en" 
  82   
  83  import sys 
  84  import pkgutil 
  85  import types 
  86  import weakref 
  87  import traceback as tb 
  88  from os import listdir, stat 
  89  from os.path import join, isdir, exists 
  90  from logging import getLogger 
  91  from warnings import warn 
  92   
  93  from six import string_types, add_metaclass 
  94   
  95  from logilab.common.modutils import modpath_from_file 
  96  from logilab.common.logging_ext import set_log_methods 
  97  from logilab.common.decorators import classproperty 
  98  from logilab.common.deprecation import deprecated 
99 100 101 -class RegistryException(Exception):
102 """Base class for registry exception."""
103
104 -class RegistryNotFound(RegistryException):
105 """Raised when an unknown registry is requested. 106 107 This is usually a programming/typo error. 108 """
109
110 -class ObjectNotFound(RegistryException):
111 """Raised when an unregistered object is requested. 112 113 This may be a programming/typo or a misconfiguration error. 114 """
115
116 -class NoSelectableObject(RegistryException):
117 """Raised when no object is selectable for a given context."""
118 - def __init__(self, args, kwargs, objects):
119 self.args = args 120 self.kwargs = kwargs 121 self.objects = objects
122
123 - def __str__(self):
124 return ('args: %s, kwargs: %s\ncandidates: %s' 125 % (self.args, self.kwargs.keys(), self.objects))
126
127 -class SelectAmbiguity(RegistryException):
128 """Raised when several objects compete at selection time with an equal 129 score. 130 131 """
132
133 134 -def _modname_from_path(path, extrapath=None):
135 modpath = modpath_from_file(path, extrapath) 136 # omit '__init__' from package's name to avoid loading that module 137 # once for each name when it is imported by some other object 138 # module. This supposes import in modules are done as:: 139 # 140 # from package import something 141 # 142 # not:: 143 # 144 # from package.__init__ import something 145 # 146 # which seems quite correct. 147 if modpath[-1] == '__init__': 148 modpath.pop() 149 return '.'.join(modpath)
150
151 152 -def _toload_info(path, extrapath, _toload=None):
153 """Return a dictionary of <modname>: <modpath> and an ordered list of 154 (file, module name) to load 155 """ 156 if _toload is None: 157 assert isinstance(path, list) 158 _toload = {}, [] 159 for fileordir in path: 160 if isdir(fileordir) and exists(join(fileordir, '__init__.py')): 161 subfiles = [join(fileordir, fname) for fname in listdir(fileordir)] 162 _toload_info(subfiles, extrapath, _toload) 163 elif fileordir[-3:] == '.py': 164 modname = _modname_from_path(fileordir, extrapath) 165 _toload[0][modname] = fileordir 166 _toload[1].append((fileordir, modname)) 167 return _toload
168
169 170 -class RegistrableObject(object):
171 """This is the base class for registrable objects which are selected 172 according to a context. 173 174 :attr:`__registry__` 175 name of the registry for this object (string like 'views', 176 'templates'...). You may want to define `__registries__` directly if your 177 object should be registered in several registries. 178 179 :attr:`__regid__` 180 object's identifier in the registry (string like 'main', 181 'primary', 'folder_box') 182 183 :attr:`__select__` 184 class'selector 185 186 Moreover, the `__abstract__` attribute may be set to True to indicate that a 187 class is abstract and should not be registered. 188 189 You don't have to inherit from this class to put it in a registry (having 190 `__regid__` and `__select__` is enough), though this is needed for classes 191 that should be automatically registered. 192 """ 193 194 __registry__ = None 195 __regid__ = None 196 __select__ = None 197 __abstract__ = True # see doc snipppets below (in Registry class) 198 199 @classproperty
200 - def __registries__(cls):
201 if cls.__registry__ is None: 202 return () 203 return (cls.__registry__,)
204
205 206 -class RegistrableInstance(RegistrableObject):
207 """Inherit this class if you want instances of the classes to be 208 automatically registered. 209 """ 210
211 - def __new__(cls, *args, **kwargs):
212 """Add a __module__ attribute telling the module where the instance was 213 created, for automatic registration. 214 """ 215 module = kwargs.pop('__module__', None) 216 obj = super(RegistrableInstance, cls).__new__(cls) 217 if module is None: 218 warn('instantiate {0} with ' 219 '__module__=__name__'.format(cls.__name__), 220 DeprecationWarning) 221 # XXX subclass must no override __new__ 222 filepath = tb.extract_stack(limit=2)[0][0] 223 obj.__module__ = _modname_from_path(filepath) 224 else: 225 obj.__module__ = module 226 return obj
227
228 - def __init__(self, __module__=None):
229 super(RegistrableInstance, self).__init__()
230
231 232 -class Registry(dict):
233 """The registry store a set of implementations associated to identifier: 234 235 * to each identifier are associated a list of implementations 236 237 * to select an implementation of a given identifier, you should use one of the 238 :meth:`select` or :meth:`select_or_none` method 239 240 * to select a list of implementations for a context, you should use the 241 :meth:`possible_objects` method 242 243 * dictionary like access to an identifier will return the bare list of 244 implementations for this identifier. 245 246 To be usable in a registry, the only requirement is to have a `__select__` 247 attribute. 248 249 At the end of the registration process, the :meth:`__registered__` 250 method is called on each registered object which have them, given the 251 registry in which it's registered as argument. 252 253 Registration methods: 254 255 .. automethod:: register 256 .. automethod:: unregister 257 258 Selection methods: 259 260 .. automethod:: select 261 .. automethod:: select_or_none 262 .. automethod:: possible_objects 263 .. automethod:: object_by_id 264 """
265 - def __init__(self, debugmode):
266 super(Registry, self).__init__() 267 self.debugmode = debugmode
268
269 - def __getitem__(self, name):
270 """return the registry (list of implementation objects) associated to 271 this name 272 """ 273 try: 274 return super(Registry, self).__getitem__(name) 275 except KeyError: 276 exc = ObjectNotFound(name) 277 exc.__traceback__ = sys.exc_info()[-1] 278 raise exc
279 280 @classmethod
281 - def objid(cls, obj):
282 """returns a unique identifier for an object stored in the registry""" 283 return '%s.%s' % (obj.__module__, cls.objname(obj))
284 285 @classmethod
286 - def objname(cls, obj):
287 """returns a readable name for an object stored in the registry""" 288 return getattr(obj, '__name__', id(obj))
289
290 - def initialization_completed(self):
291 """call method __registered__() on registered objects when the callback 292 is defined""" 293 for objects in self.values(): 294 for objectcls in objects: 295 registered = getattr(objectcls, '__registered__', None) 296 if registered: 297 registered(self) 298 if self.debugmode: 299 wrap_predicates(_lltrace)
300
301 - def register(self, obj, oid=None, clear=False):
302 """base method to add an object in the registry""" 303 assert not '__abstract__' in obj.__dict__, obj 304 assert obj.__select__, obj 305 oid = oid or obj.__regid__ 306 assert oid, ('no explicit name supplied to register object %s, ' 307 'which has no __regid__ set' % obj) 308 if clear: 309 objects = self[oid] = [] 310 else: 311 objects = self.setdefault(oid, []) 312 assert not obj in objects, 'object %s is already registered' % obj 313 objects.append(obj)
314
315 - def register_and_replace(self, obj, replaced):
316 """remove <replaced> and register <obj>""" 317 # XXXFIXME this is a duplication of unregister() 318 # remove register_and_replace in favor of unregister + register 319 # or simplify by calling unregister then register here 320 if not isinstance(replaced, string_types): 321 replaced = self.objid(replaced) 322 # prevent from misspelling 323 assert obj is not replaced, 'replacing an object by itself: %s' % obj 324 registered_objs = self.get(obj.__regid__, ()) 325 for index, registered in enumerate(registered_objs): 326 if self.objid(registered) == replaced: 327 del registered_objs[index] 328 break 329 else: 330 self.warning('trying to replace %s that is not registered with %s', 331 replaced, obj) 332 self.register(obj)
333
334 - def unregister(self, obj):
335 """remove object <obj> from this registry""" 336 objid = self.objid(obj) 337 oid = obj.__regid__ 338 for registered in self.get(oid, ()): 339 # use self.objid() to compare objects because vreg will probably 340 # have its own version of the object, loaded through execfile 341 if self.objid(registered) == objid: 342 self[oid].remove(registered) 343 break 344 else: 345 self.warning('can\'t remove %s, no id %s in the registry', 346 objid, oid)
347
348 - def all_objects(self):
349 """return a list containing all objects in this registry. 350 """ 351 result = [] 352 for objs in self.values(): 353 result += objs 354 return result
355 356 # dynamic selection methods ################################################ 357
358 - def object_by_id(self, oid, *args, **kwargs):
359 """return object with the `oid` identifier. Only one object is expected 360 to be found. 361 362 raise :exc:`ObjectNotFound` if there are no object with id `oid` in this 363 registry 364 365 raise :exc:`AssertionError` if there is more than one object there 366 """ 367 objects = self[oid] 368 assert len(objects) == 1, objects 369 return objects[0](*args, **kwargs)
370
371 - def select(self, __oid, *args, **kwargs):
372 """return the most specific object among those with the given oid 373 according to the given context. 374 375 raise :exc:`ObjectNotFound` if there are no object with id `oid` in this 376 registry 377 378 raise :exc:`NoSelectableObject` if no object can be selected 379 """ 380 obj = self._select_best(self[__oid], *args, **kwargs) 381 if obj is None: 382 raise NoSelectableObject(args, kwargs, self[__oid] ) 383 return obj
384
385 - def select_or_none(self, __oid, *args, **kwargs):
386 """return the most specific object among those with the given oid 387 according to the given context, or None if no object applies. 388 """ 389 try: 390 return self._select_best(self[__oid], *args, **kwargs) 391 except ObjectNotFound: 392 return None
393
394 - def possible_objects(self, *args, **kwargs):
395 """return an iterator on possible objects in this registry for the given 396 context 397 """ 398 for objects in self.values(): 399 obj = self._select_best(objects, *args, **kwargs) 400 if obj is None: 401 continue 402 yield obj
403
404 - def _select_best(self, objects, *args, **kwargs):
405 """return an instance of the most specific object according 406 to parameters 407 408 return None if not object apply (don't raise `NoSelectableObject` since 409 it's costly when searching objects using `possible_objects` 410 (e.g. searching for hooks). 411 """ 412 score, winners = 0, None 413 for obj in objects: 414 objectscore = obj.__select__(obj, *args, **kwargs) 415 if objectscore > score: 416 score, winners = objectscore, [obj] 417 elif objectscore > 0 and objectscore == score: 418 winners.append(obj) 419 if winners is None: 420 return None 421 if len(winners) > 1: 422 # log in production environement / test, error while debugging 423 msg = 'select ambiguity: %s\n(args: %s, kwargs: %s)' 424 if self.debugmode: 425 # raise bare exception in debug mode 426 raise SelectAmbiguity(msg % (winners, args, kwargs.keys())) 427 self.error(msg, winners, args, kwargs.keys()) 428 # return the result of calling the object 429 return self.selected(winners[0], args, kwargs)
430
431 - def selected(self, winner, args, kwargs):
432 """override here if for instance you don't want "instanciation" 433 """ 434 return winner(*args, **kwargs)
435 436 # these are overridden by set_log_methods below 437 # only defining here to prevent pylint from complaining 438 info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
439
440 441 -def obj_registries(cls, registryname=None):
442 """return a tuple of registry names (see __registries__)""" 443 if registryname: 444 return (registryname,) 445 return cls.__registries__
446
447 448 -class RegistryStore(dict):
449 """This class is responsible for loading objects and storing them 450 in their registry which is created on the fly as needed. 451 452 It handles dynamic registration of objects and provides a 453 convenient api to access them. To be recognized as an object that 454 should be stored into one of the store's registry 455 (:class:`Registry`), an object must provide the following 456 attributes, used control how they interact with the registry: 457 458 :attr:`__registries__` 459 list of registry names (string like 'views', 'templates'...) into which 460 the object should be registered 461 462 :attr:`__regid__` 463 object identifier in the registry (string like 'main', 464 'primary', 'folder_box') 465 466 :attr:`__select__` 467 the object predicate selectors 468 469 Moreover, the :attr:`__abstract__` attribute may be set to `True` 470 to indicate that an object is abstract and should not be registered 471 (such inherited attributes not considered). 472 473 .. Note:: 474 475 When using the store to load objects dynamically, you *always* have 476 to use **super()** to get the methods and attributes of the 477 superclasses, and not use the class identifier. If not, you'll get into 478 trouble at reload time. 479 480 For example, instead of writing:: 481 482 class Thing(Parent): 483 __regid__ = 'athing' 484 __select__ = yes() 485 486 def f(self, arg1): 487 Parent.f(self, arg1) 488 489 You must write:: 490 491 class Thing(Parent): 492 __regid__ = 'athing' 493 __select__ = yes() 494 495 def f(self, arg1): 496 super(Thing, self).f(arg1) 497 498 Controlling object registration 499 ------------------------------- 500 501 Dynamic loading is triggered by calling the :meth:`register_modnames` 502 method, given a list of modules names to inspect. 503 504 .. automethod:: register_modnames 505 506 For each module, by default, all compatible objects are registered 507 automatically. However if some objects come as replacement of 508 other objects, or have to be included only if some condition is 509 met, you'll have to define a `registration_callback(vreg)` 510 function in the module and explicitly register **all objects** in 511 this module, using the api defined below. 512 513 514 .. automethod:: RegistryStore.register_all 515 .. automethod:: RegistryStore.register_and_replace 516 .. automethod:: RegistryStore.register 517 .. automethod:: RegistryStore.unregister 518 519 .. Note:: 520 Once the function `registration_callback(vreg)` is implemented in a 521 module, all the objects from this module have to be explicitly 522 registered as it disables the automatic object registration. 523 524 525 Examples: 526 527 .. sourcecode:: python 528 529 def registration_callback(store): 530 # register everything in the module except BabarClass 531 store.register_all(globals().values(), __name__, (BabarClass,)) 532 533 # conditionally register BabarClass 534 if 'babar_relation' in store.schema: 535 store.register(BabarClass) 536 537 In this example, we register all application object classes defined in the module 538 except `BabarClass`. This class is then registered only if the 'babar_relation' 539 relation type is defined in the instance schema. 540 541 .. sourcecode:: python 542 543 def registration_callback(store): 544 store.register(Elephant) 545 # replace Babar by Celeste 546 store.register_and_replace(Celeste, Babar) 547 548 In this example, we explicitly register classes one by one: 549 550 * the `Elephant` class 551 * the `Celeste` to replace `Babar` 552 553 If at some point we register a new appobject class in this module, it won't be 554 registered at all without modification to the `registration_callback` 555 implementation. The first example will register it though, thanks to the call 556 to the `register_all` method. 557 558 Controlling registry instantiation 559 ---------------------------------- 560 561 The `REGISTRY_FACTORY` class dictionary allows to specify which class should 562 be instantiated for a given registry name. The class associated to `None` 563 key will be the class used when there is no specific class for a name. 564 """ 565
566 - def __init__(self, debugmode=False):
567 super(RegistryStore, self).__init__() 568 self.debugmode = debugmode
569
570 - def reset(self):
571 """clear all registries managed by this store""" 572 # don't use self.clear, we want to keep existing subdictionaries 573 for subdict in self.values(): 574 subdict.clear() 575 self._lastmodifs = {}
576
577 - def __getitem__(self, name):
578 """return the registry (dictionary of class objects) associated to 579 this name 580 """ 581 try: 582 return super(RegistryStore, self).__getitem__(name) 583 except KeyError: 584 exc = RegistryNotFound(name) 585 exc.__traceback__ = sys.exc_info()[-1] 586 raise exc
587 588 # methods for explicit (un)registration ################################### 589 590 # default class, when no specific class set 591 REGISTRY_FACTORY = {None: Registry} 592
593 - def registry_class(self, regid):
594 """return existing registry named regid or use factory to create one and 595 return it""" 596 try: 597 return self.REGISTRY_FACTORY[regid] 598 except KeyError: 599 return self.REGISTRY_FACTORY[None]
600
601 - def setdefault(self, regid):
602 try: 603 return self[regid] 604 except RegistryNotFound: 605 self[regid] = self.registry_class(regid)(self.debugmode) 606 return self[regid]
607
608 - def register_all(self, objects, modname, butclasses=()):
609 """register registrable objects into `objects`. 610 611 Registrable objects are properly configured subclasses of 612 :class:`RegistrableObject`. Objects which are not defined in the module 613 `modname` or which are in `butclasses` won't be registered. 614 615 Typical usage is: 616 617 .. sourcecode:: python 618 619 store.register_all(globals().values(), __name__, (ClassIWantToRegisterExplicitly,)) 620 621 So you get partially automatic registration, keeping manual registration 622 for some object (to use 623 :meth:`~logilab.common.registry.RegistryStore.register_and_replace` for 624 instance). 625 """ 626 assert isinstance(modname, string_types), \ 627 'modname expected to be a module name (ie string), got %r' % modname 628 for obj in objects: 629 if self.is_registrable(obj) and obj.__module__ == modname and not obj in butclasses: 630 if isinstance(obj, type): 631 self._load_ancestors_then_object(modname, obj, butclasses) 632 else: 633 self.register(obj)
634
635 - def register(self, obj, registryname=None, oid=None, clear=False):
636 """register `obj` implementation into `registryname` or 637 `obj.__registries__` if not specified, with identifier `oid` or 638 `obj.__regid__` if not specified. 639 640 If `clear` is true, all objects with the same identifier will be 641 previously unregistered. 642 """ 643 assert not obj.__dict__.get('__abstract__'), obj 644 for registryname in obj_registries(obj, registryname): 645 registry = self.setdefault(registryname) 646 registry.register(obj, oid=oid, clear=clear) 647 self.debug("register %s in %s['%s']", 648 registry.objname(obj), registryname, oid or obj.__regid__) 649 self._loadedmods.setdefault(obj.__module__, {})[registry.objid(obj)] = obj
650
651 - def unregister(self, obj, registryname=None):
652 """unregister `obj` object from the registry `registryname` or 653 `obj.__registries__` if not specified. 654 """ 655 for registryname in obj_registries(obj, registryname): 656 registry = self[registryname] 657 registry.unregister(obj) 658 self.debug("unregister %s from %s['%s']", 659 registry.objname(obj), registryname, obj.__regid__)
660
661 - def register_and_replace(self, obj, replaced, registryname=None):
662 """register `obj` object into `registryname` or 663 `obj.__registries__` if not specified. If found, the `replaced` object 664 will be unregistered first (else a warning will be issued as it is 665 generally unexpected). 666 """ 667 for registryname in obj_registries(obj, registryname): 668 registry = self[registryname] 669 registry.register_and_replace(obj, replaced) 670 self.debug("register %s in %s['%s'] instead of %s", 671 registry.objname(obj), registryname, obj.__regid__, 672 registry.objname(replaced))
673 674 # initialization methods ################################################### 675
676 - def init_registration(self, path, extrapath=None):
677 """reset registry and walk down path to return list of (path, name) 678 file modules to be loaded""" 679 # XXX make this private by renaming it to _init_registration ? 680 self.reset() 681 # compute list of all modules that have to be loaded 682 self._toloadmods, filemods = _toload_info(path, extrapath) 683 # XXX is _loadedmods still necessary ? It seems like it's useful 684 # to avoid loading same module twice, especially with the 685 # _load_ancestors_then_object logic but this needs to be checked 686 self._loadedmods = {} 687 return filemods
688 689 @deprecated('use register_modnames() instead')
690 - def register_objects(self, path, extrapath=None):
691 """register all objects found walking down <path>""" 692 # load views from each directory in the instance's path 693 # XXX inline init_registration ? 694 filemods = self.init_registration(path, extrapath) 695 for filepath, modname in filemods: 696 self.load_file(filepath, modname) 697 self.initialization_completed()
698
699 - def register_modnames(self, modnames):
700 """register all objects found in <modnames>""" 701 self.reset() 702 self._loadedmods = {} 703 self._toloadmods = {} 704 toload = [] 705 for modname in modnames: 706 filepath = pkgutil.find_loader(modname).get_filename() 707 if filepath[-4:] in ('.pyc', '.pyo'): 708 # The source file *must* exists 709 filepath = filepath[:-1] 710 self._toloadmods[modname] = filepath 711 toload.append((filepath, modname)) 712 for filepath, modname in toload: 713 self.load_file(filepath, modname) 714 self.initialization_completed()
715
716 - def initialization_completed(self):
717 """call initialization_completed() on all known registries""" 718 for reg in self.values(): 719 reg.initialization_completed()
720
721 - def _mdate(self, filepath):
722 """ return the modification date of a file path """ 723 try: 724 return stat(filepath)[-2] 725 except OSError: 726 # this typically happens on emacs backup files (.#foo.py) 727 self.warning('Unable to load %s. It is likely to be a backup file', 728 filepath) 729 return None
730
731 - def is_reload_needed(self, path):
732 """return True if something module changed and the registry should be 733 reloaded 734 """ 735 lastmodifs = self._lastmodifs 736 for fileordir in path: 737 if isdir(fileordir) and exists(join(fileordir, '__init__.py')): 738 if self.is_reload_needed([join(fileordir, fname) 739 for fname in listdir(fileordir)]): 740 return True 741 elif fileordir[-3:] == '.py': 742 mdate = self._mdate(fileordir) 743 if mdate is None: 744 continue # backup file, see _mdate implementation 745 elif "flymake" in fileordir: 746 # flymake + pylint in use, don't consider these they will corrupt the registry 747 continue 748 if fileordir not in lastmodifs or lastmodifs[fileordir] < mdate: 749 self.info('File %s changed since last visit', fileordir) 750 return True 751 return False
752
753 - def load_file(self, filepath, modname):
754 """ load registrable objects (if any) from a python file """ 755 if modname in self._loadedmods: 756 return 757 self._loadedmods[modname] = {} 758 mdate = self._mdate(filepath) 759 if mdate is None: 760 return # backup file, see _mdate implementation 761 elif "flymake" in filepath: 762 # flymake + pylint in use, don't consider these they will corrupt the registry 763 return 764 # set update time before module loading, else we get some reloading 765 # weirdness in case of syntax error or other error while importing the 766 # module 767 self._lastmodifs[filepath] = mdate 768 # load the module 769 if sys.version_info < (3,) and not isinstance(modname, str): 770 modname = str(modname) 771 module = __import__(modname, fromlist=modname.split('.')[:-1]) 772 self.load_module(module)
773
774 - def load_module(self, module):
775 """Automatically handle module objects registration. 776 777 Instances are registered as soon as they are hashable and have the 778 following attributes: 779 780 * __regid__ (a string) 781 * __select__ (a callable) 782 * __registries__ (a tuple/list of string) 783 784 For classes this is a bit more complicated : 785 786 - first ensure parent classes are already registered 787 788 - class with __abstract__ == True in their local dictionary are skipped 789 790 - object class needs to have registries and identifier properly set to a 791 non empty string to be registered. 792 """ 793 self.info('loading %s from %s', module.__name__, module.__file__) 794 if hasattr(module, 'registration_callback'): 795 module.registration_callback(self) 796 else: 797 self.register_all(vars(module).values(), module.__name__)
798
799 - def _load_ancestors_then_object(self, modname, objectcls, butclasses=()):
800 """handle class registration according to rules defined in 801 :meth:`load_module` 802 """ 803 # backward compat, we used to allow whatever else than classes 804 if not isinstance(objectcls, type): 805 if self.is_registrable(objectcls) and objectcls.__module__ == modname: 806 self.register(objectcls) 807 return 808 # imported classes 809 objmodname = objectcls.__module__ 810 if objmodname != modname: 811 # The module of the object is not the same as the currently 812 # worked on module, or this is actually an instance, which 813 # has no module at all 814 if objmodname in self._toloadmods: 815 # if this is still scheduled for loading, let's proceed immediately, 816 # but using the object module 817 self.load_file(self._toloadmods[objmodname], objmodname) 818 return 819 # ensure object hasn't been already processed 820 clsid = '%s.%s' % (modname, objectcls.__name__) 821 if clsid in self._loadedmods[modname]: 822 return 823 self._loadedmods[modname][clsid] = objectcls 824 # ensure ancestors are registered 825 for parent in objectcls.__bases__: 826 self._load_ancestors_then_object(modname, parent, butclasses) 827 # ensure object is registrable 828 if objectcls in butclasses or not self.is_registrable(objectcls): 829 return 830 # backward compat 831 reg = self.setdefault(obj_registries(objectcls)[0]) 832 if reg.objname(objectcls)[0] == '_': 833 warn("[lgc 0.59] object whose name start with '_' won't be " 834 "skipped anymore at some point, use __abstract__ = True " 835 "instead (%s)" % objectcls, DeprecationWarning) 836 return 837 # register, finally 838 self.register(objectcls)
839 840 @classmethod
841 - def is_registrable(cls, obj):
842 """ensure `obj` should be registered 843 844 as arbitrary stuff may be registered, do a lot of check and warn about 845 weird cases (think to dumb proxy objects) 846 """ 847 if isinstance(obj, type): 848 if not issubclass(obj, RegistrableObject): 849 # ducktyping backward compat 850 if not (getattr(obj, '__registries__', None) 851 and getattr(obj, '__regid__', None) 852 and getattr(obj, '__select__', None)): 853 return False 854 elif issubclass(obj, RegistrableInstance): 855 return False 856 elif not isinstance(obj, RegistrableInstance): 857 return False 858 if not obj.__regid__: 859 return False # no regid 860 registries = obj.__registries__ 861 if not registries: 862 return False # no registries 863 selector = obj.__select__ 864 if not selector: 865 return False # no selector 866 if obj.__dict__.get('__abstract__', False): 867 return False 868 # then detect potential problems that should be warned 869 if not isinstance(registries, (tuple, list)): 870 cls.warning('%s has __registries__ which is not a list or tuple', obj) 871 return False 872 if not callable(selector): 873 cls.warning('%s has not callable __select__', obj) 874 return False 875 return True
876 877 # these are overridden by set_log_methods below 878 # only defining here to prevent pylint from complaining 879 info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
880 881 882 # init logging 883 set_log_methods(RegistryStore, getLogger('registry.store')) 884 set_log_methods(Registry, getLogger('registry')) 885 886 887 # helpers for debugging selectors 888 TRACED_OIDS = None
889 890 -def _trace_selector(cls, selector, args, ret):
891 vobj = args[0] 892 if TRACED_OIDS == 'all' or vobj.__regid__ in TRACED_OIDS: 893 print('%s -> %s for %s(%s)' % (cls, ret, vobj, vobj.__regid__))
894
895 -def _lltrace(selector):
896 """use this decorator on your predicates so they become traceable with 897 :class:`traced_selection` 898 """ 899 def traced(cls, *args, **kwargs): 900 ret = selector(cls, *args, **kwargs) 901 if TRACED_OIDS is not None: 902 _trace_selector(cls, selector, args, ret) 903 return ret
904 traced.__name__ = selector.__name__ 905 traced.__doc__ = selector.__doc__ 906 return traced 907
908 -class traced_selection(object): # pylint: disable=C0103
909 """ 910 Typical usage is : 911 912 .. sourcecode:: python 913 914 >>> from logilab.common.registry import traced_selection 915 >>> with traced_selection(): 916 ... # some code in which you want to debug selectors 917 ... # for all objects 918 919 This will yield lines like this in the logs:: 920 921 selector one_line_rset returned 0 for <class 'elephant.Babar'> 922 923 You can also give to :class:`traced_selection` the identifiers of objects on 924 which you want to debug selection ('oid1' and 'oid2' in the example above). 925 926 .. sourcecode:: python 927 928 >>> with traced_selection( ('regid1', 'regid2') ): 929 ... # some code in which you want to debug selectors 930 ... # for objects with __regid__ 'regid1' and 'regid2' 931 932 A potentially useful point to set up such a tracing function is 933 the `logilab.common.registry.Registry.select` method body. 934 """ 935
936 - def __init__(self, traced='all'):
937 self.traced = traced
938
939 - def __enter__(self):
940 global TRACED_OIDS 941 TRACED_OIDS = self.traced
942
943 - def __exit__(self, exctype, exc, traceback):
944 global TRACED_OIDS 945 TRACED_OIDS = None 946 return traceback is None
947
948 # selector base classes and operations ######################################## 949 950 -def objectify_predicate(selector_func):
951 """Most of the time, a simple score function is enough to build a selector. 952 The :func:`objectify_predicate` decorator turn it into a proper selector 953 class:: 954 955 @objectify_predicate 956 def one(cls, req, rset=None, **kwargs): 957 return 1 958 959 class MyView(View): 960 __select__ = View.__select__ & one() 961 962 """ 963 return type(selector_func.__name__, (Predicate,), 964 {'__doc__': selector_func.__doc__, 965 '__call__': lambda self, *a, **kw: selector_func(*a, **kw)})
966 967 968 _PREDICATES = {}
969 970 -def wrap_predicates(decorator):
971 for predicate in _PREDICATES.values(): 972 if not '_decorators' in predicate.__dict__: 973 predicate._decorators = set() 974 if decorator in predicate._decorators: 975 continue 976 predicate._decorators.add(decorator) 977 predicate.__call__ = decorator(predicate.__call__)
978
979 -class PredicateMetaClass(type):
980 - def __new__(mcs, *args, **kwargs):
981 # use __new__ so subclasses doesn't have to call Predicate.__init__ 982 inst = type.__new__(mcs, *args, **kwargs) 983 proxy = weakref.proxy(inst, lambda p: _PREDICATES.pop(id(p))) 984 _PREDICATES[id(proxy)] = proxy 985 return inst
986
987 988 @add_metaclass(PredicateMetaClass) 989 -class Predicate(object):
990 """base class for selector classes providing implementation 991 for operators ``&``, ``|`` and ``~`` 992 993 This class is only here to give access to binary operators, the selector 994 logic itself should be implemented in the :meth:`__call__` method. Notice it 995 should usually accept any arbitrary arguments (the context), though that may 996 vary depending on your usage of the registry. 997 998 a selector is called to help choosing the correct object for a 999 particular context by returning a score (`int`) telling how well 1000 the implementation given as first argument fit to the given context. 1001 1002 0 score means that the class doesn't apply. 1003 """ 1004 1005 @property
1006 - def func_name(self):
1007 # backward compatibility 1008 return self.__class__.__name__
1009
1010 - def search_selector(self, selector):
1011 """search for the given selector, selector instance or tuple of 1012 selectors in the selectors tree. Return None if not found. 1013 """ 1014 if self is selector: 1015 return self 1016 if (isinstance(selector, type) or isinstance(selector, tuple)) and \ 1017 isinstance(self, selector): 1018 return self 1019 return None
1020
1021 - def __str__(self):
1022 return self.__class__.__name__
1023
1024 - def __and__(self, other):
1025 return AndPredicate(self, other)
1026 - def __rand__(self, other):
1027 return AndPredicate(other, self)
1028 - def __iand__(self, other):
1029 return AndPredicate(self, other)
1030 - def __or__(self, other):
1031 return OrPredicate(self, other)
1032 - def __ror__(self, other):
1033 return OrPredicate(other, self)
1034 - def __ior__(self, other):
1035 return OrPredicate(self, other)
1036
1037 - def __invert__(self):
1038 return NotPredicate(self)
1039 1040 # XXX (function | function) or (function & function) not managed yet 1041
1042 - def __call__(self, cls, *args, **kwargs):
1043 return NotImplementedError("selector %s must implement its logic " 1044 "in its __call__ method" % self.__class__)
1045
1046 - def __repr__(self):
1047 return u'<Predicate %s at %x>' % (self.__class__.__name__, id(self))
1048
1049 1050 -class MultiPredicate(Predicate):
1051 """base class for compound selector classes""" 1052
1053 - def __init__(self, *selectors):
1054 self.selectors = self.merge_selectors(selectors)
1055
1056 - def __str__(self):
1057 return '%s(%s)' % (self.__class__.__name__, 1058 ','.join(str(s) for s in self.selectors))
1059 1060 @classmethod
1061 - def merge_selectors(cls, selectors):
1062 """deal with selector instanciation when necessary and merge 1063 multi-selectors if possible: 1064 1065 AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4)) 1066 ==> AndPredicate(sel1, sel2, sel3, sel4) 1067 """ 1068 merged_selectors = [] 1069 for selector in selectors: 1070 # XXX do we really want magic-transformations below? 1071 # if so, wanna warn about them? 1072 if isinstance(selector, types.FunctionType): 1073 selector = objectify_predicate(selector)() 1074 if isinstance(selector, type) and issubclass(selector, Predicate): 1075 selector = selector() 1076 assert isinstance(selector, Predicate), selector 1077 if isinstance(selector, cls): 1078 merged_selectors += selector.selectors 1079 else: 1080 merged_selectors.append(selector) 1081 return merged_selectors
1082
1083 - def search_selector(self, selector):
1084 """search for the given selector or selector instance (or tuple of 1085 selectors) in the selectors tree. Return None if not found 1086 """ 1087 for childselector in self.selectors: 1088 if childselector is selector: 1089 return childselector 1090 found = childselector.search_selector(selector) 1091 if found is not None: 1092 return found 1093 # if not found in children, maybe we are looking for self? 1094 return super(MultiPredicate, self).search_selector(selector)
1095
1096 1097 -class AndPredicate(MultiPredicate):
1098 """and-chained selectors"""
1099 - def __call__(self, cls, *args, **kwargs):
1100 score = 0 1101 for selector in self.selectors: 1102 partscore = selector(cls, *args, **kwargs) 1103 if not partscore: 1104 return 0 1105 score += partscore 1106 return score
1107
1108 1109 -class OrPredicate(MultiPredicate):
1110 """or-chained selectors"""
1111 - def __call__(self, cls, *args, **kwargs):
1112 for selector in self.selectors: 1113 partscore = selector(cls, *args, **kwargs) 1114 if partscore: 1115 return partscore 1116 return 0
1117
1118 -class NotPredicate(Predicate):
1119 """negation selector"""
1120 - def __init__(self, selector):
1121 self.selector = selector
1122
1123 - def __call__(self, cls, *args, **kwargs):
1124 score = self.selector(cls, *args, **kwargs) 1125 return int(not score)
1126
1127 - def __str__(self):
1128 return 'NOT(%s)' % self.selector
1129
1130 1131 -class yes(Predicate): # pylint: disable=C0103
1132 """Return the score given as parameter, with a default score of 0.5 so any 1133 other selector take precedence. 1134 1135 Usually used for objects which can be selected whatever the context, or 1136 also sometimes to add arbitrary points to a score. 1137 1138 Take care, `yes(0)` could be named 'no'... 1139 """
1140 - def __init__(self, score=0.5):
1141 self.score = score
1142
1143 - def __call__(self, *args, **kwargs):
1144 return self.score
1145
1146 1147 # deprecated stuff ############################################################# 1148 1149 @deprecated('[lgc 0.59] use Registry.objid class method instead') 1150 -def classid(cls):
1151 return '%s.%s' % (cls.__module__, cls.__name__)
1152
1153 @deprecated('[lgc 0.59] use obj_registries function instead') 1154 -def class_registries(cls, registryname):
1155 return obj_registries(cls, registryname)
1156