U
    ZfU                     @   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ddgZdZ	dZ
dZG d	d deZG d
d dZG dd deZG dd deZG dd dZdS )zE
ftputil.stat - stat result, parsers, and FTP stat'ing for `ftputil`
    N
StatResultParser
UnixParserMSParser<   iQ c                   @   sF   e Zd ZdZdddddddd	d
ddddZdd Zdd Zdd ZdS )r   zP
    Support class resembling a tuple like that returned from `os.(l)stat`.
    r                           	   
      )st_modest_inost_devst_nlinkst_uidst_gidst_sizest_atimest_mtimest_ctime_st_name
_st_targetc                 C   s   d| _ d | _t| _d S )N )r   r   UNKNOWN_PRECISION_st_mtime_precision)selfsequence r#   0/tmp/pip-unpacked-wheel-g7w5sq0v/ftputil/stat.py__init__0   s    	zStatResult.__init__c                 C   s*   || j kr| | j |  S td|d S )Nz)'StatResult' object has no attribute '{}')_index_mappingAttributeErrorformat)r!   	attr_namer#   r#   r$   __getattr__=   s
    
zStatResult.__getattr__c                 C   s\   t dd | j D }g }t| D ]\}}|d|| | q$dt| jd|S )Nc                 s   s   | ]\}}||fV  qd S )Nr#   ).0kvr#   r#   r$   	<genexpr>H   s     z&StatResult.__repr__.<locals>.<genexpr>z{}={!r}z{}({})z, )	dictr&   items	enumerateappendr(   type__name__join)r!   Zindex_to_nameargument_stringsindexitemr#   r#   r$   __repr__E   s
    zStatResult.__repr__N)r4   
__module____qualname____doc__r&   r%   r*   r9   r#   r#   r#   r$   r      s"   c                   @   sz   e Zd ZdZdddddddd	d
ddddZedZdd Zd!ddZ	dd Z
dd Zedd Zd"ddZd#ddZd S )$r   zu
    Represent a parser for directory lines. Parsers for specific directory
    formats inherit from this class.
    r   r   r	   r
   r   r   r   r   r   r   r      )janfebmaraprmayjunjulaugsepoctnovdecz^total\s+\d+c                 C   s    |  sdS | j|}t|S )aR  
        Return a true value if the line should be ignored, i. e. is assumed to
        _not_ contain actual directory/file/link data. A typical example are
        summary lines like "total 23" which are emitted by some FTP servers.

        If the line should be used to extract stat data from it, return a false
        value.
        T)strip_total_regexsearchbool)r!   linematchr#   r#   r$   ignores_linej   s    
zParser.ignores_line        c                 C   s   t ddS )a  
        Return a `StatResult` object as derived from the string `line`. The
        parser code to use depends on the directory format the FTP server
        delivers (also see examples at end of file).

        If the given text line can't be parsed, raise a `ParserError`.

        For the definition of `time_shift` see the docstring of
        `FTPHost.set_time_shift` in `ftputil.py`. Not all parsers use the
        `time_shift` parameter.
        zmust be defined by subclassN)NotImplementedError)r!   rN   
time_shiftr#   r#   r$   
parse_line{   s    zParser.parse_linec              	   C   s   t |dkrtjd|d}|dd D ]}|dk}|d> | }q.|d dkr^|tjB }|d dkrt|tjB }tjtj	tj
tjtjtjtjdd	}|d }||kr||| B }ntjd
||S )a  
        Return an integer from the `mode_string`, compatible with the `st_mode`
        value in stat results. Such a mode string may look like "drwxr-xr-x".

        If the mode string can't be parsed, raise an
        `ftputil.error.ParserError`.
        r   zinvalid mode string '{}'r   r   -r	   sr   )bcdlprV   rU   ?z unknown file type character '{}')lenftputilerrorParserErrorr(   statS_ISUIDS_ISGIDS_IFBLKS_IFCHRS_IFDIRS_IFLNKS_IFIFOS_IFSOCKS_IFREG)r!   mode_stringr   bitZfile_type_to_mode	file_typer#   r#   r$   parse_unix_mode   s8    


zParser.parse_unix_modec              	   C   s8   z
t |W S  tk
r2   tjd||Y nX dS )a  
        Return `int_string` converted to an integer.

        If it can't be converted, raise a `ParserError`, using
        `int_description` in the error message. For example, if the integer
        value is a day, pass "day" for `int_description`.
        znon-integer {} value {!r}N)int
