Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 396ff06

Browse files
committed
Fix issue 8594: adds a source_address parameter to ftplib module.
1 parent 8a14a0c commit 396ff06

4 files changed

Lines changed: 49 additions & 12 deletions

File tree

Doc/library/ftplib.rst

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,16 @@ Here's a sample session using the :mod:`ftplib` module::
4040

4141
The module defines the following items:
4242

43-
.. class:: FTP(host='', user='', passwd='', acct=''[, timeout])
43+
.. class:: FTP(host='', user='', passwd='', acct='', timeout=None, source_address=None)
4444

4545
Return a new instance of the :class:`FTP` class. When *host* is given, the
4646
method call ``connect(host)`` is made. When *user* is given, additionally
4747
the method call ``login(user, passwd, acct)`` is made (where *passwd* and
4848
*acct* default to the empty string when not given). The optional *timeout*
4949
parameter specifies a timeout in seconds for blocking operations like the
5050
connection attempt (if is not specified, the global default timeout setting
51-
will be used).
51+
will be used). *source_address* is a 2-tuple ``(host, port)`` for the socket
52+
to bind to as its source address before connecting.
5253

5354
:class:`FTP` class supports the :keyword:`with` statement. Here is a sample
5455
on how using it:
@@ -68,8 +69,11 @@ The module defines the following items:
6869
.. versionchanged:: 3.2
6970
Support for the :keyword:`with` statement was added.
7071

72+
.. versionchanged:: 3.3
73+
*source_address* parameter was added.
7174

72-
.. class:: FTP_TLS(host='', user='', passwd='', acct='', [keyfile[, certfile[, context[, timeout]]]])
75+
76+
.. class:: FTP_TLS(host='', user='', passwd='', acct='', keyfile=None, certfile=None, context=None, timeout=None, source_address=None)
7377

7478
A :class:`FTP` subclass which adds TLS support to FTP as described in
7579
:rfc:`4217`.
@@ -80,10 +84,15 @@ The module defines the following items:
8084
private key and certificate chain file name for the SSL connection.
8185
*context* parameter is a :class:`ssl.SSLContext` object which allows
8286
bundling SSL configuration options, certificates and private keys into a
83-
single (potentially long-lived) structure.
87+
single (potentially long-lived) structure. *source_address* is a 2-tuple
88+
``(host, port)`` for the socket to bind to as its source address before
89+
connecting.
8490

8591
.. versionadded:: 3.2
8692

93+
.. versionchanged:: 3.3
94+
*source_address* parameter was added.
95+
8796
Here's a sample session using the :class:`FTP_TLS` class:
8897

8998
>>> from ftplib import FTP_TLS
@@ -174,18 +183,22 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
174183
debugging output, logging each line sent and received on the control connection.
175184

176185

177-
.. method:: FTP.connect(host='', port=0[, timeout])
186+
.. method:: FTP.connect(host='', port=0, timeout=None, source_address=None)
178187

179188
Connect to the given host and port. The default port number is ``21``, as
180189
specified by the FTP protocol specification. It is rarely needed to specify a
181190
different port number. This function should be called only once for each
182191
instance; it should not be called at all if a host was given when the instance
183192
was created. All other methods can only be used after a connection has been
184193
made.
185-
186194
The optional *timeout* parameter specifies a timeout in seconds for the
187195
connection attempt. If no *timeout* is passed, the global default timeout
188196
setting will be used.
197+
*source_address* is a 2-tuple ``(host, port)`` for the socket to bind to as
198+
its source address before connecting.
199+
200+
.. versionchanged:: 3.3
201+
*source_address* parameter was added.
189202

190203

191204
.. method:: FTP.getwelcome()

