U
    çZßf´  ã                   @   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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ZdgZejjr–G dd„ dejƒZnejZG dd„ dƒZdS )z]
`FTPHost` is the central class of the `ftputil` library.

See `__init__.py` for an example.
é    NÚFTPHostc                       s   e Zd Z‡ fdd„Z‡  ZS )Údefault_session_factoryc                    sL   d|kr(t jj}||d< tƒ j||Ž n tƒ j||Ž t j | |d ¡ d S )NÚencoding)ÚftputilÚpath_encodingZDEFAULT_ENCODINGÚsuperÚ__init__ÚsessionZ_maybe_send_opts_utf8_on)ÚselfÚargsÚkwargsr   ©Ú	__class__© ú0/tmp/pip-unpacked-wheel-g7w5sq0v/ftputil/host.pyr   ,   s    z default_session_factory.__init__)Ú__name__Ú
__module__Ú__qualname__r   Ú__classcell__r   r   r   r   r   +   s   r   c                   @   s|  e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
d„ ZdUddœ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eejjdfdd „ƒZd!d"„ ZdVd#d$„ZdWd%d&„Zd'd(„ ZdXd)d*„ZdYd+d,„Zd-d.„ ZdZd0d1„Zd2d3„ Zd4d5„ Zd[d6d7„Z d\d8d9„Z!d:d;„ Z"d<d=„ Z#e#Z$d]d>d?„Z%d@dA„ Z&dBdC„ Z'dDdE„ Z(d^dGdH„Z)d_dIdJ„Z*d`dKdL„Z+dMdN„ Z,dOdP„ Z-dQdR„ Z.dSdT„ Z/dS )ar   z
    FTP host class.
    c              	   O   s²   || _ || _|  ¡ | _tj | ¡| _tj | ¡| _	| j	j
| _| j ¡  tjj, | j ¡ }| j tjj|| jd¡| _W 5 Q R X g | _d| _d| _d\| _| _| _d| _d| _dS )z>
        Abstract initialization of `FTPHost` object.
        ©r   NF)Ú.z..ú/ç        )Ú_argsÚ_kwargsÚ_make_sessionÚ_sessionr   ÚpathZ_PathÚstatZ_StatÚ_statZ_lstat_cacheÚ
stat_cacheÚenableÚerrorÚftplib_error_to_ftp_os_errorÚpwdÚnormpathÚtoolÚas_str_pathÚ	_encodingÚ_cached_current_dirÚ	_childrenÚ_fileÚclosedÚcurdirÚpardirÚsepÚ_time_shiftÚuse_list_a_option)r
   r   r   Zcurrent_dirr   r   r   r   ]   s$    




ÿzFTPHost.__init__c              	   C   s"   t jj | j ¡  W 5 Q R X dS )aX  
        Try to keep the connection alive in order to avoid server timeouts.

        Note that this won't help if the connection has already timed out! In
        this case, `keep_alive` will raise an `TemporaryError`. (Actually, if
        you get a server timeout, the error - for a specific connection - will
        be permanent.)
        N)r   r"   r#   r   r$   ©r
   r   r   r   Ú
keep_alive†   s    
zFTPHost.keep_alivec              	   C   sl   | j dd… }| j ¡ }| dt¡}tjj6 |||Ž}t|dƒsVtj 	d|›d¡‚|j
| _W 5 Q R X |S )zp
        Return a new session object according to the current state of this
        `FTPHost` instance.
        NZsession_factoryr   zsession instance z" must have an `encoding` attribute)r   r   ÚcopyÚpopr   r   r"   r#   ÚhasattrZNoEncodingErrorr   r(   )r
   r   r   Úfactoryr	   r   r   r   r   ˜   s    




ÿzFTPHost._make_sessionc                 C   s   | j | j| jŽS )z9
        Return a copy of this `FTPHost` object.
        )r   r   r   r2   r   r   r   Ú_copy­   s    zFTPHost._copyc              	   C   sŽ   | j D ]‚}|jjrz|j ¡  W n^ tjk
r:   Y qY q tjk
rR   Y qY q tk
rh   Y qY q t	k
r~   Y qY qX |  S qdS )zá
        Return an available (i. e. one whose `_file` object is closed and
        doesn't have a timed-out server connection) child (`FTPHost` object)
        from the pool of children or `None` if there aren't any.
        N)
