3
dt                 @   s  d Z ddlZddlZddlZddlmZ ddlZ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d	lmZ ddlZd
dlmZmZmZ d
dlmZmZmZmZmZmZ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/m0Z0 d
dl1m2Z2 d
dl3m4Z4 d
dl5m6Z6m7Z7m8Z8m9Z9 ddl:m;Z; ej<dZ=dd Z>dd Z?d|ddZ@d}ddZAd~dd ZBdd!d"ZCdd$d%ZDd&d' ZEd(d) ZFdd+d,ZGdd-d.ZHdd/d0ZIdd1d2ZJd3d4 ZKd5d6 ZLd7d8 ZMdd9d:ZNdd;d<ZOdd=d>ZPd?d@ ZQdAdB ZRdCdD ZSddEdFZTdGdH ZUddIdJZVdKdL ZWdMdN ZXdOdP ZYdQdR ZZdSdT Z[dUdV Z\dWdX Z]dYdZ Z^d[d\ Z_d]d^ Z`ddadbZaddcddZbdedf Zcdgdh Zddidj Zedkdl ZfddmdnZgdodp fdqdrZhdsdt ZiddvdwZjddxdyZkddzd{ZldS )z$Utility routines for workflow graphs    N)defaultdict)deepcopy)glob)Path)format_exception)sha1)reduce   )loggingconfigLooseVersion)indirectoryrelpathfname_presuffixensure_listget_related_files	save_jsonsavepklloadpklwrite_rst_headerwrite_rst_dictwrite_rst_list)str2bool)create_function_from_source)rebase_path_traitsresolve_path_traitsOutputMultiPath	isdefined	Undefined)BunchInterfaceResult)CommandLine)IdentityInterface)	ProvStorepm	nipype_nsget_id)	signatureznipype.workflowc             C   s    t | dkrt| j j S | S )z
    Returns the directory name for the given parameterization string as follows:
        - If the parameterization is longer than 32 characters, then
          return the SHA-1 hex digest.
        - Otherwise, return the parameterization unchanged.
        )lenr   encode	hexdigest)param r-   >/tmp/pip-build-7vycvbft/nipype/nipype/pipeline/engine/utils.py_parameterization_dir6   s    r/   c             C   s|   yt | | W nh ttfk
rv   tj d }|tkrft| d}|jt| W dQ R X tj	d ntj
d|  Y nX dS )zStore a hashfiler   wtNz2Unable to write a particular type to the json filez)Unable to open the file in write mode: %s)r   IOError	TypeErrorsysexc_infoopen
writelinesstrloggerdebugcritical)ZhashfileZhashed_inputsZerr_typefdr-   r-   r.   save_hashfileB   s    r<   Fc             c   s   x| D ]\}}d}d}zpy|j |d}W nZ tk
r   |r> |j}g }|jrdt|jdrd|jjg}|ttj  7 }dj	|}Y nX W d|||fV  X qW dS )zX
    A generator that iterates and over a list of ``nodes`` and
    executes them.

    N)
updatehash	traceback
)
run	Exceptionresultruntimehasattrr>   r   r3   r4   join)nodesr=   Z
stop_firstinodeerrrB   r-   r-   r.   nodelist_runnerT   s    
rJ   c             C   s  t | jd d sdS | j }t|d d }|jjddd tdt|  d	d
td| j	 d| j
 gtddd
t| jj g}|dkrtjd| |jdj| dS tjd| |tddd
t| jj tddd
g7 }|j}|dkr
|dg7 }|jdj| dS t|tr*|jt|j  n$|rD|jt|j  n
|dg7 }|r|jtddd
 ttt| j| jd	 }g }xFt|D ]:}	t|d d| j|	f  d d }
|jd|	|
f  qW |jt| |jdj| dS |jtddd
 |jj|jj|jj t|jddd}x,d*D ]$}t!|j|r*t|j|||< q*W |jt| t!|jd!r|td"d#d
t|jj"g7 }t!|jd$r|td%d#d
t|jj#g7 }t!|jd&r|td'd#d
t|jj$g7 }t!|jd(r|td)d#d
t|jj%g7 }|jdj| dS )+zWrite a report file for a node.	executionZcreate_reportN_reportz
report.rstT)exist_okparentszNode: %sr   )levelzHierarchy : %szExec ID : %szOriginal Inputs   z&[Node] Writing pre-exec report to "%s"r?   z'[Node] Writing post-exec report to "%s"zExecution InputszExecution OutputsNonezOutputs object was empty.zSubnode reportsZmapflowz_%s%dzsubnode %d : %szRuntime infoZprevcwdz	<not-set>)hostnamedurationZworking_dirZprev_wdcmdlinemem_peak_gbcpu_percentmergedzTerminal output   stdoutzTerminal - standard outputstderrzTerminal - standard errorenvironEnvironment)rT   rU   rV   )&r   r   
output_dirr   parentmkdirr   get_print_namer   fullname_idr   inputs	trait_getr8   r9   Z
write_textrE   outputs
isinstancer   appendZdictcopyr)   r   getattr	iterfieldrangenamerC   rR   rS   cwdrD   rW   rY   rZ   r[   )rH   rB   
is_mapnoderl   Zreport_filelinesre   ZnitemsZsubnode_report_filesrG   Zsubnode_fileZrst_dictpropr-   r-   r.   write_node_reportn   s    




"