Lib/ftplib.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ class FTP:
107107
# Optional arguments are host (for connect()),
108108
# and user, passwd, acct (for login())
109109
def __init__(self, host='', user='', passwd='', acct='',
110-
timeout=_GLOBAL_DEFAULT_TIMEOUT):
110+
timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None):
111+
self.source_address = source_address
111112
self.timeout = timeout
112113
if host:
113114
self.connect(host)
@@ -128,18 +129,23 @@ def __exit__(self, *args):
128129
if self.sock is not None:
129130
self.close()
130131

131-
def connect(self, host='', port=0, timeout=-999):
132+
def connect(self, host='', port=0, timeout=-999, source_address=None):
132133
'''Connect to host. Arguments are:
133134
- host: hostname to connect to (string, default previous host)
134135
- port: port to connect to (integer, default previous port)
136+
- source_address: a 2-tuple (host, port) for the socket to bind
137+
to as its source address before connecting.
135138
'''
136139
if host != '':
137140
self.host = host
138141
if port > 0:
139142
self.port = port
140143
if timeout != -999:
141144
self.timeout = timeout
142-
self.sock = socket.create_connection((self.host, self.port), self.timeout)
145+
if source_address is not None:
146+
self.source_address = source_address
147+
self.sock = socket.create_connection((self.host, self.port), self.timeout,
148+
source_address=self.source_address)
143149
self.af = self.sock.family
144150
self.file = self.sock.makefile('r', encoding=self.encoding)
145151
self.welcome = self.getresp()
@@ -334,7 +340,8 @@ def ntransfercmd(self, cmd, rest=None):
334340
size = None
335341
if self.passiveserver:
336342
host, port = self.makepasv()
337-
conn = socket.create_connection((host, port), self.timeout)
343+
conn = socket.create_connection((host, port), self.timeout,
344+
source_address=self.source_address)
338345
if rest is not None:
339346
self.sendcmd("REST %s" % rest)
340347
resp = self.sendcmd(cmd)
@@ -637,7 +644,7 @@ class FTP_TLS(FTP):
637644

638645
def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
639646
certfile=None, context=None,
640-
timeout=_GLOBAL_DEFAULT_TIMEOUT):
647+
timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None):
641648
if context is not None and keyfile is not None:
642649
raise ValueError("context and keyfile arguments are mutually "
643650
"exclusive")
@@ -648,7 +655,7 @@ def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
648655
self.certfile = certfile
649656
self.context = context
650657
self._prot_p = False
651-
FTP.__init__(self, host, user, passwd, acct, timeout)
658+
FTP.__init__(self, host, user, passwd, acct, timeout, source_address)
652659

653660
def login(self, user='', passwd='', acct='', secure=True):
654661
if secure and not isinstance(self.sock, ssl.SSLSocket):

Lib/test/test_ftplib.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,20 @@ def is_client_connected():
608608
self.assertEqual(self.server.handler_instance.last_received_cmd, 'quit')
609609
self.assertFalse(is_client_connected())
610610

611+
def test_source_address(self):
612+
self.client.quit()
613+
port = support.find_unused_port()
614+
self.client.connect(self.server.host, self.server.port,
615+
source_address=(HOST, port))
616+
self.assertEqual(self.client.sock.getsockname()[1], port)
617+
self.client.quit()
618+
619+
def test_source_address_passive_connection(self):
620+
port = support.find_unused_port()
621+
self.client.source_address = (HOST, port)
622+
sock = self.client.transfercmd('list')
623+
self.assertEqual(sock.getsockname()[1], port)
624+
611625
def test_parse257(self):
612626
self.assertEqual(ftplib.parse257('257 "/foo/bar"'), '/foo/bar')
613627
self.assertEqual(ftplib.parse257('257 "/foo/bar" created'), '/foo/bar')

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ Core and Builtins
3535
Library
3636
-------
3737

38+
- Issue 8594: ftplib now provides a source_address parameter to specify which
39+
(address, port) to bind to before connecting.
40+
3841
- Issue #11326: Add the missing connect_ex() implementation for SSL sockets,
3942
and make it work for non-blocking connects.
4043

0 commit comments

Comments
 (0)