
    c&                     X   U d Z dZddlZddlZddlZddlZddlZddlm	Z	m
Z
 ddlmZmZmZ ddlmZmZmZmZmZ ddlmZ ddlmZ dd	lmZmZmZmZmZ dd
lmZ ej>                  dk\  rddlm Z  ddl!m"Z" ddl#m$Z$ ddl%m&Z& ddl'm(Z( dedefdZ)i Z*ee+ef   e,d<   deddfdZ- G d de.      Z/ G d de0e/      Z1 G d de1      Z2 G d de2      Z3 G d  d!e2      Z4 G d" d#e1      Z5 G d$ d%e1      Z6 e(d&      d'        Z7 e(d(      d)        Z8 G d* d+e9      Z: G d, d-e:      Z; G d. d/e:      Z< G d0 d1e:      Z= G d2 d3e:      Z>dLd4e?d5ee   de?fd6Z@	 dLd4ee?   d5ee   d7eeee?e?f   ef      deee?e?f   eee?e?f      f   fd8ZA G d9 d:e0      ZB G d; d<eB      ZCej>                  dk\  r e d=ee+eee   d>eed>d?      ZDnee?eee+ee   d>ef   f   ZD G d@ d>eE      ZFdLdAedBee   dee?   fdCZG G dD dEeE      ZH e$eH edF              e$eF edG             daIdH ZJdI ZK G dJ dKe0      ZLy)Ma  This module provides bases for predicates dispatching (the pattern in use
here is similar to what's refered as multi-dispatch or predicate-dispatch in the
literature, though a bit different since the idea is to select across different
implementation 'e.g. classes), not to dispatch a message to a function or
method. It contains the following classes:

* :class:`RegistryStore`, the top level object which loads implementation
  objects and stores them into registries. You'll usually use it to access
  registries and their contained objects;

* :class:`Registry`, the base class which contains objects semantically grouped
  (for instance, sharing a same API, hence the 'implementation' name). You'll
  use it to select the proper implementation according to a context. Notice you
  may use registries on their own without using the store.

.. Note::

  implementation objects are usually designed to be accessed through the
  registry and not by direct instantiation, besides to use it as base classe.

The selection procedure is delegated to a selector, which is responsible for
scoring the object according to some context. At the end of the selection, if an
implementation has been found, an instance of this class is returned. A selector
is built from one or more predicates combined together using AND, OR, NOT
operators (actually `&`, `|` and `~`). You'll thus find some base classes to
build predicates:

* :class:`Predicate`, the abstract base predicate class

* :class:`AndPredicate`, :class:`OrPredicate`, :class:`NotPredicate`, which you
  shouldn't have to use directly. You'll use `&`, `|` and '~' operators between
  predicates directly

* :func:`objectify_predicate`

You'll eventually find one concrete predicate: :class:`yes`

.. autoclass:: RegistryStore
.. autoclass:: Registry

Predicates
----------
.. autoclass:: Predicate
.. autofunction:: objectify_predicate
.. autoclass:: yes
.. autoclass:: AndPredicate
.. autoclass:: OrPredicate
.. autoclass:: NotPredicate

Debugging
---------
.. autoclass:: traced_selection

Exceptions
----------
.. autoclass:: RegistryException
.. autoclass:: RegistryNotFound
.. autoclass:: ObjectNotFound
.. autoclass:: NoSelectableObject
zrestructuredtext en    N)listdirstat)joinisdirexists)DictTypeOptionalUnionSequence)	getLogger)warn)ListTupleAnyIterableCallable)
ModuleType)      )	TypedDict)modpath_from_file)set_log_methods)classproperty)callable_deprecatedselector_funcreturnc                 \     t         j                  t        f j                   fdd      S )aX  Most of the time, a simple score function is enough to build a selector.
    The :func:`objectify_predicate` decorator turn it into a proper selector
    class::

        @objectify_predicate
        def one(cls, req, rset=None, **kwargs):
            return 1

        class MyView(View):
            __select__ = View.__select__ & one()

    c                      |i |S N )selfakwr   s      9/usr/lib/python3/dist-packages/logilab/common/registry.py<lambda>z%objectify_predicate.<locals>.<lambda>   s    }a/F2/F     )__doc____call__)type__name__	Predicater(   )r   s   `r%   objectify_predicater-   n   s1     	$,,F	
 r'   _PREDICATES	decoratorc                     t         j                         D ]`  }d|j                  vrt               |_        | |j                  v r/|j                  j                  |         | |j                        |_        b y )N_decorators)r.   values__dict__setr1   addr)   )r/   	predicates     r%   wrap_predicatesr7      sj     '') ;		 2 22$'EI!	---!!),&y'9'9:	;r'   c                       e Zd Zd Zy)PredicateMetaClassc                     t        j                  | g|i |}t        j                  |d       }|t        t        |      <   |S )Nc                 >    t         j                  t        |             S r    )r.   popid)ps    r%   r&   z,PredicateMetaClass.__new__.<locals>.<lambda>   s    koobe.D r'   )r*   __new__weakrefproxyr.   r=   )mcsargskwargsinstrA   s        r%   r?   zPredicateMetaClass.__new__   s>    ||C1$1&1d$DE!&BuIr'   N)r+   
