U
    9vfx!                     @   s   d Z ddlZddlZddlZddlZddlmZ ddlmZ ddgZ	G dd	 d	Z
e
 Zi Zd
d ZdddddZdddddZejdrejd Zesedeekrede zddlZW n ek
r   edY nX eZdd ZdS )a  
Code to support various backends in a plugin dispatch architecture.

Create a Dispatcher
-------------------

To be a valid plugin, a package must register an entry_point
of `networkx.plugins` with a key pointing to the handler.

For example::

    entry_points={'networkx.plugins': 'sparse = networkx_plugin_sparse'}

The plugin must create a Graph-like object which contains an attribute
``__networkx_plugin__`` with a value of the entry point name.

Continuing the example above::

    class WrappedSparse:
        __networkx_plugin__ = "sparse"
        ...

When a dispatchable NetworkX algorithm encounters a Graph-like object
with a ``__networkx_plugin__`` attribute, it will look for the associated
dispatch object in the entry_points, load it, and dispatch the work to it.


Testing
-------
To assist in validating the backend algorithm implementations, if an
environment variable ``NETWORKX_GRAPH_CONVERT`` is set to a registered
plugin keys, the dispatch machinery will automatically convert regular
networkx Graphs and DiGraphs to the backend equivalent by calling
``<backend dispatcher>.convert_from_nx(G, weight=weight, name=name)``.

The converted object is then passed to the backend implementation of
the algorithm. The result is then passed to
``<backend dispatcher>.convert_to_nx(result, name=name)`` to convert back
to a form expected by the NetworkX tests.

By defining ``convert_from_nx`` and ``convert_to_nx`` methods and setting
the environment variable, NetworkX will automatically route tests on
dispatchable algorithms to the backend, allowing the full networkx test
suite to be run against the backend implementation.

Example pytest invocation::

    NETWORKX_GRAPH_CONVERT=sparse pytest --pyargs networkx

Dispatchable algorithms which are not implemented by the backend
will cause a ``pytest.xfail()``, giving some indication that not all
tests are working, while avoiding causing an explicit failure.

A special ``on_start_tests(items)`` function may be defined by the backend.
It will be called with the list of NetworkX tests discovered. Each item
is a test object that can be marked as xfail if the backend does not support
the test using `item.add_marker(pytest.mark.xfail(reason=...))`.
    N)entry_points   )NetworkXNotImplemented	_dispatch_mark_testsc                   @   s<   e Zd ZdZdd Zdd Zedd Zdd	 Zd
d Z	dS )
PluginInfoz-Lazily loaded entry_points plugin informationc                 C   s
   d | _ d S )N)_itemsself r   =/tmp/pip-unpacked-wheel-_lngutwb/networkx/classes/backends.py__init__I   s    zPluginInfo.__init__c                 C   s   t | jdkS )Nr   )lenitemsr	   r   r   r   __bool__L   s    zPluginInfo.__bool__c                 C   s4   | j d kr.tjdk r"t d | _ ntdd| _ | j S )N   
   znetworkx.plugins)group)r   sysversion_infor   r	   r   r   r   r   O   s
    

zPluginInfo.itemsc                    s2   t jdk r&t fdd| jD dkS  | jjkS )Nr   c                    s   g | ]}|j  kr|qS r   name.0epr   r   r   
<listcomp>Z   s     
 z+PluginInfo.__contains__.<locals>.<listcomp>r   )r   r   r   r   namesr
   r   r   r   r   __contains__X   s    
zPluginInfo.__contains__c                    s,   t jdk r" fdd| jD d S | j  S )Nr   c                    s   g | ]}|j  kr|qS r   r   r   r   r   r   r   _   s     
 z*PluginInfo.__getitem__.<locals>.<listcomp>r   )r   r   r   r   r   r   r   __getitem__]   s    
zPluginInfo.__getitem__N)
__name__
__module____qualname____doc__r   r   propertyr   r   r    r   r   r   r   r   F   s   
r   c                 C   s(   | t krtd|  |t | < | |_d S )Nz/Algorithm already exists in dispatch registry: )_registered_algorithmsKeyErrorZdispatchname)r   Zwrapped_funcr   r   r   _register_algog   s    r(   r   c                   st    dkr"dkrt S tjt dS t tr:tjt  dS dkrH jt  fdd} |_t| |S )zbDispatches to a backend algorithm
    when the first argument is a backend graph-like object.
    Nr   c                     s   | r| d }n2z|d }W n$ t k
r>   t dd Y nX t|drtr|j}|tkrt|  }t|rt|j| |S td d|  | |S )Nr   G#() missing positional argument: 'G'__networkx_plugin__'' not implemented by )	r'   	TypeErrorhasattrpluginsr+   loadgetattr__call__r   )argskwdsgraphplugin_namebackendfuncr   r   r   wrapper   s     

z_dispatch.<locals>.wrapper)	r   	functoolspartial
isinstancestrr!   wraps
_orig_funcr(   r:   r   r;   r   r9   r   r   n   s    	

c                   s~   | dkr" dkrt S tjt  dS t| tr:tjt | dS  dkrH| j t| t|  fdd}| |_	t
 | |S )zAuto-converts the first argument into the backend equivalent,
    causing the dispatching mechanism to trigger for every
    decorated algorithm.Nr   c                     s0  t t  }t| sLtdkr6td  d|jj td  dt  j	| |}|
  | rn| ^}} n4z|d}W n$ tk
r   t  dd Y nX d }d|jkr|jd }n>d|jkrd	|jkrt|jd tr|jd }n|jd rd}|j|| d
}t| j|f| |}|j| dS )Nznx-loopbackr,   z' not found in r-   r)   r*   weightdatadefault)rC   r   r   )r0   r7   r1   r/   r   	__class__r!   pytestZxfailbindapply_defaultspopr'   r.   	argumentsr>   r?   Zconvert_from_nxr2   r3   Zconvert_to_nx)r4   r5   r8   boundr6   rC   resultr   sigr   r   r;      s4    



z'test_override_dispatch.<locals>.wrapper)test_override_dispatchr<   r=   r>   r?   r!   inspect	signaturer@   rA   r(   rB   r   rN   r   rP      s    

 
rP   NETWORKX_GRAPH_CONVERTz+No registered networkx.plugins entry_pointsz1No registered networkx.plugins entry_point named zCMissing pytest, which is required when using NETWORKX_GRAPH_CONVERTc                 C   s>   t jdr:t jd }t|  }t|dr:t|d|  dS )zXAllow backend to mark tests (skip or xfail) if they aren't able to correctly handle themrS   Zon_start_testsN)osenvirongetr0   r1   r/   r2   )r   r7   r8   r   r   r   r      s
    

)N)N)r$   r<   rQ   rT   r   importlib.metadatar   	exceptionr   __all__r   r0   r&   r(   r   rP   rU   rV   r7   	ExceptionrG   ImportErrorr   r   r   r   r   <module>   s:   :0;

