B
    âÖÓYýO  ã               @   s  d Z ddlmZ ddlZddlZddlZddlZddlm  m	Z	 ddl
m  mZ ddlmZ ddlmZmZmZ yddlZW n  ek
r    ddlmZ Y nX ddd„Zdd	„ Zd
d„ Zdd„ ZG dd„ deƒZdd„ Ze d¡Zdd„ Z dd„ Z!dd„ Z"dd„ Z#dd„ Z$dS )z3Environment class representing a conda environment.é    )Úabsolute_importN)Ú	is_string)Ú_load_stringÚ
_save_fileÚ
_YAMLErrorc                sV   dd„ }ˆd kr|‰t ‡fdd„|D ƒƒ‰ t‡ ‡fdd„| D ƒƒ}|t|ƒ }t|ƒS )Nc             S   s   | S )N© )Úitemr   r   ú8lib/python3.7/site-packages/anaconda_project/env_spec.pyÚdefault_key    s    z4_combine_keeping_last_duplicate.<locals>.default_keyc                s   g | ]}ˆ |ƒ‘qS r   r   )Ú.0r   )Úkey_funcr   r	   ú
<listcomp>%   s    z3_combine_keeping_last_duplicate.<locals>.<listcomp>c                s   g | ]}ˆ|ƒˆ kr|‘qS r   r   )r   r   )Úitems2_keysr   r   r	   r   &   s    )ÚsetÚlistÚtuple)Zitems1Zitems2r   r
   Úcombinedr   )r   r   r	   Ú_combine_keeping_last_duplicate   s    r   c             C   s    t  | ¡}|d kr| S |jS d S )N)Ú	conda_apiÚ
parse_specÚname)ÚspecÚparsedr   r   r	   Ú_conda_combine_key+   s    
r   c             C   s    t  | ¡}|d kr| S |jS d S )N)Úpip_apir   r   )r   r   r   r   r	   Ú_pip_combine_key4   s    
r   c             C   s   t | |tdS )N)r   )r   r   )ÚfirstÚsecondr   r   r	   Ú_combine_conda_package_lists=   s    r   c               @   s6  e Zd ZdZd<dd„Zedd„ ƒZedd	„ ƒZd
d„ Zedd„ ƒZ	edd„ ƒZ
edd„ ƒZd=dd„Zd>dd„Zedd„ ƒZedd„ ƒZedd„ ƒZedd„ ƒZedd„ ƒZed d!„ ƒZed"d#„ ƒZed$d%„ ƒZed&d'„ ƒZd(d)„ Zd*d+„ Zd,d-„ Zed.d/„ ƒZed0d1„ ƒZd2d3„ Zd4d5„ Zd6d7„ Zd8d9„ Zd:d;„ Z dS )?ÚEnvSpeczdRepresents a set of required conda packages we could potentially instantiate as a Conda environment.r   Nc
             C   s\  |dk	st ‚|dk	st ‚|| _t|ƒ| _t|ƒ| _t|ƒ| _|| _d| _d| _d| _	|| _