__module____qualname__r?   r!   r'   r%   r9   r9      s    r'   r9   c                   ~    e Zd ZdZed        Zdd ded    fdZd ZddZ	ddZ
dd	Zdd
ZddZddZd Zd Zd Zy)r,   a  base class for selector classes providing implementation
    for operators ``&``, ``|`` and  ``~``

    This class is only here to give access to binary operators, the selector
    logic itself should be implemented in the :meth:`__call__` method. Notice it
    should usually accept any arbitrary arguments (the context), though that may
    vary depending on your usage of the registry.

    a selector is called to help choosing the correct object for a
    particular context by returning a score (`int`) telling how well
    the implementation given as first argument fit to the given context.

    0 score means that the class doesn't apply.
    c                 .    | j                   j                  S r    	__class__r+   r"   s    r%   	func_namezPredicate.func_name   s     ~~&&&r'   selectorr   c                 l    | |u r| S t        |t              st        |t              rt        | |      r| S y)zsearch for the given selector, selector instance or tuple of
        selectors in the selectors tree. Return None if not found.
        N)
isinstancer*   tupler"   rN   s     r%   search_selectorzPredicate.search_selector   s;     8Kx&*Xu*E:(L
 Kr'   c                 .    | j                   j                  S r    rJ   rL   s    r%   __str__zPredicate.__str__   s    ~~&&&r'   c                     t        | |      S r    AndPredicater"   others     r%   __and__zPredicate.__and__       D%((r'   c                     t        ||       S r    rW   rY   s     r%   __rand__zPredicate.__rand__   s    E4((r'   c                     t        | |      S r    rW   rY   s     r%   __iand__zPredicate.__iand__   r\   r'   c                     t        | |      S r    OrPredicaterY   s     r%   __or__zPredicate.__or__       4''r'   c                     t        ||       S r    rb   rY   s     r%   __ror__zPredicate.__ror__   s    5$''r'   c                     t        | |      S r    rb   rY   s     r%   __ior__zPredicate.__ior__   re   r'   c                     t        |       S r    )NotPredicaterL   s    r%   
__invert__zPredicate.__invert__   s    D!!r'   c                 2    t        d| j                  z        S )Nz;selector %s must implement its logic in its __call__ method)NotImplementedErrorrK   )r"   clsrC   rD   s       r%   r)   zPredicate.__call__   s    "Lt~~]
 	
