B
    YJ3                 @   s"  d Z ddlmZmZ y0ddlZddlmZ ddlm	Z	 ddlm
Z
 W nD ek
r   ddlmZ ddlmZ ddlm	Z	 ddlm
Z
 Y nX 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 eZe	Ze
ZdddZdd Zdd Z dddZ!dd Z"G dd de#Z$dS )z#YAML file loading and manipulation.    )absolute_importprint_functionN)	YAMLError)CommentedMap)CommentedSeq)makedirs_ok_if_exists)rename_over_existing)	is_stringutf-8c             C   s   | d t t  }zBt|d| }|| |  |  W d Q R X t||  W d yt	
| W n ttfk
r~   Y nX X d S )Nz.tmp-w)struuidZuuid4codecsopenwriteflushcloser   osremoveIOErrorOSError)pathcontentsencodingZtmpfile r   9lib/python3.7/site-packages/anaconda_project/yaml_file.py_atomic_replace-   s    
r   c             C   s8   |   dkri S ttjtjjs$ttj| tjdS d S )N )Loader)strip
issubclassryamlRoundTripLoaderconstructorZSafeConstructorAssertionErrorload)r   r   r   r   _load_string<   s    r'   c             C   s   t j| t jdS )N)ZDumper)r"   dumpZRoundTripDumper)yamlr   r   r   _dump_stringH   s    r*   c          
   C   s   |d krt | }ytj|tjd W nr tk
r } zTtdtjd tdt| tjd tdtjd t|tjd t	dt| W d d }~X Y nX t
j|st
j|}t| t|| d S )N)r   z=ruamel.yaml bug; it failed to parse a file that it generated.)r   z  the parse error was: zGenerated file was:zFBug in ruamel.yaml library; failed to parse a file that it generated: )r*   r"   r&   r#   r   printsysstderrr   RuntimeErrorr   r   isfiledirnamer   r   )r)   filenamer   er0   r   r   r   
_save_fileL   s    "r3   c             C   s^   t | dr| j  t| tr6x:| D ]}t| q$W n$t| trZx|  D ]}t| qJW d S )Nfa)hasattrr4   Zset_block_style
isinstancelist_block_style_all_nodesdictvalues)r)   elementvaluer   r   r   r8   a   s    




r8   c               @   s   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Z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ed d! Zd"d# Zd$d% Zd&d' Zd(d) Zd/d+d,Ze
d-d. Zd*S )0YamlFilea  Abstract YAML file, base class for ``ProjectFile`` and ``LocalStateFile``.

    Be careful with creating your own instance of this class,
    because you have to think about when other code might load or
    save in a way that conflicts with your loads and saves.

    c             C   s   || _ d| _d| _|   dS )a  Load a YamlFile with the given filename.

        Raises an exception on an IOError, but if the file is
        missing this succeeds (then creates the file if and when
        you call ``save()``).

        If the file has syntax problems, this sets the
        ``corrupted`` and ``corrupted_error_message`` properties,
        and attempts to modify the file will raise an
        exception.

        r   r   N)r1   _previous_content_change_countr&   )selfr1   r   r   r   __init__v   s    zYamlFile.__init__c          
   C   st  d| _ d| _d| _d| _| jd | _y>t| jdd}| }W dQ R X t	|| _
t| j
| _W n tk
r } z|jtjkrd| _
n|W dd}~X Y n tk
r& } zld| _ t|| _t|dd}|dk	r|jdk	r|jdkr|j| _|jdk	r|jdkr|j| _d| _
W dd}~X Y nX | j
dkrp| j rFt | _
n*|  | _
t| j
 |  spt| j
| _dS )	a)  Reload the file from disk, discarding any unsaved changes.

        If the file has syntax problems, this sets the
        ``corrupted`` and ``corrupted_error_message`` properties,
        and attempts to modify the file will raise an
        exception.

        Returns:
            None
        FN   rzutf-8TZproblem_markr   )
_corrupted_corrupted_error_message_corrupted_maybe_line_corrupted_maybe_columnr?   r   r   r1   readr'   _yamlr*   r>   r   errnoZENOENTr   r   getattrlinecolumnr9   _default_contentr8   _save_default_content)r@   r   r   r2   Zmarkr   r   r   r&      s>    






zYamlFile.loadc             C   s   dS )Nz	yaml filer   )r@   r   r   r   _default_comment   s    zYamlFile._default_commentc             C   s   t  }||   |S )N)r   Zyaml_set_start_commentrP   )r@   rootr   r   r   rN      s    zYamlFile._default_contentc             C   s   dS )zHOverride to change whether we consider a default, unmodified file dirty.Tr   )r@   r   r   r   rO      s    zYamlFile._save_default_contentc             C   s   | j rtd| j| jf d S )Nz'Cannot modify corrupted YAML file %s
%s)rD   
ValueErrorr1   rE   )r@   r   r   r   _throw_if_corrupted   s    zYamlFile._throw_if_corruptedc             C   s   t j| jS )zBasename of the filename.)r   r   basenamer1   )r@   r   r   r   rT      s    zYamlFile.basenamec             C   s   | j S )zGet whether the file is corrupted.

        A corrupted file has a syntax error so we can't modify and save it.
        See ``corrupted_error_message`` for what's wrong with it.

        Returns:
            True if file is corrupted.
        )rD   )r@   r   r   r   	corrupted   s    