rp   c             C   s8   |dkrt jd| dS t| ||dkr,| jndd dS )z+Write a report file for a node - DEPRECATEDpreexecpostexecz [Node] Unknown report type "%s".N)rm   rB   )rq   rr   )r8   warningrp   rB   )rH   Zreport_typerm   r-   r-   r.   write_report   s    rt   c       
      C   sz  |dkrt jdd}tjj|}tjj|d| }tjd| | jdkr`tj	d t
||  dS y| jj }W n( tk
r   tjd t
||  dS X |st
||  dS i }zt|~ xv|D ]n}t| j|}t|r| jj|jtr| jj|jj| j|}|||< t| jj|||}	t| j||	 qW W dQ R X t
||  W dx(t|j D ]\}}	t| j||	 qXW X dS )z#Save a result pklz file to ``cwd``.NrK   use_relative_pathszresult_%s.pklzzSaving results file: '%s'z#Storing result file without outputsz5Storing non-traited results, skipping rebase of paths)r   
getbooleanospathabspathrE   r8   r9   re   rs   r   copyable_trait_namesAttributeErrorr   rh   r   traitis_trait_typer   handler	get_valuer   setattrlistitems)
rB   rl   rk   ZrebaseZresultsfileZoutput_namesZbackup_traitskeyoldvalr-   r-   r.   save_resultfile   sB    








r   Tc             C   s   t | } | j st| t| }|rt|ddry|jj }W n  tk
r^   tj	d|  |S X tj	d xnt
|j D ]^\}}t|rx|jj|jtr|jj|jj|j|}t|jj||| j}t|j|| qxW |S )a]  
    Load InterfaceResult file from path.

    Parameters
    ----------
    results_file : pathlike
        Path to an existing pickle (``result_<interface name>.pklz``) created with
        ``save_resultfile``.
        Raises ``FileNotFoundError`` if ``results_file`` does not exist.
    resolve : bool
        Determines whether relative paths will be resolved to absolute (default is ``True``).

    Returns
    -------
    result : InterfaceResult
        A Nipype object containing the runtime, inputs, outputs and other interface information
        such as a traceback in the case of errors.

    re   Nz.Outputs object of loaded result %s is a Bunch.z4Resolving paths in outputs loaded from results file.)r   existsFileNotFoundErrorr   rh   re   getr2   r8   r9   r   r   r   r|   r}   r   r~   r   r   r^   r   )Zresults_fileresolverB   re   Z
trait_namer   valuer-   r-   r.   load_resultfile  s(    

r   c             C   sN   g }xD| D ]<}t |tr*|jt|| q
|j|jtjj|d| q
W |S )z%Remove temp from a list of file pathsZ
_tempinput)rf   r   rg   
strip_tempreplacerw   rx   rE   )fileswdoutfr-   r-   r.   r   ;  s    

 r   c             C   s  g }| j jdd}xt| jj D ]\}}t| j|}t|r"t|tt	fryt
|}W n( tk
r   |jd|||f  Y npX dd |jD d }|jtj| ||kr|d jd| d	| |d< d
| }|jd |jd|||f  q"|jd|||f  q"W |S )N._z%s.inputs.%s = '%s'c             S   s   g | ]}|d kr|qS )__builtins__r-   ).0rk   r-   r-   r.   
<listcomp>S  s    z!_write_inputs.<locals>.<listcomp>r   rP   z %s(z %s_1(z%s_1z,from nipype.utils.functions import getsourcez%s.inputs.%s = getsource(%s)z%s.inputs.%s = %sr   )ra   r   r   rc   r   rh   r   rf   r7   bytesr   RuntimeErrorrg   __globals__pickleloads)rH   rn   nodenamer   r   r   funcfuncnamer-   r-   r.   _write_inputsF  s,    
r   pythonc             C   sF  ddl m} g }| jjdd}|dkrB| j}d|j|jjf }d| j }t| jj	}	g }
xF|	j
j D ]8}t| jd|j d}|dk	rf|
j|j d	| qfW d
j|
}|jj}t| |rd|||| j|f }nd||||f }|||g}|r|d||g}|jd|| jf  | jdk	r4|jd|| jf  |jt|  |S )z'Format a node in a given output syntax.rP   )MapNoder   r   r   zfrom %s import %sz
# Node: %sN=z, z-%s = MapNode(%s(%s), iterfield=%s, name="%s")z%s = Node(%s(%s), name="%s")z#from collections import OrderedDictz%s.config = %sz%s.iterables = %s)rF   r   ra   r   	interface
__module__	__class____name__r'   __init__
parametersvaluesrh   rk   rg   rE   rf   ri   r   	iterablesextendr   )rH   formatZinclude_configr   rn   rk   klassZ
importlinecommentspecZfilled_argsr,   r   args
klass_nameZnodedefr-   r-   r.   format_noded  sD    





r   c             C   s4  |st j }t| trPi }x2t| j D ]"\}}t|r(t|||d||< q(W nt| tt	frg }x(| D ] }t|rh|j
t|||d qhW t| t	rt	|}nt| r"t| ttfot jj| r|rtjddrt| |d}n| }nt jjt jj|| }t jj|s td| n| }ntdj| |S )ai  Convert paths in data structure to either full paths or relative paths

    Supports combinations of lists, dicts, tuples, strs

    Parameters
    ----------

    relative : boolean indicating whether paths should be set relative to the
               current directory
    basedir : default os.getcwd()
              what base directory to use as default
    )relativebasedirrK   ru   )startzFile %s not foundzObject {} is undefined)rw   getcwdrf   dictsortedr   r   modify_pathsr   tuplerg   r7   r   rx   isfiler   rv   r   ry   rE   r   r1   r2   r   )objectr   r   r   r   r   r-   r-   r.   r     s4    




r   c             C   s   | j }t| drj| jjjjd}| jjj}d}t|dkrFd|d  }|rV| j | }ndj| j |g| }|r|jd}t|dkrdj|dd d	 S t|dkr|d S |S )
zGet the name of the node

    For example, a node containing an instance of interfaces.fsl.BET
    would be called nodename.BET.fsl

    
_interfacer    rX   z.%sz (rP   N))	ra   rD   r   r   r   splitr   r)   rE   )rH   simple_formrk   pkglistr   Z	destclasspartsr-   r-   r.   r`     s"    