|| _|	| _tt |¡ƒ| _x2tdd„ | jD ƒƒD ]}|dksŒ|| j
ksŒt ‚qŒW tƒ }
x*| jD ] }t |¡}|dk	r¸||
|j< q¸W |
| _tƒ }x,| jD ]"}t |¡}|dk	rð| |j¡ qðW || _tƒ }x.| jD ]$}t |¡}|dk	r*|||j< q*W || _dS )az  Construct a package set with the given name and packages.

        Args:
            name (str): name of the package set
            conda_packages (list): list of package specs to pass to conda install
            channels (list): list of channel names
            pip_packages (list): list of pip package specs to pass to pip
            description (str or None): one-sentence-ish summary of what this env is
            inherit_from_name (str or None): name of what we inherit from
            inherit_from (EnvSpec or None): pull in packages and channels from
            lock_set (CondaLockSet): locked packages or None
        Nc             S   s   g | ]
}|j ‘qS r   )r   )r   r   r   r   r	   r   p   s    z$EnvSpec.__init__.<locals>.<listcomp>)ÚAssertionErrorÚ_namer   Ú_conda_packagesÚ	_channelsÚ_pip_packagesÚ_descriptionÚ_logical_hashÚ_locked_hashÚ_import_hashÚ_inherit_from_namesÚ_inherit_fromÚ	_lock_setr   Zsort_platform_listÚ
_platformsÚdictÚconda_packages_for_creater   r   Ú_conda_specs_for_create_by_namer   Úconda_packagesÚaddÚ_conda_logical_specs_name_setÚpip_packagesr   Ú_pip_specs_by_name)Úselfr   r0   Úchannelsr3   ÚdescriptionÚinherit_from_namesÚinherit_fromÚ	platformsÚlock_setZconda_specs_by_namer   r   Zname_setZpip_specs_by_namer   r   r	   Ú__init__D   sD    






zEnvSpec.__init__c             C   s   | j S )zGet name of the package set.

        May be None for the anonymous shared base spec
        (toplevel packages, channels sections).
        )r!   )r5   r   r   r	   r   Œ   s    zEnvSpec.namec             C   s   | j dkr| jS | j S dS )z'Get the description of the environment.N)r%   r!   )r5   r   r   r	   r7   •   s    
zEnvSpec.descriptionc             C   s˜   dd l }| ¡ }x|D ]}| | d¡¡ qW x| jD ]}| | d¡¡ q6W x| jD ]}| | d¡¡ qVW x|D ]}| | d¡¡ qtW | ¡ }|S )Nr   zutf-8)ÚhashlibZsha1ÚupdateÚencoder3   r6   Z	hexdigest)r5   r0   r:   r=   ÚmÚpÚcÚresultr   r   r	   Ú_compute_hash   s    

zEnvSpec._compute_hashc             C   s"   | j dkr|  | j| j¡| _ | j S )zÒGet a hash of our "logical" requirements.

        (Changing logical requirements could change the lock set
        if we recreate it.)

        Order matters (change in order will count as a change).

        N)r&   rD   r0   r:   )r5   r   r   r	   Úlogical_hash«   s    

zEnvSpec.logical_hashc             C   s"   | j dkr| j| jdd| _ | j S )z×Get a hash of our locked packages (what we'd pass to conda create).

        This is used to see if we need to reprepare
        environments. Order matters (change in order will count as
        a change).
        Nr   )r:   )r'   rD   r.   )r5   r   r   r	   Úlocked_hash¹   s    
zEnvSpec.locked_hashc             C   s"   | j dkr| j| jdd| _ | j S )zíGet a hash of parts of the env spec that can appear in environment.yml.

        This is used to see if we need to re-import the environment.yml, requirements.txt
        or whatever. Those files don't have platform information.
        Nr   )r:   )r(   rD   r0   )r5   r   r   r	   Úimport_hashÅ   s    
zEnvSpec.import_hashc                s"   d| ‰ ‡ fdd„}| j ||dS )NÚ_c                s
   t | ˆ ƒS )N)Úgetattr)r   )Úprivate_attrr   r	   ÚgetterÓ   s    z&EnvSpec._get_inherited.<locals>.getter)r   )Ú_get_inherited_with_getter)r5   Zpublic_attrr   rK   r   )rJ   r	   Ú_get_inheritedÐ   s    zEnvSpec._get_inheritedc                sp   ‡ fdd„‰ g }ˆ | g|ƒ |d | ks,t ‚g }x|D ]}| ||ƒ¡ q6W g }x|D ]}t|||d}qVW |S )Nc                s0   x*| D ]"}||krˆ |j |ƒ | |¡ qW d S )N)r*   Úappend)ÚspecsZaccumulatorr   )Ú_linearized_ancestorsr   r	   rP   Ù   s    