r'   c                 J    d| j                   j                  t        |       fz  S )Nz<Predicate %s at %x>)rK   r+   r=   rL   s    r%   __repr__zPredicate.__repr__   s     %)@)@"T((KKKr'   N)rZ   r,   r   rX   )rZ   r,   r   rc   )rZ   r,   )r+   rF   rG   r(   propertyrM   r
   rS   rU   r[   r^   r`   rd   rg   ri   rl   r)   rq   r!   r'   r%   r,   r,      sf     ' '
 
8M 
')))((("


Lr'   r,   )	metaclassc                   n     e Zd ZdZdeddfdZd Zedee	   de
e	   fd       Zde	dee	   f fd	Z xZS )
MultiPredicatez(base class for compound selector classes	selectorsr   Nc                 0    | j                  |      | _        y r    )merge_selectorsrv   )r"   rv   s     r%   __init__zMultiPredicate.__init__   s    --i8r'   c                 z    | j                   j                  ddj                  d | j                  D              dS )N(,c              3   2   K   | ]  }t        |        y wr    )str).0ss     r%   	<genexpr>z)MultiPredicate.__str__.<locals>.<genexpr>   s     <\SV<\s   ))rK   r+   r   rv   rL   s    r%   rU   zMultiPredicate.__str__   s*    >>22CHH<\T^^<\4\]]r'   c                 B   g }|D ]  }t        |t        j                        r t        |             }t        |t              rt        |t              r |       }t        |t              sJ |       t        ||       r||j                  z  }|j                  |        |S )zdeal with selector instanciation when necessary and merge
        multi-selectors if possible:

        AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4))
        ==> AndPredicate(sel1, sel2, sel3, sel4)
        )	rP   typesFunctionTyper-   r*   
issubclassr,   rv   append)ro   rv   merged_selectorsrN   s       r%   rx   zMultiPredicate.merge_selectors   s     ! 	2H (E$6$678.x8:(D)j9.M#:h	2<H<2(C( H$6$66  ''1	2  r'   rN   c                     | j                   D ]!  }||u r|c S |j                  |      }||c S  t        t        |   |      S )zsearch for the given selector or selector instance (or tuple of
        selectors) in the selectors tree. Return None if not found
        )rv   rS   superru   )r"   rN   childselectorfoundrK   s       r%   rS   zMultiPredicate.search_selector   sU     "^^ 	M($$!11(;E 	 ^T:8DDr'   )r+   rF   rG   r(   r   ry   rU   classmethodr   r,   r   rx   r
   rS   __classcell__rK   s   @r%   ru   ru      sk    293 94 9^  (;  Y    ,E	 Ehy6I E Er'   ru   c                   .    e Zd ZdZdee   dededefdZy)rX   zand-chained selectorsro   rC   rD   r   c                 V    d}| j                   D ]  } ||g|i |}|s y||z  } |S Nr   rv   )r"   ro   rC   rD   scorerN   	partscores          r%   r)   zAndPredicate.__call__  sE     	H 6t6v6IYE		
 r'   Nr+   rF   rG   r(   r
   r   intr)   r!   r'   r%   rX   rX     s*    HSM #   r'   rX   c                   .    e Zd ZdZdee   dededefdZy)rc   zor-chained selectorsro   rC   rD   r   c                 J    | j                   D ]  } ||g|i |}|s|c S  yr   r   )r"   ro   rC   rD   rN   r   s         r%   r)   zOrPredicate.__call__  s7     	!H 6t6v6I  	! r'   Nr   r!   r'   r%   rc   rc     s*    HSM #   r'   rc   c                   "    e Zd ZdZd Zd Zd Zy)rk   znegation selectorc                     || _         y r    rN   rR   s     r%   ry   zNotPredicate.__init__(  s	     r'   c                 F     | j                   |g|i |}t        |       S r    )rN   r   )r"   ro   rC   rD   r   s        r%   r)   zNotPredicate.__call__+  s(    c3D3F3u9~r'   c                      d| j                   z  S )NzNOT(%s)r   rL   s    r%   rU   zNotPredicate.__str__/  s    4==((r'   N)r+   rF   rG   r(   ry   r)   rU   r!   r'   r%   rk   rk   %  s    !)r'   rk   c                   (    e Zd ZdZddeddfdZd Zy)yesa$  Return the score given as parameter, with a default score of 0.5 so any
    other selector take precedence.

    Usually used for objects which can be selected whatever the context, or
    also sometimes to add arbitrary points to a score.

    Take care, `yes(0)` could be named 'no'...
    r   r   Nc                     || _         y r    r   )r"   r   s     r%   ry   zyes.__init__=  s	    
r'   c                     | j                   S r    r   )r"   rC   rD   s      r%   r)   zyes.__call__@  s    zzr'   )g      ?)r+   rF   rG   r(   floatry   r)   r!   r'   r%   r   r   3  s    e d r'   r   z2[lgc 0.59] use Registry.objid class method insteadc                 8    | j                   d| j                  S )N.)rF   r+   ro   s    r%   classidr   G  s    nncll33r'   z.[lgc 0.59] use obj_registries function insteadc                     t        | |      S r    )obj_registriesro   registrynames     r%   class_registriesr   L  s    #|,,r'   c                       e Zd ZdZy)RegistryExceptionz"Base class for registry exception.Nr+   rF   rG   r(   r!   r'   r%   r   r   Q  s    ,r'   r   c                       e Zd ZdZy)RegistryNotFoundzaRaised when an unknown registry is requested.

    This is usually a programming/typo error.
    Nr   r!   r'   r%   r   r   U      r'   r   c                       e Zd ZdZy)ObjectNotFoundzvRaised when an unregistered object is requested.

    This may be a programming/typo or a misconfiguration error.
    Nr   r!   r'   r%   r   r   \  r   r'   r   c                       e Zd ZdZd Zd Zy)NoSelectableObjectz8Raised when no object is selectable for a given context.c                 .    || _         || _        || _        y r    )rC   rD   objects)r"   rC   rD   r   s       r%   ry   zNoSelectableObject.__init__f  s    	r'   c                 p    d| j                   d| j                  j                         d| j                  S )Nzargs: z
, kwargs: z
candidates: )rC   rD   keysr   rL   s    r%   rU   zNoSelectableObject.__str__k  s*    IIKKLL
 	
r'   N)r+   rF   rG   r(   ry   rU   r!   r'   r%   r   r   c  s    B

r'   r   c                       e Zd ZdZy)SelectAmbiguityzTRaised when several objects compete at selection time with an equal
    score.

    Nr   r!   r'   r%   r   r   s  r   r'   r   path	extrapathc                 l    t        | |      }|d   dk(  r|j                          dj                  |      S )Nry   r   )r   r<   r   )r   r   modpaths      r%   _modname_from_pathr   z  s4    i0G r{j 88Gr'   _toloadc                 T   |t        | t              sJ i g f}| D ]  }t        |      rEt        t	        |d            r0t        |      D cg c]  }t	        ||       }}t        |||       S|dd dk(  s\t        ||      }||d   |<   |d   j                  ||f        |S c c}w )zgReturn a dictionary of <modname>: <modpath> and an ordered list of
    (file, module name) to load
    N__init__.py.pyr      )	rP   listr   r   r   r   _toload_infor   r   )r   r   r   	fileordirfnamesubfilesmodnames          r%   r   r     s     $%%%b& 
4	tI}'E F<CI<NO5Y.OHO9g6rs^u$(I>G"+GAJwAJy'23
4 N Ps   B%c                   ~    e Zd ZU dZdZee   ed<   dZee   ed<   dZ	e
deef   ed<   dZede
ee   ef   fd       Zy)	RegistrableObjecta9  This is the base class for registrable objects which are selected
    according to a context.

    :attr:`__registry__`
      name of the registry for this object (string like 'views',
      'templates'...). You may want to define `__registries__` directly if your
      object should be registered in several registries.

    :attr:`__regid__`
      object's identifier in the registry (string like 'main',
      'primary', 'folder_box')

    :attr:`__select__`
      class'selector

    Moreover, the `__abstract__` attribute may be set to True to indicate that a
    class is abstract and should not be registered.

    You don't have to inherit from this class to put it in a registry (having
    `__regid__` and `__select__` is enough), though this is needed for classes
    that should be automatically registered.
    N__registry__	__regid__
__select__Tr   c                 6    | j                   y| j                   fS )Nr!   )r   r   s    r%   __registries__z RegistrableObject.__registries__  s     #  ""r'   )r+   rF   rG   r(   r   r
   r~   __annotations__r   r   r   r,   __abstract__r   r   r   r!   r'   r%   r   r     sg    . #'L(3-&#Ix}#.2JdC*+2L#uU3Z%67 # #r'   r   c                   >     e Zd ZdZ fdZddee   ddf fdZ xZS )RegistrableInstancez`Inherit this class if you want instances of the classes to be
    automatically registered.
    c                    |j                  dd      }t        t        |   |       }|Wt	        dj                  | j                        t               t        j                  d      d   d   }t        |      |_        |S ||_        |S )zzAdd a __module__ attribute telling the module where the instance was
        created, for automatic registration.
        rF   Nz'instantiate {} with __module__=__name__   )limitr   )r<   r   r   r?   r   formatr+   DeprecationWarningtbextract_stackr   rF   )ro   rC   rD   moduleobjfilepathrK   s         r%   r?   zRegistrableInstance.__new__  s     L$/'5c:><CCCLLQ"
 ''a03A6H/9CN 
 $CN