r`   c       	      C   s   t jd ddl}|j }xd| j D ]X}| j| }t|d |d}t|d |d}|rp|j||t|d d q$|j|| q$W |S )zSCreate a graph that can be pickled.

    Ensures that edge info is pickleable.
    zcreating dot graphr   N)r   rP   connect)l)	r8   r9   networkxZDiGraphedgesget_edge_datar`   add_edger7   )	graphshow_connectinfor   nxpklgraphedgedatasrcnameZdestnamer-   r-   r.   _create_dot_graph  s    

r   c          
   C   s~  ddl }ddg}g }x|j| D ]}|j}g }x| j|ddD ]\}}	}
x|
d D ]}t|d ttfrv|d }n|d d }|d }d	t| }d
t| }|jd|jj	dd||	jj	dd|f  ||krV|j| qVW qBW dgdd t
|D  dg }g }xp| j|ddD ]^\}}	}
xP|
d D ]D}t|d ttfrN|d }n|d d }||kr,|j| q,W qW dgdd t
|D  dg }d}t|dr|jjjjd}t|dkr|d }dj|jddd }d|jdd ||f }|d|j	dddj||dj|f g7 }q"W xt
|D ]}|j| q6W |jd t|d}|jdj| W dQ R X |S )a  
    Create a dot file with connection info ::

        digraph structs {
        node [shape=record];
        struct1 [label="<f0> left|<f1> middle|<f2> right"];
        struct2 [label="<f0> one|<f1> two"];
        struct3 [label="hello\nworld |{ b |{c|<here> d|e}| f}| g | h"];
        struct1:f1 -> struct2:f0;
        struct1:f0 -> struct2:f1;
        struct1:f2 -> struct3:here;
        }
    r   Nzdigraph structs {znode [shape=record];T)Znbunchr   r   rP   zin%szout%sz%s:%s:e -> %s:%s:w;r   r   z{INc             S   s   g | ]}d t ||f qS )z
|<in%s> %s)_replacefunk)r   ipr-   r-   r.   r     s    z'_write_detailed_dot.<locals>.<listcomp>}z{OUTc             S   s   g | ]}d t ||f qS )z|<out%s> %s)r   )r   Zoportr-   r-   r.   r   *  s   r   rX   z{ %s | %s | %s }z%s [label="%s|%s|%s"];r0   r?   r   r   )r   topological_sortiternamein_edgesrf   r7   r   r   rg   r   r   	out_edgesrD   r   r   r   r   r)   rE   r5   write)r   dotfilenamer   textr   nr   ZinportsuvdZcdZoutportinportZipstripZopstripZinputstrZoutportsZ	outputstrZ
srcpackager   ZsrchierarchyZnodenamestrr   Zfilepr-   r-   r.   _write_detailed_dot  sl    





r   c             C   s$   | j ddj ddj ddj ddS )Nr   r   r   @-)r   )xr-   r-   r.   r   L  s    r   c             C   sB   t | ttfst| } | jtjd} tjdd| } | jdd} | S )zgRemove disallowed characters from path

    Removes:  [][ (){}?:<>#!|"';]
    Replaces: ',' -> '.'
    z..z[][ (){}?:<>#!|"';]r   ,r   )rf   r7   r   r   rw   sepresub)Zpathstrr-   r-   r.   _get_valid_pathstrQ  s    r   c             C   s    |rt | S ttt| j S )N)synchronize_iterablesr   walkr   )r   synchronizer-   r-   r.   expand_iterables_  s    r   c             C   s,   |rt ndd }t|dd t| j D S )zReturn the number of iterable expansion nodes.

    If synchronize is True, then the count is the maximum number
    of iterables value lists.
    Otherwise, the count is the product of the iterables value
    list sizes.
    c             S   s   | | S )Nr-   )r   yr-   r-   r.   <lambda>m  s    z!count_iterables.<locals>.<lambda>c             S   s   g | ]\}}t | qS r-   )r)   )r   r   r   r-   r-   r.   r   n  s    z#count_iterables.<locals>.<listcomp>)maxr   r   r   )r   r   opr-   r-   r.   count_iterablese  s    r   c       
      c   s   |dkri }| s|j  V  dS | d | dd  }}|\}}xF| D ]<}|rV|||< n|||< x t||d ||D ]
}	|	V  qrW qDW dS )aH  Generate all the full paths in a tree, as a dict.

    Examples
    --------
    >>> from nipype.pipeline.engine.utils import walk
    >>> iterables = [('a', lambda: [1, 2]), ('b', lambda: [3, 4])]
    >>> [val['a'] for val in walk(iterables)]
    [1, 1, 2, 2]
    >>> [val['b'] for val in walk(iterables)]
    [3, 4, 3, 4]
    r   NrP   )copyr   )
childrenrO   rx   Zusenameheadtailrk   r   childZchild_pathsr-   r-   r.   r   q  s    

r   c             C   sr   g }dd t | j D }xRi }x6|D ].\}}yt|||< W q& tk
rR   Y q&X q&W |rh|j| qP qW |S )aL  Synchronize the given iterables in item-wise order.

    Return: the {field: value} dictionary list

    Examples
    --------
    >>> from nipype.pipeline.engine.utils import synchronize_iterables
    >>> iterables = dict(a=lambda: [1, 2], b=lambda: [3, 4])
    >>> synced = synchronize_iterables(iterables)
    >>> synced == [{'a': 1, 'b': 3}, {'a': 2, 'b': 4}]
    True
    >>> iterables = dict(a=lambda: [1, 2], b=lambda: [3], c=lambda: [4, 5, 6])
    >>> synced = synchronize_iterables(iterables)
    >>> synced == [{'a': 1, 'b': 3, 'c': 4}, {'a': 2, 'c': 5}, {'c': 6}]
    True
    c             S   s   g | ]\}}|t | fqS r-   )iter)r   fieldZfvalsr-   r-   r.   r     s    z)synchronize_iterables.<locals>.<listcomp>)r   r   nextStopIterationrg   )r   Zout_listZiterable_itemsZcur_dictr   Ziter_valuesr-   r-   r.   r     s    
