Changes between Version 38 and Version 39 of Documentation


Ignore:
Timestamp:
Jun 13, 2020, 7:21:31 PM (3 months ago)
Author:
schwa
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Documentation

    v38 v39  
    11{{{
    22#!rst
    3 
    43``ftputil`` -- a high-level FTP client library
    54==============================================
    65
    7 :Version:   3.4
    8 :Date:      2017-11-08
     6:Version:   4.0.0
     7:Date:      2020-06-13
    98:Summary:   high-level FTP client library for Python
    109:Keywords:  FTP, ``ftplib`` substitute, virtual filesystem, pure Python
     
    6968* Get information about directories, files and links (``listdir``,
    7069  ``stat``, ``lstat``, ``exists``, ``isdir``, ``isfile``, ``islink``,
    71   ``abspath``, ``split``, ``join``, ``dirname``, ``basename`` etc.)
     70  ``abspath``, ``dirname``, ``basename`` etc.)
    7271
    7372* Iterate over remote file systems (``walk``)
     
    188187    ...     f = open("not_there")
    189188    ... except IOError as obj:
    190     ...     print obj.errno
    191     ...     print obj.strerror
     189    ...     print(obj.errno)
     190    ...     print(obj.strerror)
    192191    ...
    193192    2
     
    202201    ...     f = ftp_host.open("not_there")
    203202    ... except IOError as obj:
    204     ...     print obj.errno
    205     ...     print obj.strerror
     203    ...     print(obj.errno)
     204    ...     print(obj.strerror)
    206205    ...
    207206    550
     
    252251   `FTPHost.open`_.
    253252
    254 First off: If your directory and file names (both as
    255 arguments and on the server) contain only ISO 8859-1 (latin-1)
    256 characters, you can use such names in the form of byte strings or
    257 unicode strings. However, you can't mix different string types (bytes
    258 and unicode) in one call (for example in ``FTPHost.path.join``).
     253First off: If your directory and file names (both as arguments and on
     254the server) contain only ISO 8859-1 (latin-1) characters, you can use
     255such names in the form of ``bytes`` or ``str`` objects. However, you
     256can't mix different string types (``bytes`` and ``str``) in one call
     257(for example in ``FTPHost.path.join``).
    259258
    260259If you have directory or file names with characters that aren't in
    261 latin-1, it's recommended to use byte strings. In that case,
    262 returned paths will be byte strings, too.
     260latin-1, it's recommended to use ``bytes`` objects. In that case,
     261returned paths will be ``bytes`` objects, too.
    263262
    264263Read on for details.
     
    275274   read the same file with Python 3's ``ftplib`` module.
    276275
    277 Methods that take names of directories and/or files can take either
    278 byte or unicode strings. If a method got a string argument and returns
    279 one or more strings, these strings will have the same string type as
    280 the argument(s). Mixing different string arguments in one call (for
    281 example in ``FTPHost.path.join``) isn't allowed and will cause a
    282 ``TypeError``. These rules are the same as for local file system
    283 operations in Python 3. Since ``ftputil`` uses the same API for Python
    284 2, ``ftputil`` will do the same when run on Python 2.
    285 
    286 Byte strings for directory and file names will be sent to the server
    287 as-is. On the other hand, unicode strings will be encoded to byte
    288 strings, assuming latin-1 encoding. This implies that such unicode
    289 strings must only contain code points 0-255 for the latin-1 character
    290 set. Using any other characters will result in a
     276Methods that take paths of directories and/or files can take either
     277``bytes`` or ``str`` objects, or `PathLike`_ objects that can be
     278converted to ``bytes`` or ``str``.
     279
     280.. _PathLike: https://docs.python.org/3/library/os.html#os.PathLike
     281
     282If a method gets a string argument (or a string argument wrapped in a
     283PathLike_ object) and returns one or more strings, these strings will
     284have the same string type (``bytes`` or ``str``) as the argument(s).
     285Mixing different string types in one call (for example in
     286``FTPHost.path.join``) isn't allowed and will cause a ``TypeError``.
     287These rules are the same as for local file system operations in Python 3.
     288
     289``bytes`` objects for directory and file names will be sent to the
     290server as-is. On the other hand, ``str`` objects will be encoded to
     291``bytes`` objects, assuming latin-1 encoding. This implies that such
     292``str`` objects must only contain code points 0-255 for the latin-1
     293character set. Using any other characters will result in a
    291294``UnicodeEncodeError`` exception.
    292295
    293 If you have directory or file names as unicode strings with non-latin-1
    294 characters, encode the unicode strings to byte strings yourself, using
    295 the encoding you know the server uses. Decode received paths with the
    296 same encoding. Encapsulate these conversions as far as you can.
    297 Otherwise, you'd have to adapt potentially a lot of code if the server
    298 encoding changes.
    299 
    300 If you *don't* know the encoding on the server side,
    301 it's probably the best to only use byte strings for directory and file
    302 names. That said, as soon as you *show* the names to a user, you -- or
    303 the library you use for displaying the names -- has to guess an
    304 encoding.
     296If you have directory or file names as ``str`` objects with
     297non-latin-1 characters, encode the strings to ``bytes`` yourself,
     298using the encoding you know the server uses for its file system.
     299Decode received paths with the same encoding. Encapsulate these
     300conversions as far as you can. Otherwise, you'd have to adapt
     301potentially a lot of code if the server encoding changes.
     302
     303If you *don't* know the encoding on the server side, it's probably the
     304best to only use ``bytes`` for directory and file names. That said, as
     305soon as you *show* the names to a user, you -- or the library you use
     306for displaying the names -- has to guess an encoding.
     307
     308If you can decide about paths yourself, it's generally safest to use
     309only ASCII characters in FTP paths.
    305310
    306311
     
    330335
    331336    with ftputil.FTPHost(server, user, password) as ftp_host:
    332         print ftp_host.listdir(ftp_host.curdir)
     337        print(ftp_host.listdir(ftp_host.curdir))
    333338
    334339After the ``with`` block, the ``FTPHost`` instance and the
     
    344349The keyword argument ``session_factory`` may be used to generate FTP
    345350connections with other factories than the default ``ftplib.FTP``. For
    346 example, the standard library of Python 2.7 contains a class
     351example, the standard library of Python 3 contains a class
    347352``ftplib.FTP_TLS`` which extends ``ftplib.FTP`` to use an encrypted
    348353connection.
     
    384389On login, the format of the directory listings (needed for stat'ing
    385390files and directories) should be determined automatically. If not,
    386 please `file a bug report`_.
    387 
    388 .. _`file a bug report`: http://ftputil.sschwarzer.net/issuetrackernotes
     391please `enter a ticket`_.
     392
     393.. _`enter a ticket`: https://ftputil.sschwarzer.net/issuetrackernotes
    389394
    390395For the most common uses you don't need to create your own session
     
    415420  for secure connections. This is only supported for the base classes
    416421  ``ftplib.FTP_TLS`` and ``M2Crypto.ftpslib.FTP_TLS``, otherwise the
    417   the parameter is ignored.
     422  parameter is ignored.
    418423
    419424- ``debug_level`` sets the debug level for FTP session instances. The
     
    452457it with ftputil out of the box. The function ``session_factory``
    453458contains a workaround for this limitation. For details refer to `this
    454 bug report`_.
    455 
    456 .. _`this bug report`: http://ftputil.sschwarzer.net/trac/ticket/78
     459ticket`_.
     460
     461.. _`this ticket`: https://ftputil.sschwarzer.net/trac/ticket/78
    457462
    458463Hidden files and directories
     
    461466Whether ftputil sees "hidden" files and directories (usually files or
    462467directories whose names start with a dot) depends on the FTP server
    463 configuration. By default, ftputil uses the ``-a`` option in the FTP
    464 ``LIST`` command to find hidden files. However, the server may ignore
    465 this.
    466 
    467 If using the ``-a`` option leads to problems, for example if an
    468 FTP server causes an exception, you may switch off the use of the
    469 option::
     468configuration. By default, ftputil does *not* use the ``-a`` option in
     469the FTP ``LIST`` command to find hidden files.
     470
     471To tell the server to list hidden directories and files, set
     472``FTPHost.use_list_a_option`` to ``True``::
    470473
    471474    ftp_host = ftputil.FTPHost(server, user, password, account,
    472475                               session_factory=ftplib.FTP)
    473     ftp_host.use_list_a_option = False
     476    ftp_host.use_list_a_option = True
     477
     478Caveats:
     479
     480- If the server doesn't understand the ``-a`` option at all, the
     481  server may interpret ``-a`` as the name of a file or directory,
     482  which can result in odd behavior. Therefore, use ``-a`` only if
     483  you're sure the server you're talking to supports it. Another
     484  approach is to have test code for ``-a`` support and fall back to
     485  not using the option.
     486
     487- Even if the server knows about the ``-a`` option, the server may
     488  be configured to ignore it.
    474489
    475490``FTPHost`` attributes and methods
     
    597612````````````````````
    598613
    599 If the client where ``ftputil`` runs and the server have a different
    600 understanding of their local times, this has to be taken into account
    601 for ``upload_if_newer`` and ``download_if_newer`` to work correctly.
    602 
    603 Note that even if the client and the server are in the same time zone
    604 (or even on the same computer), the time shift value (see below) may
    605 be different from zero. For example, my computer is set to use local
    606 time whereas the server running on the very same host insists on using
    607 UTC time.
     614For ``upload_if_newer`` and ``download_if_newer`` to work correctly,
     615the time zone of the server must be taken into account. By default,
     616ftputil assumes that the timestamps in server listings are in UTC_.
     617
     618.. _UTC: https://en.wikipedia.org/wiki/Utc
    608619
    609620.. _`set_time_shift`:
     
    612623
    613624  sets the so-called time shift value, measured in seconds. The time
    614   shift is the difference between the local time of the server and the
    615   local time of the client at a given moment, i. e. by definition
     625  shift here is defined as the difference between the time used in
     626  server listings and UTC.
    616627
    617628  ::
    618629
    619     time_shift = server_time - client_time
    620 
    621   Setting this value is important for `upload_if_newer`_ and
    622   `download_if_newer`_ to work correctly even if the time zone of the
    623   FTP server differs from that of the client. Note that the time shift
    624   value *can be negative*.
     630    time_shift = server_time - utc_time
     631
     632  For example, a server in Berlin/Germany set to the local time
     633  (currently UTC+03:00), would require a time shift value of 3 *
     634  3600.0 = 10800.0 seconds to be handled correctly by ftputil's
     635  ``upload_if_newer`` and ``download_if_newer``, as well as the
     636  ``stat`` and ``lstat`` calls.
     637
     638  Note that servers don't necessarily send their file system listings
     639  in their local time zone. Some use UTC, which actually makes sense
     640  because UTC doesn't lead to an ambiguity when there's a switch back
     641  from the daylight saving time to the "normal" time of the server
     642  location.
    625643
    626644  If the time shift value is invalid, for example its absolute value
    627645  is larger than 24 hours, a ``TimeShiftError`` is raised.
    628646
     647  .. note::
     648
     649     Versions of ftputil before 4.0.0 used a different definition of
     650     "time shift", server_time - local_client_time.
     651
     652     This had the advantage that the default of 0.0 would be correct
     653     *if* the server was set to the same time zone as the client
     654     where ftputil runs. On the other hand, this approach meant that
     655     the time shift depended on *two* time zones, not only the one
     656     used on the server side. This could be confusing if server
     657     and client *didn't* use the same time zone.
     658
    629659  See also `synchronize_times`_ for a way to set the time shift with a
    630   simple method call.
     660  simple method call. If you can't use ``synchronize_times`` *and* the
     661  server uses the same time zone as the client, you can set the time
     662  shift value with
     663
     664  ::
     665
     666    set_time_shift(
     667      round( (datetime.datetime.now() - datetime.datetime.utcnow()).seconds, -2 )
     668    )
    631669
    632670- ``time_shift()``
     
    666704  `Exception hierarchy`_.
    667705
    668 - ``makedirs(path, [mode])``
     706- ``makedirs(path, [mode], exist_ok=False)``
    669707
    670708  works similar to ``mkdir`` (see above), but also makes intermediate
    671709  directories like ``os.makedirs``. The ``mode`` parameter is only
    672710  there for compatibility with ``os.makedirs`` and is ignored.
     711
     712  ``exist_ok`` controls whether the existence of any directory but the
     713  last in the ``path`` should be considered an error. If the default
     714  ``False`` is used or passed to ``makedirs``, ftputil will raise a
     715  ``PermanentError`` if any directory but the last already exists.
    673716
    674717- ``rmdir(path)``
     
    724767host, ``FTPHost``'s constructor tries to guess the right format, which
    725768succeeds in most cases. However, if you get strange results or
    726 ``ParserError`` exceptions by a mere ``lstat`` call, please `file a
    727 bug report`_.
     769``ParserError`` exceptions by a mere ``lstat`` call, please `enter a
     770ticket`_.
    728771
    729772If ``lstat`` or ``stat`` give wrong modification dates or times, look
     
    735778- ``lstat(path)``
    736779
    737   returns an object similar to that from `os.lstat`_. This is a
    738   "tuple" with additional attributes; see the documentation of the
     780  returns an object similar to that from `os.lstat`_. This is a kind
     781  of tuple with additional attributes; see the documentation of the
    739782  ``os`` module for details.
    740783
     
    746789    numbers, and that only if the server supplies them. This is
    747790    usually the case with Unix servers but maybe not for other FTP
    748     server programs.
     791    servers.
    749792
    750793  - Values for the time of the last modification may be rough,
    751794    depending on the information from the server. For timestamps
    752795    older than a year, this usually means that the precision of the
    753     modification timestamp value is not better than days. For newer
     796    modification timestamp value is not better than a day. For newer
    754797    files, the information may be accurate to a minute.
    755798
     
    777820.. _`os.listdir`: https://docs.python.org/library/os.html#os.listdir
    778821.. _`os.lstat`: https://docs.python.org/library/os.html#os.lstat
    779 .. _`ftputil mailing list`: http://ftputil.sschwarzer.net/mailinglist
     822.. _`ftputil mailing list`: https://ftputil.sschwarzer.net/mailinglist
    780823.. _`writing your own parser`: `Writing directory parsers`_
    781824
     
    906949        ftp_host2.remove("some_file")
    907950        # `ftp_host1` will still see the obsolete cache entry!
    908         print ftp_host1.stat("some_file")
     951        print(ftp_host1.stat("some_file"))
    909952        # Will raise an exception since an `FTPHost` object
    910953        # knows of its own changes.
    911         print ftp_host2.stat("some_file")
     954        print(ftp_host2.stat("some_file"))
    912955
    913956At first sight, it may appear to be a good idea to have a shared cache
     
    932975        ftp_host1.stat_cache.invalidate(absolute_path)
    933976        # Will now raise an exception as it should.
    934         print ftp_host1.stat("some_file")
     977        print(ftp_host1.stat("some_file"))
    935978        # Would raise an exception since an `FTPHost` object
    936979        # knows of its own changes, even without `invalidate`.
    937         print ftp_host2.stat("some_file")
     980        print(ftp_host2.stat("some_file"))
    938981
    939982The method ``invalidate`` can be used on any *absolute* path, be it a
     
    10641107  Python 3 as a local text file and ``target`` as a remote file object
    10651108  in binary mode, the transfer will fail since ``source.read`` gives
    1066   unicode strings whereas ``target.write`` only accepts byte strings.
     1109  unicode strings (``str``) whereas ``target.write`` only accepts byte
     1110  strings (``bytes``).
    10671111
    10681112  See `File-like objects`_ for the construction and use of remote
     
    11091153              data = fobj.read(100)
    11101154              # _Futile_ attempt to avoid file connection timeout.
    1111               for i in xrange(15):
     1155              for i in range(15):
    11121156                  time.sleep(60)
    11131157                  ftp_host.keep_alive()
     
    11301174never use the ``FTPFile`` constructor directly.
    11311175
    1132 The API of remote file-like objects are is modeled after the API of
    1133 the io_ module in Python 3, which has also been backported to Python
    1134 2.6 and 2.7.
    1135 
    1136 .. _io: http://docs.python.org/library/io.html
     1176The APIs for remote file-like objects is modeled after the APIs of
     1177the built-in ``open`` function and its return value.
    11371178
    11381179- ``FTPHost.open(path, mode="r", buffering=None, encoding=None,
     
    11531194
    11541195  If you open a file in binary mode, the read and write operations use
    1155   byte strings (``str`` in Python 2, ``bytes`` in Python 3). That is,
    1156   read operations return byte strings and write operations only accept
    1157   byte strings.
    1158 
    1159   Similarly, text files always work with unicode strings (``unicode``
    1160   in Python 2, ``str`` in Python 3). Here, read operations return
    1161   unicode strings and write operations only accept unicode strings.
    1162 
    1163   .. warning::
    1164 
    1165      Note that the semantics of "text mode" has changed fundamentally
    1166      from ftputil 2.8 and earlier. Previously, "text mode" implied
    1167      converting newline characters to ``\r\n`` when writing remote
    1168      files and converting newlines to ``\n`` when reading remote
    1169      files. This is in line with the "text mode" notion of FTP command
    1170      line clients. Now, "text mode" follows the semantics in Python's
    1171      ``io`` module.
    1172 
    1173   The arguments ``errors`` and ``newline`` have the same semantics as
    1174   in `io.open`_. The argument ``buffering`` currently is ignored.
    1175   It's only there for compatibility with the ``io.open`` interface.
     1196  ``bytes`` objects. That is, read operations return ``bytes`` and
     1197  write operations only accept ``bytes``.
     1198
     1199  Similarly, text files always work with strings (``str``). Here, read
     1200  operations return string and write operations only accept strings.
     1201
     1202  The arguments ``buffering``, ``errors`` and ``newline`` have the
     1203  same semantics as in open_.
    11761204
    11771205  If the file is opened in binary mode, you may pass 0 or a positive
     
    11911219     argument for a resumed transfer.
    11921220
    1193 .. _`io.open`: http://docs.python.org/library/io.html#io.open
     1221.. _`open`: https://docs.python.org/3/library/functions.html#open
    11941222
    11951223``FTPHost.open`` can also be used in a ``with`` statement::
     
    12321260            for line in input_file:
    12331261                # Do something with the line, e. g.
    1234                 print line.strip().replace("ftplib", "ftputil")
     1262                print(line.strip().replace("ftplib", "ftputil"))
    12351263
    12361264For more on file objects, see the section `File objects`_ in the
    12371265Python Library Reference.
    12381266
    1239 .. _`file objects`: https://docs.python.org/2.7/library/stdtypes.html#file-objects
     1267.. _`file objects`: https://docs.python.org/3/glossary.html#term-file-object
    12401268
    12411269
     
    13941422            should be used for stat'ing, return a false value.
    13951423            """
    1396             is_total_line = super(XyzParser, self).ignores_line(line)
     1424            is_total_line = super().ignores_line(line)
    13971425            my_test = ...
    13981426            return is_total_line or my_test
     
    14731501See the `download page`_. Announcements will be sent to the `mailing
    14741502list`_. Announcements on major updates will also be posted to the
    1475 newsgroup `comp.lang.python.announce`_ .
    1476 
    1477 .. _`download page`: http://ftputil.sschwarzer.net/download
    1478 .. _`mailing list`: http://ftputil.sschwarzer.net/mailinglist
    1479 .. _`comp.lang.python.announce`: news:comp.lang.python.announce
     1503`Python announcements list`_.
     1504
     1505.. _`download page`: https://ftputil.sschwarzer.net/download
     1506.. _`mailing list`: https://ftputil.sschwarzer.net/mailinglist
     1507.. _`Python announcements list`: https://mail.python.org/mailman3/lists/python-announce-list.python.org/
    14801508
    14811509Is there a mailing list on ``ftputil``?
    14821510~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    14831511
    1484 Yes, please visit http://ftputil.sschwarzer.net/mailinglist to
     1512Yes, please visit https://ftputil.sschwarzer.net/mailinglist to
    14851513subscribe or read the archives.
    14861514
     
    14971525have already been fixed.
    14981526
    1499 .. _`latest version`: http://ftputil.sschwarzer.net/download
    1500 
    1501 Please see http://ftputil.sschwarzer.net/issuetrackernotes for
     1527.. _`latest version`: https://ftputil.sschwarzer.net/download
     1528
     1529Please see https://ftputil.sschwarzer.net/issuetrackernotes for
    15021530guidelines on entering a bug in ``ftputil``'s ticket system. If you
    15031531are unsure if the behaviour you found is a bug or not, you should write
    1504 to the `ftputil mailing list`_. In *either* case you *must not*
    1505 include confidential information (user id, password, file names, etc.)
    1506 in the problem report! Be careful!
    1507 
    1508 Does ``ftputil`` support SSL/TLS?
    1509 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1510 
    1511 ``ftputil`` has no *built-in* SSL/TLS support.
     1532to the `ftputil mailing list`_. *Never* include confidential information
     1533(user id, password, file names, etc.) in the problem report! Be
     1534careful!
     1535
     1536Does ``ftputil`` support TLS?
     1537~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     1538
     1539``ftputil`` has no *built-in* TLS support.
    15121540
    15131541On the other hand, there are two ways to get TLS support with
    15141542ftputil:
    15151543
    1516 - In Python 2.7 and Python 3.2 and up, the ``ftplib`` library has a
    1517   class ``FTP_TLS`` that you can use for the ``session_factory``
    1518   keyword argument in the ``FTPHost`` constructor. You can't use the
    1519   class directly though if you need additional setup code in
    1520   comparison to ``ftplib.FTP``, for example calling ``prot_p``, to
    1521   secure the data connection. On the other hand,
    1522   `ftputil.session.session_factory`_ can be used to create a custom
    1523   session factory.
    1524  
    1525   If you have other requirements that ``session_factory`` can't
     1544- The ``ftplib`` library has a class ``FTP_TLS`` that you can use for
     1545  the ``session_factory`` keyword argument in the ``FTPHost``
     1546  constructor. You can't use the class directly though *if* you need
     1547  additional setup code in comparison to ``ftplib.FTP``, for example
     1548  calling ``prot_p``, to secure the data connection. On the other
     1549  hand, `ftputil.session.session_factory`_ can be used to create a
     1550  custom session factory.
     1551
     1552- If you have other requirements that ``session_factory`` can't
    15261553  fulfill, you may create your own session factory by inheriting from
    15271554  ``ftplib.FTP_TLS``::
     
    15511578.. _`ftputil.session.session_factory`: `Session factories`_
    15521579
    1553 - If you need to work with Python 2.6, you can use the
    1554   ``ftpslib.FTP_TLS`` class from the M2Crypto_ project. Again, you
    1555   can't use the class directly but need to use
    1556   ``ftputil.session.session_factory`` or a recipe similar to that
    1557   above.
    1558 
    1559   Unfortunately, ``M2Crypto.ftpslib.FTP_TLS`` (at least in version
    1560   0.22.3) doesn't work correctly if you pass unicode strings to its
    1561   methods. Since ``ftputil`` does exactly that at some point (even if
    1562   you used byte strings in ``ftputil`` calls) you need a workaround in
    1563   the session factory class::
    1564 
    1565     import M2Crypto
    1566 
    1567     import ftputil
    1568     import ftputil.tool
    1569 
    1570 
    1571     class M2CryptoSession(M2Crypto.ftpslib.FTP_TLS):
    1572 
    1573         def __init__(self, host, user, password):
    1574             M2Crypto.ftpslib.FTP_TLS.__init__(self)
    1575             # Change the port number if needed.
    1576             self.connect(host, 21)
    1577             self.auth_tls()
    1578             self.login(user, password)
    1579             self.prot_p()
    1580             self._fix_socket()
    1581             ...
    1582 
    1583         def _fix_socket(self):
    1584             """
    1585             Change the socket object so that arguments to `sendall`
    1586             are converted to byte strings before being used.
    1587             """
    1588             original_sendall = self.sock.sendall
    1589             # Bound method, therefore no `self` argument.
    1590             def sendall(data):
    1591                 data = ftputil.tool.as_bytes(data)
    1592                 return original_sendall(data)
    1593             self.sock.sendall = sendall
    1594 
    1595     # Note the `session_factory` parameter. Pass the class, not
    1596     # an instance.
    1597     with ftputil.FTPHost(server, user, password,
    1598                          session_factory=M2CryptoSession) as ftp_host:
    1599         # Use `ftp_host` as usual.
    1600         ...
    1601 
    1602   That said, ``session_factory`` has this workaround built in, so
    1603   normally you don't need to define the session factory yourself!
    1604 
    1605 .. _M2Crypto: https://github.com/martinpaljak/M2Crypto
    16061580
    16071581How do I connect to a non-default port?
     
    16351609
    16361610You may find that ``ftputil`` uploads or downloads files
    1637 unnecessarily, or not when it should. This can happen when the FTP
    1638 server is in a different time zone than the client on which
    1639 ``ftputil`` runs. Please see the section on `time zone correction`_.
    1640 It may even be sufficient to call `synchronize_times`_.
     1611unnecessarily, or not when it should. Please see the section on `time
     1612zone correction`_. It may even be sufficient to call
     1613`synchronize_times`_.
    16411614
    16421615When I use ``ftputil``, all I get is a ``ParserError`` exception
     
    16451618The FTP server you connect to may use a directory format that
    16461619``ftputil`` doesn't understand. You can either write and
    1647 `plug in an own parser`_ or ask on the `mailing list`_ for
     1620`plug in your own parser`_ or ask on the `mailing list`_ for
    16481621help.
    16491622
    1650 .. _`plug in an own parser`: `Writing directory parsers`_
     1623.. _`plug in your own parser`: `Writing directory parsers`_
    16511624
    16521625``isdir``, ``isfile`` or ``islink`` incorrectly return ``False``
     
    16711644--------------------
    16721645
    1673 - ``ftputil`` needs at least Python 2.6 to work.
     1646- ``ftputil`` needs at least Python 3.6 to work.
    16741647
    16751648- Whether ``ftputil`` "sees" "hidden" directory and file names (i. e.
     
    17041677files after installation is system-dependent.
    17051678
    1706 .. _`reStructuredText`: http://docutils.sourceforge.net/rst.html
    1707 
    1708 The files ``test_*.py`` and ``mock_ftplib.py`` are for unit-testing.
    1709 If you only *use* ``ftputil``, i. e. *don't* modify it, you can
    1710 delete these files.
     1679.. _`reStructuredText`: https://docutils.sourceforge.net/rst.html
     1680
     1681The files ``test_*.py`` and ``scripted_session.py`` are for
     1682unit-testing. If you only *use* ``ftputil``, i. e. *don't* modify it,
     1683you can delete these files.
    17111684
    17121685
     
    17141687----------
    17151688
    1716 - Mackinnon T, Freeman S, Craig P. 2000. `Endo-Testing:
    1717   Unit Testing with Mock Objects`_.
    1718 
    17191689- Postel J, Reynolds J. 1985. `RFC 959 - File Transfer Protocol (FTP)`_.
    17201690
    1721 - Van Rossum G et al. 2013. `Python Library Reference`_.
    1722 
    1723 .. _`Endo-Testing: Unit Testing with Mock Objects`:
    1724    http://www.connextra.com/aboutUs/mockobjects.pdf
    1725 .. _`RFC 959 - File Transfer Protocol (FTP)`: http://www.ietf.org/rfc/rfc959.txt
    1726 .. _`Python Library Reference`: https://docs.python.org/library/index.html
     1691- Python Software Foundation. 2020. `The Python Standard Library`_.
     1692
     1693.. _`RFC 959 - File Transfer Protocol (FTP)`: https://www.ietf.org/rfc/rfc959.txt
     1694.. _`The Python Standard Library`: https://docs.python.org/library/index.html
    17271695
    17281696
     
    17381706
    17391707Feedback is appreciated. :-)
    1740 
    17411708}}}