3
du(                 @   s   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ZddlmZ ddl	m
Z
 ddlmZ G dd deZdd	d
ZdddZG dd deZG dd deZdS )z
Using nipype with persistence and lazy recomputation but without explicit
name-steps pipeline: getting back scope in command-line based programming.
    N   )BaseInterface)Node)modify_pathsc               @   s*   e Zd ZdZd	ddZdd Zdd ZdS )
PipeFunca  Callable interface to nipype.interface objects

    Use this to wrap nipype.interface object and call them
    specifying their input with keyword arguments::

        fsl_merge = PipeFunc(fsl.Merge, base_dir='.')
        out = fsl_merge(in_files=files, dimension='t')
    Nc             C   s   t |tot|ts(td|t|f || _tjj|}tjj	| r\tjj
|r\td|| _d| jj| jjddf }|| _|| _dS )ag  

        Parameters
        ===========
        interface: a nipype interface class
            The interface class to wrap
        base_dir: a string
            The directory in which the computation will be
            stored
        callback: a callable
            An optional callable called each time after the function
            is called.
        zWthe interface argument should be a nipype interface class, but %s (type %s) was passed.z(base_dir should be an existing directoryz%s
%sT)Z
returnhelpN)
isinstancetype
issubclassr   
ValueError	interfaceospathabspathexistsisdirbase_dir__doc__helpcallback)selfr   r   r   doc r   7/tmp/pip-build-7vycvbft/nipype/nipype/caching/memory.py__init__   s    zPipeFunc.__init__c       
      K   s   t |dd}| j }|jjf | |jj }tjd}|jtj	| d|j
jjdd|j
jf }|j }t||d}tjj| j||_tj }z|j }	W d tj| X | jd k	r| j|| |	S )NF)Zrelativemd5z%s-%s.-)name)r   r   inputsZ	trait_setZget_hashvalhashlibnewupdatepickledumps	__class__
__module__replace__name__	hexdigestr   r   r   joinr   getcwdrunchdirr   )
r   kwargsr   r   Zhasherdir_namejob_namenodecwdoutr   r   r   __call__<   s&    


zPipeFunc.__call__c             C   s   dj | jj| jj| jj| jS )Nz{}({}.{}), base_dir={}))formatr$   r'   r   r%   r   )r   r   r   r   __repr__X   s
    zPipeFunc.__repr__)N)r'   r%   __qualname__r   r   r3   r5   r   r   r   r   r      s   
r   c             C   sl   |d krt  }t| dJ}xB|D ]:}|d d jd\}}|j|t }|j| |||< q W W d Q R X |S )Nr   /)dictopensplitgetsetadd)filenameZrun_dictlogfileliner.   r/   jobsr   r   r   read_logf   s    

rE   Fc             C   s   yt j| }W n tk
r"   dS X dd |D }t|j|}x@|D ]8}t jj| |}t jj|rF|rttd|  t	j
| qFW dS )zRemove all the sub-directories of base_dir, but those listed

    Parameters
    ============
    base_dir: string
        The base directory
    dirs_to_keep: set
        The names of the directories to keep
    Nc             S   s   g | ]}|j d s|qS )zlog.)
startswith).0dr   r   r   
<listcomp>   s    zrm_all_but.<locals>.<listcomp>zremoving directory: %s)r   listdirOSErrorlistsymmetric_differencer   r)   r   printshutilrmtree)r   Zdirs_to_keepwarnall_dirsZ
dirs_to_rmr.   r   r   r   
rm_all_buts   s    

rS   c               @   s    e Zd ZdZdd Zdd ZdS )_MemoryCallbackz6An object to avoid closures and have everything picklec             C   s
   || _ d S )N)memory)r   rU   r   r   r   r      s    z_MemoryCallback.__init__c             C   s   | j j|| d S )N)rU   	_log_name)r   r.   r/   r   r   r   r3      s    z_MemoryCallback.__call__N)r'   r%   r6   r   r   r3   r   r   r   r   rT      s   rT   c               @   sN   e Zd ZdZdd Zdd Zdd Zdd	d
ZdddZdddZ	dd Z
dS )Memorya  Memory context to provide caching for interfaces

    Parameters
    ==========
    base_dir: string
        The directory name of the location for the caching

    Methods
    =======
    cache
        Creates a cacheable function from an nipype Interface class
    clear_previous_runs
        Removes from the disk all the runs that where not used after
        the creation time of the specific Memory instance
    clear_previous_runs
        Removes from the disk all the runs that where not used after
        the given time
    c             C   sd   t jjt jj|d}t jj|s.t j| nt jj|sBtd|| _t	t jj|ddj
  d S )NZ