r'   NrF   r   c                 *    t         t        |           y r    )r   r   ry   )r"   rF   rK   s     r%   ry   zRegistrableInstance.__init__  s    !413r'   r    )	r+   rF   rG   r(   r?   r
   r~   ry   r   r   s   @r%   r   r     s)    $48C= 4D 4 4r'   r   SelectBestReportRegistry)all_objects	end_scorewinnerswinnerr"   rC   rD   registryc            	            e Zd ZdZdeddf fdZ fdZedede	fd       Z
edede	fd	       Zdd
Zddedee   deddfdZd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd xZxZxZxZxZZ xZS )r   a  The registry store a set of implementations associated to identifier:

    * to each identifier are associated a list of implementations

    * to select an implementation of a given identifier, you should use one of the
      :meth:`select` or :meth:`select_or_none` method

    * to select a list of implementations for a context, you should use the
      :meth:`possible_objects` method

    * dictionary like access to an identifier will return the bare list of
      implementations for this identifier.

    To be usable in a registry, the only requirement is to have a `__select__`
    attribute.

    At the end of the registration process, the :meth:`__registered__`
    method is called on each registered object which have them, given the
    registry in which it's registered as argument.

    Registration methods:

    .. automethod:: register
    .. automethod:: unregister

    Selection methods:

    .. automethod:: select
    .. automethod:: select_or_none
    .. automethod:: possible_objects
    .. automethod:: object_by_id
    	debugmoder   Nc                 F    t         t        |           || _        g | _        y r    )r   r   ry   r   _select_listenersr"   r   rK   s     r%   ry   zRegistry.__init__  s    h&("KMr'   c                     	 t         t        |   |      S # t        $ r* t	        |      }t        j                         d   |_        |w xY w)z]return the registry (list of implementation objects) associated to
        this name
        r   )r   r   __getitem__KeyErrorr   sysexc_info__traceback__r"   nameexcrK   s      r%   r   zRegistry.__getitem__  sK    	44T:: 	 &C #r 2CI		    3A	r   c                 B    |j                   d| j                  |      S )z@returns a unique identifier for an object stored in the registryr   )rF   objnamero   r   s     r%   objidzRegistry.objid(  s     ..#++c*:;;r'   c                 .    t        |dt        |            S )z<returns a readable name for an object stored in the registryr+   )getattrr=   r   s     r%   r   zRegistry.objname-  s     sJ300r'   c                     | j                         D ]!  }|D ]  }t        |dd      }|s ||         # | j                  rt        t               yy)zWcall method __registered__() on registered objects when the callback
        is defined__registered__N)r2   r  r   r7   _lltrace)r"   r   	objectcls
registereds       r%   initialization_completedz!Registry.initialization_completed2  sX     {{} 	%G$ %	$Y0@$G
t$%	%
 >>H% r'   oidclearc                    d|j                   vsJ |       |j                  sJ |       |xs |j                  }|s
J d|z         |rg x}| |<   n| j                  |g       }||vs
J d|z         |j	                  |       y)z,base method to add an object in the registryr   zKno explicit name supplied to register object %s, which has no __regid__ setzobject %s is already registeredN)r3   r   r   
setdefaultr   )r"   r   r  r	  r   s        r%   registerzRegistry.register=  s    S\\16361~~"s"~"S]] 	
\_bb	
s "$$Gd3iooc2.G'!J#Ds#JJ!sr'   c                 :   t        |t              s| j                  |      }||us
J d|z         | j                  |j                  d      }t        |      D ]  \  }}| j                  |      |k(  s||=  n | j                  d||       | j                  |       y)z$remove <replaced> and register <obj>z!replacing an object by itself: %sr!   z3trying to replace %s that is not registered with %sN)rP   r~   r   getr   	enumeratewarningr  )r"   r   replacedregistered_objsindexr  s         r%   register_and_replacezRegistry.register_and_replaceL  s    
 (C(zz(+H("M$G#$MM"((3=="5!*?!; 	_E:zz*%1#E*	_
 LLNPXZ]^cr'   c                     | j                  |      }|j                  }| j                  |d      D ],  }| j                  |      |k(  s| |   j                  |        y | j	                  d||       y)z&remove object <obj> from this registryr!   z)can't remove %s, no id %s in the registryN)r   r   r  remover  )r"   r   r   r  r  s        r%   
unregisterzRegistry.unregister^  so    

3mm((3+ 	RJ zz*%.S	  ,	R LLDeSQr'   c                 >    g }| j                         D ]  }||z  }	 |S )z6return a list containing all objects in this registry.)r2   )r"   resultobjss      r%   r   zRegistry.all_objectsk  s*    KKM 	DdNF	r'   c                 L    | |   }t        |      dk(  sJ |        |d   |i |S )a  return object with the `oid` identifier. Only one object is expected
        to be found.

        raise :exc:`ObjectNotFound` if there are no object with id `oid` in this
        registry

        raise :exc:`AssertionError` if there is more than one object there
        r   r   )len)r"   r  rC   rD   r   s        r%   object_by_idzRegistry.object_by_idt  s:     s)7|q )') wqz4*6**r'   c                 \     | j                   | |   g|i |}|t        ||| |         |S )a  return the most specific object among those with the given oid
        according to the given context.

        raise :exc:`ObjectNotFound` if there are no object with id `oid` in this
        registry

        raise :exc:`NoSelectableObject` if no object can be selected
        )_select_bestr   )r"   _Registry__oidrC   rD   r   s        r%   selectzRegistry.select  sA      dU=d=f=;$T64;??