r*   r+   r,   r   r$   ÚftplibZerror_replyZ
error_tempÚEOFErrorÚOSError©r
   Úhostr   r   r   Ú_available_childµ   s    



zFTPHost._available_childÚrN)Úrestc             	   C   sê   t jj|| jd}|  ¡ }|dkrD|  ¡ }| j |¡ t j 	|¡|_
|  ¡ }	|j |¡r^|}
n|j |	|¡}
|j |
¡\}}z| |¡ W n* t jjk
r´   t j d |¡¡‚Y nX |j
j|||||||d d|krä| j |
¡ |j
S )a   
        Return an open file(-like) object which is associated with this
        `FTPHost` object.

        The arguments `path`, `mode`, `buffering`, `encoding`, `errors` and
        `newlines` have the same meaning as for `open`. If `rest` is given as
        an integer,

        - reading will start at the byte (zero-based) `rest`
        - writing will overwrite the remote file from byte `rest`

        This method tries to reuse a child but will generate a new one if none
        is available.
        r   NzEremote directory '{}' doesn't exist or has insufficient access rights)ÚmodeÚ	bufferingr   ÚerrorsÚnewliner@   Úw)r   r&   r'   r(   r>   r8   r*   ÚappendÚfileZFTPFiler+   Úgetcwdr   ÚisabsÚjoinÚsplitÚchdirr"   ÚPermanentErrorÚ
FTPIOErrorÚformatÚ_openr    Ú
invalidate)r
   r   rA   rB   r   rC   rD   r@   r=   ÚbasedirZeffective_pathZeffective_dirZeffective_filer   r   r   Úopenä   s>    ÿÿ
ù	zFTPHost.openc              	   C   sh   | j r
dS | jD ]}|j ¡  | ¡  qz"tjj | j	 ¡  W 5 Q R X W 5 | j ¡  g | _d| _ X dS )z(
        Close host connection.
        NT)
r,   r*   r+   Úcloser    Úclearr   r"   r#   r   r<   r   r   r   rT   %  s    




zFTPHost.closec                 C   s   | j  ¡  || j_d| j_dS )a,  
        Set the parser for extracting stat results from directory listings.

        The parser interface is described in the documentation, but here are
        the most important things:

        - A parser should derive from `ftputil.stat.Parser`.

        - The parser has to implement two methods, `parse_line` and
          `ignores_line`. For the latter, there's a probably useful default
          in the class `ftputil.stat.Parser`.

        - `parse_line` should try to parse a line of a directory listing and
          return a `ftputil.stat.StatResult` instance. If parsing isn't
          possible, raise `ftputil.error.ParserError` with a useful error
          message.

        - `ignores_line` should return a true value if the line isn't assumed
          to contain stat information.
        FN)r    rU   r   Ú_parserZ_allow_parser_switching)r
   Úparserr   r   r   Ú
set_parser?  s    
zFTPHost.set_parserc                 C   sH   d}| dkrdS t | ƒ}| | }t|d|  d|  ƒd|  }|| S )z–
        Return the given time shift in seconds, but rounded to 15-minute units.
        The argument is also assumed to be given in seconds.
        ç      N@r   r   g      @g      .@)ÚabsÚint)Ú
time_shiftÚminuteZabsolute_time_shiftÚsignumÚabsolute_rounded_time_shiftr   r   r   Z__rounded_time_shift]  s    ÿþzFTPHost.__rounded_time_shiftc                 C   sr   d}d| }t |  |¡ƒ}|d| kr8tj d |¡¡‚d| }t ||  |¡ ƒ|krntj d |t|ƒ¡¡‚dS )z±
        Perform sanity checks on the time shift value (given in seconds). If
        the value is invalid, raise a `TimeShiftError`, else simply return
        `None`.
        rY   é   z!time shift abs({0:.2f} s) > 1 dayé   zFtime shift ({0:.2f} s) deviates more than {1:d} s from 15-minute unitsN)rZ   Ú_FTPHost__rounded_time_shiftr   r"   ÚTimeShiftErrorrO   r[   )r
   r\   r]   Úhourr_   Zmaximum_deviationr   r   r   Z__assert_valid_time_shiftq  s    ÿ ÿÿz!FTPHost.__assert_valid_time_shiftc                 C   s4   |   |¡ |  ¡ }||kr0| j ¡  |  |¡| _dS )a\  
        Set the time shift value.

        By (my) definition, the time shift value is the difference of
        the time zone used in server listings and UTC, i. e.

            time_shift =def= t_server - utc
        <=> t_server = utc + time_shift
        <=> utc = t_server - time_shift

        The time shift is measured in seconds.
        N)Ú!_FTPHost__assert_valid_time_shiftr\   r    rU   rb   r0   )r
   r\   Zold_time_shiftr   r   r   Úset_time_shift‰  s
    