r   c             C   sz   t | }y||ft| }W nV tk
rt } z:|jd jdr`|jd jdr`|jd df|_|W Y d d }~X nX |S )Nr   zglobal namezis not definedzUDue to engine constraints all imports have to be done inside each function definition)r   r   	NameErrorr   
startswithendswith)Zfunction_sourcer   Z	first_argr   Zoutput_valueer-   r-   r.   evaluate_connect_function  s     r  c             C   sZ   dd l }i }xH|j| D ]:}d||< x,| j|D ]}t|| || d ||< q0W qW |S )Nr   rP   )r   r   Zpredecessorsr   )Gr   levelsr   predr-   r-   r.   
get_levels  s    "r  c             C   s  | j  }dd |D }tt|t|kr2tdi }	xt|j  D ]}
|j|
j|
j }xv| jt|| D ]`}|d |j  krn|
j|
j t|	j	 krg |	|
j|
j < |	|
j|
j  j
|d | j| f qnW qDW | j| t||}|s| S t|}d|tjtj|f }x|t|D ]n\}}t|}dd |j  D }|j|}t|j  | }d}x<t|j D ],\}}dj|t|t|}|j|| qnW tjd	| t|}xB|j  D ]6}
||
 }| |fg}|
jr||
j |
_n||
_qW | j|j   | j|jd
d xt|j  D ]h}|j|j t|	j	 krtx4|	|j|j  D ] }| j|d ||d fg qPW | j|| 7  _q W qW | S )a  Merges two graphs that share a subset of nodes.

    If the subgraph needs to be replicated for multiple iterables, the
    merge happens with every copy of the subgraph. Assumes that edges
    between nodes of supergraph and subgraph contain data.

    Parameters
    ----------
    supergraph : networkx graph
    Parent graph from which subgraph was selected
    nodes : networkx nodes
    Nodes of the parent graph from which the subgraph was initially
    constructed.
    subgraph : networkx graph
    A subgraph that contains as a subset nodes from the supergraph.
    These nodes connect the subgraph to the supergraph
    nodeid : string
    Identifier of a node for which parameterization has been sought
    iterables : dict of functions
    see `pipeline.NodeWrapper` for iterable requirements

    Returns
    -------
    Returns a merged graph containing copies of the subgraph with
    appropriate edge connections to the supergraph.

    c             S   s   g | ]}|j |j qS r-   )
_hierarchyrb   )r   r   r-   r-   r.   r     s    z!_merge_graphs.<locals>.<listcomp>zSExecution graph does not have a unique set of node names. Please rerun the workflowr   z	.%s%%0%ddc             S   s   g | ]}|j |j qS r-   )r  rb   )r   r   r-   r-   r.   r     s    r   z{}_{}_{}zParameterization: paramstr=%sT)r   rP   )rF   r)   setrA   r   indexr  rb   r   keysrg   r   remove_nodes_fromr   npceillog10	enumerater   r   r   r   r   	set_inputr8   r9   r  parameterizationadd_nodes_fromadd_edges_fromr   )Z
supergraphrF   subgraphZnodeidr   prefixr   Z
supernodesidsedgeinfor   Znidxr   Ziterable_paramscounttemplaterG   paramsZGcZnodeidxrootnodeZparamstrr   r   r  Zpath_lengthZ	paramlistrH   infor-   r-   r.   _merge_graphs  s\     


 r  c             C   sB   | j ||dd}|s0d|i}| j|||fg n|d j| dS )z"Add a connection between two nodesN)defaultr   )r   r  r   )r   srcnodedestnodeZconnection_infor   r-   r-   r.   _connect_nodes;  s
    r!  c             C   s.   x(t | | D ]}t|dst| | qW | S )zRemove non-join identity nodes from the given graph

    Iterable nodes are retained if and only if the keep_iterables
    flag is set to True.
    
joinsource)_identity_nodesrD   _remove_identity_node)r   keep_iterablesrH   r-   r-   r.   _remove_nonjoin_identity_nodesE  s    
r&  c                s    ddl } fdd|j| D S )zReturn the IdentityInterface nodes in the graph

    The nodes are in topological sort order. The iterable nodes
    are included if and only if the include_iterables flag is set
    to True.
    r   Nc                s.   g | ]&}t |jtr s&t|d dkr|qS )r   N)rf   r   r"   rh   )r   rH   )include_iterablesr-   r.   r   ]  s   z#_identity_nodes.<locals>.<listcomp>)r   r   )r   r'  r   r-   )r'  r.   r#  S  s    
r#  c             C   sh   t | |\}}x<t|j D ],\}}|r:t| |||| qt| ||| qW | j|g tjd| dS )z-Remove identity nodes from an execution graphz,Removed the identity node %s from the graph.N)_node_portsr   r   _propagate_internal_output_propagate_root_outputr  r8   r9   )r   rH   
portinputsportoutputsr   connectionsr-   r-   r.   r$  d  s    r$  c             C   s   i }i }x>| j |ddD ],\}}}x |d D ]\}}||f||< q,W qW xn| j|ddD ]\\}}	}xP|d D ]D\}}t|tr|d }
n|}
|
|krg ||
< ||
 j|	||f qlW qXW ||fS )al  Return the given node's input and output ports

    The return value is the (inputs, outputs) dictionaries.
    The inputs is a {destination field: (source node, source field)}
    dictionary.
    The outputs is a {source field: destination items} dictionary,
    where each destination item is a
    (destination node, destination field, source field) tuple.
    T)r   r   r   )r   r   rf   r   rg   )r   rH   r+  r,  r   r   r   srcdestr   srcportr-   r-   r.   r(  p  s    


r(  c             C   sN   xH|D ]@\}}}t |j|}t|tr:t|d |d |}|j|| qW dS )ziPropagates the given graph root node output port
    field connections to the out-edge destination nodes.rP   rX   N)rh   rc   rf   r   r  r  )r   rH   r   r-  r   r   r.  r   r-   r-   r.   r*    s
    
r*  c             C   s>  x6|D ],\}}}||kr|| \}}	t |	trtt |trt|	d jdd }
|d jdd }tdj|
|||| j||dg id}t |tr|d j|	|d |d f|f nd|	|fgi}| j||dg id}|d  |d 7  < | j|||fg qt|j	|}t |tr*t
|d |d |}|j|| qW dS )	zPropagates the given graph internal node output port
    field connections to the out-edge source node and in-edge
    destination nodes.rP   z\nr   z}Does not support two inline functions in series ('{}'  and '{}'), found when connecting {} to {}. Please use a Function node.r   )r  rX   N)rf   r   r   
ValueErrorr   r   rg   r  rh   rc   r  r  )r   rH   r   r-  r+  r   r   r.  r  r0  Zsrc_funcZdst_funcr   Zold_connectr   r-   r-   r.   r)    s*    
"r)  c       *         sl  ddl y
j}W n tk
r,   j}Y nX tjd t dd x j D ]}|jrNt	| qNW t
d}t }tjd| x|r&|d tjd  fd	d
 j D }i }x~|D ]vi  }|< g }x2 jdD ]"\}	}
}|||	j< |j|	|
f qW x,|D ]$\}	}
 j|	|
 tjd|	|
 qW qW jr4j\}t|ttfrd|g}y$t fdd j D W n& tk
r   tdf Y nX tjd i }fdd
|D }t|dkr|d nt|tfdd
jD }dd tfdd
t
|j D }n
jj }d_tjd| dd
 | D }dd
 |D }tdd
 |D }|sd}n,|d- dkrtd||j|d. d  }tjd|f  jd| 7  _tj tdk r j!|}n j!|j }t" ||j#j ||j$ x|D ]| }t%t
}x~ D ]v}xnt
|j& D ]^}|j}t'|d r||krq`|j(|r`|t|d }t)j*d!|r`|| j| q`W qNW x.t
|j D ]\}}tjd"|t| qW x&t
|j+ D ]}|j,d#d$ d% qW t-|j$}fd&d
t.|D }xt
|j D ]\}}xt/|D ]\}}|| } t0| }!|!d' }"fd(d
|"D }#|| }$xNt/|"D ]B\}%}&|&\}'}(|(|$kr|$|( })|'|)f|"|%< tjd)||(|) qW  j1|f|! tjd*| qbW qPW q2W t }qW x0 j D ]$}|j2r2d+d
 t|j2D |_2q2W tjd, t S )/aZ  Generates an expanded graph based on node parameterization

    Parameterization is controlled using the `iterables` field of the
    pipeline elements.  Thus if there are two nodes with iterables a=[1,2]
    and b=[3,4] this procedure will generate a graph with sub-graphs
    parameterized as (a=1,b=3), (a=1,b=4), (a=2,b=3) and (a=2,b=4).
    r   NzPE: expanding iterablesT)r%  abcdefghijklmnopqrstuvwxyzzDetected iterable nodes %sz!Expanding the iterable node %s...c                s4   g | ],}t |d rj|jkrj |r|qS )r"  )rD   rk   r"  has_path)r   rH   )graph_ininoder   r-   r.   r     s   
z+generate_expanded_graph.<locals>.<listcomp>z'Excised the %s -> %s join node in-edge.c             3   s*   | ]"}|j krj |r|V  qd S )N)rk   r3  )r   rH   )r4  r5  r   src_namer-   r.   	<genexpr>  s   z*generate_expanded_graph.<locals>.<genexpr>zLThe node %s itersource %s was not found among the iterable predecessor nodesz'The node %s has iterable source node %sc                s   g | ]}t  j|qS r-   )rh   rc   )r   r   )iter_srcr-   r.   r     s    rP   c                s$   g | ]\}} |kr||  fqS r-   r-   )r   r   lookup)r   r-   r.   r     s   c                 s    d  fddfS )Nr   c                  s    d S )NrP   r-   r-   )pairr-   r.   r     s    zBgenerate_expanded_graph.<locals>.make_field_func.<locals>.<lambda>r-   )r:  r-   )r:  r.   make_field_func  s    z0generate_expanded_graph.<locals>.make_field_funcc                s   g | ]} | qS r-   r-   )r   r:  )r;  r-   r.   r      s    znode: %s iterables: %sc             S   s   g | ]}|qS r-   r-   )r   sr-   r-   r.   r   (  s    c             S   s    g | ]}|j rtjd |j qS )z\.(.)I)rb   r   findall)r   r<  r-   r-   r.   r   )  s    c             S   s   g | ]}|D ]}|qqS r-   r-   )r   itemr   r-   r-   r.   r   *  s    azz"Too many iterables in the workflowz	subnodes:z.%sI2	joinfieldz((\.[a-z](I\.[a-z])?|J)\d+)?z3The join node %s input %s was expanded to %d nodes.c             S   s   | j S )N)rb   )rH   r-   r-   r.   r   h  s    z)generate_expanded_graph.<locals>.<lambda>)r   c                s   g | ]} j  qS r-   )Z_add_join_item_fields)r   r   )jnoder-   r.   r   n  s    r   c                s   g | ]\}}| j kr|qS r-   )rB  )r   r   r   )rC  r-   r.   r     s    z+Qualified the %s -> %s join field %s as %s.zAConnected the join node %s subgraph to the expanded join point %sc             S   s   g | ]\}}|qS r-   r-   )r   r   r,   r-   r-   r.   r     s    z PE: expanding iterables ... doner   r   )3r   dfs_preorderr{   Zdfs_preorder_nodesr8   r9   r&  rF   r   _standardize_iterablesr   _iterable_nodesr   r   rg   Zremove_edge
itersourcerf   r7   r   r   r   r1  r)   r   r   r   r   r   r	  rb   r   __version__r  r  r  r   r   r
  rD   r   r   	fullmatchr   sortr   rj   r  r   r   r  )*r4  rD  rH   ZallprefixesinodesZjnodesZ
jedge_dictr   Zedges2remover.  r/  r   Z
src_fieldsr   Z
src_valuesZ	iter_dictZsubnodesZprior_prefixZiterable_prefixr  Zold_edge_dictZ
expansionsZsrc_idr   suffixin_idZin_nodesZiter_cntZ
slot_dictsZold_idZin_idxZin_nodeZolddatanewdataZconnectsZjoin_fieldsslotsZcon_idxr   Z	src_fieldZ
dest_fieldZ
slot_fieldr-   )r4  r5  r8  rC  r   r;  r   r6  r.   generate_expanded_graph  s    


	







	

rP  c             C   sL   ddl }|j| }dd |D }dd |D }dd |D }|j  || S )a  Returns the iterable nodes in the given graph and their join
    dependencies.

    The nodes are ordered as follows:

    - nodes without an itersource precede nodes with an itersource
    - nodes without an itersource are sorted in reverse topological order
    - nodes with an itersource are sorted in topological order

    This order implies the following:

    - every iterable node without an itersource is expanded before any
      node with an itersource

    - every iterable node without an itersource is expanded before any
      of it's predecessor iterable nodes without an itersource

    - every node with an itersource is expanded before any of it's
      successor nodes with an itersource

    Return the iterable nodes list
    r   Nc             S   s   g | ]}|j d k	r|qS )N)r   )r   rH   r-   r-   r.   r     s    z#_iterable_nodes.<locals>.<listcomp>c             S   s   g | ]}|j s|qS r-   )rG  )r   rH   r-   r-   r.   r     s    c             S   s   g | ]}|j r|qS r-   )rG  )r   rH   r-   r-   r.   r     s    )r   r   reverse)r4  r   rF   rK  Zinodes_no_srcZ
inodes_srcr-   r-   r.   rF    s    
rF  c                s   | j s
dS | j }t| jj  | jrXt|dkrX|\}}t fdd|D rXt||}t|t	rh|g}t
| |  t|tr| jsdd fdd|D }t|}|| _ dS )	ztConverts the given iterables to a {field: function} dictionary,
    if necessary, where the function returns a list.NrX   c             3   s$   | ]}t |ttfo| kV  qd S )N)rf   r7   r   )r   r>  )fieldsr-   r.   r7    s    z)_standardize_iterables.<locals>.<genexpr>c                 s    d  fddfS )Nr   c                  s    d S )NrP   r-   r-   )r:  r-   r.   r     s    zA_standardize_iterables.<locals>.make_field_func.<locals>.<lambda>r-   )r:  r-   )r:  r.   r;    s    z/_standardize_iterables.<locals>.make_field_funcc                s   g | ]} | qS r-   r-   )r   Zfield_value1)r;  r-   r.   r     s    z*_standardize_iterables.<locals>.<listcomp>)r   r  rc   rz   r   r)   all_transpose_iterablesrf   r   _validate_iterablesr   rG  r   )rH   r   firstlastZ
iter_itemsr-   )rR  r;  r.   rE    s&    


rE  c             C   s   t |trt|j }n,t |t rDt |t rDtd| j|jf x|D ]z}yt|dkrjtd| j W n4 t	k
r } zt	d| j|f W Y dd}~X nX |\}}||krJtd| j|f qJW dS )z
    Raise TypeError if an iterables member is not iterable.

    Raise ValueError if an iterables member is not a (field, values) pair.

    Raise ValueError if an iterable field is not in the inputs.
    z7The %s iterables type is not a list or a dictionary: %srX   z0The %s iterables is not a [(field, values)] listz)A %s iterables member is not iterable: %sNz*The %s iterables field is unrecognized: %s)
rf   r   r   r   r   r1  rk   r   r)   r2   )rH   r   rR  r>  r  r   r   r-   r-   r.   rU    s$    	

"rU  c             C   s   t |trtdd | D }xZt|j D ]J\}}x@|D ]8}x2t|D ]&\}}|dk	rF|| |  | j| qFW q8W q*W t|j S tt| dd t| D S )a  
    Converts the given fields and tuple values into a standardized
    iterables value.

    If the input values is a synchronize iterables dictionary, then
    the result is a (field, {key: values}) list.

    Otherwise, the result is a list of (field: value list) pairs.
    c             S   s   g | ]}|t tfqS r-   )r   r   )r   r   r-   r-   r.   r     s    z(_transpose_iterables.<locals>.<listcomp>Nc             S   s   g | ]}d d t |D qS )c             S   s   g | ]}|d k	r|qS )Nr-   )r   r   r-   r-   r.   r   *  s    z3_transpose_iterables.<locals>.<listcomp>.<listcomp>)r   )r   Z	transposer-   r-   r.   r   *  s   )rf   r   r   r   r  rg   zip)rR  r   Z
transposedr   ZtuplesZkvalsidxr   r-   r-   r.   rT    s    


"rT  	graph.dotpngc             C   s6  ddl }t| }	|r(t|	}	tjd n
tjd |dkrBtj }tj|dd t|dd|d	}
t	|	|
 t
|
|d
\}}|dk	r|jjrtjd|jj t|	||}t|dd|d	}|jjj|| t
||d
\}}|dk	r|jjrtjd|jj |r(|j|dd}|j|| |r(|j|| |r2|S |S )a*  Displays the graph layout of the pipeline

    This function requires that pygraphviz and matplotlib are available on
    the system.

    Parameters
    ----------

    show : boolean
    Indicate whether to generate pygraphviz output fromn
    networkx. default [False]

    use_execgraph : boolean
    Indicates whether to use the specification graph or the
    execution graph. default [False]

    show_connectioninfo : boolean
    Indicates whether to show the edge data on the graph. This
    makes the graph rather cluttered. default [False]
    r   Nzusing execgraphzusing input graphT)rM   z_detailed.dotF)rL  Zuse_extnewpath)
format_extzdot2png: %sz.dotdot)prog)r   r   rP  r8   r9   rw   r   makedirsr   r   _run_dotrC   
returncoders   rZ   r   ZdrawingZnx_pydotZ	write_dotZgraphviz_layoutZdrawZdraw_networkx_edge_labels)r4  base_dirshowZuse_execgraphr   r   r   r   r   r   Zout_dotZoutfnameresr   Z
simple_dotZsimplefnameposr-   r-   r.   export_graph1  s8    

rg  c             C   sV   yt | |d\}}W n< tk
rP } z dt|kr<tdn|W Y dd}~X nX |S )z=Dump a directed graph (Linux only; install via `brew` on OSX))r]  zcould not be foundz;Cannot draw directed graph; executable 'dot' is unavailableN)ra  r1   r7   )r   r   formatted_dotr   Zioer-   r-   r.   
format_doty  s    
ri  c             C   sT   |dkr| d fS t jj| d }dj||}dj||| }t|dddj }||fS )Nr^  r   z{}.{}zdot -T{} -o"{}" "{}"Z	allatonceF)Zterminal_outputZresource_monitor)rw   rx   splitextr   r!   r@   )r   r]  Zdot_baserh  cmdre  r-   r-   r.   ra    s    ra  c             C   s`   | g}| j dr<|j| d d d  |j| d d	 d  | j dr\|j| d d
 d  |S )Nz.img   z.hdrz.matz.img.gz   z.hdr.gzrn  i)r   rg   )infiler   r-   r-   r.   get_all_files  s    