ValueErrorr^   r_   r`   r(   )r!   Z
int_stringZint_descriptionr#   r#   r$   _as_int   s    

zParser._as_intc                 C   s|   zt j | |||||t jjdW S  tk
rv   | dd|dd|dd|dd|dd|d}tjd|Y nX dS )	z
        Return UTC `datetime.datetime` object for the given year, month, day,
        hour, minute and second.

        If there are invalid values, for example minute > 59, raise a
        `ParserError`.
        )tzinfoZ04drU   Z02d :zinvalid datetime {0!r}N)datetimetimezoneutcrp   r^   r_   r`   r(   )yearmonthdayhourminutesecondZinvalid_datetimer#   r#   r$   	_datetime   s     	      0zParser._datetimeFc                 C   sH  z| j |  }W n& tk
r8   tjd|Y nX | |d}d|k}|rpt}| |ddd  }	}
}nt	}|
d\}
}d| |
d| |d  }}
}tjtjjtj|d	 }|j}	| |	|||
|d|jdd
tjdd	 kr|	d8 }	| |	|||
|dtj|d	 }| }|dk r2t}d}|r@||fS |S dS )a  
        Return a floating point number, like from `time.mktime`, by parsing the
        string arguments `month_abbreviation`, `day` and `year_or_time`. The
        parameter `time_shift` is the difference "time on server" - "time on
        client" and is available as the `time_shift` parameter in the
        `parse_line` interface.

        If `with_precision` is true return a two-element tuple consisting of
        the floating point number as described in the previous paragraph and
        the precision of the time in seconds. The default is `False` for
        backward compatibility with custom parsers.

        The precision value takes into account that, for example, a time string
        like "May 26  2005" has only a precision of one day. This information
        is important for the `upload_if_newer` and `download_if_newer` methods
        in the `FTPHost` class.

        Times in Unix-style directory listings typically have one of these
        formats:

        - "Nov 23 02:33" (month name, day of month, time)

        - "May 26  2005" (month name, day of month, year)

        If this method can't make sense of the given arguments, it raises an
        `ftputil.error.ParserError`.
        z invalid month abbreviation {0!r}rz   rt   rx   r   Nr{   r|   seconds)r}   x   r   rQ   )_month_numberslowerKeyErrorr^   r_   r`   r(   rq   DAY_PRECISIONMINUTE_PRECISIONsplitru   nowrv   rw   	timedeltarx   r~   replace	timestampr   )r!   Zmonth_abbreviationrz   year_or_timerS   with_precisionry   Zyear_is_knownst_mtime_precisionZserver_yearr{   r|   Z_yearZ
server_nowZserver_utc_datetimer   r#   r#   r$   parse_unix_time   sf    




          

zParser.parse_unix_timec                    s2   fdd| dD \}}}|dkr(n|dkr:d| }nd| }z(|dd	 |d
d |d   }}	}
W n& tk
r   tjd|Y nX  |d |	d }}	|dkr|
dkrd}|dkr|
dkr|d7 } |||||	d}|tj	|d }|
 }|dk rt}d}nt}|r*||fS |S dS )am  
        Return a floating point number, like from `time.mktime`, by parsing the
        string arguments `date` and `time_`. The parameter `time_shift` is the
        difference

            "time on server" - "time on client"

        and can be set as the `time_shift` parameter in the `parse_line`
        interface.

        If `with_precision` is true return a two-element tuple consisting of
        the floating point number as described in the previous paragraph and
        the precision of the time in seconds. The default is `False` for
        backward compatibility with custom parsers.

        The precision value takes into account that, for example, a time string
        like "10-23-2001 03:25PM" has only a precision of one minute. This
        information is important for the `upload_if_newer` and
        `download_if_newer` methods in the `FTPHost` class.

        Usually, the returned precision is `MINUTE_PRECISION`, except when the
        date is before the epoch, in which case the returned `st_mtime` value
        is set to 0.0 and the precision to `UNKNOWN_PRECISION`.

        Times in MS-style directory listings typically have the format
        "10-23-01 03:25PM" (month-day_of_month-two_digit_year, hour:minute,
        am/pm).

        If this method can't make sense of the given arguments, it raises an
        `ftputil.error.ParserError`.
        c                    s   g | ]}  |d qS )zyear/month/day)rq   )r+   partr!   r#   r$   
<listcomp>n  s    z(Parser.parse_ms_time.<locals>.<listcomp>rU   i  F   il  i  r   r   r	   r   zinvalid time string '{}'r{   r|   r=   APr   rQ   N)r   
IndexErrorr^   r_   r`   r(   rq   r~   ru   r   r   r   r   )r!   datetime_rS   r   ry   rz   rx   r{   r|   Zam_pmZserver_datetimeZclient_datetimer   r   r#   r   r$   parse_ms_timeC  s6    +

(
zParser.parse_ms_timeN)rQ   )F)F)r4   r:   r;   r<   r   recompilerK   rP   rT   rn   rq   staticmethodr~   r   r   r#   r#   r#   r$   r   R   s0   