zYamlFile.corruptedc             C   s   | j S )zGet the error message if file is corrupted, or None if it isn't.

        Use this to display the problem if the file is corrupted.

        Returns:
            Corruption message or None.
        )rE   )r@   r   r   r   corrupted_error_message   s    	z YamlFile.corrupted_error_messagec             C   s   | j S )z}Get the line number for syntax error, or None if unavailable.

        Returns:
            Corruption line or None.
        )rF   )r@   r   r   r   corrupted_maybe_line   s    zYamlFile.corrupted_maybe_linec             C   s   | j S )zzGet the column for syntax error, or None if unavailable.

        Returns:
            Corruption column or None.
        )rG   )r@   r   r   r   corrupted_maybe_column   s    zYamlFile.corrupted_maybe_columnc             C   s   | j S )aB  Get the number of times we've resynced with the file on disk (reloaded or saved changes).

        This is used for cache invalidation. If a cached value becomes invalid whenever
        ``change_count`` increments, then the cached value will be recomputed whenever
        we save new changes or reload the file.
        )r?   )r@   r   r   r   change_count  s    zYamlFile.change_countc             C   s   | j t| jkS )z"Get whether changes are all saved.)r>   r*   rI   )r@   r   r   r   has_unsaved_changes  s    zYamlFile.has_unsaved_changesc             C   s   | j d | _ dS )zApply any in-memory changes as if we'd saved, but don't actually save.

        This is used to "try out" a change before we save it. We can load()
        to undo our changes.
        rB   N)r?   )r@   r   r   r   use_changes_without_saving  s    z#YamlFile.use_changes_without_savingc             C   sB   |    t| j}|| jkr>t| j| j| | jd | _|| _dS )zWrite the file to disk, only if any changes have been made.

        Raises ``IOError`` if it fails for some reason.

        Returns:
            None
        rB   N)rS   r*   rI   r>   r3   r1   r?   )r@   r   r   r   r   save  s    

zYamlFile.savec             C   sB   t |r|fS ytdd |D S  tk
r<   tdY nX d S )Nc             s   s   | ]
}|V  qd S )Nr   ).0r;   r   r   r   	<genexpr>3  s    z!YamlFile._path.<locals>.<genexpr>z9YAML file path must be a string or an iterable of strings)r	   r7   	TypeErrorrR   )clsr   r   r   r   _path-  s    zYamlFile._pathc             C   s<   | j }x0|D ](}||kr0t|| tr0|| }qd S qW |S )N)rI   r6   r9   )r@   piecescurrentpr   r   r   _get_dict_or_none7  s    

zYamlFile._get_dict_or_nonec             C   sT   |    | j}x@|D ]8}||ks.t|| tsDt ||< t||  || }qW |S )N)rS   rI   r6   r9   r   r8   )r@   rb   rc   rd   r   r   r   _ensure_dicts_at_path@  s    

zYamlFile._ensure_dicts_at_pathc             C   s4   |    | |}| |dd }|||d < dS )a6  Set a single value at the given path.

        Overwrites any existing value at the path.

        This method does not save the file, call ``save()`` to do that.

        Args:
            path (str or list of str): single key, or list of nested keys
            value: any YAML-compatible value type
        N)rS   ra   rf   )r@   r   r<   existingr   r   r   	set_valueN  s    
zYamlFile.set_valuec             C   sF   |    | |}| |dd }|d }|dk	rB||krB||= dS )zRemove a single value at the given path.

        This method does not save the file, call ``save()`` to do that.

        Args:
            path (str or list of str): single key, or list of nested keys
        Nrg   )rS   ra   re   )r@   r   rh   keyr   r   r   unset_value_  s    
zYamlFile.unset_valueNc             C   s<   |  |}| |dd }|dkr(|S ||d |S dS )a
  Get a single value from the YAML file.

        Args:
            path (str or list of str): single key, or list of nested keys
            default: any YAML-compatible value type

        Returns:
            the value from the file or the provided default
        Nrg   )ra   re   get)r@   r   defaultrh   r   r   r   	get_valuep  s
    

zYamlFile.get_valuec             C   s   |    | jS )z+Get the outermost value from the yaml file.)rS   rI   )r@   r   r   r   rQ     s    zYamlFile.root)N)__name__
__module____qualname____doc__rA   r&   rP   rN   rO   rS   propertyrT   rU   rV   rW   rX   rY   rZ   r[   r\   classmethodra   re   rf   ri   rk   rn   rQ   r   r   r   r   r=   m   s.   <		

	
r=   )r
   )N)%rr   Z
__future__r   r   Zruamel_yamlr"   Zruamel_yaml.errorr   Zruamel_yaml.commentsr   r   ImportErrorZruamel.yamlr)   Zruamel.yaml.errorZruamel.yaml.commentsr   rJ   r   r,   r   Z"anaconda_project.internal.makedirsr   Z anaconda_project.internal.renamer   Z$anaconda_project.internal.py2_compatr	   Z
_YAMLErrorZ_CommentedMapZ_CommentedSeqr   r'   r*   r3   r8   objectr=   r   r   r   r   <module>   s8   