r'   c                 T    	  | j                   | |   g|i |S # t        $ r Y yw xY w)zreturn the most specific object among those with the given oid
        according to the given context, or None if no object applies.
        N)r  r   )r"   r   rC   rD   s       r%   select_or_nonezRegistry.select_or_none  s:    	$4$$T%[B4B6BB 		s    	''c              /   p   K   | j                         D ]  } | j                  |g|i |}|| ! yw)z^return an iterator on possible objects in this registry for the given
        context
        N)r2   r  )r"   rC   rD   r   r   s        r%   possible_objectszRegistry.possible_objects  sF      {{} 	G#$##G=d=f=C{I		s   46c                 :    | j                   j                  |       y)a{  
        Add a listener to the list of one parameters callables (function/method)
        that will be called everytime the selection Registry._select_best is
        done and they will recieve a dict of the following form::

          {"all_objects": [], "end_score": 0, "winners": [], "winner": None or winner,
          "self": self, "args": args, "kwargs": kwargs, }
        N)r   r   )r"   listeners     r%   add_select_best_listenerz!Registry.add_select_best_listener  s     	%%h/r'   c           	         | j                   r| g dg d| ||d}d\  }}|D ]c  } |j                  |g|i |}| j                   rd   j                  ||d       ||kD  r||g}}G|dkD  sM||k(  sS|j                  |       e | j                   rA|r|j                         ng d<   |r|d   n||d<   ||d	<   | j                   D ]
  }	 |	|        |yt	        |      d
kD  rOd}
| j
                  rt        |
|||j                         fz        | j                  |
|||j                                | j                  |d   ||      S )aQ  return an instance of the most specific object according
        to parameters

        return None if not object apply (don't raise `NoSelectableObject` since
        it's costly when searching objects using `possible_objects`
        (e.g. searching for hooks).

        Every callable stored in Registry._select_listeners will be called
        once the selection is done and will recieve a dict of the following form::

          {"all_objects": [], "end_score": 0, "winners": [], "winner": None or winner,
          "self": self, "args": args, "kwargs": kwargs, "registry": self}
        r   N)r   r   r   r   r   r"   rC   rD   )r   Nr   )objectr   r   r   r   r   z+select ambiguity: %s
(args: %s, kwargs: %s))
r   r   r   copyr  r   r   r   errorselected)r"   r   rC   rD   select_best_reportr   r   r   objectscorer'  msgs              r%   r  zRegistry._select_best  s|    !! ! 	4 !w 
	$C(#..>t>v>K%%"=188CR]9^_U"!,sewq[E%9s#
	$ !!>EGLLN2y)9@71:gx(.3{+ 22 -+,- ?w<!@C~~%cWdFKKM,J&JKKJJsGT6;;=9 }}WQZv66r'   c                      ||i |S )z=override here if for instance you don't want "instanciation" r!   )r"   r   rC   rD   s       r%   r-  zRegistry.selected  s    t&v&&r'   c                      y r    r!   r0  r#   r$   s      r%   r&   zRegistry.<lambda>      r'   r   N)NF) r+   rF   rG   r(   boolry   r   r   r   r~   r   r   r  r
   r  r  r  r   r  r!  r#  r%  r(  r  r-  infor  r,  critical	exceptiondebugr   r   s   @r%   r   r     s    BN$ N4 N
	 < < < < 1# 1# 1 1	&C hsm 4 TX $R+	0=7~' =WVDV7VUVXV	Er'   ro   r   c                 $    |r|fS | j                   S )z5return a tuple of registry names (see __registries__))r   r   s     r%   r   r     s    r'   c                       e Zd ZU dZd*deddf fdZd+dZdedef fdZ	deiZ