zAEnvSpec._get_inherited_with_getter.<locals>._linearized_ancestorséÿÿÿÿ)r   )r    rN   r   )r5   rK   r   Z	ancestorsZ
to_combiner   r   r   r   )rP   r	   rL   Ø   s    

z"EnvSpec._get_inherited_with_getterc             C   s   |   dt¡S )zDGet the conda packages to install in the environment as an iterable.r0   )rM   r   )r5   r   r   r	   r0   ë   s    zEnvSpec.conda_packagesc             C   s
   |   d¡S )z0Get the channels to install conda packages from.r6   )rM   )r5   r   r   r	   r6   ð   s    zEnvSpec.channelsc             C   s
   |   d¡S )z,Get the platforms the environment can be on.r:   )rM   )r5   r   r   r	   r:   õ   s    zEnvSpec.platformsc             C   s   |   dt¡S )zBGet the pip packages to install in the environment as an iterable.r3   )rM   r   )r5   r   r   r	   r3   ú   s    zEnvSpec.pip_packagesc             C   s   | j S )z5Conda package names that we require, as a Python set.)r2   )r5   r   r   r	   Úconda_package_names_setÿ   s    zEnvSpec.conda_package_names_setc             C   s   t | j ¡ ƒS )z5Conda package names that we require, as a Python set.)r   r/   Úkeys)r5   r   r   r	   Ú"conda_package_names_for_create_set  s    z*EnvSpec.conda_package_names_for_create_setc             C   s   t | j ¡ ƒS )z3Pip package names that we require, as a Python set.)r   r4   rS   )r5   r   r   r	   Úpip_package_names_set	  s    zEnvSpec.pip_package_names_setc             C   s   | j S )z'Get ``CondaLockSet`` for this env spec.)r+   )r5   r   r   r	   r;     s    zEnvSpec.lock_setc             C   s,   | j dk	r"| j jr"| j jr"| j jS | jS dS )z=Get conda packages (preferring the lock set list if present).N)r+   ZenabledZsupports_current_platformZ"package_specs_for_current_platformr0   )r5   r   r   r	   r.     s    z!EnvSpec.conda_packages_for_createc             C   s4   g }x*|D ]"}|  |d ¡}|d k	r
| |¡ q
W |S )N)ÚgetrN   )r5   ÚnamesÚmappingrO   r   r   r   r   r	   Ú_specs_for_package_names  s    
z EnvSpec._specs_for_package_namesc             C   s   |   || j¡S )z>Get the full install specs given an iterable of package names.)rY   r/   )r5   rW   r   r   r	   Úspecs_for_conda_package_names#  s    z%EnvSpec.specs_for_conda_package_namesc             C   s   |   || j¡S )z>Get the full install specs given an iterable of package names.)rY   r4   )r5   rW   r   r   r	   Úspecs_for_pip_package_names'  s    z#EnvSpec.specs_for_pip_package_namesc             C   s   | j S )z$Env spec that we inherit stuff from.)r*   )r5   r   r   r	   r9   +  s    zEnvSpec.inherit_fromc             C   s   | j S )z*Env spec names that we inherit stuff from.)r)   )r5   r   r   r	   r8   0  s    zEnvSpec.inherit_from_namesc             C   s(   t j dt j |d¡¡}t j || j¡S )zEThe filesystem path to the default conda env containing our packages.ZANACONDA_PROJECT_ENVS_PATHZenvs)ÚosÚenvironrV   ÚpathÚjoinr   )r5   Zproject_dirZ	envs_pathr   r   r	   r^   5  s    zEnvSpec.pathc             C   s†   t t |j| j¡ƒ}t t |j| j¡ƒ}t t |j| j¡ƒ}|rXdgt tdd„ |ƒƒ }|rtdgt tdd„ |ƒƒ }d || | ¡S )zFA string showing the comparison between this env spec and another one.z  pip:c             S   s   d|  S )Nz    r   )Úxr   r   r	   Ú<lambda>A  s    z#EnvSpec.diff_from.<locals>.<lambda>z  channels:c             S   s   d|  S )Nz    r   )r`   r   r   r	   ra   C  s    Ú
)r   ÚdifflibÚndiffr6   r0   r3   Úmapr_   )r5   ÚoldÚchannels_diffÚ
conda_diffÚpip_diffr   r   r	   Ú	diff_from;  s    zEnvSpec.diff_fromc             C   s¦   dd„ dD ƒ}dd„ }|t  |j| j¡ƒ}x|D ]}||kr0| |¡ q0W t|ƒdkrZdS |t  |j| j¡ƒ}t|ƒdkr~dS |t  |j| j¡ƒ}t|ƒdkr¢dS dS )	a%  Check whether the diff is exclusively removing 'bokeh' or 'notebook'.

        This is used for a hack, because we can auto-add 'bokeh' or 'notebook'
        packages when we anaconda-project init, and that alone shouldn't result
        in being out of sync with the environment.yml.
        c             S   s   g | ]}d | ‘qS )z- r   )r   Úrr   r   r	   r   M  s    z?EnvSpec.diff_only_removes_notebook_or_bokeh.<locals>.<listcomp>)ZbokehZnotebookc             S   s   t tdd„ | ƒƒS )Nc             S   s   |   d¡p|   d¡S )Nz- z+ )Ú