-
 
ec                   @   s&   e Zd ZdZedd ZdddZdS )	r   z<
    `Parser` class for Unix-specific directory format.
    c                 C   s   |   }d}|d }t||k r2tjd| zt|d  W n$ tk
rf   |  d|d }Y n"X |  d|d }d}||d |S )a  
        Split a line in metadata, nlink, user, group, size, month, day,
        year_or_time and name and return the result as an nine-element list of
        these values. If the name is a link, it will be encoded as a string
        "link_name -> link_target".
        r   r   line '{}' can't be parsedr   Nr   )	r   r]   r^   r_   r`   r(   ro   rp   insert)rN   Z
line_partsZFIELD_COUNT_WITHOUT_USERIDZFIELD_COUNT_WITH_USERIDZUSER_FIELD_INDEXr#   r#   r$   _split_line  s    
zUnixParser._split_linerQ   c                 C   s  z |  |\	}}}}}}}	}
}W n2 tk
rR } ztjt|W 5 d}~X Y nX | |}d}d}t|}|}|}t|}d}| j||	|
|dd\}}d}|	ddkrtjd
|n(|	ddkr|d\}}n
|d }}t||||||||||f
}||_||_||_|S )a  
        Return a `StatResult` instance corresponding to the given text line.
        The `time_shift` value is needed to determine to which year a datetime
        without an explicit year belongs.

        If the line can't be parsed, raise a `ParserError`.
        NTr   z -> r   z%name '{}' contains more than one "->")r   rp   r^   r_   r`   strrn   ro   r   countr(   r   r   r    r   r   )r!   rN   rS   rk   Znlinkusergroupsizery   rz   r   nameexcr   r   r   r   r   r   r   r   r   r   r   st_nameZ	st_targetstat_resultr#   r#   r$   rT     sn    
"
    

zUnixParser.parse_lineN)rQ   )r4   r:   r;   r<   r   r   rT   r#   r#   r#   r$   r     s   
c                   @   s   e Zd ZdZdddZdS )r   z:
    `Parser` class for MS-specific directory format.
    rQ   c                 C   s  z| dd\}}}}W n& tk
r>   tjd|Y nX d}|dkrX|tjB }n
|tjB }d}d}	d}
d}d}|dkrzt	|}W q tk
r   tjd|Y qX nd}d}| j
|||dd\}}d}t|||	|
||||||f
}||_d|_||_|S )	aY  
        Return a `StatResult` instance corresponding to the given text line
        from a FTP server which emits "Microsoft format" (see end of file).

        If the line can't be parsed, raise a `ParserError`.

        The parameter `time_shift` isn't used in this method but is listed for
        compatibility with the base class.
        Nr	   r      z<DIR>zinvalid size {}Tr   )r   rp   r^   r_   r`   r(   ra   rf   rj   ro   r   r   r   r   r    )r!   rN   rS   r   r   Zdir_or_sizer   r   r   r   r   r   r   r   r   r   r   r   r   r#   r#   r$   rT     sX    
   
zMSParser.parse_lineN)rQ   )r4   r:   r;   r<   rT   r#   r#   r#   r$   r     s   c                   @   sh   e Zd ZdZdd Zdd Zdd Zdd	 ZdddZdddZ	dd Z
dd ZdddZdddZdS )_StatzD
    Methods for stat'ing directories, links and regular files.
    c                 C   s,   || _ |j| _t | _d| _tj | _	d S )NT)
_hostpath_pathr   _parser_allow_parser_switchingr^   Z
stat_cacheZ	StatCache_lstat_cache)r!   hostr#   r#   r$   r%   U  s
    z_Stat.__init__c                 C   s   | j |S )zm
        Return a list of lines, as fetched by FTP's `LIST` command, when
        applied to `path`.
        )r   Z_dirr!   r   r#   r#   r$   	_host_dir`  s    z_Stat._host_dirc                 c   s   |  |}| j}|jrFt||jjkrFttdt| }|	| |D ]\}| j
