cpython: 26839edf3cc1 (original) (raw)
Mercurial > cpython
changeset 71617:26839edf3cc1
Fix closes Issue11281 - smtplib.STMP gets source_address parameter, which adds the ability to bind to specific source address on a machine with multiple interfaces. Patch by Paulo Scardine.
Senthil Kumaran senthil@uthcode.com | |
---|---|
date | Sat, 30 Jul 2011 10:56:50 +0800 |
parents | 018e14a46454 |
children | f207a41c6063 |
files | Doc/library/smtplib.rst Lib/smtplib.py Lib/test/mock_socket.py Lib/test/test_smtplib.py Misc/NEWS |
diffstat | 5 files changed, 75 insertions(+), 22 deletions(-)[+] [-] Doc/library/smtplib.rst 33 Lib/smtplib.py 40 Lib/test/mock_socket.py 3 Lib/test/test_smtplib.py 17 Misc/NEWS 4 |
line wrap: on
line diff
--- a/Doc/library/smtplib.rst
+++ b/Doc/library/smtplib.rst
@@ -20,7 +20,7 @@ details of SMTP and ESMTP operation, con
Protocol) and :rfc:1869
(SMTP Service Extensions).
-.. class:: SMTP(host='', port=0, local_hostname=None[, timeout])
+.. class:: SMTP(host='', port=0, local_hostname=None[, timeout], source_address=None)
A :class:SMTP
instance encapsulates an SMTP connection. It has methods
that support a full repertoire of SMTP and ESMTP operations. If the optional
@@ -29,7 +29,12 @@ Protocol) and :rfc:1869
(SMTP Service
raised if the specified host doesn't respond correctly. The optional
timeout parameter specifies a timeout in seconds for blocking operations
like the connection attempt (if not specified, the global default timeout
- setting will be used). The optional source_address parameter allows to bind to some
- specific source address in a machine with multiple network interfaces,
- and/or to some specific source tcp port. It takes a 2-tuple (host, port),
- for the socket to bind to as its source address before connecting. If
- ommited (or if host or port are '' and/or 0 respectively) the OS default
- behavior will be used.
For normal use, you should only require the initialization/connect,
:meth:
sendmail
, and :meth:quit
methods. An example is included below. @@ -48,8 +53,10 @@ Protocol) and :rfc:1869
(SMTP Service .. versionchanged:: 3.3 Support for the :keyword:with
statement was added. - .. versionadded:: 3.3
source_address parameter.[](#l1.31)
-.. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None[, timeout], context=None)
+.. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None[, timeout], context=None, source_address=None)
A :class:SMTP_SSL
instance behaves exactly the same as instances of
:class:SMTP
. :class:SMTP_SSL
should be used for situations where SSL is
@@ -62,18 +69,28 @@ Protocol) and :rfc:1869
(SMTP Service
keyfile and certfile must be None. The optional timeout
parameter specifies a timeout in seconds for blocking operations like the
connection attempt (if not specified, the global default timeout setting
- will be used). The optional source_address parameter allows to bind to some
- specific source address in a machine with multiple network interfaces,
- and/or to some specific source tcp port. It takes a 2-tuple (host, port),
- for the socket to bind to as its source address before connecting. If
- ommited (or if host or port are '' and/or 0 respectively) the OS default
- behavior will be used. .. versionchanged:: 3.3 context was added.
- .. versionadded:: 3.3
source_address parameter.[](#l1.54)
-.. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None) + +.. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None, source_address=None) The LMTP protocol, which is very similar to ESMTP, is heavily based on the
- standard SMTP client. It's common to use Unix sockets for LMTP, so our :meth:
connect
- method must support that as well as a regular host:port server. To specify a
- Unix socket, you must use an absolute path for host, starting with a '/'.
- standard SMTP client. It's common to use Unix sockets for LMTP, so our
- :meth:
connect
method must support that as well as a regular host:port - server. The optional parameters local_hostname and source_address has the
- same meaning as that of SMTP client.To specify a Unix socket, you must use
- an absolute path for host, starting with a '/'. Authentication is supported, using the regular SMTP mechanism. When using a Unix socket, LMTP generally don't support or require any authentication, but your
--- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -215,7 +215,8 @@ class SMTP: default_port = SMTP_PORT def init(self, host='', port=0, local_hostname=None,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT):[](#l2.7)
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,[](#l2.8)
source_address=None):[](#l2.9) """Initialize a new instance.[](#l2.10)
If specified, host' is the name of the remote host to which to[](#l2.12) @@ -223,11 +224,16 @@ class SMTP:[](#l2.13) By default, smtplib.SMTP_PORT is used. An SMTPConnectError is raised[](#l2.14) if the specified
host' doesn't respond correctly. If specified,
local_hostname
is used as the FQDN of the local host. By default,
the local hostname is found using socket.getfqdn().[](#l2.17)
the local hostname is found using socket.getfqdn(). The[](#l2.18)
`source_address` parameter takes a 2-tuple (host, port) for the socket[](#l2.19)
to bind to as its source address before connecting. If the host is ''[](#l2.20)
and port is 0, the OS default behavior will be used.[](#l2.21)
""" self.timeout = timeout self.esmtp_features = {}
self.source_address = source_address[](#l2.26)
+ if host: (code, msg) = self.connect(host, port) if code != 220: @@ -276,10 +282,11 @@ class SMTP: # This makes it simpler for SMTP_SSL to use the SMTP connect code # and just alter the socket connection bit. if self.debuglevel > 0:
print('connect:', (host, port), file=stderr)[](#l2.35)
return socket.create_connection((host, port), timeout)[](#l2.36)
print('connect: to', (host, port), self.source_address, file=stderr)[](#l2.37)
return socket.create_connection((host, port), timeout,[](#l2.38)
self.source_address)[](#l2.39)
- def connect(self, host='localhost', port=0, source_address=None): """Connect to a host on a given port.
If the hostname ends with a colon (`:') followed by a number, and @@ -290,6 +297,7 @@ class SMTP: specified during instantiation. """
if source_address: self.source_address = source_address[](#l2.50) if not port and (host.find(':') == host.rfind(':')):[](#l2.51) i = host.rfind(':')[](#l2.52) if i >= 0:[](#l2.53)
@@ -829,7 +837,8 @@ if _have_ssl: """ This is a subclass derived from SMTP that connects over an SSL encrypted socket (to use this class you need a socket module that was compiled with SSL support). If host is not specified, '' (the local host) is used. If port is
omitted, the standard SMTP-over-SSL port (465) is used. keyfile and certfile[](#l2.58)
omitted, the standard SMTP-over-SSL port (465) is used. The optional[](#l2.59)
source_address takes a two-tuple (host,port) for socket to bind to. keyfile and certfile[](#l2.60) are also optional - they can contain a PEM formatted private key and[](#l2.61) certificate chain file for the SSL connection. context also optional, can contain[](#l2.62) a SSLContext, and is an alternative to keyfile and certfile; If it is specified both[](#l2.63)
@@ -840,7 +849,8 @@ if _have_ssl: def init(self, host='', port=0, local_hostname=None, keyfile=None, certfile=None,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None):[](#l2.68)
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,[](#l2.69)
source_address=None, context=None):[](#l2.70) if context is not None and keyfile is not None:[](#l2.71) raise ValueError("context and keyfile arguments are mutually "[](#l2.72) "exclusive")[](#l2.73)
@@ -850,12 +860,14 @@ if _have_ssl: self.keyfile = keyfile self.certfile = certfile self.context = context
SMTP.__init__(self, host, port, local_hostname, timeout)[](#l2.78)
SMTP.__init__(self, host, port, local_hostname, timeout,[](#l2.79)
source_address)[](#l2.80)
def _get_socket(self, host, port, timeout): if self.debuglevel > 0: print('connect:', (host, port), file=stderr)
new_socket = socket.create_connection((host, port), timeout)[](#l2.85)
new_socket = socket.create_connection((host, port), timeout,[](#l2.86)
self.source_address)[](#l2.87) if self.context is not None:[](#l2.88) new_socket = self.context.wrap_socket(new_socket)[](#l2.89) else:[](#l2.90)
@@ -884,14 +896,16 @@ class LMTP(SMTP): ehlo_msg = "lhlo"
- def init(self, host='', port=LMTP_PORT, local_hostname=None,
source_address=None):[](#l2.97) """Initialize a new instance."""[](#l2.98)
SMTP.__init__(self, host, port, local_hostname)[](#l2.99)
SMTP.__init__(self, host, port, local_hostname = local_hostname,[](#l2.100)
source_address = source_address)[](#l2.101)
- def connect(self, host='localhost', port=0, source_address=None): """Connect to the LMTP daemon, on either a Unix or a TCP socket.""" if host[0] != '/':
return SMTP.connect(self, host, port)[](#l2.107)
return SMTP.connect(self, host, port, source_address = source_address)[](#l2.108)
# Handle Unix-domain sockets. try:
--- a/Lib/test/mock_socket.py +++ b/Lib/test/mock_socket.py @@ -106,7 +106,8 @@ def socket(family=None, type=None, proto return MockSocket() -def create_connection(address, timeout=socket_module._GLOBAL_DEFAULT_TIMEOUT): +def create_connection(address, timeout=socket_module._GLOBAL_DEFAULT_TIMEOUT,
--- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -72,6 +72,14 @@ class GeneralTests(unittest.TestCase): smtp = smtplib.SMTP(HOST, self.port) smtp.close()
- def testSourceAddress(self):
mock_socket.reply_with(b"220 Hola mundo")[](#l4.8)
# connects[](#l4.9)
smtp = smtplib.SMTP(HOST, self.port,[](#l4.10)
source_address=('127.0.0.1',19876))[](#l4.11)
self.assertEqual(smtp.source_address, ('127.0.0.1', 19876))[](#l4.12)
smtp.close()[](#l4.13)
+ def testBasic2(self): mock_socket.reply_with(b"220 Hola mundo") # connects, include port in host name @@ -206,6 +214,15 @@ class DebuggingServerTests(unittest.Test smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3) smtp.quit()
- def testSourceAddress(self):
# connect[](#l4.23)
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3,[](#l4.24)
source_address=('127.0.0.1', 19876))[](#l4.25)
self.assertEqual(smtp.source_address, ('127.0.0.1', 19876))[](#l4.26)
self.assertEqual(smtp.local_hostname, 'localhost')[](#l4.27)
print(dir(smtp))[](#l4.28)
smtp.quit()[](#l4.29)
+ def testNOOP(self): smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3) expected = (250, b'Ok')
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -246,6 +246,10 @@ Core and Builtins Library ------- +- Issue #11281: smtplib.STMP gets source_address parameter, which adds the