zFTPHost.set_time_shiftc                 C   s   | j S )zŒ
        Return the time shift between FTP server and client. See the
        docstring of `set_time_shift` for more on this value.
        )r0   r2   r   r   r   r\   Ÿ  s    zFTPHost.time_shiftc              	   C   sä   d}z|   |d¡}| ¡  W n. tjjk
rJ   tj d |  ¡ ¡¡‚Y nX z| j 	|¡}|  
|¡ W n$ tjjk
rŠ   tj d¡‚Y nX t ¡ }|| }|dk rÖtjj|tjjd}|j|jd d}| ¡ | }|  |¡ d	S )
ai  
        Synchronize the local times of FTP client and server. This is necessary
        to let `upload_if_newer` and `download_if_newer` work correctly. If
        `synchronize_times` isn't applicable (see below), the time shift can
        still be set explicitly with `set_time_shift`.

        This implementation of `synchronize_times` requires _all_ of the
        following:

        - The connection between server and client is established.
        - The client has write access to the directory that is current when
          `synchronize_times` is called.

        The common usage pattern of `synchronize_times` is to call it directly
        after the connection is established. (As can be concluded from the
        points above, this requires write access to the login directory.)

        If `synchronize_times` fails, it raises a `TimeShiftError`.
        Z_ftputil_sync_rE   z,couldn't write helper file in directory '{}'z)could write helper file but not unlink iti d%þ)Útzé   )ÚyearN)rS   rT   r   r"   rN   rc   rO   rH   r   ÚgetmtimeÚunlinkÚ
FTPOSErrorÚtimeÚdatetimeÚfromtimestampÚtimezoneÚutcÚreplaceri   Ú	timestamprf   )r
   Zhelper_file_nameÚfile_Zserver_timeÚnowr\   Zserver_datetimer   r   r   Úsynchronize_times¦  s2    ÿ
ÿ
 ÿzFTPHost.synchronize_timesc                 C   s   t j | |||¡ dS )zX
        Copy data from file-like object `source` to file-like object `target`.
        N)r   Úfile_transferÚcopyfileobj)ÚsourceÚtargetZmax_chunk_sizeÚcallbackr   r   r   rx   î  s    
zFTPHost.copyfileobjc                 C   s&   t j |d¡}t j | |d¡}||fS )zõ
        Return a `LocalFile` and `RemoteFile` as source and target,
        respectively.

        The strings `source_path` and `target_path` are the (absolute or
        relative) paths of the local and the remote file, respectively.
        ÚrbÚwb)r   rw   Ú	LocalFileÚ