|r\qJ| j
|| j }|j| jj| jjfkrqJ| j||j}|||< |V  qJdS )z
        Yield stat results extracted from the directory listing `path`. Omit
        the special entries for the directory itself and its parent directory.
        g?N)r   r   Z_enabledr]   _cacher   ro   mathceilresizer   rP   rT   r   rS   r   curdirpardirr   r5   )r!   r   linescacheZnew_sizerN   r   Z	loop_pathr#   r#   r$   _stat_results_from_dirg  s    

z_Stat._stat_results_from_dirc                 C   sR   | j |}| j |s*tjd|g }| |D ]}|j}|	| q8|S )a  
        Return a list of directories, files etc. in the directory named `path`.

        Like `os.listdir` the returned list elements have the type of the path
        argument.

        If the directory listing from the server can't be parsed, raise a
        `ParserError`.
        z8550 {}: no such directory or wrong directory parser used)
r   abspathisdirr^   r_   PermanentErrorr(   r   r   r2   )r!   r   namesr   r   r#   r#   r$   _real_listdir  s    z_Stat._real_listdirTc                 C   s   | j |}|| jkr | j| S |dkr4tjd| j |\}}| j |sX|sXdS d}| |D ]}|j	|krf|}qf|dk	r|S |rtj
d|ndS dS )a#  
        Return an object similar to that returned by `os.lstat`.

        If the directory listing from the server can't be parsed, raise a
        `ParserError`. If the directory can be parsed and the `path` is not
        found, raise a `PermanentError`. That means that if the directory
        containing `path` can't be parsed we get a `ParserError`, independent
        on the presence of `path` on the server.

        (`_exception_for_missing_path` is an implementation aid and _not_
        intended for use by ftputil clients.)
        /z can't stat remote root directoryNz!550 {}: no such file or directory)r   r   r   r^   r_   ZRootDirErrorr   r   r   r   r   r(   )r!   r   _exception_for_missing_pathdirnamebasenameZlstat_result_for_pathr   r#   r#   r$   _real_lstat  s&    


z_Stat._real_lstatc                 C   s   |}t  }| ||}|dkr"dS t|js2|S | j|\}}| j||j}| j	| j
|}||krtjd||| q
dS )a  
        Return info from a "stat" call on `path`.

        If the directory containing `path` can't be parsed, raise a
        `ParserError`. If the listing can be parsed but the `path` can't be
        found, raise a `PermanentError`. Also raise a `PermanentError` if
        there's an endless (cyclic) chain of symbolic links "behind" the
        `path`.

        (`_exception_for_missing_path` is an implementation aid and _not_
        intended for use by ftputil clients.)
        Nz6recursive link structure detected for remote path '{}')setr   ra   S_ISLNKr   r   r   r5   r   r   normpathr^   r_   ZRecursiveLinksErrorr(   add)r!   r   r   Zoriginal_pathZvisited_pathsZlstat_resultr   _r#   r#   r$   
_real_stat  s"    z_Stat._real_statc                 O   sf   z$|||}|| j k	r |r d| _|W S  tjjk
r`   | jrZd| _t | _||| Y S  Y nX dS )z
        Call `method` with the `args` and `kwargs` once. If that results in a
        `ParserError` and only one parser has been used yet, try the other
        parser. If that still fails, propagate the `ParserError`.
        FN)r   r   r^   r_   r`   r   r   )r!   methodargskwargsresultr#   r#   r$   Z__call_with_parser_retry  s    	
z_Stat.__call_with_parser_retryc                 C   s   |  | j|S )z
        Return a list of items in `path`.

        Raise a `PermanentError` if the path doesn't exist, but maybe raise
        other exceptions depending on the state of the server (e. g. timeout).
        )_Stat__call_with_parser_retryr   r   r#   r#   r$   _listdir2  s    z_Stat._listdirc                 C   s   |  | j||S )z
        Return a `StatResult` without following links.

        Raise a `PermanentError` if the path doesn't exist, but maybe raise
        other exceptions depending on the state of the server (e. g. timeout).
        )r   r   r!   r   r   r#   r#   r$   _lstat;  s
      z_Stat._lstatc                 C   s   |  | j||S )z
        Return a `StatResult` with following links.

        Raise a `PermanentError` if the path doesn't exist, but maybe raise
        other exceptions depending on the state of the server (e. g. timeout).
        )r   r   r   r#   r#   r$   _statF  s
      z_Stat._statN)T)T)T)T)r4   r:   r;   r<   r%   r   r   r   r   r   r   r   r   r   r#   r#   r#   r$   r   N  s   4
:
*	
r   )r<   ru   r   r   ra   Zftputil.errorr^   Zftputil.stat_cache__all__r   r   r   tupler   r   r   r   r   r#   r#   r#   r$   <module>   s"   6  ?pO