startswith)Úliner   r   r	   ra   P  s    zUEnvSpec.diff_only_removes_notebook_or_bokeh.<locals>.filter_context.<locals>.<lambda>)r   Úfilter)Úitemsr   r   r	   Úfilter_contextO  s    zCEnvSpec.diff_only_removes_notebook_or_bokeh.<locals>.filter_contextr   FT)rc   rd   r0   ÚremoveÚlenr6   r3   )r5   rf   Z	to_removerp   rh   rk   rg   ri   r   r   r	   Ú#diff_only_removes_notebook_or_bokehF  s    
z+EnvSpec.diff_only_removes_notebook_or_bokehc             C   sæ   t | jƒ}t | jƒ}|r(| t|d¡ t | jƒ}t | jƒ}tjdtj	d}| j
dk	rf| j
|d d< n
|d d= ||d d< ||d d< t|ƒd	kr ||d d
< t| jƒd	krÞt| jƒdkrÈ| jd	 }n
t | jƒ}||d d< |d S )z6Get JSON for an anaconda-project.yml env spec section.)ÚpipzCsomething:
    description: null
    packages: []
    channels: []
)ÚLoaderNZ	somethingr7   Úpackagesr6   r   r:   é   r9   )r   r"   r$   rN   r-   r#   r,   ÚryamlÚloadÚRoundTripLoaderr%   rr   r8   )r5   rv   r3   r6   r:   Ztemplate_jsonrW   r   r   r	   Úto_jsond  s*    







zEnvSpec.to_jsonc             C   sx   t | jƒ}t | jƒ}|r(| t|d¡ t | jƒ}tjdtjd}| j	dk	sPt
‚| j	|d< ||d< ||d< t||ƒ dS )z Save as an environment.yml file.)rt   z%name: 
dependencies: []
channels: []
)ru   Nr   Údependenciesr6   )r   r0   r3   rN   r-   r6   rx   ry   rz   r   r    r   )r5   Úfilenamerv   r3   r6   Úyamlr   r   r	   Úsave_environment_ymlŒ  s    



