Skip to content

Commit ef0e72b

Browse files
authored
gh-94172: Remove keyfile, certfile and check_hostname parameters (#94173)
Remove the keyfile, certfile and check_hostname parameters, deprecated since Python 3.6, in modules: ftplib, http.client, imaplib, poplib and smtplib. Use the context parameter (ssl_context in imaplib) instead. Parameters following the removed parameters become keyword-only parameters. ftplib: Remove the FTP_TLS.ssl_version class attribute: use the context parameter instead.
1 parent 9c4ae03 commit ef0e72b

File tree

12 files changed

+55
-181
lines changed

12 files changed

+55
-181
lines changed

‎Doc/whatsnew/3.12.rst‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,16 @@ Removed
540540
<https://github.com/sphinx-contrib/sphinx-lint>`_.
541541
(Contributed by Julien Palard in :gh:`98179`.)
542542

543+
* Remove the *keyfile*, *certfile* and *check_hostname* parameters, deprecated
544+
since Python 3.6, in modules: :mod:`ftplib`, :mod:`http.client`,
545+
:mod:`imaplib`, :mod:`poplib` and :mod:`smtplib`. Use the *context* parameter
546+
(*ssl_context* in :mod:`imaplib`) instead.
547+
(Contributed by Victor Stinner in :gh:`94172`.)
548+
549+
* :mod:`ftplib`: Remove the ``FTP_TLS.ssl_version`` class attribute: use the
550+
*context* parameter instead.
551+
(Contributed by Victor Stinner in :gh:`94172`.)
552+
543553

544554
Porting to Python 3.12
545555
======================

‎Lib/ftplib.py‎

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -713,28 +713,12 @@ class FTP_TLS(FTP):
713713
'221 Goodbye.'
714714
>>>
715715
'''
716-
ssl_version=ssl.PROTOCOL_TLS_CLIENT
717716

718717
def__init__(self, host='', user='', passwd='', acct='',
719-
keyfile=None, certfile=None, context=None,
720-
timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None, *,
721-
encoding='utf-8'):
722-
ifcontextisnotNoneandkeyfileisnotNone:
723-
raiseValueError("context and keyfile arguments are mutually "
724-
"exclusive")
725-
ifcontextisnotNoneandcertfileisnotNone:
726-
raiseValueError("context and certfile arguments are mutually "
727-
"exclusive")
728-
ifkeyfileisnotNoneorcertfileisnotNone:
729-
importwarnings
730-
warnings.warn("keyfile and certfile are deprecated, use a "
731-
"custom context instead", DeprecationWarning, 2)
732-
self.keyfile=keyfile
733-
self.certfile=certfile
718+
*, context=None, timeout=_GLOBAL_DEFAULT_TIMEOUT,
719+
source_address=None, encoding='utf-8'):
734720
ifcontextisNone:
735-
context=ssl._create_stdlib_context(self.ssl_version,
736-
certfile=certfile,
737-
keyfile=keyfile)
721+
context=ssl._create_stdlib_context()
738722
self.context=context
739723
self._prot_p=False
740724
super().__init__(host, user, passwd, acct,
@@ -749,7 +733,7 @@ def auth(self):
749733
'''Set up secure control connection by using TLS/SSL.'''
750734
ifisinstance(self.sock, ssl.SSLSocket):
751735
raiseValueError("Already using TLS")
752-
ifself.ssl_version>=ssl.PROTOCOL_TLS:
736+
ifself.context.protocol>=ssl.PROTOCOL_TLS:
753737
resp=self.voidcmd('AUTH TLS')
754738
else:
755739
resp=self.voidcmd('AUTH SSL')

‎Lib/http/client.py‎

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,33 +1414,14 @@ class HTTPSConnection(HTTPConnection):
14141414

14151415
default_port=HTTPS_PORT
14161416

1417-
# XXX Should key_file and cert_file be deprecated in favour of context?
1418-
1419-
def__init__(self, host, port=None, key_file=None, cert_file=None,
1420-
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
1421-
source_address=None, *, context=None,
1422-
check_hostname=None, blocksize=8192):
1417+
def__init__(self, host, port=None,
1418+
*, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
1419+
source_address=None, context=None, blocksize=8192):
14231420
super(HTTPSConnection, self).__init__(host, port, timeout,
14241421
source_address,
14251422
blocksize=blocksize)
1426-
if (key_fileisnotNoneorcert_fileisnotNoneor
1427-
check_hostnameisnotNone):
1428-
importwarnings
1429-
warnings.warn("key_file, cert_file and check_hostname are "
1430-
"deprecated, use a custom context instead.",
1431-
DeprecationWarning, 2)
1432-
self.key_file=key_file
1433-
self.cert_file=cert_file
14341423
ifcontextisNone:
14351424
context=_create_https_context(self._http_vsn)
1436-
ifcheck_hostnameisnotNone:
1437-
context.check_hostname=check_hostname
1438-
ifkey_fileorcert_file:
1439-
context.load_cert_chain(cert_file, key_file)
1440-
# cert and key file means the user wants to authenticate.
1441-
# enable TLS 1.3 PHA implicitly even for custom contexts.
1442-
ifcontext.post_handshake_authisnotNone:
1443-
context.post_handshake_auth=True
14441425
self._context=context
14451426

14461427
defconnect(self):

‎Lib/imaplib.py‎

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,40 +1285,23 @@ class IMAP4_SSL(IMAP4):
12851285

12861286
"""IMAP4 client class over SSL connection
12871287
1288-
Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile[, ssl_context[, timeout=None]]]]]])
1288+
Instantiate with: IMAP4_SSL([host[, port[, ssl_context[, timeout=None]]]])
12891289
12901290
host - host's name (default: localhost);
12911291
port - port number (default: standard IMAP4 SSL port);
1292-
keyfile - PEM formatted file that contains your private key (default: None);
1293-
certfile - PEM formatted certificate chain file (default: None);
12941292
ssl_context - a SSLContext object that contains your certificate chain
12951293
and private key (default: None)
1296-
Note: if ssl_context is provided, then parameters keyfile or
1297-
certfile should not be set otherwise ValueError is raised.
12981294
timeout - socket timeout (default: None) If timeout is not given or is None,
12991295
the global default socket timeout is used
13001296
13011297
for more documentation see the docstring of the parent class IMAP4.
13021298
"""
13031299

13041300

1305-
def__init__(self, host='', port=IMAP4_SSL_PORT, keyfile=None,
1306-
certfile=None, ssl_context=None, timeout=None):
1307-
ifssl_contextisnotNoneandkeyfileisnotNone:
1308-
raiseValueError("ssl_context and keyfile arguments are mutually "
1309-
"exclusive")
1310-
ifssl_contextisnotNoneandcertfileisnotNone:
1311-
raiseValueError("ssl_context and certfile arguments are mutually "
1312-
"exclusive")
1313-
ifkeyfileisnotNoneorcertfileisnotNone:
1314-
importwarnings
1315-
warnings.warn("keyfile and certfile are deprecated, use a "
1316-
"custom ssl_context instead", DeprecationWarning, 2)
1317-
self.keyfile=keyfile
1318-
self.certfile=certfile
1301+
def__init__(self, host='', port=IMAP4_SSL_PORT,
1302+
*, ssl_context=None, timeout=None):
13191303
ifssl_contextisNone:
1320-
ssl_context=ssl._create_stdlib_context(certfile=certfile,
1321-
keyfile=keyfile)
1304+
ssl_context=ssl._create_stdlib_context()
13221305
self.ssl_context=ssl_context
13231306
IMAP4.__init__(self, host, port, timeout)
13241307

‎Lib/poplib.py‎

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -419,35 +419,19 @@ def stls(self, context=None):
419419
classPOP3_SSL(POP3):
420420
"""POP3 client class over SSL connection
421421
422-
Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None,
423-
context=None)
422+
Instantiate with: POP3_SSL(hostname, port=995, context=None)
424423
425424
hostname - the hostname of the pop3 over ssl server
426425
port - port number
427-
keyfile - PEM formatted file that contains your private key
428-
certfile - PEM formatted certificate chain file
429426
context - a ssl.SSLContext
430427
431428
See the methods of the parent class POP3 for more documentation.
432429
"""
433430

434-
def__init__(self, host, port=POP3_SSL_PORT, keyfile=None, certfile=None,
435-
timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None):
436-
ifcontextisnotNoneandkeyfileisnotNone:
437-
raiseValueError("context and keyfile arguments are mutually "
438-
"exclusive")
439-
ifcontextisnotNoneandcertfileisnotNone:
440-
raiseValueError("context and certfile arguments are mutually "
441-
"exclusive")
442-
ifkeyfileisnotNoneorcertfileisnotNone:
443-
importwarnings
444-
warnings.warn("keyfile and certfile are deprecated, use a "
445-
"custom context instead", DeprecationWarning, 2)
446-
self.keyfile=keyfile
447-
self.certfile=certfile
431+
def__init__(self, host, port=POP3_SSL_PORT,
432+
*, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None):
448433
ifcontextisNone:
449-
context=ssl._create_stdlib_context(certfile=certfile,
450-
keyfile=keyfile)
434+
context=ssl._create_stdlib_context()
451435
self.context=context
452436
POP3.__init__(self, host, port, timeout)
453437

@@ -457,7 +441,7 @@ def _create_socket(self, timeout):
457441
server_hostname=self.host)
458442
returnsock
459443

460-
defstls(self, keyfile=None, certfile=None, context=None):
444+
defstls(self, context=None):
461445
"""The method unconditionally raises an exception since the
462446
STLS command doesn't make any sense on an already established
463447
SSL/TLS session.

‎Lib/smtplib.py‎

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -749,14 +749,14 @@ def login(self, user, password, *, initial_response_ok=True):
749749
# We could not login successfully. Return result of last attempt.
750750
raiselast_exception
751751

752-
defstarttls(self, keyfile=None, certfile=None, context=None):
752+
defstarttls(self, *, context=None):
753753
"""Puts the connection to the SMTP server into TLS mode.
754754
755755
If there has been no previous EHLO or HELO command this session, this
756756
method tries ESMTP EHLO first.
757757
758758
If the server supports TLS, this will encrypt the rest of the SMTP
759-
session. If you provide the keyfile and certfile parameters,
759+
session. If you provide the context parameter,
760760
the identity of the SMTP server and client can be checked. This,
761761
however, depends on whether the socket module really checks the
762762
certificates.
@@ -774,19 +774,8 @@ def starttls(self, keyfile=None, certfile=None, context=None):
774774
ifresp==220:
775775
ifnot_have_ssl:
776776
raiseRuntimeError("No SSL support included in this Python")
777-
ifcontextisnotNoneandkeyfileisnotNone:
778-
raiseValueError("context and keyfile arguments are mutually "
779-
"exclusive")
780-
ifcontextisnotNoneandcertfileisnotNone:
781-
raiseValueError("context and certfile arguments are mutually "
782-
"exclusive")
783-
ifkeyfileisnotNoneorcertfileisnotNone:
784-
importwarnings
785-
warnings.warn("keyfile and certfile are deprecated, use a "
786-
"custom context instead", DeprecationWarning, 2)
787777
ifcontextisNone:
788-
context=ssl._create_stdlib_context(certfile=certfile,
789-
keyfile=keyfile)
778+
context=ssl._create_stdlib_context()
790779
self.sock=context.wrap_socket(self.sock,
791780
server_hostname=self._host)
792781
self.file=None
@@ -1017,35 +1006,18 @@ class SMTP_SSL(SMTP):
10171006
compiled with SSL support). If host is not specified, '' (the local
10181007
host) is used. If port is omitted, the standard SMTP-over-SSL port
10191008
(465) is used. local_hostname and source_address have the same meaning
1020-
as they do in the SMTP class. keyfile and certfile are also optional -
1021-
they can contain a PEM formatted private key and certificate chain file
1022-
for the SSL connection. context also optional, can contain a
1023-
SSLContext, and is an alternative to keyfile and certfile; If it is
1024-
specified both keyfile and certfile must be None.
1009+
as they do in the SMTP class. context also optional, can contain a
1010+
SSLContext.
10251011
10261012
"""
10271013

10281014
default_port=SMTP_SSL_PORT
10291015

10301016
def__init__(self, host='', port=0, local_hostname=None,
1031-
keyfile=None, certfile=None,
1032-
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
1017+
*, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
10331018
source_address=None, context=None):
1034-
ifcontextisnotNoneandkeyfileisnotNone:
1035-
raiseValueError("context and keyfile arguments are mutually "
1036-
"exclusive")
1037-
ifcontextisnotNoneandcertfileisnotNone:
1038-
raiseValueError("context and certfile arguments are mutually "
1039-
"exclusive")
1040-
ifkeyfileisnotNoneorcertfileisnotNone:
1041-
importwarnings
1042-
warnings.warn("keyfile and certfile are deprecated, use a "
1043-
"custom context instead", DeprecationWarning, 2)
1044-
self.keyfile=keyfile
1045-
self.certfile=certfile
10461019
ifcontextisNone:
1047-
context=ssl._create_stdlib_context(certfile=certfile,
1048-
keyfile=keyfile)
1020+
context=ssl._create_stdlib_context()
10491021
self.context=context
10501022
SMTP.__init__(self, host, port, local_hostname, timeout,
10511023
source_address)

‎Lib/test/test_ftplib.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -984,11 +984,11 @@ def test_context(self):
984984
ctx=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
985985
ctx.check_hostname=False
986986
ctx.verify_mode=ssl.CERT_NONE
987-
self.assertRaises(ValueError, ftplib.FTP_TLS, keyfile=CERTFILE,
987+
self.assertRaises(TypeError, ftplib.FTP_TLS, keyfile=CERTFILE,
988988
context=ctx)
989-
self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
989+
self.assertRaises(TypeError, ftplib.FTP_TLS, certfile=CERTFILE,
990990
context=ctx)
991-
self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
991+
self.assertRaises(TypeError, ftplib.FTP_TLS, certfile=CERTFILE,
992992
keyfile=CERTFILE, context=ctx)
993993

994994
self.client=ftplib.FTP_TLS(context=ctx, timeout=TIMEOUT)

‎Lib/test/test_httplib.py‎

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,7 +1978,7 @@ def test_local_unknown_cert(self):
19781978
self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
19791979

19801980
deftest_local_good_hostname(self):
1981-
# The (valid) cert validates the HTTP hostname
1981+
# The (valid) cert validates the HTTPS hostname
19821982
importssl
19831983
server=self.make_server(CERT_localhost)
19841984
context=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
@@ -1991,46 +1991,29 @@ def test_local_good_hostname(self):
19911991
self.assertEqual(resp.status, 404)
19921992

19931993
deftest_local_bad_hostname(self):
1994-
# The (valid) cert doesn't validate the HTTP hostname
1994+
# The (valid) cert doesn't validate the HTTPS hostname
19951995
importssl
19961996
server=self.make_server(CERT_fakehostname)
19971997
context=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
19981998
context.load_verify_locations(CERT_fakehostname)
19991999
h=client.HTTPSConnection('localhost', server.port, context=context)
20002000
withself.assertRaises(ssl.CertificateError):
20012001
h.request('GET', '/')
2002-
# Same with explicit check_hostname=True
2003-
withwarnings_helper.check_warnings(('', DeprecationWarning)):
2004-
h=client.HTTPSConnection('localhost', server.port,
2005-
context=context, check_hostname=True)
2002+
2003+
# Same with explicit context.check_hostname=True
2004+
context.check_hostname=True
2005+
h=client.HTTPSConnection('localhost', server.port, context=context)
20062006
withself.assertRaises(ssl.CertificateError):
20072007
h.request('GET', '/')
2008-
# With check_hostname=False, the mismatching is ignored
2009-
context.check_hostname=False
2010-
withwarnings_helper.check_warnings(('', DeprecationWarning)):
2011-
h=client.HTTPSConnection('localhost', server.port,
2012-
context=context, check_hostname=False)
2013-
h.request('GET', '/nonexistent')
2014-
resp=h.getresponse()
2015-
resp.close()
2016-
h.close()
2017-
self.assertEqual(resp.status, 404)
2018-
# The context's check_hostname setting is used if one isn't passed to
2019-
# HTTPSConnection.
2008+
2009+
# With context.check_hostname=False, the mismatching is ignored
20202010
context.check_hostname=False
20212011
h=client.HTTPSConnection('localhost', server.port, context=context)
20222012
h.request('GET', '/nonexistent')
20232013
resp=h.getresponse()
2024-
self.assertEqual(resp.status, 404)
20252014
resp.close()
20262015
h.close()
2027-
# Passing check_hostname to HTTPSConnection should override the
2028-
# context's setting.
2029-
withwarnings_helper.check_warnings(('', DeprecationWarning)):
2030-
h=client.HTTPSConnection('localhost', server.port,
2031-
context=context, check_hostname=True)
2032-
withself.assertRaises(ssl.CertificateError):
2033-
h.request('GET', '/')
2016+
self.assertEqual(resp.status, 404)
20342017

20352018
@unittest.skipIf(nothasattr(client, 'HTTPSConnection'),
20362019
'http.client.HTTPSConnection not available')
@@ -2066,11 +2049,9 @@ def test_tls13_pha(self):
20662049
self.assertIs(h._context, context)
20672050
self.assertFalse(h._context.post_handshake_auth)
20682051

2069-
withwarnings.catch_warnings():
2070-
warnings.filterwarnings('ignore', 'key_file, cert_file and check_hostname are deprecated',
2071-
DeprecationWarning)
2072-
h=client.HTTPSConnection('localhost', 443, context=context,
2073-
cert_file=CERT_localhost)
2052+
context=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT, cert_file=CERT_localhost)
2053+
context.post_handshake_auth=True
2054+
h=client.HTTPSConnection('localhost', 443, context=context)
20742055
self.assertTrue(h._context.post_handshake_auth)
20752056

20762057

‎Lib/test/test_imaplib.py‎

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -573,15 +573,6 @@ def test_ssl_verified(self):
573573
ssl_context=ssl_context)
574574
client.shutdown()
575575

576-
# Mock the private method _connect(), so mark the test as specific
577-
# to CPython stdlib
578-
@cpython_only
579-
deftest_certfile_arg_warn(self):
580-
withwarnings_helper.check_warnings(('', DeprecationWarning)):
581-
withmock.patch.object(self.imap_class, 'open'):
582-
withmock.patch.object(self.imap_class, '_connect'):
583-
self.imap_class('localhost', 143, certfile=CERTFILE)
584-
585576
classThreadedNetworkedTests(unittest.TestCase):
586577
server_class=socketserver.TCPServer
587578
imap_class=imaplib.IMAP4
@@ -1070,18 +1061,6 @@ def test_logout(self):
10701061
rs=_server.logout()
10711062
self.assertEqual(rs[0], 'BYE', rs)
10721063

1073-
deftest_ssl_context_certfile_exclusive(self):
1074-
withsocket_helper.transient_internet(self.host):
1075-
self.assertRaises(
1076-
ValueError, self.imap_class, self.host, self.port,
1077-
certfile=CERTFILE, ssl_context=self.create_ssl_context())
1078-
1079-
deftest_ssl_context_keyfile_exclusive(self):
1080-
withsocket_helper.transient_internet(self.host):
1081-
self.assertRaises(
1082-
ValueError, self.imap_class, self.host, self.port,
1083-
keyfile=CERTFILE, ssl_context=self.create_ssl_context())
1084-
10851064

10861065
if__name__=="__main__":
10871066
unittest.main()

0 commit comments

Comments
(0)