eedef   ef   ed	<   d
edefdZd
edefdZd,dedededdfdZ	 	 	 d-dedee   dee   deddf
dZd.dZd.dZ	 d.dee   dee   deeeef      fdZ ed      d.dee   dee   ddfd       Zdee   ddfdZd+dZd edee    fd!Z!d" Z"d ededdfd#Z#d$e$ddfd%Z%	 d,ded&edee   ddfd'Z&e'dedefd(       Z(d) xZ)xZ*xZ+xZ,xZ-Z. xZ/S )/RegistryStoreaV  This class is responsible for loading objects and storing them
    in their registry which is created on the fly as needed.

    It handles dynamic registration of objects and provides a
    convenient api to access them. To be recognized as an object that
    should be stored into one of the store's registry
    (:class:`Registry`), an object must provide the following
    attributes, used control how they interact with the registry:

    :attr:`__registries__`
      list of registry names (string like 'views', 'templates'...) into which
      the object should be registered

    :attr:`__regid__`
      object identifier in the registry (string like 'main',
      'primary', 'folder_box')

    :attr:`__select__`
      the object predicate selectors

    Moreover, the :attr:`__abstract__` attribute may be set to `True`
    to indicate that an object is abstract and should not be registered
    (such inherited attributes not considered).

    .. Note::

      When using the store to load objects dynamically, you *always* have
      to use **super()** to get the methods and attributes of the
      superclasses, and not use the class identifier. If not, you'll get into
      trouble at reload time.

      For example, instead of writing::

          class Thing(Parent):
              __regid__ = 'athing'
              __select__ = yes()

              def f(self, arg1):
                  Parent.f(self, arg1)

      You must write::

          class Thing(Parent):
              __regid__ = 'athing'
              __select__ = yes()

              def f(self, arg1):
                  super(Thing, self).f(arg1)

    Controlling object registration
    -------------------------------

    Dynamic loading is triggered by calling the :meth:`register_modnames`
    method, given a list of modules names to inspect.

    .. automethod:: register_modnames

    For each module, by default, all compatible objects are registered
    automatically. However if some objects come as replacement of
    other objects, or have to be included only if some condition is
    met, you'll have to define a `registration_callback(vreg)`
    function in the module and explicitly register **all objects** in
    this module, using the api defined below.


    .. automethod:: RegistryStore.register_all
    .. automethod:: RegistryStore.register_and_replace
    .. automethod:: RegistryStore.register
    .. automethod:: RegistryStore.unregister

    .. Note::
        Once the function `registration_callback(vreg)` is implemented in a
        module, all the objects from this module have to be explicitly
        registered as it disables the automatic object registration.


    Examples:

    .. sourcecode:: python

       def registration_callback(store):
          # register everything in the module except BabarClass
          store.register_all(globals().values(), __name__, (BabarClass,))

          # conditionally register BabarClass
          if 'babar_relation' in store.schema:
              store.register(BabarClass)

    In this example, we register all application object classes defined in the module
    except `BabarClass`. This class is then registered only if the 'babar_relation'
    relation type is defined in the instance schema.

    .. sourcecode:: python

       def registration_callback(store):
          store.register(Elephant)
          # replace Babar by Celeste
          store.register_and_replace(Celeste, Babar)

    In this example, we explicitly register classes one by one:

    * the `Elephant` class
    * the `Celeste` to replace `Babar`

    If at some point we register a new appobject class in this module, it won't be
    registered at all without modification to the `registration_callback`
    implementation. The first example will register it though, thanks to the call
    to the `register_all` method.

    Controlling registry instantiation
    ----------------------------------

    The `REGISTRY_FACTORY` class dictionary allows to specify which class should
    be instantiated for a given registry name. The class associated to `None`
    key will be the class used when there is no specific class for a name.
    r   r   Nc                 8    t         t        |           || _        y r    )r   r=  ry   r   r   s     r%   ry   zRegistryStore.__init__r  s    mT+-"r'   c                 \    | j                         D ]  }|j                           i | _        y)z*clear all registries managed by this storeN)r2   r	  _lastmodifs)r"   subdicts     r%   resetzRegistryStore.resetv  s*     {{} 	GMMO	+-r'   r   c                     	 t         t        |   |      S # t        $ r* t	        |      }t        j                         d   |_        |w xY w)zZreturn the registry (dictionary of class objects) associated to
        this name
        r   )r   r=  r   r   r   r   r   r   r   s      r%   r   zRegistryStore.__getitem__}  sK    	9$?? 	"4(C #r 2CI	r   REGISTRY_FACTORYregidc                 ^    	 | j                   |   S # t        $ r | j                   d   cY S w xY w)zWreturn existing registry named regid or use factory to create one and
        return itN)rD  r   r"   rE  s     r%   registry_classzRegistryStore.registry_class  s8    	/((// 	/((..	/s    ,,c                 ~    	 | |   S # t         $ r,  | j                  |      | j                        | |<   | |   cY S w xY wr    )r   rH  r   rG  s     r%   r  zRegistryStore.setdefault  sI    	; 	4$--e4T^^DDK;	s    2<<r   r   