zEnvSpec.save_environment_yml)r   Nr   r   r   N)N)N)!Ú__name__Ú
__module__Ú__qualname__Ú__doc__r<   Úpropertyr   r7   rD   rE   rF   rG   rM   rL   r0   r6   r:   r3   rR   rT   rU   r;   r.   rY   rZ   r[   r9   r8   r^   rj   rs   r{   r   r   r   r   r	   r   A   sD        
?	

(r   c          	   C   sp  y.t  | dd¡}| ¡ }W dQ R X t|ƒ}W n ttfk
rF   dS X d}d|kr\|d }|s€d|kr€|d r€tj |d ¡}|stj | ¡}| 	dg ¡}t
|tƒsªg }| 	dg ¡}t
|tƒsÄg }g }g }xh|D ]`}	t|	ƒrê| |	¡ qÒt
|	tƒrÒd|	krÒt
|	d tƒrÒx&|	d D ]}
t|
ƒr| |
¡ qW qÒW g }x"|D ]}t|ƒr@| |¡ q@W t||||d	d
S )z=Load an environment.yml as an EnvSpec, or None if not loaded.rk   zutf-8Nr   Úprefixr|   r6   rt   r   )r   r0   r6   r3   r:   )ÚcodecsÚopenÚreadr   ÚIOErrorr   r\   r^   ÚbasenamerV   Ú
isinstancer   r   rN   r-   r   )r}   ÚfileÚcontentsr~   r   Zraw_dependenciesZraw_channelsr0   r3   ZdepZpip_depr6   Zchannelr   r   r	   Ú_load_environment_ymlŸ  sD    	


 


rŽ   z^-([-a-zA-Z0-9]+)\s(.*)c       
   	   C   sþ   y&t  | dd¡}| ¡ }W dQ R X W n ttfk
r>   dS X g }x¨|D ] }| ¡ }| d¡sJ|dkrjqJt |¡}|dk	rà| 	d¡}| 	d¡}|dkr¤| 
|¡ qê|dkrêtj tj | ¡|¡}t|ƒ}	|	dk	rê| |	j¡ qJ| 
|¡ qJW td	d
d
|dS )z=Load a requirements.txt as an EnvSpec, or None if not loaded.rk   zutf-8Nú#Ú rw   é   ÚeÚdefaultr   )r   r0   r6   r3   )r†   r‡   Ú	readlinesr‰   r   Ústriprl   Ú_requirement_option_reÚsearchÚgrouprN   r\   r^   r_   ÚdirnameÚ_load_requirements_txtÚextendr3   r   )
r}   rŒ   Úlinesrv   rm   r@   ZoptionÚpackager^   Z
child_specr   r   r	   rš   ×  s.    	



rš   c             C   s   |   d¡rt| ƒS t| ƒS d S )Nz.txt)Úendswithrš   rŽ   )r}   r   r   r	   Ú_load_importable  s    
rŸ   c             C   s<   d}x2|D ]*}t j | |¡}t|ƒ}|d k	r
||fS q
W dS )N)zenvironment.ymlzenvironment.yamlzrequirements.txt)NN)r\   r^   r_   rŸ   )Údirectory_pathÚ	filenamesr}   Zfullr   r   r   r	   Ú_find_importable_spec	  s    
r¢   c             C   sJ   t |ƒ\}}|d krdS x(| D ] }|j|jkr|j|jkrdS qW ||fS )N)NN)r¢   r   rG   )Zproject_specsr    r   r}   Úexistingr   r   r	   Ú!_find_out_of_sync_importable_spec  s    
r¤   c          	   C   s0   | d krd}n| f}t ddgg t ¡ dd|dS )Nr   r“   Zanacondaz-Default environment spec for running commands)r   r0   r6   r:   r7   r8   r9   )r   r   Zdefault_platforms_with_current)Zshared_base_specr9   r   r   r	   Ú_anaconda_default_env_spec"  s    r¥   )N)%rƒ   Z
__future__r   r†   rc   r\   ÚreZ#anaconda_project.internal.conda_apiZinternalr   Z!anaconda_project.internal.pip_apir   Z$anaconda_project.internal.py2_compatr   Zanaconda_project.yaml_filer   r   r   Zruamel_yamlrx   ÚImportErrorZruamel.yamlr~   r   r   r   r   Úobjectr   rŽ   Úcompiler–   rš   rŸ   r¢   r¤   r¥   r   r   r   r	   Ú<module>   s6   
		  `5
+