RemoteFile©r
   Úsource_pathÚtarget_pathÚsource_fileÚtarget_filer   r   r   Ú_upload_filesú  s    zFTPHost._upload_filesc                 C   sZ   |dkrt dƒ‚tjj|dd tjj|| jd}|  ||¡\}}tjj||d|d dS )	a  
        Upload a file from the local source (name) to the remote target (name).

        If a callable `callback` is given, it's called after every chunk of
        transferred data. The chunk size is a constant defined in
        `file_transfer`. The callback will be called with a single argument,
        the data chunk that was transferred before the callback was called.
        ©Ú ó    úpath argument `source` is emptyrz   ©Zpath_argument_namer   F©Zconditionalr{   N)	ÚIOErrorr   r&   Úraise_for_empty_pathr'   r(   r…   rw   Ú	copy_file©r
   ry   rz   r{   rƒ   r„   r   r   r   Úupload  s    	   ÿzFTPHost.uploadc                 C   sV   t jj|dd |dkr tdƒ‚t jj|| jd}|  ||¡\}}t jj||d|dS )a7  
        Upload a file only if it's newer than the target on the remote host or
        if the target file does not exist. See the method `upload` for the
        meaning of the parameters.

        If an upload was necessary, return `True`, else return `False`.

        If a callable `callback` is given, it's called after every chunk of
        transferred data. The chunk size is a constant defined in
        `file_transfer`. The callback will be called with a single argument,
        the data chunk that was transferred before the callback was called.
        ry   rŠ   r†   úpath argument `target` is emptyr   Tr‹   )	r   r&   r   rŒ   r'   r(   r…   rw   rŽ   r   r   r   r   Úupload_if_newer  s       ÿzFTPHost.upload_if_newerc                 C   s&   t j | |d¡}t j |d¡}||fS )zõ
        Return a `RemoteFile` and `LocalFile` as source and target,
        respectively.

        The strings `source_path` and `target_path` are the (absolute or
        relative) paths of the remote and the local file, respectively.
        r|   r}   )r   rw   r   r~   r€   r   r   r   Ú_download_files/  s    zFTPHost._download_filesc                 C   sZ   t jj|dd |dkr tdƒ‚t jj|| jd}|  ||¡\}}t jj||d|d dS )	a‹  
        Download a file from the remote source (name) to the local target
        (name).

        If a callable `callback` is given, it's called after every chunk of
        transferred data. The chunk size is a constant defined in
        `file_transfer`. The callback will be called with a single argument,
        the data chunk that was transferred before the callback was called.
        ry   rŠ   r†   r‘   r   Fr‹   N)	r   r&   r   rŒ   r'   r(   r“   rw   rŽ   r   r   r   r   Údownload;  s    
   ÿzFTPHost.downloadc                 C   sV   |dkrt dƒ‚tjj|dd tjj|| jd}|  ||¡\}}tjj||d|dS )a;  
        Download a file only if it's newer than the target on the local host or
        if the target file does not exist. See the method `download` for the
        meaning of the parameters.

        If a download was necessary, return `True`, else return `False`.

        If a callable `callback` is given, it's called after every chunk of
        transferred data. The chunk size is a constant defined in
        `file_transfer`. The callback will be called with a single argument,
        the data chunk that was transferred before the callback was called.
        r†   r‰   rz   rŠ   r   Tr‹   )	rŒ   r   r&   r   r'   r(   r“   rw   rŽ   r   r   r   r   Údownload_if_newerN  s       ÿzFTPHost.download_if_newerc                 C   sF   |   ¡ }z|  |¡ W n* tjjk
r@   tj d |¡¡‚Y nX dS )zÇ
        Raise an `InaccessibleLoginDirError` exception if we can't change to
        the login directory. This test is only reliable if the current
        directory is the login directory.
        z directory '{}' is not accessibleN)rH   rL   r   r"   rM   ZInaccessibleLoginDirErrorrO   )r
   Zpresumable_login_dirr   r   r   Ú#_check_inaccessible_login_directoryg  s    ÿz+FTPHost._check_inaccessible_login_directoryFc              	   C   sj   |   ¡  |  ¡ }zH|r.|  |¡ || dƒW ¢.S | j |¡\}}|  |¡ || |ƒW ¢S W 5 |  |¡ X dS )a  
        Run an FTP command on a path. The return value of the method is the
        return value of the command.

        If `descend_deeply` is true (the default is false), descend deeply,
        i. e. change the directory to the end of the path.
        r‡   N)r–   rH   rL   r   rK   )r
   Úcommandr   Údescend_deeplyÚold_dirÚheadÚtailr   r   r   Ú_robust_ftp_commandw  s    

zFTPHost._robust_ftp_commandc                 C   s   | j S )z4
        Return the current directory path.
        )r)   r2   r   r   r   rH   ž  s    zFTPHost.getcwdc              	   C   sP   t jj|| jd}t jj | j |¡ W 5 Q R X | j 	| j 
| j|¡¡| _dS )z=
        Change the directory on the host to `path`.
        r   N)r   r&   r'   r(   r"   r#   r   Úcwdr   r%   rJ   r)   ©r
   r   r   r   r   rL   ¤  s    