butclassesc                     t        |t              s
J d|z         |D ]^  }| j                  |      s|j                  |k(  s%||vs*t        |t              r| j                  |||       N| j                  |       ` y)a~  register registrable objects into `objects`.

        Registrable objects are properly configured subclasses of
        :class:`RegistrableObject`.  Objects which are not defined in the module
        `modname` or which are in `butclasses` won't be registered.

        Typical usage is:

        .. sourcecode:: python

            store.register_all(globals().values(), __name__, (ClassIWantToRegisterExplicitly,))

        So you get partially automatic registration, keeping manual registration
        for some object (to use
        :meth:`~logilab.common.registry.RegistryStore.register_and_replace` for
        instance).
        z8modname expected to be a module name (ie string), got %rN)rP   r~   is_registrablerF   r*   _load_ancestors_then_objectr  )r"   r   r   rJ  r   s        r%   register_allzRegistryStore.register_all  s}    $ '3' 	
FP	
'  	'C""3'CNNg,E#U_J_c4(44Wc:NMM#&	'r'   r   r   r  r	  c                    |j                   j                  d      rJ |       t        ||      D ]  }| j                  |      }|j	                  |||       | j                  d|j                  |      ||xs |j                         || j                  j                  |j                  i       |j                  |      <    y)a  register `obj` implementation into `registryname` or
        `obj.__registries__` if not specified, with identifier `oid` or
        `obj.__regid__` if not specified.

        If `clear` is true, all objects with the same identifier will be
        previously unregistered.
        r   )r  r	  zregister %s in %s['%s']N)r3   r  r   r  r  r:  r   r   _loadedmodsrF   r   )r"   r   r   r  r	  r   s         r%   r  zRegistryStore.register  s     <<##N38S83*3= 	WL|4Hcs%8JJ)8+;+;C+@,PSPdWZWdWd TWD'';HNN3<OP	Wr'   c                     t        ||      D ]E  }| |   }|j                  |       | j                  d|j                  |      ||j                         G y)zsunregister `obj` object from the registry `registryname` or
        `obj.__registries__` if not specified.
        zunregister %s from %s['%s']N)r   r  r:  r   r   )r"   r   r   r   s       r%   r  zRegistryStore.unregister  sY     +3= 	LL)H$JJ-x/?/?/DlTWTaTa	r'   c           
          t        ||      D ]V  }| |   }|j                  ||       | j                  d|j                  |      ||j                  |j                  |             X y)zregister `obj` object into `registryname` or
        `obj.__registries__` if not specified. If found, the `replaced` object
        will be unregistered first (else a warning will be issued as it is
        generally unexpected).
        z%register %s in %s['%s'] instead of %sN)r   r  r:  r   r   )r"   r   r  r   r   s        r%   r  z"RegistryStore.register_and_replace  si     +3= 		LL)H))#x8JJ7  %  *		r'   r   r   c                 \    | j                          t        ||      \  | _        }i | _        |S )zbreset registry and walk down path to return list of (path, name)
        file modules to be loaded)rB  r   _toloadmodsrP  )r"   r   r   filemodss       r%   init_registrationzRegistryStore.init_registration  s0     	

%1$	%B"( 8:r'   zuse register_modnames() insteadc                     | j                  ||      }|D ]  \  }}| j                  ||        | j                          y)z.register all objects found walking down <path>N)rV  	load_filer  )r"   r   r   rU  r   r   s         r%   register_objectszRegistryStore.register_objects  sD    
 ))$	:!) 	.HgNN8W-	.%%'r'   modnamesc                 X   | j                          i | _        i | _        g }|D ]Y  }t        j                  |      }|J |j                         }|dd dv r|dd }|| j                  |<   |j                  ||f       [ |D ]  \  }}| j                  ||        | j                          y)z(register all objects found in <modnames>N)z.pycz.pyor   )	rB  rP  rT  pkgutilfind_loaderget_filenamer   rX  r  )r"   rZ  toloadr   loaderr   s         r%   register_modnameszRegistryStore.register_modnames  s    

 
	/G((1F%%% **,H} 00#CR=(0DW%MM8W-.
	/ "( 	.HgNN8W-	.%%'r'   c                 N    | j                         D ]  }|j                           y)z7call initialization_completed() on all known registriesN)r2   r  )r"   regs     r%   r  z&RegistryStore.initialization_completed  s#    ;;= 	+C((*	+r'   r   c                 b    	 t        |      d   S # t        $ r | j                  d|       Y yw xY w)z+return the modification date of a file pathz3Unable to load %s. It is likely to be a backup fileN)r   OSErrorr  )r"   r   s     r%   _mdatezRegistryStore._mdate"  s8    	>"%% 	LLNPXY	s    ..c           
      b   | j                   }|D ]  }t        |      rJt        t        |d            r5| j	                  t        |      D cg c]  }t        ||       c}      sV y|dd dk(  sa| j                  |      }|ud|v rz||vs	||   |k  s| j                  d|        y yc c}w )	z\return True if something module changed and the registry should be
        reloaded
        r   Tr   Nr   flymakez File %s changed since last visitF)r@  r   r   r   is_reload_neededr   rh  r7  )r"   r   
lastmodifsr   r   mdates         r%   rk  zRegistryStore.is_reload_needed+  s     %%
 	 IYF4	=+I$J((gV_N`)aU$y%*@)ab235(I.=)+J.*Y2G%2OII@)L	   *bs   
B,
c                 L   || j                   v ryi | j                   |<   | j                  |      }|yd|v ry|| j                  |<   t        j                  dk  rt        |t              st        |      }t        ||j                  d      dd       }| j                  |       y)z4load registrable objects (if any) from a python fileNrj  )r   r   r   )fromlist)
rP  rh  r@  r   version_inforP   r~   
__import__splitload_module)r"   r   r   rm  r   s        r%   rX  zRegistryStore.load_file@  s    d&&&$&!H%=(" &+"d":gs+C'lGGgmmC.@".EF r'   r   c                     | j                  d|j                  |j                         t        |d      r|j	                  |        y| j                  t        |      j                         |j                         y)a\  Automatically handle module objects registration.

        Instances are registered as soon as they are hashable and have the
        following attributes:

        * __regid__ (a string)
        * __select__ (a callable)
        * __registries__ (a tuple/list of string)

        For classes this is a bit more complicated :

        - first ensure parent classes are already registered

        - class with __abstract__ == True in their local dictionary are skipped

        - object class needs to have registries and identifier properly set to a
          non empty string to be registered.
        zloading %s from %sregistration_callbackN)r7  r+   __file__hasattrru  rN  varsr2   )r"   r   s     r%   rs  zRegistryStore.load_moduleU  sY    & 			&I623 ((.d6l113V__Er'   r  c                    t        |t              s2| j                  |      r |j                  |k(  r| j	                  |       y|j                  }||k7  r.|| j
                  v r| j                  | j
                  |   |       y|d|j                  }|| j                  |   v ry|| j                  |   |<   |j                  D ]  }| j                  |||        ||v s| j                  |      sy| j                  t        |      d         }|j                  |      d   dk(  rt        d|z  t               y| j	                  |       y)z\handle class registration according to rules defined in
        :meth:`load_module`
        Nr   r   _zx[lgc 0.59] object whose name start with '_' won't be skipped anymore at some point, use __abstract__ = True instead (%s))rP   r*   rL  rF   r  rT  rX  r+   rP  	__bases__rM  r  r   r   r   r   )r"   r   r  rJ  