rp  c             C   s   g }t | tr@xt| j D ]\}}t|r|jt| qW nt | ttfrtx|| D ]}t|rT|jt| qTW nXt| rt | t	t
frtjj| stjj| rdd t| D }ntjj| r| dfg}|S )z8Extract every file and directory from a python structurec             S   s   g | ]}|d fqS )r   r-   )r   filenamer-   r-   r.   r     s    z walk_outputs.<locals>.<listcomp>r   )rf   r   r   r   r   r   walk_outputsr   r   r7   r   rw   rx   islinkr   rp  isdir)r   r   r   r   r-   r-   r.   rr    s    


rr  c             c   s<   x6t j| D ](\}}}x|D ]}t jj||V  qW qW d S )N)rw   r   rx   rE   )rl   rx   r   r   r   r-   r-   r.   
walk_files  s    
ru  c          	      s  | sdS t | j j }|r0t|d d r0|}g }| j }	x|D ]}
|jt|	|
  qBW dd |D }t|d d rg }|j }|jt| |dd |D 7 }x$dD ]}|jttjj	|| qW |r|jt
| dd |D }|r |jt
| x&dD ]}|jttjj	|| qW g }x|D ]}|jt| q2W |}tjddj	| tjddj	| g }t|d d rxt|D ]D  |kr|s|j  n"t fdd|D s|j  qW nlt|d d sHg }|j }|jt| dd |D }x0t|D ]$  |kr  |kr |j  q W tjddj	| x|D ] tj  q`W x(| j D ]}||kr~t| |t q~W | S )zDRemoves all files not needed for further analysis from the directoryNrK   Zremove_unnecessary_outputsc             S   s   g | ]\}}|d kr|qS )r   r-   )r   rx   typer-   r-   r.   r     s    z+clean_working_directory.<locals>.<listcomp>Zkeep_inputsc             S   s   g | ]\}}|d kr|qS )r   r-   )r   rx   rv  r-   r-   r.   r     s    	_0x*.jsonprovenance.*pyscript*.mpyjobs*.matcommand.txtresult*.pklz_inputs.pklz
_node.pklz.proc-*c             S   s   g | ]\}}|d kr|qS )r   r-   )r   rx   rv  r-   r-   r.   r     s    _nipyperL   zNeeded files: %s;zNeeded dirs: %sc                s   g | ]} j |qS r-   )r   )r   Zdname)r   r-   r.   r     s    c             S   s   g | ]\}}|d kr|qS )r   r-   )r   rx   rv  r-   r-   r.   r     s    zRemoving files: %s)	rw  rx  ry  rz  r{  r|  r}  r~  r  )r  rL   )r   rd   r
  r   r   rr  r   rw   rx   rE   r   r   r8   r9   ru  rg   anyremoverz   r   r   )re   rl   rc   Zneeded_outputsr   Z