ÿzFTPHost.chdirc                 C   s6   t j |¡ t jj|| jd}dd„ }|  ||¡ dS )z™
        Make the directory path on the remote host. The argument `mode` is
        ignored and only "supported" for similarity with `os.mkdir`.
        r   c              	   S   s$   t jj | j |¡ W 5 Q R X dS ©úCallback function.N)r   r"   r#   r   Zmkdrž   r   r   r   r—   »  s    
zFTPHost.mkdir.<locals>.commandN)r   r&   r   r'   r(   rœ   ©r
   r   rA   r—   r   r   r   Úmkdir³  s    zFTPHost.mkdirc           	      C   s  t j |¡ t jj|| jd}| j |¡}| | j¡}|  	¡ }zÆtdt|ƒƒD ]²}| j| jj|d|d … Ž  }z|  
|¡ W nN t jjk
rÌ   z|  |¡ W n& t jjk
rÆ   | j |¡sÂ‚ Y nX Y qNX |t|ƒd krN|sNt j d |¡¡}tj|_|‚qNW 5 |  
|¡ X dS )at  
        Make the directory `path`, but also make not yet existing intermediate
        directories, like `os.makedirs`.

        The value of `mode` is only accepted for compatibility with
        `os.makedirs` but otherwise ignored.

        If `exist_ok` is `False` (the default) and the leaf directory exists,
        raise a `PermanentError` with `errno` 17.
        r   rh   Nzpath {!r} exists)r   r&   r   r'   r(   r   ÚabspathrK   r/   rH   rL   ÚrangeÚlenrJ   r"   rM   r¢   ÚisdirrO   ÚerrnoÚEEXIST)	r
   r   rA   Úexist_okÚdirectoriesr™   ÚindexZnext_directoryZftp_os_errorr   r   r   ÚmakedirsÉ  s.    ÿ
zFTPHost.makedirsc              	   C   sr   t j |¡ t jj|| jd}| j |¡}|  |¡rFt j 	d 
|¡¡‚dd„ }z|  ||¡ W 5 | j |¡ X dS )a
  
        Remove the _empty_ directory `path` on the remote host.

        Compatibility note:

        Previous versions of ftputil could possibly delete non-empty
        directories as well, - if the server allowed it. This is no longer
        supported.
        r   zdirectory '{}' not emptyc              	   S   s$   t jj | j |¡ W 5 Q R X dS rŸ   )r   r"   r#   r   Zrmdrž   r   r   r   r—     s    
zFTPHost.rmdir.<locals>.commandN)r   r&   r   r'   r(   r   r£   Úlistdirr"   rM   rO   r    rQ   rœ   ©r
   r   r—   r   r   r   Úrmdirÿ  s    

