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