files2keepZ	dirs2keepZoutputs_to_keepZoutput_filesZ
outputdictoutputZneeded_filesZinput_filesZ	inputdictextraZneeded_dirstemprq  Zfiles2remover   r-   )r   r.   clean_working_directory  s|    
        




r  c             C   s   |S )Nr-   )r   r   r-   r-   r.   r     s    r   c             C   sn   t | ts|| |S t| }|dkr(|S x@t|j D ]0\}}||kr^t|| ||d||< q6|||< q6W |S )a  
    Merges two dictionaries, non-destructively, combining
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous
    types in your dicts, so specifying a merge technique can be
    valuable.)

    Examples:

    >>> d1 = {'a': 1, 'c': 3, 'b': 2}
    >>> d2 = merge_dict(d1, d1)
    >>> len(d2)
    3
    >>> [d2[k] for k in ['a', 'b', 'c']]
    [1, 2, 3]

    >>> d3 = merge_dict(d1, d1, lambda x,y: x+y)
    >>> len(d3)
    3
    >>> [d3[k] for k in ['a', 'b', 'c']]
    [2, 4, 6]

    N)merge)rf   r   r   r   
merge_dict)Zd1Zd2r  rB   kr   r-   r-   r.   r    s    

r  c             C   s    x|j  D ]}| j| q
W | S )N)get_recordsZ_add_record)Zg1Zg2Zrecr-   r-   r.   merge_bundles"  s    r  rS  c             C   s  |st jjt j d}t }g }| j }x|D ]}|j}|jjj	}|j
 \}	}
}	}	tjd t| tjd dj||jftd |
i}|jjt dd|}t|jtr|jtjd td i xt|jD ]\}}t|j| |i d}|jr|t|jk r|j| |_|jrdxNt|jj D ]<\}}	t|j|}t|r$|t|k r$|| |j|< q$W t j|}tj|j  t d	}|jj!| |jj"|j#d
tj$id}|jj%|| qW nz|jtjd td i |j&r|j&}nt j|}tj|j  t d	}|jj!| |jj"|j#d
tj$id}|jj%|| |j'| q0W xNt| j( D ]>\}}|jj)|t|j*|d  |t|j*|d  d qRW |j+||d |jS )zWrite W3C PROV Model JSON fileZworkflow_provenancerv  labelr   hashvalNr   )re   )
identifierz	prov:type)Zother_attributesNoderP   r   )Zstarter)r   ),rw   rx   rE   r   r#   rF   rB   r   r   r   Zhash_existsr$   ZPROVr%   rk   gZactivityr&   rf   rC   r   Zadd_attributesr  r    rc   r)   re   r   rh   r   Zadd_resultsZ
ProvBundler  Z
add_bundleentityr  ZPROV_BUNDLEZwasGeneratedByZ
provenancerg   r   ZwasStartedByr	  Zwrite_provenance)r   rq  r   ZpsZ	processesrF   rH   rB   	classnamer   r  attrsprocessrY  rC   Z	subresultr   r   Zsub_docZ
sub_bundleZbundle_entityZprov_docZresult_bundler  r-   r-   r.   write_workflow_prov(  s`    
 r  c             C   s&  ddl }tjdd|}|s,tjjtj d}|dkrFttjddd}g g g g g g g g d}|rtjj|rt	|d	}|j
|}W dQ R X xlt| j D ]Z\}}|j}|jjj}	d
}
|jrdjdd |jD }
y|jj}W n& tk
r   tjd||	 wY nX t|ts|g}xt|D ]\}}yt|jd }W n2 tk
rr   tjd||d t| w&Y nX x$dD ]}||  |j| 7  < qzW |d  |	g| 7  < |d  |g| 7  < |d  |g| 7  < |d  |
g| 7  < q&W qW t	|d}|j||dd W dQ R X |S )z
    Generate a JSON file with profiling traces that can be loaded
    in a pandas DataFrame or processed with JavaScript like D3.js
    r   NZ
monitoringZsummary_filezresource_monitor.jsonZsummary_appendtrue)timerk   r   rss_GiBvms_GiBcpusmapnoder  rr   r   c             S   s   g | ]}d j |qS )z{})r   )r   pr-   r-   r.   r     s    z,write_workflow_resources.<locals>.<listcomp>z8Could not access runtime info for node %s (%s interface)r  zGCould not retrieve profiling information for node "%s" (mapflow %d/%d).rP   r  r  r  r   rk   r  r  wF)ensure_ascii)r  r  r  r  )Z
simplejsonr   r   rw   rx   rE   r   r   r   r5   loadr  rF   ra   r   r   r   r  rB   rC   rA   r8   rs   rf   r   r)   Z	prof_dictr{   dump)r   rq  rg   jsonZbig_dictZrsfr   rH   r   r  r  Zrt_listZsubidxrC   Znsamplesr   r-   r-   r.   write_workflow_resourcesk  sh    



 r  c             C   s   ddl }t|j| }|s"|dfS tjd g }g }d}|j }|j| j  |j| j	  |j
|}x|D ]}	|d7 }g }
x|	D ]}|
j|j| qW |jtj|tj|
tj|
  j  x|	D ]}|j| qW |j|gt|	  qlW ||fS )z9Returns a depth first sorted order if depth_first is Truer   NzPerforming depth first searchrP   )r   r   r   r8   r9   ZGraphr  rF   r  r   Zconnected_componentsrg   r	  r   r  arrayZargsorttolistr  r)   )r   Zdepth_firstr   ZnodesortrF   groupsgroupr  
componentsdescindicesrH   r-   r-   r.   r     s.    



$
r   )FF)NF)NF)N)T)r   F)TN)T)FT)F)F)r   NT)F)F)NFFFrZ  r[  T)r[  )NN)NrS  )NN)F)m__doc__rw   r3   r   collectionsr   r   r   r   r   Zpathlibr   r>   r   hashlibr   	functoolsr   numpyr  r   r
   r   r   Zutils.filemanipr   r   r   r   r   r   r   r   r   r   r   Z
utils.miscr   Zutils.functionsr   Z interfaces.base.traits_extensionr   r   r   r   r   Zinterfaces.base.supportr   r    Zinterfaces.baser!   Zinterfaces.utilityr"   Zutils.provenancer#   r$   r%   r&   inspectr'   	getLoggerr8   r/   r<   rJ   rp   rt   r   r   r   r   r   r   r`   r   r   r   r   r   r   r   r   r  r  r  r!  r&  r#  r$  r(  r*  r)  rP  rF  rE  rU  rT  rg  ri  ra  rp  rr  ru  r  r  r  r  r  r   r-   r-   r-   r.   <module>   s   4


e

-
.
.
.

]


!$
h


" u!&!       
@


F'
C
S