objmodnameclsidparentrd  s           r%   rM  z)RegistryStore._load_ancestors_then_objectp  sP    )T*""9-)2F2F'2Qi())
  T--- t//
;ZH"I$6$67D$$W--+4!%()) 	JF,,WfjI	J 
"$*=*=i*HoonY7:;;;y!!$+!*+ #	 i r'   c                    t        |t              rIt        |t              s(t	        |dd      rt	        |dd      rt	        |dd      s#yt        |t
              ryt        |t
              sy|j                  sy|j                  }|sy|j                  }|sy|j                  j                  dd      ryt        |t        t        f      s| j                  d|       yt        |      s| j                  d|       yy	)
zensure `obj` should be registered

        as arbitrary stuff may be registered, do a lot of check and warn about
        weird cases (think to dumb proxy objects)
        r   Nr   r   Fr   z2%s has __registries__ which is not a list or tuplez%s has not callable __select__T)rP   r*   r   r   r  r   r   r   r   r3   r  rQ   r   r  callable)ro   r   
registriesrN   s       r%   rL  zRegistryStore.is_registrable  s     c4 c#45 C!148[$7\48 C!45C!45}}''
>><<NE2 *udm4KKLcR!KK8#>r'   c                      y r    r!   r3  s      r%   r&   zRegistryStore.<lambda>  r4  r'   )Fr5  )r!   )NNFr    )0r+   rF   rG   r(   r6  ry   rB  r~   r   r   rD  r   r   r*   r   rH  r  r   r   rN  r   r
   r  r  r  r   r   rV  r   rY  rb  r  r   rh  rk  rX  r   rs  rM  r   rL  r7  r  r,  r8  r9  r:  r   r   s   @r%   r=  r=    sF   sj#$ #4 #.	 	 	 7;H5Ed5s+T12E/C /D /  'H 's ' 'Z^ '> '+!WW smW c]	W
 W 
W.	( ;?I*23-	eCHo	 :;(T#Y (8C= (TX ( <(($s) ( (*+
s x} *!# ! ! !*F* F F8 JL,!,!'+,!9A#,!	,!\ , , , ,` =WVDV7VUVXV	Er'   r=  zregistry.storer   c           
          |d   }t         dk(  s|j                  t         v r"t        | d|d|d|j                  d       y y )Nr   allz -> z for r{   r   )TRACED_OIDSr   print)ro   rN   rC   retvobjs        r%   _trace_selectorr    s8    7Det~~<sCt~~FG  =r'   c                 V      fd} j                   |_          j                  |_        |S )zfuse this decorator on your predicates so they become traceable with
    :class:`traced_selection`
    c                 H     | g|i |}t         t        | ||       |S r    )r  r  )ro   rC   rD   r  rN   s       r%   tracedz_lltrace.<locals>.traced  s0    s,T,V,"C45
r'   )r+   r(   )rN   r  s   ` r%   r  r    s)    
 ''FO%%FNMr'   c                   $    e Zd ZdZddZd Zd Zy)traced_selectiona  
    Typical usage is :

    .. sourcecode:: python

        >>> from logilab.common.registry import traced_selection
        >>> with traced_selection():
        ...     # some code in which you want to debug selectors
        ...     # for all objects

    This will yield lines like this in the logs::

        selector one_line_rset returned 0 for <class 'elephant.Babar'>

    You can also give to :class:`traced_selection` the identifiers of objects on
    which you want to debug selection ('oid1' and 'oid2' in the example above).

    .. sourcecode:: python

        >>> with traced_selection( ('regid1', 'regid2') ):
        ...     # some code in which you want to debug selectors
        ...     # for objects with __regid__ 'regid1' and 'regid2'

    A potentially useful point to set up such a tracing function is
    the `logilab.common.registry.Registry.select` method body.
    c                     || _         y r    )r  )r"   r  s     r%   ry   ztraced_selection.__init__  s	    r'   c                     | j                   ay r    )r  r  rL   s    r%   	__enter__ztraced_selection.__enter__  s    kkr'   c                     d a |d u S r    )r  )r"   exctyper   	tracebacks       r%   __exit__ztraced_selection.__exit__  s    D  r'   N)r  )r+   rF   rG   r(   ry   r  r  r!   r'   r%   r  r    s    6"!r'   r  r    )Mr(   __docformat__r   r]  r   r@   r  r   osr   r   os.pathr   r   r   typingr   r	   r
   r   r   loggingr   warningsr   r   r   r   r   r   r   rp  r   logilab.common.modutilsr   logilab.common.logging_extr   logilab.common.decoratorsr   logilab.common.deprecationr   r-   r.   r   r   r7   r*   r9   r*  r,   ru   rX   rc   rk   r   r   r   	Exceptionr   r   r   r   r   r~   r   r   r   r   r   dictr   r   r=  r  r  r  r  r!   r'   r%   <module>r     s  $;| & 
      ' ' 8 8   7 7 v 
 6 6 3 :x C .  "T#t)_ !;x ;D ; AL"4 ALH+EY +E\
> 
. )9 )) ( IJ4 K4 EF- G--	 -( & 
* 
 ' S Xc] c & ae
s) (8@tCQTH~W[G[A\8]
4S>4c3h0012!# !#H4+ 46 v sm"		
 CtS(3-T'Q!RRS|Wt |W~ 8C= E#J SWD SWn y)9: ; )J/ 0 H &!v &!r'   