zFTPHost.rmdirc              	   C   sˆ   t j |¡ t jj|| jd}| j |¡}| j |¡sN| j |¡sN| j 	|¡sxdd„ }z|  ||¡ W 5 | j
 |¡ X nt j d¡‚dS )zÖ
        Remove the file or link given by `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).
        r   c              	   S   s$   t jj | j |¡ W 5 Q R X dS rŸ   )r   r"   r#   r   Údeleterž   r   r   r   r—   /  s    
zFTPHost.remove.<locals>.commandz>remove/unlink can only delete files and links, not directoriesN)r   r&   r   r'   r(   r   r£   ÚisfileÚislinkÚexistsr    rQ   rœ   r"   rM   r®   r   r   r   Úremove  s     
ÿ
þ
ýÿzFTPHost.removec           	   	   C   s\  t j |¡ t jj|| jd}|r,dd„ }n|dkr>dd„ }n|}g }z|  |¡}W n* t jjk
r~   || j|t 	¡ ƒ Y nX |D ]–}| j
 ||¡}z|  |¡j}W n t jjk
rÂ   d}Y nX t |¡rÞ|  |||¡ q„z|  |¡ W q„ t jjk
r   || j|t 	¡ ƒ Y q„X q„z|  |¡ W n, t jjk
rV   || j|t 	¡ ƒ Y nX dS )a  
        Remove the given remote, possibly non-empty, directory tree. The
        interface of this method is rather complex, in favor of compatibility
        with `shutil.rmtree`.

        If `ignore_errors` is set to a true value, errors are ignored. If
        `ignore_errors` is a false value _and_ `onerror` isn't set, all
        exceptions occurring during the tree iteration and processing are
        raised. These exceptions are all of type `PermanentError`.

        To distinguish between error situations, pass in a callable for
        `onerror`. This callable must accept three arguments: `func`, `path`
        and `exc_info`. `func` is a bound method object, _for example_
        `your_host_object.listdir`. `path` is the path that was the recent
        argument of the respective method (`listdir`, `remove`, `rmdir`).
        `exc_info` is the exception info as it's got from `sys.exc_info`.

        Implementation note: The code is copied from `shutil.rmtree` in
        Python 2.4 and adapted to ftputil.
        r   c                  W   s   dS )zDo nothing.Nr   ©r   r   r   r   Únew_onerror]  s    z#FTPHost.rmtree.<locals>.new_onerrorNc                   W   s   ‚ dS )zRe-raise exception.Nr   rµ   r   r   r   r¶   d  s    r   )r   r&   r   r'   r(   r­   r"   rM   ÚsysÚexc_infor   rJ   ÚlstatÚst_moder   ÚS_ISDIRÚrmtreer´   r¯   rl   )	r
   r   Úignore_errorsÚonerrorr¶   ÚnamesÚnameZ	full_namerA   r   r   r   r¼   B  s8    



zFTPHost.rmtreec           
   	      sÒ   t jj|dd t jj|dd t jj|ˆ jd}t jj|ˆ jd}ˆ j |¡\}}ˆ j |¡\}}‡ fdd„}ˆ  ¡  d|kp†d|k}|rÄ||krÄˆ  ¡ }	zˆ  	|¡ |||ƒ W 5 ˆ  	|	¡ X n
|||ƒ dS )	zB
        Rename the `source` on the FTP host to `target`.
        ry   rŠ   rz   r   c              	      s^   z&tjj ˆ j | |¡ W 5 Q R X W 5 ˆ j  | ¡}ˆ j  |¡}ˆ j |¡ ˆ j |¡ X d S ©N)	r   r£   r    rQ   r   r"   r#   r   Úrename)Z
source_argZ
target_argZsource_absolute_pathZtarget_absolute_pathr2   r   r   Úrename_with_cleanup’  s    
z+FTPHost.rename.<locals>.rename_with_cleanupú N)
r   r&   r   r'   r(   r   rK   r–   rH   rL   )
r
   ry   rz   Zsource_headZsource_tailZtarget_headZtarget_tailrÃ   Zpaths_contain_whitespacer™   r   r2   r   rÂ   ‚  s     
zFTPHost.renamec                 C   s   dd„ }| j ||dd}|S )zj
        Return a directory listing as made by FTP's `LIST` command as a list of
        strings.
        c              	      sP   g ‰ ‡ ‡fdd„}t jj, ˆjr4ˆj d||¡ nˆj ||¡ W 5 Q R X ˆ S )r    c                    s   ˆ   tjj| ˆjd¡ dS )r    r   N)rF   r   r&   Zas_strr(   )Úline©Úlinesr
   r   r   r{   ¾  s    z<FTPHost._dir.<locals>._FTPHost_dir_command.<locals>.callbackz-a)r   r"   r#   r1   r   Údir)r
   r   r{   r   rÆ   r   Ú_FTPHost_dir_commandº  s    
z*FTPHost._dir.<locals>._FTPHost_dir_commandT)r˜   )rœ   )r
   r   rÉ   rÇ   r   r   r   Ú_dir²  s      ÿzFTPHost._dirc                    sB   t j |¡ |‰ t jj|ˆjd}ˆj |¡}‡ ‡fdd„|D ƒS )zÜ
        Return a list of directories, files etc. in the directory named `path`.

        If the directory listing from the server can't be parsed with any of
        the available parsers raise a `ParserError`.
        r   c                    s   g | ]}t j ˆ |ˆj¡‘qS r   )r   r&   Zsame_string_type_asr(   )Ú.0Úitem©Zoriginal_pathr
   r   r   Ú
<listcomp>Ý  s   ÿz#FTPHost.listdir.<locals>.<listcomp>)r   r&   r   r'   r(   r   Ú_listdir)r
   r   Úitemsr   rÍ   r   r­   Ò  s    þzFTPHost.listdirTc                 C   s,   t j |¡ t jj|| jd}| j ||¡S )a©  
        Return an object similar to that returned by `os.lstat`.

        If the directory listing from the server can't be parsed with any of
        the available parsers, raise a `ParserError`. If the directory _can_ be
        parsed and the `path` is _not_ found, raise a `PermanentError`.

        (`_exception_for_missing_path` is an implementation aid and _not_
        intended for use by ftputil clients.)
        r   )r   r&   r   r'   r(   r   Z_lstat©r
   r   Z_exception_for_missing_pathr   r   r   r¹   â  s    zFTPHost.lstatc                 C   s,   t j |¡ t jj|| jd}| j ||¡S )aü  
        Return info from a "stat" call on `path`.

        If the directory containing `path` can't be parsed, raise a
        `ParserError`. If the directory containing `path` 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.)
        r   )r   r&   r   r'   r(   r   rÑ   r   r   r   r   ñ  s    zFTPHost.statc              
   c   s  t jj|dd t jj|| jd}z|  |¡}W n< t jjk
rl } z|dk	rV||ƒ W Y ¢dS d}~X Y nX g g  }}|D ]0}	| j 	| j 
||	¡¡r¢| |	¡ q|| |	¡ q||r¾|||fV  |D ]8}	| j 
||	¡}
|sä| j |
¡sÂ|  |
|||¡E dH  qÂ|s|||fV  dS )zÖ
        Iterate over directory tree and return a tuple (dirpath, dirnames,
        filenames) on each iteration, like the `os.walk` function (see
        https://docs.python.org/library/os.html#os.walk ).
        ÚtoprŠ   r   N)r   r&   r   r'   r(   r­   r"   rl   r   r¦   rJ   rF   r²   Úwalk)r
   rÒ   Útopdownr¾   Úfollowlinksr¿   ÚerrÚdirsÚnondirsrÀ   r   r   r   r   rÓ     s*    
zFTPHost.walkc                    sR   t j |¡ t jj|| jd}| j |¡}‡ fdd„}|  ||¡ | j 	|¡ dS )a¢  
        Change the mode of a remote `path` (a string) to the integer `mode`.
        This integer uses the same bits as the mode value returned by the
        `stat` and `lstat` commands.

        If something goes wrong, raise a `TemporaryError` or a
        `PermanentError`, according to the status code returned by the server.
        In particular, a non-existent path usually causes a `PermanentError`.
        r   c              	      s,   t jj | j d ˆ |¡¡ W 5 Q R X dS )r    zSITE CHMOD 0{0:o} {1}N)r   r"   r#   r   ÚvoidcmdrO   rž   ©rA   r   r   r—   /  s    
zFTPHost.chmod.<locals>.commandN)
r   r&   r   r'   r(   r   r£   rœ   r    rQ   r¡   r   rÚ   r   Úchmod!  s    
zFTPHost.chmodc                 C   s   t dƒ‚d S )Nzcannot serialize FTPHost object)Ú	TypeErrorr2   r   r   r   Ú__getstate__7  s    zFTPHost.__getstate__c                 C   s   | S rÁ   r   r2   r   r   r   Ú	__enter__=  s    zFTPHost.__enter__c                 C   s   |   ¡  dS )NF)rT   )r
   Úexc_typeÚexc_valÚexc_tbr   r   r   Ú__exit__B  s    zFTPHost.__exit__)r?   NNNN)N)N)N)N)F)N)NF)FN)T)T)TNF)0r   r   r   Ú__doc__r   r3   r   r8   r>   rS   rT   rX   Ústaticmethodrb   re   rf   r\   rv   r   rw   ZMAX_COPY_CHUNK_SIZErx   r…   r   r’   r“   r”   r•   r–   rœ   rH   rL   r¢   r¬   r¯   r´   rk   r¼   rÂ   rÊ   r­   r¹   r   rÓ   rÛ   rÝ   rÞ   râ   r   r   r   r   r   E   sh   )2     ù	÷A
Hü




'

6$
@0 


)rã   rn   r§   r9   r   r·   rm   Zftputil.errorr   Zftputil.fileZftputil.file_transferZftputil.pathZftputil.path_encodingZftputil.sessionZftputil.statZftputil.toolÚ__all__r   ZRUNNING_UNDER_PY39_AND_UPÚFTPr   r   r   r   r   r   Ú<module>   s&   