nipype_memzbase_dir should be a directoryzlog.currenta)r   r   r)   r   r   mkdirr   r
   r   r<   close)r   r   r   r   r   r      s    zMemory.__init__c             C   s   t || jt| S )a  Returns a callable that caches the output of an interface

        Parameters
        ==========
        interface: nipype interface
            The nipype interface class to be wrapped and cached

        Returns
        =======
        pipe_func: a PipeFunc callable object
            An object that can be used as a function to apply the
            interface to arguments. Inputs of the interface are given
            as keyword arguments, bearing the same name as the name
            in the inputs specs of the interface.

        Examples
        ========

        >>> from tempfile import mkdtemp
        >>> mem = Memory(mkdtemp())
        >>> from nipype.interfaces import fsl

        Here we create a callable that can be used to apply an
        fsl.Merge interface to files

        >>> fsl_merge = mem.cache(fsl.Merge)

        Now we apply it to a list of files. We need to specify the
        list of input files and the dimension along which the files
        should be merged.

        >>> results = fsl_merge(in_files=['a.nii', 'b.nii'],
        ...                     dimension='t') # doctest: +SKIP

        We can retrieve the resulting file from the outputs:
        >>> results.outputs.merged_file # doctest: +SKIP
        '...'
        )r   r   rT   )r   r   r   r   r   cache   s    'zMemory.cachec       	   "   C   s   | j }ttjj|dd}|jd||f  W dQ R X tj }tjj|d|j }ytj	| W n t
k
rv   Y nX tjj|d|j }ytj	| W n t
k
r   Y nX ttjj|d|j d}|jd||f  W dQ R X dS )z?Increment counters tracking which cached function get executed.zlog.currentrX   z%s/%s
Nzlog.%iz%02iz%02i.log)r   r<   r   r   r)   writetime	localtimetm_yearrY   rK   tm_montm_mday)	r   r.   r/   r   Z
currentlogtZyear_dirZ	month_dirZ
rotatefiler   r   r   rV      s     zMemory._log_nameTc             C   s*   | j }ttjj|d}| j||d dS )a;  Remove all the cache that where not used in the latest run of
        the memory object: i.e. since the corresponding Python object
        was created.

        Parameters
        ==========
        warn: boolean, optional
            If true, echoes warning messages for all directory
            removed
        zlog.current)rQ   N)r   rE   r   r   r)   _clear_all_but)r   rQ   r   Zlatest_runsr   r   r   clear_previous_runs   s    zMemory.clear_previous_runsNc             C   s   t j }|dk	r|n|j}|dk	r&|n|j}|dk	r8|n|j}| j}d||||f }t }t }	x4tjd| D ]"}
|
|k r|j	|
 qpt
|
|	}	qpW | j|	|d x|D ]}
tj|
 qW dS )a  Remove all the cache that where not used since the given date

        Parameters
        ==========
        day, month, year: integers, optional
            The integers specifying the latest day (in localtime) that
            a node should have been accessed to be kept. If not
            given, the current date is used.
        warn: boolean, optional
            If true, echoes warning messages for all directory
            removed
        Nz%s/log.%i/%02i/%02i.logz%s/log.*/*/*.log)rQ   )r]   r^   ra   r`   r_   r   rL   r;   globappendrE   rc   r   remove)r   daymonthyearrQ   rb   r   Zcut_off_fileZlogs_to_flushZrecent_runsZlog_namer   r   r   clear_runs_since  s    
zMemory.clear_runs_sincec             C   sP   t | jt|j |d x2t|j D ]"\}}t tjj| j|||d q&W dS )zSRemove all the runs appart from those given to the function
        input.
        )rQ   N)	rS   r   r?   keysrL   itemsr   r   r)   )r   runsrQ   r.   Z	job_namesr   r   r   rc   !  s    zMemory._clear_all_butc             C   s   dj | jj| jS )Nz{}(base_dir={}))r4   r$   r'   r   )r   r   r   r   r5   )  s    zMemory.__repr__)T)NNNT)T)r'   r%   r6   r   r   r[   rV   rd   rk   rc   r5   r   r   r   r   rW      s   	)


rW   )N)F)r   r   r   r"   r]   rO   re   Zinterfaces.baser   Zpipeline.enginer   Zpipeline.engine.utilsr   objectr   rE   rS   rT   rW   r   r   r   r   <module>   s   Q


