From webhook-mailer at python.org Mon Jul 1 02:04:26 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 06:04:26 -0000 Subject: [Python-checkins] bpo-36168: Lowercase the word "subsequent" in get_value doc (GH-14485) Message-ID: https://github.com/python/cpython/commit/12b436e3b079fb3e3a7197c089df90a77e3bdd77 commit: 12b436e3b079fb3e3a7197c089df90a77e3bdd77 branch: master author: Krishna Oza committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-06-30T23:04:20-07:00 summary: bpo-36168: Lowercase the word "subsequent" in get_value doc (GH-14485) Subsequent -> subsequent https://bugs.python.org/issue36168 files: M Doc/library/string.rst diff --git a/Doc/library/string.rst b/Doc/library/string.rst index 288dde6b3fe4..af8b9b358cc3 100644 --- a/Doc/library/string.rst +++ b/Doc/library/string.rst @@ -146,7 +146,7 @@ implementation as the built-in :meth:`~str.format` method. keyword arguments. For compound field names, these functions are only called for the first - component of the field name; Subsequent components are handled through + component of the field name; subsequent components are handled through normal attribute and indexing operations. So for example, the field expression '0.name' would cause From webhook-mailer at python.org Mon Jul 1 02:29:22 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 06:29:22 -0000 Subject: [Python-checkins] bpo-37428: Don't set PHA verify flag on client side (GH-14421) Message-ID: https://github.com/python/cpython/commit/f0f5930ac88482ef896283db5be9b8d508d077db commit: f0f5930ac88482ef896283db5be9b8d508d077db branch: master author: Christian Heimes committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-06-30T23:29:17-07:00 summary: bpo-37428: Don't set PHA verify flag on client side (GH-14421) SSLContext.post_handshake_auth = True no longer sets SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the option is documented as ignored for clients, OpenSSL implicitly enables cert chain validation when the flag is set. Signed-off-by: Christian Heimes https://bugs.python.org/issue37428 files: A Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst M Lib/test/test_ssl.py M Modules/_ssl.c diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 064f0e8d4de6..d83ee2cc974d 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4434,6 +4434,37 @@ def test_pha_not_tls13(self): s.write(b'PHA') self.assertIn(b'WRONG_SSL_VERSION', s.recv(1024)) + def test_bpo37428_pha_cert_none(self): + # verify that post_handshake_auth does not implicitly enable cert + # validation. + hostname = SIGNED_CERTFILE_HOSTNAME + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.post_handshake_auth = True + client_context.load_cert_chain(SIGNED_CERTFILE) + # no cert validation and CA on client side + client_context.check_hostname = False + client_context.verify_mode = ssl.CERT_NONE + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.load_cert_chain(SIGNED_CERTFILE) + server_context.load_verify_locations(SIGNING_CA) + server_context.post_handshake_auth = True + server_context.verify_mode = ssl.CERT_REQUIRED + + server = ThreadedEchoServer(context=server_context, chatty=False) + with server: + with client_context.wrap_socket(socket.socket(), + server_hostname=hostname) as s: + s.connect((HOST, server.port)) + s.write(b'HASCERT') + self.assertEqual(s.recv(1024), b'FALSE\n') + s.write(b'PHA') + self.assertEqual(s.recv(1024), b'OK\n') + s.write(b'HASCERT') + self.assertEqual(s.recv(1024), b'TRUE\n') + # server cert has not been validated + self.assertEqual(s.getpeercert(), {}) + HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename') requires_keylog = unittest.skipUnless( diff --git a/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst b/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst new file mode 100644 index 000000000000..2cdce6b24dc6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst @@ -0,0 +1,4 @@ +SSLContext.post_handshake_auth = True no longer sets +SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the +option is documented as ignored for clients, OpenSSL implicitly enables cert +chain validation when the flag is set. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 2331c58ad77d..3351af6cdefd 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -963,6 +963,26 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, SSL_set_mode(self->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY); +#ifdef TLS1_3_VERSION + if (sslctx->post_handshake_auth == 1) { + if (socket_type == PY_SSL_SERVER) { + /* bpo-37428: OpenSSL does not ignore SSL_VERIFY_POST_HANDSHAKE. + * Set SSL_VERIFY_POST_HANDSHAKE flag only for server sockets and + * only in combination with SSL_VERIFY_PEER flag. */ + int mode = SSL_get_verify_mode(self->ssl); + if (mode & SSL_VERIFY_PEER) { + int (*verify_cb)(int, X509_STORE_CTX *) = NULL; + verify_cb = SSL_get_verify_callback(self->ssl); + mode |= SSL_VERIFY_POST_HANDSHAKE; + SSL_set_verify(self->ssl, mode, verify_cb); + } + } else { + /* client socket */ + SSL_set_post_handshake_auth(self->ssl, 1); + } + } +#endif + if (server_hostname != NULL) { if (_ssl_configure_hostname(self, server_hostname) < 0) { Py_DECREF(self); @@ -2986,10 +3006,10 @@ _set_verify_mode(PySSLContext *self, enum py_ssl_cert_requirements n) "invalid value for verify_mode"); return -1; } -#ifdef TLS1_3_VERSION - if (self->post_handshake_auth) - mode |= SSL_VERIFY_POST_HANDSHAKE; -#endif + + /* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for + * server sockets and SSL_set_post_handshake_auth() for client. */ + /* keep current verify cb */ verify_cb = SSL_CTX_get_verify_callback(self->ctx); SSL_CTX_set_verify(self->ctx, mode, verify_cb); @@ -3735,8 +3755,6 @@ get_post_handshake_auth(PySSLContext *self, void *c) { #if TLS1_3_VERSION static int set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) { - int (*verify_cb)(int, X509_STORE_CTX *) = NULL; - int mode = SSL_CTX_get_verify_mode(self->ctx); if (arg == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; @@ -3748,17 +3766,8 @@ set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) { } self->post_handshake_auth = pha; - /* client-side socket setting, ignored by server-side */ - SSL_CTX_set_post_handshake_auth(self->ctx, pha); - - /* server-side socket setting, ignored by client-side */ - verify_cb = SSL_CTX_get_verify_callback(self->ctx); - if (pha) { - mode |= SSL_VERIFY_POST_HANDSHAKE; - } else { - mode ^= SSL_VERIFY_POST_HANDSHAKE; - } - SSL_CTX_set_verify(self->ctx, mode, verify_cb); + /* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for + * server sockets and SSL_set_post_handshake_auth() for client. */ return 0; } From webhook-mailer at python.org Mon Jul 1 02:32:28 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 06:32:28 -0000 Subject: [Python-checkins] bpo-37440: Enable TLS 1.3 post-handshake auth in http.client (GH-14448) Message-ID: https://github.com/python/cpython/commit/d1bd6e79da1ee56dc1b902d804216ffd267399db commit: d1bd6e79da1ee56dc1b902d804216ffd267399db branch: master author: Christian Heimes committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-06-30T23:32:24-07:00 summary: bpo-37440: Enable TLS 1.3 post-handshake auth in http.client (GH-14448) Post-handshake authentication is required for conditional client cert authentication with TLS 1.3. https://bugs.python.org/issue37440 files: A Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst M Doc/library/http.client.rst M Lib/http/client.py M Lib/test/test_httplib.py diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index beaa720d732b..4e761cd39a01 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -94,6 +94,11 @@ The module provides the following classes: :func:`ssl._create_unverified_context` can be passed to the *context* parameter. + .. versionchanged:: 3.8 + This class now enables TLS 1.3 + :attr:`ssl.SSLContext.post_handshake_auth` for the default *context* or + when *cert_file* is passed with a custom *context*. + .. deprecated:: 3.6 *key_file* and *cert_file* are deprecated in favor of *context*. diff --git a/Lib/http/client.py b/Lib/http/client.py index 82908ebe3afd..f61267e108a5 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1358,6 +1358,9 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, self.cert_file = cert_file if context is None: context = ssl._create_default_https_context() + # enable PHA for TLS 1.3 connections if available + if context.post_handshake_auth is not None: + context.post_handshake_auth = True will_verify = context.verify_mode != ssl.CERT_NONE if check_hostname is None: check_hostname = context.check_hostname @@ -1366,6 +1369,10 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, "either CERT_OPTIONAL or CERT_REQUIRED") if key_file or cert_file: context.load_cert_chain(cert_file, key_file) + # cert and key file means the user wants to authenticate. + # enable TLS 1.3 PHA implicitly even for custom contexts. + if context.post_handshake_auth is not None: + context.post_handshake_auth = True self._context = context if check_hostname is not None: self._context.check_hostname = check_hostname diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 968cbd86a1e4..9148169cc7c2 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1745,6 +1745,24 @@ def test_host_port(self): self.assertEqual(h, c.host) self.assertEqual(p, c.port) + def test_tls13_pha(self): + import ssl + if not ssl.HAS_TLSv1_3: + self.skipTest('TLS 1.3 support required') + # just check status of PHA flag + h = client.HTTPSConnection('localhost', 443) + self.assertTrue(h._context.post_handshake_auth) + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + self.assertFalse(context.post_handshake_auth) + h = client.HTTPSConnection('localhost', 443, context=context) + self.assertIs(h._context, context) + self.assertFalse(h._context.post_handshake_auth) + + h = client.HTTPSConnection('localhost', 443, context=context, + cert_file=CERT_localhost) + self.assertTrue(h._context.post_handshake_auth) + class RequestBodyTest(TestCase): """Test cases where a request includes a message body.""" diff --git a/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst b/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst new file mode 100644 index 000000000000..b336e061e15e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst @@ -0,0 +1,2 @@ +http.client now enables TLS 1.3 post-handshake authentication for default +context or if a cert_file is passed to HTTPSConnection. From webhook-mailer at python.org Mon Jul 1 02:51:44 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 06:51:44 -0000 Subject: [Python-checkins] [3.7] bpo-37428: Don't set PHA verify flag on client side (GH-14421) (GH-14493) Message-ID: https://github.com/python/cpython/commit/cf7617460a920dd75ced017792045d3ae77648ad commit: cf7617460a920dd75ced017792045d3ae77648ad branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-06-30T23:51:40-07:00 summary: [3.7] bpo-37428: Don't set PHA verify flag on client side (GH-14421) (GH-14493) SSLContext.post_handshake_auth = True no longer sets SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the option is documented as ignored for clients, OpenSSL implicitly enables cert chain validation when the flag is set. Signed-off-by: Christian Heimes https://bugs.python.org/issue37428 (cherry picked from commit f0f5930ac88482ef896283db5be9b8d508d077db) Co-authored-by: Christian Heimes https://bugs.python.org/issue37428 files: A Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst M Lib/test/test_ssl.py M Modules/_ssl.c diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 73b6bdf01e7f..86f790b4a22e 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4437,6 +4437,37 @@ def test_pha_not_tls13(self): s.write(b'PHA') self.assertIn(b'WRONG_SSL_VERSION', s.recv(1024)) + def test_bpo37428_pha_cert_none(self): + # verify that post_handshake_auth does not implicitly enable cert + # validation. + hostname = SIGNED_CERTFILE_HOSTNAME + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.post_handshake_auth = True + client_context.load_cert_chain(SIGNED_CERTFILE) + # no cert validation and CA on client side + client_context.check_hostname = False + client_context.verify_mode = ssl.CERT_NONE + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.load_cert_chain(SIGNED_CERTFILE) + server_context.load_verify_locations(SIGNING_CA) + server_context.post_handshake_auth = True + server_context.verify_mode = ssl.CERT_REQUIRED + + server = ThreadedEchoServer(context=server_context, chatty=False) + with server: + with client_context.wrap_socket(socket.socket(), + server_hostname=hostname) as s: + s.connect((HOST, server.port)) + s.write(b'HASCERT') + self.assertEqual(s.recv(1024), b'FALSE\n') + s.write(b'PHA') + self.assertEqual(s.recv(1024), b'OK\n') + s.write(b'HASCERT') + self.assertEqual(s.recv(1024), b'TRUE\n') + # server cert has not been validated + self.assertEqual(s.getpeercert(), {}) + def test_main(verbose=False): if support.verbose: diff --git a/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst b/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst new file mode 100644 index 000000000000..2cdce6b24dc6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst @@ -0,0 +1,4 @@ +SSLContext.post_handshake_auth = True no longer sets +SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the +option is documented as ignored for clients, OpenSSL implicitly enables cert +chain validation when the flag is set. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 30c91f59310f..e8955eedfa53 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -931,6 +931,26 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, SSL_set_mode(self->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY); +#ifdef TLS1_3_VERSION + if (sslctx->post_handshake_auth == 1) { + if (socket_type == PY_SSL_SERVER) { + /* bpo-37428: OpenSSL does not ignore SSL_VERIFY_POST_HANDSHAKE. + * Set SSL_VERIFY_POST_HANDSHAKE flag only for server sockets and + * only in combination with SSL_VERIFY_PEER flag. */ + int mode = SSL_get_verify_mode(self->ssl); + if (mode & SSL_VERIFY_PEER) { + int (*verify_cb)(int, X509_STORE_CTX *) = NULL; + verify_cb = SSL_get_verify_callback(self->ssl); + mode |= SSL_VERIFY_POST_HANDSHAKE; + SSL_set_verify(self->ssl, mode, verify_cb); + } + } else { + /* client socket */ + SSL_set_post_handshake_auth(self->ssl, 1); + } + } +#endif + if (server_hostname != NULL) { if (_ssl_configure_hostname(self, server_hostname) < 0) { Py_DECREF(self); @@ -2928,10 +2948,10 @@ _set_verify_mode(PySSLContext *self, enum py_ssl_cert_requirements n) "invalid value for verify_mode"); return -1; } -#ifdef TLS1_3_VERSION - if (self->post_handshake_auth) - mode |= SSL_VERIFY_POST_HANDSHAKE; -#endif + + /* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for + * server sockets and SSL_set_post_handshake_auth() for client. */ + /* keep current verify cb */ verify_cb = SSL_CTX_get_verify_callback(self->ctx); SSL_CTX_set_verify(self->ctx, mode, verify_cb); @@ -3628,8 +3648,6 @@ get_post_handshake_auth(PySSLContext *self, void *c) { #if TLS1_3_VERSION static int set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) { - int (*verify_cb)(int, X509_STORE_CTX *) = NULL; - int mode = SSL_CTX_get_verify_mode(self->ctx); if (arg == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; @@ -3641,17 +3659,8 @@ set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) { } self->post_handshake_auth = pha; - /* client-side socket setting, ignored by server-side */ - SSL_CTX_set_post_handshake_auth(self->ctx, pha); - - /* server-side socket setting, ignored by client-side */ - verify_cb = SSL_CTX_get_verify_callback(self->ctx); - if (pha) { - mode |= SSL_VERIFY_POST_HANDSHAKE; - } else { - mode ^= SSL_VERIFY_POST_HANDSHAKE; - } - SSL_CTX_set_verify(self->ctx, mode, verify_cb); + /* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for + * server sockets and SSL_set_post_handshake_auth() for client. */ return 0; } From webhook-mailer at python.org Mon Jul 1 03:07:50 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 07:07:50 -0000 Subject: [Python-checkins] [3.8] bpo-37440: Enable TLS 1.3 post-handshake auth in http.client (GH-14448) (GH-14495) Message-ID: https://github.com/python/cpython/commit/ee72dda9616258b57c19eb5af00f3e80a3fb8e22 commit: ee72dda9616258b57c19eb5af00f3e80a3fb8e22 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-01T00:07:44-07:00 summary: [3.8] bpo-37440: Enable TLS 1.3 post-handshake auth in http.client (GH-14448) (GH-14495) Post-handshake authentication is required for conditional client cert authentication with TLS 1.3. https://bugs.python.org/issue37440 (cherry picked from commit d1bd6e79da1ee56dc1b902d804216ffd267399db) Co-authored-by: Christian Heimes https://bugs.python.org/issue37440 files: A Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst M Doc/library/http.client.rst M Lib/http/client.py M Lib/test/test_httplib.py diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index beaa720d732b..4e761cd39a01 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -94,6 +94,11 @@ The module provides the following classes: :func:`ssl._create_unverified_context` can be passed to the *context* parameter. + .. versionchanged:: 3.8 + This class now enables TLS 1.3 + :attr:`ssl.SSLContext.post_handshake_auth` for the default *context* or + when *cert_file* is passed with a custom *context*. + .. deprecated:: 3.6 *key_file* and *cert_file* are deprecated in favor of *context*. diff --git a/Lib/http/client.py b/Lib/http/client.py index 82908ebe3afd..f61267e108a5 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1358,6 +1358,9 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, self.cert_file = cert_file if context is None: context = ssl._create_default_https_context() + # enable PHA for TLS 1.3 connections if available + if context.post_handshake_auth is not None: + context.post_handshake_auth = True will_verify = context.verify_mode != ssl.CERT_NONE if check_hostname is None: check_hostname = context.check_hostname @@ -1366,6 +1369,10 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, "either CERT_OPTIONAL or CERT_REQUIRED") if key_file or cert_file: context.load_cert_chain(cert_file, key_file) + # cert and key file means the user wants to authenticate. + # enable TLS 1.3 PHA implicitly even for custom contexts. + if context.post_handshake_auth is not None: + context.post_handshake_auth = True self._context = context if check_hostname is not None: self._context.check_hostname = check_hostname diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 968cbd86a1e4..9148169cc7c2 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1745,6 +1745,24 @@ def test_host_port(self): self.assertEqual(h, c.host) self.assertEqual(p, c.port) + def test_tls13_pha(self): + import ssl + if not ssl.HAS_TLSv1_3: + self.skipTest('TLS 1.3 support required') + # just check status of PHA flag + h = client.HTTPSConnection('localhost', 443) + self.assertTrue(h._context.post_handshake_auth) + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + self.assertFalse(context.post_handshake_auth) + h = client.HTTPSConnection('localhost', 443, context=context) + self.assertIs(h._context, context) + self.assertFalse(h._context.post_handshake_auth) + + h = client.HTTPSConnection('localhost', 443, context=context, + cert_file=CERT_localhost) + self.assertTrue(h._context.post_handshake_auth) + class RequestBodyTest(TestCase): """Test cases where a request includes a message body.""" diff --git a/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst b/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst new file mode 100644 index 000000000000..b336e061e15e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst @@ -0,0 +1,2 @@ +http.client now enables TLS 1.3 post-handshake authentication for default +context or if a cert_file is passed to HTTPSConnection. From webhook-mailer at python.org Mon Jul 1 03:07:56 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 07:07:56 -0000 Subject: [Python-checkins] [3.7] bpo-37440: Enable TLS 1.3 post-handshake auth in http.client (GH-14448) (GH-14496) Message-ID: https://github.com/python/cpython/commit/6be91102f75aa4b4b8c1e55960aa22008ff9e319 commit: 6be91102f75aa4b4b8c1e55960aa22008ff9e319 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-01T00:07:52-07:00 summary: [3.7] bpo-37440: Enable TLS 1.3 post-handshake auth in http.client (GH-14448) (GH-14496) Post-handshake authentication is required for conditional client cert authentication with TLS 1.3. https://bugs.python.org/issue37440 (cherry picked from commit d1bd6e79da1ee56dc1b902d804216ffd267399db) Co-authored-by: Christian Heimes https://bugs.python.org/issue37440 files: A Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst M Doc/library/http.client.rst M Lib/http/client.py M Lib/test/test_httplib.py diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index 3408c103e2f3..3ebeb10aee9a 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -94,6 +94,11 @@ The module provides the following classes: :func:`ssl._create_unverified_context` can be passed to the *context* parameter. + .. versionchanged:: 3.7.4 + This class now enables TLS 1.3 + :attr:`ssl.SSLContext.post_handshake_auth` for the default *context* or + when *cert_file* is passed with a custom *context*. + .. deprecated:: 3.6 *key_file* and *cert_file* are deprecated in favor of *context*. diff --git a/Lib/http/client.py b/Lib/http/client.py index 2afd452fe30f..dd23edcd597c 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1381,6 +1381,9 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, self.cert_file = cert_file if context is None: context = ssl._create_default_https_context() + # enable PHA for TLS 1.3 connections if available + if context.post_handshake_auth is not None: + context.post_handshake_auth = True will_verify = context.verify_mode != ssl.CERT_NONE if check_hostname is None: check_hostname = context.check_hostname @@ -1389,6 +1392,10 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, "either CERT_OPTIONAL or CERT_REQUIRED") if key_file or cert_file: context.load_cert_chain(cert_file, key_file) + # cert and key file means the user wants to authenticate. + # enable TLS 1.3 PHA implicitly even for custom contexts. + if context.post_handshake_auth is not None: + context.post_handshake_auth = True self._context = context if check_hostname is not None: self._context.check_hostname = check_hostname diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 49263a8a3a0d..c4246671586f 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1748,6 +1748,24 @@ def test_host_port(self): self.assertEqual(h, c.host) self.assertEqual(p, c.port) + def test_tls13_pha(self): + import ssl + if not ssl.HAS_TLSv1_3: + self.skipTest('TLS 1.3 support required') + # just check status of PHA flag + h = client.HTTPSConnection('localhost', 443) + self.assertTrue(h._context.post_handshake_auth) + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + self.assertFalse(context.post_handshake_auth) + h = client.HTTPSConnection('localhost', 443, context=context) + self.assertIs(h._context, context) + self.assertFalse(h._context.post_handshake_auth) + + h = client.HTTPSConnection('localhost', 443, context=context, + cert_file=CERT_localhost) + self.assertTrue(h._context.post_handshake_auth) + class RequestBodyTest(TestCase): """Test cases where a request includes a message body.""" diff --git a/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst b/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst new file mode 100644 index 000000000000..b336e061e15e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst @@ -0,0 +1,2 @@ +http.client now enables TLS 1.3 post-handshake authentication for default +context or if a cert_file is passed to HTTPSConnection. From webhook-mailer at python.org Mon Jul 1 03:25:54 2019 From: webhook-mailer at python.org (Christian Heimes) Date: Mon, 01 Jul 2019 07:25:54 -0000 Subject: [Python-checkins] [3.8] bpo-37428: Don't set PHA verify flag on client side (GH-14494) Message-ID: https://github.com/python/cpython/commit/f22c4cf11d10f52faa86e0b308dd28f11819efd8 commit: f22c4cf11d10f52faa86e0b308dd28f11819efd8 branch: 3.8 author: Christian Heimes committer: GitHub date: 2019-07-01T09:25:48+02:00 summary: [3.8] bpo-37428: Don't set PHA verify flag on client side (GH-14494) SSLContext.post_handshake_auth = True no longer sets SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the option is documented as ignored for clients, OpenSSL implicitly enables cert chain validation when the flag is set. Signed-off-by: Christian Heimes https://bugs.python.org/issue37428 (cherry picked from commit f0f5930ac88482ef896283db5be9b8d508d077db) files: A Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst M Lib/test/test_ssl.py M Modules/_ssl.c diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 38fdf3f375cc..66369fe60dfe 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4428,6 +4428,37 @@ def test_pha_not_tls13(self): s.write(b'PHA') self.assertIn(b'WRONG_SSL_VERSION', s.recv(1024)) + def test_bpo37428_pha_cert_none(self): + # verify that post_handshake_auth does not implicitly enable cert + # validation. + hostname = SIGNED_CERTFILE_HOSTNAME + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.post_handshake_auth = True + client_context.load_cert_chain(SIGNED_CERTFILE) + # no cert validation and CA on client side + client_context.check_hostname = False + client_context.verify_mode = ssl.CERT_NONE + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.load_cert_chain(SIGNED_CERTFILE) + server_context.load_verify_locations(SIGNING_CA) + server_context.post_handshake_auth = True + server_context.verify_mode = ssl.CERT_REQUIRED + + server = ThreadedEchoServer(context=server_context, chatty=False) + with server: + with client_context.wrap_socket(socket.socket(), + server_hostname=hostname) as s: + s.connect((HOST, server.port)) + s.write(b'HASCERT') + self.assertEqual(s.recv(1024), b'FALSE\n') + s.write(b'PHA') + self.assertEqual(s.recv(1024), b'OK\n') + s.write(b'HASCERT') + self.assertEqual(s.recv(1024), b'TRUE\n') + # server cert has not been validated + self.assertEqual(s.getpeercert(), {}) + HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename') requires_keylog = unittest.skipUnless( diff --git a/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst b/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst new file mode 100644 index 000000000000..2cdce6b24dc6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst @@ -0,0 +1,4 @@ +SSLContext.post_handshake_auth = True no longer sets +SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the +option is documented as ignored for clients, OpenSSL implicitly enables cert +chain validation when the flag is set. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 2331c58ad77d..3351af6cdefd 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -963,6 +963,26 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, SSL_set_mode(self->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY); +#ifdef TLS1_3_VERSION + if (sslctx->post_handshake_auth == 1) { + if (socket_type == PY_SSL_SERVER) { + /* bpo-37428: OpenSSL does not ignore SSL_VERIFY_POST_HANDSHAKE. + * Set SSL_VERIFY_POST_HANDSHAKE flag only for server sockets and + * only in combination with SSL_VERIFY_PEER flag. */ + int mode = SSL_get_verify_mode(self->ssl); + if (mode & SSL_VERIFY_PEER) { + int (*verify_cb)(int, X509_STORE_CTX *) = NULL; + verify_cb = SSL_get_verify_callback(self->ssl); + mode |= SSL_VERIFY_POST_HANDSHAKE; + SSL_set_verify(self->ssl, mode, verify_cb); + } + } else { + /* client socket */ + SSL_set_post_handshake_auth(self->ssl, 1); + } + } +#endif + if (server_hostname != NULL) { if (_ssl_configure_hostname(self, server_hostname) < 0) { Py_DECREF(self); @@ -2986,10 +3006,10 @@ _set_verify_mode(PySSLContext *self, enum py_ssl_cert_requirements n) "invalid value for verify_mode"); return -1; } -#ifdef TLS1_3_VERSION - if (self->post_handshake_auth) - mode |= SSL_VERIFY_POST_HANDSHAKE; -#endif + + /* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for + * server sockets and SSL_set_post_handshake_auth() for client. */ + /* keep current verify cb */ verify_cb = SSL_CTX_get_verify_callback(self->ctx); SSL_CTX_set_verify(self->ctx, mode, verify_cb); @@ -3735,8 +3755,6 @@ get_post_handshake_auth(PySSLContext *self, void *c) { #if TLS1_3_VERSION static int set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) { - int (*verify_cb)(int, X509_STORE_CTX *) = NULL; - int mode = SSL_CTX_get_verify_mode(self->ctx); if (arg == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; @@ -3748,17 +3766,8 @@ set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) { } self->post_handshake_auth = pha; - /* client-side socket setting, ignored by server-side */ - SSL_CTX_set_post_handshake_auth(self->ctx, pha); - - /* server-side socket setting, ignored by client-side */ - verify_cb = SSL_CTX_get_verify_callback(self->ctx); - if (pha) { - mode |= SSL_VERIFY_POST_HANDSHAKE; - } else { - mode ^= SSL_VERIFY_POST_HANDSHAKE; - } - SSL_CTX_set_verify(self->ctx, mode, verify_cb); + /* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for + * server sockets and SSL_set_post_handshake_auth() for client. */ return 0; } From webhook-mailer at python.org Mon Jul 1 05:53:48 2019 From: webhook-mailer at python.org (Ned Deily) Date: Mon, 01 Jul 2019 09:53:48 -0000 Subject: [Python-checkins] Minor updates to the macOS installer screens for 3.8.0b2 (GH-14501) Message-ID: https://github.com/python/cpython/commit/fc1fbe6099e826e8304eadf781af7c10d739fc40 commit: fc1fbe6099e826e8304eadf781af7c10d739fc40 branch: master author: Ned Deily committer: GitHub date: 2019-07-01T05:53:42-04:00 summary: Minor updates to the macOS installer screens for 3.8.0b2 (GH-14501) files: M Mac/BuildScript/resources/ReadMe.rtf M Mac/BuildScript/resources/Welcome.rtf diff --git a/Mac/BuildScript/resources/ReadMe.rtf b/Mac/BuildScript/resources/ReadMe.rtf index ab7aeff5376c..ec83750c6e69 100644 --- a/Mac/BuildScript/resources/ReadMe.rtf +++ b/Mac/BuildScript/resources/ReadMe.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf200 +{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf500 {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;\f2\fswiss\fcharset0 Helvetica-Oblique; \f3\fmodern\fcharset0 CourierNewPSMT;} {\colortbl;\red255\green255\blue255;} @@ -8,12 +8,18 @@ \f0\fs24 \cf0 This package will install Python $FULL_VERSION for macOS $MACOSX_DEPLOYMENT_TARGET for the following architecture(s): $ARCHITECTURES.\ \ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\partightenfactor0 + +\f1\b \cf0 NOTE: +\f0\b0 This is a beta test preview of Python 3.8.0, the next feature release of Python 3.8. It is not intended for production use.\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 +\cf0 \ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 \f1\b \cf0 \ul \ulc0 Certificate verification and OpenSSL\ \f0\b0 \ulnone \ -This package includes its own private copy of OpenSSL 1.1.0. The trust certificates in system and user keychains managed by the +This package includes its own private copy of OpenSSL 1.1.1. The trust certificates in system and user keychains managed by the \f2\i Keychain Access \f0\i0 application and the \f2\i security diff --git a/Mac/BuildScript/resources/Welcome.rtf b/Mac/BuildScript/resources/Welcome.rtf index df5d20da9ebb..053084d0425b 100644 --- a/Mac/BuildScript/resources/Welcome.rtf +++ b/Mac/BuildScript/resources/Welcome.rtf @@ -1,5 +1,6 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf200 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;} +{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf500 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;\f2\fmodern\fcharset0 CourierNewPSMT; +} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} \paperw11905\paperh16837\margl1440\margr1440\vieww12200\viewh10880\viewkind0 @@ -17,7 +18,11 @@ \f1\b IDLE \f0\b0 .\ \ +At the end of this install, click on +\f2 Install Certificates +\f0 for SSL root certificates\ +\ \f1\b NOTE: -\f0\b0 This is an alpha test preview of Python 3.8.0, the next feature release of Python 3.8. It is not intended for production use.\ +\f0\b0 This is a beta test preview of Python 3.8.0, the next feature release of Python 3.8. It is not intended for production use.\ } \ No newline at end of file From webhook-mailer at python.org Mon Jul 1 06:35:11 2019 From: webhook-mailer at python.org (Petr Viktorin) Date: Mon, 01 Jul 2019 10:35:11 -0000 Subject: [Python-checkins] bpo-37221: Add PyCode_NewWithPosOnlyArgs to be used internally and set PyCode_New as a compatibility wrapper (GH-13959) Message-ID: https://github.com/python/cpython/commit/4a2edc34a405150d0b23ecfdcb401e7cf59f4650 commit: 4a2edc34a405150d0b23ecfdcb401e7cf59f4650 branch: master author: Pablo Galindo committer: Petr Viktorin date: 2019-07-01T12:35:05+02:00 summary: bpo-37221: Add PyCode_NewWithPosOnlyArgs to be used internally and set PyCode_New as a compatibility wrapper (GH-13959) Add PyCode_NewEx to be used internally and set PyCode_New as a compatibility wrapper files: A Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst M Doc/c-api/code.rst M Doc/data/refcounts.dat M Doc/whatsnew/3.8.rst M Include/code.h M Objects/codeobject.c M Python/compile.c M Python/marshal.c diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 7353df56e7d3..3c4f66923da5 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -33,20 +33,21 @@ bound into a function. Return the number of free variables in *co*. -.. c:function:: PyCodeObject* PyCode_New(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) +.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) - Return a new code object. If you need a dummy code object to - create a frame, use :c:func:`PyCode_NewEmpty` instead. Calling - :c:func:`PyCode_New` directly can bind you to a precise Python - version since the definition of the bytecode changes often. - - .. versionchanged:: 3.8 - An extra parameter is required (*posonlyargcount*) to support :PEP:`570`. - The first parameter (*argcount*) now represents the total number of positional arguments, - including positional-only. + Return a new code object. If you need a dummy code object to create a frame, + use :c:func:`PyCode_NewEmpty` instead. Calling :c:func:`PyCode_New` directly + can bind you to a precise Python version since the definition of the bytecode + changes often. .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags c.PyCode_New +.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) + + Similar to :c:func:`PyCode_New`, but with an extra "posonlyargcount" for positonal-only arguments. + + .. versionadded:: 3.8 + .. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) Return a new empty code object with the specified filename, diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index aca57a1dae9d..fda347eab102 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -234,9 +234,26 @@ PyCode_Check:PyObject*:co:0: PyCode_GetNumFree:int::: PyCode_GetNumFree:PyCodeObject*:co:0: +PyCode_NewWithPosOnlyArgs:PyCodeObject*::+1: +PyCode_NewWithPosOnlyArgs:int:argcount:: +PyCode_NewWithPosOnlyArgs:int:posonlyargcount:: +PyCode_NewWithPosOnlyArgs:int:kwonlyargcount:: +PyCode_NewWithPosOnlyArgs:int:nlocals:: +PyCode_NewWithPosOnlyArgs:int:stacksize:: +PyCode_NewWithPosOnlyArgs:int:flags:: +PyCode_NewWithPosOnlyArgs:PyObject*:code:0: +PyCode_NewWithPosOnlyArgs:PyObject*:consts:0: +PyCode_NewWithPosOnlyArgs:PyObject*:names:0: +PyCode_NewWithPosOnlyArgs:PyObject*:varnames:0: +PyCode_NewWithPosOnlyArgs:PyObject*:freevars:0: +PyCode_NewWithPosOnlyArgs:PyObject*:cellvars:0: +PyCode_NewWithPosOnlyArgs:PyObject*:filename:0: +PyCode_NewWithPosOnlyArgs:PyObject*:name:0: +PyCode_NewWithPosOnlyArgs:int:firstlineno:: +PyCode_NewWithPosOnlyArgs:PyObject*:lnotab:0: + PyCode_New:PyCodeObject*::+1: PyCode_New:int:argcount:: -PyCode_New:int:posonlyargcount:: PyCode_New:int:kwonlyargcount:: PyCode_New:int:nlocals:: PyCode_New:int:stacksize:: diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 46e531f58995..5aab191f1a48 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -1045,6 +1045,11 @@ Build and C API Changes allocation or deallocation may need to be adjusted. (Contributed by Eddie Elizondo in :issue:`35810`.) +* The new function :c:func:`PyCode_NewWithPosOnlyArgs` allows to create + code objects like :c:func:`PyCode_New`, but with an extra *posonlyargcount* + parameter for indicating the number of positional-only arguments. + (Contributed by Pablo Galindo in :issue:`37221`.) + Deprecated ========== diff --git a/Include/code.h b/Include/code.h index b79d977394e0..3afddd20c80d 100644 --- a/Include/code.h +++ b/Include/code.h @@ -120,6 +120,11 @@ PyAPI_DATA(PyTypeObject) PyCode_Type; /* Public interface */ PyAPI_FUNC(PyCodeObject *) PyCode_New( + int, int, int, int, int, PyObject *, PyObject *, + PyObject *, PyObject *, PyObject *, PyObject *, + PyObject *, PyObject *, int, PyObject *); + +PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs( int, int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *); diff --git a/Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst b/Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst new file mode 100644 index 000000000000..0ea8b9b86788 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst @@ -0,0 +1,3 @@ +The new function :c:func:`PyCode_NewWithPosOnlyArgs` allows to create +code objects like :c:func:`PyCode_New`, but with an extra *posonlyargcount* +parameter for indicating the number of positonal-only arguments. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 1333cc833e1e..39bf6fc6f228 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -102,14 +102,13 @@ intern_string_constants(PyObject *tuple) return modified; } - PyCodeObject * -PyCode_New(int argcount, int posonlyargcount, int kwonlyargcount, - int nlocals, int stacksize, int flags, - PyObject *code, PyObject *consts, PyObject *names, - PyObject *varnames, PyObject *freevars, PyObject *cellvars, - PyObject *filename, PyObject *name, int firstlineno, - PyObject *lnotab) +PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, + int nlocals, int stacksize, int flags, + PyObject *code, PyObject *consts, PyObject *names, + PyObject *varnames, PyObject *freevars, PyObject *cellvars, + PyObject *filename, PyObject *name, int firstlineno, + PyObject *lnotab) { PyCodeObject *co; Py_ssize_t *cell2arg = NULL; @@ -243,6 +242,20 @@ PyCode_New(int argcount, int posonlyargcount, int kwonlyargcount, return co; } +PyCodeObject * +PyCode_New(int argcount, int kwonlyargcount, + int nlocals, int stacksize, int flags, + PyObject *code, PyObject *consts, PyObject *names, + PyObject *varnames, PyObject *freevars, PyObject *cellvars, + PyObject *filename, PyObject *name, int firstlineno, + PyObject *lnotab) +{ + return PyCode_NewWithPosOnlyArgs(argcount, 0, kwonlyargcount, nlocals, + stacksize, flags, code, consts, names, + varnames, freevars, cellvars, filename, + name, firstlineno, lnotab); +} + int _PyCode_InitOpcache(PyCodeObject *co) { @@ -311,7 +324,8 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) if (filename_ob == NULL) goto failed; - result = PyCode_New(0, /* argcount */ + result = PyCode_NewWithPosOnlyArgs( + 0, /* argcount */ 0, /* posonlyargcount */ 0, /* kwonlyargcount */ 0, /* nlocals */ @@ -492,12 +506,14 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (ourcellvars == NULL) goto cleanup; - co = (PyObject *)PyCode_New(argcount, posonlyargcount, kwonlyargcount, - nlocals, stacksize, flags, - code, consts, ournames, ourvarnames, - ourfreevars, ourcellvars, filename, - name, firstlineno, lnotab); - cleanup: + co = (PyObject *)PyCode_NewWithPosOnlyArgs(argcount, posonlyargcount, + kwonlyargcount, + nlocals, stacksize, flags, + code, consts, ournames, + ourvarnames, ourfreevars, + ourcellvars, filename, + name, firstlineno, lnotab); + cleanup: Py_XDECREF(ournames); Py_XDECREF(ourvarnames); Py_XDECREF(ourfreevars); @@ -625,7 +641,7 @@ code_replace_impl(PyCodeObject *self, int co_argcount, #undef CHECK_INT_ARG - return (PyObject *)PyCode_New( + return (PyObject *)PyCode_NewWithPosOnlyArgs( co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, diff --git a/Python/compile.c b/Python/compile.c index 7bdf406079d3..9cce8aeb4e1f 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -5813,13 +5813,11 @@ makecode(struct compiler *c, struct assembler *a) if (maxdepth < 0) { goto error; } - co = PyCode_New(posonlyargcount+posorkeywordargcount, posonlyargcount, - kwonlyargcount, nlocals_int, maxdepth, flags, - bytecode, consts, names, varnames, - freevars, cellvars, - c->c_filename, c->u->u_name, - c->u->u_firstlineno, - a->a_lnotab); + co = PyCode_NewWithPosOnlyArgs(posonlyargcount+posorkeywordargcount, + posonlyargcount, kwonlyargcount, nlocals_int, + maxdepth, flags, bytecode, consts, names, + varnames, freevars, cellvars, c->c_filename, + c->u->u_name, c->u->u_firstlineno, a->a_lnotab); error: Py_XDECREF(consts); Py_XDECREF(names); diff --git a/Python/marshal.c b/Python/marshal.c index caaddfe9e44e..b2daff2c8a3b 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -1396,7 +1396,7 @@ r_object(RFILE *p) if (lnotab == NULL) goto code_error; - v = (PyObject *) PyCode_New( + v = (PyObject *) PyCode_NewWithPosOnlyArgs( argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, From webhook-mailer at python.org Mon Jul 1 07:41:25 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 01 Jul 2019 11:41:25 -0000 Subject: [Python-checkins] bpo-32934: Clarified meaning of 'capacity' for BufferingHandler and MemoryHandler. (GH-14498) Message-ID: https://github.com/python/cpython/commit/84de34e39eb9e49b2ae691c6f67df8d7da3561de commit: 84de34e39eb9e49b2ae691c6f67df8d7da3561de branch: master author: Vinay Sajip committer: GitHub date: 2019-07-01T12:41:21+01:00 summary: bpo-32934: Clarified meaning of 'capacity' for BufferingHandler and MemoryHandler. (GH-14498) files: M Doc/howto/logging-cookbook.rst M Doc/library/logging.handlers.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 71f9fc920fdf..87ac79ef8072 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2266,9 +2266,9 @@ The script just arranges to decorate ``foo`` with a decorator which will do the conditional logging that's required. The decorator takes a logger as a parameter and attaches a memory handler for the duration of the call to the decorated function. The decorator can be additionally parameterised using a target handler, -a level at which flushing should occur, and a capacity for the buffer. These -default to a :class:`~logging.StreamHandler` which writes to ``sys.stderr``, -``logging.ERROR`` and ``100`` respectively. +a level at which flushing should occur, and a capacity for the buffer (number of +records buffered). These default to a :class:`~logging.StreamHandler` which +writes to ``sys.stderr``, ``logging.ERROR`` and ``100`` respectively. Here's the script:: diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index 703d66d7ff6a..df5bfefaa275 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -861,7 +861,8 @@ should, then :meth:`flush` is expected to do the flushing. .. class:: BufferingHandler(capacity) - Initializes the handler with a buffer of the specified capacity. + Initializes the handler with a buffer of the specified capacity. Here, + *capacity* means the number of logging records buffered. .. method:: emit(record) @@ -885,12 +886,13 @@ should, then :meth:`flush` is expected to do the flushing. .. class:: MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True) Returns a new instance of the :class:`MemoryHandler` class. The instance is - initialized with a buffer size of *capacity*. If *flushLevel* is not specified, - :const:`ERROR` is used. If no *target* is specified, the target will need to be - set using :meth:`setTarget` before this handler does anything useful. If - *flushOnClose* is specified as ``False``, then the buffer is *not* flushed when - the handler is closed. If not specified or specified as ``True``, the previous - behaviour of flushing the buffer will occur when the handler is closed. + initialized with a buffer size of *capacity* (number of records buffered). + If *flushLevel* is not specified, :const:`ERROR` is used. If no *target* is + specified, the target will need to be set using :meth:`setTarget` before this + handler does anything useful. If *flushOnClose* is specified as ``False``, + then the buffer is *not* flushed when the handler is closed. If not specified + or specified as ``True``, the previous behaviour of flushing the buffer will + occur when the handler is closed. .. versionchanged:: 3.6 The *flushOnClose* parameter was added. From webhook-mailer at python.org Mon Jul 1 08:12:03 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 01 Jul 2019 12:12:03 -0000 Subject: [Python-checkins] bpo-32934: Clarified meaning of 'capacity' for BufferingHandler and MemoryHandler. (GH-14498) (GH-14508) Message-ID: https://github.com/python/cpython/commit/471d785dc759eb2e9c06f077f323cf136d32506b commit: 471d785dc759eb2e9c06f077f323cf136d32506b branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-01T13:11:37+01:00 summary: bpo-32934: Clarified meaning of 'capacity' for BufferingHandler and MemoryHandler. (GH-14498) (GH-14508) (cherry picked from commit 84de34e39eb9e49b2ae691c6f67df8d7da3561de) files: M Doc/howto/logging-cookbook.rst M Doc/library/logging.handlers.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index e391506ce2e4..105ae5ed25a0 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2267,9 +2267,9 @@ The script just arranges to decorate ``foo`` with a decorator which will do the conditional logging that's required. The decorator takes a logger as a parameter and attaches a memory handler for the duration of the call to the decorated function. The decorator can be additionally parameterised using a target handler, -a level at which flushing should occur, and a capacity for the buffer. These -default to a :class:`~logging.StreamHandler` which writes to ``sys.stderr``, -``logging.ERROR`` and ``100`` respectively. +a level at which flushing should occur, and a capacity for the buffer (number of +records buffered). These default to a :class:`~logging.StreamHandler` which +writes to ``sys.stderr``, ``logging.ERROR`` and ``100`` respectively. Here's the script:: diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index b48d50f9e9e5..b2ebafa83b2e 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -840,7 +840,8 @@ should, then :meth:`flush` is expected to do the flushing. .. class:: BufferingHandler(capacity) - Initializes the handler with a buffer of the specified capacity. + Initializes the handler with a buffer of the specified capacity. Here, + *capacity* means the number of logging records buffered. .. method:: emit(record) @@ -864,12 +865,13 @@ should, then :meth:`flush` is expected to do the flushing. .. class:: MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True) Returns a new instance of the :class:`MemoryHandler` class. The instance is - initialized with a buffer size of *capacity*. If *flushLevel* is not specified, - :const:`ERROR` is used. If no *target* is specified, the target will need to be - set using :meth:`setTarget` before this handler does anything useful. If - *flushOnClose* is specified as ``False``, then the buffer is *not* flushed when - the handler is closed. If not specified or specified as ``True``, the previous - behaviour of flushing the buffer will occur when the handler is closed. + initialized with a buffer size of *capacity* (number of records buffered). + If *flushLevel* is not specified, :const:`ERROR` is used. If no *target* is + specified, the target will need to be set using :meth:`setTarget` before this + handler does anything useful. If *flushOnClose* is specified as ``False``, + then the buffer is *not* flushed when the handler is closed. If not specified + or specified as ``True``, the previous behaviour of flushing the buffer will + occur when the handler is closed. .. versionchanged:: 3.6 The *flushOnClose* parameter was added. From webhook-mailer at python.org Mon Jul 1 08:12:14 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 01 Jul 2019 12:12:14 -0000 Subject: [Python-checkins] bpo-32934: Clarified meaning of 'capacity' for BufferingHandler and MemoryHandler. (GH-14498) (GH-14507) Message-ID: https://github.com/python/cpython/commit/3db5c5c7630af92336a8c0a269e05607cd68f9e7 commit: 3db5c5c7630af92336a8c0a269e05607cd68f9e7 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-01T13:12:08+01:00 summary: bpo-32934: Clarified meaning of 'capacity' for BufferingHandler and MemoryHandler. (GH-14498) (GH-14507) (cherry picked from commit 84de34e39eb9e49b2ae691c6f67df8d7da3561de) files: M Doc/howto/logging-cookbook.rst M Doc/library/logging.handlers.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 71f9fc920fdf..87ac79ef8072 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2266,9 +2266,9 @@ The script just arranges to decorate ``foo`` with a decorator which will do the conditional logging that's required. The decorator takes a logger as a parameter and attaches a memory handler for the duration of the call to the decorated function. The decorator can be additionally parameterised using a target handler, -a level at which flushing should occur, and a capacity for the buffer. These -default to a :class:`~logging.StreamHandler` which writes to ``sys.stderr``, -``logging.ERROR`` and ``100`` respectively. +a level at which flushing should occur, and a capacity for the buffer (number of +records buffered). These default to a :class:`~logging.StreamHandler` which +writes to ``sys.stderr``, ``logging.ERROR`` and ``100`` respectively. Here's the script:: diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index b48d50f9e9e5..b2ebafa83b2e 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -840,7 +840,8 @@ should, then :meth:`flush` is expected to do the flushing. .. class:: BufferingHandler(capacity) - Initializes the handler with a buffer of the specified capacity. + Initializes the handler with a buffer of the specified capacity. Here, + *capacity* means the number of logging records buffered. .. method:: emit(record) @@ -864,12 +865,13 @@ should, then :meth:`flush` is expected to do the flushing. .. class:: MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True) Returns a new instance of the :class:`MemoryHandler` class. The instance is - initialized with a buffer size of *capacity*. If *flushLevel* is not specified, - :const:`ERROR` is used. If no *target* is specified, the target will need to be - set using :meth:`setTarget` before this handler does anything useful. If - *flushOnClose* is specified as ``False``, then the buffer is *not* flushed when - the handler is closed. If not specified or specified as ``True``, the previous - behaviour of flushing the buffer will occur when the handler is closed. + initialized with a buffer size of *capacity* (number of records buffered). + If *flushLevel* is not specified, :const:`ERROR` is used. If no *target* is + specified, the target will need to be set using :meth:`setTarget` before this + handler does anything useful. If *flushOnClose* is specified as ``False``, + then the buffer is *not* flushed when the handler is closed. If not specified + or specified as ``True``, the previous behaviour of flushing the buffer will + occur when the handler is closed. .. versionchanged:: 3.6 The *flushOnClose* parameter was added. From webhook-mailer at python.org Mon Jul 1 08:12:44 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 01 Jul 2019 12:12:44 -0000 Subject: [Python-checkins] bpo-10945: Drop support for bdist_wininst on non-Windows systems (GH-14506) Message-ID: https://github.com/python/cpython/commit/72cd653c4ed7a4f8f8fb06ac364b08a97085a2b5 commit: 72cd653c4ed7a4f8f8fb06ac364b08a97085a2b5 branch: master author: Miro Hron?ok committer: Victor Stinner date: 2019-07-01T14:12:40+02:00 summary: bpo-10945: Drop support for bdist_wininst on non-Windows systems (GH-14506) bdist_wininst depends on MBCS codec, unavailable on non-Windows, and bdist_wininst have not worked since at least Python 3.2, possibly never on Python 3. Here we document that bdist_wininst is only supported on Windows, and we mark it unsupported otherwise to skip tests. Distributors of Python 3 can now safely drop the bdist_wininst .exe files without the need to skip bdist_wininst related tests. files: A Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst M Doc/distutils/builtdist.rst M Lib/distutils/command/bdist_wininst.py diff --git a/Doc/distutils/builtdist.rst b/Doc/distutils/builtdist.rst index f44d0d039f45..8c65d9d59118 100644 --- a/Doc/distutils/builtdist.rst +++ b/Doc/distutils/builtdist.rst @@ -315,8 +315,8 @@ or the :command:`bdist` command with the :option:`!--formats` option:: If you have a pure module distribution (only containing pure Python modules and packages), the resulting installer will be version independent and have a name -like :file:`foo-1.0.win32.exe`. These installers can even be created on Unix -platforms or Mac OS X. +like :file:`foo-1.0.win32.exe`. Note that creating ``wininst`` binary +distributions in only supported on Windows systems. If you have a non-pure distribution, the extensions can only be created on a Windows platform, and will be Python version dependent. The installer filename diff --git a/Lib/distutils/command/bdist_wininst.py b/Lib/distutils/command/bdist_wininst.py index 3a616883bee5..acaa184b5f71 100644 --- a/Lib/distutils/command/bdist_wininst.py +++ b/Lib/distutils/command/bdist_wininst.py @@ -55,6 +55,9 @@ class bdist_wininst(Command): boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', 'skip-build'] + # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows + _unsupported = (sys.platform != "win32") + def initialize_options(self): self.bdist_dir = None self.plat_name = None diff --git a/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst b/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst new file mode 100644 index 000000000000..d39576545efa --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst @@ -0,0 +1,2 @@ +Officially drop support for creating bdist_wininst installers on non-Windows +systems. From webhook-mailer at python.org Mon Jul 1 08:42:16 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 12:42:16 -0000 Subject: [Python-checkins] bpo-10945: Drop support for bdist_wininst on non-Windows systems (GH-14506) Message-ID: https://github.com/python/cpython/commit/45c10da40912e04c0d0de02af4b23438ed0de49b commit: 45c10da40912e04c0d0de02af4b23438ed0de49b branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-01T05:42:08-07:00 summary: bpo-10945: Drop support for bdist_wininst on non-Windows systems (GH-14506) bdist_wininst depends on MBCS codec, unavailable on non-Windows, and bdist_wininst have not worked since at least Python 3.2, possibly never on Python 3. Here we document that bdist_wininst is only supported on Windows, and we mark it unsupported otherwise to skip tests. Distributors of Python 3 can now safely drop the bdist_wininst .exe files without the need to skip bdist_wininst related tests. (cherry picked from commit 72cd653c4ed7a4f8f8fb06ac364b08a97085a2b5) Co-authored-by: Miro Hron?ok files: A Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst M Doc/distutils/builtdist.rst M Lib/distutils/command/bdist_wininst.py diff --git a/Doc/distutils/builtdist.rst b/Doc/distutils/builtdist.rst index f1f347126160..0a83f8bd6599 100644 --- a/Doc/distutils/builtdist.rst +++ b/Doc/distutils/builtdist.rst @@ -313,8 +313,8 @@ or the :command:`bdist` command with the :option:`!--formats` option:: If you have a pure module distribution (only containing pure Python modules and packages), the resulting installer will be version independent and have a name -like :file:`foo-1.0.win32.exe`. These installers can even be created on Unix -platforms or Mac OS X. +like :file:`foo-1.0.win32.exe`. Note that creating ``wininst`` binary +distributions in only supported on Windows systems. If you have a non-pure distribution, the extensions can only be created on a Windows platform, and will be Python version dependent. The installer filename diff --git a/Lib/distutils/command/bdist_wininst.py b/Lib/distutils/command/bdist_wininst.py index fde56754e891..15434c3a9898 100644 --- a/Lib/distutils/command/bdist_wininst.py +++ b/Lib/distutils/command/bdist_wininst.py @@ -55,6 +55,9 @@ class bdist_wininst(Command): boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', 'skip-build'] + # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows + _unsupported = (sys.platform != "win32") + def initialize_options(self): self.bdist_dir = None self.plat_name = None diff --git a/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst b/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst new file mode 100644 index 000000000000..d39576545efa --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst @@ -0,0 +1,2 @@ +Officially drop support for creating bdist_wininst installers on non-Windows +systems. From webhook-mailer at python.org Mon Jul 1 08:54:26 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 12:54:26 -0000 Subject: [Python-checkins] bpo-10945: Drop support for bdist_wininst on non-Windows systems (GH-14506) Message-ID: https://github.com/python/cpython/commit/be5bb52f5f2d4da4b9d6f42399f7275ab47910f3 commit: be5bb52f5f2d4da4b9d6f42399f7275ab47910f3 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-01T05:54:19-07:00 summary: bpo-10945: Drop support for bdist_wininst on non-Windows systems (GH-14506) bdist_wininst depends on MBCS codec, unavailable on non-Windows, and bdist_wininst have not worked since at least Python 3.2, possibly never on Python 3. Here we document that bdist_wininst is only supported on Windows, and we mark it unsupported otherwise to skip tests. Distributors of Python 3 can now safely drop the bdist_wininst .exe files without the need to skip bdist_wininst related tests. (cherry picked from commit 72cd653c4ed7a4f8f8fb06ac364b08a97085a2b5) Co-authored-by: Miro Hron?ok files: A Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst M Doc/distutils/builtdist.rst M Lib/distutils/command/bdist_wininst.py diff --git a/Doc/distutils/builtdist.rst b/Doc/distutils/builtdist.rst index f44d0d039f45..8c65d9d59118 100644 --- a/Doc/distutils/builtdist.rst +++ b/Doc/distutils/builtdist.rst @@ -315,8 +315,8 @@ or the :command:`bdist` command with the :option:`!--formats` option:: If you have a pure module distribution (only containing pure Python modules and packages), the resulting installer will be version independent and have a name -like :file:`foo-1.0.win32.exe`. These installers can even be created on Unix -platforms or Mac OS X. +like :file:`foo-1.0.win32.exe`. Note that creating ``wininst`` binary +distributions in only supported on Windows systems. If you have a non-pure distribution, the extensions can only be created on a Windows platform, and will be Python version dependent. The installer filename diff --git a/Lib/distutils/command/bdist_wininst.py b/Lib/distutils/command/bdist_wininst.py index 3a616883bee5..acaa184b5f71 100644 --- a/Lib/distutils/command/bdist_wininst.py +++ b/Lib/distutils/command/bdist_wininst.py @@ -55,6 +55,9 @@ class bdist_wininst(Command): boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', 'skip-build'] + # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows + _unsupported = (sys.platform != "win32") + def initialize_options(self): self.bdist_dir = None self.plat_name = None diff --git a/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst b/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst new file mode 100644 index 000000000000..d39576545efa --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst @@ -0,0 +1,2 @@ +Officially drop support for creating bdist_wininst installers on non-Windows +systems. From webhook-mailer at python.org Mon Jul 1 09:52:01 2019 From: webhook-mailer at python.org (Antoine Pitrou) Date: Mon, 01 Jul 2019 13:52:01 -0000 Subject: [Python-checkins] bpo-37209: Add pickle entry for 3.8 whatsnew (GH-14503) Message-ID: https://github.com/python/cpython/commit/ec6c1bd0491590f3c0e2908a7b2dfb91b6acdae9 commit: ec6c1bd0491590f3c0e2908a7b2dfb91b6acdae9 branch: master author: Pierre Glaser committer: Antoine Pitrou date: 2019-07-01T15:51:57+02:00 summary: bpo-37209: Add pickle entry for 3.8 whatsnew (GH-14503) files: M Doc/whatsnew/3.8.rst diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 5aab191f1a48..61e1d3da989d 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -625,6 +625,20 @@ to a path. (Contributed by Joannah Nanjekye in :issue:`26978`) +pickle +------ + +Reduction methods can now include a 6th item in the tuple they return. This +item should specify a custom state-setting method that's called instead of the +regular ``__setstate__`` method. +(Contributed by Pierre Glaser and Olivier Grisel in :issue:`35900`) + +:mod:`pickle` extensions subclassing the C-optimized :class:`~pickle.Pickler` +can now override the pickling logic of functions and classes by defining the +special :meth:`~pickle.Pickler.reducer_override` method. +(Contributed by Pierre Glaser and Olivier Grisel in :issue:`35900`) + + plistlib -------- From webhook-mailer at python.org Mon Jul 1 10:05:10 2019 From: webhook-mailer at python.org (Antoine Pitrou) Date: Mon, 01 Jul 2019 14:05:10 -0000 Subject: [Python-checkins] bpo-37209: Add pickle entry for 3.8 whatsnew (GH-14503) (GH-14512) Message-ID: https://github.com/python/cpython/commit/e224d2865aa0f021b25d68de9a6c2be617341f4c commit: e224d2865aa0f021b25d68de9a6c2be617341f4c branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Antoine Pitrou date: 2019-07-01T16:05:02+02:00 summary: bpo-37209: Add pickle entry for 3.8 whatsnew (GH-14503) (GH-14512) (cherry picked from commit ec6c1bd0491590f3c0e2908a7b2dfb91b6acdae9) Co-authored-by: Pierre Glaser files: M Doc/whatsnew/3.8.rst diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index f423765c8917..1f5694caf9a4 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -622,6 +622,20 @@ to a path. (Contributed by Joannah Nanjekye in :issue:`26978`) +pickle +------ + +Reduction methods can now include a 6th item in the tuple they return. This +item should specify a custom state-setting method that's called instead of the +regular ``__setstate__`` method. +(Contributed by Pierre Glaser and Olivier Grisel in :issue:`35900`) + +:mod:`pickle` extensions subclassing the C-optimized :class:`~pickle.Pickler` +can now override the pickling logic of functions and classes by defining the +special :meth:`~pickle.Pickler.reducer_override` method. +(Contributed by Pierre Glaser and Olivier Grisel in :issue:`35900`) + + plistlib -------- From webhook-mailer at python.org Mon Jul 1 10:51:26 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 01 Jul 2019 14:51:26 -0000 Subject: [Python-checkins] bpo-37467: Fix PyErr_Display() for bytes filename (GH-14504) Message-ID: https://github.com/python/cpython/commit/f9b7457bd7f438263e0d2dd1f70589ad56a2585e commit: f9b7457bd7f438263e0d2dd1f70589ad56a2585e branch: master author: Victor Stinner committer: GitHub date: 2019-07-01T16:51:18+02:00 summary: bpo-37467: Fix PyErr_Display() for bytes filename (GH-14504) Fix sys.excepthook() and PyErr_Display() if a filename is a bytes string. For example, for a SyntaxError exception where the filename attribute is a bytes string. Cleanup also test_sys: * Sort imports. * Rename numruns global var to INTERN_NUMRUNS. * Add DisplayHookTest and ExceptHookTest test case classes. * Don't save/restore sys.stdout and sys.displayhook using setUp()/tearDown(): do it in each test method. * Test error case (call hook with no argument) after the success case. files: A Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst M Lib/test/test_sys.py M Python/pythonrun.c diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index c223f92ba6bb..8852aaef9437 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1,81 +1,104 @@ -import unittest, test.support +from test import support from test.support.script_helper import assert_python_ok, assert_python_failure -import sys, io, os +import builtins +import codecs +import gc +import io +import locale +import operator +import os import struct import subprocess +import sys +import sysconfig +import test.support import textwrap +import unittest import warnings -import operator -import codecs -import gc -import sysconfig -import locale + # count the number of test runs, used to create unique # strings to intern in test_intern() -numruns = 0 +INTERN_NUMRUNS = 0 -class SysModuleTest(unittest.TestCase): +class DisplayHookTest(unittest.TestCase): - def setUp(self): - self.orig_stdout = sys.stdout - self.orig_stderr = sys.stderr - self.orig_displayhook = sys.displayhook + def test_original_displayhook(self): + dh = sys.__displayhook__ - def tearDown(self): - sys.stdout = self.orig_stdout - sys.stderr = self.orig_stderr - sys.displayhook = self.orig_displayhook - test.support.reap_children() + with support.captured_stdout() as out: + dh(42) - def test_original_displayhook(self): - import builtins - out = io.StringIO() - sys.stdout = out + self.assertEqual(out.getvalue(), "42\n") + self.assertEqual(builtins._, 42) - dh = sys.__displayhook__ + del builtins._ - self.assertRaises(TypeError, dh) - if hasattr(builtins, "_"): - del builtins._ + with support.captured_stdout() as out: + dh(None) - dh(None) self.assertEqual(out.getvalue(), "") self.assertTrue(not hasattr(builtins, "_")) - dh(42) - self.assertEqual(out.getvalue(), "42\n") - self.assertEqual(builtins._, 42) - del sys.stdout - self.assertRaises(RuntimeError, dh, 42) + # sys.displayhook() requires arguments + self.assertRaises(TypeError, dh) + + stdout = sys.stdout + try: + del sys.stdout + self.assertRaises(RuntimeError, dh, 42) + finally: + sys.stdout = stdout def test_lost_displayhook(self): - del sys.displayhook - code = compile("42", "", "single") - self.assertRaises(RuntimeError, eval, code) + displayhook = sys.displayhook + try: + del sys.displayhook + code = compile("42", "", "single") + self.assertRaises(RuntimeError, eval, code) + finally: + sys.displayhook = displayhook def test_custom_displayhook(self): def baddisplayhook(obj): raise ValueError - sys.displayhook = baddisplayhook - code = compile("42", "", "single") - self.assertRaises(ValueError, eval, code) - def test_original_excepthook(self): - err = io.StringIO() - sys.stderr = err + with support.swap_attr(sys, 'displayhook', baddisplayhook): + code = compile("42", "", "single") + self.assertRaises(ValueError, eval, code) - eh = sys.__excepthook__ - self.assertRaises(TypeError, eh) +class ExceptHookTest(unittest.TestCase): + + def test_original_excepthook(self): try: raise ValueError(42) except ValueError as exc: - eh(*sys.exc_info()) + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) self.assertTrue(err.getvalue().endswith("ValueError: 42\n")) + self.assertRaises(TypeError, sys.__excepthook__) + + def test_excepthook_bytes_filename(self): + # bpo-37467: sys.excepthook() must not crash if a filename + # is a bytes string + with warnings.catch_warnings(): + warnings.simplefilter('ignore', BytesWarning) + + try: + raise SyntaxError("msg", (b"bytes_filename", 123, 0, "text")) + except SyntaxError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + + err = err.getvalue() + self.assertIn(""" File "b'bytes_filename'", line 123\n""", err) + self.assertIn(""" text\n""", err) + self.assertTrue(err.endswith("SyntaxError: msg\n")) + def test_excepthook(self): with test.support.captured_output("stderr") as stderr: sys.excepthook(1, '1', 1) @@ -85,6 +108,12 @@ def test_excepthook(self): # FIXME: testing the code for a lost or replaced excepthook in # Python/pythonrun.c::PyErr_PrintEx() is tricky. + +class SysModuleTest(unittest.TestCase): + + def tearDown(self): + test.support.reap_children() + def test_exit(self): # call with two arguments self.assertRaises(TypeError, sys.exit, 42, 42) @@ -492,10 +521,10 @@ def test_43581(self): self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding) def test_intern(self): - global numruns - numruns += 1 + global INTERN_NUMRUNS + INTERN_NUMRUNS += 1 self.assertRaises(TypeError, sys.intern) - s = "never interned before" + str(numruns) + s = "never interned before" + str(INTERN_NUMRUNS) self.assertTrue(sys.intern(s) is s) s2 = s.swapcase().swapcase() self.assertTrue(sys.intern(s2) is s) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst new file mode 100644 index 000000000000..5e809646b4b8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst @@ -0,0 +1,3 @@ +Fix :func:`sys.excepthook` and :c:func:`PyErr_Display` if a filename is a +bytes string. For example, for a SyntaxError exception where the filename +attribute is a bytes string. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 8f3ee19279d9..f1d946a0b0f8 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -797,7 +797,7 @@ print_exception(PyObject *f, PyObject *value) Py_DECREF(value); value = message; - line = PyUnicode_FromFormat(" File \"%U\", line %d\n", + line = PyUnicode_FromFormat(" File \"%S\", line %d\n", filename, lineno); Py_DECREF(filename); if (line != NULL) { From webhook-mailer at python.org Mon Jul 1 11:11:21 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 15:11:21 -0000 Subject: [Python-checkins] bpo-37467: Fix PyErr_Display() for bytes filename (GH-14504) Message-ID: https://github.com/python/cpython/commit/2683ded568b24fff1139edd9127a349f432292a6 commit: 2683ded568b24fff1139edd9127a349f432292a6 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-01T08:11:15-07:00 summary: bpo-37467: Fix PyErr_Display() for bytes filename (GH-14504) Fix sys.excepthook() and PyErr_Display() if a filename is a bytes string. For example, for a SyntaxError exception where the filename attribute is a bytes string. Cleanup also test_sys: * Sort imports. * Rename numruns global var to INTERN_NUMRUNS. * Add DisplayHookTest and ExceptHookTest test case classes. * Don't save/restore sys.stdout and sys.displayhook using setUp()/tearDown(): do it in each test method. * Test error case (call hook with no argument) after the success case. (cherry picked from commit f9b7457bd7f438263e0d2dd1f70589ad56a2585e) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst M Lib/test/test_sys.py M Python/pythonrun.c diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index a7df7a2fc8c9..96db7de4938f 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1,81 +1,104 @@ -import unittest, test.support +from test import support from test.support.script_helper import assert_python_ok, assert_python_failure -import sys, io, os +import builtins +import codecs +import gc +import io +import locale +import operator +import os import struct import subprocess +import sys +import sysconfig +import test.support import textwrap +import unittest import warnings -import operator -import codecs -import gc -import sysconfig -import locale + # count the number of test runs, used to create unique # strings to intern in test_intern() -numruns = 0 +INTERN_NUMRUNS = 0 -class SysModuleTest(unittest.TestCase): +class DisplayHookTest(unittest.TestCase): - def setUp(self): - self.orig_stdout = sys.stdout - self.orig_stderr = sys.stderr - self.orig_displayhook = sys.displayhook + def test_original_displayhook(self): + dh = sys.__displayhook__ - def tearDown(self): - sys.stdout = self.orig_stdout - sys.stderr = self.orig_stderr - sys.displayhook = self.orig_displayhook - test.support.reap_children() + with support.captured_stdout() as out: + dh(42) - def test_original_displayhook(self): - import builtins - out = io.StringIO() - sys.stdout = out + self.assertEqual(out.getvalue(), "42\n") + self.assertEqual(builtins._, 42) - dh = sys.__displayhook__ + del builtins._ - self.assertRaises(TypeError, dh) - if hasattr(builtins, "_"): - del builtins._ + with support.captured_stdout() as out: + dh(None) - dh(None) self.assertEqual(out.getvalue(), "") self.assertTrue(not hasattr(builtins, "_")) - dh(42) - self.assertEqual(out.getvalue(), "42\n") - self.assertEqual(builtins._, 42) - del sys.stdout - self.assertRaises(RuntimeError, dh, 42) + # sys.displayhook() requires arguments + self.assertRaises(TypeError, dh) + + stdout = sys.stdout + try: + del sys.stdout + self.assertRaises(RuntimeError, dh, 42) + finally: + sys.stdout = stdout def test_lost_displayhook(self): - del sys.displayhook - code = compile("42", "", "single") - self.assertRaises(RuntimeError, eval, code) + displayhook = sys.displayhook + try: + del sys.displayhook + code = compile("42", "", "single") + self.assertRaises(RuntimeError, eval, code) + finally: + sys.displayhook = displayhook def test_custom_displayhook(self): def baddisplayhook(obj): raise ValueError - sys.displayhook = baddisplayhook - code = compile("42", "", "single") - self.assertRaises(ValueError, eval, code) - def test_original_excepthook(self): - err = io.StringIO() - sys.stderr = err + with support.swap_attr(sys, 'displayhook', baddisplayhook): + code = compile("42", "", "single") + self.assertRaises(ValueError, eval, code) + - eh = sys.__excepthook__ +class ExceptHookTest(unittest.TestCase): - self.assertRaises(TypeError, eh) + def test_original_excepthook(self): try: raise ValueError(42) except ValueError as exc: - eh(*sys.exc_info()) + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) self.assertTrue(err.getvalue().endswith("ValueError: 42\n")) + self.assertRaises(TypeError, sys.__excepthook__) + + def test_excepthook_bytes_filename(self): + # bpo-37467: sys.excepthook() must not crash if a filename + # is a bytes string + with warnings.catch_warnings(): + warnings.simplefilter('ignore', BytesWarning) + + try: + raise SyntaxError("msg", (b"bytes_filename", 123, 0, "text")) + except SyntaxError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + + err = err.getvalue() + self.assertIn(""" File "b'bytes_filename'", line 123\n""", err) + self.assertIn(""" text\n""", err) + self.assertTrue(err.endswith("SyntaxError: msg\n")) + def test_excepthook(self): with test.support.captured_output("stderr") as stderr: sys.excepthook(1, '1', 1) @@ -85,6 +108,12 @@ def test_excepthook(self): # FIXME: testing the code for a lost or replaced excepthook in # Python/pythonrun.c::PyErr_PrintEx() is tricky. + +class SysModuleTest(unittest.TestCase): + + def tearDown(self): + test.support.reap_children() + def test_exit(self): # call with two arguments self.assertRaises(TypeError, sys.exit, 42, 42) @@ -501,10 +530,10 @@ def test_43581(self): self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding) def test_intern(self): - global numruns - numruns += 1 + global INTERN_NUMRUNS + INTERN_NUMRUNS += 1 self.assertRaises(TypeError, sys.intern) - s = "never interned before" + str(numruns) + s = "never interned before" + str(INTERN_NUMRUNS) self.assertTrue(sys.intern(s) is s) s2 = s.swapcase().swapcase() self.assertTrue(sys.intern(s2) is s) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst new file mode 100644 index 000000000000..5e809646b4b8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst @@ -0,0 +1,3 @@ +Fix :func:`sys.excepthook` and :c:func:`PyErr_Display` if a filename is a +bytes string. For example, for a SyntaxError exception where the filename +attribute is a bytes string. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 8f3ee19279d9..f1d946a0b0f8 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -797,7 +797,7 @@ print_exception(PyObject *f, PyObject *value) Py_DECREF(value); value = message; - line = PyUnicode_FromFormat(" File \"%U\", line %d\n", + line = PyUnicode_FromFormat(" File \"%S\", line %d\n", filename, lineno); Py_DECREF(filename); if (line != NULL) { From webhook-mailer at python.org Mon Jul 1 11:41:50 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 01 Jul 2019 15:41:50 -0000 Subject: [Python-checkins] bpo-37467: Fix PyErr_Display() for bytes filename (GH-14504) (GH-14515) Message-ID: https://github.com/python/cpython/commit/8cbffc4d96d1da0fbc38da6f34f2da30c5ffd601 commit: 8cbffc4d96d1da0fbc38da6f34f2da30c5ffd601 branch: 3.7 author: Victor Stinner committer: GitHub date: 2019-07-01T17:41:38+02:00 summary: bpo-37467: Fix PyErr_Display() for bytes filename (GH-14504) (GH-14515) Fix sys.excepthook() and PyErr_Display() if a filename is a bytes string. For example, for a SyntaxError exception where the filename attribute is a bytes string. Cleanup also test_sys: * Sort imports. * Rename numruns global var to INTERN_NUMRUNS. * Add DisplayHookTest and ExceptHookTest test case classes. * Don't save/restore sys.stdout and sys.displayhook using setUp()/tearDown(): do it in each test method. * Test error case (call hook with no argument) after the success case. (cherry picked from commit f9b7457bd7f438263e0d2dd1f70589ad56a2585e) files: A Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst M Lib/test/test_sys.py M Python/pythonrun.c diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ef3fee13b961..84927a393f17 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1,82 +1,103 @@ -import unittest, test.support +from test import support from test.support.script_helper import assert_python_ok, assert_python_failure -import sys, io, os +import builtins +import codecs +import gc +import locale +import operator +import os import struct import subprocess +import sys +import sysconfig +import test.support import textwrap +import unittest import warnings -import operator -import codecs -import gc -import sysconfig -import locale -import threading + # count the number of test runs, used to create unique # strings to intern in test_intern() -numruns = 0 +INTERN_NUMRUNS = 0 -class SysModuleTest(unittest.TestCase): +class DisplayHookTest(unittest.TestCase): - def setUp(self): - self.orig_stdout = sys.stdout - self.orig_stderr = sys.stderr - self.orig_displayhook = sys.displayhook + def test_original_displayhook(self): + dh = sys.__displayhook__ - def tearDown(self): - sys.stdout = self.orig_stdout - sys.stderr = self.orig_stderr - sys.displayhook = self.orig_displayhook - test.support.reap_children() + with support.captured_stdout() as out: + dh(42) - def test_original_displayhook(self): - import builtins - out = io.StringIO() - sys.stdout = out + self.assertEqual(out.getvalue(), "42\n") + self.assertEqual(builtins._, 42) - dh = sys.__displayhook__ + del builtins._ - self.assertRaises(TypeError, dh) - if hasattr(builtins, "_"): - del builtins._ + with support.captured_stdout() as out: + dh(None) - dh(None) self.assertEqual(out.getvalue(), "") self.assertTrue(not hasattr(builtins, "_")) - dh(42) - self.assertEqual(out.getvalue(), "42\n") - self.assertEqual(builtins._, 42) - del sys.stdout - self.assertRaises(RuntimeError, dh, 42) + # sys.displayhook() requires arguments + self.assertRaises(TypeError, dh) + + stdout = sys.stdout + try: + del sys.stdout + self.assertRaises(RuntimeError, dh, 42) + finally: + sys.stdout = stdout def test_lost_displayhook(self): - del sys.displayhook - code = compile("42", "", "single") - self.assertRaises(RuntimeError, eval, code) + displayhook = sys.displayhook + try: + del sys.displayhook + code = compile("42", "", "single") + self.assertRaises(RuntimeError, eval, code) + finally: + sys.displayhook = displayhook def test_custom_displayhook(self): def baddisplayhook(obj): raise ValueError - sys.displayhook = baddisplayhook - code = compile("42", "", "single") - self.assertRaises(ValueError, eval, code) - def test_original_excepthook(self): - err = io.StringIO() - sys.stderr = err + with support.swap_attr(sys, 'displayhook', baddisplayhook): + code = compile("42", "", "single") + self.assertRaises(ValueError, eval, code) + - eh = sys.__excepthook__ +class ExceptHookTest(unittest.TestCase): - self.assertRaises(TypeError, eh) + def test_original_excepthook(self): try: raise ValueError(42) except ValueError as exc: - eh(*sys.exc_info()) + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) self.assertTrue(err.getvalue().endswith("ValueError: 42\n")) + self.assertRaises(TypeError, sys.__excepthook__) + + def test_excepthook_bytes_filename(self): + # bpo-37467: sys.excepthook() must not crash if a filename + # is a bytes string + with warnings.catch_warnings(): + warnings.simplefilter('ignore', BytesWarning) + + try: + raise SyntaxError("msg", (b"bytes_filename", 123, 0, "text")) + except SyntaxError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + + err = err.getvalue() + self.assertIn(""" File "b'bytes_filename'", line 123\n""", err) + self.assertIn(""" text\n""", err) + self.assertTrue(err.endswith("SyntaxError: msg\n")) + def test_excepthook(self): with test.support.captured_output("stderr") as stderr: sys.excepthook(1, '1', 1) @@ -86,6 +107,12 @@ def test_excepthook(self): # FIXME: testing the code for a lost or replaced excepthook in # Python/pythonrun.c::PyErr_PrintEx() is tricky. + +class SysModuleTest(unittest.TestCase): + + def tearDown(self): + test.support.reap_children() + def test_exit(self): # call with two arguments self.assertRaises(TypeError, sys.exit, 42, 42) @@ -502,10 +529,10 @@ def test_43581(self): self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding) def test_intern(self): - global numruns - numruns += 1 + global INTERN_NUMRUNS + INTERN_NUMRUNS += 1 self.assertRaises(TypeError, sys.intern) - s = "never interned before" + str(numruns) + s = "never interned before" + str(INTERN_NUMRUNS) self.assertTrue(sys.intern(s) is s) s2 = s.swapcase().swapcase() self.assertTrue(sys.intern(s2) is s) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst new file mode 100644 index 000000000000..5e809646b4b8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst @@ -0,0 +1,3 @@ +Fix :func:`sys.excepthook` and :c:func:`PyErr_Display` if a filename is a +bytes string. For example, for a SyntaxError exception where the filename +attribute is a bytes string. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index c4ec5ac66c47..4c974cef39c7 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -750,7 +750,7 @@ print_exception(PyObject *f, PyObject *value) Py_DECREF(value); value = message; - line = PyUnicode_FromFormat(" File \"%U\", line %d\n", + line = PyUnicode_FromFormat(" File \"%S\", line %d\n", filename, lineno); Py_DECREF(filename); if (line != NULL) { From webhook-mailer at python.org Mon Jul 1 12:28:29 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 01 Jul 2019 16:28:29 -0000 Subject: [Python-checkins] Remove unused imports in tests (GH-14518) Message-ID: https://github.com/python/cpython/commit/8f4ef3b019ce380022018587571b0f970e668de3 commit: 8f4ef3b019ce380022018587571b0f970e668de3 branch: master author: Victor Stinner committer: GitHub date: 2019-07-01T18:28:25+02:00 summary: Remove unused imports in tests (GH-14518) files: M Lib/test/libregrtest/main.py M Lib/test/support/__init__.py M Lib/test/test__xxsubinterpreters.py M Lib/test/test_argparse.py M Lib/test/test_asynchat.py M Lib/test/test_asyncio/test_events.py M Lib/test/test_asyncio/test_pep492.py M Lib/test/test_asyncio/test_proactor_events.py M Lib/test/test_asyncio/test_server.py M Lib/test/test_asyncio/test_sslproto.py M Lib/test/test_asyncio/test_unix_events.py M Lib/test/test_asyncio/test_windows_events.py M Lib/test/test_audit.py M Lib/test/test_c_locale_coercion.py M Lib/test/test_capi.py M Lib/test/test_cgi.py M Lib/test/test_cmath.py M Lib/test/test_cmd_line.py M Lib/test/test_codeccallbacks.py M Lib/test/test_codecs.py M Lib/test/test_collections.py M Lib/test/test_cprofile.py M Lib/test/test_docxmlrpc.py M Lib/test/test_email/test_policy.py M Lib/test/test_embed.py M Lib/test/test_exceptions.py M Lib/test/test_faulthandler.py M Lib/test/test_fork1.py M Lib/test/test_generators.py M Lib/test/test_gettext.py M Lib/test/test_imaplib.py M Lib/test/test_import/__init__.py M Lib/test/test_importlib/test_util.py M Lib/test/test_locale.py M Lib/test/test_math.py M Lib/test/test_os.py M Lib/test/test_peepholer.py M Lib/test/test_picklebuffer.py M Lib/test/test_platform.py M Lib/test/test_posixpath.py M Lib/test/test_pyclbr.py M Lib/test/test_pydoc.py M Lib/test/test_runpy.py M Lib/test/test_signal.py M Lib/test/test_smtplib.py M Lib/test/test_ssl.py M Lib/test/test_string_literals.py M Lib/test/test_subprocess.py M Lib/test/test_sys.py M Lib/test/test_tabnanny.py M Lib/test/test_threaded_import.py M Lib/test/test_threadedtempfile.py M Lib/test/test_zipfile.py diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 1dfbe47a2fab..e2274254fdb8 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -587,7 +587,6 @@ def create_temp_dir(self): def cleanup(self): import glob - import shutil path = os.path.join(self.tmp_dir, 'test_python_*') print("Cleanup %s directory" % self.tmp_dir) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index a65de4a5abe8..611c1cc9776d 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -6,7 +6,6 @@ import asyncio.events import collections.abc import contextlib -import datetime import errno import faulthandler import fnmatch @@ -15,7 +14,6 @@ import glob import importlib import importlib.util -import io import logging.handlers import nntplib import os diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 1eece9659249..78b2030a1f6d 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -4,7 +4,7 @@ import os import pickle import sys -from textwrap import dedent, indent +from textwrap import dedent import threading import time import unittest diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index bcf2cc9b26a3..9079d4bc7aa7 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1,6 +1,5 @@ # Author: Steven J. Bethard . -import codecs import inspect import os import shutil diff --git a/Lib/test/test_asynchat.py b/Lib/test/test_asynchat.py index 1d147c741961..14c0ec43d422 100644 --- a/Lib/test/test_asynchat.py +++ b/Lib/test/test_asynchat.py @@ -7,7 +7,6 @@ import errno import socket import sys -import _thread as thread import threading import time import unittest diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 045654e87a85..e5ad72fe5ba8 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -26,8 +26,6 @@ import tty import asyncio -from asyncio import base_events -from asyncio import constants from asyncio import coroutines from asyncio import events from asyncio import proactor_events diff --git a/Lib/test/test_asyncio/test_pep492.py b/Lib/test/test_asyncio/test_pep492.py index 11c0ce495d52..a5cf37ded7c9 100644 --- a/Lib/test/test_asyncio/test_pep492.py +++ b/Lib/test/test_asyncio/test_pep492.py @@ -4,7 +4,6 @@ import types import unittest -from test import support from unittest import mock import asyncio diff --git a/Lib/test/test_asyncio/test_proactor_events.py b/Lib/test/test_asyncio/test_proactor_events.py index 2e9995d32807..b2fd60683c57 100644 --- a/Lib/test/test_asyncio/test_proactor_events.py +++ b/Lib/test/test_asyncio/test_proactor_events.py @@ -4,11 +4,9 @@ import socket import unittest import sys -from collections import deque from unittest import mock import asyncio -from asyncio import events from asyncio.proactor_events import BaseProactorEventLoop from asyncio.proactor_events import _ProactorSocketTransport from asyncio.proactor_events import _ProactorWritePipeTransport diff --git a/Lib/test/test_asyncio/test_server.py b/Lib/test/test_asyncio/test_server.py index 0e38e6c8ecd4..d47ccc027677 100644 --- a/Lib/test/test_asyncio/test_server.py +++ b/Lib/test/test_asyncio/test_server.py @@ -1,5 +1,4 @@ import asyncio -import socket import time import threading import unittest diff --git a/Lib/test/test_asyncio/test_sslproto.py b/Lib/test/test_asyncio/test_sslproto.py index 1c2285063ef6..9457bc982b06 100644 --- a/Lib/test/test_asyncio/test_sslproto.py +++ b/Lib/test/test_asyncio/test_sslproto.py @@ -15,7 +15,6 @@ from asyncio import log from asyncio import protocols from asyncio import sslproto -from asyncio import tasks from test.test_asyncio import utils as test_utils from test.test_asyncio import functional as func_tests diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 462a8b3c7859..1daa870a7b27 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -22,8 +22,6 @@ import asyncio from asyncio import log -from asyncio import base_events -from asyncio import events from asyncio import unix_events from test.test_asyncio import utils as test_utils diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 1e1c01d713b5..64543268b1ef 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -2,7 +2,6 @@ import signal import socket import sys -import subprocess import time import threading import unittest @@ -12,14 +11,12 @@ raise unittest.SkipTest('Windows only') import _overlapped -import _testcapi import _winapi import asyncio from asyncio import windows_events from asyncio.streams import _StreamProtocol from test.test_asyncio import utils as test_utils -from test.support.script_helper import spawn_python def tearDownModule(): diff --git a/Lib/test/test_audit.py b/Lib/test/test_audit.py index 2fc41bddcb8a..41f9fae10223 100644 --- a/Lib/test/test_audit.py +++ b/Lib/test/test_audit.py @@ -1,7 +1,6 @@ """Tests for sys.audit and sys.addaudithook """ -import os import subprocess import sys import unittest diff --git a/Lib/test/test_c_locale_coercion.py b/Lib/test/test_c_locale_coercion.py index 35272b5c15ac..8149e2b98bb3 100644 --- a/Lib/test/test_c_locale_coercion.py +++ b/Lib/test/test_c_locale_coercion.py @@ -2,7 +2,6 @@ import locale import os -import shutil import subprocess import sys import sysconfig @@ -10,10 +9,8 @@ from collections import namedtuple from test import support -from test.support.script_helper import ( - run_python_until_end, - interpreter_requires_environment, -) +from test.support.script_helper import run_python_until_end + # Set the list of ways we expect to be able to ask for the "C" locale EXPECTED_C_LOCALE_EQUIVALENTS = ["C", "invalid.ascii"] diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 45fabd599159..7b35ba60b53a 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -8,7 +8,6 @@ import re import subprocess import sys -import sysconfig import textwrap import threading import time diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index b86638e1c283..092255598259 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -1,4 +1,3 @@ -from test.support import check_warnings import cgi import os import sys diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index a00185f43dbf..668f27c8a082 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -6,7 +6,7 @@ from cmath import phase, polar, rect, pi import platform import sys -import sysconfig + INF = float('inf') NAN = float('nan') diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index f7925eb795c7..497bfa9eb89d 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -5,7 +5,6 @@ import os import subprocess import sys -import sysconfig import tempfile import unittest from test import support diff --git a/Lib/test/test_codeccallbacks.py b/Lib/test/test_codeccallbacks.py index 585992be1f0b..243f002c4eca 100644 --- a/Lib/test/test_codeccallbacks.py +++ b/Lib/test/test_codeccallbacks.py @@ -1,10 +1,10 @@ import codecs import html.entities import sys -import test.support import unicodedata import unittest + class PosReturn: # this can be used for configurable callbacks diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 4317dfceb039..b187ca650dc6 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -29,7 +29,7 @@ def check(input, expect): # On small versions of Windows like Windows IoT or Windows Nano Server not all codepages are present def is_code_page_present(cp): - from ctypes import POINTER, WINFUNCTYPE, windll, WinError, Structure, WinDLL + from ctypes import POINTER, WINFUNCTYPE, WinDLL from ctypes.wintypes import BOOL, UINT, BYTE, WCHAR, UINT, DWORD MAX_LEADBYTES = 12 # 5 ranges, 2 bytes ea., 0 term. diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index e2d04d5b4761..e532be6eeaf0 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -13,7 +13,7 @@ import types import unittest -from collections import namedtuple, Counter, OrderedDict, _count_elements, _tuplegetter +from collections import namedtuple, Counter, OrderedDict, _count_elements from collections import UserDict, UserString, UserList from collections import ChainMap from collections import deque diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py index 5c70037f39a2..4ec769885292 100644 --- a/Lib/test/test_cprofile.py +++ b/Lib/test/test_cprofile.py @@ -6,7 +6,7 @@ # rip off all interesting stuff from test_profile import cProfile from test.test_profile import ProfileTest, regenerate_expected_output -from test.support.script_helper import assert_python_failure, assert_python_ok +from test.support.script_helper import assert_python_failure from test import support diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py index f077f05f5b4f..116e626740df 100644 --- a/Lib/test/test_docxmlrpc.py +++ b/Lib/test/test_docxmlrpc.py @@ -2,7 +2,6 @@ import http.client import sys import threading -from test import support import unittest def make_request_and_skipIf(condition, reason): diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index 0aea934df434..1e39aa062c0a 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -1,5 +1,4 @@ import io -import sys import types import textwrap import unittest diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 9c78aa059fc3..e1cf4be50668 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -5,7 +5,6 @@ from collections import namedtuple import json import os -import platform import re import subprocess import sys diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index d7e11d2d30a8..10c1e076464e 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -9,7 +9,7 @@ import errno from test.support import (TESTFN, captured_stderr, check_impl_detail, - check_warnings, cpython_only, gc_collect, run_unittest, + check_warnings, cpython_only, gc_collect, no_tracing, unlink, import_module, script_helper, SuppressCrashReport) from test import support diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index f0be91844ffa..1cf20db1c7ff 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -9,7 +9,6 @@ from test import support from test.support import script_helper, is_android import tempfile -import threading import unittest from textwrap import dedent diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index 9ca9724c4c91..2ab856ff5690 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -10,8 +10,7 @@ import unittest from test.fork_wait import ForkWait -from test.support import (reap_children, get_attribute, - import_module, verbose) +from test.support import reap_children, get_attribute, verbose # Skip test if fork does not exist. diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index a34e4ec2eda7..f8d86da5e2f5 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -3,7 +3,6 @@ import pickle import sys import unittest -import warnings import weakref import inspect diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index 9d1a96b8b0d1..baf300b05724 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -2,7 +2,6 @@ import base64 import contextlib import gettext -import locale import unittest from test import support diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 9305e47ee993..8ab532af3f0d 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -1,7 +1,6 @@ from test import support from contextlib import contextmanager -import errno import imaplib import os.path import socketserver diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 84cd0da94b36..50406d9aa1d9 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -5,7 +5,6 @@ import builtins import marshal import os -import platform import py_compile import random import shutil @@ -23,9 +22,9 @@ import test.support from test.support import ( - EnvironmentVarGuard, TESTFN, check_warnings, forget, is_jython, - make_legacy_pyc, rmtree, run_unittest, swap_attr, swap_item, temp_umask, - unlink, unload, create_empty_file, cpython_only, TESTFN_UNENCODABLE, + TESTFN, forget, is_jython, + make_legacy_pyc, rmtree, swap_attr, swap_item, temp_umask, + unlink, unload, cpython_only, TESTFN_UNENCODABLE, temp_dir, DirsOnSysPath) from test.support import script_helper from test.test_importlib.util import uncache diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index db0899aff6b9..0350a5a5cc05 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -4,7 +4,6 @@ machinery = util.import_importlib('importlib.machinery') importlib_util = util.import_importlib('importlib.util') -import contextlib import importlib.util import os import pathlib diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index e2c2178ae6cc..792a15c50f92 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -3,7 +3,7 @@ import locale import sys import codecs -import warnings + class BaseLocalizedTest(unittest.TestCase): # diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 362d09370d45..393cdaff1818 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -12,7 +12,7 @@ import random import struct import sys -import sysconfig + eps = 1E-05 NAN = float('nan') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 784000a2eb36..b2cd4cca5f21 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -9,7 +9,6 @@ import decimal import errno import fractions -import getpass import itertools import locale import mmap diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index b5f85bd55973..c90a53210a93 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1,10 +1,9 @@ import dis import unittest -import types -import textwrap from test.bytecode_helper import BytecodeTestCase + def count_instr_recursively(f, opname): count = 0 for instr in dis.get_instructions(f): diff --git a/Lib/test/test_picklebuffer.py b/Lib/test/test_picklebuffer.py index 7e72157fd022..97981c882e82 100644 --- a/Lib/test/test_picklebuffer.py +++ b/Lib/test/test_picklebuffer.py @@ -5,7 +5,6 @@ import gc from pickle import PickleBuffer -import sys import weakref import unittest diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 8b64923e174c..3084663a8fad 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -2,13 +2,12 @@ import platform import subprocess import sys -import sysconfig -import tempfile import unittest from unittest import mock from test import support + class PlatformTest(unittest.TestCase): def clear_caches(self): platform._platform_cache.clear() diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 983e2dd6ff27..4d3d8976d604 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -1,7 +1,6 @@ import os import posixpath import unittest -import warnings from posixpath import realpath, abspath, dirname, basename from test import support, test_genericpath from test.support import FakePath @@ -12,6 +11,7 @@ except ImportError: posix = None + # An absolute path to a temporary filename for testing. We can't rely on TESTFN # being an absolute path, so we need this. diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index 531304021312..4385271cd0f2 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -3,15 +3,13 @@ Nick Mathewson ''' -import os import sys from textwrap import dedent from types import FunctionType, MethodType, BuiltinFunctionType import pyclbr from unittest import TestCase, main as unittest_main -from test import support from test.test_importlib import util as test_importlib_util -from functools import partial + StaticMethodType = type(staticmethod(lambda: None)) ClassMethodType = type(classmethod(lambda c: None)) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 6efdeb047c21..c80477c50f09 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -21,7 +21,6 @@ import xml.etree import xml.etree.ElementTree import textwrap -import threading from io import StringIO from collections import namedtuple from test.support.script_helper import assert_python_ok diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py index 800b483b7e15..f00308611163 100644 --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -11,8 +11,7 @@ from test.support import ( forget, make_legacy_pyc, unload, verbose, no_tracing, create_empty_file, temp_dir) -from test.support.script_helper import ( - make_pkg, make_script, make_zip_pkg, make_zip_script) +from test.support.script_helper import make_script, make_zip_script import runpy diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 063b35ca230f..d41e94b07f43 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -6,7 +6,6 @@ import statistics import subprocess import sys -import threading import time import unittest from test import support diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index fdcf6f219256..f1332e9ef782 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -19,7 +19,7 @@ import unittest from test import support, mock_socket -from test.support import HOST, HOSTv4, HOSTv6 +from test.support import HOST from test.support import threading_setup, threading_cleanup, join_thread from unittest.mock import Mock diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index d83ee2cc974d..ef1723903a15 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -26,7 +26,7 @@ ssl = support.import_module("ssl") -from ssl import TLSVersion, _TLSContentType, _TLSMessageType, _TLSAlertType +from ssl import TLSVersion, _TLSContentType, _TLSMessageType Py_DEBUG = hasattr(sys, 'gettotalrefcount') Py_DEBUG_WIN32 = Py_DEBUG and sys.platform == 'win32' @@ -4601,7 +4601,6 @@ def msg_cb(conn, direction, version, content_type, msg_type, data): def test_main(verbose=False): if support.verbose: - import warnings plats = { 'Mac': platform.mac_ver, 'Windows': platform.win32_ver, diff --git a/Lib/test/test_string_literals.py b/Lib/test/test_string_literals.py index 048f40d90a4b..5961d591c448 100644 --- a/Lib/test/test_string_literals.py +++ b/Lib/test/test_string_literals.py @@ -31,7 +31,6 @@ import sys import shutil import tempfile -import warnings import unittest diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 6b8acb258ee3..e58d0925df3b 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -3,7 +3,6 @@ from test import support import subprocess import sys -import platform import signal import io import itertools @@ -20,18 +19,12 @@ import textwrap from test.support import FakePath -try: - import ctypes -except ImportError: - ctypes = None -else: - import ctypes.util - try: import _testcapi except ImportError: _testcapi = None + if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 8852aaef9437..9961dee754c6 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -3,7 +3,6 @@ import builtins import codecs import gc -import io import locale import operator import os diff --git a/Lib/test/test_tabnanny.py b/Lib/test/test_tabnanny.py index 81549d14ae2b..95840d6ac0c5 100644 --- a/Lib/test/test_tabnanny.py +++ b/Lib/test/test_tabnanny.py @@ -7,7 +7,6 @@ from unittest import mock import errno import os -import sys import tabnanny import tokenize import tempfile diff --git a/Lib/test/test_threaded_import.py b/Lib/test/test_threaded_import.py index 035344be4b89..8607f363db21 100644 --- a/Lib/test/test_threaded_import.py +++ b/Lib/test/test_threaded_import.py @@ -15,7 +15,7 @@ import unittest from unittest import mock from test.support import ( - verbose, import_module, run_unittest, TESTFN, reap_threads, + verbose, run_unittest, TESTFN, reap_threads, forget, unlink, rmtree, start_threads) def task(N, done, done_tasks, errors): diff --git a/Lib/test/test_threadedtempfile.py b/Lib/test/test_threadedtempfile.py index f3d4ba36377d..e1d7a10179cc 100644 --- a/Lib/test/test_threadedtempfile.py +++ b/Lib/test/test_threadedtempfile.py @@ -13,19 +13,22 @@ provoking a 2.0 failure under Linux. """ -NUM_THREADS = 20 -FILES_PER_THREAD = 50 - import tempfile -from test.support import start_threads, import_module +from test.support import start_threads import unittest import io import threading from traceback import print_exc + +NUM_THREADS = 20 +FILES_PER_THREAD = 50 + + startEvent = threading.Event() + class TempFileGreedy(threading.Thread): error_count = 0 ok_count = 0 diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index bf5bb4d0f13e..19b550f80187 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -4,9 +4,7 @@ import os import pathlib import posixpath -import shutil import struct -import tempfile import time import unittest import zipfile From webhook-mailer at python.org Mon Jul 1 12:30:52 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 16:30:52 -0000 Subject: [Python-checkins] bpo-36168: Lowercase the word "subsequent" in get_value doc (GH-14485) Message-ID: https://github.com/python/cpython/commit/59ec9ee4d7f3c2444efb989e96f5124e1c246de5 commit: 59ec9ee4d7f3c2444efb989e96f5124e1c246de5 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-01T09:30:48-07:00 summary: bpo-36168: Lowercase the word "subsequent" in get_value doc (GH-14485) Subsequent -> subsequent https://bugs.python.org/issue36168 (cherry picked from commit 12b436e3b079fb3e3a7197c089df90a77e3bdd77) Co-authored-by: Krishna Oza files: M Doc/library/string.rst diff --git a/Doc/library/string.rst b/Doc/library/string.rst index 288dde6b3fe4..af8b9b358cc3 100644 --- a/Doc/library/string.rst +++ b/Doc/library/string.rst @@ -146,7 +146,7 @@ implementation as the built-in :meth:`~str.format` method. keyword arguments. For compound field names, these functions are only called for the first - component of the field name; Subsequent components are handled through + component of the field name; subsequent components are handled through normal attribute and indexing operations. So for example, the field expression '0.name' would cause From webhook-mailer at python.org Mon Jul 1 12:35:13 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 01 Jul 2019 16:35:13 -0000 Subject: [Python-checkins] bpo-37472: Remove Lib/test/outstanding_bugs.py (GH-14516) Message-ID: https://github.com/python/cpython/commit/e21b45a8e71d06a6a03f99261cab33e72b896bb9 commit: e21b45a8e71d06a6a03f99261cab33e72b896bb9 branch: master author: Victor Stinner committer: GitHub date: 2019-07-01T18:35:07+02:00 summary: bpo-37472: Remove Lib/test/outstanding_bugs.py (GH-14516) files: A Misc/NEWS.d/next/Tests/2019-07-01-17-19-47.bpo-37472.WzkEAx.rst D Lib/test/outstanding_bugs.py M PCbuild/lib.pyproj diff --git a/Lib/test/outstanding_bugs.py b/Lib/test/outstanding_bugs.py deleted file mode 100644 index 7e527a670643..000000000000 --- a/Lib/test/outstanding_bugs.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# This file is for everybody to add tests for bugs that aren't -# fixed yet. Please add a test case and appropriate bug description. -# -# When you fix one of the bugs, please move the test to the correct -# test_ module. -# - -import unittest -from test import support - -# -# No test cases for outstanding bugs at the moment. -# - - -if __name__ == "__main__": - unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2019-07-01-17-19-47.bpo-37472.WzkEAx.rst b/Misc/NEWS.d/next/Tests/2019-07-01-17-19-47.bpo-37472.WzkEAx.rst new file mode 100644 index 000000000000..f62b5d54867f --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2019-07-01-17-19-47.bpo-37472.WzkEAx.rst @@ -0,0 +1 @@ +Remove ``Lib/test/outstanding_bugs.py``. diff --git a/PCbuild/lib.pyproj b/PCbuild/lib.pyproj index 683335e04489..0ddeef3eaa3b 100644 --- a/PCbuild/lib.pyproj +++ b/PCbuild/lib.pyproj @@ -830,7 +830,6 @@ - From webhook-mailer at python.org Mon Jul 1 13:01:57 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 01 Jul 2019 17:01:57 -0000 Subject: [Python-checkins] Remove unused imports in tests (GH-14518) (GH-14520) Message-ID: https://github.com/python/cpython/commit/466e18e8c2738b06a24915cb186d954821d0414f commit: 466e18e8c2738b06a24915cb186d954821d0414f branch: 3.8 author: Victor Stinner committer: GitHub date: 2019-07-01T19:01:52+02:00 summary: Remove unused imports in tests (GH-14518) (GH-14520) (cherry picked from commit 8f4ef3b019ce380022018587571b0f970e668de3) files: M Lib/test/libregrtest/main.py M Lib/test/support/__init__.py M Lib/test/test__xxsubinterpreters.py M Lib/test/test_argparse.py M Lib/test/test_asynchat.py M Lib/test/test_asyncio/test_events.py M Lib/test/test_asyncio/test_pep492.py M Lib/test/test_asyncio/test_proactor_events.py M Lib/test/test_asyncio/test_server.py M Lib/test/test_asyncio/test_sslproto.py M Lib/test/test_asyncio/test_unix_events.py M Lib/test/test_asyncio/test_windows_events.py M Lib/test/test_audit.py M Lib/test/test_c_locale_coercion.py M Lib/test/test_capi.py M Lib/test/test_cgi.py M Lib/test/test_cmath.py M Lib/test/test_cmd_line.py M Lib/test/test_codeccallbacks.py M Lib/test/test_codecs.py M Lib/test/test_collections.py M Lib/test/test_cprofile.py M Lib/test/test_docxmlrpc.py M Lib/test/test_email/test_policy.py M Lib/test/test_embed.py M Lib/test/test_exceptions.py M Lib/test/test_faulthandler.py M Lib/test/test_fork1.py M Lib/test/test_generators.py M Lib/test/test_gettext.py M Lib/test/test_imaplib.py M Lib/test/test_import/__init__.py M Lib/test/test_importlib/test_util.py M Lib/test/test_locale.py M Lib/test/test_math.py M Lib/test/test_os.py M Lib/test/test_peepholer.py M Lib/test/test_picklebuffer.py M Lib/test/test_platform.py M Lib/test/test_posixpath.py M Lib/test/test_pyclbr.py M Lib/test/test_pydoc.py M Lib/test/test_runpy.py M Lib/test/test_signal.py M Lib/test/test_smtplib.py M Lib/test/test_ssl.py M Lib/test/test_string_literals.py M Lib/test/test_subprocess.py M Lib/test/test_sys.py M Lib/test/test_tabnanny.py M Lib/test/test_threaded_import.py M Lib/test/test_threadedtempfile.py M Lib/test/test_zipfile.py diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 1dfbe47a2fab..e2274254fdb8 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -587,7 +587,6 @@ def create_temp_dir(self): def cleanup(self): import glob - import shutil path = os.path.join(self.tmp_dir, 'test_python_*') print("Cleanup %s directory" % self.tmp_dir) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 31b0dc8fc2ca..1c91fc434eec 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -6,7 +6,6 @@ import asyncio.events import collections.abc import contextlib -import datetime import errno import faulthandler import fnmatch @@ -15,7 +14,6 @@ import glob import importlib import importlib.util -import io import logging.handlers import nntplib import os diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 1eece9659249..78b2030a1f6d 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -4,7 +4,7 @@ import os import pickle import sys -from textwrap import dedent, indent +from textwrap import dedent import threading import time import unittest diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index bcf2cc9b26a3..9079d4bc7aa7 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1,6 +1,5 @@ # Author: Steven J. Bethard . -import codecs import inspect import os import shutil diff --git a/Lib/test/test_asynchat.py b/Lib/test/test_asynchat.py index 1d147c741961..14c0ec43d422 100644 --- a/Lib/test/test_asynchat.py +++ b/Lib/test/test_asynchat.py @@ -7,7 +7,6 @@ import errno import socket import sys -import _thread as thread import threading import time import unittest diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 045654e87a85..e5ad72fe5ba8 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -26,8 +26,6 @@ import tty import asyncio -from asyncio import base_events -from asyncio import constants from asyncio import coroutines from asyncio import events from asyncio import proactor_events diff --git a/Lib/test/test_asyncio/test_pep492.py b/Lib/test/test_asyncio/test_pep492.py index 11c0ce495d52..a5cf37ded7c9 100644 --- a/Lib/test/test_asyncio/test_pep492.py +++ b/Lib/test/test_asyncio/test_pep492.py @@ -4,7 +4,6 @@ import types import unittest -from test import support from unittest import mock import asyncio diff --git a/Lib/test/test_asyncio/test_proactor_events.py b/Lib/test/test_asyncio/test_proactor_events.py index 2e9995d32807..b2fd60683c57 100644 --- a/Lib/test/test_asyncio/test_proactor_events.py +++ b/Lib/test/test_asyncio/test_proactor_events.py @@ -4,11 +4,9 @@ import socket import unittest import sys -from collections import deque from unittest import mock import asyncio -from asyncio import events from asyncio.proactor_events import BaseProactorEventLoop from asyncio.proactor_events import _ProactorSocketTransport from asyncio.proactor_events import _ProactorWritePipeTransport diff --git a/Lib/test/test_asyncio/test_server.py b/Lib/test/test_asyncio/test_server.py index 0e38e6c8ecd4..d47ccc027677 100644 --- a/Lib/test/test_asyncio/test_server.py +++ b/Lib/test/test_asyncio/test_server.py @@ -1,5 +1,4 @@ import asyncio -import socket import time import threading import unittest diff --git a/Lib/test/test_asyncio/test_sslproto.py b/Lib/test/test_asyncio/test_sslproto.py index 1c2285063ef6..9457bc982b06 100644 --- a/Lib/test/test_asyncio/test_sslproto.py +++ b/Lib/test/test_asyncio/test_sslproto.py @@ -15,7 +15,6 @@ from asyncio import log from asyncio import protocols from asyncio import sslproto -from asyncio import tasks from test.test_asyncio import utils as test_utils from test.test_asyncio import functional as func_tests diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 462a8b3c7859..1daa870a7b27 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -22,8 +22,6 @@ import asyncio from asyncio import log -from asyncio import base_events -from asyncio import events from asyncio import unix_events from test.test_asyncio import utils as test_utils diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 1e1c01d713b5..64543268b1ef 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -2,7 +2,6 @@ import signal import socket import sys -import subprocess import time import threading import unittest @@ -12,14 +11,12 @@ raise unittest.SkipTest('Windows only') import _overlapped -import _testcapi import _winapi import asyncio from asyncio import windows_events from asyncio.streams import _StreamProtocol from test.test_asyncio import utils as test_utils -from test.support.script_helper import spawn_python def tearDownModule(): diff --git a/Lib/test/test_audit.py b/Lib/test/test_audit.py index 2fc41bddcb8a..41f9fae10223 100644 --- a/Lib/test/test_audit.py +++ b/Lib/test/test_audit.py @@ -1,7 +1,6 @@ """Tests for sys.audit and sys.addaudithook """ -import os import subprocess import sys import unittest diff --git a/Lib/test/test_c_locale_coercion.py b/Lib/test/test_c_locale_coercion.py index 35272b5c15ac..8149e2b98bb3 100644 --- a/Lib/test/test_c_locale_coercion.py +++ b/Lib/test/test_c_locale_coercion.py @@ -2,7 +2,6 @@ import locale import os -import shutil import subprocess import sys import sysconfig @@ -10,10 +9,8 @@ from collections import namedtuple from test import support -from test.support.script_helper import ( - run_python_until_end, - interpreter_requires_environment, -) +from test.support.script_helper import run_python_until_end + # Set the list of ways we expect to be able to ask for the "C" locale EXPECTED_C_LOCALE_EQUIVALENTS = ["C", "invalid.ascii"] diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 45fabd599159..7b35ba60b53a 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -8,7 +8,6 @@ import re import subprocess import sys -import sysconfig import textwrap import threading import time diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index b86638e1c283..092255598259 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -1,4 +1,3 @@ -from test.support import check_warnings import cgi import os import sys diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index a00185f43dbf..668f27c8a082 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -6,7 +6,7 @@ from cmath import phase, polar, rect, pi import platform import sys -import sysconfig + INF = float('inf') NAN = float('nan') diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index f7925eb795c7..497bfa9eb89d 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -5,7 +5,6 @@ import os import subprocess import sys -import sysconfig import tempfile import unittest from test import support diff --git a/Lib/test/test_codeccallbacks.py b/Lib/test/test_codeccallbacks.py index 585992be1f0b..243f002c4eca 100644 --- a/Lib/test/test_codeccallbacks.py +++ b/Lib/test/test_codeccallbacks.py @@ -1,10 +1,10 @@ import codecs import html.entities import sys -import test.support import unicodedata import unittest + class PosReturn: # this can be used for configurable callbacks diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 4317dfceb039..b187ca650dc6 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -29,7 +29,7 @@ def check(input, expect): # On small versions of Windows like Windows IoT or Windows Nano Server not all codepages are present def is_code_page_present(cp): - from ctypes import POINTER, WINFUNCTYPE, windll, WinError, Structure, WinDLL + from ctypes import POINTER, WINFUNCTYPE, WinDLL from ctypes.wintypes import BOOL, UINT, BYTE, WCHAR, UINT, DWORD MAX_LEADBYTES = 12 # 5 ranges, 2 bytes ea., 0 term. diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index e2d04d5b4761..e532be6eeaf0 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -13,7 +13,7 @@ import types import unittest -from collections import namedtuple, Counter, OrderedDict, _count_elements, _tuplegetter +from collections import namedtuple, Counter, OrderedDict, _count_elements from collections import UserDict, UserString, UserList from collections import ChainMap from collections import deque diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py index 5c70037f39a2..4ec769885292 100644 --- a/Lib/test/test_cprofile.py +++ b/Lib/test/test_cprofile.py @@ -6,7 +6,7 @@ # rip off all interesting stuff from test_profile import cProfile from test.test_profile import ProfileTest, regenerate_expected_output -from test.support.script_helper import assert_python_failure, assert_python_ok +from test.support.script_helper import assert_python_failure from test import support diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py index f077f05f5b4f..116e626740df 100644 --- a/Lib/test/test_docxmlrpc.py +++ b/Lib/test/test_docxmlrpc.py @@ -2,7 +2,6 @@ import http.client import sys import threading -from test import support import unittest def make_request_and_skipIf(condition, reason): diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index 0aea934df434..1e39aa062c0a 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -1,5 +1,4 @@ import io -import sys import types import textwrap import unittest diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 5a90b9f6f247..ed1100cfabc5 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -5,7 +5,6 @@ from collections import namedtuple import json import os -import platform import re import subprocess import sys diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index d7e11d2d30a8..10c1e076464e 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -9,7 +9,7 @@ import errno from test.support import (TESTFN, captured_stderr, check_impl_detail, - check_warnings, cpython_only, gc_collect, run_unittest, + check_warnings, cpython_only, gc_collect, no_tracing, unlink, import_module, script_helper, SuppressCrashReport) from test import support diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index f0be91844ffa..1cf20db1c7ff 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -9,7 +9,6 @@ from test import support from test.support import script_helper, is_android import tempfile -import threading import unittest from textwrap import dedent diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index 9ca9724c4c91..2ab856ff5690 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -10,8 +10,7 @@ import unittest from test.fork_wait import ForkWait -from test.support import (reap_children, get_attribute, - import_module, verbose) +from test.support import reap_children, get_attribute, verbose # Skip test if fork does not exist. diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index a34e4ec2eda7..f8d86da5e2f5 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -3,7 +3,6 @@ import pickle import sys import unittest -import warnings import weakref import inspect diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index 9d1a96b8b0d1..baf300b05724 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -2,7 +2,6 @@ import base64 import contextlib import gettext -import locale import unittest from test import support diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 9305e47ee993..8ab532af3f0d 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -1,7 +1,6 @@ from test import support from contextlib import contextmanager -import errno import imaplib import os.path import socketserver diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 84cd0da94b36..50406d9aa1d9 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -5,7 +5,6 @@ import builtins import marshal import os -import platform import py_compile import random import shutil @@ -23,9 +22,9 @@ import test.support from test.support import ( - EnvironmentVarGuard, TESTFN, check_warnings, forget, is_jython, - make_legacy_pyc, rmtree, run_unittest, swap_attr, swap_item, temp_umask, - unlink, unload, create_empty_file, cpython_only, TESTFN_UNENCODABLE, + TESTFN, forget, is_jython, + make_legacy_pyc, rmtree, swap_attr, swap_item, temp_umask, + unlink, unload, cpython_only, TESTFN_UNENCODABLE, temp_dir, DirsOnSysPath) from test.support import script_helper from test.test_importlib.util import uncache diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index db0899aff6b9..0350a5a5cc05 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -4,7 +4,6 @@ machinery = util.import_importlib('importlib.machinery') importlib_util = util.import_importlib('importlib.util') -import contextlib import importlib.util import os import pathlib diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index e2c2178ae6cc..792a15c50f92 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -3,7 +3,7 @@ import locale import sys import codecs -import warnings + class BaseLocalizedTest(unittest.TestCase): # diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index b7dac5eeef63..96af655061f6 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -12,7 +12,7 @@ import random import struct import sys -import sysconfig + eps = 1E-05 NAN = float('nan') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 784000a2eb36..b2cd4cca5f21 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -9,7 +9,6 @@ import decimal import errno import fractions -import getpass import itertools import locale import mmap diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 5d00240e2595..9c206d1d09c0 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1,10 +1,9 @@ import dis import unittest -import types -import textwrap from test.bytecode_helper import BytecodeTestCase + def count_instr_recursively(f, opname): count = 0 for instr in dis.get_instructions(f): diff --git a/Lib/test/test_picklebuffer.py b/Lib/test/test_picklebuffer.py index 7e72157fd022..97981c882e82 100644 --- a/Lib/test/test_picklebuffer.py +++ b/Lib/test/test_picklebuffer.py @@ -5,7 +5,6 @@ import gc from pickle import PickleBuffer -import sys import weakref import unittest diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 8b64923e174c..3084663a8fad 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -2,13 +2,12 @@ import platform import subprocess import sys -import sysconfig -import tempfile import unittest from unittest import mock from test import support + class PlatformTest(unittest.TestCase): def clear_caches(self): platform._platform_cache.clear() diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 983e2dd6ff27..4d3d8976d604 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -1,7 +1,6 @@ import os import posixpath import unittest -import warnings from posixpath import realpath, abspath, dirname, basename from test import support, test_genericpath from test.support import FakePath @@ -12,6 +11,7 @@ except ImportError: posix = None + # An absolute path to a temporary filename for testing. We can't rely on TESTFN # being an absolute path, so we need this. diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index 0b3934f6226e..fafe17ce5f1b 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -3,15 +3,13 @@ Nick Mathewson ''' -import os import sys from textwrap import dedent from types import FunctionType, MethodType, BuiltinFunctionType import pyclbr from unittest import TestCase, main as unittest_main -from test import support from test.test_importlib import util as test_importlib_util -from functools import partial + StaticMethodType = type(staticmethod(lambda: None)) ClassMethodType = type(classmethod(lambda c: None)) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 6efdeb047c21..c80477c50f09 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -21,7 +21,6 @@ import xml.etree import xml.etree.ElementTree import textwrap -import threading from io import StringIO from collections import namedtuple from test.support.script_helper import assert_python_ok diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py index 800b483b7e15..f00308611163 100644 --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -11,8 +11,7 @@ from test.support import ( forget, make_legacy_pyc, unload, verbose, no_tracing, create_empty_file, temp_dir) -from test.support.script_helper import ( - make_pkg, make_script, make_zip_pkg, make_zip_script) +from test.support.script_helper import make_script, make_zip_script import runpy diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 063b35ca230f..d41e94b07f43 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -6,7 +6,6 @@ import statistics import subprocess import sys -import threading import time import unittest from test import support diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index fdcf6f219256..f1332e9ef782 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -19,7 +19,7 @@ import unittest from test import support, mock_socket -from test.support import HOST, HOSTv4, HOSTv6 +from test.support import HOST from test.support import threading_setup, threading_cleanup, join_thread from unittest.mock import Mock diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 66369fe60dfe..1898990fb796 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -26,7 +26,7 @@ ssl = support.import_module("ssl") -from ssl import TLSVersion, _TLSContentType, _TLSMessageType, _TLSAlertType +from ssl import TLSVersion, _TLSContentType, _TLSMessageType PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) HOST = support.HOST @@ -4592,7 +4592,6 @@ def msg_cb(conn, direction, version, content_type, msg_type, data): def test_main(verbose=False): if support.verbose: - import warnings plats = { 'Mac': platform.mac_ver, 'Windows': platform.win32_ver, diff --git a/Lib/test/test_string_literals.py b/Lib/test/test_string_literals.py index 048f40d90a4b..5961d591c448 100644 --- a/Lib/test/test_string_literals.py +++ b/Lib/test/test_string_literals.py @@ -31,7 +31,6 @@ import sys import shutil import tempfile -import warnings import unittest diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 97d21904b9ce..9bfc21123cc0 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -3,7 +3,6 @@ from test import support import subprocess import sys -import platform import signal import io import itertools @@ -20,18 +19,12 @@ import textwrap from test.support import FakePath -try: - import ctypes -except ImportError: - ctypes = None -else: - import ctypes.util - try: import _testcapi except ImportError: _testcapi = None + if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 96db7de4938f..af0e54bd0e23 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -3,7 +3,6 @@ import builtins import codecs import gc -import io import locale import operator import os diff --git a/Lib/test/test_tabnanny.py b/Lib/test/test_tabnanny.py index 81549d14ae2b..95840d6ac0c5 100644 --- a/Lib/test/test_tabnanny.py +++ b/Lib/test/test_tabnanny.py @@ -7,7 +7,6 @@ from unittest import mock import errno import os -import sys import tabnanny import tokenize import tempfile diff --git a/Lib/test/test_threaded_import.py b/Lib/test/test_threaded_import.py index 035344be4b89..8607f363db21 100644 --- a/Lib/test/test_threaded_import.py +++ b/Lib/test/test_threaded_import.py @@ -15,7 +15,7 @@ import unittest from unittest import mock from test.support import ( - verbose, import_module, run_unittest, TESTFN, reap_threads, + verbose, run_unittest, TESTFN, reap_threads, forget, unlink, rmtree, start_threads) def task(N, done, done_tasks, errors): diff --git a/Lib/test/test_threadedtempfile.py b/Lib/test/test_threadedtempfile.py index f3d4ba36377d..e1d7a10179cc 100644 --- a/Lib/test/test_threadedtempfile.py +++ b/Lib/test/test_threadedtempfile.py @@ -13,19 +13,22 @@ provoking a 2.0 failure under Linux. """ -NUM_THREADS = 20 -FILES_PER_THREAD = 50 - import tempfile -from test.support import start_threads, import_module +from test.support import start_threads import unittest import io import threading from traceback import print_exc + +NUM_THREADS = 20 +FILES_PER_THREAD = 50 + + startEvent = threading.Event() + class TempFileGreedy(threading.Thread): error_count = 0 ok_count = 0 diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index bf5bb4d0f13e..19b550f80187 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -4,9 +4,7 @@ import os import pathlib import posixpath -import shutil import struct -import tempfile import time import unittest import zipfile From webhook-mailer at python.org Mon Jul 1 13:13:54 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 01 Jul 2019 17:13:54 -0000 Subject: [Python-checkins] bpo-36763: Add PyConfig_SetWideStringList() (GH-14444) Message-ID: https://github.com/python/cpython/commit/36242fd871d0f0977e720d4fae5700774bd8c09a commit: 36242fd871d0f0977e720d4fae5700774bd8c09a branch: master author: Victor Stinner committer: GitHub date: 2019-07-01T19:13:50+02:00 summary: bpo-36763: Add PyConfig_SetWideStringList() (GH-14444) files: A Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst M Doc/c-api/init_config.rst M Include/cpython/initconfig.h M Python/initconfig.c diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 0d94e6b8f27b..d2c1f9a2f3e3 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -25,6 +25,7 @@ Functions: * :c:func:`PyConfig_SetBytesArgv` * :c:func:`PyConfig_SetBytesString` * :c:func:`PyConfig_SetString` +* :c:func:`PyConfig_SetWideStringList` * :c:func:`PyPreConfig_InitIsolatedConfig` * :c:func:`PyPreConfig_InitPythonConfig` * :c:func:`PyStatus_Error` @@ -368,6 +369,12 @@ PyConfig Preinitialize Python if needed. + .. c:function:: PyStatus PyConfig_SetWideStringList(PyConfig *config, PyWideStringList *list, Py_ssize_t length, wchar_t **items) + + Set the list of wide strings *list* to *length* and *items*. + + Preinitialize Python if needed. + .. c:function:: PyStatus PyConfig_Read(PyConfig *config) Read all Python configuration. diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index 297fbf70792f..bd07a4829b47 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -422,6 +422,9 @@ PyAPI_FUNC(PyStatus) PyConfig_SetBytesArgv( PyAPI_FUNC(PyStatus) PyConfig_SetArgv(PyConfig *config, Py_ssize_t argc, wchar_t * const *argv); +PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config, + PyWideStringList *list, + Py_ssize_t length, wchar_t **items); #ifdef __cplusplus } diff --git a/Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst b/Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst new file mode 100644 index 000000000000..095d58116385 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst @@ -0,0 +1 @@ +Add :func:`PyConfig_SetWideStringList` function. diff --git a/Python/initconfig.c b/Python/initconfig.c index 786f6945c171..c44ae6bdfacc 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -732,7 +732,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2) } while (0) #define COPY_WSTRLIST(LIST) \ do { \ - if (_PyWideStringList_Copy(&config->LIST, &config2->LIST) < 0 ) { \ + if (_PyWideStringList_Copy(&config->LIST, &config2->LIST) < 0) { \ return _PyStatus_NO_MEMORY(); \ } \ } while (0) @@ -2324,6 +2324,23 @@ PyConfig_SetArgv(PyConfig *config, Py_ssize_t argc, wchar_t * const *argv) } +PyStatus +PyConfig_SetWideStringList(PyConfig *config, PyWideStringList *list, + Py_ssize_t length, wchar_t **items) +{ + PyStatus status = _Py_PreInitializeFromConfig(config, NULL); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + + PyWideStringList list2 = {.length = length, .items = items}; + if (_PyWideStringList_Copy(list, &list2) < 0) { + return _PyStatus_NO_MEMORY(); + } + return _PyStatus_OK(); +} + + /* Read the configuration into PyConfig from: * Command line arguments From webhook-mailer at python.org Mon Jul 1 13:40:03 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 17:40:03 -0000 Subject: [Python-checkins] bpo-36763: Add PyConfig_SetWideStringList() (GH-14444) Message-ID: https://github.com/python/cpython/commit/96f581cf9d2f1d7888d2fd9bb89f19f10c0477bf commit: 96f581cf9d2f1d7888d2fd9bb89f19f10c0477bf branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-01T10:39:58-07:00 summary: bpo-36763: Add PyConfig_SetWideStringList() (GH-14444) (cherry picked from commit 36242fd871d0f0977e720d4fae5700774bd8c09a) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst M Doc/c-api/init_config.rst M Include/cpython/initconfig.h M Python/initconfig.c diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 0d94e6b8f27b..d2c1f9a2f3e3 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -25,6 +25,7 @@ Functions: * :c:func:`PyConfig_SetBytesArgv` * :c:func:`PyConfig_SetBytesString` * :c:func:`PyConfig_SetString` +* :c:func:`PyConfig_SetWideStringList` * :c:func:`PyPreConfig_InitIsolatedConfig` * :c:func:`PyPreConfig_InitPythonConfig` * :c:func:`PyStatus_Error` @@ -368,6 +369,12 @@ PyConfig Preinitialize Python if needed. + .. c:function:: PyStatus PyConfig_SetWideStringList(PyConfig *config, PyWideStringList *list, Py_ssize_t length, wchar_t **items) + + Set the list of wide strings *list* to *length* and *items*. + + Preinitialize Python if needed. + .. c:function:: PyStatus PyConfig_Read(PyConfig *config) Read all Python configuration. diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index 297fbf70792f..bd07a4829b47 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -422,6 +422,9 @@ PyAPI_FUNC(PyStatus) PyConfig_SetBytesArgv( PyAPI_FUNC(PyStatus) PyConfig_SetArgv(PyConfig *config, Py_ssize_t argc, wchar_t * const *argv); +PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config, + PyWideStringList *list, + Py_ssize_t length, wchar_t **items); #ifdef __cplusplus } diff --git a/Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst b/Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst new file mode 100644 index 000000000000..095d58116385 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst @@ -0,0 +1 @@ +Add :func:`PyConfig_SetWideStringList` function. diff --git a/Python/initconfig.c b/Python/initconfig.c index e791a0d6a09e..1c7078a6b570 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -732,7 +732,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2) } while (0) #define COPY_WSTRLIST(LIST) \ do { \ - if (_PyWideStringList_Copy(&config->LIST, &config2->LIST) < 0 ) { \ + if (_PyWideStringList_Copy(&config->LIST, &config2->LIST) < 0) { \ return _PyStatus_NO_MEMORY(); \ } \ } while (0) @@ -2277,6 +2277,23 @@ PyConfig_SetArgv(PyConfig *config, Py_ssize_t argc, wchar_t * const *argv) } +PyStatus +PyConfig_SetWideStringList(PyConfig *config, PyWideStringList *list, + Py_ssize_t length, wchar_t **items) +{ + PyStatus status = _Py_PreInitializeFromConfig(config, NULL); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + + PyWideStringList list2 = {.length = length, .items = items}; + if (_PyWideStringList_Copy(list, &list2) < 0) { + return _PyStatus_NO_MEMORY(); + } + return _PyStatus_OK(); +} + + /* Read the configuration into PyConfig from: * Command line arguments From webhook-mailer at python.org Mon Jul 1 13:45:11 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 01 Jul 2019 17:45:11 -0000 Subject: [Python-checkins] bpo-37469: Document usability of SimpleQueue with QueueHandler and QueueListener. (GH-14521) Message-ID: https://github.com/python/cpython/commit/e6b64b756f940147728ea7808fb686ffcae89176 commit: e6b64b756f940147728ea7808fb686ffcae89176 branch: master author: Vinay Sajip committer: GitHub date: 2019-07-01T18:45:07+01:00 summary: bpo-37469: Document usability of SimpleQueue with QueueHandler and QueueListener. (GH-14521) files: M Doc/library/logging.handlers.rst diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index df5bfefaa275..592d7e5daf93 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -992,9 +992,11 @@ possible, while any potentially slow operations (such as sending an email via .. class:: QueueHandler(queue) Returns a new instance of the :class:`QueueHandler` class. The instance is - initialized with the queue to send messages to. The queue can be any - queue-like object; it's used as-is by the :meth:`enqueue` method, which needs - to know how to send messages to it. + initialized with the queue to send messages to. The *queue* can be any + queue-like object; it's used as-is by the :meth:`enqueue` method, which + needs to know how to send messages to it. The queue is not *required* to + have the task tracking API, which means that you can use + :class:`~queue.SimpleQueue` instances for *queue*. .. method:: emit(record) @@ -1050,11 +1052,14 @@ possible, while any potentially slow operations (such as sending an email via initialized with the queue to send messages to and a list of handlers which will handle entries placed on the queue. The queue can be any queue-like object; it's passed as-is to the :meth:`dequeue` method, which needs - to know how to get messages from it. If ``respect_handler_level`` is ``True``, - a handler's level is respected (compared with the level for the message) when - deciding whether to pass messages to that handler; otherwise, the behaviour - is as in previous Python versions - to always pass each message to each - handler. + to know how to get messages from it. The queue is not *required* to have the + task tracking API (though it's used if available), which means that you can + use :class:`~queue.SimpleQueue` instances for *queue*. + + If ``respect_handler_level`` is ``True``, a handler's level is respected + (compared with the level for the message) when deciding whether to pass + messages to that handler; otherwise, the behaviour is as in previous Python + versions - to always pass each message to each handler. .. versionchanged:: 3.5 The ``respect_handler_levels`` argument was added. From webhook-mailer at python.org Mon Jul 1 13:52:52 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 01 Jul 2019 17:52:52 -0000 Subject: [Python-checkins] bpo-36763: Use PyConfig_Clear() (GH-14445) Message-ID: https://github.com/python/cpython/commit/67310023f299b5a2fad71fca449b46d280036690 commit: 67310023f299b5a2fad71fca449b46d280036690 branch: master author: Victor Stinner committer: GitHub date: 2019-07-01T19:52:45+02:00 summary: bpo-36763: Use PyConfig_Clear() (GH-14445) Stop using "static PyConfig", PyConfig must now always use dynamically allocated strings: use PyConfig_SetString(), PyConfig_SetArgv() and PyConfig_Clear(). files: M Lib/test/test_embed.py M Modules/main.c M Programs/_freeze_importlib.c M Programs/_testembed.c diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index e1cf4be50668..b2cd55016e46 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -695,10 +695,19 @@ def test_init_from_config(self): 'pycache_prefix': 'conf_pycache_prefix', 'program_name': './conf_program_name', - 'argv': ['-c', 'arg2'], + 'argv': ['-c', 'arg2', ], 'parse_argv': 1, - 'xoptions': ['xoption1=3', 'xoption2=', 'xoption3'], - 'warnoptions': ['error::ResourceWarning', 'default::BytesWarning'], + 'xoptions': [ + 'config_xoption1=3', + 'config_xoption2=', + 'config_xoption3', + 'cmdline_xoption', + ], + 'warnoptions': [ + 'config_warnoption', + 'cmdline_warnoption', + 'default::BytesWarning', + ], 'run_command': 'pass\n', 'site_import': 0, diff --git a/Modules/main.c b/Modules/main.c index 853afedd7b90..b126f4554d41 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -62,7 +62,7 @@ pymain_init(const _PyArgv *args) PyConfig config; status = PyConfig_InitPythonConfig(&config); if (_PyStatus_EXCEPTION(status)) { - return status; + goto done; } /* pass NULL as the config: config is read from command line arguments, @@ -74,14 +74,18 @@ pymain_init(const _PyArgv *args) status = PyConfig_SetArgv(&config, args->argc, args->wchar_argv); } if (_PyStatus_EXCEPTION(status)) { - return status; + goto done; } status = Py_InitializeFromConfig(&config); if (_PyStatus_EXCEPTION(status)) { - return status; + goto done; } - return _PyStatus_OK(); + status = _PyStatus_OK(); + +done: + PyConfig_Clear(&config); + return status; } diff --git a/Programs/_freeze_importlib.c b/Programs/_freeze_importlib.c index 13375b0a3819..74735f279c58 100644 --- a/Programs/_freeze_importlib.c +++ b/Programs/_freeze_importlib.c @@ -88,7 +88,7 @@ main(int argc, char *argv[]) config.site_import = 0; status = PyConfig_SetString(&config, &config.program_name, - L"./_freeze_importlib"); + L"./_freeze_importlib"); if (PyStatus_Exception(status)) { PyConfig_Clear(&config); Py_ExitStatusException(status); diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 9633f4610b54..856144b85e17 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -329,6 +329,56 @@ static int test_init_initialize_config(void) } +static void config_set_string(PyConfig *config, wchar_t **config_str, const wchar_t *str) +{ + PyStatus status = PyConfig_SetString(config, config_str, str); + if (PyStatus_Exception(status)) { + PyConfig_Clear(config); + Py_ExitStatusException(status); + } +} + + +static void config_set_argv(PyConfig *config, Py_ssize_t argc, wchar_t * const *argv) +{ + PyStatus status = PyConfig_SetArgv(config, argc, argv); + if (PyStatus_Exception(status)) { + PyConfig_Clear(config); + Py_ExitStatusException(status); + } +} + + +static void +config_set_wide_string_list(PyConfig *config, PyWideStringList *list, + Py_ssize_t length, wchar_t **items) +{ + PyStatus status = PyConfig_SetWideStringList(config, list, length, items); + if (PyStatus_Exception(status)) { + PyConfig_Clear(config); + Py_ExitStatusException(status); + } +} + + +static void config_set_program_name(PyConfig *config) +{ + /* Use path starting with "./" avoids a search along the PATH */ + const wchar_t *program_name = L"./_testembed"; + config_set_string(config, &config->program_name, program_name); +} + + +static void init_from_config_clear(PyConfig *config) +{ + PyStatus status = Py_InitializeFromConfig(config); + PyConfig_Clear(config); + if (PyStatus_Exception(status)) { + Py_ExitStatusException(status); + } +} + + static int check_init_compat_config(int preinit) { PyStatus status; @@ -345,12 +395,8 @@ static int check_init_compat_config(int preinit) PyConfig config; _PyConfig_InitCompatConfig(&config); - config.program_name = L"./_testembed"; - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); dump_config(); Py_Finalize(); @@ -438,7 +484,6 @@ static int test_init_from_config(void) Py_ExitStatusException(status); } - /* Test Py_InitializeFromConfig() */ PyConfig config; _PyConfig_InitCompatConfig(&config); config.install_signal_handlers = 0; @@ -468,34 +513,37 @@ static int test_init_from_config(void) config.malloc_stats = 1; putenv("PYTHONPYCACHEPREFIX=env_pycache_prefix"); - config.pycache_prefix = L"conf_pycache_prefix"; + config_set_string(&config, &config.pycache_prefix, L"conf_pycache_prefix"); Py_SetProgramName(L"./globalvar"); - config.program_name = L"./conf_program_name"; + config_set_string(&config, &config.program_name, L"./conf_program_name"); - static wchar_t* argv[] = { + wchar_t* argv[] = { L"python3", + L"-W", + L"cmdline_warnoption", + L"-X", + L"cmdline_xoption", L"-c", L"pass", L"arg2", }; - config.argv.length = Py_ARRAY_LENGTH(argv); - config.argv.items = argv; + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); config.parse_argv = 1; - static wchar_t* xoptions[3] = { - L"xoption1=3", - L"xoption2=", - L"xoption3", + wchar_t* xoptions[3] = { + L"config_xoption1=3", + L"config_xoption2=", + L"config_xoption3", }; - config.xoptions.length = Py_ARRAY_LENGTH(xoptions); - config.xoptions.items = xoptions; + config_set_wide_string_list(&config, &config.xoptions, + Py_ARRAY_LENGTH(xoptions), xoptions); - static wchar_t* warnoptions[1] = { - L"error::ResourceWarning", + wchar_t* warnoptions[1] = { + L"config_warnoption", }; - config.warnoptions.length = Py_ARRAY_LENGTH(warnoptions); - config.warnoptions.items = warnoptions; + config_set_wide_string_list(&config, &config.warnoptions, + Py_ARRAY_LENGTH(warnoptions), warnoptions); /* FIXME: test pythonpath_env */ /* FIXME: test home */ @@ -544,22 +592,20 @@ static int test_init_from_config(void) Force it to 0 through the config. */ config.legacy_windows_stdio = 0; #endif - config.stdio_encoding = L"iso8859-1"; - config.stdio_errors = L"replace"; + config_set_string(&config, &config.stdio_encoding, L"iso8859-1"); + config_set_string(&config, &config.stdio_errors, L"replace"); putenv("PYTHONNOUSERSITE="); Py_NoUserSiteDirectory = 0; config.user_site_directory = 0; - config.check_hash_pycs_mode = L"always"; + config_set_string(&config, &config.check_hash_pycs_mode, L"always"); Py_FrozenFlag = 0; config.pathconfig_warnings = 0; - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -576,7 +622,9 @@ static int check_init_parse_argv(int parse_argv) Py_ExitStatusException(status); } - static wchar_t* argv[] = { + config.parse_argv = parse_argv; + + wchar_t* argv[] = { L"./argv0", L"-E", L"-c", @@ -585,15 +633,9 @@ static int check_init_parse_argv(int parse_argv) L"-v", L"arg3", }; + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + init_from_config_clear(&config); - config.argv.length = Py_ARRAY_LENGTH(argv); - config.argv.items = argv; - config.parse_argv = parse_argv; - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } dump_config(); Py_Finalize(); return 0; @@ -664,12 +706,10 @@ static int test_init_python_env(void) if (PyStatus_Exception(status)) { Py_ExitStatusException(status); } - config.program_name = L"./_testembed"; - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -723,14 +763,10 @@ static int test_init_isolated_flag(void) Py_IsolatedFlag = 0; config.isolated = 1; - /* Use path starting with "./" avoids a search along the PATH */ - config.program_name = L"./_testembed"; - + config_set_program_name(&config); set_all_env_vars(); - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -753,13 +789,10 @@ static int test_preinit_isolated1(void) PyConfig config; _PyConfig_InitCompatConfig(&config); - config.program_name = L"./_testembed"; - + config_set_program_name(&config); set_all_env_vars(); - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -787,14 +820,10 @@ static int test_preinit_isolated2(void) Py_IsolatedFlag = 0; config.isolated = 1; - /* Use path starting with "./" avoids a search along the PATH */ - config.program_name = L"./_testembed"; - + config_set_program_name(&config); set_all_env_vars(); - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -819,44 +848,28 @@ static int test_preinit_dont_parse_argv(void) L"script.py"}; status = Py_PreInitializeFromArgs(&preconfig, Py_ARRAY_LENGTH(argv), argv); if (PyStatus_Exception(status)) { - goto failed; + Py_ExitStatusException(status); } PyConfig config; status = PyConfig_InitIsolatedConfig(&config); if (PyStatus_Exception(status)) { - goto failed; + PyConfig_Clear(&config); + Py_ExitStatusException(status); } config.isolated = 0; /* Pre-initialize implicitly using argv: make sure that -X dev is used to configure the allocation in preinitialization */ - status = PyConfig_SetArgv(&config, Py_ARRAY_LENGTH(argv), argv); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = PyConfig_SetString(&config, &config.program_name, - L"./_testembed"); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - goto failed; - } - PyConfig_Clear(&config); + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + config_set_program_name(&config); + init_from_config_clear(&config); dump_config(); Py_Finalize(); return 0; - -failed: - PyConfig_Clear(&config); - Py_ExitStatusException(status); } @@ -867,36 +880,20 @@ static int test_preinit_parse_argv(void) status = PyConfig_InitPythonConfig(&config); if (PyStatus_Exception(status)) { - goto failed; + PyConfig_Clear(&config); + Py_ExitStatusException(status); } /* Pre-initialize implicitly using argv: make sure that -X dev is used to configure the allocation in preinitialization */ wchar_t *argv[] = {L"python3", L"-X", L"dev", L"script.py"}; - status = PyConfig_SetArgv(&config, Py_ARRAY_LENGTH(argv), argv); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = PyConfig_SetString(&config, &config.program_name, - L"./_testembed"); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - goto failed; - } - PyConfig_Clear(&config); + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + config_set_program_name(&config); + init_from_config_clear(&config); dump_config(); Py_Finalize(); return 0; - -failed: - PyConfig_Clear(&config); - Py_ExitStatusException(status); } @@ -955,13 +952,8 @@ static int check_preinit_isolated_config(int preinit) PyConfig_Clear(&config); Py_ExitStatusException(status); } - config.program_name = L"./_testembed"; - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - PyConfig_Clear(&config); - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); rt_preconfig = &_PyRuntime.preconfig; assert(rt_preconfig->isolated == 1); @@ -1017,12 +1009,9 @@ static int check_init_python_config(int preinit) if (PyStatus_Exception(status)) { Py_ExitStatusException(status); } - config.program_name = L"./_testembed"; + config_set_program_name(&config); + init_from_config_clear(&config); - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } dump_config(); Py_Finalize(); return 0; @@ -1061,11 +1050,8 @@ static int test_init_dont_configure_locale(void) if (PyStatus_Exception(status)) { Py_ExitStatusException(status); } - config.program_name = L"./_testembed"; - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); dump_config(); Py_Finalize(); @@ -1084,11 +1070,9 @@ static int test_init_dev_mode(void) putenv("PYTHONFAULTHANDLER="); putenv("PYTHONMALLOC="); config.dev_mode = 1; - config.program_name = L"./_testembed"; - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -1278,16 +1262,9 @@ static int test_init_read_set(void) } /* override executable computed by PyConfig_Read() */ - status = PyConfig_SetString(&config, &config.executable, L"my_executable"); - if (PyStatus_Exception(status)) { - goto fail; - } + config_set_string(&config, &config.executable, L"my_executable"); + init_from_config_clear(&config); - status = Py_InitializeFromConfig(&config); - PyConfig_Clear(&config); - if (PyStatus_Exception(status)) { - goto fail; - } dump_config(); Py_Finalize(); return 0; @@ -1297,19 +1274,18 @@ static int test_init_read_set(void) } -wchar_t *init_main_argv[] = { - L"python3", L"-c", - (L"import _testinternalcapi, json; " - L"print(json.dumps(_testinternalcapi.get_configs()))"), - L"arg2"}; - - static void configure_init_main(PyConfig *config) { - config->argv.length = Py_ARRAY_LENGTH(init_main_argv); - config->argv.items = init_main_argv; + wchar_t* argv[] = { + L"python3", L"-c", + (L"import _testinternalcapi, json; " + L"print(json.dumps(_testinternalcapi.get_configs()))"), + L"arg2"}; + config->parse_argv = 1; - config->program_name = L"./python3"; + + config_set_argv(config, Py_ARRAY_LENGTH(argv), argv); + config_set_string(config, &config->program_name, L"./python3"); } @@ -1322,11 +1298,7 @@ static int test_init_run_main(void) Py_ExitStatusException(status); } configure_init_main(&config); - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); return Py_RunMain(); } @@ -1343,11 +1315,7 @@ static int test_init_main(void) } configure_init_main(&config); config._init_main = 0; - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); /* sys.stdout don't exist yet: it is created by _Py_InitializeMain() */ int res = PyRun_SimpleString( @@ -1374,35 +1342,19 @@ static int test_run_main(void) status = PyConfig_InitPythonConfig(&config); if (PyStatus_Exception(status)) { - goto failed; + PyConfig_Clear(&config); + Py_ExitStatusException(status); } wchar_t *argv[] = {L"python3", L"-c", (L"import sys; " L"print(f'Py_RunMain(): sys.argv={sys.argv}')"), L"arg2"}; - status = PyConfig_SetArgv(&config, Py_ARRAY_LENGTH(argv), argv); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = PyConfig_SetString(&config, &config.program_name, - L"./python3"); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - goto failed; - } - PyConfig_Clear(&config); + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + config_set_string(&config, &config.program_name, L"./python3"); + init_from_config_clear(&config); return Py_RunMain(); - -failed: - PyConfig_Clear(&config); - Py_ExitStatusException(status); } From webhook-mailer at python.org Mon Jul 1 14:02:43 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 01 Jul 2019 18:02:43 -0000 Subject: [Python-checkins] Remove unused imports in tests (GH-14518) (GH-14522) Message-ID: https://github.com/python/cpython/commit/e34b5f4d6483187969d5149c801d056b72ef2ddb commit: e34b5f4d6483187969d5149c801d056b72ef2ddb branch: 3.7 author: Victor Stinner committer: GitHub date: 2019-07-01T20:02:39+02:00 summary: Remove unused imports in tests (GH-14518) (GH-14522) (cherry picked from commit 8f4ef3b019ce380022018587571b0f970e668de3) files: M Lib/test/libregrtest/main.py M Lib/test/support/__init__.py M Lib/test/test_aifc.py M Lib/test/test_argparse.py M Lib/test/test_asynchat.py M Lib/test/test_asyncio/test_pep492.py M Lib/test/test_asyncio/test_sslproto.py M Lib/test/test_asyncio/test_unix_events.py M Lib/test/test_c_locale_coercion.py M Lib/test/test_capi.py M Lib/test/test_cmd_line.py M Lib/test/test_collections.py M Lib/test/test_contextlib.py M Lib/test/test_coroutines.py M Lib/test/test_docxmlrpc.py M Lib/test/test_email/test_policy.py M Lib/test/test_exceptions.py M Lib/test/test_faulthandler.py M Lib/test/test_fork1.py M Lib/test/test_frozen.py M Lib/test/test_gdb.py M Lib/test/test_generators.py M Lib/test/test_gettext.py M Lib/test/test_grammar.py M Lib/test/test_imaplib.py M Lib/test/test_import/__init__.py M Lib/test/test_importlib/test_locks.py M Lib/test/test_locale.py M Lib/test/test_netrc.py M Lib/test/test_os.py M Lib/test/test_posixpath.py M Lib/test/test_pyclbr.py M Lib/test/test_pydoc.py M Lib/test/test_queue.py M Lib/test/test_resource.py M Lib/test/test_runpy.py M Lib/test/test_sax.py M Lib/test/test_signal.py M Lib/test/test_smtplib.py M Lib/test/test_subprocess.py M Lib/test/test_thread.py M Lib/test/test_threaded_import.py M Lib/test/test_threadedtempfile.py M Lib/test/test_time.py M Lib/test/test_tokenize.py M Lib/test/test_traceback.py M Lib/test/test_utf8_mode.py M Lib/test/test_venv.py diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 1dfbe47a2fab..e2274254fdb8 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -587,7 +587,6 @@ def create_temp_dir(self): def cleanup(self): import glob - import shutil path = os.path.join(self.tmp_dir, 'test_python_*') print("Cleanup %s directory" % self.tmp_dir) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 87bfa9f54627..6d10e8b576e4 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -5,7 +5,6 @@ import collections.abc import contextlib -import datetime import errno import faulthandler import fnmatch @@ -13,7 +12,6 @@ import gc import importlib import importlib.util -import io import logging.handlers import nntplib import os diff --git a/Lib/test/test_aifc.py b/Lib/test/test_aifc.py index ff52f5b6feb8..e82cfc1a4690 100644 --- a/Lib/test/test_aifc.py +++ b/Lib/test/test_aifc.py @@ -7,7 +7,6 @@ import sys import struct import aifc -import warnings class AifcTest(audiotests.AudioWriteTests, diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 51f0effaf2ff..0c342e2e4e0d 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1,6 +1,5 @@ # Author: Steven J. Bethard . -import codecs import inspect import os import shutil diff --git a/Lib/test/test_asynchat.py b/Lib/test/test_asynchat.py index 1d147c741961..14c0ec43d422 100644 --- a/Lib/test/test_asynchat.py +++ b/Lib/test/test_asynchat.py @@ -7,7 +7,6 @@ import errno import socket import sys -import _thread as thread import threading import time import unittest diff --git a/Lib/test/test_asyncio/test_pep492.py b/Lib/test/test_asyncio/test_pep492.py index f2d588f54445..ac3ae6811fb5 100644 --- a/Lib/test/test_asyncio/test_pep492.py +++ b/Lib/test/test_asyncio/test_pep492.py @@ -4,7 +4,6 @@ import types import unittest -from test import support from unittest import mock import asyncio diff --git a/Lib/test/test_asyncio/test_sslproto.py b/Lib/test/test_asyncio/test_sslproto.py index 866ef81fb2ee..3b9c12f24ed3 100644 --- a/Lib/test/test_asyncio/test_sslproto.py +++ b/Lib/test/test_asyncio/test_sslproto.py @@ -15,7 +15,6 @@ from asyncio import log from asyncio import protocols from asyncio import sslproto -from asyncio import tasks from test.test_asyncio import utils as test_utils from test.test_asyncio import functional as func_tests diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index ec171fa83da3..51d474cf152c 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -22,7 +22,6 @@ import asyncio from asyncio import log -from asyncio import base_events from asyncio import events from asyncio import unix_events from test.test_asyncio import utils as test_utils diff --git a/Lib/test/test_c_locale_coercion.py b/Lib/test/test_c_locale_coercion.py index 134f07a17ec1..f2351fa5a2ea 100644 --- a/Lib/test/test_c_locale_coercion.py +++ b/Lib/test/test_c_locale_coercion.py @@ -2,7 +2,6 @@ import locale import os -import shutil import subprocess import sys import sysconfig @@ -10,10 +9,8 @@ from collections import namedtuple import test.support -from test.support.script_helper import ( - run_python_until_end, - interpreter_requires_environment, -) +from test.support.script_helper import run_python_until_end + # Set the list of ways we expect to be able to ask for the "C" locale EXPECTED_C_LOCALE_EQUIVALENTS = ["C", "invalid.ascii"] diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index d94ee0227c87..6eb3bd967b86 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -8,7 +8,6 @@ import re import subprocess import sys -import sysconfig import textwrap import threading import time diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 95cdc8db7efb..f90bfb067483 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -5,7 +5,6 @@ import os import subprocess import sys -import sysconfig import tempfile import unittest from test import support diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 16735b815e53..1aca9facda69 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -3,11 +3,9 @@ import collections import copy import doctest -import keyword import operator import pickle from random import choice, randrange -import re import string import sys from test import support diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 30c2e27b3c7e..ced2290288db 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -1,6 +1,5 @@ """Unit tests for contextlib.py, and other context managers.""" -import asyncio import io import sys import tempfile diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index ac24f3976738..f32320956bcc 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2,7 +2,6 @@ import copy import inspect import pickle -import re import sys import types import unittest diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py index f077f05f5b4f..116e626740df 100644 --- a/Lib/test/test_docxmlrpc.py +++ b/Lib/test/test_docxmlrpc.py @@ -2,7 +2,6 @@ import http.client import sys import threading -from test import support import unittest def make_request_and_skipIf(condition, reason): diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index 0aea934df434..1e39aa062c0a 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -1,5 +1,4 @@ import io -import sys import types import textwrap import unittest diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 9d10df5f9425..0196c4d0005b 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -9,7 +9,7 @@ import errno from test.support import (TESTFN, captured_stderr, check_impl_detail, - check_warnings, cpython_only, gc_collect, run_unittest, + check_warnings, cpython_only, gc_collect, no_tracing, unlink, import_module, script_helper, SuppressCrashReport) class NaiveException(Exception): diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 700b7ad6b885..5283596cebe4 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -9,7 +9,6 @@ from test import support from test.support import script_helper, is_android import tempfile -import threading import unittest from textwrap import dedent diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index 9ca9724c4c91..2ab856ff5690 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -10,8 +10,7 @@ import unittest from test.fork_wait import ForkWait -from test.support import (reap_children, get_attribute, - import_module, verbose) +from test.support import reap_children, get_attribute, verbose # Skip test if fork does not exist. diff --git a/Lib/test/test_frozen.py b/Lib/test/test_frozen.py index a7c748422b1d..142f17d518e7 100644 --- a/Lib/test/test_frozen.py +++ b/Lib/test/test_frozen.py @@ -13,7 +13,6 @@ import sys import unittest from test.support import captured_stdout -from importlib import util class TestFrozen(unittest.TestCase): diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 7758e1fcd86c..b78c0845b1df 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -3,7 +3,6 @@ # The code for testing gdb was adapted from similar work in Unladen Swallow's # Lib/test/test_jit_gdb.py -import locale import os import platform import re diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 7a21cb7e954a..c45086562d36 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -3,7 +3,6 @@ import pickle import sys import unittest -import warnings import weakref import inspect diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index b5ed05eab7bb..fac38000e635 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -1,7 +1,6 @@ import os import base64 import gettext -import locale import unittest from test import support diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 88c22b89d444..241ac853d73c 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -4,7 +4,6 @@ from test.support import check_syntax_error import inspect import unittest -import sys # testing import * from sys import * @@ -12,7 +11,6 @@ # with import machinery import test.ann_module as ann_module import typing -from collections import ChainMap from test import ann_module2 import test diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 9305e47ee993..8ab532af3f0d 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -1,7 +1,6 @@ from test import support from contextlib import contextmanager -import errno import imaplib import os.path import socketserver diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 1fc4de11e178..4c4f0d9efc03 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -5,7 +5,6 @@ import builtins import marshal import os -import platform import py_compile import random import stat @@ -20,9 +19,9 @@ import test.support from test.support import ( - EnvironmentVarGuard, TESTFN, check_warnings, forget, is_jython, - make_legacy_pyc, rmtree, run_unittest, swap_attr, swap_item, temp_umask, - unlink, unload, create_empty_file, cpython_only, TESTFN_UNENCODABLE, + TESTFN, forget, is_jython, + make_legacy_pyc, rmtree, swap_attr, swap_item, temp_umask, + unlink, unload, cpython_only, TESTFN_UNENCODABLE, temp_dir, DirsOnSysPath) from test.support import script_helper from test.test_importlib.util import uncache diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py index d86172ab5837..21794d911ef6 100644 --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -4,7 +4,6 @@ import sys import threading -import unittest import weakref from test import support diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index e2c2178ae6cc..792a15c50f92 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -3,7 +3,7 @@ import locale import sys import codecs -import warnings + class BaseLocalizedTest(unittest.TestCase): # diff --git a/Lib/test/test_netrc.py b/Lib/test/test_netrc.py index f59e5371acad..ae53988c45a6 100644 --- a/Lib/test/test_netrc.py +++ b/Lib/test/test_netrc.py @@ -1,5 +1,4 @@ import netrc, os, unittest, sys, tempfile, textwrap -from unittest import mock from test import support diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 8032da053067..1ec9a6a95091 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -9,7 +9,6 @@ import decimal import errno import fractions -import getpass import itertools import locale import mmap diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index e73b31cb648b..6bca89003a97 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -1,7 +1,6 @@ import os import posixpath import unittest -import warnings from posixpath import realpath, abspath, dirname, basename from test import support, test_genericpath from test.support import FakePath @@ -12,6 +11,7 @@ except ImportError: posix = None + # An absolute path to a temporary filename for testing. We can't rely on TESTFN # being an absolute path, so we need this. diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index eaab591f74ef..0489b41d7c7b 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -3,14 +3,11 @@ Nick Mathewson ''' -import os import sys from textwrap import dedent from types import FunctionType, MethodType, BuiltinFunctionType import pyclbr from unittest import TestCase, main as unittest_main -from test import support -from functools import partial StaticMethodType = type(staticmethod(lambda: None)) ClassMethodType = type(classmethod(lambda c: None)) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 198cea93eb52..8e30b4c8f675 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -21,7 +21,6 @@ import xml.etree import xml.etree.ElementTree import textwrap -import threading from io import StringIO from collections import namedtuple from test.support.script_helper import assert_python_ok diff --git a/Lib/test/test_queue.py b/Lib/test/test_queue.py index 1a8d5f8856c5..c8528f97741d 100644 --- a/Lib/test/test_queue.py +++ b/Lib/test/test_queue.py @@ -1,10 +1,8 @@ # Some simple queue module tests, plus some failure conditions # to ensure the Queue locks remain stable. -import collections import itertools import queue import random -import sys import threading import time import unittest diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py index b07eb73b2aeb..62c7963fe699 100644 --- a/Lib/test/test_resource.py +++ b/Lib/test/test_resource.py @@ -1,6 +1,5 @@ import contextlib import sys -import os import unittest from test import support import time diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py index 02b4d62567e5..0da6f3a60430 100644 --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -11,8 +11,7 @@ from test.support import ( forget, make_legacy_pyc, unload, verbose, no_tracing, create_empty_file, temp_dir) -from test.support.script_helper import ( - make_pkg, make_script, make_zip_pkg, make_zip_script) +from test.support.script_helper import make_script, make_zip_script import runpy diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py index 3044960a0ed1..251a03a54ddc 100644 --- a/Lib/test/test_sax.py +++ b/Lib/test/test_sax.py @@ -17,7 +17,6 @@ from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl from io import BytesIO, StringIO import codecs -import gc import os.path import shutil from urllib.error import URLError diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 406684bdbec7..c3f5b148448c 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -5,7 +5,6 @@ import statistics import subprocess import sys -import threading import time import unittest from test import support diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index b4149d3ef007..4add8fcabfd1 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -19,7 +19,7 @@ import unittest from test import support, mock_socket -from test.support import HOST, HOSTv4, HOSTv6 +from test.support import HOST from unittest.mock import Mock diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 8419061b2a90..36cf22a9f898 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -3,7 +3,6 @@ from test import support import subprocess import sys -import platform import signal import io import itertools @@ -20,18 +19,12 @@ import textwrap from test.support import FakePath -try: - import ctypes -except ImportError: - ctypes = None -else: - import ctypes.util - try: import _testcapi except ImportError: _testcapi = None + if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") diff --git a/Lib/test/test_thread.py b/Lib/test/test_thread.py index 64ffe4605298..f4eb830cf6d7 100644 --- a/Lib/test/test_thread.py +++ b/Lib/test/test_thread.py @@ -4,7 +4,6 @@ from test import support import _thread as thread import time -import sys import weakref from test import lock_tests diff --git a/Lib/test/test_threaded_import.py b/Lib/test/test_threaded_import.py index 035344be4b89..8607f363db21 100644 --- a/Lib/test/test_threaded_import.py +++ b/Lib/test/test_threaded_import.py @@ -15,7 +15,7 @@ import unittest from unittest import mock from test.support import ( - verbose, import_module, run_unittest, TESTFN, reap_threads, + verbose, run_unittest, TESTFN, reap_threads, forget, unlink, rmtree, start_threads) def task(N, done, done_tasks, errors): diff --git a/Lib/test/test_threadedtempfile.py b/Lib/test/test_threadedtempfile.py index f3d4ba36377d..e1d7a10179cc 100644 --- a/Lib/test/test_threadedtempfile.py +++ b/Lib/test/test_threadedtempfile.py @@ -13,19 +13,22 @@ provoking a 2.0 failure under Linux. """ -NUM_THREADS = 20 -FILES_PER_THREAD = 50 - import tempfile -from test.support import start_threads, import_module +from test.support import start_threads import unittest import io import threading from traceback import print_exc + +NUM_THREADS = 20 +FILES_PER_THREAD = 50 + + startEvent = threading.Event() + class TempFileGreedy(threading.Thread): error_count = 0 ok_count = 0 diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 4e31abf4ec8e..35952799ba6d 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -9,7 +9,6 @@ import time import threading import unittest -import warnings try: import _testcapi except ImportError: diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index d596f7db61ab..7457a7ed0434 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1,7 +1,7 @@ from test import support from tokenize import (tokenize, _tokenize, untokenize, NUMBER, NAME, OP, STRING, ENDMARKER, ENCODING, tok_name, detect_encoding, - open as tokenize_open, Untokenizer, generate_tokens, + open as tokenize_open, Untokenizer, NEWLINE) from io import BytesIO import unittest diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 8a3aa8a8648f..8749d095ddee 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -110,7 +110,7 @@ def test_encoded_file(self): # Test that tracebacks are correctly printed for encoded source files: # - correct line number (Issue2384) # - respect file encoding (Issue3975) - import tempfile, sys, subprocess, os + import sys, subprocess # The spawned subprocess has its stdout redirected to a PIPE, and its # encoding may be different from the current interpreter, on Windows diff --git a/Lib/test/test_utf8_mode.py b/Lib/test/test_utf8_mode.py index 06fe1979ddcc..a7d1dc940ad6 100644 --- a/Lib/test/test_utf8_mode.py +++ b/Lib/test/test_utf8_mode.py @@ -3,7 +3,6 @@ """ import locale -import os import sys import textwrap import unittest diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 67f9f46e65e0..3821d9accfdf 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -15,7 +15,6 @@ import tempfile from test.support import (captured_stdout, captured_stderr, requires_zlib, can_symlink, EnvironmentVarGuard, rmtree) -import threading import unittest import venv From webhook-mailer at python.org Mon Jul 1 14:28:59 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 01 Jul 2019 18:28:59 -0000 Subject: [Python-checkins] bpo-36763: Use PyConfig_Clear() (GH-14445) Message-ID: https://github.com/python/cpython/commit/4c227e6a56a9704fe5b4e4509071796f3359f4bb commit: 4c227e6a56a9704fe5b4e4509071796f3359f4bb branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-01T11:28:55-07:00 summary: bpo-36763: Use PyConfig_Clear() (GH-14445) Stop using "static PyConfig", PyConfig must now always use dynamically allocated strings: use PyConfig_SetString(), PyConfig_SetArgv() and PyConfig_Clear(). (cherry picked from commit 67310023f299b5a2fad71fca449b46d280036690) Co-authored-by: Victor Stinner files: M Lib/test/test_embed.py M Modules/main.c M Programs/_freeze_importlib.c M Programs/_testembed.c diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index ed1100cfabc5..e31d66d78c85 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -695,10 +695,19 @@ def test_init_from_config(self): 'pycache_prefix': 'conf_pycache_prefix', 'program_name': './conf_program_name', - 'argv': ['-c', 'arg2'], + 'argv': ['-c', 'arg2', ], 'parse_argv': 1, - 'xoptions': ['xoption1=3', 'xoption2=', 'xoption3'], - 'warnoptions': ['error::ResourceWarning', 'default::BytesWarning'], + 'xoptions': [ + 'config_xoption1=3', + 'config_xoption2=', + 'config_xoption3', + 'cmdline_xoption', + ], + 'warnoptions': [ + 'config_warnoption', + 'cmdline_warnoption', + 'default::BytesWarning', + ], 'run_command': 'pass\n', 'site_import': 0, diff --git a/Modules/main.c b/Modules/main.c index 853afedd7b90..b126f4554d41 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -62,7 +62,7 @@ pymain_init(const _PyArgv *args) PyConfig config; status = PyConfig_InitPythonConfig(&config); if (_PyStatus_EXCEPTION(status)) { - return status; + goto done; } /* pass NULL as the config: config is read from command line arguments, @@ -74,14 +74,18 @@ pymain_init(const _PyArgv *args) status = PyConfig_SetArgv(&config, args->argc, args->wchar_argv); } if (_PyStatus_EXCEPTION(status)) { - return status; + goto done; } status = Py_InitializeFromConfig(&config); if (_PyStatus_EXCEPTION(status)) { - return status; + goto done; } - return _PyStatus_OK(); + status = _PyStatus_OK(); + +done: + PyConfig_Clear(&config); + return status; } diff --git a/Programs/_freeze_importlib.c b/Programs/_freeze_importlib.c index 13375b0a3819..74735f279c58 100644 --- a/Programs/_freeze_importlib.c +++ b/Programs/_freeze_importlib.c @@ -88,7 +88,7 @@ main(int argc, char *argv[]) config.site_import = 0; status = PyConfig_SetString(&config, &config.program_name, - L"./_freeze_importlib"); + L"./_freeze_importlib"); if (PyStatus_Exception(status)) { PyConfig_Clear(&config); Py_ExitStatusException(status); diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 9633f4610b54..856144b85e17 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -329,6 +329,56 @@ static int test_init_initialize_config(void) } +static void config_set_string(PyConfig *config, wchar_t **config_str, const wchar_t *str) +{ + PyStatus status = PyConfig_SetString(config, config_str, str); + if (PyStatus_Exception(status)) { + PyConfig_Clear(config); + Py_ExitStatusException(status); + } +} + + +static void config_set_argv(PyConfig *config, Py_ssize_t argc, wchar_t * const *argv) +{ + PyStatus status = PyConfig_SetArgv(config, argc, argv); + if (PyStatus_Exception(status)) { + PyConfig_Clear(config); + Py_ExitStatusException(status); + } +} + + +static void +config_set_wide_string_list(PyConfig *config, PyWideStringList *list, + Py_ssize_t length, wchar_t **items) +{ + PyStatus status = PyConfig_SetWideStringList(config, list, length, items); + if (PyStatus_Exception(status)) { + PyConfig_Clear(config); + Py_ExitStatusException(status); + } +} + + +static void config_set_program_name(PyConfig *config) +{ + /* Use path starting with "./" avoids a search along the PATH */ + const wchar_t *program_name = L"./_testembed"; + config_set_string(config, &config->program_name, program_name); +} + + +static void init_from_config_clear(PyConfig *config) +{ + PyStatus status = Py_InitializeFromConfig(config); + PyConfig_Clear(config); + if (PyStatus_Exception(status)) { + Py_ExitStatusException(status); + } +} + + static int check_init_compat_config(int preinit) { PyStatus status; @@ -345,12 +395,8 @@ static int check_init_compat_config(int preinit) PyConfig config; _PyConfig_InitCompatConfig(&config); - config.program_name = L"./_testembed"; - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); dump_config(); Py_Finalize(); @@ -438,7 +484,6 @@ static int test_init_from_config(void) Py_ExitStatusException(status); } - /* Test Py_InitializeFromConfig() */ PyConfig config; _PyConfig_InitCompatConfig(&config); config.install_signal_handlers = 0; @@ -468,34 +513,37 @@ static int test_init_from_config(void) config.malloc_stats = 1; putenv("PYTHONPYCACHEPREFIX=env_pycache_prefix"); - config.pycache_prefix = L"conf_pycache_prefix"; + config_set_string(&config, &config.pycache_prefix, L"conf_pycache_prefix"); Py_SetProgramName(L"./globalvar"); - config.program_name = L"./conf_program_name"; + config_set_string(&config, &config.program_name, L"./conf_program_name"); - static wchar_t* argv[] = { + wchar_t* argv[] = { L"python3", + L"-W", + L"cmdline_warnoption", + L"-X", + L"cmdline_xoption", L"-c", L"pass", L"arg2", }; - config.argv.length = Py_ARRAY_LENGTH(argv); - config.argv.items = argv; + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); config.parse_argv = 1; - static wchar_t* xoptions[3] = { - L"xoption1=3", - L"xoption2=", - L"xoption3", + wchar_t* xoptions[3] = { + L"config_xoption1=3", + L"config_xoption2=", + L"config_xoption3", }; - config.xoptions.length = Py_ARRAY_LENGTH(xoptions); - config.xoptions.items = xoptions; + config_set_wide_string_list(&config, &config.xoptions, + Py_ARRAY_LENGTH(xoptions), xoptions); - static wchar_t* warnoptions[1] = { - L"error::ResourceWarning", + wchar_t* warnoptions[1] = { + L"config_warnoption", }; - config.warnoptions.length = Py_ARRAY_LENGTH(warnoptions); - config.warnoptions.items = warnoptions; + config_set_wide_string_list(&config, &config.warnoptions, + Py_ARRAY_LENGTH(warnoptions), warnoptions); /* FIXME: test pythonpath_env */ /* FIXME: test home */ @@ -544,22 +592,20 @@ static int test_init_from_config(void) Force it to 0 through the config. */ config.legacy_windows_stdio = 0; #endif - config.stdio_encoding = L"iso8859-1"; - config.stdio_errors = L"replace"; + config_set_string(&config, &config.stdio_encoding, L"iso8859-1"); + config_set_string(&config, &config.stdio_errors, L"replace"); putenv("PYTHONNOUSERSITE="); Py_NoUserSiteDirectory = 0; config.user_site_directory = 0; - config.check_hash_pycs_mode = L"always"; + config_set_string(&config, &config.check_hash_pycs_mode, L"always"); Py_FrozenFlag = 0; config.pathconfig_warnings = 0; - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -576,7 +622,9 @@ static int check_init_parse_argv(int parse_argv) Py_ExitStatusException(status); } - static wchar_t* argv[] = { + config.parse_argv = parse_argv; + + wchar_t* argv[] = { L"./argv0", L"-E", L"-c", @@ -585,15 +633,9 @@ static int check_init_parse_argv(int parse_argv) L"-v", L"arg3", }; + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + init_from_config_clear(&config); - config.argv.length = Py_ARRAY_LENGTH(argv); - config.argv.items = argv; - config.parse_argv = parse_argv; - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } dump_config(); Py_Finalize(); return 0; @@ -664,12 +706,10 @@ static int test_init_python_env(void) if (PyStatus_Exception(status)) { Py_ExitStatusException(status); } - config.program_name = L"./_testembed"; - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -723,14 +763,10 @@ static int test_init_isolated_flag(void) Py_IsolatedFlag = 0; config.isolated = 1; - /* Use path starting with "./" avoids a search along the PATH */ - config.program_name = L"./_testembed"; - + config_set_program_name(&config); set_all_env_vars(); - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -753,13 +789,10 @@ static int test_preinit_isolated1(void) PyConfig config; _PyConfig_InitCompatConfig(&config); - config.program_name = L"./_testembed"; - + config_set_program_name(&config); set_all_env_vars(); - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -787,14 +820,10 @@ static int test_preinit_isolated2(void) Py_IsolatedFlag = 0; config.isolated = 1; - /* Use path starting with "./" avoids a search along the PATH */ - config.program_name = L"./_testembed"; - + config_set_program_name(&config); set_all_env_vars(); - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -819,44 +848,28 @@ static int test_preinit_dont_parse_argv(void) L"script.py"}; status = Py_PreInitializeFromArgs(&preconfig, Py_ARRAY_LENGTH(argv), argv); if (PyStatus_Exception(status)) { - goto failed; + Py_ExitStatusException(status); } PyConfig config; status = PyConfig_InitIsolatedConfig(&config); if (PyStatus_Exception(status)) { - goto failed; + PyConfig_Clear(&config); + Py_ExitStatusException(status); } config.isolated = 0; /* Pre-initialize implicitly using argv: make sure that -X dev is used to configure the allocation in preinitialization */ - status = PyConfig_SetArgv(&config, Py_ARRAY_LENGTH(argv), argv); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = PyConfig_SetString(&config, &config.program_name, - L"./_testembed"); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - goto failed; - } - PyConfig_Clear(&config); + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + config_set_program_name(&config); + init_from_config_clear(&config); dump_config(); Py_Finalize(); return 0; - -failed: - PyConfig_Clear(&config); - Py_ExitStatusException(status); } @@ -867,36 +880,20 @@ static int test_preinit_parse_argv(void) status = PyConfig_InitPythonConfig(&config); if (PyStatus_Exception(status)) { - goto failed; + PyConfig_Clear(&config); + Py_ExitStatusException(status); } /* Pre-initialize implicitly using argv: make sure that -X dev is used to configure the allocation in preinitialization */ wchar_t *argv[] = {L"python3", L"-X", L"dev", L"script.py"}; - status = PyConfig_SetArgv(&config, Py_ARRAY_LENGTH(argv), argv); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = PyConfig_SetString(&config, &config.program_name, - L"./_testembed"); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - goto failed; - } - PyConfig_Clear(&config); + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + config_set_program_name(&config); + init_from_config_clear(&config); dump_config(); Py_Finalize(); return 0; - -failed: - PyConfig_Clear(&config); - Py_ExitStatusException(status); } @@ -955,13 +952,8 @@ static int check_preinit_isolated_config(int preinit) PyConfig_Clear(&config); Py_ExitStatusException(status); } - config.program_name = L"./_testembed"; - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - PyConfig_Clear(&config); - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); rt_preconfig = &_PyRuntime.preconfig; assert(rt_preconfig->isolated == 1); @@ -1017,12 +1009,9 @@ static int check_init_python_config(int preinit) if (PyStatus_Exception(status)) { Py_ExitStatusException(status); } - config.program_name = L"./_testembed"; + config_set_program_name(&config); + init_from_config_clear(&config); - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } dump_config(); Py_Finalize(); return 0; @@ -1061,11 +1050,8 @@ static int test_init_dont_configure_locale(void) if (PyStatus_Exception(status)) { Py_ExitStatusException(status); } - config.program_name = L"./_testembed"; - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); dump_config(); Py_Finalize(); @@ -1084,11 +1070,9 @@ static int test_init_dev_mode(void) putenv("PYTHONFAULTHANDLER="); putenv("PYTHONMALLOC="); config.dev_mode = 1; - config.program_name = L"./_testembed"; - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + config_set_program_name(&config); + init_from_config_clear(&config); + dump_config(); Py_Finalize(); return 0; @@ -1278,16 +1262,9 @@ static int test_init_read_set(void) } /* override executable computed by PyConfig_Read() */ - status = PyConfig_SetString(&config, &config.executable, L"my_executable"); - if (PyStatus_Exception(status)) { - goto fail; - } + config_set_string(&config, &config.executable, L"my_executable"); + init_from_config_clear(&config); - status = Py_InitializeFromConfig(&config); - PyConfig_Clear(&config); - if (PyStatus_Exception(status)) { - goto fail; - } dump_config(); Py_Finalize(); return 0; @@ -1297,19 +1274,18 @@ static int test_init_read_set(void) } -wchar_t *init_main_argv[] = { - L"python3", L"-c", - (L"import _testinternalcapi, json; " - L"print(json.dumps(_testinternalcapi.get_configs()))"), - L"arg2"}; - - static void configure_init_main(PyConfig *config) { - config->argv.length = Py_ARRAY_LENGTH(init_main_argv); - config->argv.items = init_main_argv; + wchar_t* argv[] = { + L"python3", L"-c", + (L"import _testinternalcapi, json; " + L"print(json.dumps(_testinternalcapi.get_configs()))"), + L"arg2"}; + config->parse_argv = 1; - config->program_name = L"./python3"; + + config_set_argv(config, Py_ARRAY_LENGTH(argv), argv); + config_set_string(config, &config->program_name, L"./python3"); } @@ -1322,11 +1298,7 @@ static int test_init_run_main(void) Py_ExitStatusException(status); } configure_init_main(&config); - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); return Py_RunMain(); } @@ -1343,11 +1315,7 @@ static int test_init_main(void) } configure_init_main(&config); config._init_main = 0; - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } + init_from_config_clear(&config); /* sys.stdout don't exist yet: it is created by _Py_InitializeMain() */ int res = PyRun_SimpleString( @@ -1374,35 +1342,19 @@ static int test_run_main(void) status = PyConfig_InitPythonConfig(&config); if (PyStatus_Exception(status)) { - goto failed; + PyConfig_Clear(&config); + Py_ExitStatusException(status); } wchar_t *argv[] = {L"python3", L"-c", (L"import sys; " L"print(f'Py_RunMain(): sys.argv={sys.argv}')"), L"arg2"}; - status = PyConfig_SetArgv(&config, Py_ARRAY_LENGTH(argv), argv); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = PyConfig_SetString(&config, &config.program_name, - L"./python3"); - if (PyStatus_Exception(status)) { - goto failed; - } - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - goto failed; - } - PyConfig_Clear(&config); + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + config_set_string(&config, &config.program_name, L"./python3"); + init_from_config_clear(&config); return Py_RunMain(); - -failed: - PyConfig_Clear(&config); - Py_ExitStatusException(status); } From webhook-mailer at python.org Mon Jul 1 14:53:02 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 01 Jul 2019 18:53:02 -0000 Subject: [Python-checkins] bpo-37469: Document usability of SimpleQueue with QueueHandler and QueueListener. (GH-14521) (GH-14526) Message-ID: https://github.com/python/cpython/commit/b0ab95bbe792b38e952688f8fa1657a78b35410e commit: b0ab95bbe792b38e952688f8fa1657a78b35410e branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-01T19:52:57+01:00 summary: bpo-37469: Document usability of SimpleQueue with QueueHandler and QueueListener. (GH-14521) (GH-14526) (cherry picked from commit e6b64b756f940147728ea7808fb686ffcae89176) files: M Doc/library/logging.handlers.rst diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index b2ebafa83b2e..d5944ec87d2d 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -971,9 +971,11 @@ possible, while any potentially slow operations (such as sending an email via .. class:: QueueHandler(queue) Returns a new instance of the :class:`QueueHandler` class. The instance is - initialized with the queue to send messages to. The queue can be any - queue-like object; it's used as-is by the :meth:`enqueue` method, which needs - to know how to send messages to it. + initialized with the queue to send messages to. The *queue* can be any + queue-like object; it's used as-is by the :meth:`enqueue` method, which + needs to know how to send messages to it. The queue is not *required* to + have the task tracking API, which means that you can use + :class:`~queue.SimpleQueue` instances for *queue*. .. method:: emit(record) @@ -1029,11 +1031,14 @@ possible, while any potentially slow operations (such as sending an email via initialized with the queue to send messages to and a list of handlers which will handle entries placed on the queue. The queue can be any queue-like object; it's passed as-is to the :meth:`dequeue` method, which needs - to know how to get messages from it. If ``respect_handler_level`` is ``True``, - a handler's level is respected (compared with the level for the message) when - deciding whether to pass messages to that handler; otherwise, the behaviour - is as in previous Python versions - to always pass each message to each - handler. + to know how to get messages from it. The queue is not *required* to have the + task tracking API (though it's used if available), which means that you can + use :class:`~queue.SimpleQueue` instances for *queue*. + + If ``respect_handler_level`` is ``True``, a handler's level is respected + (compared with the level for the message) when deciding whether to pass + messages to that handler; otherwise, the behaviour is as in previous Python + versions - to always pass each message to each handler. .. versionchanged:: 3.5 The ``respect_handler_levels`` argument was added. From webhook-mailer at python.org Mon Jul 1 14:53:32 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 01 Jul 2019 18:53:32 -0000 Subject: [Python-checkins] bpo-37469: Document usability of SimpleQueue with QueueHandler and QueueListener. (GH-14521) (GH-14525) Message-ID: https://github.com/python/cpython/commit/6cde61369e8174c493ca240cb52ebc9c2a2fe667 commit: 6cde61369e8174c493ca240cb52ebc9c2a2fe667 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-01T19:53:28+01:00 summary: bpo-37469: Document usability of SimpleQueue with QueueHandler and QueueListener. (GH-14521) (GH-14525) (cherry picked from commit e6b64b756f940147728ea7808fb686ffcae89176) files: M Doc/library/logging.handlers.rst diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index b2ebafa83b2e..d5944ec87d2d 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -971,9 +971,11 @@ possible, while any potentially slow operations (such as sending an email via .. class:: QueueHandler(queue) Returns a new instance of the :class:`QueueHandler` class. The instance is - initialized with the queue to send messages to. The queue can be any - queue-like object; it's used as-is by the :meth:`enqueue` method, which needs - to know how to send messages to it. + initialized with the queue to send messages to. The *queue* can be any + queue-like object; it's used as-is by the :meth:`enqueue` method, which + needs to know how to send messages to it. The queue is not *required* to + have the task tracking API, which means that you can use + :class:`~queue.SimpleQueue` instances for *queue*. .. method:: emit(record) @@ -1029,11 +1031,14 @@ possible, while any potentially slow operations (such as sending an email via initialized with the queue to send messages to and a list of handlers which will handle entries placed on the queue. The queue can be any queue-like object; it's passed as-is to the :meth:`dequeue` method, which needs - to know how to get messages from it. If ``respect_handler_level`` is ``True``, - a handler's level is respected (compared with the level for the message) when - deciding whether to pass messages to that handler; otherwise, the behaviour - is as in previous Python versions - to always pass each message to each - handler. + to know how to get messages from it. The queue is not *required* to have the + task tracking API (though it's used if available), which means that you can + use :class:`~queue.SimpleQueue` instances for *queue*. + + If ``respect_handler_level`` is ``True``, a handler's level is respected + (compared with the level for the message) when deciding whether to pass + messages to that handler; otherwise, the behaviour is as in previous Python + versions - to always pass each message to each handler. .. versionchanged:: 3.5 The ``respect_handler_levels`` argument was added. From webhook-mailer at python.org Mon Jul 1 15:45:09 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 01 Jul 2019 19:45:09 -0000 Subject: [Python-checkins] bpo-37470: Document more clearly the error handling for QueueHandler.emit(). (GH-14532) Message-ID: https://github.com/python/cpython/commit/0f4e8132820947d93eccf31b9e526b81c6ffa53d commit: 0f4e8132820947d93eccf31b9e526b81c6ffa53d branch: master author: Vinay Sajip committer: GitHub date: 2019-07-01T20:45:01+01:00 summary: bpo-37470: Document more clearly the error handling for QueueHandler.emit(). (GH-14532) files: M Doc/library/logging.handlers.rst diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index 592d7e5daf93..b7445a135b74 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -1001,7 +1001,12 @@ possible, while any potentially slow operations (such as sending an email via .. method:: emit(record) - Enqueues the result of preparing the LogRecord. + Enqueues the result of preparing the LogRecord. Should an exception + occur (e.g. because a bounded queue has filled up), the + :meth:`~logging.Handler.handleError` method is called to handle the + error. This can result in the record silently being dropped (if + :attr:`logging.raiseExceptions` is ``False``) or a message printed to + ``sys.stderr`` (if :attr:`logging.raiseExceptions` is ``True``). .. method:: prepare(record) From webhook-mailer at python.org Mon Jul 1 15:51:25 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 01 Jul 2019 19:51:25 -0000 Subject: [Python-checkins] bpo-37470: Document more clearly the error handling for QueueHandler.emit(). (GH-14532) (GH-14534) Message-ID: https://github.com/python/cpython/commit/844a9d64a4f640d1b20dc6ea54ab375680332d93 commit: 844a9d64a4f640d1b20dc6ea54ab375680332d93 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-01T20:51:20+01:00 summary: bpo-37470: Document more clearly the error handling for QueueHandler.emit(). (GH-14532) (GH-14534) (cherry picked from commit 0f4e8132820947d93eccf31b9e526b81c6ffa53d) files: M Doc/library/logging.handlers.rst diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index d5944ec87d2d..32919c1a2e29 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -980,7 +980,12 @@ possible, while any potentially slow operations (such as sending an email via .. method:: emit(record) - Enqueues the result of preparing the LogRecord. + Enqueues the result of preparing the LogRecord. Should an exception + occur (e.g. because a bounded queue has filled up), the + :meth:`~logging.Handler.handleError` method is called to handle the + error. This can result in the record silently being dropped (if + :attr:`logging.raiseExceptions` is ``False``) or a message printed to + ``sys.stderr`` (if :attr:`logging.raiseExceptions` is ``True``). .. method:: prepare(record) From webhook-mailer at python.org Mon Jul 1 15:53:43 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 01 Jul 2019 19:53:43 -0000 Subject: [Python-checkins] bpo-37470: Document more clearly the error handling for QueueHandler.emit(). (GH-14532) (GH-14533) Message-ID: https://github.com/python/cpython/commit/91f9f098fcdb023dbb89d06c8833e89a11cbae4c commit: 91f9f098fcdb023dbb89d06c8833e89a11cbae4c branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-01T20:53:39+01:00 summary: bpo-37470: Document more clearly the error handling for QueueHandler.emit(). (GH-14532) (GH-14533) (cherry picked from commit 0f4e8132820947d93eccf31b9e526b81c6ffa53d) files: M Doc/library/logging.handlers.rst diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index d5944ec87d2d..32919c1a2e29 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -980,7 +980,12 @@ possible, while any potentially slow operations (such as sending an email via .. method:: emit(record) - Enqueues the result of preparing the LogRecord. + Enqueues the result of preparing the LogRecord. Should an exception + occur (e.g. because a bounded queue has filled up), the + :meth:`~logging.Handler.handleError` method is called to handle the + error. This can result in the record silently being dropped (if + :attr:`logging.raiseExceptions` is ``False``) or a message printed to + ``sys.stderr`` (if :attr:`logging.raiseExceptions` is ``True``). .. method:: prepare(record) From webhook-mailer at python.org Mon Jul 1 19:03:59 2019 From: webhook-mailer at python.org (Steve Dower) Date: Mon, 01 Jul 2019 23:03:59 -0000 Subject: [Python-checkins] bpo-37363: Add audit events on startup for the run commands (GH-14524) Message-ID: https://github.com/python/cpython/commit/e226e83d36dfc7220d836fb7a249ce18e70cb4a6 commit: e226e83d36dfc7220d836fb7a249ce18e70cb4a6 branch: master author: Steve Dower committer: GitHub date: 2019-07-01T16:03:53-07:00 summary: bpo-37363: Add audit events on startup for the run commands (GH-14524) files: A Misc/NEWS.d/next/Security/2019-07-01-10-31-14.bpo-37363.fSjatj.rst M Doc/library/sys.rst M Doc/tools/extensions/pyspecific.py M Doc/using/cmdline.rst M Lib/test/test_embed.py M Modules/main.c M Programs/_testembed.c diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 131aea0def62..acd54421a370 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -905,6 +905,12 @@ always available. read, so that you can set this hook there. The :mod:`site` module :ref:`sets this `. + .. audit-event:: cpython.run_interactivehook hook sys.__interactivehook__ + + Raises an :ref:`auditing event ` + ``cpython.run_interactivehook`` with the hook object as the argument when + the hook is called on startup. + .. versionadded:: 3.4 diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index a6f39b02b5f8..8839033b983c 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -199,13 +199,18 @@ def run(self): .format(name, info['args'], new_info['args']) ) - if len(self.arguments) >= 3 and self.arguments[2]: - target = self.arguments[2] - ids = [] - else: - target = "audit_event_{}_{}".format(name, len(info['source'])) - target = re.sub(r'\W', '_', label) - ids = [target] + ids = [] + try: + target = self.arguments[2].strip("\"'") + except (IndexError, TypeError): + target = None + if not target: + target = "audit_event_{}_{}".format( + re.sub(r'\W', '_', name), + len(info['source']), + ) + ids.append(target) + info['source'].append((env.docname, target)) pnode = nodes.paragraph(text, classes=["audit-hook"], ids=ids) @@ -560,7 +565,8 @@ def process_audit_events(app, doctree, fromdocname): row += nodes.entry('', node) node = nodes.paragraph() - for i, (doc, label) in enumerate(audit_event['source'], start=1): + backlinks = enumerate(sorted(set(audit_event['source'])), start=1) + for i, (doc, label) in backlinks: if isinstance(label, str): ref = nodes.reference("", nodes.Text("[{}]".format(i)), internal=True) ref['refuri'] = "{}#{}".format( diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index e11fe31c2fbb..22f42d966a55 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -70,6 +70,7 @@ source. :data:`sys.path` (allowing modules in that directory to be imported as top level modules). + .. audit-event:: cpython.run_command command cmdoption-c .. cmdoption:: -m @@ -106,13 +107,14 @@ source. python -mtimeit -s 'setup here' 'benchmarked code here' python -mtimeit -h # for details + .. audit-event:: cpython.run_module module-name cmdoption-m + .. seealso:: :func:`runpy.run_module` Equivalent functionality directly available to Python code :pep:`338` -- Executing modules as scripts - .. versionchanged:: 3.1 Supply the package name to run a ``__main__`` submodule. @@ -129,6 +131,7 @@ source. ``"-"`` and the current directory will be added to the start of :data:`sys.path`. + .. audit-event:: cpython.run_stdin "" "" .. describe:: - + @@ -135,113 +135,75 @@

Menus

File menu (Shell and Editor)?

-
New File
-
Create a new file editing window.
-
Open?
-
Open an existing file with an Open dialog.
-
Recent Files
-
Open a list of recent files. Click one to open it.
-
Open Module?
-
Open an existing module (searches sys.path).
+
New File
Create a new file editing window.
+
Open?
Open an existing file with an Open dialog.
+
Recent Files
Open a list of recent files. Click one to open it.
+
Open Module?
Open an existing module (searches sys.path).
-
Class Browser
-
Show functions, classes, and methods in the current Editor file in a +
Class Browser
Show functions, classes, and methods in the current Editor file in a tree structure. In the shell, open a module first.
-
Path Browser
-
Show sys.path directories, modules, functions, classes and methods in a +
Path Browser
Show sys.path directories, modules, functions, classes and methods in a tree structure.
-
Save
-
Save the current window to the associated file, if there is one. Windows +
Save
Save the current window to the associated file, if there is one. Windows that have been changed since being opened or last saved have a * before and after the window title. If there is no associated file, do Save As instead.
-
Save As?
-
Save the current window with a Save As dialog. The file saved becomes the +
Save As?
Save the current window with a Save As dialog. The file saved becomes the new associated file for the window.
-
Save Copy As?
-
Save the current window to different file without changing the associated +
Save Copy As?
Save the current window to different file without changing the associated file.
-
Print Window
-
Print the current window to the default printer.
-
Close
-
Close the current window (ask to save if unsaved).
-
Exit
-
Close all windows and quit IDLE (ask to save unsaved windows).
+
Print Window
Print the current window to the default printer.
+
Close
Close the current window (ask to save if unsaved).
+
Exit
Close all windows and quit IDLE (ask to save unsaved windows).

Edit menu (Shell and Editor)?

-
Undo
-
Undo the last change to the current window. A maximum of 1000 changes may +
Undo
Undo the last change to the current window. A maximum of 1000 changes may be undone.
-
Redo
-
Redo the last undone change to the current window.
-
Cut
-
Copy selection into the system-wide clipboard; then delete the selection.
-
Copy
-
Copy selection into the system-wide clipboard.
-
Paste
-
Insert contents of the system-wide clipboard into the current window.
+
Redo
Redo the last undone change to the current window.
+
Cut
Copy selection into the system-wide clipboard; then delete the selection.
+
Copy
Copy selection into the system-wide clipboard.
+
Paste
Insert contents of the system-wide clipboard into the current window.

The clipboard functions are also available in context menus.

-
Select All
-
Select the entire contents of the current window.
-
Find?
-
Open a search dialog with many options
-
Find Again
-
Repeat the last search, if there is one.
-
Find Selection
-
Search for the currently selected string, if there is one.
-
Find in Files?
-
Open a file search dialog. Put results in a new output window.
-
Replace?
-
Open a search-and-replace dialog.
-
Go to Line
-
Move cursor to the line number requested and make that line visible.
-
Show Completions
-
Open a scrollable list allowing selection of keywords and attributes. See +
Select All
Select the entire contents of the current window.
+
Find?
Open a search dialog with many options
+
Find Again
Repeat the last search, if there is one.
+
Find Selection
Search for the currently selected string, if there is one.
+
Find in Files?
Open a file search dialog. Put results in a new output window.
+
Replace?
Open a search-and-replace dialog.
+
Go to Line
Move cursor to the line number requested and make that line visible.
+
Show Completions
Open a scrollable list allowing selection of keywords and attributes. See Completions in the Editing and navigation section below.
-
Expand Word
-
Expand a prefix you have typed to match a full word in the same window; +
Expand Word
Expand a prefix you have typed to match a full word in the same window; repeat to get a different expansion.
-
Show call tip
-
After an unclosed parenthesis for a function, open a small window with +
Show call tip
After an unclosed parenthesis for a function, open a small window with function parameter hints. See Calltips in the Editing and navigation section below.
-
Show surrounding parens
-
Highlight the surrounding parenthesis.
+
Show surrounding parens
Highlight the surrounding parenthesis.

Format menu (Editor window only)?

-
Indent Region
-
Shift selected lines right by the indent width (default 4 spaces).
-
Dedent Region
-
Shift selected lines left by the indent width (default 4 spaces).
-
Comment Out Region
-
Insert ## in front of selected lines.
-
Uncomment Region
-
Remove leading # or ## from selected lines.
-
Tabify Region
-
Turn leading stretches of spaces into tabs. (Note: We recommend using +
Indent Region
Shift selected lines right by the indent width (default 4 spaces).
+
Dedent Region
Shift selected lines left by the indent width (default 4 spaces).
+
Comment Out Region
Insert ## in front of selected lines.
+
Uncomment Region
Remove leading # or ## from selected lines.
+
Tabify Region
Turn leading stretches of spaces into tabs. (Note: We recommend using 4 space blocks to indent Python code.)
-
Untabify Region
-
Turn all tabs into the correct number of spaces.
-
Toggle Tabs
-
Open a dialog to switch between indenting with spaces and tabs.
-
New Indent Width
-
Open a dialog to change indent width. The accepted default by the Python +
Untabify Region
Turn all tabs into the correct number of spaces.
+
Toggle Tabs
Open a dialog to switch between indenting with spaces and tabs.
+
New Indent Width
Open a dialog to change indent width. The accepted default by the Python community is 4 spaces.
-
Format Paragraph
-
Reformat the current blank-line-delimited paragraph in comment block or +
Format Paragraph
Reformat the current blank-line-delimited paragraph in comment block or multiline string or selected line in a string. All lines in the paragraph will be formatted to less than N columns, where N defaults to 72.
-
Strip trailing whitespace
-
Remove trailing space and other whitespace characters after the last +
Strip trailing whitespace
Remove trailing space and other whitespace characters after the last non-whitespace character of a line by applying str.rstrip to each line, including lines within multiline strings.
@@ -249,20 +211,17 @@

Edit menu (Shell and Editor)

Run menu (Editor window only)?

-
Python Shell
-
Open or wake up the Python Shell window.
+
Python Shell
Open or wake up the Python Shell window.
-
Check Module
-
Check the syntax of the module currently open in the Editor window. If the +
Check Module
Check the syntax of the module currently open in the Editor window. If the module has not been saved IDLE will either prompt the user to save or autosave, as selected in the General tab of the Idle Settings dialog. If there is a syntax error, the approximate location is indicated in the Editor window.
-
Run Module
-
Do Check Module. If no error, restart the shell to clean the +
Run Module
Do Check Module. If no error, restart the shell to clean the environment, then execute the module. Output is displayed in the Shell window. Note that output requires use of print or write. When execution is complete, the Shell retains focus and displays a prompt. @@ -271,8 +230,7 @@

Edit menu (Shell and Editor) -
Run? Customized
-
Same as Run Module, but run the module with customized +
Run? Customized
Same as Run Module, but run the module with customized settings. Command Line Arguments extend sys.argv as if passed on a command line. The module can be run in the Shell without restarting.

@@ -280,56 +238,44 @@

Edit menu (Shell and Editor)

Shell menu (Shell window only)?

-
View Last Restart
-
Scroll the shell window to the last Shell restart.
-
Restart Shell
-
Restart the shell to clean the environment.
-
Previous History
-
Cycle through earlier commands in history which match the current entry.
-
Next History
-
Cycle through later commands in history which match the current entry.
-
Interrupt Execution
-
Stop a running program.
+
View Last Restart
Scroll the shell window to the last Shell restart.
+
Restart Shell
Restart the shell to clean the environment.
+
Previous History
Cycle through earlier commands in history which match the current entry.
+
Next History
Cycle through later commands in history which match the current entry.
+
Interrupt Execution
Stop a running program.

Debug menu (Shell window only)?

-
Go to File/Line
-
Look on the current line. with the cursor, and the line above for a filename +
Go to File/Line
Look on the current line. with the cursor, and the line above for a filename and line number. If found, open the file if not already open, and show the line. Use this to view source lines referenced in an exception traceback and lines found by Find in Files. Also available in the context menu of the Shell window and Output windows.
-
Debugger (toggle)
-
When activated, code entered in the Shell or run from an Editor will run +
Debugger (toggle)
When activated, code entered in the Shell or run from an Editor will run under the debugger. In the Editor, breakpoints can be set with the context menu. This feature is still incomplete and somewhat experimental.
-
Stack Viewer
-
Show the stack traceback of the last exception in a tree widget, with +
Stack Viewer
Show the stack traceback of the last exception in a tree widget, with access to locals and globals.
-
Auto-open Stack Viewer
-
Toggle automatically opening the stack viewer on an unhandled exception.
+
Auto-open Stack Viewer
Toggle automatically opening the stack viewer on an unhandled exception.

Options menu (Shell and Editor)?

-
Configure IDLE
-
Open a configuration dialog and change preferences for the following: +
Configure IDLE
Open a configuration dialog and change preferences for the following: fonts, indentation, keybindings, text color themes, startup windows and size, additional help sources, and extensions. On macOS, open the configuration dialog by selecting Preferences in the application menu. For more, see Setting preferences under Help and preferences.
-
Show/Hide Code Context (Editor Window only)
-
Open a pane at the top of the edit window which shows the block context +
Show/Hide Code Context (Editor Window only)
Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window. See Code Context in the Editing and Navigation section below.
-
Zoom/Restore Height
-
Toggles the window between normal size and maximum height. The initial size +
Zoom/Restore Height
Toggles the window between normal size and maximum height. The initial size defaults to 40 lines by 80 chars unless changed on the General tab of the Configure IDLE dialog. The maximum height for a screen is determined by momentarily maximizing a window the first time one is zoomed on the screen. @@ -345,16 +291,12 @@

Window menu (Shell and Editor)

Help menu (Shell and Editor)?

-
About IDLE
-
Display version, copyright, license, credits, and more.
-
IDLE Help
-
Display this IDLE document, detailing the menu options, basic editing and +
About IDLE
Display version, copyright, license, credits, and more.
+
IDLE Help
Display this IDLE document, detailing the menu options, basic editing and navigation, and other tips.
-
Python Docs
-
Access local Python documentation, if installed, or start a web browser +
Python Docs
Access local Python documentation, if installed, or start a web browser and open docs.python.org showing the latest Python documentation.
-
Turtle Demo
-
Run the turtledemo module with example Python code and turtle drawings.
+
Turtle Demo
Run the turtledemo module with example Python code and turtle drawings.

Additional help sources may be added here with the Configure IDLE dialog under the General tab. See the Help sources subsection below @@ -365,32 +307,25 @@

Help menu (Shell and Editor) -
Cut
-
Copy selection into the system-wide clipboard; then delete the selection.
-
Copy
-
Copy selection into the system-wide clipboard.
-
Paste
-
Insert contents of the system-wide clipboard into the current window.
+
Cut
Copy selection into the system-wide clipboard; then delete the selection.
+
Copy
Copy selection into the system-wide clipboard.
+
Paste
Insert contents of the system-wide clipboard into the current window.

Editor windows also have breakpoint functions. Lines with a breakpoint set are specially marked. Breakpoints only have an effect when running under the debugger. Breakpoints for a file are saved in the user?s .idlerc directory.

-
Set Breakpoint
-
Set a breakpoint on the current line.
-
Clear Breakpoint
-
Clear the breakpoint on that line.
+
Set Breakpoint
Set a breakpoint on the current line.
+
Clear Breakpoint
Clear the breakpoint on that line.

Shell and Output windows also have the following.

-
Go to file/line
-
Same as in Debug menu.
+
Go to file/line
Same as in Debug menu.

The Shell window also has an output squeezing facility explained in the Python Shell window subsection below.

-
Squeeze
-
If the cursor is over an output line, squeeze all the output between +
Squeeze
If the cursor is over an output line, squeeze all the output between the code above and the prompt below down to a ?Squeezed text? label.
@@ -670,6 +605,9 @@

Running user codeprint or write to sys.stdout or sys.stderr, IDLE should be started in a command line window. The secondary subprocess will then be attached to that window for input and output.

+

The IDLE code running in the execution process adds frames to the call stack +that would not be there otherwise. IDLE wraps sys.getrecursionlimit and +sys.setrecursionlimit to reduce their visibility.

If sys is reset by user code, such as with importlib.reload(sys), IDLE?s changes are lost and input from the keyboard and output to the screen will not work correctly.

@@ -772,7 +710,7 @@

Running without a subprocess -

Deprecated since version 3.4.

+

Deprecated since version 3.4.

@@ -885,7 +823,7 @@

Table of Contents

Previous topic

tkinter.scrolledtext ? Scrolled Text Widget

+ title="previous chapter">tkinter.scrolledtext ? Scrolled Text Widget

Next topic

Other Graphical User Interface Packages

@@ -957,11 +895,11 @@

Navigation



- Last updated on Jun 17, 2019. + Last updated on Jul 04, 2019. Found a bug?
- Created using Sphinx 1.8.1. + Created using Sphinx 2.1.1. diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py index 46f0235fbfdc..d0f1e9207bb1 100644 --- a/Lib/idlelib/idle_test/test_run.py +++ b/Lib/idlelib/idle_test/test_run.py @@ -6,6 +6,8 @@ from test.support import captured_stderr import io +import sys + class RunTest(unittest.TestCase): @@ -260,5 +262,36 @@ def test_close(self): self.assertRaises(TypeError, f.close, 1) +class TestSysRecursionLimitWrappers(unittest.TestCase): + + def test_bad_setrecursionlimit_calls(self): + run.install_recursionlimit_wrappers() + self.addCleanup(run.uninstall_recursionlimit_wrappers) + f = sys.setrecursionlimit + self.assertRaises(TypeError, f, limit=100) + self.assertRaises(TypeError, f, 100, 1000) + self.assertRaises(ValueError, f, 0) + + def test_roundtrip(self): + run.install_recursionlimit_wrappers() + self.addCleanup(run.uninstall_recursionlimit_wrappers) + + # check that setting the recursion limit works + orig_reclimit = sys.getrecursionlimit() + self.addCleanup(sys.setrecursionlimit, orig_reclimit) + sys.setrecursionlimit(orig_reclimit + 3) + + # check that the new limit is returned by sys.getrecursionlimit() + new_reclimit = sys.getrecursionlimit() + self.assertEqual(new_reclimit, orig_reclimit + 3) + + def test_default_recursion_limit_preserved(self): + orig_reclimit = sys.getrecursionlimit() + run.install_recursionlimit_wrappers() + self.addCleanup(run.uninstall_recursionlimit_wrappers) + new_reclimit = sys.getrecursionlimit() + self.assertEqual(new_reclimit, orig_reclimit) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 6b3928b7bf2b..c6ed76b23a2d 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -4,10 +4,12 @@ f'''{sys.executable} -c "__import__('idlelib.run').run.main()"''' '.run' is needed because __import__ returns idlelib, not idlelib.run. """ +import functools import io import linecache import queue import sys +import textwrap import time import traceback import _thread as thread @@ -305,6 +307,64 @@ def fix_scaling(root): font['size'] = round(-0.75*size) +RECURSIONLIMIT_DELTA = 30 +def install_recursionlimit_wrappers(): + """Install wrappers to always add 30 to the recursion limit.""" + # see: bpo-26806 + + @functools.wraps(sys.setrecursionlimit) + def setrecursionlimit(*args, **kwargs): + # mimic the original sys.setrecursionlimit()'s input handling + if kwargs: + raise TypeError( + "setrecursionlimit() takes no keyword arguments") + try: + limit, = args + except ValueError: + raise TypeError(f"setrecursionlimit() takes exactly one " + f"argument ({len(args)} given)") + if not limit > 0: + raise ValueError( + "recursion limit must be greater or equal than 1") + + return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA) + + setrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ + This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible + uninterruptible loops. + """).strip()) + + @functools.wraps(sys.getrecursionlimit) + def getrecursionlimit(): + return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA + + getrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ + This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate for + the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit. + """).strip()) + + # add the delta to the default recursion limit, to compensate + sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA) + + sys.setrecursionlimit = setrecursionlimit + sys.getrecursionlimit = getrecursionlimit + + +def uninstall_recursionlimit_wrappers(): + """Uninstall the recursion limit wrappers from the sys module. + + IDLE only uses this for tests. Users can import run and call + this to remove the wrapping. + """ + if ( + getattr(sys.setrecursionlimit, '__wrapped__', None) and + getattr(sys.getrecursionlimit, '__wrapped__', None) + ): + sys.setrecursionlimit = sys.setrecursionlimit.__wrapped__ + sys.getrecursionlimit = sys.getrecursionlimit.__wrapped__ + sys.setrecursionlimit(sys.getrecursionlimit() - RECURSIONLIMIT_DELTA) + + class MyRPCServer(rpc.RPCServer): def handle_error(self, request, client_address): @@ -448,6 +508,8 @@ def handle(self): # sys.stdin gets changed from within IDLE's shell. See issue17838. self._keep_stdin = sys.stdin + install_recursionlimit_wrappers() + self.interp = self.get_remote_proxy("interp") rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05) diff --git a/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst b/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst new file mode 100644 index 000000000000..8514bb9292fe --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst @@ -0,0 +1,4 @@ +To compensate for stack frames added by IDLE and avoid possible problems +with low recursion limits, add 30 to limits in the user code execution +process. Subtract 30 when reporting recursion limits to make this addition +mostly transparent. From webhook-mailer at python.org Sat Jul 6 08:54:21 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 06 Jul 2019 12:54:21 -0000 Subject: [Python-checkins] bpo-26806: add 30 to the recursion limit in IDLE's shell (GH-13944) Message-ID: https://github.com/python/cpython/commit/d4af55391f56286ab8d478591017174a5a0a5ce2 commit: d4af55391f56286ab8d478591017174a5a0a5ce2 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-06T05:54:17-07:00 summary: bpo-26806: add 30 to the recursion limit in IDLE's shell (GH-13944) This is done to compensate for the extra stack frames added by IDLE itself, which cause problems when setting the recursion limit to low values. This wraps sys.setrecursionlimit() and sys.getrecursionlimit() as invisibly as possible. (cherry picked from commit fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst M Doc/library/idle.rst M Lib/idlelib/NEWS.txt M Lib/idlelib/help.html M Lib/idlelib/idle_test/test_run.py M Lib/idlelib/run.py diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index fb886a714d74..de58f266bf5e 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -713,6 +713,10 @@ or ``print`` or ``write`` to sys.stdout or sys.stderr, IDLE should be started in a command line window. The secondary subprocess will then be attached to that window for input and output. +The IDLE code running in the execution process adds frames to the call stack +that would not be there otherwise. IDLE wraps ``sys.getrecursionlimit`` and +``sys.setrecursionlimit`` to reduce the effect of the additional stack frames. + If ``sys`` is reset by user code, such as with ``importlib.reload(sys)``, IDLE's changes are lost and input from the keyboard and output to the screen will not work correctly. diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 689539e73c12..5d4a936d63f5 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,11 @@ Released on 2019-10-20? ====================================== +bpo-26806: To compensate for stack frames added by IDLE and avoid +possible problems with low recursion limits, add 30 to limits in the +user code execution process. Subtract 30 when reporting recursion +limits to make this addition mostly transparent. + bpo-37325: Fix tab focus traversal order for help source and custom run dialogs. diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 91803fd06c8f..cee6887df68f 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -14,7 +14,7 @@ - + @@ -135,113 +135,75 @@

Menus

File menu (Shell and Editor)?

-
New File
-
Create a new file editing window.
-
Open?
-
Open an existing file with an Open dialog.
-
Recent Files
-
Open a list of recent files. Click one to open it.
-
Open Module?
-
Open an existing module (searches sys.path).
+
New File
Create a new file editing window.
+
Open?
Open an existing file with an Open dialog.
+
Recent Files
Open a list of recent files. Click one to open it.
+
Open Module?
Open an existing module (searches sys.path).
-
Class Browser
-
Show functions, classes, and methods in the current Editor file in a +
Class Browser
Show functions, classes, and methods in the current Editor file in a tree structure. In the shell, open a module first.
-
Path Browser
-
Show sys.path directories, modules, functions, classes and methods in a +
Path Browser
Show sys.path directories, modules, functions, classes and methods in a tree structure.
-
Save
-
Save the current window to the associated file, if there is one. Windows +
Save
Save the current window to the associated file, if there is one. Windows that have been changed since being opened or last saved have a * before and after the window title. If there is no associated file, do Save As instead.
-
Save As?
-
Save the current window with a Save As dialog. The file saved becomes the +
Save As?
Save the current window with a Save As dialog. The file saved becomes the new associated file for the window.
-
Save Copy As?
-
Save the current window to different file without changing the associated +
Save Copy As?
Save the current window to different file without changing the associated file.
-
Print Window
-
Print the current window to the default printer.
-
Close
-
Close the current window (ask to save if unsaved).
-
Exit
-
Close all windows and quit IDLE (ask to save unsaved windows).
+
Print Window
Print the current window to the default printer.
+
Close
Close the current window (ask to save if unsaved).
+
Exit
Close all windows and quit IDLE (ask to save unsaved windows).

Edit menu (Shell and Editor)?

-
Undo
-
Undo the last change to the current window. A maximum of 1000 changes may +
Undo
Undo the last change to the current window. A maximum of 1000 changes may be undone.
-
Redo
-
Redo the last undone change to the current window.
-
Cut
-
Copy selection into the system-wide clipboard; then delete the selection.
-
Copy
-
Copy selection into the system-wide clipboard.
-
Paste
-
Insert contents of the system-wide clipboard into the current window.
+
Redo
Redo the last undone change to the current window.
+
Cut
Copy selection into the system-wide clipboard; then delete the selection.
+
Copy
Copy selection into the system-wide clipboard.
+
Paste
Insert contents of the system-wide clipboard into the current window.

The clipboard functions are also available in context menus.

-
Select All
-
Select the entire contents of the current window.
-
Find?
-
Open a search dialog with many options
-
Find Again
-
Repeat the last search, if there is one.
-
Find Selection
-
Search for the currently selected string, if there is one.
-
Find in Files?
-
Open a file search dialog. Put results in a new output window.
-
Replace?
-
Open a search-and-replace dialog.
-
Go to Line
-
Move cursor to the line number requested and make that line visible.
-
Show Completions
-
Open a scrollable list allowing selection of keywords and attributes. See +
Select All
Select the entire contents of the current window.
+
Find?
Open a search dialog with many options
+
Find Again
Repeat the last search, if there is one.
+
Find Selection
Search for the currently selected string, if there is one.
+
Find in Files?
Open a file search dialog. Put results in a new output window.
+
Replace?
Open a search-and-replace dialog.
+
Go to Line
Move cursor to the line number requested and make that line visible.
+
Show Completions
Open a scrollable list allowing selection of keywords and attributes. See Completions in the Editing and navigation section below.
-
Expand Word
-
Expand a prefix you have typed to match a full word in the same window; +
Expand Word
Expand a prefix you have typed to match a full word in the same window; repeat to get a different expansion.
-
Show call tip
-
After an unclosed parenthesis for a function, open a small window with +
Show call tip
After an unclosed parenthesis for a function, open a small window with function parameter hints. See Calltips in the Editing and navigation section below.
-
Show surrounding parens
-
Highlight the surrounding parenthesis.
+
Show surrounding parens
Highlight the surrounding parenthesis.

Format menu (Editor window only)?

-
Indent Region
-
Shift selected lines right by the indent width (default 4 spaces).
-
Dedent Region
-
Shift selected lines left by the indent width (default 4 spaces).
-
Comment Out Region
-
Insert ## in front of selected lines.
-
Uncomment Region
-
Remove leading # or ## from selected lines.
-
Tabify Region
-
Turn leading stretches of spaces into tabs. (Note: We recommend using +
Indent Region
Shift selected lines right by the indent width (default 4 spaces).
+
Dedent Region
Shift selected lines left by the indent width (default 4 spaces).
+
Comment Out Region
Insert ## in front of selected lines.
+
Uncomment Region
Remove leading # or ## from selected lines.
+
Tabify Region
Turn leading stretches of spaces into tabs. (Note: We recommend using 4 space blocks to indent Python code.)
-
Untabify Region
-
Turn all tabs into the correct number of spaces.
-
Toggle Tabs
-
Open a dialog to switch between indenting with spaces and tabs.
-
New Indent Width
-
Open a dialog to change indent width. The accepted default by the Python +
Untabify Region
Turn all tabs into the correct number of spaces.
+
Toggle Tabs
Open a dialog to switch between indenting with spaces and tabs.
+
New Indent Width
Open a dialog to change indent width. The accepted default by the Python community is 4 spaces.
-
Format Paragraph
-
Reformat the current blank-line-delimited paragraph in comment block or +
Format Paragraph
Reformat the current blank-line-delimited paragraph in comment block or multiline string or selected line in a string. All lines in the paragraph will be formatted to less than N columns, where N defaults to 72.
-
Strip trailing whitespace
-
Remove trailing space and other whitespace characters after the last +
Strip trailing whitespace
Remove trailing space and other whitespace characters after the last non-whitespace character of a line by applying str.rstrip to each line, including lines within multiline strings.
@@ -249,20 +211,17 @@

Edit menu (Shell and Editor)

Run menu (Editor window only)?

-
Python Shell
-
Open or wake up the Python Shell window.
+
Python Shell
Open or wake up the Python Shell window.
-
Check Module
-
Check the syntax of the module currently open in the Editor window. If the +
Check Module
Check the syntax of the module currently open in the Editor window. If the module has not been saved IDLE will either prompt the user to save or autosave, as selected in the General tab of the Idle Settings dialog. If there is a syntax error, the approximate location is indicated in the Editor window.
-
Run Module
-
Do Check Module. If no error, restart the shell to clean the +
Run Module
Do Check Module. If no error, restart the shell to clean the environment, then execute the module. Output is displayed in the Shell window. Note that output requires use of print or write. When execution is complete, the Shell retains focus and displays a prompt. @@ -271,8 +230,7 @@

Edit menu (Shell and Editor) -
Run? Customized
-
Same as Run Module, but run the module with customized +
Run? Customized
Same as Run Module, but run the module with customized settings. Command Line Arguments extend sys.argv as if passed on a command line. The module can be run in the Shell without restarting.

@@ -280,56 +238,44 @@

Edit menu (Shell and Editor)

Shell menu (Shell window only)?

-
View Last Restart
-
Scroll the shell window to the last Shell restart.
-
Restart Shell
-
Restart the shell to clean the environment.
-
Previous History
-
Cycle through earlier commands in history which match the current entry.
-
Next History
-
Cycle through later commands in history which match the current entry.
-
Interrupt Execution
-
Stop a running program.
+
View Last Restart
Scroll the shell window to the last Shell restart.
+
Restart Shell
Restart the shell to clean the environment.
+
Previous History
Cycle through earlier commands in history which match the current entry.
+
Next History
Cycle through later commands in history which match the current entry.
+
Interrupt Execution
Stop a running program.

Debug menu (Shell window only)?

-
Go to File/Line
-
Look on the current line. with the cursor, and the line above for a filename +
Go to File/Line
Look on the current line. with the cursor, and the line above for a filename and line number. If found, open the file if not already open, and show the line. Use this to view source lines referenced in an exception traceback and lines found by Find in Files. Also available in the context menu of the Shell window and Output windows.
-
Debugger (toggle)
-
When activated, code entered in the Shell or run from an Editor will run +
Debugger (toggle)
When activated, code entered in the Shell or run from an Editor will run under the debugger. In the Editor, breakpoints can be set with the context menu. This feature is still incomplete and somewhat experimental.
-
Stack Viewer
-
Show the stack traceback of the last exception in a tree widget, with +
Stack Viewer
Show the stack traceback of the last exception in a tree widget, with access to locals and globals.
-
Auto-open Stack Viewer
-
Toggle automatically opening the stack viewer on an unhandled exception.
+
Auto-open Stack Viewer
Toggle automatically opening the stack viewer on an unhandled exception.

Options menu (Shell and Editor)?

-
Configure IDLE
-
Open a configuration dialog and change preferences for the following: +
Configure IDLE
Open a configuration dialog and change preferences for the following: fonts, indentation, keybindings, text color themes, startup windows and size, additional help sources, and extensions. On macOS, open the configuration dialog by selecting Preferences in the application menu. For more, see Setting preferences under Help and preferences.
-
Show/Hide Code Context (Editor Window only)
-
Open a pane at the top of the edit window which shows the block context +
Show/Hide Code Context (Editor Window only)
Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window. See Code Context in the Editing and Navigation section below.
-
Zoom/Restore Height
-
Toggles the window between normal size and maximum height. The initial size +
Zoom/Restore Height
Toggles the window between normal size and maximum height. The initial size defaults to 40 lines by 80 chars unless changed on the General tab of the Configure IDLE dialog. The maximum height for a screen is determined by momentarily maximizing a window the first time one is zoomed on the screen. @@ -345,16 +291,12 @@

Window menu (Shell and Editor)

Help menu (Shell and Editor)?

-
About IDLE
-
Display version, copyright, license, credits, and more.
-
IDLE Help
-
Display this IDLE document, detailing the menu options, basic editing and +
About IDLE
Display version, copyright, license, credits, and more.
+
IDLE Help
Display this IDLE document, detailing the menu options, basic editing and navigation, and other tips.
-
Python Docs
-
Access local Python documentation, if installed, or start a web browser +
Python Docs
Access local Python documentation, if installed, or start a web browser and open docs.python.org showing the latest Python documentation.
-
Turtle Demo
-
Run the turtledemo module with example Python code and turtle drawings.
+
Turtle Demo
Run the turtledemo module with example Python code and turtle drawings.

Additional help sources may be added here with the Configure IDLE dialog under the General tab. See the Help sources subsection below @@ -365,32 +307,25 @@

Help menu (Shell and Editor) -
Cut
-
Copy selection into the system-wide clipboard; then delete the selection.
-
Copy
-
Copy selection into the system-wide clipboard.
-
Paste
-
Insert contents of the system-wide clipboard into the current window.
+
Cut
Copy selection into the system-wide clipboard; then delete the selection.
+
Copy
Copy selection into the system-wide clipboard.
+
Paste
Insert contents of the system-wide clipboard into the current window.

Editor windows also have breakpoint functions. Lines with a breakpoint set are specially marked. Breakpoints only have an effect when running under the debugger. Breakpoints for a file are saved in the user?s .idlerc directory.

-
Set Breakpoint
-
Set a breakpoint on the current line.
-
Clear Breakpoint
-
Clear the breakpoint on that line.
+
Set Breakpoint
Set a breakpoint on the current line.
+
Clear Breakpoint
Clear the breakpoint on that line.

Shell and Output windows also have the following.

-
Go to file/line
-
Same as in Debug menu.
+
Go to file/line
Same as in Debug menu.

The Shell window also has an output squeezing facility explained in the Python Shell window subsection below.

-
Squeeze
-
If the cursor is over an output line, squeeze all the output between +
Squeeze
If the cursor is over an output line, squeeze all the output between the code above and the prompt below down to a ?Squeezed text? label.
@@ -670,6 +605,9 @@

Running user codeprint or write to sys.stdout or sys.stderr, IDLE should be started in a command line window. The secondary subprocess will then be attached to that window for input and output.

+

The IDLE code running in the execution process adds frames to the call stack +that would not be there otherwise. IDLE wraps sys.getrecursionlimit and +sys.setrecursionlimit to reduce their visibility.

If sys is reset by user code, such as with importlib.reload(sys), IDLE?s changes are lost and input from the keyboard and output to the screen will not work correctly.

@@ -772,7 +710,7 @@

Running without a subprocess -

Deprecated since version 3.4.

+

Deprecated since version 3.4.

@@ -885,7 +823,7 @@

Table of Contents

Previous topic

tkinter.scrolledtext ? Scrolled Text Widget

+ title="previous chapter">tkinter.scrolledtext ? Scrolled Text Widget

Next topic

Other Graphical User Interface Packages

@@ -957,11 +895,11 @@

Navigation



- Last updated on Jun 17, 2019. + Last updated on Jul 04, 2019. Found a bug?
- Created using Sphinx 1.8.1. + Created using Sphinx 2.1.1. diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py index 46f0235fbfdc..d0f1e9207bb1 100644 --- a/Lib/idlelib/idle_test/test_run.py +++ b/Lib/idlelib/idle_test/test_run.py @@ -6,6 +6,8 @@ from test.support import captured_stderr import io +import sys + class RunTest(unittest.TestCase): @@ -260,5 +262,36 @@ def test_close(self): self.assertRaises(TypeError, f.close, 1) +class TestSysRecursionLimitWrappers(unittest.TestCase): + + def test_bad_setrecursionlimit_calls(self): + run.install_recursionlimit_wrappers() + self.addCleanup(run.uninstall_recursionlimit_wrappers) + f = sys.setrecursionlimit + self.assertRaises(TypeError, f, limit=100) + self.assertRaises(TypeError, f, 100, 1000) + self.assertRaises(ValueError, f, 0) + + def test_roundtrip(self): + run.install_recursionlimit_wrappers() + self.addCleanup(run.uninstall_recursionlimit_wrappers) + + # check that setting the recursion limit works + orig_reclimit = sys.getrecursionlimit() + self.addCleanup(sys.setrecursionlimit, orig_reclimit) + sys.setrecursionlimit(orig_reclimit + 3) + + # check that the new limit is returned by sys.getrecursionlimit() + new_reclimit = sys.getrecursionlimit() + self.assertEqual(new_reclimit, orig_reclimit + 3) + + def test_default_recursion_limit_preserved(self): + orig_reclimit = sys.getrecursionlimit() + run.install_recursionlimit_wrappers() + self.addCleanup(run.uninstall_recursionlimit_wrappers) + new_reclimit = sys.getrecursionlimit() + self.assertEqual(new_reclimit, orig_reclimit) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 6b3928b7bf2b..c6ed76b23a2d 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -4,10 +4,12 @@ f'''{sys.executable} -c "__import__('idlelib.run').run.main()"''' '.run' is needed because __import__ returns idlelib, not idlelib.run. """ +import functools import io import linecache import queue import sys +import textwrap import time import traceback import _thread as thread @@ -305,6 +307,64 @@ def fix_scaling(root): font['size'] = round(-0.75*size) +RECURSIONLIMIT_DELTA = 30 +def install_recursionlimit_wrappers(): + """Install wrappers to always add 30 to the recursion limit.""" + # see: bpo-26806 + + @functools.wraps(sys.setrecursionlimit) + def setrecursionlimit(*args, **kwargs): + # mimic the original sys.setrecursionlimit()'s input handling + if kwargs: + raise TypeError( + "setrecursionlimit() takes no keyword arguments") + try: + limit, = args + except ValueError: + raise TypeError(f"setrecursionlimit() takes exactly one " + f"argument ({len(args)} given)") + if not limit > 0: + raise ValueError( + "recursion limit must be greater or equal than 1") + + return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA) + + setrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ + This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible + uninterruptible loops. + """).strip()) + + @functools.wraps(sys.getrecursionlimit) + def getrecursionlimit(): + return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA + + getrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ + This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate for + the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit. + """).strip()) + + # add the delta to the default recursion limit, to compensate + sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA) + + sys.setrecursionlimit = setrecursionlimit + sys.getrecursionlimit = getrecursionlimit + + +def uninstall_recursionlimit_wrappers(): + """Uninstall the recursion limit wrappers from the sys module. + + IDLE only uses this for tests. Users can import run and call + this to remove the wrapping. + """ + if ( + getattr(sys.setrecursionlimit, '__wrapped__', None) and + getattr(sys.getrecursionlimit, '__wrapped__', None) + ): + sys.setrecursionlimit = sys.setrecursionlimit.__wrapped__ + sys.getrecursionlimit = sys.getrecursionlimit.__wrapped__ + sys.setrecursionlimit(sys.getrecursionlimit() - RECURSIONLIMIT_DELTA) + + class MyRPCServer(rpc.RPCServer): def handle_error(self, request, client_address): @@ -448,6 +508,8 @@ def handle(self): # sys.stdin gets changed from within IDLE's shell. See issue17838. self._keep_stdin = sys.stdin + install_recursionlimit_wrappers() + self.interp = self.get_remote_proxy("interp") rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05) diff --git a/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst b/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst new file mode 100644 index 000000000000..8514bb9292fe --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst @@ -0,0 +1,4 @@ +To compensate for stack frames added by IDLE and avoid possible problems +with low recursion limits, add 30 to limits in the user code execution +process. Subtract 30 when reporting recursion limits to make this addition +mostly transparent. From webhook-mailer at python.org Sat Jul 6 08:56:14 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 06 Jul 2019 12:56:14 -0000 Subject: [Python-checkins] bpo-26806: add 30 to the recursion limit in IDLE's shell (GH-13944) Message-ID: https://github.com/python/cpython/commit/d666217b26c373784761e3a84f243f02682bccb1 commit: d666217b26c373784761e3a84f243f02682bccb1 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-06T05:56:10-07:00 summary: bpo-26806: add 30 to the recursion limit in IDLE's shell (GH-13944) This is done to compensate for the extra stack frames added by IDLE itself, which cause problems when setting the recursion limit to low values. This wraps sys.setrecursionlimit() and sys.getrecursionlimit() as invisibly as possible. (cherry picked from commit fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst M Doc/library/idle.rst M Lib/idlelib/NEWS.txt M Lib/idlelib/help.html M Lib/idlelib/idle_test/test_run.py M Lib/idlelib/run.py diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index fb886a714d74..de58f266bf5e 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -713,6 +713,10 @@ or ``print`` or ``write`` to sys.stdout or sys.stderr, IDLE should be started in a command line window. The secondary subprocess will then be attached to that window for input and output. +The IDLE code running in the execution process adds frames to the call stack +that would not be there otherwise. IDLE wraps ``sys.getrecursionlimit`` and +``sys.setrecursionlimit`` to reduce the effect of the additional stack frames. + If ``sys`` is reset by user code, such as with ``importlib.reload(sys)``, IDLE's changes are lost and input from the keyboard and output to the screen will not work correctly. diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 834646f7f776..4fad45b2de9c 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,11 @@ Released on 2019-06-24? ====================================== +bpo-26806: To compensate for stack frames added by IDLE and avoid +possible problems with low recursion limits, add 30 to limits in the +user code execution process. Subtract 30 when reporting recursion +limits to make this addition mostly transparent. + bpo-37325: Fix tab focus traversal order for help source and custom run dialogs. diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 91803fd06c8f..cee6887df68f 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -14,7 +14,7 @@ - + @@ -135,113 +135,75 @@

Menus

File menu (Shell and Editor)?

-
New File
-
Create a new file editing window.
-
Open?
-
Open an existing file with an Open dialog.
-
Recent Files
-
Open a list of recent files. Click one to open it.
-
Open Module?
-
Open an existing module (searches sys.path).
+
New File
Create a new file editing window.
+
Open?
Open an existing file with an Open dialog.
+
Recent Files
Open a list of recent files. Click one to open it.
+
Open Module?
Open an existing module (searches sys.path).
-
Class Browser
-
Show functions, classes, and methods in the current Editor file in a +
Class Browser
Show functions, classes, and methods in the current Editor file in a tree structure. In the shell, open a module first.
-
Path Browser
-
Show sys.path directories, modules, functions, classes and methods in a +
Path Browser
Show sys.path directories, modules, functions, classes and methods in a tree structure.
-
Save
-
Save the current window to the associated file, if there is one. Windows +
Save
Save the current window to the associated file, if there is one. Windows that have been changed since being opened or last saved have a * before and after the window title. If there is no associated file, do Save As instead.
-
Save As?
-
Save the current window with a Save As dialog. The file saved becomes the +
Save As?
Save the current window with a Save As dialog. The file saved becomes the new associated file for the window.
-
Save Copy As?
-
Save the current window to different file without changing the associated +
Save Copy As?
Save the current window to different file without changing the associated file.
-
Print Window
-
Print the current window to the default printer.
-
Close
-
Close the current window (ask to save if unsaved).
-
Exit
-
Close all windows and quit IDLE (ask to save unsaved windows).
+
Print Window
Print the current window to the default printer.
+
Close
Close the current window (ask to save if unsaved).
+
Exit
Close all windows and quit IDLE (ask to save unsaved windows).

Edit menu (Shell and Editor)?

-
Undo
-
Undo the last change to the current window. A maximum of 1000 changes may +
Undo
Undo the last change to the current window. A maximum of 1000 changes may be undone.
-
Redo
-
Redo the last undone change to the current window.
-
Cut
-
Copy selection into the system-wide clipboard; then delete the selection.
-
Copy
-
Copy selection into the system-wide clipboard.
-
Paste
-
Insert contents of the system-wide clipboard into the current window.
+
Redo
Redo the last undone change to the current window.
+
Cut
Copy selection into the system-wide clipboard; then delete the selection.
+
Copy
Copy selection into the system-wide clipboard.
+
Paste
Insert contents of the system-wide clipboard into the current window.

The clipboard functions are also available in context menus.

-
Select All
-
Select the entire contents of the current window.
-
Find?
-
Open a search dialog with many options
-
Find Again
-
Repeat the last search, if there is one.
-
Find Selection
-
Search for the currently selected string, if there is one.
-
Find in Files?
-
Open a file search dialog. Put results in a new output window.
-
Replace?
-
Open a search-and-replace dialog.
-
Go to Line
-
Move cursor to the line number requested and make that line visible.
-
Show Completions
-
Open a scrollable list allowing selection of keywords and attributes. See +
Select All
Select the entire contents of the current window.
+
Find?
Open a search dialog with many options
+
Find Again
Repeat the last search, if there is one.
+
Find Selection
Search for the currently selected string, if there is one.
+
Find in Files?
Open a file search dialog. Put results in a new output window.
+
Replace?
Open a search-and-replace dialog.
+
Go to Line
Move cursor to the line number requested and make that line visible.
+
Show Completions
Open a scrollable list allowing selection of keywords and attributes. See Completions in the Editing and navigation section below.
-
Expand Word
-
Expand a prefix you have typed to match a full word in the same window; +
Expand Word
Expand a prefix you have typed to match a full word in the same window; repeat to get a different expansion.
-
Show call tip
-
After an unclosed parenthesis for a function, open a small window with +
Show call tip
After an unclosed parenthesis for a function, open a small window with function parameter hints. See Calltips in the Editing and navigation section below.
-
Show surrounding parens
-
Highlight the surrounding parenthesis.
+
Show surrounding parens
Highlight the surrounding parenthesis.

Format menu (Editor window only)?

-
Indent Region
-
Shift selected lines right by the indent width (default 4 spaces).
-
Dedent Region
-
Shift selected lines left by the indent width (default 4 spaces).
-
Comment Out Region
-
Insert ## in front of selected lines.
-
Uncomment Region
-
Remove leading # or ## from selected lines.
-
Tabify Region
-
Turn leading stretches of spaces into tabs. (Note: We recommend using +
Indent Region
Shift selected lines right by the indent width (default 4 spaces).
+
Dedent Region
Shift selected lines left by the indent width (default 4 spaces).
+
Comment Out Region
Insert ## in front of selected lines.
+
Uncomment Region
Remove leading # or ## from selected lines.
+
Tabify Region
Turn leading stretches of spaces into tabs. (Note: We recommend using 4 space blocks to indent Python code.)
-
Untabify Region
-
Turn all tabs into the correct number of spaces.
-
Toggle Tabs
-
Open a dialog to switch between indenting with spaces and tabs.
-
New Indent Width
-
Open a dialog to change indent width. The accepted default by the Python +
Untabify Region
Turn all tabs into the correct number of spaces.
+
Toggle Tabs
Open a dialog to switch between indenting with spaces and tabs.
+
New Indent Width
Open a dialog to change indent width. The accepted default by the Python community is 4 spaces.
-
Format Paragraph
-
Reformat the current blank-line-delimited paragraph in comment block or +
Format Paragraph
Reformat the current blank-line-delimited paragraph in comment block or multiline string or selected line in a string. All lines in the paragraph will be formatted to less than N columns, where N defaults to 72.
-
Strip trailing whitespace
-
Remove trailing space and other whitespace characters after the last +
Strip trailing whitespace
Remove trailing space and other whitespace characters after the last non-whitespace character of a line by applying str.rstrip to each line, including lines within multiline strings.
@@ -249,20 +211,17 @@

Edit menu (Shell and Editor)

Run menu (Editor window only)?

-
Python Shell
-
Open or wake up the Python Shell window.
+
Python Shell
Open or wake up the Python Shell window.
-
Check Module
-
Check the syntax of the module currently open in the Editor window. If the +
Check Module
Check the syntax of the module currently open in the Editor window. If the module has not been saved IDLE will either prompt the user to save or autosave, as selected in the General tab of the Idle Settings dialog. If there is a syntax error, the approximate location is indicated in the Editor window.
-
Run Module
-
Do Check Module. If no error, restart the shell to clean the +
Run Module
Do Check Module. If no error, restart the shell to clean the environment, then execute the module. Output is displayed in the Shell window. Note that output requires use of print or write. When execution is complete, the Shell retains focus and displays a prompt. @@ -271,8 +230,7 @@

Edit menu (Shell and Editor) -
Run? Customized
-
Same as Run Module, but run the module with customized +
Run? Customized
Same as Run Module, but run the module with customized settings. Command Line Arguments extend sys.argv as if passed on a command line. The module can be run in the Shell without restarting.

@@ -280,56 +238,44 @@

Edit menu (Shell and Editor)

Shell menu (Shell window only)?

-
View Last Restart
-
Scroll the shell window to the last Shell restart.
-
Restart Shell
-
Restart the shell to clean the environment.
-
Previous History
-
Cycle through earlier commands in history which match the current entry.
-
Next History
-
Cycle through later commands in history which match the current entry.
-
Interrupt Execution
-
Stop a running program.
+
View Last Restart
Scroll the shell window to the last Shell restart.
+
Restart Shell
Restart the shell to clean the environment.
+
Previous History
Cycle through earlier commands in history which match the current entry.
+
Next History
Cycle through later commands in history which match the current entry.
+
Interrupt Execution
Stop a running program.

Debug menu (Shell window only)?

-
Go to File/Line
-
Look on the current line. with the cursor, and the line above for a filename +
Go to File/Line
Look on the current line. with the cursor, and the line above for a filename and line number. If found, open the file if not already open, and show the line. Use this to view source lines referenced in an exception traceback and lines found by Find in Files. Also available in the context menu of the Shell window and Output windows.
-
Debugger (toggle)
-
When activated, code entered in the Shell or run from an Editor will run +
Debugger (toggle)
When activated, code entered in the Shell or run from an Editor will run under the debugger. In the Editor, breakpoints can be set with the context menu. This feature is still incomplete and somewhat experimental.
-
Stack Viewer
-
Show the stack traceback of the last exception in a tree widget, with +
Stack Viewer
Show the stack traceback of the last exception in a tree widget, with access to locals and globals.
-
Auto-open Stack Viewer
-
Toggle automatically opening the stack viewer on an unhandled exception.
+
Auto-open Stack Viewer
Toggle automatically opening the stack viewer on an unhandled exception.

Options menu (Shell and Editor)?

-
Configure IDLE
-
Open a configuration dialog and change preferences for the following: +
Configure IDLE
Open a configuration dialog and change preferences for the following: fonts, indentation, keybindings, text color themes, startup windows and size, additional help sources, and extensions. On macOS, open the configuration dialog by selecting Preferences in the application menu. For more, see Setting preferences under Help and preferences.
-
Show/Hide Code Context (Editor Window only)
-
Open a pane at the top of the edit window which shows the block context +
Show/Hide Code Context (Editor Window only)
Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window. See Code Context in the Editing and Navigation section below.
-
Zoom/Restore Height
-
Toggles the window between normal size and maximum height. The initial size +
Zoom/Restore Height
Toggles the window between normal size and maximum height. The initial size defaults to 40 lines by 80 chars unless changed on the General tab of the Configure IDLE dialog. The maximum height for a screen is determined by momentarily maximizing a window the first time one is zoomed on the screen. @@ -345,16 +291,12 @@

Window menu (Shell and Editor)

Help menu (Shell and Editor)?

-
About IDLE
-
Display version, copyright, license, credits, and more.
-
IDLE Help
-
Display this IDLE document, detailing the menu options, basic editing and +
About IDLE
Display version, copyright, license, credits, and more.
+
IDLE Help
Display this IDLE document, detailing the menu options, basic editing and navigation, and other tips.
-
Python Docs
-
Access local Python documentation, if installed, or start a web browser +
Python Docs
Access local Python documentation, if installed, or start a web browser and open docs.python.org showing the latest Python documentation.
-
Turtle Demo
-
Run the turtledemo module with example Python code and turtle drawings.
+
Turtle Demo
Run the turtledemo module with example Python code and turtle drawings.

Additional help sources may be added here with the Configure IDLE dialog under the General tab. See the Help sources subsection below @@ -365,32 +307,25 @@

Help menu (Shell and Editor) -
Cut
-
Copy selection into the system-wide clipboard; then delete the selection.
-
Copy
-
Copy selection into the system-wide clipboard.
-
Paste
-
Insert contents of the system-wide clipboard into the current window.
+
Cut
Copy selection into the system-wide clipboard; then delete the selection.
+
Copy
Copy selection into the system-wide clipboard.
+
Paste
Insert contents of the system-wide clipboard into the current window.

Editor windows also have breakpoint functions. Lines with a breakpoint set are specially marked. Breakpoints only have an effect when running under the debugger. Breakpoints for a file are saved in the user?s .idlerc directory.

-
Set Breakpoint
-
Set a breakpoint on the current line.
-
Clear Breakpoint
-
Clear the breakpoint on that line.
+
Set Breakpoint
Set a breakpoint on the current line.
+
Clear Breakpoint
Clear the breakpoint on that line.

Shell and Output windows also have the following.

-
Go to file/line
-
Same as in Debug menu.
+
Go to file/line
Same as in Debug menu.

The Shell window also has an output squeezing facility explained in the Python Shell window subsection below.

-
Squeeze
-
If the cursor is over an output line, squeeze all the output between +
Squeeze
If the cursor is over an output line, squeeze all the output between the code above and the prompt below down to a ?Squeezed text? label.
@@ -670,6 +605,9 @@

Running user codeprint or write to sys.stdout or sys.stderr, IDLE should be started in a command line window. The secondary subprocess will then be attached to that window for input and output.

+

The IDLE code running in the execution process adds frames to the call stack +that would not be there otherwise. IDLE wraps sys.getrecursionlimit and +sys.setrecursionlimit to reduce their visibility.

If sys is reset by user code, such as with importlib.reload(sys), IDLE?s changes are lost and input from the keyboard and output to the screen will not work correctly.

@@ -772,7 +710,7 @@

Running without a subprocess -

Deprecated since version 3.4.

+

Deprecated since version 3.4.

@@ -885,7 +823,7 @@

Table of Contents

Previous topic

tkinter.scrolledtext ? Scrolled Text Widget

+ title="previous chapter">tkinter.scrolledtext ? Scrolled Text Widget

Next topic

Other Graphical User Interface Packages

@@ -957,11 +895,11 @@

Navigation



- Last updated on Jun 17, 2019. + Last updated on Jul 04, 2019. Found a bug?
- Created using Sphinx 1.8.1. + Created using Sphinx 2.1.1. diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py index 46f0235fbfdc..d0f1e9207bb1 100644 --- a/Lib/idlelib/idle_test/test_run.py +++ b/Lib/idlelib/idle_test/test_run.py @@ -6,6 +6,8 @@ from test.support import captured_stderr import io +import sys + class RunTest(unittest.TestCase): @@ -260,5 +262,36 @@ def test_close(self): self.assertRaises(TypeError, f.close, 1) +class TestSysRecursionLimitWrappers(unittest.TestCase): + + def test_bad_setrecursionlimit_calls(self): + run.install_recursionlimit_wrappers() + self.addCleanup(run.uninstall_recursionlimit_wrappers) + f = sys.setrecursionlimit + self.assertRaises(TypeError, f, limit=100) + self.assertRaises(TypeError, f, 100, 1000) + self.assertRaises(ValueError, f, 0) + + def test_roundtrip(self): + run.install_recursionlimit_wrappers() + self.addCleanup(run.uninstall_recursionlimit_wrappers) + + # check that setting the recursion limit works + orig_reclimit = sys.getrecursionlimit() + self.addCleanup(sys.setrecursionlimit, orig_reclimit) + sys.setrecursionlimit(orig_reclimit + 3) + + # check that the new limit is returned by sys.getrecursionlimit() + new_reclimit = sys.getrecursionlimit() + self.assertEqual(new_reclimit, orig_reclimit + 3) + + def test_default_recursion_limit_preserved(self): + orig_reclimit = sys.getrecursionlimit() + run.install_recursionlimit_wrappers() + self.addCleanup(run.uninstall_recursionlimit_wrappers) + new_reclimit = sys.getrecursionlimit() + self.assertEqual(new_reclimit, orig_reclimit) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 6b3928b7bf2b..c6ed76b23a2d 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -4,10 +4,12 @@ f'''{sys.executable} -c "__import__('idlelib.run').run.main()"''' '.run' is needed because __import__ returns idlelib, not idlelib.run. """ +import functools import io import linecache import queue import sys +import textwrap import time import traceback import _thread as thread @@ -305,6 +307,64 @@ def fix_scaling(root): font['size'] = round(-0.75*size) +RECURSIONLIMIT_DELTA = 30 +def install_recursionlimit_wrappers(): + """Install wrappers to always add 30 to the recursion limit.""" + # see: bpo-26806 + + @functools.wraps(sys.setrecursionlimit) + def setrecursionlimit(*args, **kwargs): + # mimic the original sys.setrecursionlimit()'s input handling + if kwargs: + raise TypeError( + "setrecursionlimit() takes no keyword arguments") + try: + limit, = args + except ValueError: + raise TypeError(f"setrecursionlimit() takes exactly one " + f"argument ({len(args)} given)") + if not limit > 0: + raise ValueError( + "recursion limit must be greater or equal than 1") + + return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA) + + setrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ + This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible + uninterruptible loops. + """).strip()) + + @functools.wraps(sys.getrecursionlimit) + def getrecursionlimit(): + return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA + + getrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ + This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate for + the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit. + """).strip()) + + # add the delta to the default recursion limit, to compensate + sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA) + + sys.setrecursionlimit = setrecursionlimit + sys.getrecursionlimit = getrecursionlimit + + +def uninstall_recursionlimit_wrappers(): + """Uninstall the recursion limit wrappers from the sys module. + + IDLE only uses this for tests. Users can import run and call + this to remove the wrapping. + """ + if ( + getattr(sys.setrecursionlimit, '__wrapped__', None) and + getattr(sys.getrecursionlimit, '__wrapped__', None) + ): + sys.setrecursionlimit = sys.setrecursionlimit.__wrapped__ + sys.getrecursionlimit = sys.getrecursionlimit.__wrapped__ + sys.setrecursionlimit(sys.getrecursionlimit() - RECURSIONLIMIT_DELTA) + + class MyRPCServer(rpc.RPCServer): def handle_error(self, request, client_address): @@ -448,6 +508,8 @@ def handle(self): # sys.stdin gets changed from within IDLE's shell. See issue17838. self._keep_stdin = sys.stdin + install_recursionlimit_wrappers() + self.interp = self.get_remote_proxy("interp") rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05) diff --git a/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst b/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst new file mode 100644 index 000000000000..8514bb9292fe --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst @@ -0,0 +1,4 @@ +To compensate for stack frames added by IDLE and avoid possible problems +with low recursion limits, add 30 to limits in the user code execution +process. Subtract 30 when reporting recursion limits to make this addition +mostly transparent. From webhook-mailer at python.org Sat Jul 6 17:40:31 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sat, 06 Jul 2019 21:40:31 -0000 Subject: [Python-checkins] bpo-37487: Fix PyList_GetItem index description. (GH-14623) Message-ID: https://github.com/python/cpython/commit/f8709e804d16ec5d44b1d2f00d59a0f78df7b792 commit: f8709e804d16ec5d44b1d2f00d59a0f78df7b792 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-06T17:40:27-04:00 summary: bpo-37487: Fix PyList_GetItem index description. (GH-14623) 0 is a legal index. files: A Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst M Doc/c-api/list.rst diff --git a/Doc/c-api/list.rst b/Doc/c-api/list.rst index 279783a243a1..dc9026605be3 100644 --- a/Doc/c-api/list.rst +++ b/Doc/c-api/list.rst @@ -59,9 +59,9 @@ List Objects .. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index) Return the object at position *index* in the list pointed to by *list*. The - position must be positive, indexing from the end of the list is not - supported. If *index* is out of bounds, return *NULL* and set an - :exc:`IndexError` exception. + position must be non-negative; indexing from the end of the list is not + supported. If *index* is out of bounds (<0 or >=len(list)), + return *NULL* and set an :exc:`IndexError` exception. .. c:function:: PyObject* PyList_GET_ITEM(PyObject *list, Py_ssize_t i) diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst new file mode 100644 index 000000000000..605d08c3c040 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst @@ -0,0 +1 @@ +Fix PyList_GetItem index description to include 0. From webhook-mailer at python.org Sat Jul 6 17:55:00 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sat, 06 Jul 2019 21:55:00 -0000 Subject: [Python-checkins] bpo-37487: Fix PyList_GetItem index description. (GH-14623) (GH-14624) Message-ID: https://github.com/python/cpython/commit/ad3720359faa933d04bde3d3222fd54e73ee7feb commit: ad3720359faa933d04bde3d3222fd54e73ee7feb branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-06T17:54:56-04:00 summary: bpo-37487: Fix PyList_GetItem index description. (GH-14623) (GH-14624) 0 is a legal index. (cherry picked from commit f8709e804d16ec5d44b1d2f00d59a0f78df7b792) Co-authored-by: Terry Jan Reedy files: A Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst M Doc/c-api/list.rst diff --git a/Doc/c-api/list.rst b/Doc/c-api/list.rst index 279783a243a1..dc9026605be3 100644 --- a/Doc/c-api/list.rst +++ b/Doc/c-api/list.rst @@ -59,9 +59,9 @@ List Objects .. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index) Return the object at position *index* in the list pointed to by *list*. The - position must be positive, indexing from the end of the list is not - supported. If *index* is out of bounds, return *NULL* and set an - :exc:`IndexError` exception. + position must be non-negative; indexing from the end of the list is not + supported. If *index* is out of bounds (<0 or >=len(list)), + return *NULL* and set an :exc:`IndexError` exception. .. c:function:: PyObject* PyList_GET_ITEM(PyObject *list, Py_ssize_t i) diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst new file mode 100644 index 000000000000..605d08c3c040 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst @@ -0,0 +1 @@ +Fix PyList_GetItem index description to include 0. From webhook-mailer at python.org Sat Jul 6 17:55:23 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sat, 06 Jul 2019 21:55:23 -0000 Subject: [Python-checkins] bpo-37487: Fix PyList_GetItem index description. (GH-14623) (GH-14625) Message-ID: https://github.com/python/cpython/commit/9c930d076a7225694b369d30636a29acb556c2be commit: 9c930d076a7225694b369d30636a29acb556c2be branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-06T17:55:19-04:00 summary: bpo-37487: Fix PyList_GetItem index description. (GH-14623) (GH-14625) 0 is a legal index. (cherry picked from commit f8709e804d16ec5d44b1d2f00d59a0f78df7b792) Co-authored-by: Terry Jan Reedy files: A Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst M Doc/c-api/list.rst diff --git a/Doc/c-api/list.rst b/Doc/c-api/list.rst index 5b263a7b1cdf..a5cd634a454a 100644 --- a/Doc/c-api/list.rst +++ b/Doc/c-api/list.rst @@ -59,9 +59,9 @@ List Objects .. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index) Return the object at position *index* in the list pointed to by *list*. The - position must be positive, indexing from the end of the list is not - supported. If *index* is out of bounds, return *NULL* and set an - :exc:`IndexError` exception. + position must be non-negative; indexing from the end of the list is not + supported. If *index* is out of bounds (<0 or >=len(list)), + return *NULL* and set an :exc:`IndexError` exception. .. c:function:: PyObject* PyList_GET_ITEM(PyObject *list, Py_ssize_t i) diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst new file mode 100644 index 000000000000..605d08c3c040 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst @@ -0,0 +1 @@ +Fix PyList_GetItem index description to include 0. From webhook-mailer at python.org Sat Jul 6 17:55:45 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sat, 06 Jul 2019 21:55:45 -0000 Subject: [Python-checkins] bpo-37487: Fix PyList_GetItem index description. (GH-14623) (GH-14626) Message-ID: https://github.com/python/cpython/commit/dd3862e167d573b6e9a3348c365229ca958d1f1f commit: dd3862e167d573b6e9a3348c365229ca958d1f1f branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-06T17:55:41-04:00 summary: bpo-37487: Fix PyList_GetItem index description. (GH-14623) (GH-14626) 0 is a legal index. (cherry picked from commit f8709e804d16ec5d44b1d2f00d59a0f78df7b792) Co-authored-by: Terry Jan Reedy files: A Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst M Doc/c-api/list.rst diff --git a/Doc/c-api/list.rst b/Doc/c-api/list.rst index 0aed0f3e8899..a5e4a45492b6 100644 --- a/Doc/c-api/list.rst +++ b/Doc/c-api/list.rst @@ -76,9 +76,9 @@ List Objects .. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index) Return the object at position *index* in the list pointed to by *list*. The - position must be positive, indexing from the end of the list is not - supported. If *index* is out of bounds, return *NULL* and set an - :exc:`IndexError` exception. + position must be non-negative; indexing from the end of the list is not + supported. If *index* is out of bounds (<0 or >=len(list)), + return *NULL* and set an :exc:`IndexError` exception. .. versionchanged:: 2.5 This function used an :c:type:`int` for *index*. This might require diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst new file mode 100644 index 000000000000..605d08c3c040 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst @@ -0,0 +1 @@ +Fix PyList_GetItem index description to include 0. From webhook-mailer at python.org Sat Jul 6 18:13:07 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sat, 06 Jul 2019 22:13:07 -0000 Subject: [Python-checkins] bpo-37456: Slash ('/') is now part of syntax. (GH-14627) Message-ID: https://github.com/python/cpython/commit/6f2a8c08573c71b78d2f6e2bfaf31641a0cd092b commit: 6f2a8c08573c71b78d2f6e2bfaf31641a0cd092b branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-06T18:13:02-04:00 summary: bpo-37456: Slash ('/') is now part of syntax. (GH-14627) files: A Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst M Doc/faq/programming.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index a00c6a053ef1..a36fa4aefe88 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -800,10 +800,6 @@ an error:: File "", line 1, in TypeError: pow() takes no keyword arguments -Note that as of this writing this is only documentational and no valid syntax -in Python, although there is :pep:`570`, which proposes a syntax for -position-only parameters in Python. - Numbers and strings =================== diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst new file mode 100644 index 000000000000..4d158733b0ea --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst @@ -0,0 +1 @@ +Slash ('/') is now part of syntax. From webhook-mailer at python.org Sat Jul 6 18:25:50 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sat, 06 Jul 2019 22:25:50 -0000 Subject: [Python-checkins] bpo-37456: Slash ('/') is now part of syntax. (GH-14627) (GH-14628) Message-ID: https://github.com/python/cpython/commit/90631f9bc5f78ec6cdc2096d5c5ae26e41e5f150 commit: 90631f9bc5f78ec6cdc2096d5c5ae26e41e5f150 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-06T18:25:47-04:00 summary: bpo-37456: Slash ('/') is now part of syntax. (GH-14627) (GH-14628) (cherry picked from commit 6f2a8c08573c71b78d2f6e2bfaf31641a0cd092b) Co-authored-by: Terry Jan Reedy files: A Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst M Doc/faq/programming.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index a00c6a053ef1..a36fa4aefe88 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -800,10 +800,6 @@ an error:: File "", line 1, in TypeError: pow() takes no keyword arguments -Note that as of this writing this is only documentational and no valid syntax -in Python, although there is :pep:`570`, which proposes a syntax for -position-only parameters in Python. - Numbers and strings =================== diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst new file mode 100644 index 000000000000..4d158733b0ea --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst @@ -0,0 +1 @@ +Slash ('/') is now part of syntax. From webhook-mailer at python.org Sat Jul 6 21:20:20 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sun, 07 Jul 2019 01:20:20 -0000 Subject: [Python-checkins] bpo-37478: Specify possible exceptions for os.chdir() (GH-14611) Message-ID: https://github.com/python/cpython/commit/0717b4d9b3899c5c2ca13031e4ff619a15a4d368 commit: 0717b4d9b3899c5c2ca13031e4ff619a15a4d368 branch: master author: Kyle Stanley committer: Terry Jan Reedy date: 2019-07-06T21:20:15-04:00 summary: bpo-37478: Specify possible exceptions for os.chdir() (GH-14611) files: A Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 6e8df88cf052..760c05c6e52d 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1599,6 +1599,9 @@ features: This function can support :ref:`specifying a file descriptor `. The descriptor must refer to an opened directory, not an open file. + This function can raise :exc:`OSError` subclasses such as + :exc:`FileNotFoundError`, :exc:`PermissionError`, and :exc:`NotADirectoryError`. + .. versionadded:: 3.3 Added support for specifying *path* as a file descriptor on some platforms. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst new file mode 100644 index 000000000000..55b136621762 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst @@ -0,0 +1 @@ +Added possible exceptions to the description of os.chdir(). \ No newline at end of file From webhook-mailer at python.org Sat Jul 6 22:18:53 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sun, 07 Jul 2019 02:18:53 -0000 Subject: [Python-checkins] bpo-37478: Specify possible exceptions for os.chdir() (GH-14611) (GH-14629) Message-ID: https://github.com/python/cpython/commit/4e6bfc4c605d92d2395fbcded9cf45cdd1ced810 commit: 4e6bfc4c605d92d2395fbcded9cf45cdd1ced810 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-06T22:18:50-04:00 summary: bpo-37478: Specify possible exceptions for os.chdir() (GH-14611) (GH-14629) (cherry picked from commit 0717b4d9b3899c5c2ca13031e4ff619a15a4d368) Co-authored-by: Kyle Stanley files: A Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 6e8df88cf052..760c05c6e52d 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1599,6 +1599,9 @@ features: This function can support :ref:`specifying a file descriptor `. The descriptor must refer to an opened directory, not an open file. + This function can raise :exc:`OSError` subclasses such as + :exc:`FileNotFoundError`, :exc:`PermissionError`, and :exc:`NotADirectoryError`. + .. versionadded:: 3.3 Added support for specifying *path* as a file descriptor on some platforms. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst new file mode 100644 index 000000000000..55b136621762 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst @@ -0,0 +1 @@ +Added possible exceptions to the description of os.chdir(). \ No newline at end of file From webhook-mailer at python.org Sat Jul 6 22:19:11 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sun, 07 Jul 2019 02:19:11 -0000 Subject: [Python-checkins] bpo-37478: Specify possible exceptions for os.chdir() (GH-14611) (GH-14630) Message-ID: https://github.com/python/cpython/commit/1dd65075955337183ba2f78cb11a1eec2466dc74 commit: 1dd65075955337183ba2f78cb11a1eec2466dc74 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-06T22:19:08-04:00 summary: bpo-37478: Specify possible exceptions for os.chdir() (GH-14611) (GH-14630) (cherry picked from commit 0717b4d9b3899c5c2ca13031e4ff619a15a4d368) Co-authored-by: Kyle Stanley files: A Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 63d118d40f9a..856ef2b2dbe4 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1566,6 +1566,9 @@ features: This function can support :ref:`specifying a file descriptor `. The descriptor must refer to an opened directory, not an open file. + This function can raise :exc:`OSError` subclasses such as + :exc:`FileNotFoundError`, :exc:`PermissionError`, and :exc:`NotADirectoryError`. + .. versionadded:: 3.3 Added support for specifying *path* as a file descriptor on some platforms. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst new file mode 100644 index 000000000000..55b136621762 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst @@ -0,0 +1 @@ +Added possible exceptions to the description of os.chdir(). \ No newline at end of file From webhook-mailer at python.org Sat Jul 6 22:44:04 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sun, 07 Jul 2019 02:44:04 -0000 Subject: [Python-checkins] bpo-37478: Add missing 'and'. (GH-14631) Message-ID: https://github.com/python/cpython/commit/a9b40e4546ca631e5ab41376b5b72e8f296f557d commit: a9b40e4546ca631e5ab41376b5b72e8f296f557d branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-06T22:44:01-04:00 summary: bpo-37478: Add missing 'and'. (GH-14631) files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 760c05c6e52d..519d5581603b 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1599,7 +1599,7 @@ features: This function can support :ref:`specifying a file descriptor `. The descriptor must refer to an opened directory, not an open file. - This function can raise :exc:`OSError` subclasses such as + This function can raise :exc:`OSError` and subclasses such as :exc:`FileNotFoundError`, :exc:`PermissionError`, and :exc:`NotADirectoryError`. .. versionadded:: 3.3 From webhook-mailer at python.org Sat Jul 6 22:49:42 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 07 Jul 2019 02:49:42 -0000 Subject: [Python-checkins] bpo-37478: Add missing 'and'. (GH-14631) Message-ID: https://github.com/python/cpython/commit/e841a54206c65770aeb2b936cdc830dd4ed8bf9e commit: e841a54206c65770aeb2b936cdc830dd4ed8bf9e branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-06T19:49:39-07:00 summary: bpo-37478: Add missing 'and'. (GH-14631) (cherry picked from commit a9b40e4546ca631e5ab41376b5b72e8f296f557d) Co-authored-by: Terry Jan Reedy files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 856ef2b2dbe4..24f14480ae04 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1566,7 +1566,7 @@ features: This function can support :ref:`specifying a file descriptor `. The descriptor must refer to an opened directory, not an open file. - This function can raise :exc:`OSError` subclasses such as + This function can raise :exc:`OSError` and subclasses such as :exc:`FileNotFoundError`, :exc:`PermissionError`, and :exc:`NotADirectoryError`. .. versionadded:: 3.3 From webhook-mailer at python.org Sat Jul 6 22:50:52 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 07 Jul 2019 02:50:52 -0000 Subject: [Python-checkins] bpo-37478: Add missing 'and'. (GH-14631) Message-ID: https://github.com/python/cpython/commit/e414aa9cb002427a39dfd157cdad156336f93ca9 commit: e414aa9cb002427a39dfd157cdad156336f93ca9 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-06T19:50:48-07:00 summary: bpo-37478: Add missing 'and'. (GH-14631) (cherry picked from commit a9b40e4546ca631e5ab41376b5b72e8f296f557d) Co-authored-by: Terry Jan Reedy files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 760c05c6e52d..519d5581603b 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1599,7 +1599,7 @@ features: This function can support :ref:`specifying a file descriptor `. The descriptor must refer to an opened directory, not an open file. - This function can raise :exc:`OSError` subclasses such as + This function can raise :exc:`OSError` and subclasses such as :exc:`FileNotFoundError`, :exc:`PermissionError`, and :exc:`NotADirectoryError`. .. versionadded:: 3.3 From webhook-mailer at python.org Sun Jul 7 11:40:31 2019 From: webhook-mailer at python.org (Xiang Zhang) Date: Sun, 07 Jul 2019 15:40:31 -0000 Subject: [Python-checkins] bpo-37513: Change ValueError to TypeError in an example in ctypes doc (GH-14615) Message-ID: https://github.com/python/cpython/commit/f6cdd3ff687ebbf8209d793a18a042ea495c4aeb commit: f6cdd3ff687ebbf8209d793a18a042ea495c4aeb branch: master author: Hai Shi committer: Xiang Zhang date: 2019-07-07T23:40:07+08:00 summary: bpo-37513: Change ValueError to TypeError in an example in ctypes doc (GH-14615) files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 2c57cc7ec427..680703d4483f 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -576,7 +576,7 @@ Here is a simple example of a POINT structure, which contains two integers named >>> POINT(1, 2, 3) Traceback (most recent call last): File "", line 1, in - ValueError: too many initializers + TypeError: too many initializers >>> You can, however, build much more complicated structures. A structure can From webhook-mailer at python.org Sun Jul 7 11:46:06 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 07 Jul 2019 15:46:06 -0000 Subject: [Python-checkins] bpo-37513: Change ValueError to TypeError in an example in ctypes doc (GH-14615) Message-ID: https://github.com/python/cpython/commit/bc0a6ced30267d4e21e7566bfd6d14b30d6d1604 commit: bc0a6ced30267d4e21e7566bfd6d14b30d6d1604 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-07T08:45:59-07:00 summary: bpo-37513: Change ValueError to TypeError in an example in ctypes doc (GH-14615) (cherry picked from commit f6cdd3ff687ebbf8209d793a18a042ea495c4aeb) Co-authored-by: Hai Shi files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 60a2ec1c5e89..46a9d23ac392 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -576,7 +576,7 @@ Here is a simple example of a POINT structure, which contains two integers named >>> POINT(1, 2, 3) Traceback (most recent call last): File "", line 1, in - ValueError: too many initializers + TypeError: too many initializers >>> You can, however, build much more complicated structures. A structure can From webhook-mailer at python.org Sun Jul 7 11:46:50 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 07 Jul 2019 15:46:50 -0000 Subject: [Python-checkins] bpo-37513: Change ValueError to TypeError in an example in ctypes doc (GH-14615) Message-ID: https://github.com/python/cpython/commit/3f7d0c9665ca546bb0073376cb83e31dd13b48d9 commit: 3f7d0c9665ca546bb0073376cb83e31dd13b48d9 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-07T08:46:46-07:00 summary: bpo-37513: Change ValueError to TypeError in an example in ctypes doc (GH-14615) (cherry picked from commit f6cdd3ff687ebbf8209d793a18a042ea495c4aeb) Co-authored-by: Hai Shi files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 2c57cc7ec427..680703d4483f 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -576,7 +576,7 @@ Here is a simple example of a POINT structure, which contains two integers named >>> POINT(1, 2, 3) Traceback (most recent call last): File "", line 1, in - ValueError: too many initializers + TypeError: too many initializers >>> You can, however, build much more complicated structures. A structure can From webhook-mailer at python.org Sun Jul 7 11:59:18 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 07 Jul 2019 15:59:18 -0000 Subject: [Python-checkins] bpo-37513: Change ValueError to TypeError in an example in ctypes doc (GH-14615) Message-ID: https://github.com/python/cpython/commit/00bf4d64ecb01027be40c32d822e47e55d6b5c76 commit: 00bf4d64ecb01027be40c32d822e47e55d6b5c76 branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-07T08:59:14-07:00 summary: bpo-37513: Change ValueError to TypeError in an example in ctypes doc (GH-14615) (cherry picked from commit f6cdd3ff687ebbf8209d793a18a042ea495c4aeb) Co-authored-by: Hai Shi files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index df9ccbf39084..6a5299145f95 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -565,7 +565,7 @@ Here is a simple example of a POINT structure, which contains two integers named >>> POINT(1, 2, 3) Traceback (most recent call last): File "", line 1, in - ValueError: too many initializers + TypeError: too many initializers >>> You can, however, build much more complicated structures. A structure can From webhook-mailer at python.org Sun Jul 7 17:37:54 2019 From: webhook-mailer at python.org (Jason R. Coombs) Date: Sun, 07 Jul 2019 21:37:54 -0000 Subject: [Python-checkins] bpo-37520: Correct behavior for zipfile.Path.parent (GH-14638) Message-ID: https://github.com/python/cpython/commit/38f44b4a4adc37e8f5f8971917d8b3145f351a56 commit: 38f44b4a4adc37e8f5f8971917d8b3145f351a56 branch: master author: Jason R. Coombs committer: GitHub date: 2019-07-07T17:37:50-04:00 summary: bpo-37520: Correct behavior for zipfile.Path.parent (GH-14638) * bpo-37520: Correct behavior for zipfile.Path.parent * ?? Added by blurb_it. files: A Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst M Lib/test/test_zipfile.py M Lib/zipfile.py diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 19b550f80187..0c8ffcdbf14a 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -2514,5 +2514,16 @@ def test_parent(self): assert (root / 'a').parent.at == '' assert (root / 'a' / 'b').parent.at == 'a/' + def test_dir_parent(self): + for zipfile_abcde in self.zipfile_abcde(): + root = zipfile.Path(zipfile_abcde) + assert (root / 'b').parent.at == '' + assert (root / 'b/').parent.at == '' + + def test_missing_dir_parent(self): + for zipfile_abcde in self.zipfile_abcde(): + root = zipfile.Path(zipfile_abcde) + assert (root / 'missing dir/').parent.at == '' + if __name__ == "__main__": unittest.main() diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 62f2fd27d3ce..3c1f1235034a 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -2236,7 +2236,7 @@ def _add_implied_dirs(names): @property def parent(self): - parent_at = posixpath.dirname(self.at) + parent_at = posixpath.dirname(self.at.rstrip('/')) if parent_at: parent_at += '/' return self._next(parent_at) diff --git a/Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst b/Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst new file mode 100644 index 000000000000..6584d3ebe1ed --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst @@ -0,0 +1 @@ +Correct behavior for zipfile.Path.parent when the path object identifies a subdirectory. \ No newline at end of file From webhook-mailer at python.org Sun Jul 7 18:05:57 2019 From: webhook-mailer at python.org (Jason R. Coombs) Date: Sun, 07 Jul 2019 22:05:57 -0000 Subject: [Python-checkins] bpo-37520: Correct behavior for zipfile.Path.parent (GH-14638) (GH-14641) Message-ID: https://github.com/python/cpython/commit/66905d14672517d50dc8ba516b9839f9ddbcc131 commit: 66905d14672517d50dc8ba516b9839f9ddbcc131 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Jason R. Coombs date: 2019-07-07T18:05:53-04:00 summary: bpo-37520: Correct behavior for zipfile.Path.parent (GH-14638) (GH-14641) * bpo-37520: Correct behavior for zipfile.Path.parent * ?? Added by blurb_it. (cherry picked from commit 38f44b4a4adc37e8f5f8971917d8b3145f351a56) Co-authored-by: Jason R. Coombs files: A Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst M Lib/test/test_zipfile.py M Lib/zipfile.py diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 19b550f80187..0c8ffcdbf14a 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -2514,5 +2514,16 @@ def test_parent(self): assert (root / 'a').parent.at == '' assert (root / 'a' / 'b').parent.at == 'a/' + def test_dir_parent(self): + for zipfile_abcde in self.zipfile_abcde(): + root = zipfile.Path(zipfile_abcde) + assert (root / 'b').parent.at == '' + assert (root / 'b/').parent.at == '' + + def test_missing_dir_parent(self): + for zipfile_abcde in self.zipfile_abcde(): + root = zipfile.Path(zipfile_abcde) + assert (root / 'missing dir/').parent.at == '' + if __name__ == "__main__": unittest.main() diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 62f2fd27d3ce..3c1f1235034a 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -2236,7 +2236,7 @@ def _add_implied_dirs(names): @property def parent(self): - parent_at = posixpath.dirname(self.at) + parent_at = posixpath.dirname(self.at.rstrip('/')) if parent_at: parent_at += '/' return self._next(parent_at) diff --git a/Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst b/Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst new file mode 100644 index 000000000000..6584d3ebe1ed --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst @@ -0,0 +1 @@ +Correct behavior for zipfile.Path.parent when the path object identifies a subdirectory. \ No newline at end of file From webhook-mailer at python.org Mon Jul 8 04:19:33 2019 From: webhook-mailer at python.org (Inada Naoki) Date: Mon, 08 Jul 2019 08:19:33 -0000 Subject: [Python-checkins] bpo-37337: Add _PyObject_CallMethodNoArgs() (GH-14267) Message-ID: https://github.com/python/cpython/commit/762f93ff2efd6b7ef0177cad57939c0ab2002eac commit: 762f93ff2efd6b7ef0177cad57939c0ab2002eac branch: master author: Jeroen Demeyer committer: Inada Naoki date: 2019-07-08T17:19:25+09:00 summary: bpo-37337: Add _PyObject_CallMethodNoArgs() (GH-14267) files: M Doc/c-api/object.rst M Include/cpython/abstract.h M Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst M Modules/_asynciomodule.c M Modules/_collectionsmodule.c M Modules/_ctypes/_ctypes.c M Modules/_cursesmodule.c M Modules/_datetimemodule.c M Modules/_dbmmodule.c M Modules/_gdbmmodule.c M Modules/_io/_iomodule.c M Modules/_io/bufferedio.c M Modules/_io/iobase.c M Modules/_io/stringio.c M Modules/_io/textio.c M Modules/_pickle.c M Modules/_posixsubprocess.c M Modules/_sqlite/connection.c M Modules/_sqlite/cursor.c M Modules/_sqlite/module.c M Modules/_threadmodule.c M Modules/faulthandler.c M Modules/mmapmodule.c M Modules/ossaudiodev.c M Modules/selectmodule.c M Objects/abstract.c M Objects/descrobject.c M Objects/fileobject.c M Objects/odictobject.c M Objects/typeobject.c M Objects/weakrefobject.c M Python/bltinmodule.c M Python/errors.c M Python/import.c M Python/pylifecycle.c M Python/pythonrun.c M Python/traceback.c diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 6d138558d60a..8ca034452954 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -364,6 +364,17 @@ Object Protocol *NULL* on failure. +.. c:function:: PyObject* _PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name) + + Call a method of the Python object *obj* without arguments, + where the name of the method is given as a Python string object in *name*. + + Return the result of the call on success, or raise an exception and return + *NULL* on failure. + + .. versionadded:: 3.9 + + .. c:function:: PyObject* _PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) Call a callable Python object *callable*, using diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h index c9a8a0754588..e9a23195f414 100644 --- a/Include/cpython/abstract.h +++ b/Include/cpython/abstract.h @@ -156,6 +156,13 @@ PyAPI_FUNC(PyObject *) _PyObject_VectorcallMethod( PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static inline PyObject * +_PyObject_CallMethodNoArgs(PyObject *self, PyObject *name) +{ + return _PyObject_VectorcallMethod(name, &self, + 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); +} + /* Like PyObject_CallMethod(), but expect a _Py_Identifier* as the method name. */ PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj, @@ -184,6 +191,13 @@ _PyObject_VectorcallMethodId( return _PyObject_VectorcallMethod(oname, args, nargsf, kwnames); } +static inline PyObject * +_PyObject_CallMethodIdNoArgs(PyObject *self, _Py_Identifier *name) +{ + return _PyObject_VectorcallMethodId(name, &self, + 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); +} + PyAPI_FUNC(int) _PyObject_HasLen(PyObject *o); /* Guess the size of object 'o' using len(o) or o.__length_hint__(). diff --git a/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst b/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst index 96ba2d0ec085..df0e807e1166 100644 --- a/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst +++ b/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst @@ -1 +1,2 @@ -Add :c:func:`_PyObject_VectorcallMethod` for fast calling of methods. +Add fast functions for calling methods: :c:func:`_PyObject_VectorcallMethod` +and :c:func:`_PyObject_CallMethodNoArgs` diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 443acc5723c3..6c469d2a2ec5 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -332,7 +332,7 @@ get_event_loop(void) return NULL; } - loop = _PyObject_CallMethodId(policy, &PyId_get_event_loop, NULL); + loop = _PyObject_CallMethodIdNoArgs(policy, &PyId_get_event_loop); Py_DECREF(policy); return loop; } @@ -493,7 +493,7 @@ future_init(FutureObj *fut, PyObject *loop) } fut->fut_loop = loop; - res = _PyObject_CallMethodId(fut->fut_loop, &PyId_get_debug, NULL); + res = _PyObject_CallMethodIdNoArgs(fut->fut_loop, &PyId_get_debug); if (res == NULL) { return -1; } @@ -1295,9 +1295,8 @@ FutureObj_repr(FutureObj *fut) ENSURE_FUTURE_ALIVE(fut) - PyObject *rinfo = _PyObject_CallMethodIdObjArgs((PyObject*)fut, - &PyId__repr_info, - NULL); + PyObject *rinfo = _PyObject_CallMethodIdNoArgs((PyObject*)fut, + &PyId__repr_info); if (rinfo == NULL) { return NULL; } @@ -2197,8 +2196,7 @@ _asyncio_Task_cancel_impl(TaskObj *self) PyObject *res; int is_true; - res = _PyObject_CallMethodId( - self->task_fut_waiter, &PyId_cancel, NULL); + res = _PyObject_CallMethodIdNoArgs(self->task_fut_waiter, &PyId_cancel); if (res == NULL) { return NULL; } @@ -2735,7 +2733,7 @@ task_step_impl(TaskObj *task, PyObject *exc) if (task->task_must_cancel) { PyObject *r; int is_true; - r = _PyObject_CallMethodId(result, &PyId_cancel, NULL); + r = _PyObject_CallMethodIdNoArgs(result, &PyId_cancel); if (r == NULL) { return NULL; } @@ -2826,7 +2824,7 @@ task_step_impl(TaskObj *task, PyObject *exc) if (task->task_must_cancel) { PyObject *r; int is_true; - r = _PyObject_CallMethodId(result, &PyId_cancel, NULL); + r = _PyObject_CallMethodIdNoArgs(result, &PyId_cancel); if (r == NULL) { return NULL; } diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 7e9cf8a283a2..1d23973fd056 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2039,7 +2039,7 @@ defdict_reduce(defdictobject *dd, PyObject *Py_UNUSED(ignored)) args = PyTuple_Pack(1, dd->default_factory); if (args == NULL) return NULL; - items = _PyObject_CallMethodId((PyObject *)dd, &PyId_items, NULL); + items = _PyObject_CallMethodIdNoArgs((PyObject *)dd, &PyId_items); if (items == NULL) { Py_DECREF(args); return NULL; diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index c6ae487b4cf0..c1941c16400e 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3974,7 +3974,7 @@ _build_result(PyObject *result, PyObject *callargs, _Py_IDENTIFIER(__ctypes_from_outparam__); v = PyTuple_GET_ITEM(callargs, i); - v = _PyObject_CallMethodId(v, &PyId___ctypes_from_outparam__, NULL); + v = _PyObject_CallMethodIdNoArgs(v, &PyId___ctypes_from_outparam__); if (v == NULL || numretvals == 1) { Py_DECREF(callargs); return v; diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 2435e1c12955..8595b6282c1b 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -2906,7 +2906,7 @@ _curses_getwin(PyObject *module, PyObject *file) if (_Py_set_inheritable(fileno(fp), 0, NULL) < 0) goto error; - data = _PyObject_CallMethodId(file, &PyId_read, NULL); + data = _PyObject_CallMethodIdNoArgs(file, &PyId_read); if (data == NULL) goto error; if (!PyBytes_Check(data)) { diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 2e0211cbbef8..0546368d1df7 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1659,7 +1659,7 @@ time_time(void) if (time != NULL) { _Py_IDENTIFIER(time); - result = _PyObject_CallMethodId(time, &PyId_time, NULL); + result = _PyObject_CallMethodIdNoArgs(time, &PyId_time); Py_DECREF(time); } return result; @@ -1918,7 +1918,7 @@ get_float_as_integer_ratio(PyObject *floatobj) PyObject *ratio; assert(floatobj && PyFloat_Check(floatobj)); - ratio = _PyObject_CallMethodId(floatobj, &PyId_as_integer_ratio, NULL); + ratio = _PyObject_CallMethodIdNoArgs(floatobj, &PyId_as_integer_ratio); if (ratio == NULL) { return NULL; } @@ -3162,7 +3162,7 @@ date_isoformat(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored)) static PyObject * date_str(PyDateTime_Date *self) { - return _PyObject_CallMethodId((PyObject *)self, &PyId_isoformat, NULL); + return _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_isoformat); } @@ -3188,7 +3188,7 @@ date_strftime(PyDateTime_Date *self, PyObject *args, PyObject *kw) &format)) return NULL; - tuple = _PyObject_CallMethodId((PyObject *)self, &PyId_timetuple, NULL); + tuple = _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_timetuple); if (tuple == NULL) return NULL; result = wrap_strftime((PyObject *)self, format, tuple, @@ -4175,7 +4175,7 @@ time_repr(PyDateTime_Time *self) static PyObject * time_str(PyDateTime_Time *self) { - return _PyObject_CallMethodId((PyObject *)self, &PyId_isoformat, NULL); + return _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_isoformat); } static PyObject * diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index ea0a9d6fc957..b317b57e7ae5 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -370,7 +370,7 @@ static PyObject * dbm__exit__(PyObject *self, PyObject *args) { _Py_IDENTIFIER(close); - return _PyObject_CallMethodId(self, &PyId_close, NULL); + return _PyObject_CallMethodIdNoArgs(self, &PyId_close); } diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 77e788752506..dd4a348d136c 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -497,7 +497,7 @@ static PyObject * dbm__exit__(PyObject *self, PyObject *args) { _Py_IDENTIFIER(close); - return _PyObject_CallMethodId(self, &PyId_close, NULL); + return _PyObject_CallMethodIdNoArgs(self, &PyId_close); } static PyMethodDef dbm_methods[] = { diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 5c2f019e840b..96426e0276ab 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -400,7 +400,7 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, /* buffering */ if (buffering < 0) { - PyObject *res = _PyObject_CallMethodId(raw, &PyId_isatty, NULL); + PyObject *res = _PyObject_CallMethodIdNoArgs(raw, &PyId_isatty); if (res == NULL) goto error; isatty = PyLong_AsLong(res); @@ -494,7 +494,7 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode, if (result != NULL) { PyObject *exc, *val, *tb, *close_result; PyErr_Fetch(&exc, &val, &tb); - close_result = _PyObject_CallMethodId(result, &PyId_close, NULL); + close_result = _PyObject_CallMethodIdNoArgs(result, &PyId_close); _PyErr_ChainExceptions(exc, val, tb); Py_XDECREF(close_result); Py_DECREF(result); diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 44e12db6a30e..9e7e5f3d0935 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -461,7 +461,7 @@ static PyObject * buffered_simple_flush(buffered *self, PyObject *args) { CHECK_INITIALIZED(self) - return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_flush, NULL); + return _PyObject_CallMethodNoArgs(self->raw, _PyIO_str_flush); } static int @@ -513,7 +513,7 @@ buffered_close(buffered *self, PyObject *args) } /* flush() will most probably re-take the lock, so drop it first */ LEAVE_BUFFERED(self) - res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL); + res = _PyObject_CallMethodNoArgs((PyObject *)self, _PyIO_str_flush); if (!ENTER_BUFFERED(self)) return NULL; if (res == NULL) @@ -521,7 +521,7 @@ buffered_close(buffered *self, PyObject *args) else Py_DECREF(res); - res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_close, NULL); + res = _PyObject_CallMethodNoArgs(self->raw, _PyIO_str_close); if (self->buffer) { PyMem_Free(self->buffer); @@ -545,7 +545,7 @@ buffered_detach(buffered *self, PyObject *Py_UNUSED(ignored)) { PyObject *raw, *res; CHECK_INITIALIZED(self) - res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL); + res = _PyObject_CallMethodNoArgs((PyObject *)self, _PyIO_str_flush); if (res == NULL) return NULL; Py_DECREF(res); @@ -562,21 +562,21 @@ static PyObject * buffered_seekable(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) - return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_seekable, NULL); + return _PyObject_CallMethodNoArgs(self->raw, _PyIO_str_seekable); } static PyObject * buffered_readable(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) - return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_readable, NULL); + return _PyObject_CallMethodNoArgs(self->raw, _PyIO_str_readable); } static PyObject * buffered_writable(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) - return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_writable, NULL); + return _PyObject_CallMethodNoArgs(self->raw, _PyIO_str_writable); } static PyObject * @@ -599,14 +599,14 @@ static PyObject * buffered_fileno(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) - return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_fileno, NULL); + return _PyObject_CallMethodNoArgs(self->raw, _PyIO_str_fileno); } static PyObject * buffered_isatty(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) - return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_isatty, NULL); + return _PyObject_CallMethodNoArgs(self->raw, _PyIO_str_isatty); } /* Forward decls */ @@ -670,7 +670,7 @@ _buffered_raw_tell(buffered *self) { Py_off_t n; PyObject *res; - res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_tell, NULL); + res = _PyObject_CallMethodNoArgs(self->raw, _PyIO_str_tell); if (res == NULL) return -1; n = PyNumber_AsOff_t(res, PyExc_ValueError); @@ -1350,8 +1350,8 @@ buffered_iternext(buffered *self) line = _buffered_readline(self, -1); } else { - line = PyObject_CallMethodObjArgs((PyObject *)self, - _PyIO_str_readline, NULL); + line = _PyObject_CallMethodNoArgs((PyObject *)self, + _PyIO_str_readline); if (line && !PyBytes_Check(line)) { PyErr_Format(PyExc_OSError, "readline() should have returned a bytes object, " @@ -1566,7 +1566,7 @@ _bufferedreader_read_all(buffered *self) } /* Read until EOF or until read() would block. */ - data = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_read, NULL); + data = _PyObject_CallMethodNoArgs(self->raw, _PyIO_str_read); if (data == NULL) goto cleanup; if (data != Py_None && !PyBytes_Check(data)) { diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 82cc77676523..d51fc944e1a6 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -235,7 +235,7 @@ _io__IOBase_close_impl(PyObject *self) Py_RETURN_NONE; } - res = PyObject_CallMethodObjArgs(self, _PyIO_str_flush, NULL); + res = _PyObject_CallMethodNoArgs(self, _PyIO_str_flush); PyErr_Fetch(&exc, &val, &tb); rc = _PyObject_SetAttrId(self, &PyId___IOBase_closed, Py_True); @@ -281,8 +281,7 @@ iobase_finalize(PyObject *self) finalization process. */ if (_PyObject_SetAttrId(self, &PyId__finalizing, Py_True)) PyErr_Clear(); - res = PyObject_CallMethodObjArgs((PyObject *) self, _PyIO_str_close, - NULL); + res = _PyObject_CallMethodNoArgs((PyObject *)self, _PyIO_str_close); /* Silencing I/O errors is bad, but printing spurious tracebacks is equally as bad, and potentially more frequent (because of shutdown issues). */ @@ -383,7 +382,7 @@ _io__IOBase_seekable_impl(PyObject *self) PyObject * _PyIOBase_check_seekable(PyObject *self, PyObject *args) { - PyObject *res = PyObject_CallMethodObjArgs(self, _PyIO_str_seekable, NULL); + PyObject *res = _PyObject_CallMethodNoArgs(self, _PyIO_str_seekable); if (res == NULL) return NULL; if (res != Py_True) { @@ -416,7 +415,7 @@ _io__IOBase_readable_impl(PyObject *self) PyObject * _PyIOBase_check_readable(PyObject *self, PyObject *args) { - PyObject *res = PyObject_CallMethodObjArgs(self, _PyIO_str_readable, NULL); + PyObject *res = _PyObject_CallMethodNoArgs(self, _PyIO_str_readable); if (res == NULL) return NULL; if (res != Py_True) { @@ -449,7 +448,7 @@ _io__IOBase_writable_impl(PyObject *self) PyObject * _PyIOBase_check_writable(PyObject *self, PyObject *args) { - PyObject *res = PyObject_CallMethodObjArgs(self, _PyIO_str_writable, NULL); + PyObject *res = _PyObject_CallMethodNoArgs(self, _PyIO_str_writable); if (res == NULL) return NULL; if (res != Py_True) { @@ -478,7 +477,7 @@ iobase_enter(PyObject *self, PyObject *args) static PyObject * iobase_exit(PyObject *self, PyObject *args) { - return PyObject_CallMethodObjArgs(self, _PyIO_str_close, NULL); + return _PyObject_CallMethodNoArgs(self, _PyIO_str_close); } /* Lower-level APIs */ @@ -656,7 +655,7 @@ iobase_iter(PyObject *self) static PyObject * iobase_iternext(PyObject *self) { - PyObject *line = PyObject_CallMethodObjArgs(self, _PyIO_str_readline, NULL); + PyObject *line = _PyObject_CallMethodNoArgs(self, _PyIO_str_readline); if (line == NULL) return NULL; @@ -921,7 +920,7 @@ _io__RawIOBase_read_impl(PyObject *self, Py_ssize_t n) if (n < 0) { _Py_IDENTIFIER(readall); - return _PyObject_CallMethodId(self, &PyId_readall, NULL); + return _PyObject_CallMethodIdNoArgs(self, &PyId_readall); } /* TODO: allocate a bytes object directly instead and manually construct diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 9e9724db2d33..810cad6d63ce 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -408,8 +408,8 @@ stringio_iternext(stringio *self) } else { /* XXX is subclassing StringIO really supported? */ - line = PyObject_CallMethodObjArgs((PyObject *)self, - _PyIO_str_readline, NULL); + line = _PyObject_CallMethodNoArgs((PyObject *)self, + _PyIO_str_readline); if (line && !PyUnicode_Check(line)) { PyErr_Format(PyExc_OSError, "readline() should have returned a str object, " diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 021231e4c6b5..ed1dc005c69a 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -527,8 +527,8 @@ _io_IncrementalNewlineDecoder_getstate_impl(nldecoder_object *self) unsigned long long flag; if (self->decoder != Py_None) { - PyObject *state = PyObject_CallMethodObjArgs(self->decoder, - _PyIO_str_getstate, NULL); + PyObject *state = _PyObject_CallMethodNoArgs(self->decoder, + _PyIO_str_getstate); if (state == NULL) return NULL; if (!PyTuple_Check(state)) { @@ -601,7 +601,7 @@ _io_IncrementalNewlineDecoder_reset_impl(nldecoder_object *self) self->seennl = 0; self->pendingcr = 0; if (self->decoder != Py_None) - return PyObject_CallMethodObjArgs(self->decoder, _PyIO_str_reset, NULL); + return _PyObject_CallMethodNoArgs(self->decoder, _PyIO_str_reset); else Py_RETURN_NONE; } @@ -862,7 +862,7 @@ _textiowrapper_set_decoder(textio *self, PyObject *codec_info, PyObject *res; int r; - res = _PyObject_CallMethodId(self->buffer, &PyId_readable, NULL); + res = _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_readable); if (res == NULL) return -1; @@ -917,7 +917,7 @@ _textiowrapper_set_encoder(textio *self, PyObject *codec_info, PyObject *res; int r; - res = _PyObject_CallMethodId(self->buffer, &PyId_writable, NULL); + res = _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_writable); if (res == NULL) return -1; @@ -963,8 +963,8 @@ _textiowrapper_fix_encoder_state(textio *self) self->encoding_start_of_stream = 1; - PyObject *cookieObj = PyObject_CallMethodObjArgs( - self->buffer, _PyIO_str_tell, NULL); + PyObject *cookieObj = _PyObject_CallMethodNoArgs( + self->buffer, _PyIO_str_tell); if (cookieObj == NULL) { return -1; } @@ -1126,7 +1126,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, state = IO_STATE(); if (state == NULL) goto error; - fileno = _PyObject_CallMethodId(buffer, &PyId_fileno, NULL); + fileno = _PyObject_CallMethodIdNoArgs(buffer, &PyId_fileno); /* Ignore only AttributeError and UnsupportedOperation */ if (fileno == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError) || @@ -1241,7 +1241,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, } } - res = _PyObject_CallMethodId(buffer, &PyId_seekable, NULL); + res = _PyObject_CallMethodIdNoArgs(buffer, &PyId_seekable); if (res == NULL) goto error; r = PyObject_IsTrue(res); @@ -1386,7 +1386,7 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding, return NULL; } - PyObject *res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL); + PyObject *res = _PyObject_CallMethodNoArgs((PyObject *)self, _PyIO_str_flush); if (res == NULL) { return NULL; } @@ -1525,7 +1525,7 @@ _io_TextIOWrapper_detach_impl(textio *self) { PyObject *buffer, *res; CHECK_ATTACHED(self); - res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL); + res = _PyObject_CallMethodNoArgs((PyObject *)self, _PyIO_str_flush); if (res == NULL) return NULL; Py_DECREF(res); @@ -1720,7 +1720,7 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) } if (needflush) { - ret = PyObject_CallMethodObjArgs(self->buffer, _PyIO_str_flush, NULL); + ret = _PyObject_CallMethodNoArgs(self->buffer, _PyIO_str_flush); if (ret == NULL) return NULL; Py_DECREF(ret); @@ -1730,7 +1730,7 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) Py_CLEAR(self->snapshot); if (self->decoder) { - ret = _PyObject_CallMethodId(self->decoder, &PyId_reset, NULL); + ret = _PyObject_CallMethodIdNoArgs(self->decoder, &PyId_reset); if (ret == NULL) return NULL; Py_DECREF(ret); @@ -1810,9 +1810,8 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) /* To prepare for tell(), we need to snapshot a point in the file * where the decoder's input buffer is empty. */ - - PyObject *state = PyObject_CallMethodObjArgs(self->decoder, - _PyIO_str_getstate, NULL); + PyObject *state = _PyObject_CallMethodNoArgs(self->decoder, + _PyIO_str_getstate); if (state == NULL) return -1; /* Given this, we know there was a valid snapshot point @@ -1935,7 +1934,7 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) if (n < 0) { /* Read everything */ - PyObject *bytes = _PyObject_CallMethodId(self->buffer, &PyId_read, NULL); + PyObject *bytes = _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_read); PyObject *decoded; if (bytes == NULL) goto fail; @@ -2396,7 +2395,7 @@ _textiowrapper_decoder_setstate(textio *self, cookie_type *cookie) utf-16, that we are expecting a BOM). */ if (cookie->start_pos == 0 && cookie->dec_flags == 0) - res = PyObject_CallMethodObjArgs(self->decoder, _PyIO_str_reset, NULL); + res = _PyObject_CallMethodNoArgs(self->decoder, _PyIO_str_reset); else res = _PyObject_CallMethodId(self->decoder, &PyId_setstate, "((yi))", "", cookie->dec_flags); @@ -2411,7 +2410,7 @@ _textiowrapper_encoder_reset(textio *self, int start_of_stream) { PyObject *res; if (start_of_stream) { - res = PyObject_CallMethodObjArgs(self->encoder, _PyIO_str_reset, NULL); + res = _PyObject_CallMethodNoArgs(self->encoder, _PyIO_str_reset); self->encoding_start_of_stream = 1; } else { @@ -2476,7 +2475,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) * sync the underlying buffer with the current position. */ Py_DECREF(cookieObj); - cookieObj = _PyObject_CallMethodId((PyObject *)self, &PyId_tell, NULL); + cookieObj = _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_tell); if (cookieObj == NULL) goto fail; break; @@ -2492,7 +2491,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) goto fail; } - res = _PyObject_CallMethodId((PyObject *)self, &PyId_flush, NULL); + res = _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_flush); if (res == NULL) goto fail; Py_DECREF(res); @@ -2500,7 +2499,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) textiowrapper_set_decoded_chars(self, NULL); Py_CLEAR(self->snapshot); if (self->decoder) { - res = _PyObject_CallMethodId(self->decoder, &PyId_reset, NULL); + res = _PyObject_CallMethodIdNoArgs(self->decoder, &PyId_reset); if (res == NULL) goto fail; Py_DECREF(res); @@ -2540,7 +2539,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) goto fail; } - res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL); + res = _PyObject_CallMethodNoArgs((PyObject *)self, _PyIO_str_flush); if (res == NULL) goto fail; Py_DECREF(res); @@ -2663,12 +2662,12 @@ _io_TextIOWrapper_tell_impl(textio *self) if (_textiowrapper_writeflush(self) < 0) return NULL; - res = _PyObject_CallMethodId((PyObject *)self, &PyId_flush, NULL); + res = _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_flush); if (res == NULL) goto fail; Py_DECREF(res); - posobj = _PyObject_CallMethodId(self->buffer, &PyId_tell, NULL); + posobj = _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_tell); if (posobj == NULL) goto fail; @@ -2704,15 +2703,15 @@ _io_TextIOWrapper_tell_impl(textio *self) chars_to_skip = self->decoded_chars_used; /* Decoder state will be restored at the end */ - saved_state = PyObject_CallMethodObjArgs(self->decoder, - _PyIO_str_getstate, NULL); + saved_state = _PyObject_CallMethodNoArgs(self->decoder, + _PyIO_str_getstate); if (saved_state == NULL) goto fail; #define DECODER_GETSTATE() do { \ PyObject *dec_buffer; \ - PyObject *_state = PyObject_CallMethodObjArgs(self->decoder, \ - _PyIO_str_getstate, NULL); \ + PyObject *_state = _PyObject_CallMethodNoArgs(self->decoder, \ + _PyIO_str_getstate); \ if (_state == NULL) \ goto fail; \ if (!PyTuple_Check(_state)) { \ @@ -2874,7 +2873,7 @@ _io_TextIOWrapper_truncate_impl(textio *self, PyObject *pos) CHECK_ATTACHED(self) - res = PyObject_CallMethodObjArgs((PyObject *) self, _PyIO_str_flush, NULL); + res = _PyObject_CallMethodNoArgs((PyObject *)self, _PyIO_str_flush); if (res == NULL) return NULL; Py_DECREF(res); @@ -2963,7 +2962,7 @@ _io_TextIOWrapper_fileno_impl(textio *self) /*[clinic end generated code: output=21490a4c3da13e6c input=c488ca83d0069f9b]*/ { CHECK_ATTACHED(self); - return _PyObject_CallMethodId(self->buffer, &PyId_fileno, NULL); + return _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_fileno); } /*[clinic input] @@ -2975,7 +2974,7 @@ _io_TextIOWrapper_seekable_impl(textio *self) /*[clinic end generated code: output=ab223dbbcffc0f00 input=8b005ca06e1fca13]*/ { CHECK_ATTACHED(self); - return _PyObject_CallMethodId(self->buffer, &PyId_seekable, NULL); + return _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_seekable); } /*[clinic input] @@ -2987,7 +2986,7 @@ _io_TextIOWrapper_readable_impl(textio *self) /*[clinic end generated code: output=72ff7ba289a8a91b input=0704ea7e01b0d3eb]*/ { CHECK_ATTACHED(self); - return _PyObject_CallMethodId(self->buffer, &PyId_readable, NULL); + return _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_readable); } /*[clinic input] @@ -2999,7 +2998,7 @@ _io_TextIOWrapper_writable_impl(textio *self) /*[clinic end generated code: output=a728c71790d03200 input=c41740bc9d8636e8]*/ { CHECK_ATTACHED(self); - return _PyObject_CallMethodId(self->buffer, &PyId_writable, NULL); + return _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_writable); } /*[clinic input] @@ -3011,7 +3010,7 @@ _io_TextIOWrapper_isatty_impl(textio *self) /*[clinic end generated code: output=12be1a35bace882e input=fb68d9f2c99bbfff]*/ { CHECK_ATTACHED(self); - return _PyObject_CallMethodId(self->buffer, &PyId_isatty, NULL); + return _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_isatty); } /*[clinic input] @@ -3027,7 +3026,7 @@ _io_TextIOWrapper_flush_impl(textio *self) self->telling = self->seekable; if (_textiowrapper_writeflush(self) < 0) return NULL; - return _PyObject_CallMethodId(self->buffer, &PyId_flush, NULL); + return _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_flush); } /*[clinic input] @@ -3064,13 +3063,13 @@ _io_TextIOWrapper_close_impl(textio *self) else PyErr_Clear(); } - res = _PyObject_CallMethodId((PyObject *)self, &PyId_flush, NULL); + res = _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_flush); if (res == NULL) PyErr_Fetch(&exc, &val, &tb); else Py_DECREF(res); - res = _PyObject_CallMethodId(self->buffer, &PyId_close, NULL); + res = _PyObject_CallMethodIdNoArgs(self->buffer, &PyId_close); if (exc != NULL) { _PyErr_ChainExceptions(exc, val, tb); Py_CLEAR(res); @@ -3092,8 +3091,8 @@ textiowrapper_iternext(textio *self) line = _textiowrapper_readline(self, -1); } else { - line = PyObject_CallMethodObjArgs((PyObject *)self, - _PyIO_str_readline, NULL); + line = _PyObject_CallMethodNoArgs((PyObject *)self, + _PyIO_str_readline); if (line && !PyUnicode_Check(line)) { PyErr_Format(PyExc_OSError, "readline() should have returned a str object, " diff --git a/Modules/_pickle.c b/Modules/_pickle.c index d6d1250d04f7..0b0928f5cd2f 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -3305,7 +3305,7 @@ save_dict(PicklerObject *self, PyObject *obj) } else { _Py_IDENTIFIER(items); - items = _PyObject_CallMethodId(obj, &PyId_items, NULL); + items = _PyObject_CallMethodIdNoArgs(obj, &PyId_items); if (items == NULL) goto error; iter = PyObject_GetIter(items); diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 81a23c6d3300..60c8eab90a15 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -60,7 +60,7 @@ _enable_gc(int need_to_reenable_gc, PyObject *gc_module) if (need_to_reenable_gc) { PyErr_Fetch(&exctype, &val, &tb); - result = _PyObject_CallMethodId(gc_module, &PyId_enable, NULL); + result = _PyObject_CallMethodIdNoArgs(gc_module, &PyId_enable); if (exctype != NULL) { PyErr_Restore(exctype, val, tb); } @@ -606,7 +606,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args) gc_module = PyImport_ImportModule("gc"); if (gc_module == NULL) return NULL; - result = _PyObject_CallMethodId(gc_module, &PyId_isenabled, NULL); + result = _PyObject_CallMethodIdNoArgs(gc_module, &PyId_isenabled); if (result == NULL) { Py_DECREF(gc_module); return NULL; @@ -617,7 +617,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args) Py_DECREF(gc_module); return NULL; } - result = _PyObject_CallMethodId(gc_module, &PyId_disable, NULL); + result = _PyObject_CallMethodIdNoArgs(gc_module, &PyId_disable); if (result == NULL) { Py_DECREF(gc_module); return NULL; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 08604b99a6ac..30a9b889a9df 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -713,7 +713,7 @@ void _pysqlite_final_callback(sqlite3_context* context) PyErr_Fetch(&exception, &value, &tb); restore = 1; - function_result = _PyObject_CallMethodId(*aggregate_instance, &PyId_finalize, NULL); + function_result = _PyObject_CallMethodIdNoArgs(*aggregate_instance, &PyId_finalize); Py_DECREF(*aggregate_instance); @@ -1275,7 +1275,7 @@ PyObject* pysqlite_connection_execute(pysqlite_Connection* self, PyObject* args) PyObject* result = 0; PyObject* method = 0; - cursor = _PyObject_CallMethodId((PyObject*)self, &PyId_cursor, NULL); + cursor = _PyObject_CallMethodIdNoArgs((PyObject*)self, &PyId_cursor); if (!cursor) { goto error; } @@ -1304,7 +1304,7 @@ PyObject* pysqlite_connection_executemany(pysqlite_Connection* self, PyObject* a PyObject* result = 0; PyObject* method = 0; - cursor = _PyObject_CallMethodId((PyObject*)self, &PyId_cursor, NULL); + cursor = _PyObject_CallMethodIdNoArgs((PyObject*)self, &PyId_cursor); if (!cursor) { goto error; } @@ -1333,7 +1333,7 @@ PyObject* pysqlite_connection_executescript(pysqlite_Connection* self, PyObject* PyObject* result = 0; PyObject* method = 0; - cursor = _PyObject_CallMethodId((PyObject*)self, &PyId_cursor, NULL); + cursor = _PyObject_CallMethodIdNoArgs((PyObject*)self, &PyId_cursor); if (!cursor) { goto error; } diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index e6fcac887fe3..6d9685b29642 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -106,7 +106,7 @@ _pysqlite_get_converter(const char *keystr, Py_ssize_t keylen) if (!key) { return NULL; } - upcase_key = _PyObject_CallMethodId(key, &PyId_upper, NULL); + upcase_key = _PyObject_CallMethodIdNoArgs(key, &PyId_upper); Py_DECREF(key); if (!upcase_key) { return NULL; diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 9fe0dc952f0b..d5c353ea7bee 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -203,7 +203,7 @@ static PyObject* module_register_converter(PyObject* self, PyObject* args) } /* convert the name to upper case */ - name = _PyObject_CallMethodId(orig_name, &PyId_upper, NULL); + name = _PyObject_CallMethodIdNoArgs(orig_name, &PyId_upper); if (!name) { goto error; } diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 9ab8e7a0ceb3..6665827fe2ba 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1358,7 +1358,7 @@ thread_excepthook_file(PyObject *file, PyObject *exc_type, PyObject *exc_value, _PyErr_Display(file, exc_type, exc_value, exc_traceback); /* Call file.flush() */ - PyObject *res = _PyObject_CallMethodId(file, &PyId_flush, NULL); + PyObject *res = _PyObject_CallMethodIdNoArgs(file, &PyId_flush); if (!res) { return -1; } diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index f2b5a503f4ee..2331051f7907 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -172,7 +172,7 @@ faulthandler_get_fileno(PyObject **file_ptr) return fd; } - result = _PyObject_CallMethodId(file, &PyId_fileno, NULL); + result = _PyObject_CallMethodIdNoArgs(file, &PyId_fileno); if (result == NULL) return -1; @@ -190,7 +190,7 @@ faulthandler_get_fileno(PyObject **file_ptr) return -1; } - result = _PyObject_CallMethodId(file, &PyId_flush, NULL); + result = _PyObject_CallMethodIdNoArgs(file, &PyId_flush); if (result != NULL) Py_DECREF(result); else { @@ -1305,7 +1305,7 @@ faulthandler_init_enable(void) return -1; } - PyObject *res = _PyObject_CallMethodId(module, &PyId_enable, NULL); + PyObject *res = _PyObject_CallMethodIdNoArgs(module, &PyId_enable); Py_DECREF(module); if (res == NULL) { return -1; diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 9e3414f94e31..51ab3f054f24 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -692,7 +692,7 @@ mmap__exit__method(PyObject *self, PyObject *args) { _Py_IDENTIFIER(close); - return _PyObject_CallMethodId(self, &PyId_close, NULL); + return _PyObject_CallMethodIdNoArgs(self, &PyId_close); } #ifdef MS_WINDOWS diff --git a/Modules/ossaudiodev.c b/Modules/ossaudiodev.c index affaf1d9680b..6e050e4bc4ca 100644 --- a/Modules/ossaudiodev.c +++ b/Modules/ossaudiodev.c @@ -539,7 +539,7 @@ oss_exit(PyObject *self, PyObject *unused) { _Py_IDENTIFIER(close); - PyObject *ret = _PyObject_CallMethodId(self, &PyId_close, NULL); + PyObject *ret = _PyObject_CallMethodIdNoArgs(self, &PyId_close); if (!ret) return NULL; Py_DECREF(ret); diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index ed71d8b0d598..2e06a897132a 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -1634,7 +1634,7 @@ select_epoll___exit___impl(pyEpoll_Object *self, PyObject *exc_type, { _Py_IDENTIFIER(close); - return _PyObject_CallMethodId((PyObject *)self, &PyId_close, NULL); + return _PyObject_CallMethodIdNoArgs((PyObject *)self, &PyId_close); } static PyGetSetDef pyepoll_getsetlist[] = { diff --git a/Objects/abstract.c b/Objects/abstract.c index 86178a74ea38..db1c3064db6f 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2221,7 +2221,7 @@ method_output_as_list(PyObject *o, _Py_Identifier *meth_id) PyObject *it, *result, *meth_output; assert(o != NULL); - meth_output = _PyObject_CallMethodId(o, meth_id, NULL); + meth_output = _PyObject_CallMethodIdNoArgs(o, meth_id); if (meth_output == NULL || PyList_CheckExact(meth_output)) { return meth_output; } diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 99855d8ae44c..edce250c7948 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1019,28 +1019,28 @@ static PyObject * mappingproxy_keys(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(keys); - return _PyObject_CallMethodId(pp->mapping, &PyId_keys, NULL); + return _PyObject_CallMethodIdNoArgs(pp->mapping, &PyId_keys); } static PyObject * mappingproxy_values(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(values); - return _PyObject_CallMethodId(pp->mapping, &PyId_values, NULL); + return _PyObject_CallMethodIdNoArgs(pp->mapping, &PyId_values); } static PyObject * mappingproxy_items(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(items); - return _PyObject_CallMethodId(pp->mapping, &PyId_items, NULL); + return _PyObject_CallMethodIdNoArgs(pp->mapping, &PyId_items); } static PyObject * mappingproxy_copy(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(copy); - return _PyObject_CallMethodId(pp->mapping, &PyId_copy, NULL); + return _PyObject_CallMethodIdNoArgs(pp->mapping, &PyId_copy); } /* WARNING: mappingproxy methods must not give access diff --git a/Objects/fileobject.c b/Objects/fileobject.c index a21e490817eb..0faf7e70b5d8 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -61,7 +61,7 @@ PyFile_GetLine(PyObject *f, int n) } if (n <= 0) { - result = _PyObject_CallMethodIdObjArgs(f, &PyId_readline, NULL); + result = _PyObject_CallMethodIdNoArgs(f, &PyId_readline); } else { result = _PyObject_CallMethodId(f, &PyId_readline, "i", n); diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 4c9ae3bc9346..dfbd30a976ca 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -920,7 +920,7 @@ odict_reduce(register PyODictObject *od, PyObject *Py_UNUSED(ignored)) if (args == NULL) goto Done; - items = _PyObject_CallMethodIdObjArgs((PyObject *)od, &PyId_items, NULL); + items = _PyObject_CallMethodIdNoArgs((PyObject *)od, &PyId_items); if (items == NULL) goto Done; @@ -1421,8 +1421,8 @@ odict_repr(PyODictObject *self) Py_SIZE(pieces) = count; } else { - PyObject *items = _PyObject_CallMethodIdObjArgs((PyObject *)self, - &PyId_items, NULL); + PyObject *items = _PyObject_CallMethodIdNoArgs((PyObject *)self, + &PyId_items); if (items == NULL) goto Done; pieces = PySequence_List(items); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 3b9a537936a3..96021eeccc57 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4425,7 +4425,7 @@ _PyObject_GetItemsIter(PyObject *obj, PyObject **listitems, PyObject *items; _Py_IDENTIFIER(items); - items = _PyObject_CallMethodIdObjArgs(obj, &PyId_items, NULL); + items = _PyObject_CallMethodIdNoArgs(obj, &PyId_items); if (items == NULL) { Py_CLEAR(*listitems); return -1; diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index ae3f6dca9eed..e8a429ab5b54 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -455,7 +455,7 @@ proxy_checkref(PyWeakReference *proxy) method(PyObject *proxy, PyObject *Py_UNUSED(ignored)) { \ _Py_IDENTIFIER(special); \ UNWRAP(proxy); \ - return _PyObject_CallMethodId(proxy, &PyId_##special, NULL); \ + return _PyObject_CallMethodIdNoArgs(proxy, &PyId_##special); \ } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 5de43636d96b..65110d8d12c6 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1889,7 +1889,7 @@ builtin_print(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject if (do_flush == -1) return NULL; else if (do_flush) { - tmp = _PyObject_CallMethodId(file, &PyId_flush, NULL); + tmp = _PyObject_CallMethodIdNoArgs(file, &PyId_flush); if (tmp == NULL) return NULL; else @@ -1959,7 +1959,7 @@ builtin_input_impl(PyObject *module, PyObject *prompt) } /* First of all, flush stderr */ - tmp = _PyObject_CallMethodId(ferr, &PyId_flush, NULL); + tmp = _PyObject_CallMethodIdNoArgs(ferr, &PyId_flush); if (tmp == NULL) PyErr_Clear(); else @@ -1968,7 +1968,7 @@ builtin_input_impl(PyObject *module, PyObject *prompt) /* We should only use (GNU) readline if Python's sys.stdin and sys.stdout are the same as C's stdin and stdout, because we need to pass it those. */ - tmp = _PyObject_CallMethodId(fin, &PyId_fileno, NULL); + tmp = _PyObject_CallMethodIdNoArgs(fin, &PyId_fileno); if (tmp == NULL) { PyErr_Clear(); tty = 0; @@ -1981,7 +1981,7 @@ builtin_input_impl(PyObject *module, PyObject *prompt) tty = fd == fileno(stdin) && isatty(fd); } if (tty) { - tmp = _PyObject_CallMethodId(fout, &PyId_fileno, NULL); + tmp = _PyObject_CallMethodIdNoArgs(fout, &PyId_fileno); if (tmp == NULL) { PyErr_Clear(); tty = 0; @@ -2019,7 +2019,7 @@ builtin_input_impl(PyObject *module, PyObject *prompt) stdin_errors_str = PyUnicode_AsUTF8(stdin_errors); if (!stdin_encoding_str || !stdin_errors_str) goto _readline_errors; - tmp = _PyObject_CallMethodId(fout, &PyId_flush, NULL); + tmp = _PyObject_CallMethodIdNoArgs(fout, &PyId_flush); if (tmp == NULL) PyErr_Clear(); else @@ -2114,7 +2114,7 @@ builtin_input_impl(PyObject *module, PyObject *prompt) if (PyFile_WriteObject(prompt, fout, Py_PRINT_RAW) != 0) return NULL; } - tmp = _PyObject_CallMethodId(fout, &PyId_flush, NULL); + tmp = _PyObject_CallMethodIdNoArgs(fout, &PyId_flush); if (tmp == NULL) PyErr_Clear(); else diff --git a/Python/errors.c b/Python/errors.c index a7d40c132d7b..b935341636f7 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1263,7 +1263,7 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type, } /* Explicitly call file.flush() */ - PyObject *res = _PyObject_CallMethodId(file, &PyId_flush, NULL); + PyObject *res = _PyObject_CallMethodIdNoArgs(file, &PyId_flush); if (!res) { return -1; } diff --git a/Python/import.c b/Python/import.c index 76866ae645a7..df258751c82e 100644 --- a/Python/import.c +++ b/Python/import.c @@ -539,7 +539,7 @@ _PyImport_Cleanup(PyThreadState *tstate) } else { _Py_IDENTIFIER(clear); - if (_PyObject_CallMethodId(modules, &PyId_clear, "") == NULL) { + if (_PyObject_CallMethodIdNoArgs(modules, &PyId_clear) == NULL) { PyErr_WriteUnraisable(NULL); } } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 10765dab8f5d..e3333db4328a 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1106,7 +1106,7 @@ flush_std_files(void) int status = 0; if (fout != NULL && fout != Py_None && !file_is_closed(fout)) { - tmp = _PyObject_CallMethodId(fout, &PyId_flush, NULL); + tmp = _PyObject_CallMethodIdNoArgs(fout, &PyId_flush); if (tmp == NULL) { PyErr_WriteUnraisable(fout); status = -1; @@ -1116,7 +1116,7 @@ flush_std_files(void) } if (ferr != NULL && ferr != Py_None && !file_is_closed(ferr)) { - tmp = _PyObject_CallMethodId(ferr, &PyId_flush, NULL); + tmp = _PyObject_CallMethodIdNoArgs(ferr, &PyId_flush); if (tmp == NULL) { PyErr_Clear(); status = -1; @@ -1762,7 +1762,7 @@ create_stdio(const PyConfig *config, PyObject* io, text = PyUnicode_FromString(name); if (text == NULL || _PyObject_SetAttrId(raw, &PyId_name, text) < 0) goto error; - res = _PyObject_CallMethodId(raw, &PyId_isatty, NULL); + res = _PyObject_CallMethodIdNoArgs(raw, &PyId_isatty); if (res == NULL) goto error; isatty = PyObject_IsTrue(res); @@ -2026,7 +2026,7 @@ _Py_FatalError_PrintExc(int fd) Py_XDECREF(tb); /* sys.stderr may be buffered: call sys.stderr.flush() */ - res = _PyObject_CallMethodId(ferr, &PyId_flush, NULL); + res = _PyObject_CallMethodIdNoArgs(ferr, &PyId_flush); if (res == NULL) { _PyErr_Clear(tstate); } @@ -2220,7 +2220,7 @@ wait_for_thread_shutdown(PyThreadState *tstate) /* else: threading not imported */ return; } - result = _PyObject_CallMethodId(threading, &PyId__shutdown, NULL); + result = _PyObject_CallMethodIdNoArgs(threading, &PyId__shutdown); if (result == NULL) { PyErr_WriteUnraisable(threading); } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index f1d946a0b0f8..608908607873 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -978,7 +978,7 @@ _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *t Py_XDECREF(seen); /* Call file.flush() */ - PyObject *res = _PyObject_CallMethodId(file, &PyId_flush, NULL); + PyObject *res = _PyObject_CallMethodIdNoArgs(file, &PyId_flush); if (!res) { /* Silently ignore file.flush() error */ PyErr_Clear(); @@ -1072,7 +1072,7 @@ flush_io(void) f = _PySys_GetObjectId(&PyId_stderr); if (f != NULL) { - r = _PyObject_CallMethodId(f, &PyId_flush, NULL); + r = _PyObject_CallMethodIdNoArgs(f, &PyId_flush); if (r) Py_DECREF(r); else @@ -1080,7 +1080,7 @@ flush_io(void) } f = _PySys_GetObjectId(&PyId_stdout); if (f != NULL) { - r = _PyObject_CallMethodId(f, &PyId_flush, NULL); + r = _PyObject_CallMethodIdNoArgs(f, &PyId_flush); if (r) Py_DECREF(r); else diff --git a/Python/traceback.c b/Python/traceback.c index 0463eb6d8c98..f396f1ad983c 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -430,7 +430,7 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent) if (fob == NULL) { PyErr_Clear(); - res = _PyObject_CallMethodId(binary, &PyId_close, NULL); + res = _PyObject_CallMethodIdNoArgs(binary, &PyId_close); Py_DECREF(binary); if (res) Py_DECREF(res); @@ -450,7 +450,7 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent) break; } } - res = _PyObject_CallMethodId(fob, &PyId_close, NULL); + res = _PyObject_CallMethodIdNoArgs(fob, &PyId_close); if (res) Py_DECREF(res); else From webhook-mailer at python.org Mon Jul 8 04:49:19 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 08 Jul 2019 08:49:19 -0000 Subject: [Python-checkins] bpo-37421: test_concurrent_futures stops ForkServer (GH-14643) Message-ID: https://github.com/python/cpython/commit/e676244235895aeb6ec3b81ca3ccf4a70e487919 commit: e676244235895aeb6ec3b81ca3ccf4a70e487919 branch: master author: Victor Stinner committer: GitHub date: 2019-07-08T10:49:11+02:00 summary: bpo-37421: test_concurrent_futures stops ForkServer (GH-14643) test_concurrent_futures now explicitly stops the ForkServer instance if it's running. files: A Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst M Lib/test/test_concurrent_futures.py diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index c782d4c6e881..ff9a49380340 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -1309,6 +1309,9 @@ def tearDownModule(): # cleanup multiprocessing multiprocessing.process._cleanup() + # Stop the ForkServer process if it's running + from multiprocessing import forkserver + forkserver._forkserver._stop() # bpo-37421: Explicitly call _run_finalizers() to remove immediately # temporary directories created by multiprocessing.util.get_temp_dir(). multiprocessing.util._run_finalizers() diff --git a/Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst b/Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst new file mode 100644 index 000000000000..0766d70f6eda --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst @@ -0,0 +1,2 @@ +test_concurrent_futures now explicitly stops the ForkServer instance if it's +running. From webhook-mailer at python.org Mon Jul 8 05:52:04 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 08 Jul 2019 09:52:04 -0000 Subject: [Python-checkins] bpo-37421: test_concurrent_futures stops ForkServer (GH-14643) (GH-14645) Message-ID: https://github.com/python/cpython/commit/cdada40b23b1f7f527797ba7cb14c25820b05981 commit: cdada40b23b1f7f527797ba7cb14c25820b05981 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Victor Stinner date: 2019-07-08T11:51:49+02:00 summary: bpo-37421: test_concurrent_futures stops ForkServer (GH-14643) (GH-14645) test_concurrent_futures now explicitly stops the ForkServer instance if it's running. (cherry picked from commit e676244235895aeb6ec3b81ca3ccf4a70e487919) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst M Lib/test/test_concurrent_futures.py diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index fa298207f6c5..98c9bc9b507a 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -1310,6 +1310,9 @@ def tearDownModule(): # cleanup multiprocessing multiprocessing.process._cleanup() + # Stop the ForkServer process if it's running + from multiprocessing import forkserver + forkserver._forkserver._stop() # bpo-37421: Explicitly call _run_finalizers() to remove immediately # temporary directories created by multiprocessing.util.get_temp_dir(). multiprocessing.util._run_finalizers() diff --git a/Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst b/Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst new file mode 100644 index 000000000000..0766d70f6eda --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst @@ -0,0 +1,2 @@ +test_concurrent_futures now explicitly stops the ForkServer instance if it's +running. From webhook-mailer at python.org Mon Jul 8 12:57:48 2019 From: webhook-mailer at python.org (Ned Deily) Date: Mon, 08 Jul 2019 16:57:48 -0000 Subject: [Python-checkins] bpo-37149: Replace dead link for online Tkinter reference (GH-14616) Message-ID: https://github.com/python/cpython/commit/317c33e67cb6076c5a87a66c75e8c35ac581398d commit: 317c33e67cb6076c5a87a66c75e8c35ac581398d branch: 3.6 author: Ned Deily committer: Ned Deily date: 2019-07-08T12:50:54-04:00 summary: bpo-37149: Replace dead link for online Tkinter reference (GH-14616) Also fix a name misspelling. Co-authored-by: Terry Jan Reedy files: M Doc/library/tkinter.rst diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 60cf892e0888..2515cf4f8b08 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -31,7 +31,7 @@ installed, so you can read the Tcl/Tk documentation specific to that version. `TKDocs `_ Extensive tutorial plus friendlier widget pages for some of the widgets. - `Tkinter reference: a GUI for Python `_ + `Tkinter 8.5 reference: a GUI for Python `_ On-line reference material. `Tkinter docs from effbot `_ @@ -41,7 +41,7 @@ installed, so you can read the Tcl/Tk documentation specific to that version. Book by Mark Lutz, has excellent coverage of Tkinter. `Modern Tkinter for Busy Python Developers `_ - Book by Mark Rozerman about building attractive and modern graphical user interfaces with Python and Tkinter. + Book by Mark Roseman about building attractive and modern graphical user interfaces with Python and Tkinter. `Python and Tkinter Programming `_ Book by John Grayson (ISBN 1-884777-81-3). From webhook-mailer at python.org Mon Jul 8 17:06:45 2019 From: webhook-mailer at python.org (Julien Palard) Date: Mon, 08 Jul 2019 21:06:45 -0000 Subject: [Python-checkins] Doc: Fix: Proper UpperCamelCase and lowercase. (GH-14644) Message-ID: https://github.com/python/cpython/commit/2da622ff77a763327895656779370b80a833d95c commit: 2da622ff77a763327895656779370b80a833d95c branch: master author: Julien Palard committer: GitHub date: 2019-07-08T23:06:32+02:00 summary: Doc: Fix: Proper UpperCamelCase and lowercase. (GH-14644) Initial report by Michael Blankenship on docs@ files: M Doc/tutorial/controlflow.rst diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index 79111f8518d9..92c042e26dfb 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -920,7 +920,7 @@ extracted for you: bracketing constructs: ``a = f(1, 2) + g(3, 4)``. * Name your classes and functions consistently; the convention is to use - ``CamelCase`` for classes and ``lower_case_with_underscores`` for functions + ``UpperCamelCase`` for classes and ``lowercase_with_underscores`` for functions and methods. Always use ``self`` as the name for the first method argument (see :ref:`tut-firstclasses` for more on classes and methods). From webhook-mailer at python.org Mon Jul 8 17:08:11 2019 From: webhook-mailer at python.org (Julien Palard) Date: Mon, 08 Jul 2019 21:08:11 -0000 Subject: [Python-checkins] Doc: Fix example title. (GH-14639) Message-ID: https://github.com/python/cpython/commit/66b4150f6f001640521ae6c9571cd4325cd67394 commit: 66b4150f6f001640521ae6c9571cd4325cd67394 branch: master author: Julien Palard committer: GitHub date: 2019-07-08T23:08:07+02:00 summary: Doc: Fix example title. (GH-14639) files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 8575f8a72af3..9dd557fabaae 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3815,7 +3815,7 @@ copying. >>> z.nbytes 48 - Cast 1D/unsigned char to 2D/unsigned long:: + Cast 1D/unsigned long to 2D/unsigned long:: >>> buf = struct.pack("L"*6, *list(range(6))) >>> x = memoryview(buf) From webhook-mailer at python.org Mon Jul 8 17:14:02 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 08 Jul 2019 21:14:02 -0000 Subject: [Python-checkins] Doc: Fix: Proper UpperCamelCase and lowercase. (GH-14644) Message-ID: https://github.com/python/cpython/commit/975d4d3651c5fe9422801d8f7145486b23d46a13 commit: 975d4d3651c5fe9422801d8f7145486b23d46a13 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-08T14:13:57-07:00 summary: Doc: Fix: Proper UpperCamelCase and lowercase. (GH-14644) Initial report by Michael Blankenship on docs@ (cherry picked from commit 2da622ff77a763327895656779370b80a833d95c) Co-authored-by: Julien Palard files: M Doc/tutorial/controlflow.rst diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index 79111f8518d9..92c042e26dfb 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -920,7 +920,7 @@ extracted for you: bracketing constructs: ``a = f(1, 2) + g(3, 4)``. * Name your classes and functions consistently; the convention is to use - ``CamelCase`` for classes and ``lower_case_with_underscores`` for functions + ``UpperCamelCase`` for classes and ``lowercase_with_underscores`` for functions and methods. Always use ``self`` as the name for the first method argument (see :ref:`tut-firstclasses` for more on classes and methods). From webhook-mailer at python.org Mon Jul 8 17:14:03 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 08 Jul 2019 21:14:03 -0000 Subject: [Python-checkins] Doc: Fix: Proper UpperCamelCase and lowercase. (GH-14644) Message-ID: https://github.com/python/cpython/commit/6f5574866c4017e438b7ac5f5374889389c4eb24 commit: 6f5574866c4017e438b7ac5f5374889389c4eb24 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-08T14:13:59-07:00 summary: Doc: Fix: Proper UpperCamelCase and lowercase. (GH-14644) Initial report by Michael Blankenship on docs@ (cherry picked from commit 2da622ff77a763327895656779370b80a833d95c) Co-authored-by: Julien Palard files: M Doc/tutorial/controlflow.rst diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index c3ce1205e52e..482a34399c7b 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -744,7 +744,7 @@ extracted for you: bracketing constructs: ``a = f(1, 2) + g(3, 4)``. * Name your classes and functions consistently; the convention is to use - ``CamelCase`` for classes and ``lower_case_with_underscores`` for functions + ``UpperCamelCase`` for classes and ``lowercase_with_underscores`` for functions and methods. Always use ``self`` as the name for the first method argument (see :ref:`tut-firstclasses` for more on classes and methods). From webhook-mailer at python.org Mon Jul 8 17:17:35 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 08 Jul 2019 21:17:35 -0000 Subject: [Python-checkins] Doc: Fix example title. (GH-14639) Message-ID: https://github.com/python/cpython/commit/54348f46f8c8c023b7e78f9cebe8e54818227b92 commit: 54348f46f8c8c023b7e78f9cebe8e54818227b92 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-08T14:17:32-07:00 summary: Doc: Fix example title. (GH-14639) (cherry picked from commit 66b4150f6f001640521ae6c9571cd4325cd67394) Co-authored-by: Julien Palard files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 35a17a180809..965167640c98 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3801,7 +3801,7 @@ copying. >>> z.nbytes 48 - Cast 1D/unsigned char to 2D/unsigned long:: + Cast 1D/unsigned long to 2D/unsigned long:: >>> buf = struct.pack("L"*6, *list(range(6))) >>> x = memoryview(buf) From webhook-mailer at python.org Mon Jul 8 17:18:00 2019 From: webhook-mailer at python.org (Ivan Levkivskyi) Date: Mon, 08 Jul 2019 21:18:00 -0000 Subject: [Python-checkins] bpo-18374: fix wrong col_offset of some ast.BinOp instances (GH-14607) Message-ID: https://github.com/python/cpython/commit/110a47c4f42cf4db88edc1876899fff8f05190fb commit: 110a47c4f42cf4db88edc1876899fff8f05190fb branch: master author: Carl Friedrich Bolz-Tereick committer: Ivan Levkivskyi date: 2019-07-08T22:17:56+01:00 summary: bpo-18374: fix wrong col_offset of some ast.BinOp instances (GH-14607) Nested BinOp instances (e.g. a+b+c) had a wrong col_offset for the second BinOp (e.g. 2 instead of 0 in the example). Fix it by using the correct st node to copy the line and col_offset from in ast.c. files: A Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst M Lib/test/test_ast.py M Misc/ACKS M Python/ast.c diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index e251e254afdd..1e07c573c846 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -576,6 +576,36 @@ def bad_normalize(*args): with support.swap_attr(unicodedata, 'normalize', bad_normalize): self.assertRaises(TypeError, ast.parse, '\u03D5') + def test_issue18374_binop_col_offset(self): + tree = ast.parse('4+5+6+7') + parent_binop = tree.body[0].value + child_binop = parent_binop.left + grandchild_binop = child_binop.left + self.assertEqual(parent_binop.col_offset, 0) + self.assertEqual(parent_binop.end_col_offset, 7) + self.assertEqual(child_binop.col_offset, 0) + self.assertEqual(child_binop.end_col_offset, 5) + self.assertEqual(grandchild_binop.col_offset, 0) + self.assertEqual(grandchild_binop.end_col_offset, 3) + + tree = ast.parse('4+5-\\\n 6-7') + parent_binop = tree.body[0].value + child_binop = parent_binop.left + grandchild_binop = child_binop.left + self.assertEqual(parent_binop.col_offset, 0) + self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(parent_binop.end_col_offset, 4) + self.assertEqual(parent_binop.end_lineno, 2) + + self.assertEqual(child_binop.col_offset, 0) + self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(child_binop.end_col_offset, 2) + self.assertEqual(parent_binop.end_lineno, 2) + + self.assertEqual(grandchild_binop.col_offset, 0) + self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(grandchild_binop.end_col_offset, 3) + self.assertEqual(parent_binop.end_lineno, 2) class ASTHelpers_Test(unittest.TestCase): maxDiff = None diff --git a/Misc/ACKS b/Misc/ACKS index 36fe727c8421..d916c45a8e44 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -178,6 +178,7 @@ Nikolay Bogoychev David Bolen Wouter Bolsterlee Gawain Bolton +Carl Friedrich Bolz-Tereick Forest Bond Gregory Bond M?d?ric Boquien diff --git a/Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst b/Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst new file mode 100644 index 000000000000..f9e99e0474a4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst @@ -0,0 +1,2 @@ +Fix the ``.col_offset`` attribute of nested :class:`ast.BinOp` instances +which had a too large value in some situations. diff --git a/Python/ast.c b/Python/ast.c index 16895ce62ec0..8dc86c23d62d 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -2645,7 +2645,7 @@ ast_for_binop(struct compiling *c, const node *n) return NULL; tmp_result = BinOp(result, newoperator, tmp, - LINENO(next_oper), next_oper->n_col_offset, + LINENO(n), n->n_col_offset, CHILD(n, i * 2 + 2)->n_end_lineno, CHILD(n, i * 2 + 2)->n_end_col_offset, c->c_arena); From webhook-mailer at python.org Mon Jul 8 17:18:19 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 08 Jul 2019 21:18:19 -0000 Subject: [Python-checkins] Doc: Fix example title. (GH-14639) Message-ID: https://github.com/python/cpython/commit/8b1135fc602859119ce2e649b070c0d8d9dce7ca commit: 8b1135fc602859119ce2e649b070c0d8d9dce7ca branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-08T14:18:15-07:00 summary: Doc: Fix example title. (GH-14639) (cherry picked from commit 66b4150f6f001640521ae6c9571cd4325cd67394) Co-authored-by: Julien Palard files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 3b74331e51f2..d35c171aba39 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -3733,7 +3733,7 @@ copying. >>> z.nbytes 48 - Cast 1D/unsigned char to 2D/unsigned long:: + Cast 1D/unsigned long to 2D/unsigned long:: >>> buf = struct.pack("L"*6, *list(range(6))) >>> x = memoryview(buf) From webhook-mailer at python.org Mon Jul 8 17:41:38 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 08 Jul 2019 21:41:38 -0000 Subject: [Python-checkins] bpo-18374: fix wrong col_offset of some ast.BinOp instances (GH-14607) Message-ID: https://github.com/python/cpython/commit/c7be35c2abd598f02a633879133caec356593241 commit: c7be35c2abd598f02a633879133caec356593241 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-08T14:41:34-07:00 summary: bpo-18374: fix wrong col_offset of some ast.BinOp instances (GH-14607) Nested BinOp instances (e.g. a+b+c) had a wrong col_offset for the second BinOp (e.g. 2 instead of 0 in the example). Fix it by using the correct st node to copy the line and col_offset from in ast.c. (cherry picked from commit 110a47c4f42cf4db88edc1876899fff8f05190fb) Co-authored-by: Carl Friedrich Bolz-Tereick files: A Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst M Lib/test/test_ast.py M Misc/ACKS M Python/ast.c diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index e251e254afdd..1e07c573c846 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -576,6 +576,36 @@ def bad_normalize(*args): with support.swap_attr(unicodedata, 'normalize', bad_normalize): self.assertRaises(TypeError, ast.parse, '\u03D5') + def test_issue18374_binop_col_offset(self): + tree = ast.parse('4+5+6+7') + parent_binop = tree.body[0].value + child_binop = parent_binop.left + grandchild_binop = child_binop.left + self.assertEqual(parent_binop.col_offset, 0) + self.assertEqual(parent_binop.end_col_offset, 7) + self.assertEqual(child_binop.col_offset, 0) + self.assertEqual(child_binop.end_col_offset, 5) + self.assertEqual(grandchild_binop.col_offset, 0) + self.assertEqual(grandchild_binop.end_col_offset, 3) + + tree = ast.parse('4+5-\\\n 6-7') + parent_binop = tree.body[0].value + child_binop = parent_binop.left + grandchild_binop = child_binop.left + self.assertEqual(parent_binop.col_offset, 0) + self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(parent_binop.end_col_offset, 4) + self.assertEqual(parent_binop.end_lineno, 2) + + self.assertEqual(child_binop.col_offset, 0) + self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(child_binop.end_col_offset, 2) + self.assertEqual(parent_binop.end_lineno, 2) + + self.assertEqual(grandchild_binop.col_offset, 0) + self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(grandchild_binop.end_col_offset, 3) + self.assertEqual(parent_binop.end_lineno, 2) class ASTHelpers_Test(unittest.TestCase): maxDiff = None diff --git a/Misc/ACKS b/Misc/ACKS index 082fa567f23a..f427d8b5a929 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -178,6 +178,7 @@ Nikolay Bogoychev David Bolen Wouter Bolsterlee Gawain Bolton +Carl Friedrich Bolz-Tereick Forest Bond Gregory Bond M?d?ric Boquien diff --git a/Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst b/Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst new file mode 100644 index 000000000000..f9e99e0474a4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst @@ -0,0 +1,2 @@ +Fix the ``.col_offset`` attribute of nested :class:`ast.BinOp` instances +which had a too large value in some situations. diff --git a/Python/ast.c b/Python/ast.c index 2a5941572148..317a42b7f121 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -2645,7 +2645,7 @@ ast_for_binop(struct compiling *c, const node *n) return NULL; tmp_result = BinOp(result, newoperator, tmp, - LINENO(next_oper), next_oper->n_col_offset, + LINENO(n), n->n_col_offset, CHILD(n, i * 2 + 2)->n_end_lineno, CHILD(n, i * 2 + 2)->n_end_col_offset, c->c_arena); From webhook-mailer at python.org Mon Jul 8 17:54:52 2019 From: webhook-mailer at python.org (Ned Deily) Date: Mon, 08 Jul 2019 21:54:52 -0000 Subject: [Python-checkins] [3.7] bpo-37500: Revert commit 85ed1712e428f93408f56fc684816f9a85b0ebc0 (GH-14605) Message-ID: https://github.com/python/cpython/commit/4834c80d799471a6c9ddaad9c5c82c8af156e4fd commit: 4834c80d799471a6c9ddaad9c5c82c8af156e4fd branch: 3.7 author: Pablo Galindo committer: Ned Deily date: 2019-07-08T12:08:31-04:00 summary: [3.7] bpo-37500: Revert commit 85ed1712e428f93408f56fc684816f9a85b0ebc0 (GH-14605) https://bugs.python.org/issue37500 files: M Lib/test/test_syntax.py M Python/compile.c M Python/peephole.c diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 4918e5c4c428..2b96a94401a8 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -650,20 +650,6 @@ def error2(): def test_break_outside_loop(self): self._check_error("break", "outside loop") - def test_yield_outside_function(self): - self._check_error("if 0: yield", "outside function") - self._check_error("class C:\n if 0: yield", "outside function") - - def test_return_outside_function(self): - self._check_error("if 0: return", "outside function") - self._check_error("class C:\n if 0: return", "outside function") - - def test_break_outside_loop(self): - self._check_error("if 0: break", "outside loop") - - def test_continue_outside_loop(self): - self._check_error("if 0: continue", "not properly in loop") - def test_unexpected_indent(self): self._check_error("foo()\n bar()\n", "unexpected indent", subclass=IndentationError) diff --git a/Python/compile.c b/Python/compile.c index d2729d4c6ca3..5688ef8479c0 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2301,12 +2301,13 @@ compiler_if(struct compiler *c, stmt_ty s) return 0; constant = expr_constant(s->v.If.test); - /* constant = 0: "if 0" Leave the optimizations to - * the pephole optimizer to check for syntax errors - * in the block. + /* constant = 0: "if 0" * constant = 1: "if 1", "if 2", ... * constant = -1: rest */ - if (constant == 1) { + if (constant == 0) { + if (s->v.If.orelse) + VISIT_SEQ(c, stmt, s->v.If.orelse); + } else if (constant == 1) { VISIT_SEQ(c, stmt, s->v.If.body); } else { if (asdl_seq_LEN(s->v.If.orelse)) { diff --git a/Python/peephole.c b/Python/peephole.c index 277a216ae075..95b3dbb6bf51 100644 --- a/Python/peephole.c +++ b/Python/peephole.c @@ -304,18 +304,11 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, case LOAD_CONST: cumlc = lastlc + 1; if (nextop != POP_JUMP_IF_FALSE || - !ISBASICBLOCK(blocks, op_start, i + 1)) { + !ISBASICBLOCK(blocks, op_start, i + 1) || + !PyObject_IsTrue(PyList_GET_ITEM(consts, get_arg(codestr, i)))) break; - } - PyObject* cnt = PyList_GET_ITEM(consts, get_arg(codestr, i)); - int is_true = PyObject_IsTrue(cnt); - if (is_true == -1) { - goto exitError; - } - if (is_true == 1) { - fill_nops(codestr, op_start, nexti + 1); - cumlc = 0; - } + fill_nops(codestr, op_start, nexti + 1); + cumlc = 0; break; /* Try to fold tuples of constants. From webhook-mailer at python.org Tue Jul 9 06:37:00 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 09 Jul 2019 10:37:00 -0000 Subject: [Python-checkins] bpo-37322: Fix test_ssl.test_pha_required_nocert() ResourceWarning (GH-14662) Message-ID: https://github.com/python/cpython/commit/cf9c41c422de3774862db964fe3153086bad3f61 commit: cf9c41c422de3774862db964fe3153086bad3f61 branch: master author: Victor Stinner committer: GitHub date: 2019-07-09T12:36:55+02:00 summary: bpo-37322: Fix test_ssl.test_pha_required_nocert() ResourceWarning (GH-14662) Close the TLS connection in test_pha_required_nocert() of test_ssl to fix a ResourceWarning. files: M Lib/test/test_ssl.py diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index d2b9e2046d0e..166e28683e26 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2382,6 +2382,7 @@ def run(self): if self.server.chatty and support.verbose: sys.stdout.write(err.args[1]) # test_pha_required_nocert is expecting this exception + self.close() raise ssl.SSLError('tlsv13 alert certificate required') except OSError: if self.server.chatty: From webhook-mailer at python.org Tue Jul 9 07:00:27 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 09 Jul 2019 11:00:27 -0000 Subject: [Python-checkins] bpo-37526: Add support.catch_threading_exception() (GH-14664) Message-ID: https://github.com/python/cpython/commit/91b4f7ab7f9a5e0908b91379ee085ae087a76483 commit: 91b4f7ab7f9a5e0908b91379ee085ae087a76483 branch: master author: Victor Stinner committer: GitHub date: 2019-07-09T13:00:23+02:00 summary: bpo-37526: Add support.catch_threading_exception() (GH-14664) Context manager catching threading.Thread exception using threading.excepthook. files: A Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst M Doc/library/test.rst M Lib/test/support/__init__.py diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 920c018084b8..7d62a94d839b 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -1081,6 +1081,39 @@ The :mod:`test.support` module defines the following functions: :exc:`PermissionError` is raised. +.. function:: catch_threading_exception() + + Context manager catching :class:`threading.Thread` exception using + :func:`threading.excepthook`. + + Attributes set when an exception is catched: + + * ``exc_type`` + * ``exc_value`` + * ``exc_traceback`` + * ``thread`` + + See :func:`threading.excepthook` documentation. + + These attributes are deleted at the context manager exit. + + Usage:: + + with support.catch_threading_exception() as cm: + # code spawning a thread which raises an exception + ... + + # check the thread exception, use cm attributes: + # exc_type, exc_value, exc_traceback, thread + ... + + # exc_type, exc_value, exc_traceback, thread attributes of cm no longer + # exists at this point + # (to avoid reference cycles) + + .. versionadded:: 3.8 + + .. function:: catch_unraisable_exception() Context manager catching unraisable exception using diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 611c1cc9776d..423bb3ebd80d 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -3159,3 +3159,60 @@ def __enter__(self): def __exit__(self, *exc_info): sys.unraisablehook = self._old_hook del self.unraisable + + +class catch_threading_exception: + """ + Context manager catching threading.Thread exception using + threading.excepthook. + + Attributes set when an exception is catched: + + * exc_type + * exc_value + * exc_traceback + * thread + + See threading.excepthook() documentation for these attributes. + + These attributes are deleted at the context manager exit. + + Usage: + + with support.catch_threading_exception() as cm: + # code spawning a thread which raises an exception + ... + + # check the thread exception, use cm attributes: + # exc_type, exc_value, exc_traceback, thread + ... + + # exc_type, exc_value, exc_traceback, thread attributes of cm no longer + # exists at this point + # (to avoid reference cycles) + """ + + def __init__(self): + self.exc_type = None + self.exc_value = None + self.exc_traceback = None + self.thread = None + self._old_hook = None + + def _hook(self, args): + self.exc_type = args.exc_type + self.exc_value = args.exc_value + self.exc_traceback = args.exc_traceback + self.thread = args.thread + + def __enter__(self): + self._old_hook = threading.excepthook + threading.excepthook = self._hook + return self + + def __exit__(self, *exc_info): + threading.excepthook = self._old_hook + del self.exc_type + del self.exc_value + del self.exc_traceback + del self.thread diff --git a/Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst b/Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst new file mode 100644 index 000000000000..aff6b6d1f12c --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst @@ -0,0 +1,2 @@ +Add :func:`test.support.catch_threading_exception`: context manager catching +:class:`threading.Thread` exception using :func:`threading.excepthook`. From webhook-mailer at python.org Tue Jul 9 07:30:56 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 09 Jul 2019 11:30:56 -0000 Subject: [Python-checkins] bpo-37120: Fix _ssl get_num_tickets() (GH-14668) Message-ID: https://github.com/python/cpython/commit/76611c7c0af6b2f4d0d98a5db827d34cff54ce25 commit: 76611c7c0af6b2f4d0d98a5db827d34cff54ce25 branch: master author: Victor Stinner committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-09T04:30:52-07:00 summary: bpo-37120: Fix _ssl get_num_tickets() (GH-14668) Replace PyLong_FromLong() with PyLong_FromSize_t(): SSL_CTX_get_num_tickets() return type is size_t. https://bugs.python.org/issue37120 files: M Modules/_ssl.c diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 3351af6cdefd..da30cbb758e2 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -3641,7 +3641,7 @@ set_maximum_version(PySSLContext *self, PyObject *arg, void *c) static PyObject * get_num_tickets(PySSLContext *self, void *c) { - return PyLong_FromLong(SSL_CTX_get_num_tickets(self->ctx)); + return PyLong_FromSize_t(SSL_CTX_get_num_tickets(self->ctx)); } static int From webhook-mailer at python.org Tue Jul 9 07:35:51 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 09 Jul 2019 11:35:51 -0000 Subject: [Python-checkins] Revert "bpo-37322: Fix test_ssl.test_pha_required_nocert() ResourceWarning (GH-14662)" (GH-14669) Message-ID: https://github.com/python/cpython/commit/61b1bc56069719fc6f17c73fdf2193636dbf6cc2 commit: 61b1bc56069719fc6f17c73fdf2193636dbf6cc2 branch: master author: Victor Stinner committer: GitHub date: 2019-07-09T13:35:47+02:00 summary: Revert "bpo-37322: Fix test_ssl.test_pha_required_nocert() ResourceWarning (GH-14662)" (GH-14669) This reverts commit cf9c41c422de3774862db964fe3153086bad3f61. files: M Lib/test/test_ssl.py diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 166e28683e26..d2b9e2046d0e 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2382,7 +2382,6 @@ def run(self): if self.server.chatty and support.verbose: sys.stdout.write(err.args[1]) # test_pha_required_nocert is expecting this exception - self.close() raise ssl.SSLError('tlsv13 alert certificate required') except OSError: if self.server.chatty: From webhook-mailer at python.org Tue Jul 9 07:36:03 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 09 Jul 2019 11:36:03 -0000 Subject: [Python-checkins] bpo-37526: Add support.catch_threading_exception() (GH-14664) (GH-14666) Message-ID: https://github.com/python/cpython/commit/58f2c7f424fe91ba035918f0f66306af73a37543 commit: 58f2c7f424fe91ba035918f0f66306af73a37543 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Victor Stinner date: 2019-07-09T13:35:59+02:00 summary: bpo-37526: Add support.catch_threading_exception() (GH-14664) (GH-14666) Context manager catching threading.Thread exception using threading.excepthook. (cherry picked from commit 91b4f7ab7f9a5e0908b91379ee085ae087a76483) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst M Doc/library/test.rst M Lib/test/support/__init__.py diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 920c018084b8..7d62a94d839b 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -1081,6 +1081,39 @@ The :mod:`test.support` module defines the following functions: :exc:`PermissionError` is raised. +.. function:: catch_threading_exception() + + Context manager catching :class:`threading.Thread` exception using + :func:`threading.excepthook`. + + Attributes set when an exception is catched: + + * ``exc_type`` + * ``exc_value`` + * ``exc_traceback`` + * ``thread`` + + See :func:`threading.excepthook` documentation. + + These attributes are deleted at the context manager exit. + + Usage:: + + with support.catch_threading_exception() as cm: + # code spawning a thread which raises an exception + ... + + # check the thread exception, use cm attributes: + # exc_type, exc_value, exc_traceback, thread + ... + + # exc_type, exc_value, exc_traceback, thread attributes of cm no longer + # exists at this point + # (to avoid reference cycles) + + .. versionadded:: 3.8 + + .. function:: catch_unraisable_exception() Context manager catching unraisable exception using diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 1c91fc434eec..a0fe086049a1 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -3157,3 +3157,60 @@ def __enter__(self): def __exit__(self, *exc_info): sys.unraisablehook = self._old_hook del self.unraisable + + +class catch_threading_exception: + """ + Context manager catching threading.Thread exception using + threading.excepthook. + + Attributes set when an exception is catched: + + * exc_type + * exc_value + * exc_traceback + * thread + + See threading.excepthook() documentation for these attributes. + + These attributes are deleted at the context manager exit. + + Usage: + + with support.catch_threading_exception() as cm: + # code spawning a thread which raises an exception + ... + + # check the thread exception, use cm attributes: + # exc_type, exc_value, exc_traceback, thread + ... + + # exc_type, exc_value, exc_traceback, thread attributes of cm no longer + # exists at this point + # (to avoid reference cycles) + """ + + def __init__(self): + self.exc_type = None + self.exc_value = None + self.exc_traceback = None + self.thread = None + self._old_hook = None + + def _hook(self, args): + self.exc_type = args.exc_type + self.exc_value = args.exc_value + self.exc_traceback = args.exc_traceback + self.thread = args.thread + + def __enter__(self): + self._old_hook = threading.excepthook + threading.excepthook = self._hook + return self + + def __exit__(self, *exc_info): + threading.excepthook = self._old_hook + del self.exc_type + del self.exc_value + del self.exc_traceback + del self.thread diff --git a/Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst b/Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst new file mode 100644 index 000000000000..aff6b6d1f12c --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst @@ -0,0 +1,2 @@ +Add :func:`test.support.catch_threading_exception`: context manager catching +:class:`threading.Thread` exception using :func:`threading.excepthook`. From webhook-mailer at python.org Tue Jul 9 08:20:11 2019 From: webhook-mailer at python.org (Ivan Levkivskyi) Date: Tue, 09 Jul 2019 12:20:11 -0000 Subject: [Python-checkins] bpo-18374: fix tests to check the correct thing about line numbers (GH-14659) Message-ID: https://github.com/python/cpython/commit/430a9f44fe22f029ae8cfeecb46621d7e199414b commit: 430a9f44fe22f029ae8cfeecb46621d7e199414b branch: master author: Carl Friedrich Bolz-Tereick committer: Ivan Levkivskyi date: 2019-07-09T13:20:01+01:00 summary: bpo-18374: fix tests to check the correct thing about line numbers (GH-14659) files: M Lib/test/test_ast.py diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 1e07c573c846..6bd27ce12a62 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -598,14 +598,14 @@ def test_issue18374_binop_col_offset(self): self.assertEqual(parent_binop.end_lineno, 2) self.assertEqual(child_binop.col_offset, 0) - self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(child_binop.lineno, 1) self.assertEqual(child_binop.end_col_offset, 2) - self.assertEqual(parent_binop.end_lineno, 2) + self.assertEqual(child_binop.end_lineno, 2) self.assertEqual(grandchild_binop.col_offset, 0) - self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(grandchild_binop.lineno, 1) self.assertEqual(grandchild_binop.end_col_offset, 3) - self.assertEqual(parent_binop.end_lineno, 2) + self.assertEqual(grandchild_binop.end_lineno, 1) class ASTHelpers_Test(unittest.TestCase): maxDiff = None From webhook-mailer at python.org Tue Jul 9 08:33:58 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 09 Jul 2019 12:33:58 -0000 Subject: [Python-checkins] bpo-37322: ssl test_pha_required_nocert() ignores expected SSLError (GH-14670) Message-ID: https://github.com/python/cpython/commit/73ea54620a6f91c3f2e53880373dd47813691a21 commit: 73ea54620a6f91c3f2e53880373dd47813691a21 branch: master author: Victor Stinner committer: GitHub date: 2019-07-09T14:33:49+02:00 summary: bpo-37322: ssl test_pha_required_nocert() ignores expected SSLError (GH-14670) test_ssl.test_pha_required_nocert() now uses support.catch_threading_exception() to ignore the expected SSLError in ConnectionHandler of ThreadedEchoServer (it is only raised sometimes on Windows). files: M Lib/test/test_ssl.py diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index d2b9e2046d0e..afc5be9a7e99 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4326,21 +4326,24 @@ def test_pha_required_nocert(self): server_context.verify_mode = ssl.CERT_REQUIRED client_context.post_handshake_auth = True - server = ThreadedEchoServer(context=server_context, chatty=False) - with server: - with client_context.wrap_socket(socket.socket(), - server_hostname=hostname) as s: - s.connect((HOST, server.port)) - s.write(b'PHA') - # receive CertificateRequest - self.assertEqual(s.recv(1024), b'OK\n') - # send empty Certificate + Finish - s.write(b'HASCERT') - # receive alert - with self.assertRaisesRegex( - ssl.SSLError, - 'tlsv13 alert certificate required'): - s.recv(1024) + # Ignore expected SSLError in ConnectionHandler of ThreadedEchoServer + # (it is only raised sometimes on Windows) + with support.catch_threading_exception() as cm: + server = ThreadedEchoServer(context=server_context, chatty=False) + with server: + with client_context.wrap_socket(socket.socket(), + server_hostname=hostname) as s: + s.connect((HOST, server.port)) + s.write(b'PHA') + # receive CertificateRequest + self.assertEqual(s.recv(1024), b'OK\n') + # send empty Certificate + Finish + s.write(b'HASCERT') + # receive alert + with self.assertRaisesRegex( + ssl.SSLError, + 'tlsv13 alert certificate required'): + s.recv(1024) def test_pha_optional(self): if support.verbose: From webhook-mailer at python.org Tue Jul 9 08:43:04 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 09 Jul 2019 12:43:04 -0000 Subject: [Python-checkins] bpo-37120: Fix _ssl get_num_tickets() (GH-14668) Message-ID: https://github.com/python/cpython/commit/bbad695e7890513be7a9bc662e2d8ae13bfcd313 commit: bbad695e7890513be7a9bc662e2d8ae13bfcd313 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-09T05:42:49-07:00 summary: bpo-37120: Fix _ssl get_num_tickets() (GH-14668) Replace PyLong_FromLong() with PyLong_FromSize_t(): SSL_CTX_get_num_tickets() return type is size_t. https://bugs.python.org/issue37120 (cherry picked from commit 76611c7c0af6b2f4d0d98a5db827d34cff54ce25) Co-authored-by: Victor Stinner files: M Modules/_ssl.c diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 3351af6cdefd..da30cbb758e2 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -3641,7 +3641,7 @@ set_maximum_version(PySSLContext *self, PyObject *arg, void *c) static PyObject * get_num_tickets(PySSLContext *self, void *c) { - return PyLong_FromLong(SSL_CTX_get_num_tickets(self->ctx)); + return PyLong_FromSize_t(SSL_CTX_get_num_tickets(self->ctx)); } static int From webhook-mailer at python.org Tue Jul 9 08:55:14 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 09 Jul 2019 12:55:14 -0000 Subject: [Python-checkins] bpo-37322: ssl test_pha_required_nocert() ignores expected SSLError (GH-14670) Message-ID: https://github.com/python/cpython/commit/4c403b8ca2d0ef44b691cceaeeac9e110fd3f05a commit: 4c403b8ca2d0ef44b691cceaeeac9e110fd3f05a branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-09T05:55:08-07:00 summary: bpo-37322: ssl test_pha_required_nocert() ignores expected SSLError (GH-14670) test_ssl.test_pha_required_nocert() now uses support.catch_threading_exception() to ignore the expected SSLError in ConnectionHandler of ThreadedEchoServer (it is only raised sometimes on Windows). (cherry picked from commit 73ea54620a6f91c3f2e53880373dd47813691a21) Co-authored-by: Victor Stinner files: M Lib/test/test_ssl.py diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index f1b7aa731e9a..879d944f327e 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4320,21 +4320,24 @@ def test_pha_required_nocert(self): server_context.verify_mode = ssl.CERT_REQUIRED client_context.post_handshake_auth = True - server = ThreadedEchoServer(context=server_context, chatty=False) - with server: - with client_context.wrap_socket(socket.socket(), - server_hostname=hostname) as s: - s.connect((HOST, server.port)) - s.write(b'PHA') - # receive CertificateRequest - self.assertEqual(s.recv(1024), b'OK\n') - # send empty Certificate + Finish - s.write(b'HASCERT') - # receive alert - with self.assertRaisesRegex( - ssl.SSLError, - 'tlsv13 alert certificate required'): - s.recv(1024) + # Ignore expected SSLError in ConnectionHandler of ThreadedEchoServer + # (it is only raised sometimes on Windows) + with support.catch_threading_exception() as cm: + server = ThreadedEchoServer(context=server_context, chatty=False) + with server: + with client_context.wrap_socket(socket.socket(), + server_hostname=hostname) as s: + s.connect((HOST, server.port)) + s.write(b'PHA') + # receive CertificateRequest + self.assertEqual(s.recv(1024), b'OK\n') + # send empty Certificate + Finish + s.write(b'HASCERT') + # receive alert + with self.assertRaisesRegex( + ssl.SSLError, + 'tlsv13 alert certificate required'): + s.recv(1024) def test_pha_optional(self): if support.verbose: From webhook-mailer at python.org Tue Jul 9 09:29:01 2019 From: webhook-mailer at python.org (Ivan Levkivskyi) Date: Tue, 09 Jul 2019 13:29:01 -0000 Subject: [Python-checkins] bpo-18374: fix tests to check the correct thing about line numbers (GH-14659) (GH-14672) Message-ID: https://github.com/python/cpython/commit/68bd9c5691c4899d21cc7fe6cce7cd22b2f5ccb0 commit: 68bd9c5691c4899d21cc7fe6cce7cd22b2f5ccb0 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Ivan Levkivskyi date: 2019-07-09T14:28:56+01:00 summary: bpo-18374: fix tests to check the correct thing about line numbers (GH-14659) (GH-14672) (cherry picked from commit 430a9f44fe22f029ae8cfeecb46621d7e199414b) Co-authored-by: Carl Friedrich Bolz-Tereick files: M Lib/test/test_ast.py diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 1e07c573c846..6bd27ce12a62 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -598,14 +598,14 @@ def test_issue18374_binop_col_offset(self): self.assertEqual(parent_binop.end_lineno, 2) self.assertEqual(child_binop.col_offset, 0) - self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(child_binop.lineno, 1) self.assertEqual(child_binop.end_col_offset, 2) - self.assertEqual(parent_binop.end_lineno, 2) + self.assertEqual(child_binop.end_lineno, 2) self.assertEqual(grandchild_binop.col_offset, 0) - self.assertEqual(parent_binop.lineno, 1) + self.assertEqual(grandchild_binop.lineno, 1) self.assertEqual(grandchild_binop.end_col_offset, 3) - self.assertEqual(parent_binop.end_lineno, 2) + self.assertEqual(grandchild_binop.end_lineno, 1) class ASTHelpers_Test(unittest.TestCase): maxDiff = None From webhook-mailer at python.org Tue Jul 9 14:00:33 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 09 Jul 2019 18:00:33 -0000 Subject: [Python-checkins] bpo-27679: Remove set_bitfields() from _ctypes_test (GH-14648) Message-ID: https://github.com/python/cpython/commit/3a3db970de344efbb4017fb9dde9204f0fd4bbdc commit: 3a3db970de344efbb4017fb9dde9204f0fd4bbdc branch: master author: Hai Shi committer: Victor Stinner date: 2019-07-09T20:00:27+02:00 summary: bpo-27679: Remove set_bitfields() from _ctypes_test (GH-14648) files: M Modules/_ctypes/_ctypes_test.c diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index bae4976a08d3..4789d6bdda8c 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -425,30 +425,6 @@ struct BITS { #endif }; -EXPORT(void) set_bitfields(struct BITS *bits, char name, int value) -{ - switch (name) { - case 'A': bits->A = value; break; - case 'B': bits->B = value; break; - case 'C': bits->C = value; break; - case 'D': bits->D = value; break; - case 'E': bits->E = value; break; - case 'F': bits->F = value; break; - case 'G': bits->G = value; break; - case 'H': bits->H = value; break; - case 'I': bits->I = value; break; -#ifdef SIGNED_SHORT_BITFIELDS - case 'M': bits->M = value; break; - case 'N': bits->N = value; break; - case 'O': bits->O = value; break; - case 'P': bits->P = value; break; - case 'Q': bits->Q = value; break; - case 'R': bits->R = value; break; - case 'S': bits->S = value; break; -#endif - } -} - EXPORT(int) unpack_bitfields(struct BITS *bits, char name) { switch (name) { From webhook-mailer at python.org Tue Jul 9 14:37:31 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 09 Jul 2019 18:37:31 -0000 Subject: [Python-checkins] bpo-26806: IDLE should run without docstrings (#14657) Message-ID: https://github.com/python/cpython/commit/6aeb2fe606408aae14c246470794f1303b3be812 commit: 6aeb2fe606408aae14c246470794f1303b3be812 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-09T14:37:25-04:00 summary: bpo-26806: IDLE should run without docstrings (#14657) After fcf1d00, IDLE startup failed with python compiled without docstrings. files: M Lib/idlelib/idle_test/test_run.py M Lib/idlelib/run.py diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py index d0f1e9207bb1..cad0b4d98f8e 100644 --- a/Lib/idlelib/idle_test/test_run.py +++ b/Lib/idlelib/idle_test/test_run.py @@ -292,6 +292,14 @@ def test_default_recursion_limit_preserved(self): new_reclimit = sys.getrecursionlimit() self.assertEqual(new_reclimit, orig_reclimit) + def test_fixdoc(self): + def func(): "docstring" + run.fixdoc(func, "more") + self.assertEqual(func.__doc__, "docstring\n\nmore") + func.__doc__ = None + run.fixdoc(func, "more") + self.assertEqual(func.__doc__, "more") + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index c6ed76b23a2d..41e0ded44029 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -307,7 +307,12 @@ def fix_scaling(root): font['size'] = round(-0.75*size) +def fixdoc(fun, text): + tem = (fun.__doc__ + '\n\n') if fun.__doc__ is not None else '' + fun.__doc__ = tem + textwrap.fill(textwrap.dedent(text)) + RECURSIONLIMIT_DELTA = 30 + def install_recursionlimit_wrappers(): """Install wrappers to always add 30 to the recursion limit.""" # see: bpo-26806 @@ -329,19 +334,17 @@ def setrecursionlimit(*args, **kwargs): return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA) - setrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ - This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible - uninterruptible loops. - """).strip()) + fixdoc(setrecursionlimit, f"""\ + This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible + uninterruptible loops.""") @functools.wraps(sys.getrecursionlimit) def getrecursionlimit(): return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA - getrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ - This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate for - the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit. - """).strip()) + fixdoc(getrecursionlimit, f"""\ + This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate + for the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit.""") # add the delta to the default recursion limit, to compensate sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA) From webhook-mailer at python.org Tue Jul 9 14:59:30 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 09 Jul 2019 18:59:30 -0000 Subject: [Python-checkins] bpo-26806: IDLE should run without docstrings (GH-14657) (GH-14677) Message-ID: https://github.com/python/cpython/commit/b82188d9bad1774624cb1788dbdec2f0d7d65688 commit: b82188d9bad1774624cb1788dbdec2f0d7d65688 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-09T14:59:19-04:00 summary: bpo-26806: IDLE should run without docstrings (GH-14657) (GH-14677) After fcf1d00, IDLE startup failed with python compiled without docstrings. (cherry picked from commit 6aeb2fe606408aae14c246470794f1303b3be812) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/idle_test/test_run.py M Lib/idlelib/run.py diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py index d0f1e9207bb1..cad0b4d98f8e 100644 --- a/Lib/idlelib/idle_test/test_run.py +++ b/Lib/idlelib/idle_test/test_run.py @@ -292,6 +292,14 @@ def test_default_recursion_limit_preserved(self): new_reclimit = sys.getrecursionlimit() self.assertEqual(new_reclimit, orig_reclimit) + def test_fixdoc(self): + def func(): "docstring" + run.fixdoc(func, "more") + self.assertEqual(func.__doc__, "docstring\n\nmore") + func.__doc__ = None + run.fixdoc(func, "more") + self.assertEqual(func.__doc__, "more") + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index c6ed76b23a2d..41e0ded44029 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -307,7 +307,12 @@ def fix_scaling(root): font['size'] = round(-0.75*size) +def fixdoc(fun, text): + tem = (fun.__doc__ + '\n\n') if fun.__doc__ is not None else '' + fun.__doc__ = tem + textwrap.fill(textwrap.dedent(text)) + RECURSIONLIMIT_DELTA = 30 + def install_recursionlimit_wrappers(): """Install wrappers to always add 30 to the recursion limit.""" # see: bpo-26806 @@ -329,19 +334,17 @@ def setrecursionlimit(*args, **kwargs): return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA) - setrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ - This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible - uninterruptible loops. - """).strip()) + fixdoc(setrecursionlimit, f"""\ + This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible + uninterruptible loops.""") @functools.wraps(sys.getrecursionlimit) def getrecursionlimit(): return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA - getrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ - This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate for - the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit. - """).strip()) + fixdoc(getrecursionlimit, f"""\ + This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate + for the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit.""") # add the delta to the default recursion limit, to compensate sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA) From webhook-mailer at python.org Tue Jul 9 15:16:49 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 09 Jul 2019 19:16:49 -0000 Subject: [Python-checkins] bpo-26806: IDLE should run without docstrings (GH-14657) (GH-14678) Message-ID: https://github.com/python/cpython/commit/f54f062f68ac82fc3e7c92345b4d5fbe2534dc84 commit: f54f062f68ac82fc3e7c92345b4d5fbe2534dc84 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-09T15:16:44-04:00 summary: bpo-26806: IDLE should run without docstrings (GH-14657) (GH-14678) After fcf1d00, IDLE startup failed with python compiled without docstrings. (cherry picked from commit 6aeb2fe606408aae14c246470794f1303b3be812) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/idle_test/test_run.py M Lib/idlelib/run.py diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py index d0f1e9207bb1..cad0b4d98f8e 100644 --- a/Lib/idlelib/idle_test/test_run.py +++ b/Lib/idlelib/idle_test/test_run.py @@ -292,6 +292,14 @@ def test_default_recursion_limit_preserved(self): new_reclimit = sys.getrecursionlimit() self.assertEqual(new_reclimit, orig_reclimit) + def test_fixdoc(self): + def func(): "docstring" + run.fixdoc(func, "more") + self.assertEqual(func.__doc__, "docstring\n\nmore") + func.__doc__ = None + run.fixdoc(func, "more") + self.assertEqual(func.__doc__, "more") + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index c6ed76b23a2d..41e0ded44029 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -307,7 +307,12 @@ def fix_scaling(root): font['size'] = round(-0.75*size) +def fixdoc(fun, text): + tem = (fun.__doc__ + '\n\n') if fun.__doc__ is not None else '' + fun.__doc__ = tem + textwrap.fill(textwrap.dedent(text)) + RECURSIONLIMIT_DELTA = 30 + def install_recursionlimit_wrappers(): """Install wrappers to always add 30 to the recursion limit.""" # see: bpo-26806 @@ -329,19 +334,17 @@ def setrecursionlimit(*args, **kwargs): return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA) - setrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ - This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible - uninterruptible loops. - """).strip()) + fixdoc(setrecursionlimit, f"""\ + This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible + uninterruptible loops.""") @functools.wraps(sys.getrecursionlimit) def getrecursionlimit(): return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA - getrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ - This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate for - the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit. - """).strip()) + fixdoc(getrecursionlimit, f"""\ + This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate + for the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit.""") # add the delta to the default recursion limit, to compensate sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA) From webhook-mailer at python.org Wed Jul 10 11:56:19 2019 From: webhook-mailer at python.org (Stefan Krah) Date: Wed, 10 Jul 2019 15:56:19 -0000 Subject: [Python-checkins] Really remove vcstdint.h. (#14686) Message-ID: https://github.com/python/cpython/commit/4749dbe54cfbcae64a06da2623e44f4017032207 commit: 4749dbe54cfbcae64a06da2623e44f4017032207 branch: master author: Stefan Krah committer: GitHub date: 2019-07-10T17:55:48+02:00 summary: Really remove vcstdint.h. (#14686) files: D Modules/_decimal/libmpdec/vcstdint.h M Modules/_decimal/libmpdec/README.txt M Modules/_decimal/libmpdec/vccompat.h diff --git a/Modules/_decimal/libmpdec/README.txt b/Modules/_decimal/libmpdec/README.txt index 96b72232d2ad..4e0e4f30bed0 100644 --- a/Modules/_decimal/libmpdec/README.txt +++ b/Modules/_decimal/libmpdec/README.txt @@ -30,7 +30,6 @@ Files required for the Python _decimal module Visual Studio only: ~~~~~~~~~~~~~~~~~~~ vccompat.h -> snprintf <==> sprintf_s and similar things. - vcstdint.h -> stdint.h (included in VS 2010 but not in VS 2008). vcdiv64.asm -> Double word division used in typearith.h. VS 2008 does not allow inline asm for x64. Also, it does not provide an intrinsic for double word division. diff --git a/Modules/_decimal/libmpdec/vccompat.h b/Modules/_decimal/libmpdec/vccompat.h index dd131d8da264..2ba805dcc564 100644 --- a/Modules/_decimal/libmpdec/vccompat.h +++ b/Modules/_decimal/libmpdec/vccompat.h @@ -30,7 +30,7 @@ #define VCCOMPAT_H -/* Visual C fixes: no stdint.h, no snprintf ... */ +/* Visual C fixes: no snprintf ... */ #ifdef _MSC_VER #undef inline #define inline __inline diff --git a/Modules/_decimal/libmpdec/vcstdint.h b/Modules/_decimal/libmpdec/vcstdint.h deleted file mode 100644 index 17dcad4541ee..000000000000 --- a/Modules/_decimal/libmpdec/vcstdint.h +++ /dev/null @@ -1,232 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2008 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The name of the author may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include - -// For Visual Studio 6 in C++ mode wrap include with 'extern "C++" {}' -// or compiler give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#if (_MSC_VER < 1300) && defined(__cplusplus) - extern "C++" { -#endif -# include -#if (_MSC_VER < 1300) && defined(__cplusplus) - } -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types -typedef __int8 int8_t; -typedef __int16 int16_t; -typedef __int32 int32_t; -typedef __int64 int64_t; -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ - typedef __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ - typedef _W64 int intptr_t; - typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -#define INTMAX_C INT64_C -#define UINTMAX_C UINT64_C - -#endif // __STDC_CONSTANT_MACROS ] - - -#endif // _MSC_STDINT_H_ ] From webhook-mailer at python.org Wed Jul 10 12:27:45 2019 From: webhook-mailer at python.org (Stefan Krah) Date: Wed, 10 Jul 2019 16:27:45 -0000 Subject: [Python-checkins] Rename memory.c to mpalloc.c for consistency with the header file. (#14687) Message-ID: https://github.com/python/cpython/commit/f117d871c467e82d98b127fd77d2542600d67c39 commit: f117d871c467e82d98b127fd77d2542600d67c39 branch: master author: Stefan Krah committer: GitHub date: 2019-07-10T18:27:38+02:00 summary: Rename memory.c to mpalloc.c for consistency with the header file. (#14687) files: A Modules/_decimal/libmpdec/mpalloc.c D Modules/_decimal/libmpdec/memory.c M Modules/_decimal/libmpdec/README.txt M PCbuild/_decimal.vcxproj M PCbuild/_decimal.vcxproj.filters M setup.py diff --git a/Modules/_decimal/libmpdec/README.txt b/Modules/_decimal/libmpdec/README.txt index 4e0e4f30bed0..dc97820a6eb0 100644 --- a/Modules/_decimal/libmpdec/README.txt +++ b/Modules/_decimal/libmpdec/README.txt @@ -20,7 +20,7 @@ Files required for the Python _decimal module context.c -> Context functions. io.{c,h} -> Conversions between mpd_t and ASCII strings, mpd_t formatting (allows UTF-8 fill character). - memory.{c,h} -> Allocation handlers with overflow detection + mpalloc.{c,h} -> Allocation handlers with overflow detection and functions for switching between static and dynamic mpd_t. mpdecimal.{c,h} -> All (quiet) functions of the specification. diff --git a/Modules/_decimal/libmpdec/memory.c b/Modules/_decimal/libmpdec/mpalloc.c similarity index 100% rename from Modules/_decimal/libmpdec/memory.c rename to Modules/_decimal/libmpdec/mpalloc.c diff --git a/PCbuild/_decimal.vcxproj b/PCbuild/_decimal.vcxproj index 465a7ade9a01..f0f387f3bfaa 100644 --- a/PCbuild/_decimal.vcxproj +++ b/PCbuild/_decimal.vcxproj @@ -131,7 +131,7 @@ - + diff --git a/PCbuild/_decimal.vcxproj.filters b/PCbuild/_decimal.vcxproj.filters index 7e19aa2f6596..1aa9d020d672 100644 --- a/PCbuild/_decimal.vcxproj.filters +++ b/PCbuild/_decimal.vcxproj.filters @@ -92,7 +92,7 @@ Source Files - + Source Files diff --git a/setup.py b/setup.py index e54d31f53338..3ec89cedfd57 100644 --- a/setup.py +++ b/setup.py @@ -2095,7 +2095,7 @@ def detect_decimal(self): '_decimal/libmpdec/fnt.c', '_decimal/libmpdec/fourstep.c', '_decimal/libmpdec/io.c', - '_decimal/libmpdec/memory.c', + '_decimal/libmpdec/mpalloc.c', '_decimal/libmpdec/mpdecimal.c', '_decimal/libmpdec/numbertheory.c', '_decimal/libmpdec/sixstep.c', From webhook-mailer at python.org Wed Jul 10 15:04:39 2019 From: webhook-mailer at python.org (Neil Schemenauer) Date: Wed, 10 Jul 2019 19:04:39 -0000 Subject: [Python-checkins] bpo-37537: Compute allocated blocks in _Py_GetAllocatedBlocks() (#14680) Message-ID: https://github.com/python/cpython/commit/5d25f2b70351fc6a56ce5513ccf5f58556c18837 commit: 5d25f2b70351fc6a56ce5513ccf5f58556c18837 branch: master author: Neil Schemenauer committer: GitHub date: 2019-07-10T12:04:16-07:00 summary: bpo-37537: Compute allocated blocks in _Py_GetAllocatedBlocks() (#14680) Keeping an account of allocated blocks slows down _PyObject_Malloc() and _PyObject_Free() by a measureable amount. Have _Py_GetAllocatedBlocks() iterate over the arenas to sum up the allocated blocks for pymalloc. files: A Misc/NEWS.d/next/Core and Builtins/2019-07-10-09-56-47.bpo-37537.OkB0wd.rst M Objects/obmalloc.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-10-09-56-47.bpo-37537.OkB0wd.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-10-09-56-47.bpo-37537.OkB0wd.rst new file mode 100644 index 000000000000..abf874475d6d --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-10-09-56-47.bpo-37537.OkB0wd.rst @@ -0,0 +1,3 @@ +Compute allocated pymalloc blocks inside _Py_GetAllocatedBlocks(). This +slows down _Py_GetAllocatedBlocks() but gives a small speedup to +_PyObject_Malloc() and _PyObject_Free(). diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 622da3ad08fc..bb154c76ab18 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -1206,12 +1206,29 @@ static size_t ntimes_arena_allocated = 0; /* High water mark (max value ever seen) for narenas_currently_allocated. */ static size_t narenas_highwater = 0; -static Py_ssize_t _Py_AllocatedBlocks = 0; +static Py_ssize_t raw_allocated_blocks; Py_ssize_t _Py_GetAllocatedBlocks(void) { - return _Py_AllocatedBlocks; + Py_ssize_t n = raw_allocated_blocks; + /* add up allocated blocks for used pools */ + for (uint i = 0; i < maxarenas; ++i) { + /* Skip arenas which are not allocated. */ + if (arenas[i].address == NULL) { + continue; + } + + uintptr_t base = (uintptr_t)_Py_ALIGN_UP(arenas[i].address, POOL_SIZE); + + /* visit every pool in the arena */ + assert(base <= (uintptr_t) arenas[i].pool_address); + for (; base < (uintptr_t) arenas[i].pool_address; base += POOL_SIZE) { + poolp p = (poolp)base; + n += p->ref.count; + } + } + return n; } @@ -1622,13 +1639,12 @@ _PyObject_Malloc(void *ctx, size_t nbytes) { void* ptr; if (pymalloc_alloc(ctx, &ptr, nbytes)) { - _Py_AllocatedBlocks++; return ptr; } ptr = PyMem_RawMalloc(nbytes); if (ptr != NULL) { - _Py_AllocatedBlocks++; + raw_allocated_blocks++; } return ptr; } @@ -1644,13 +1660,12 @@ _PyObject_Calloc(void *ctx, size_t nelem, size_t elsize) if (pymalloc_alloc(ctx, &ptr, nbytes)) { memset(ptr, 0, nbytes); - _Py_AllocatedBlocks++; return ptr; } ptr = PyMem_RawCalloc(nelem, elsize); if (ptr != NULL) { - _Py_AllocatedBlocks++; + raw_allocated_blocks++; } return ptr; } @@ -1899,10 +1914,10 @@ _PyObject_Free(void *ctx, void *p) return; } - _Py_AllocatedBlocks--; if (!pymalloc_free(ctx, p)) { /* pymalloc didn't allocate this address */ PyMem_RawFree(p); + raw_allocated_blocks--; } } From webhook-mailer at python.org Wed Jul 10 17:24:05 2019 From: webhook-mailer at python.org (Tim Peters) Date: Wed, 10 Jul 2019 21:24:05 -0000 Subject: [Python-checkins] Fix compiler warning in new code. (#14690) Message-ID: https://github.com/python/cpython/commit/b64c2c66e5cfe6d138b342ee58ee3b13a8d7ef16 commit: b64c2c66e5cfe6d138b342ee58ee3b13a8d7ef16 branch: master author: Tim Peters committer: GitHub date: 2019-07-10T16:24:01-05:00 summary: Fix compiler warning in new code. (#14690) uintptr_t is an integer type, and can't be compared to NULL directly. files: M Objects/obmalloc.c diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index bb154c76ab18..2c00efc255dc 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -1215,7 +1215,7 @@ _Py_GetAllocatedBlocks(void) /* add up allocated blocks for used pools */ for (uint i = 0; i < maxarenas; ++i) { /* Skip arenas which are not allocated. */ - if (arenas[i].address == NULL) { + if (arenas[i].address == 0) { continue; } From webhook-mailer at python.org Wed Jul 10 22:43:08 2019 From: webhook-mailer at python.org (Benjamin Peterson) Date: Thu, 11 Jul 2019 02:43:08 -0000 Subject: [Python-checkins] Document default parameter of .seek() in the signature. (GH-14691) Message-ID: https://github.com/python/cpython/commit/2a3d4d9c53dd4831c3ecf56bc7c4a289c33030d6 commit: 2a3d4d9c53dd4831c3ecf56bc7c4a289c33030d6 branch: master author: Benjamin Peterson committer: GitHub date: 2019-07-10T19:43:04-07:00 summary: Document default parameter of .seek() in the signature. (GH-14691) files: M Doc/library/io.rst diff --git a/Doc/library/io.rst b/Doc/library/io.rst index fce9a747fd32..70e01153d419 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -327,7 +327,7 @@ I/O Base Classes Note that it's already possible to iterate on file objects using ``for line in file: ...`` without calling ``file.readlines()``. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given byte *offset*. *offset* is interpreted relative to the position indicated by *whence*. The default @@ -831,7 +831,7 @@ Text I/O If *size* is specified, at most *size* characters will be read. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given *offset*. Behaviour depends on the *whence* parameter. The default value for *whence* is From webhook-mailer at python.org Wed Jul 10 22:49:41 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 02:49:41 -0000 Subject: [Python-checkins] Document default parameter of .seek() in the signature. (GH-14691) Message-ID: https://github.com/python/cpython/commit/2847a75c21821dce21ad496980031751330b33b9 commit: 2847a75c21821dce21ad496980031751330b33b9 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-10T19:49:37-07:00 summary: Document default parameter of .seek() in the signature. (GH-14691) (cherry picked from commit 2a3d4d9c53dd4831c3ecf56bc7c4a289c33030d6) Co-authored-by: Benjamin Peterson files: M Doc/library/io.rst diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 966fb9cc7663..6bf56bc415b2 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -306,7 +306,7 @@ I/O Base Classes Note that it's already possible to iterate on file objects using ``for line in file: ...`` without calling ``file.readlines()``. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given byte *offset*. *offset* is interpreted relative to the position indicated by *whence*. The default @@ -810,7 +810,7 @@ Text I/O If *size* is specified, at most *size* characters will be read. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given *offset*. Behaviour depends on the *whence* parameter. The default value for *whence* is From webhook-mailer at python.org Wed Jul 10 22:49:52 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 02:49:52 -0000 Subject: [Python-checkins] Document default parameter of .seek() in the signature. (GH-14691) Message-ID: https://github.com/python/cpython/commit/aca82977632fafa1d97c938999d76fae37c64f23 commit: aca82977632fafa1d97c938999d76fae37c64f23 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-10T19:49:49-07:00 summary: Document default parameter of .seek() in the signature. (GH-14691) (cherry picked from commit 2a3d4d9c53dd4831c3ecf56bc7c4a289c33030d6) Co-authored-by: Benjamin Peterson files: M Doc/library/io.rst diff --git a/Doc/library/io.rst b/Doc/library/io.rst index fce9a747fd32..70e01153d419 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -327,7 +327,7 @@ I/O Base Classes Note that it's already possible to iterate on file objects using ``for line in file: ...`` without calling ``file.readlines()``. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given byte *offset*. *offset* is interpreted relative to the position indicated by *whence*. The default @@ -831,7 +831,7 @@ Text I/O If *size* is specified, at most *size* characters will be read. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given *offset*. Behaviour depends on the *whence* parameter. The default value for *whence* is From webhook-mailer at python.org Wed Jul 10 22:50:03 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 02:50:03 -0000 Subject: [Python-checkins] Document default parameter of .seek() in the signature. (GH-14691) Message-ID: https://github.com/python/cpython/commit/0517375c4494b728171b0e306959e3f2703e0046 commit: 0517375c4494b728171b0e306959e3f2703e0046 branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-10T19:49:59-07:00 summary: Document default parameter of .seek() in the signature. (GH-14691) (cherry picked from commit 2a3d4d9c53dd4831c3ecf56bc7c4a289c33030d6) Co-authored-by: Benjamin Peterson files: M Doc/library/io.rst diff --git a/Doc/library/io.rst b/Doc/library/io.rst index dcdd01cd0aa5..7c053f973aa1 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -304,7 +304,7 @@ I/O Base Classes Note that it's already possible to iterate on file objects using ``for line in file: ...`` without calling ``file.readlines()``. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given byte *offset*. *offset* is interpreted relative to the position indicated by *whence*. The default @@ -736,7 +736,7 @@ Text I/O If *limit* is specified, at most *limit* characters will be read. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given *offset*. Behaviour depends on the *whence* parameter. The default value for *whence* is From webhook-mailer at python.org Thu Jul 11 04:59:23 2019 From: webhook-mailer at python.org (Inada Naoki) Date: Thu, 11 Jul 2019 08:59:23 -0000 Subject: [Python-checkins] bpo-37547: add _PyObject_CallMethodOneArg (GH-14685) Message-ID: https://github.com/python/cpython/commit/59ad110d7a7784d53d0b502eebce0346597a6bef commit: 59ad110d7a7784d53d0b502eebce0346597a6bef branch: master author: Jeroen Demeyer committer: Inada Naoki date: 2019-07-11T17:59:05+09:00 summary: bpo-37547: add _PyObject_CallMethodOneArg (GH-14685) files: M Doc/c-api/object.rst M Include/cpython/abstract.h M Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst M Modules/_abc.c M Modules/_asynciomodule.c M Modules/_ctypes/callproc.c M Modules/_datetimemodule.c M Modules/_elementtree.c M Modules/_io/bufferedio.c M Modules/_io/fileio.c M Modules/_io/textio.c M Modules/_io/winconsoleio.c M Modules/_pickle.c M Modules/_sqlite/connection.c M Modules/arraymodule.c M Modules/cjkcodecs/multibytecodec.c M Objects/dictobject.c M Objects/typeobject.c M Python/_warnings.c M Python/ceval.c M Python/import.c M Python/marshal.c M Python/sysmodule.c diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 8ca034452954..2cf032821afb 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -375,6 +375,18 @@ Object Protocol .. versionadded:: 3.9 +.. c:function:: PyObject* _PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg) + + Call a method of the Python object *obj* with a single positional argument + *arg*, where the name of the method is given as a Python string object in + *name*. + + Return the result of the call on success, or raise an exception and return + *NULL* on failure. + + .. versionadded:: 3.9 + + .. c:function:: PyObject* _PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) Call a callable Python object *callable*, using diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h index e9a23195f414..d57aa54bc466 100644 --- a/Include/cpython/abstract.h +++ b/Include/cpython/abstract.h @@ -163,6 +163,15 @@ _PyObject_CallMethodNoArgs(PyObject *self, PyObject *name) 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); } +static inline PyObject * +_PyObject_CallMethodOneArg(PyObject *self, PyObject *name, PyObject *arg) +{ + assert(arg != NULL); + PyObject *args[2] = {self, arg}; + return _PyObject_VectorcallMethod(name, args, + 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); +} + /* Like PyObject_CallMethod(), but expect a _Py_Identifier* as the method name. */ PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj, @@ -198,6 +207,15 @@ _PyObject_CallMethodIdNoArgs(PyObject *self, _Py_Identifier *name) 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); } +static inline PyObject * +_PyObject_CallMethodIdOneArg(PyObject *self, _Py_Identifier *name, PyObject *arg) +{ + assert(arg != NULL); + PyObject *args[2] = {self, arg}; + return _PyObject_VectorcallMethodId(name, args, + 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); +} + PyAPI_FUNC(int) _PyObject_HasLen(PyObject *o); /* Guess the size of object 'o' using len(o) or o.__length_hint__(). diff --git a/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst b/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst index df0e807e1166..63b355ecdb33 100644 --- a/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst +++ b/Misc/NEWS.d/next/C API/2019-06-19-12-06-31.bpo-37337.gXIGyU.rst @@ -1,2 +1,2 @@ -Add fast functions for calling methods: :c:func:`_PyObject_VectorcallMethod` -and :c:func:`_PyObject_CallMethodNoArgs` +Add fast functions for calling methods: :c:func:`_PyObject_VectorcallMethod`, +:c:func:`_PyObject_CallMethodNoArgs` and :c:func:`_PyObject_CallMethodOneArg`. diff --git a/Modules/_abc.c b/Modules/_abc.c index 219ea0dd628f..7b5d4180bf87 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -480,7 +480,6 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self, /*[clinic end generated code: output=b8b5148f63b6b56f input=a4f4525679261084]*/ { PyObject *subtype, *result = NULL, *subclass = NULL; - PyObject *margs[2]; _abc_data *impl = _get_impl(self); if (impl == NULL) { return NULL; @@ -515,16 +514,12 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self, } } /* Fall back to the subclass check. */ - margs[0] = self; - margs[1] = subclass; - result = _PyObject_VectorcallMethodId(&PyId___subclasscheck__, margs, - 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + result = _PyObject_CallMethodIdOneArg(self, &PyId___subclasscheck__, + subclass); goto end; } - margs[0] = self; - margs[1] = subclass; - result = _PyObject_VectorcallMethodId(&PyId___subclasscheck__, margs, - 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + result = _PyObject_CallMethodIdOneArg(self, &PyId___subclasscheck__, + subclass); if (result == NULL) { goto end; } @@ -536,10 +531,8 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self, break; case 0: Py_DECREF(result); - margs[0] = self; - margs[1] = subtype; - result = _PyObject_VectorcallMethodId(&PyId___subclasscheck__, margs, - 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + result = _PyObject_CallMethodIdOneArg(self, &PyId___subclasscheck__, + subtype); break; case 1: // Nothing to do. break; @@ -620,8 +613,8 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, } /* 3. Check the subclass hook. */ - ok = _PyObject_CallMethodIdObjArgs((PyObject *)self, &PyId___subclasshook__, - subclass, NULL); + ok = _PyObject_CallMethodIdOneArg((PyObject *)self, &PyId___subclasshook__, + subclass); if (ok == NULL) { goto end; } diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 6c469d2a2ec5..e9e6c5682d62 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1842,8 +1842,8 @@ register_task(PyObject *task) { _Py_IDENTIFIER(add); - PyObject *res = _PyObject_CallMethodIdObjArgs( - all_tasks, &PyId_add, task, NULL); + PyObject *res = _PyObject_CallMethodIdOneArg(all_tasks, + &PyId_add, task); if (res == NULL) { return -1; } @@ -1857,8 +1857,8 @@ unregister_task(PyObject *task) { _Py_IDENTIFIER(discard); - PyObject *res = _PyObject_CallMethodIdObjArgs( - all_tasks, &PyId_discard, task, NULL); + PyObject *res = _PyObject_CallMethodIdOneArg(all_tasks, + &PyId_discard, task); if (res == NULL) { return -1; } @@ -2611,13 +2611,11 @@ task_step_impl(TaskObj *task, PyObject *exc) result = _PyGen_Send((PyGenObject*)coro, Py_None); } else { - result = _PyObject_CallMethodIdObjArgs(coro, &PyId_send, - Py_None, NULL); + result = _PyObject_CallMethodIdOneArg(coro, &PyId_send, Py_None); } } else { - result = _PyObject_CallMethodIdObjArgs(coro, &PyId_throw, - exc, NULL); + result = _PyObject_CallMethodIdOneArg(coro, &PyId_throw, exc); if (clear_exc) { /* We created 'exc' during this call */ Py_DECREF(exc); diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 973a654bf355..bd67c6eeaeed 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1699,7 +1699,7 @@ unpickle(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "OO!", &typ, &PyTuple_Type, &state)) return NULL; - obj = _PyObject_CallMethodIdObjArgs(typ, &PyId___new__, typ, NULL); + obj = _PyObject_CallMethodIdOneArg(typ, &PyId___new__, typ); if (obj == NULL) return NULL; diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0546368d1df7..19d3d7e848f5 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1237,8 +1237,7 @@ call_tzname(PyObject *tzinfo, PyObject *tzinfoarg) if (tzinfo == Py_None) Py_RETURN_NONE; - result = _PyObject_CallMethodIdObjArgs(tzinfo, &PyId_tzname, - tzinfoarg, NULL); + result = _PyObject_CallMethodIdOneArg(tzinfo, &PyId_tzname, tzinfoarg); if (result == NULL || result == Py_None) return result; @@ -1693,8 +1692,7 @@ build_struct_time(int y, int m, int d, int hh, int mm, int ss, int dstflag) return NULL; } - result = _PyObject_CallMethodIdObjArgs(time, &PyId_struct_time, - args, NULL); + result = _PyObject_CallMethodIdOneArg(time, &PyId_struct_time, args); Py_DECREF(time); Py_DECREF(args); return result; @@ -2894,8 +2892,7 @@ date_today(PyObject *cls, PyObject *dummy) * time.time() delivers; if someone were gonzo about optimization, * date.today() could get away with plain C time(). */ - result = _PyObject_CallMethodIdObjArgs(cls, &PyId_fromtimestamp, - time, NULL); + result = _PyObject_CallMethodIdOneArg(cls, &PyId_fromtimestamp, time); Py_DECREF(time); return result; } @@ -3209,8 +3206,8 @@ date_format(PyDateTime_Date *self, PyObject *args) if (PyUnicode_GetLength(format) == 0) return PyObject_Str((PyObject *)self); - return _PyObject_CallMethodIdObjArgs((PyObject *)self, &PyId_strftime, - format, NULL); + return _PyObject_CallMethodIdOneArg((PyObject *)self, &PyId_strftime, + format); } /* ISO methods. */ @@ -5960,7 +5957,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) temp = (PyObject *)result; result = (PyDateTime_DateTime *) - _PyObject_CallMethodIdObjArgs(tzinfo, &PyId_fromutc, temp, NULL); + _PyObject_CallMethodIdOneArg(tzinfo, &PyId_fromutc, temp); Py_DECREF(temp); return result; diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index b93ec3d12786..b9b50165bfda 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2679,7 +2679,7 @@ treebuilder_add_subelement(PyObject *element, PyObject *child) } else { PyObject *res; - res = _PyObject_CallMethodIdObjArgs(element, &PyId_append, child, NULL); + res = _PyObject_CallMethodIdOneArg(element, &PyId_append, child); if (res == NULL) return -1; Py_DECREF(res); diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 9e7e5f3d0935..86dd277f94c1 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -439,8 +439,8 @@ buffered_dealloc_warn(buffered *self, PyObject *source) { if (self->ok && self->raw) { PyObject *r; - r = _PyObject_CallMethodIdObjArgs(self->raw, &PyId__dealloc_warn, - source, NULL); + r = _PyObject_CallMethodIdOneArg(self->raw, &PyId__dealloc_warn, + source); if (r) Py_DECREF(r); else @@ -1323,7 +1323,7 @@ _io__Buffered_truncate_impl(buffered *self, PyObject *pos) goto end; Py_CLEAR(res); } - res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_truncate, pos, NULL); + res = _PyObject_CallMethodOneArg(self->raw, _PyIO_str_truncate, pos); if (res == NULL) goto end; /* Reset cached position */ @@ -1467,7 +1467,7 @@ _bufferedreader_raw_read(buffered *self, char *start, Py_ssize_t len) raised (see issue #10956). */ do { - res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_readinto, memobj, NULL); + res = _PyObject_CallMethodOneArg(self->raw, _PyIO_str_readinto, memobj); } while (res == NULL && _PyIO_trap_eintr()); Py_DECREF(memobj); if (res == NULL) @@ -1815,7 +1815,7 @@ _bufferedwriter_raw_write(buffered *self, char *start, Py_ssize_t len) */ do { errno = 0; - res = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_write, memobj, NULL); + res = _PyObject_CallMethodOneArg(self->raw, _PyIO_str_write, memobj); errnum = errno; } while (res == NULL && _PyIO_trap_eintr()); Py_DECREF(memobj); diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 7f784a34c30d..e4cbbfa9bb8c 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -145,8 +145,8 @@ _io_FileIO_close_impl(fileio *self) PyObject *exc, *val, *tb; int rc; _Py_IDENTIFIER(close); - res = _PyObject_CallMethodIdObjArgs((PyObject*)&PyRawIOBase_Type, - &PyId_close, self, NULL); + res = _PyObject_CallMethodIdOneArg((PyObject*)&PyRawIOBase_Type, + &PyId_close, (PyObject *)self); if (!self->closefd) { self->fd = -1; return res; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index ed1dc005c69a..05911d9222ff 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -977,8 +977,8 @@ _textiowrapper_fix_encoder_state(textio *self) if (cmp == 0) { self->encoding_start_of_stream = 0; - PyObject *res = PyObject_CallMethodObjArgs( - self->encoder, _PyIO_str_setstate, _PyLong_Zero, NULL); + PyObject *res = _PyObject_CallMethodOneArg( + self->encoder, _PyIO_str_setstate, _PyLong_Zero); if (res == NULL) { return -1; } @@ -1155,8 +1155,8 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, PyObject *locale_module = _PyIO_get_locale_module(state); if (locale_module == NULL) goto catch_ImportError; - self->encoding = _PyObject_CallMethodIdObjArgs( - locale_module, &PyId_getpreferredencoding, Py_False, NULL); + self->encoding = _PyObject_CallMethodIdOneArg( + locale_module, &PyId_getpreferredencoding, Py_False); Py_DECREF(locale_module); if (self->encoding == NULL) { catch_ImportError: @@ -1597,8 +1597,7 @@ _textiowrapper_writeflush(textio *self) PyObject *ret; do { - ret = PyObject_CallMethodObjArgs(self->buffer, - _PyIO_str_write, b, NULL); + ret = _PyObject_CallMethodOneArg(self->buffer, _PyIO_str_write, b); } while (ret == NULL && _PyIO_trap_eintr()); Py_DECREF(b); if (ret == NULL) @@ -1668,8 +1667,7 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) self->encoding_start_of_stream = 0; } else - b = PyObject_CallMethodObjArgs(self->encoder, - _PyIO_str_encode, text, NULL); + b = _PyObject_CallMethodOneArg(self->encoder, _PyIO_str_encode, text); Py_DECREF(text); if (b == NULL) @@ -1851,9 +1849,9 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) if (chunk_size == NULL) goto fail; - input_chunk = PyObject_CallMethodObjArgs(self->buffer, + input_chunk = _PyObject_CallMethodOneArg(self->buffer, (self->has_read1 ? _PyIO_str_read1: _PyIO_str_read), - chunk_size, NULL); + chunk_size); Py_DECREF(chunk_size); if (input_chunk == NULL) goto fail; @@ -2414,8 +2412,8 @@ _textiowrapper_encoder_reset(textio *self, int start_of_stream) self->encoding_start_of_stream = 1; } else { - res = PyObject_CallMethodObjArgs(self->encoder, _PyIO_str_setstate, - _PyLong_Zero, NULL); + res = _PyObject_CallMethodOneArg(self->encoder, _PyIO_str_setstate, + _PyLong_Zero); self->encoding_start_of_stream = 0; } if (res == NULL) @@ -2554,8 +2552,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) posobj = PyLong_FromOff_t(cookie.start_pos); if (posobj == NULL) goto fail; - res = PyObject_CallMethodObjArgs(self->buffer, - _PyIO_str_seek, posobj, NULL); + res = _PyObject_CallMethodOneArg(self->buffer, _PyIO_str_seek, posobj); Py_DECREF(posobj); if (res == NULL) goto fail; @@ -2837,7 +2834,7 @@ _io_TextIOWrapper_tell_impl(textio *self) } finally: - res = _PyObject_CallMethodIdObjArgs(self->decoder, &PyId_setstate, saved_state, NULL); + res = _PyObject_CallMethodIdOneArg(self->decoder, &PyId_setstate, saved_state); Py_DECREF(saved_state); if (res == NULL) return NULL; @@ -2851,7 +2848,7 @@ _io_TextIOWrapper_tell_impl(textio *self) if (saved_state) { PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); - res = _PyObject_CallMethodIdObjArgs(self->decoder, &PyId_setstate, saved_state, NULL); + res = _PyObject_CallMethodIdOneArg(self->decoder, &PyId_setstate, saved_state); _PyErr_ChainExceptions(type, value, traceback); Py_DECREF(saved_state); Py_XDECREF(res); @@ -2878,7 +2875,7 @@ _io_TextIOWrapper_truncate_impl(textio *self, PyObject *pos) return NULL; Py_DECREF(res); - return PyObject_CallMethodObjArgs(self->buffer, _PyIO_str_truncate, pos, NULL); + return _PyObject_CallMethodOneArg(self->buffer, _PyIO_str_truncate, pos); } static PyObject * @@ -3055,9 +3052,9 @@ _io_TextIOWrapper_close_impl(textio *self) else { PyObject *exc = NULL, *val, *tb; if (self->finalizing) { - res = _PyObject_CallMethodIdObjArgs(self->buffer, - &PyId__dealloc_warn, - self, NULL); + res = _PyObject_CallMethodIdOneArg(self->buffer, + &PyId__dealloc_warn, + (PyObject *)self); if (res) Py_DECREF(res); else diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index ea5d24f950a1..f27a6dc93a2b 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -204,8 +204,8 @@ _io__WindowsConsoleIO_close_impl(winconsoleio *self) PyObject *exc, *val, *tb; int rc; _Py_IDENTIFIER(close); - res = _PyObject_CallMethodIdObjArgs((PyObject*)&PyRawIOBase_Type, - &PyId_close, self, NULL); + res = _PyObject_CallMethodIdOneArg((PyObject*)&PyRawIOBase_Type, + &PyId_close, self); if (!self->closehandle) { self->handle = INVALID_HANDLE_VALUE; return res; diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 0b0928f5cd2f..735c13d03d8f 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -5773,7 +5773,7 @@ instantiate(PyObject *cls, PyObject *args) return NULL; } if (func == NULL) { - return _PyObject_CallMethodIdObjArgs(cls, &PyId___new__, cls, NULL); + return _PyObject_CallMethodIdOneArg(cls, &PyId___new__, cls); } Py_DECREF(func); } diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 30a9b889a9df..47b8b62cea26 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1185,9 +1185,9 @@ pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* iso return -1; } - uppercase_level = _PyObject_CallMethodIdObjArgs( + uppercase_level = _PyObject_CallMethodIdOneArg( (PyObject *)&PyUnicode_Type, &PyId_upper, - isolation_level, NULL); + isolation_level); if (!uppercase_level) { return -1; } @@ -1648,8 +1648,8 @@ pysqlite_connection_create_collation(pysqlite_Connection* self, PyObject* args) goto finally; } - uppercase_name = _PyObject_CallMethodIdObjArgs((PyObject *)&PyUnicode_Type, - &PyId_upper, name, NULL); + uppercase_name = _PyObject_CallMethodIdOneArg((PyObject *)&PyUnicode_Type, + &PyId_upper, name); if (!uppercase_name) { goto finally; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 26c90a8a5983..48986c419029 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1507,7 +1507,7 @@ array_array_tofile(arrayobject *self, PyObject *f) bytes = PyBytes_FromStringAndSize(ptr, size); if (bytes == NULL) return NULL; - res = _PyObject_CallMethodIdObjArgs(f, &PyId_write, bytes, NULL); + res = _PyObject_CallMethodIdOneArg(f, &PyId_write, bytes); Py_DECREF(bytes); if (res == NULL) return NULL; diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index 052ed8878c1c..4de678757b38 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -1776,7 +1776,7 @@ mbstreamwriter_iwrite(MultibyteStreamWriterObject *self, if (str == NULL) return -1; - wr = _PyObject_CallMethodIdObjArgs(self->stream, &PyId_write, str, NULL); + wr = _PyObject_CallMethodIdOneArg(self->stream, &PyId_write, str); Py_DECREF(str); if (wr == NULL) return -1; @@ -1870,7 +1870,7 @@ _multibytecodec_MultibyteStreamWriter_reset_impl(MultibyteStreamWriterObject *se if (PyBytes_Size(pwrt) > 0) { PyObject *wr; - wr = _PyObject_CallMethodIdObjArgs(self->stream, &PyId_write, pwrt); + wr = _PyObject_CallMethodIdOneArg(self->stream, &PyId_write, pwrt); if (wr == NULL) { Py_DECREF(pwrt); return NULL; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 422f4b0230ba..b6205d93ca14 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4159,7 +4159,7 @@ dictviews_sub(PyObject* self, PyObject *other) if (result == NULL) return NULL; - tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_difference_update, other, NULL); + tmp = _PyObject_CallMethodIdOneArg(result, &PyId_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; @@ -4179,7 +4179,7 @@ _PyDictView_Intersect(PyObject* self, PyObject *other) if (result == NULL) return NULL; - tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_intersection_update, other, NULL); + tmp = _PyObject_CallMethodIdOneArg(result, &PyId_intersection_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; @@ -4199,7 +4199,7 @@ dictviews_or(PyObject* self, PyObject *other) if (result == NULL) return NULL; - tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_update, other, NULL); + tmp = _PyObject_CallMethodIdOneArg(result, &PyId_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; @@ -4219,7 +4219,7 @@ dictviews_xor(PyObject* self, PyObject *other) if (result == NULL) return NULL; - tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_symmetric_difference_update, other, NULL); + tmp = _PyObject_CallMethodIdOneArg(result, &PyId_symmetric_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 96021eeccc57..8acf678fc9a4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4145,8 +4145,8 @@ _PyType_GetSlotNames(PyTypeObject *cls) /* Use _slotnames function from the copyreg module to find the slots by this class and its bases. This function will cache the result in __slotnames__. */ - slotnames = _PyObject_CallMethodIdObjArgs(copyreg, &PyId__slotnames, - cls, NULL); + slotnames = _PyObject_CallMethodIdOneArg(copyreg, &PyId__slotnames, + (PyObject *)cls); Py_DECREF(copyreg); if (slotnames == NULL) return NULL; diff --git a/Python/_warnings.c b/Python/_warnings.c index b1762e6bedaf..ecee399db6e8 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -164,7 +164,7 @@ check_matched(PyObject *obj, PyObject *arg) } /* Otherwise assume a regex filter and call its match() method */ - result = _PyObject_CallMethodIdObjArgs(obj, &PyId_match, arg, NULL); + result = _PyObject_CallMethodIdOneArg(obj, &PyId_match, arg); if (result == NULL) return -1; diff --git a/Python/ceval.c b/Python/ceval.c index fdd299561c61..7c7359166dad 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2052,7 +2052,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) if (v == Py_None) retval = Py_TYPE(receiver)->tp_iternext(receiver); else - retval = _PyObject_CallMethodIdObjArgs(receiver, &PyId_send, v, NULL); + retval = _PyObject_CallMethodIdOneArg(receiver, &PyId_send, v); } Py_DECREF(v); if (retval == NULL) { diff --git a/Python/import.c b/Python/import.c index df258751c82e..15f1d9417600 100644 --- a/Python/import.c +++ b/Python/import.c @@ -962,9 +962,8 @@ PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, external= PyObject_GetAttrString(interp->importlib, "_bootstrap_external"); if (external != NULL) { - pathobj = _PyObject_CallMethodIdObjArgs(external, - &PyId__get_sourcefile, cpathobj, - NULL); + pathobj = _PyObject_CallMethodIdOneArg( + external, &PyId__get_sourcefile, cpathobj); Py_DECREF(external); } if (pathobj == NULL) @@ -1827,9 +1826,8 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, */ spec = _PyObject_GetAttrId(mod, &PyId___spec__); if (_PyModuleSpec_IsInitializing(spec)) { - PyObject *value = _PyObject_CallMethodIdObjArgs(interp->importlib, - &PyId__lock_unlock_module, abs_name, - NULL); + PyObject *value = _PyObject_CallMethodIdOneArg( + interp->importlib, &PyId__lock_unlock_module, abs_name); if (value == NULL) { Py_DECREF(spec); goto error; @@ -1968,7 +1966,7 @@ PyImport_ReloadModule(PyObject *m) } } - reloaded_module = _PyObject_CallMethodIdObjArgs(imp, &PyId_reload, m, NULL); + reloaded_module = _PyObject_CallMethodIdOneArg(imp, &PyId_reload, m); Py_DECREF(imp); return reloaded_module; } diff --git a/Python/marshal.c b/Python/marshal.c index b2daff2c8a3b..cb11c8c74ef3 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -1653,7 +1653,7 @@ marshal_dump_impl(PyObject *module, PyObject *value, PyObject *file, s = PyMarshal_WriteObjectToString(value, version); if (s == NULL) return NULL; - res = _PyObject_CallMethodIdObjArgs(file, &PyId_write, s, NULL); + res = _PyObject_CallMethodIdOneArg(file, &PyId_write, s); Py_DECREF(s); return res; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index dc198a5d1e05..103a11152412 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -581,7 +581,7 @@ sys_displayhook_unencodable(PyThreadState *tstate, PyObject *outf, PyObject *o) buffer = _PyObject_GetAttrId(outf, &PyId_buffer); if (buffer) { - result = _PyObject_CallMethodIdObjArgs(buffer, &PyId_write, encoded, NULL); + result = _PyObject_CallMethodIdOneArg(buffer, &PyId_write, encoded); Py_DECREF(buffer); Py_DECREF(encoded); if (result == NULL) @@ -3114,9 +3114,7 @@ sys_pyfile_write_unicode(PyObject *unicode, PyObject *file) if (file == NULL) return -1; assert(unicode != NULL); - PyObject *margs[2] = {file, unicode}; - PyObject *result = _PyObject_VectorcallMethodId(&PyId_write, margs, - 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + PyObject *result = _PyObject_CallMethodIdOneArg(file, &PyId_write, unicode); if (result == NULL) { return -1; } From webhook-mailer at python.org Thu Jul 11 10:01:02 2019 From: webhook-mailer at python.org (Tal Einat) Date: Thu, 11 Jul 2019 14:01:02 -0000 Subject: [Python-checkins] bpo-34369: make kqueue.control() docs better reflect that timeout is positional-only (GH-9499) Message-ID: https://github.com/python/cpython/commit/79042ac4348ccc09344014f20dd49401579f8795 commit: 79042ac4348ccc09344014f20dd49401579f8795 branch: master author: Tal Einat committer: GitHub date: 2019-07-11T17:00:34+03:00 summary: bpo-34369: make kqueue.control() docs better reflect that timeout is positional-only (GH-9499) files: M Doc/library/select.rst diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 733a91e20b68..8f5a2cea9257 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -480,13 +480,14 @@ Kqueue Objects Create a kqueue object from a given file descriptor. -.. method:: kqueue.control(changelist, max_events[, timeout=None]) -> eventlist +.. method:: kqueue.control(changelist, max_events[, timeout]) -> eventlist Low level interface to kevent - - changelist must be an iterable of kevent object or ``None`` + - changelist must be an iterable of kevent objects or ``None`` - max_events must be 0 or a positive integer - - timeout in seconds (floats possible) + - timeout in seconds (floats possible); the default is ``None``, + to wait forever .. versionchanged:: 3.5 The function is now retried with a recomputed timeout when interrupted by From webhook-mailer at python.org Thu Jul 11 10:16:10 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 14:16:10 -0000 Subject: [Python-checkins] bpo-34369: make kqueue.control() docs better reflect that timeout is positional-only (GH-9499) Message-ID: https://github.com/python/cpython/commit/dc0b6af42eca70e520b67d0bcf4dc5278a3f02dd commit: dc0b6af42eca70e520b67d0bcf4dc5278a3f02dd branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-11T07:16:03-07:00 summary: bpo-34369: make kqueue.control() docs better reflect that timeout is positional-only (GH-9499) (cherry picked from commit 79042ac4348ccc09344014f20dd49401579f8795) Co-authored-by: Tal Einat files: M Doc/library/select.rst diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 733a91e20b68..8f5a2cea9257 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -480,13 +480,14 @@ Kqueue Objects Create a kqueue object from a given file descriptor. -.. method:: kqueue.control(changelist, max_events[, timeout=None]) -> eventlist +.. method:: kqueue.control(changelist, max_events[, timeout]) -> eventlist Low level interface to kevent - - changelist must be an iterable of kevent object or ``None`` + - changelist must be an iterable of kevent objects or ``None`` - max_events must be 0 or a positive integer - - timeout in seconds (floats possible) + - timeout in seconds (floats possible); the default is ``None``, + to wait forever .. versionchanged:: 3.5 The function is now retried with a recomputed timeout when interrupted by From webhook-mailer at python.org Thu Jul 11 10:16:49 2019 From: webhook-mailer at python.org (Tal Einat) Date: Thu, 11 Jul 2019 14:16:49 -0000 Subject: [Python-checkins] bpo-34369: make kqueue.control() docs better reflect that timeout is positional-only (GH-9499) Message-ID: https://github.com/python/cpython/commit/d3747fd8fa91b768b73b60f2e2a14044e5404afa commit: d3747fd8fa91b768b73b60f2e2a14044e5404afa branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Tal Einat date: 2019-07-11T17:16:45+03:00 summary: bpo-34369: make kqueue.control() docs better reflect that timeout is positional-only (GH-9499) (cherry picked from commit 79042ac4348ccc09344014f20dd49401579f8795) Co-authored-by: Tal Einat files: M Doc/library/select.rst diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 5b8fd7ee805c..7d65363e4931 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -480,13 +480,14 @@ Kqueue Objects Create a kqueue object from a given file descriptor. -.. method:: kqueue.control(changelist, max_events[, timeout=None]) -> eventlist +.. method:: kqueue.control(changelist, max_events[, timeout]) -> eventlist Low level interface to kevent - - changelist must be an iterable of kevent object or ``None`` + - changelist must be an iterable of kevent objects or ``None`` - max_events must be 0 or a positive integer - - timeout in seconds (floats possible) + - timeout in seconds (floats possible); the default is ``None``, + to wait forever .. versionchanged:: 3.5 The function is now retried with a recomputed timeout when interrupted by From webhook-mailer at python.org Thu Jul 11 10:17:12 2019 From: webhook-mailer at python.org (Tal Einat) Date: Thu, 11 Jul 2019 14:17:12 -0000 Subject: [Python-checkins] bpo-34369: make kqueue.control() docs better reflect that timeout is positional-only (GH-9499) Message-ID: https://github.com/python/cpython/commit/46c2eff5adca7dc309f077ec65faf95b95c47b43 commit: 46c2eff5adca7dc309f077ec65faf95b95c47b43 branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Tal Einat date: 2019-07-11T17:17:08+03:00 summary: bpo-34369: make kqueue.control() docs better reflect that timeout is positional-only (GH-9499) (cherry picked from commit 79042ac4348ccc09344014f20dd49401579f8795) Co-authored-by: Tal Einat files: M Doc/library/select.rst diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 7a080457a05e..b68b32486ad8 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -291,13 +291,14 @@ Kqueue Objects Create a kqueue object from a given file descriptor. -.. method:: kqueue.control(changelist, max_events[, timeout=None]) -> eventlist +.. method:: kqueue.control(changelist, max_events[, timeout]) -> eventlist Low level interface to kevent - - changelist must be an iterable of kevent object or ``None`` + - changelist must be an iterable of kevent objects or ``None`` - max_events must be 0 or a positive integer - - timeout in seconds (floats possible) + - timeout in seconds (floats possible); the default is ``None``, + to wait forever .. _kevent-objects: From webhook-mailer at python.org Thu Jul 11 10:20:21 2019 From: webhook-mailer at python.org (Tal Einat) Date: Thu, 11 Jul 2019 14:20:21 -0000 Subject: [Python-checkins] bpo-36390: simplify classifyws(), rename it and add unit tests (GH-14500) Message-ID: https://github.com/python/cpython/commit/9b5ce62cac27fec9dea473865d79c2c654312957 commit: 9b5ce62cac27fec9dea473865d79c2c654312957 branch: master author: Tal Einat committer: GitHub date: 2019-07-11T17:20:14+03:00 summary: bpo-36390: simplify classifyws(), rename it and add unit tests (GH-14500) files: M Lib/idlelib/editor.py M Lib/idlelib/idle_test/test_editor.py diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 606de71a6add..9b5364f0c774 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -1281,7 +1281,7 @@ def smart_indent_event(self, event): text.delete(first, last) text.mark_set("insert", first) prefix = text.get("insert linestart", "insert") - raw, effective = classifyws(prefix, self.tabwidth) + raw, effective = get_line_indent(prefix, self.tabwidth) if raw == len(prefix): # only whitespace to the left self.reindent_to(effective + self.indentwidth) @@ -1415,7 +1415,7 @@ def indent_region_event(self, event): for pos in range(len(lines)): line = lines[pos] if line: - raw, effective = classifyws(line, self.tabwidth) + raw, effective = get_line_indent(line, self.tabwidth) effective = effective + self.indentwidth lines[pos] = self._make_blanks(effective) + line[raw:] self.set_region(head, tail, chars, lines) @@ -1426,7 +1426,7 @@ def dedent_region_event(self, event): for pos in range(len(lines)): line = lines[pos] if line: - raw, effective = classifyws(line, self.tabwidth) + raw, effective = get_line_indent(line, self.tabwidth) effective = max(effective - self.indentwidth, 0) lines[pos] = self._make_blanks(effective) + line[raw:] self.set_region(head, tail, chars, lines) @@ -1461,7 +1461,7 @@ def tabify_region_event(self, event): for pos in range(len(lines)): line = lines[pos] if line: - raw, effective = classifyws(line, tabwidth) + raw, effective = get_line_indent(line, tabwidth) ntabs, nspaces = divmod(effective, tabwidth) lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] self.set_region(head, tail, chars, lines) @@ -1575,8 +1575,8 @@ def _asktabwidth(self): def guess_indent(self): opener, indented = IndentSearcher(self.text, self.tabwidth).run() if opener and indented: - raw, indentsmall = classifyws(opener, self.tabwidth) - raw, indentlarge = classifyws(indented, self.tabwidth) + raw, indentsmall = get_line_indent(opener, self.tabwidth) + raw, indentlarge = get_line_indent(indented, self.tabwidth) else: indentsmall = indentlarge = 0 return indentlarge - indentsmall @@ -1585,23 +1585,16 @@ def guess_indent(self): def index2line(index): return int(float(index)) -# Look at the leading whitespace in s. -# Return pair (# of leading ws characters, -# effective # of leading blanks after expanding -# tabs to width tabwidth) - -def classifyws(s, tabwidth): - raw = effective = 0 - for ch in s: - if ch == ' ': - raw = raw + 1 - effective = effective + 1 - elif ch == '\t': - raw = raw + 1 - effective = (effective // tabwidth + 1) * tabwidth - else: - break - return raw, effective + +_line_indent_re = re.compile(r'[ \t]*') +def get_line_indent(line, tabwidth): + """Return a line's indentation as (# chars, effective # of spaces). + + The effective # of spaces is the length after properly "expanding" + the tabs into spaces, as done by str.expandtabs(tabwidth). + """ + m = _line_indent_re.match(line) + return m.end(), len(m.group().expandtabs(tabwidth)) class IndentSearcher(object): diff --git a/Lib/idlelib/idle_test/test_editor.py b/Lib/idlelib/idle_test/test_editor.py index 12bc84736683..4af4ff0242d7 100644 --- a/Lib/idlelib/idle_test/test_editor.py +++ b/Lib/idlelib/idle_test/test_editor.py @@ -42,5 +42,66 @@ class dummy(): self.assertEqual(func(dummy, inp), out) +class TestGetLineIndent(unittest.TestCase): + def test_empty_lines(self): + for tabwidth in [1, 2, 4, 6, 8]: + for line in ['', '\n']: + with self.subTest(line=line, tabwidth=tabwidth): + self.assertEqual( + editor.get_line_indent(line, tabwidth=tabwidth), + (0, 0), + ) + + def test_tabwidth_4(self): + # (line, (raw, effective)) + tests = (('no spaces', (0, 0)), + # Internal space isn't counted. + (' space test', (4, 4)), + ('\ttab test', (1, 4)), + ('\t\tdouble tabs test', (2, 8)), + # Different results when mixing tabs and spaces. + (' \tmixed test', (5, 8)), + (' \t mixed test', (5, 6)), + ('\t mixed test', (5, 8)), + # Spaces not divisible by tabwidth. + (' \tmixed test', (3, 4)), + (' \t mixed test', (3, 5)), + ('\t mixed test', (3, 6)), + # Only checks spaces and tabs. + ('\nnewline test', (0, 0))) + + for line, expected in tests: + with self.subTest(line=line): + self.assertEqual( + editor.get_line_indent(line, tabwidth=4), + expected, + ) + + def test_tabwidth_8(self): + # (line, (raw, effective)) + tests = (('no spaces', (0, 0)), + # Internal space isn't counted. + (' space test', (8, 8)), + ('\ttab test', (1, 8)), + ('\t\tdouble tabs test', (2, 16)), + # Different results when mixing tabs and spaces. + (' \tmixed test', (9, 16)), + (' \t mixed test', (9, 10)), + ('\t mixed test', (9, 16)), + # Spaces not divisible by tabwidth. + (' \tmixed test', (3, 8)), + (' \t mixed test', (3, 9)), + ('\t mixed test', (3, 10)), + # Only checks spaces and tabs. + ('\nnewline test', (0, 0))) + + for line, expected in tests: + with self.subTest(line=line): + self.assertEqual( + editor.get_line_indent(line, tabwidth=8), + expected, + ) + + if __name__ == '__main__': unittest.main(verbosity=2) From webhook-mailer at python.org Thu Jul 11 10:55:27 2019 From: webhook-mailer at python.org (Tal Einat) Date: Thu, 11 Jul 2019 14:55:27 -0000 Subject: [Python-checkins] bpo-36390: simplify classifyws(), rename it and add unit tests (GH-14500) Message-ID: https://github.com/python/cpython/commit/242ad1f375bf564c35cf99cd754b2c5819c4d056 commit: 242ad1f375bf564c35cf99cd754b2c5819c4d056 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Tal Einat date: 2019-07-11T17:55:05+03:00 summary: bpo-36390: simplify classifyws(), rename it and add unit tests (GH-14500) (cherry picked from commit 9b5ce62cac27fec9dea473865d79c2c654312957) Co-authored-by: Tal Einat files: M Lib/idlelib/editor.py M Lib/idlelib/idle_test/test_editor.py diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 606de71a6add..9b5364f0c774 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -1281,7 +1281,7 @@ def smart_indent_event(self, event): text.delete(first, last) text.mark_set("insert", first) prefix = text.get("insert linestart", "insert") - raw, effective = classifyws(prefix, self.tabwidth) + raw, effective = get_line_indent(prefix, self.tabwidth) if raw == len(prefix): # only whitespace to the left self.reindent_to(effective + self.indentwidth) @@ -1415,7 +1415,7 @@ def indent_region_event(self, event): for pos in range(len(lines)): line = lines[pos] if line: - raw, effective = classifyws(line, self.tabwidth) + raw, effective = get_line_indent(line, self.tabwidth) effective = effective + self.indentwidth lines[pos] = self._make_blanks(effective) + line[raw:] self.set_region(head, tail, chars, lines) @@ -1426,7 +1426,7 @@ def dedent_region_event(self, event): for pos in range(len(lines)): line = lines[pos] if line: - raw, effective = classifyws(line, self.tabwidth) + raw, effective = get_line_indent(line, self.tabwidth) effective = max(effective - self.indentwidth, 0) lines[pos] = self._make_blanks(effective) + line[raw:] self.set_region(head, tail, chars, lines) @@ -1461,7 +1461,7 @@ def tabify_region_event(self, event): for pos in range(len(lines)): line = lines[pos] if line: - raw, effective = classifyws(line, tabwidth) + raw, effective = get_line_indent(line, tabwidth) ntabs, nspaces = divmod(effective, tabwidth) lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] self.set_region(head, tail, chars, lines) @@ -1575,8 +1575,8 @@ def _asktabwidth(self): def guess_indent(self): opener, indented = IndentSearcher(self.text, self.tabwidth).run() if opener and indented: - raw, indentsmall = classifyws(opener, self.tabwidth) - raw, indentlarge = classifyws(indented, self.tabwidth) + raw, indentsmall = get_line_indent(opener, self.tabwidth) + raw, indentlarge = get_line_indent(indented, self.tabwidth) else: indentsmall = indentlarge = 0 return indentlarge - indentsmall @@ -1585,23 +1585,16 @@ def guess_indent(self): def index2line(index): return int(float(index)) -# Look at the leading whitespace in s. -# Return pair (# of leading ws characters, -# effective # of leading blanks after expanding -# tabs to width tabwidth) - -def classifyws(s, tabwidth): - raw = effective = 0 - for ch in s: - if ch == ' ': - raw = raw + 1 - effective = effective + 1 - elif ch == '\t': - raw = raw + 1 - effective = (effective // tabwidth + 1) * tabwidth - else: - break - return raw, effective + +_line_indent_re = re.compile(r'[ \t]*') +def get_line_indent(line, tabwidth): + """Return a line's indentation as (# chars, effective # of spaces). + + The effective # of spaces is the length after properly "expanding" + the tabs into spaces, as done by str.expandtabs(tabwidth). + """ + m = _line_indent_re.match(line) + return m.end(), len(m.group().expandtabs(tabwidth)) class IndentSearcher(object): diff --git a/Lib/idlelib/idle_test/test_editor.py b/Lib/idlelib/idle_test/test_editor.py index 12bc84736683..4af4ff0242d7 100644 --- a/Lib/idlelib/idle_test/test_editor.py +++ b/Lib/idlelib/idle_test/test_editor.py @@ -42,5 +42,66 @@ class dummy(): self.assertEqual(func(dummy, inp), out) +class TestGetLineIndent(unittest.TestCase): + def test_empty_lines(self): + for tabwidth in [1, 2, 4, 6, 8]: + for line in ['', '\n']: + with self.subTest(line=line, tabwidth=tabwidth): + self.assertEqual( + editor.get_line_indent(line, tabwidth=tabwidth), + (0, 0), + ) + + def test_tabwidth_4(self): + # (line, (raw, effective)) + tests = (('no spaces', (0, 0)), + # Internal space isn't counted. + (' space test', (4, 4)), + ('\ttab test', (1, 4)), + ('\t\tdouble tabs test', (2, 8)), + # Different results when mixing tabs and spaces. + (' \tmixed test', (5, 8)), + (' \t mixed test', (5, 6)), + ('\t mixed test', (5, 8)), + # Spaces not divisible by tabwidth. + (' \tmixed test', (3, 4)), + (' \t mixed test', (3, 5)), + ('\t mixed test', (3, 6)), + # Only checks spaces and tabs. + ('\nnewline test', (0, 0))) + + for line, expected in tests: + with self.subTest(line=line): + self.assertEqual( + editor.get_line_indent(line, tabwidth=4), + expected, + ) + + def test_tabwidth_8(self): + # (line, (raw, effective)) + tests = (('no spaces', (0, 0)), + # Internal space isn't counted. + (' space test', (8, 8)), + ('\ttab test', (1, 8)), + ('\t\tdouble tabs test', (2, 16)), + # Different results when mixing tabs and spaces. + (' \tmixed test', (9, 16)), + (' \t mixed test', (9, 10)), + ('\t mixed test', (9, 16)), + # Spaces not divisible by tabwidth. + (' \tmixed test', (3, 8)), + (' \t mixed test', (3, 9)), + ('\t mixed test', (3, 10)), + # Only checks spaces and tabs. + ('\nnewline test', (0, 0))) + + for line, expected in tests: + with self.subTest(line=line): + self.assertEqual( + editor.get_line_indent(line, tabwidth=8), + expected, + ) + + if __name__ == '__main__': unittest.main(verbosity=2) From webhook-mailer at python.org Thu Jul 11 10:58:11 2019 From: webhook-mailer at python.org (Tal Einat) Date: Thu, 11 Jul 2019 14:58:11 -0000 Subject: [Python-checkins] bpo-36390: simplify classifyws(), rename it and add unit tests (GH-14500) Message-ID: https://github.com/python/cpython/commit/a2cf88efc417f1991720856f6913d16660e48941 commit: a2cf88efc417f1991720856f6913d16660e48941 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Tal Einat date: 2019-07-11T17:57:45+03:00 summary: bpo-36390: simplify classifyws(), rename it and add unit tests (GH-14500) (cherry picked from commit 9b5ce62cac27fec9dea473865d79c2c654312957) Co-authored-by: Tal Einat files: M Lib/idlelib/editor.py M Lib/idlelib/idle_test/test_editor.py diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 606de71a6add..9b5364f0c774 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -1281,7 +1281,7 @@ def smart_indent_event(self, event): text.delete(first, last) text.mark_set("insert", first) prefix = text.get("insert linestart", "insert") - raw, effective = classifyws(prefix, self.tabwidth) + raw, effective = get_line_indent(prefix, self.tabwidth) if raw == len(prefix): # only whitespace to the left self.reindent_to(effective + self.indentwidth) @@ -1415,7 +1415,7 @@ def indent_region_event(self, event): for pos in range(len(lines)): line = lines[pos] if line: - raw, effective = classifyws(line, self.tabwidth) + raw, effective = get_line_indent(line, self.tabwidth) effective = effective + self.indentwidth lines[pos] = self._make_blanks(effective) + line[raw:] self.set_region(head, tail, chars, lines) @@ -1426,7 +1426,7 @@ def dedent_region_event(self, event): for pos in range(len(lines)): line = lines[pos] if line: - raw, effective = classifyws(line, self.tabwidth) + raw, effective = get_line_indent(line, self.tabwidth) effective = max(effective - self.indentwidth, 0) lines[pos] = self._make_blanks(effective) + line[raw:] self.set_region(head, tail, chars, lines) @@ -1461,7 +1461,7 @@ def tabify_region_event(self, event): for pos in range(len(lines)): line = lines[pos] if line: - raw, effective = classifyws(line, tabwidth) + raw, effective = get_line_indent(line, tabwidth) ntabs, nspaces = divmod(effective, tabwidth) lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] self.set_region(head, tail, chars, lines) @@ -1575,8 +1575,8 @@ def _asktabwidth(self): def guess_indent(self): opener, indented = IndentSearcher(self.text, self.tabwidth).run() if opener and indented: - raw, indentsmall = classifyws(opener, self.tabwidth) - raw, indentlarge = classifyws(indented, self.tabwidth) + raw, indentsmall = get_line_indent(opener, self.tabwidth) + raw, indentlarge = get_line_indent(indented, self.tabwidth) else: indentsmall = indentlarge = 0 return indentlarge - indentsmall @@ -1585,23 +1585,16 @@ def guess_indent(self): def index2line(index): return int(float(index)) -# Look at the leading whitespace in s. -# Return pair (# of leading ws characters, -# effective # of leading blanks after expanding -# tabs to width tabwidth) - -def classifyws(s, tabwidth): - raw = effective = 0 - for ch in s: - if ch == ' ': - raw = raw + 1 - effective = effective + 1 - elif ch == '\t': - raw = raw + 1 - effective = (effective // tabwidth + 1) * tabwidth - else: - break - return raw, effective + +_line_indent_re = re.compile(r'[ \t]*') +def get_line_indent(line, tabwidth): + """Return a line's indentation as (# chars, effective # of spaces). + + The effective # of spaces is the length after properly "expanding" + the tabs into spaces, as done by str.expandtabs(tabwidth). + """ + m = _line_indent_re.match(line) + return m.end(), len(m.group().expandtabs(tabwidth)) class IndentSearcher(object): diff --git a/Lib/idlelib/idle_test/test_editor.py b/Lib/idlelib/idle_test/test_editor.py index 12bc84736683..4af4ff0242d7 100644 --- a/Lib/idlelib/idle_test/test_editor.py +++ b/Lib/idlelib/idle_test/test_editor.py @@ -42,5 +42,66 @@ class dummy(): self.assertEqual(func(dummy, inp), out) +class TestGetLineIndent(unittest.TestCase): + def test_empty_lines(self): + for tabwidth in [1, 2, 4, 6, 8]: + for line in ['', '\n']: + with self.subTest(line=line, tabwidth=tabwidth): + self.assertEqual( + editor.get_line_indent(line, tabwidth=tabwidth), + (0, 0), + ) + + def test_tabwidth_4(self): + # (line, (raw, effective)) + tests = (('no spaces', (0, 0)), + # Internal space isn't counted. + (' space test', (4, 4)), + ('\ttab test', (1, 4)), + ('\t\tdouble tabs test', (2, 8)), + # Different results when mixing tabs and spaces. + (' \tmixed test', (5, 8)), + (' \t mixed test', (5, 6)), + ('\t mixed test', (5, 8)), + # Spaces not divisible by tabwidth. + (' \tmixed test', (3, 4)), + (' \t mixed test', (3, 5)), + ('\t mixed test', (3, 6)), + # Only checks spaces and tabs. + ('\nnewline test', (0, 0))) + + for line, expected in tests: + with self.subTest(line=line): + self.assertEqual( + editor.get_line_indent(line, tabwidth=4), + expected, + ) + + def test_tabwidth_8(self): + # (line, (raw, effective)) + tests = (('no spaces', (0, 0)), + # Internal space isn't counted. + (' space test', (8, 8)), + ('\ttab test', (1, 8)), + ('\t\tdouble tabs test', (2, 16)), + # Different results when mixing tabs and spaces. + (' \tmixed test', (9, 16)), + (' \t mixed test', (9, 10)), + ('\t mixed test', (9, 16)), + # Spaces not divisible by tabwidth. + (' \tmixed test', (3, 8)), + (' \t mixed test', (3, 9)), + ('\t mixed test', (3, 10)), + # Only checks spaces and tabs. + ('\nnewline test', (0, 0))) + + for line, expected in tests: + with self.subTest(line=line): + self.assertEqual( + editor.get_line_indent(line, tabwidth=8), + expected, + ) + + if __name__ == '__main__': unittest.main(verbosity=2) From webhook-mailer at python.org Thu Jul 11 11:58:01 2019 From: webhook-mailer at python.org (Inada Naoki) Date: Thu, 11 Jul 2019 15:58:01 -0000 Subject: [Python-checkins] bpo-29548: no longer use PyEval_Call* functions (GH-14683) Message-ID: https://github.com/python/cpython/commit/1dbd084f1f68d7293718b663df675cfbd0c65712 commit: 1dbd084f1f68d7293718b663df675cfbd0c65712 branch: master author: Jeroen Demeyer committer: Inada Naoki date: 2019-07-12T00:57:32+09:00 summary: bpo-29548: no longer use PyEval_Call* functions (GH-14683) files: M Modules/pyexpat.c M Modules/signalmodule.c M Objects/call.c M Python/codecs.c diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 3d193e717c88..b0096e664780 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -208,7 +208,7 @@ call_with_frame(const char *funcname, int lineno, PyObject* func, PyObject* args { PyObject *res; - res = PyEval_CallObject(func, args); + res = PyObject_Call(func, args, NULL); if (res == NULL) { _PyTraceback_Add(funcname, __FILE__, lineno); XML_StopParser(self->itself, XML_FALSE); diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 7698984ff3af..95569b931d60 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1667,8 +1667,7 @@ _PyErr_CheckSignals(void) _Py_atomic_store_relaxed(&Handlers[i].tripped, 0); if (arglist) { - result = PyEval_CallObject(Handlers[i].func, - arglist); + result = PyObject_Call(Handlers[i].func, arglist, NULL); Py_DECREF(arglist); } if (!result) { diff --git a/Objects/call.c b/Objects/call.c index df90595d6c6a..7d917891bc0c 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -457,7 +457,16 @@ PyEval_CallObjectWithKeywords(PyObject *callable, PyObject * PyObject_CallObject(PyObject *callable, PyObject *args) { - return PyEval_CallObjectWithKeywords(callable, args, NULL); + assert(!PyErr_Occurred()); + if (args == NULL) { + return _PyObject_CallNoArg(callable); + } + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_TypeError, + "argument list must be a tuple"); + return NULL; + } + return PyObject_Call(callable, args, NULL); } diff --git a/Python/codecs.c b/Python/codecs.c index 75b60ec6bd77..4f38b33e0b76 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -416,7 +416,7 @@ _PyCodec_EncodeInternal(PyObject *object, if (args == NULL) goto onError; - result = PyEval_CallObject(encoder, args); + result = PyObject_Call(encoder, args, NULL); if (result == NULL) { wrap_codec_error("encoding", encoding); goto onError; @@ -462,7 +462,7 @@ _PyCodec_DecodeInternal(PyObject *object, if (args == NULL) goto onError; - result = PyEval_CallObject(decoder,args); + result = PyObject_Call(decoder, args, NULL); if (result == NULL) { wrap_codec_error("decoding", encoding); goto onError; From webhook-mailer at python.org Thu Jul 11 13:31:54 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 17:31:54 -0000 Subject: [Python-checkins] closes bpo-37554: Remove `q:q` in os.rst documentation (GH-14692) Message-ID: https://github.com/python/cpython/commit/7cbef72902f32866a416ca6c4e732af4541951b8 commit: 7cbef72902f32866a416ca6c4e732af4541951b8 branch: master author: Mariatta committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-11T10:31:19-07:00 summary: closes bpo-37554: Remove `q:q` in os.rst documentation (GH-14692) https://bugs.python.org/issue37554 files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 519d5581603b..c74d687f08fb 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2098,7 +2098,7 @@ features: On Windows, if *dst* exists a :exc:`FileExistsError` is always raised. - On Unix, if *src* is a file and *dst* is a directory or vice-versa, anq:q + On Unix, if *src* is a file and *dst* is a directory or vice-versa, an :exc:`IsADirectoryError` or a :exc:`NotADirectoryError` will be raised respectively. If both are directories and *dst* is empty, *dst* will be silently replaced. If *dst* is a non-empty directory, an :exc:`OSError` From webhook-mailer at python.org Thu Jul 11 13:45:41 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 17:45:41 -0000 Subject: [Python-checkins] closes bpo-37554: Remove `q:q` in os.rst documentation (GH-14692) Message-ID: https://github.com/python/cpython/commit/107171500d7d6e60f463eeb4db492c0ae292a669 commit: 107171500d7d6e60f463eeb4db492c0ae292a669 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-11T10:45:36-07:00 summary: closes bpo-37554: Remove `q:q` in os.rst documentation (GH-14692) https://bugs.python.org/issue37554 (cherry picked from commit 7cbef72902f32866a416ca6c4e732af4541951b8) Co-authored-by: Mariatta files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 519d5581603b..c74d687f08fb 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2098,7 +2098,7 @@ features: On Windows, if *dst* exists a :exc:`FileExistsError` is always raised. - On Unix, if *src* is a file and *dst* is a directory or vice-versa, anq:q + On Unix, if *src* is a file and *dst* is a directory or vice-versa, an :exc:`IsADirectoryError` or a :exc:`NotADirectoryError` will be raised respectively. If both are directories and *dst* is empty, *dst* will be silently replaced. If *dst* is a non-empty directory, an :exc:`OSError` From webhook-mailer at python.org Thu Jul 11 13:48:06 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 17:48:06 -0000 Subject: [Python-checkins] closes bpo-37554: Remove `q:q` in os.rst documentation (GH-14692) Message-ID: https://github.com/python/cpython/commit/71435f685c0423f878946e584f4b9eb01233d332 commit: 71435f685c0423f878946e584f4b9eb01233d332 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-11T10:48:00-07:00 summary: closes bpo-37554: Remove `q:q` in os.rst documentation (GH-14692) https://bugs.python.org/issue37554 (cherry picked from commit 7cbef72902f32866a416ca6c4e732af4541951b8) Co-authored-by: Mariatta files: M Doc/library/os.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 24f14480ae04..5572b62420fe 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2056,7 +2056,7 @@ features: On Windows, if *dst* exists a :exc:`FileExistsError` is always raised. - On Unix, if *src* is a file and *dst* is a directory or vice-versa, anq:q + On Unix, if *src* is a file and *dst* is a directory or vice-versa, an :exc:`IsADirectoryError` or a :exc:`NotADirectoryError` will be raised respectively. If both are directories and *dst* is empty, *dst* will be silently replaced. If *dst* is a non-empty directory, an :exc:`OSError` From webhook-mailer at python.org Thu Jul 11 14:04:19 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 18:04:19 -0000 Subject: [Python-checkins] bpo-37558: Shared memory tests are failing due to double slashes (GH-14703) Message-ID: https://github.com/python/cpython/commit/4737265622251756a9480ab84af2442b6b986850 commit: 4737265622251756a9480ab84af2442b6b986850 branch: master author: Jakub Kul?k committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-11T11:04:09-07:00 summary: bpo-37558: Shared memory tests are failing due to double slashes (GH-14703) With the addition of shared memory into Python 3.8, we now have three tests failing on Solaris, namely `test_multiprocessing_fork`, `test_multiprocessing_forkserver` and `test_multiprocessing_spawn`. The reason seems to be incorrect name handling which results in two slashes being prepended. https://bugs.python.org/issue37558 files: A Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst M Lib/test/_test_multiprocessing.py diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 9a39f385952f..2fe0def2bcd2 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -3990,7 +3990,7 @@ def test_shared_memory_cleaned_after_process_termination(self): # Create a shared_memory segment, and send the segment name sm = shared_memory.SharedMemory(create=True, size=10) - sys.stdout.write(sm._name + '\\n') + sys.stdout.write(sm.name + '\\n') sys.stdout.flush() time.sleep(100) ''' diff --git a/Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst b/Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst new file mode 100644 index 000000000000..9f393d71a3f2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst @@ -0,0 +1 @@ +Fix test_shared_memory_cleaned_after_process_termination name handling From webhook-mailer at python.org Thu Jul 11 14:38:59 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 18:38:59 -0000 Subject: [Python-checkins] bpo-37558: Shared memory tests are failing due to double slashes (GH-14703) Message-ID: https://github.com/python/cpython/commit/3d58b78481e0238593f85cc182b798fe3b77648c commit: 3d58b78481e0238593f85cc182b798fe3b77648c branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-11T11:38:37-07:00 summary: bpo-37558: Shared memory tests are failing due to double slashes (GH-14703) With the addition of shared memory into Python 3.8, we now have three tests failing on Solaris, namely `test_multiprocessing_fork`, `test_multiprocessing_forkserver` and `test_multiprocessing_spawn`. The reason seems to be incorrect name handling which results in two slashes being prepended. https://bugs.python.org/issue37558 (cherry picked from commit 4737265622251756a9480ab84af2442b6b986850) Co-authored-by: Jakub Kul?k files: A Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst M Lib/test/_test_multiprocessing.py diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 9a39f385952f..2fe0def2bcd2 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -3990,7 +3990,7 @@ def test_shared_memory_cleaned_after_process_termination(self): # Create a shared_memory segment, and send the segment name sm = shared_memory.SharedMemory(create=True, size=10) - sys.stdout.write(sm._name + '\\n') + sys.stdout.write(sm.name + '\\n') sys.stdout.flush() time.sleep(100) ''' diff --git a/Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst b/Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst new file mode 100644 index 000000000000..9f393d71a3f2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst @@ -0,0 +1 @@ +Fix test_shared_memory_cleaned_after_process_termination name handling From webhook-mailer at python.org Thu Jul 11 17:57:54 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 11 Jul 2019 21:57:54 -0000 Subject: [Python-checkins] Remove redundant check from arraymodule b_getitem (GH-14676) Message-ID: https://github.com/python/cpython/commit/13ab570febac64bb55fb46d866a9ba5a46ab8902 commit: 13ab570febac64bb55fb46d866a9ba5a46ab8902 branch: master author: Disconnect3d committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-11T14:57:42-07:00 summary: Remove redundant check from arraymodule b_getitem (GH-14676) The `arraymodule`'s `b_getitem` function returns a `PyLong` converted from `arrayobject`'s array, by dereferencing a pointer to `char`. When the `char` type is `signed char`, the `if (x >= 128) x -= 256;` comparison/code is redundant because a `signed char` will have a value of `[-128, 127]` and so `x` will never be greater or equal than 128. This check was indeed needed for situations where a given compiler would assume `char` being `unsigned char` which would make `x` in `[0, 256]` range. However, the check can be removed if we cast the `ob_item` into a signed char pointer (`signed char*`) instead of `char*`. This PR/commit introduces this change. files: M Modules/arraymodule.c diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 48986c419029..6aa981daca1d 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -185,9 +185,7 @@ in bounds; that's the responsibility of the caller. static PyObject * b_getitem(arrayobject *ap, Py_ssize_t i) { - long x = ((char *)ap->ob_item)[i]; - if (x >= 128) - x -= 256; + long x = ((signed char *)ap->ob_item)[i]; return PyLong_FromLong(x); } From webhook-mailer at python.org Thu Jul 11 22:18:16 2019 From: webhook-mailer at python.org (Benjamin Peterson) Date: Fri, 12 Jul 2019 02:18:16 -0000 Subject: [Python-checkins] closes bpo-37566: Remove _realsocket from socket.py. (GH-14711) Message-ID: https://github.com/python/cpython/commit/c8e7146de257930ea8d0d4aa74b3a64fcaa79d4b commit: c8e7146de257930ea8d0d4aa74b3a64fcaa79d4b branch: master author: Hai Shi committer: Benjamin Peterson date: 2019-07-11T19:17:52-07:00 summary: closes bpo-37566: Remove _realsocket from socket.py. (GH-14711) files: M Lib/socket.py diff --git a/Lib/socket.py b/Lib/socket.py index 0dd8ec70e168..3016b03104d4 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -104,8 +104,6 @@ def _intenum_converter(value, enum_klass): except ValueError: return value -_realsocket = socket - # WSA error codes if sys.platform.lower().startswith("win"): errorTab = {} From webhook-mailer at python.org Mon Jul 1 07:29:19 2019 From: webhook-mailer at python.org (=?utf-8?q?=C5=81ukasz?= Langa) Date: Mon, 01 Jul 2019 11:29:19 -0000 Subject: [Python-checkins] (no subject) Message-ID: To: python-checkins at python.org Subject: bpo-37221: Add PyCode_NewWithPosOnlyArgs to be used internally and set PyCode_New as a compatibility wrapper (GH-13959) (#14505) Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 https://github.com/python/cpython/commit/cb083f7cdf604c1d9d264f387f9e8846bc95= 3eb3 commit: cb083f7cdf604c1d9d264f387f9e8846bc953eb3 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.co= m> committer: =C5=81ukasz Langa date: 2019-07-01T13:29:14+02:00 summary: bpo-37221: Add PyCode_NewWithPosOnlyArgs to be used internally and set PyCode= _New as a compatibility wrapper (GH-13959) (#14505) Add PyCode_NewEx to be used internally and set PyCode_New as a compatibility = wrapper (cherry picked from commit 4a2edc34a405150d0b23ecfdcb401e7cf59f4650) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst M Doc/c-api/code.rst M Doc/data/refcounts.dat M Doc/whatsnew/3.8.rst M Include/code.h M Objects/codeobject.c M Python/compile.c M Python/marshal.c diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 7353df56e7d3..3c4f66923da5 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -33,20 +33,21 @@ bound into a function. =20 Return the number of free variables in *co*. =20 -.. c:function:: PyCodeObject* PyCode_New(int argcount, int posonlyargcount, = int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, Py= Object *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyOb= ject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject= *lnotab) +.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, i= nt nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObj= ect *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObj= ect *filename, PyObject *name, int firstlineno, PyObject *lnotab) =20 - Return a new code object. If you need a dummy code object to - create a frame, use :c:func:`PyCode_NewEmpty` instead. Calling - :c:func:`PyCode_New` directly can bind you to a precise Python - version since the definition of the bytecode changes often. - - .. versionchanged:: 3.8 - An extra parameter is required (*posonlyargcount*) to support :PEP:`57= 0`. - The first parameter (*argcount*) now represents the total number of po= sitional arguments, - including positional-only. + Return a new code object. If you need a dummy code object to create a fr= ame, + use :c:func:`PyCode_NewEmpty` instead. Calling :c:func:`PyCode_New` dire= ctly + can bind you to a precise Python version since the definition of the byte= code + changes often. =20 .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount= ,kwonlyargcount,nlocals,stacksize,flags c.PyCode_New =20 +.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int po= sonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyO= bject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject = *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstl= ineno, PyObject *lnotab) + + Similar to :c:func:`PyCode_New`, but with an extra "posonlyargcount" for = positonal-only arguments. + + .. versionadded:: 3.8 + .. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const ch= ar *funcname, int firstlineno) =20 Return a new empty code object with the specified filename, diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index aca57a1dae9d..fda347eab102 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -234,9 +234,26 @@ PyCode_Check:PyObject*:co:0: PyCode_GetNumFree:int::: PyCode_GetNumFree:PyCodeObject*:co:0: =20 +PyCode_NewWithPosOnlyArgs:PyCodeObject*::+1: +PyCode_NewWithPosOnlyArgs:int:argcount:: +PyCode_NewWithPosOnlyArgs:int:posonlyargcount:: +PyCode_NewWithPosOnlyArgs:int:kwonlyargcount:: +PyCode_NewWithPosOnlyArgs:int:nlocals:: +PyCode_NewWithPosOnlyArgs:int:stacksize:: +PyCode_NewWithPosOnlyArgs:int:flags:: +PyCode_NewWithPosOnlyArgs:PyObject*:code:0: +PyCode_NewWithPosOnlyArgs:PyObject*:consts:0: +PyCode_NewWithPosOnlyArgs:PyObject*:names:0: +PyCode_NewWithPosOnlyArgs:PyObject*:varnames:0: +PyCode_NewWithPosOnlyArgs:PyObject*:freevars:0: +PyCode_NewWithPosOnlyArgs:PyObject*:cellvars:0: +PyCode_NewWithPosOnlyArgs:PyObject*:filename:0: +PyCode_NewWithPosOnlyArgs:PyObject*:name:0: +PyCode_NewWithPosOnlyArgs:int:firstlineno:: +PyCode_NewWithPosOnlyArgs:PyObject*:lnotab:0: + PyCode_New:PyCodeObject*::+1: PyCode_New:int:argcount:: -PyCode_New:int:posonlyargcount:: PyCode_New:int:kwonlyargcount:: PyCode_New:int:nlocals:: PyCode_New:int:stacksize:: diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 8c2f80ce24e0..f423765c8917 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -1042,6 +1042,11 @@ Build and C API Changes allocation or deallocation may need to be adjusted. (Contributed by Eddie Elizondo in :issue:`35810`.) =20 +* The new function :c:func:`PyCode_NewWithPosOnlyArgs` allows to create + code objects like :c:func:`PyCode_New`, but with an extra *posonlyargcount* + parameter for indicating the number of positional-only arguments. + (Contributed by Pablo Galindo in :issue:`37221`.) + =20 Deprecated =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D diff --git a/Include/code.h b/Include/code.h index b79d977394e0..3afddd20c80d 100644 --- a/Include/code.h +++ b/Include/code.h @@ -120,6 +120,11 @@ PyAPI_DATA(PyTypeObject) PyCode_Type; =20 /* Public interface */ PyAPI_FUNC(PyCodeObject *) PyCode_New( + int, int, int, int, int, PyObject *, PyObject *, + PyObject *, PyObject *, PyObject *, PyObject *, + PyObject *, PyObject *, int, PyObject *); + +PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs( int, int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *); diff --git a/Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst = b/Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst new file mode 100644 index 000000000000..0ea8b9b86788 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst=09 @@ -0,0 +1,3 @@ +The new function :c:func:`PyCode_NewWithPosOnlyArgs` allows to create +code objects like :c:func:`PyCode_New`, but with an extra *posonlyargcount* +parameter for indicating the number of positonal-only arguments. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 1333cc833e1e..39bf6fc6f228 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -102,14 +102,13 @@ intern_string_constants(PyObject *tuple) return modified; } =20 - PyCodeObject * -PyCode_New(int argcount, int posonlyargcount, int kwonlyargcount, - int nlocals, int stacksize, int flags, - PyObject *code, PyObject *consts, PyObject *names, - PyObject *varnames, PyObject *freevars, PyObject *cellvars, - PyObject *filename, PyObject *name, int firstlineno, - PyObject *lnotab) +PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargco= unt, + int nlocals, int stacksize, int flags, + PyObject *code, PyObject *consts, PyObject *names, + PyObject *varnames, PyObject *freevars, PyObject *= cellvars, + PyObject *filename, PyObject *name, int firstlinen= o, + PyObject *lnotab) { PyCodeObject *co; Py_ssize_t *cell2arg =3D NULL; @@ -243,6 +242,20 @@ PyCode_New(int argcount, int posonlyargcount, int kwonly= argcount, return co; } =20 +PyCodeObject * +PyCode_New(int argcount, int kwonlyargcount, + int nlocals, int stacksize, int flags, + PyObject *code, PyObject *consts, PyObject *names, + PyObject *varnames, PyObject *freevars, PyObject *cellvars, + PyObject *filename, PyObject *name, int firstlineno, + PyObject *lnotab) +{ + return PyCode_NewWithPosOnlyArgs(argcount, 0, kwonlyargcount, nlocals, + stacksize, flags, code, consts, names, + varnames, freevars, cellvars, filename, + name, firstlineno, lnotab); +} + int _PyCode_InitOpcache(PyCodeObject *co) { @@ -311,7 +324,8 @@ PyCode_NewEmpty(const char *filename, const char *funcnam= e, int firstlineno) if (filename_ob =3D=3D NULL) goto failed; =20 - result =3D PyCode_New(0, /* argcount */ + result =3D PyCode_NewWithPosOnlyArgs( + 0, /* argcount */ 0, /* posonlyargcount */ 0, /* kwonlyargcount */ 0, /* nlocals */ @@ -492,12 +506,14 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *= kw) if (ourcellvars =3D=3D NULL) goto cleanup; =20 - co =3D (PyObject *)PyCode_New(argcount, posonlyargcount, kwonlyargcount, - nlocals, stacksize, flags, - code, consts, ournames, ourvarnames, - ourfreevars, ourcellvars, filename, - name, firstlineno, lnotab); - cleanup: + co =3D (PyObject *)PyCode_NewWithPosOnlyArgs(argcount, posonlyargcount, + kwonlyargcount, + nlocals, stacksize, flags, + code, consts, ournames, + ourvarnames, ourfreevars, + ourcellvars, filename, + name, firstlineno, lnotab); + cleanup:=20 Py_XDECREF(ournames); Py_XDECREF(ourvarnames); Py_XDECREF(ourfreevars); @@ -625,7 +641,7 @@ code_replace_impl(PyCodeObject *self, int co_argcount, =20 #undef CHECK_INT_ARG =20 - return (PyObject *)PyCode_New( + return (PyObject *)PyCode_NewWithPosOnlyArgs( co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names, co_varnames, co_freevars, co_cellvars, co_filename, co_name, diff --git a/Python/compile.c b/Python/compile.c index 7bdf406079d3..9cce8aeb4e1f 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -5813,13 +5813,11 @@ makecode(struct compiler *c, struct assembler *a) if (maxdepth < 0) { goto error; } - co =3D PyCode_New(posonlyargcount+posorkeywordargcount, posonlyargcount, - kwonlyargcount, nlocals_int, maxdepth, flags, - bytecode, consts, names, varnames, - freevars, cellvars, - c->c_filename, c->u->u_name, - c->u->u_firstlineno, - a->a_lnotab); + co =3D PyCode_NewWithPosOnlyArgs(posonlyargcount+posorkeywordargcount, + posonlyargcount, kwonlyargcount, nlocals_= int,=20 + maxdepth, flags, bytecode, consts, names, + varnames, freevars, cellvars, c->c_filena= me, + c->u->u_name, c->u->u_firstlineno, a->a_l= notab); error: Py_XDECREF(consts); Py_XDECREF(names); diff --git a/Python/marshal.c b/Python/marshal.c index caaddfe9e44e..b2daff2c8a3b 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -1396,7 +1396,7 @@ r_object(RFILE *p) if (lnotab =3D=3D NULL) goto code_error; =20 - v =3D (PyObject *) PyCode_New( + v =3D (PyObject *) PyCode_NewWithPosOnlyArgs( argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames, From webhook-mailer at python.org Tue Jul 2 07:32:47 2019 From: webhook-mailer at python.org (=?utf-8?q?=C5=81ukasz?= Langa) Date: Tue, 02 Jul 2019 11:32:47 -0000 Subject: [Python-checkins] (no subject) Message-ID: To: python-checkins at python.org Subject: Stop using Argument Clinic for dict_pop (GH-13935) Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 https://github.com/python/cpython/commit/d4c664736e43284405100d98d516fa269a64= 7461 commit: d4c664736e43284405100d98d516fa269a647461 branch: 3.8 author: Inada Naoki committer: =C5=81ukasz Langa date: 2019-07-02T13:32:43+02:00 summary: Stop using Argument Clinic for dict_pop (GH-13935) files: M Objects/clinic/dictobject.c.h M Objects/dictobject.c diff --git a/Objects/clinic/dictobject.c.h b/Objects/clinic/dictobject.c.h index b87244d87348..8d5493330835 100644 --- a/Objects/clinic/dictobject.c.h +++ b/Objects/clinic/dictobject.c.h @@ -116,42 +116,6 @@ dict_setdefault(PyDictObject *self, PyObject *const *arg= s, Py_ssize_t nargs) return return_value; } =20 -PyDoc_STRVAR(dict_pop__doc__, -"pop($self, key, default=3DNone, /)\n" -"--\n" -"\n" -"Remove specified key and return the corresponding value.\n" -"\n" -"If key is not found, default is returned if given, otherwise KeyError is ra= ised"); - -#define DICT_POP_METHODDEF \ - {"pop", (PyCFunction)(void(*)(void))dict_pop, METH_FASTCALL, dict_pop__d= oc__}, - -static PyObject * -dict_pop_impl(PyDictObject *self, PyObject *key, PyObject *default_value); - -static PyObject * -dict_pop(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value =3D NULL; - PyObject *key; - PyObject *default_value =3D NULL; - - if (!_PyArg_CheckPositional("pop", nargs, 1, 2)) { - goto exit; - } - key =3D args[0]; - if (nargs < 2) { - goto skip_optional; - } - default_value =3D args[1]; -skip_optional: - return_value =3D dict_pop_impl(self, key, default_value); - -exit: - return return_value; -} - PyDoc_STRVAR(dict_popitem__doc__, "popitem($self, /)\n" "--\n" @@ -190,4 +154,4 @@ dict___reversed__(PyDictObject *self, PyObject *Py_UNUSED= (ignored)) { return dict___reversed___impl(self); } -/*[clinic end generated code: output=3D0fd5cafc61a51d3c input=3Da9049054013a= 1b77]*/ +/*[clinic end generated code: output=3D676532dcc941d399 input=3Da9049054013a= 1b77]*/ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 0cc144375006..e417cd2119c6 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2987,23 +2987,37 @@ dict_clear(PyDictObject *mp, PyObject *Py_UNUSED(igno= red)) Py_RETURN_NONE; } =20 -/*[clinic input] -dict.pop - - key: object - default: object =3D NULL - / +/* +We don't use Argument Clinic for dict.pop because it doesn't support +custom signature for now. +*/ +PyDoc_STRVAR(dict_pop__doc__, +"D.pop(k[,d]) -> v, remove specified key and return the corresponding value.= \n\ +If key is not found, d is returned if given, otherwise KeyError is raised"); =20 -Remove specified key and return the corresponding value. - -If key is not found, default is returned if given, otherwise KeyError is rai= sed -[clinic start generated code]*/ +#define DICT_POP_METHODDEF \ + {"pop", (PyCFunction)(void(*)(void))dict_pop, METH_FASTCALL, dict_pop__d= oc__}, =20 static PyObject * -dict_pop_impl(PyDictObject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=3D3abb47b89f24c21c input=3D016f6a000e4e= 633b]*/ +dict_pop(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs) { - return _PyDict_Pop((PyObject*)self, key, default_value); + PyObject *return_value =3D NULL; + PyObject *key; + PyObject *default_value =3D NULL; + + if (!_PyArg_CheckPositional("pop", nargs, 1, 2)) { + goto exit; + } + key =3D args[0]; + if (nargs < 2) { + goto skip_optional; + } + default_value =3D args[1]; +skip_optional: + return_value =3D _PyDict_Pop((PyObject*)self, key, default_value); + +exit: + return return_value; } =20 /*[clinic input] From webhook-mailer at python.org Thu Jul 4 17:47:48 2019 From: webhook-mailer at python.org (=?utf-8?q?=C5=81ukasz?= Langa) Date: Thu, 04 Jul 2019 21:47:48 -0000 Subject: [Python-checkins] (no subject) Message-ID: To: python-checkins at python.org Subject: Python 3.8.0b2 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 https://github.com/python/cpython/commit/21dd01dad7b6b38d4fd40b37d65f1ac7203e= c4e6 commit: 21dd01dad7b6b38d4fd40b37d65f1ac7203ec4e6 branch: 3.8 author: =C5=81ukasz Langa committer: =C5=81ukasz Langa date: 2019-07-04T12:50:19+02:00 summary: Python 3.8.0b2 files: A Misc/NEWS.d/3.8.0b2.rst D Misc/NEWS.d/next/Build/2019-06-17-09-40-59.bpo-37189.j5ebdT.rst D Misc/NEWS.d/next/C API/2019-06-06-08-47-04.bpo-37170.hO_fpM.rst D Misc/NEWS.d/next/C API/2019-06-07-10-47-37.bpo-37191.iGL1_K.rst D Misc/NEWS.d/next/C API/2019-06-10-15-32-34.bpo-37215.yzoNyU.rst D Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst D Misc/NEWS.d/next/C API/2019-06-12-11-45-36.bpo-37221.RhP1E7.rst D Misc/NEWS.d/next/C API/2019-06-14-14-03-51.bpo-28805.qZC0N_.rst D Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst D Misc/NEWS.d/next/Core and Builtins/2019-05-28-11-47-44.bpo-37077.S1h0Fc.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-05-09-24-17.bpo-37160.O3IAY3.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-06-11-00-55.bpo-36974.wdzzym.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-06-13-59-52.bpo-36922.EMZ3TF.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-10-23-18-31.bpo-37219.jPSufq.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-11-11-15-19.bpo-37213.UPii5K.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-14-06-32-33.bpo-37269.SjVVAe.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-17-03-53-16.bpo-37316.LytDX_.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-17-06-03-55.bpo-35224.FHWPGv.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-22-12-45-20.bpo-24214.hIiHeD.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-26-18-41-00.bpo-37417.VsZeHL.rst D Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu.rst D Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst D Misc/NEWS.d/next/IDLE/2019-06-03-00-39-29.bpo-5680.VCQfOO.rst D Misc/NEWS.d/next/IDLE/2019-06-04-20-36-24.bpo-35763.7XdoWz.rst D Misc/NEWS.d/next/IDLE/2019-06-04-23-27-33.bpo-37039.FN_fBf.rst D Misc/NEWS.d/next/IDLE/2019-06-07-00-17-41.bpo-37177.voU6pQ.rst D Misc/NEWS.d/next/IDLE/2019-06-17-16-35-30.bpo-37321.zVTTGS.rst D Misc/NEWS.d/next/IDLE/2019-06-18-16-40-05.bpo-37325.GssOf1.rst D Misc/NEWS.d/next/Library/2017-08-15-11-24-41.bpo-4963.LRYres.rst D Misc/NEWS.d/next/Library/2018-11-12-19-08-50.bpo-11122.Gj7BQn.rst D Misc/NEWS.d/next/Library/2019-02-03-19-13-08.bpo-32627.b68f64.rst D Misc/NEWS.d/next/Library/2019-05-09-18-50-55.bpo-35070.4vaqNL.rst D Misc/NEWS.d/next/Library/2019-05-17-15-11-08.bpo-35805.E4YwYz.rst D Misc/NEWS.d/next/Library/2019-05-27-15-29-46.bpo-30835.3FoaWH.rst D Misc/NEWS.d/next/Library/2019-05-28-02-37-00.bpo-36520.W4tday.rst D Misc/NEWS.d/next/Library/2019-05-28-19-03-46.bpo-35621.Abc1lf.rst D Misc/NEWS.d/next/Library/2019-06-04-14-44-41.bpo-37150.TTzHxj.rst D Misc/NEWS.d/next/Library/2019-06-04-22-25-38.bpo-37158.JKm15S.rst D Misc/NEWS.d/next/Library/2019-06-04-23-44-52.bpo-34767.BpDShN.rst D Misc/NEWS.d/next/Library/2019-06-05-11-48-19.bpo-37165.V_rwfE.rst D Misc/NEWS.d/next/Library/2019-06-07-08-18-05.bpo-37163.36JkUh.rst D Misc/NEWS.d/next/Library/2019-06-07-17-11-34.bpo-37178.b1StSv.rst D Misc/NEWS.d/next/Library/2019-06-07-17-16-09.bpo-37178.Day_oB.rst D Misc/NEWS.d/next/Library/2019-06-08-11-33-48.bpo-37173.0e_8gS.rst D Misc/NEWS.d/next/Library/2019-06-08-16-03-19.bpo-34886.Ov-pc9.rst D Misc/NEWS.d/next/Library/2019-06-11-00-35-02.bpo-36402.b0IJVp.rst D Misc/NEWS.d/next/Library/2019-06-11-01-54-19.bpo-18748.ADqCkq.rst D Misc/NEWS.d/next/Library/2019-06-11-13-52-04.bpo-36607.5_mJkQ.rst D Misc/NEWS.d/next/Library/2019-06-11-16-41-40.bpo-35766.v1Kj-T.rst D Misc/NEWS.d/next/Library/2019-06-11-19-34-29.bpo-35922.rxpzWr.rst D Misc/NEWS.d/next/Library/2019-06-12-16-10-50.bpo-37210.r4yMg6.rst D Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst D Misc/NEWS.d/next/Library/2019-06-14-13-25-56.bpo-37279.OHlW6l.rst D Misc/NEWS.d/next/Library/2019-06-14-13-30-47.bpo-37280.Fxur0F.rst D Misc/NEWS.d/next/Library/2019-06-15-14-39-50.bpo-33972.XxnNPw.rst D Misc/NEWS.d/next/Library/2019-06-25-02-10-00.bpo-37394.srZ1zx.rst D Misc/NEWS.d/next/Library/2019-06-25-05-07-48.bpo-36546.RUcxaK.rst D Misc/NEWS.d/next/Library/2019-06-25-19-27-25.bpo-29412.n4Zqdh.rst D Misc/NEWS.d/next/Library/2019-06-26-16-28-59.bpo-37412.lx0VjC.rst D Misc/NEWS.d/next/Library/2019-06-26-22-25-05.bpo-37420.CxFJ09.rst D Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst D Misc/NEWS.d/next/Library/2019-06-27-20-33-50.bpo-37437.du39_A.rst D Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst D Misc/NEWS.d/next/Security/2019-06-17-09-34-25.bpo-34631.DBfM4j.rst D Misc/NEWS.d/next/Security/2019-06-21-14-42-53.bpo-37364.IIRc2s.rst D Misc/NEWS.d/next/Security/2019-06-21-15-58-59.bpo-37363.diouyl.rst D Misc/NEWS.d/next/Security/2019-07-01-08-46-14.bpo-37463.1CHwjE.rst D Misc/NEWS.d/next/Security/2019-07-01-10-31-14.bpo-37363.fSjatj.rst D Misc/NEWS.d/next/Tests/2019-04-11-07-59-43.bpo-28009.s85urF.rst D Misc/NEWS.d/next/Tests/2019-06-04-18-30-39.bpo-37153.711INB.rst D Misc/NEWS.d/next/Tests/2019-06-07-12-23-15.bpo-37169.yfXTFg.rst D Misc/NEWS.d/next/Tests/2019-06-13-12-19-56.bpo-37261.NuKFVo.rst D Misc/NEWS.d/next/Tests/2019-06-14-12-21-47.bpo-37278.z0HUOr.rst D Misc/NEWS.d/next/Tests/2019-06-14-17-05-49.bpo-35998.yX82oD.rst D Misc/NEWS.d/next/Tests/2019-06-21-15-47-33.bpo-37362.D3xppx.rst D Misc/NEWS.d/next/Tests/2019-06-24-10-47-07.bpo-37359.CkdtyO.rst D Misc/NEWS.d/next/Tests/2019-06-25-16-02-43.bpo-37400.cx_EWv.rst D Misc/NEWS.d/next/Tests/2019-06-26-15-28-45.bpo-37411.5lGNhM.rst D Misc/NEWS.d/next/Tests/2019-06-27-00-37-59.bpo-37421.rVJb3x.rst D Misc/NEWS.d/next/Tests/2019-06-28-16-37-52.bpo-37335.o5S2hY.rst D Misc/NEWS.d/next/Tests/2019-06-29-23-56-28.bpo-37199.FHDsLf.rst D Misc/NEWS.d/next/Tests/2019-07-01-19-57-26.bpo-37421.NFH1f0.rst D Misc/NEWS.d/next/Tests/2019-07-02-23-20-35.bpo-37421.HCkKWz.rst D Misc/NEWS.d/next/Tests/2019-07-02-23-29-06.bpo-37421.WEfc5A.rst D Misc/NEWS.d/next/Tests/2019-07-03-00-05-28.bpo-37421.ORGRSG.rst D Misc/NEWS.d/next/Windows/2019-06-11-15-41-34.bpo-36779.0TMw6f.rst D Misc/NEWS.d/next/Windows/2019-06-13-04-15-51.bpo-37267.Ygo5ef.rst D Misc/NEWS.d/next/Windows/2019-06-18-09-05-08.bpo-35360.tdqSmo.rst D Misc/NEWS.d/next/Windows/2019-06-20-12-50-32.bpo-37351.asTnVW.rst D Misc/NEWS.d/next/Windows/2019-06-28-09-44-08.bpo-37369.1iVpxq.rst D Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst D Misc/NEWS.d/next/macOS/2019-06-18-00-30-40.bpo-34631.vSifcv.rst D Misc/NEWS.d/next/macOS/2019-06-18-08-58-30.bpo-35360.-CWbfy.rst D Misc/NEWS.d/next/macOS/2019-07-02-01-06-47.bpo-34602.10d4wl.rst M Include/patchlevel.h M Lib/pydoc_data/topics.py M README.rst diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 4a52f833a525..bb31b47bddd6 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 8 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_BETA -#define PY_RELEASE_SERIAL 1 +#define PY_RELEASE_SERIAL 2 =20 /* Version as a string */ -#define PY_VERSION "3.8.0b1+" +#define PY_VERSION "3.8.0b2" /*--end constants--*/ =20 /* Version as a single 4-byte hex number, e.g. 0x010502B2 =3D=3D 1.5.2b2. diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 1fb19f241ac3..636219108342 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Tue Jun 4 19:40:37 2019 +# Autogenerated by Sphinx on Thu Jul 4 12:44:09 2019 topics =3D {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -3758,6 +3758,8 @@ '\n' " import pdb; pdb.Pdb(skip=3D['django.*']).set_trace()\n" '\n' + ' Raises an auditing event "pdb.Pdb" with no arguments.\n' + '\n' ' New in version 3.1: The *skip* argument.\n' '\n' ' New in version 3.2: The *nosigint* argument. Previously, a= ' @@ -4289,7 +4291,14 @@ 'section The standard type hierarchy. (To summarize, the key type\= n' 'should be *hashable*, which excludes all mutable objects.) Clashe= s\n' 'between duplicate keys are not detected; the last datum (textually= \n' - 'rightmost in the display) stored for a given key value prevails.\n= ', + 'rightmost in the display) stored for a given key value prevails.\n' + '\n' + 'Changed in version 3.8: Prior to Python 3.8, in dict ' + 'comprehensions,\n' + 'the evaluation order of key and value was not well-defined. In\n' + 'CPython, the value was evaluated before the key. Starting with ' + '3.8,\n' + 'the key is evaluated before the value, as proposed by **PEP 572**.= \n', 'dynamic-features': 'Interaction with dynamic features\n' '*********************************\n' '\n' diff --git a/Misc/NEWS.d/3.8.0b2.rst b/Misc/NEWS.d/3.8.0b2.rst new file mode 100644 index 000000000000..f462e753956b --- /dev/null +++ b/Misc/NEWS.d/3.8.0b2.rst @@ -0,0 +1,933 @@ +.. bpo: 37363 +.. date: 2019-07-01-10-31-14 +.. nonce: fSjatj +.. release date: 2019-07-04 +.. section: Security + +Adds audit events for the range of supported run commands (see +:ref:`using-on-general`). + +.. + +.. bpo: 37463 +.. date: 2019-07-01-08-46-14 +.. nonce: 1CHwjE +.. section: Security + +ssl.match_hostname() no longer accepts IPv4 addresses with additional text +after the address and only quad-dotted notation without trailing +whitespaces. Some inet_aton() implementations ignore whitespace and all data +after whitespace, e.g. '127.0.0.1 whatever'. + +.. + +.. bpo: 37363 +.. date: 2019-06-21-15-58-59 +.. nonce: diouyl +.. section: Security + +Adds audit events for :mod:`ensurepip`, :mod:`ftplib`, :mod:`glob`, +:mod:`imaplib`, :mod:`nntplib`, :mod:`pdb`, :mod:`poplib`, :mod:`shutil`, +:mod:`smtplib`, :mod:`sqlite3`, :mod:`subprocess`, :mod:`telnetlib`, +:mod:`tempfile` and :mod:`webbrowser`, as well as :func:`os.listdir`, +:func:`os.scandir` and :func:`breakpoint`. + +.. + +.. bpo: 37364 +.. date: 2019-06-21-14-42-53 +.. nonce: IIRc2s +.. section: Security + +:func:`io.open_code` is now used when reading :file:`.pth` files. + +.. + +.. bpo: 34631 +.. date: 2019-06-17-09-34-25 +.. nonce: DBfM4j +.. section: Security + +Updated OpenSSL to 1.1.1c in Windows installer + +.. + +.. bpo: 37467 +.. date: 2019-07-01-12-22-44 +.. nonce: u-XyEu +.. section: Core and Builtins + +Fix :func:`sys.excepthook` and :c:func:`PyErr_Display` if a filename is a +bytes string. For example, for a SyntaxError exception where the filename +attribute is a bytes string. + +.. + +.. bpo: 37417 +.. date: 2019-06-26-18-41-00 +.. nonce: VsZeHL +.. section: Core and Builtins + +:meth:`bytearray.extend` now correctly handles errors that arise during +iteration. Patch by Brandt Bucher. + +.. + +.. bpo: 24214 +.. date: 2019-06-22-12-45-20 +.. nonce: hIiHeD +.. section: Core and Builtins + +Improved support of the surrogatepass error handler in the UTF-8 and UTF-16 +incremental decoders. + +.. + +.. bpo: 35224 +.. date: 2019-06-17-06-03-55 +.. nonce: FHWPGv +.. section: Core and Builtins + +Reverse evaluation order of key: value in dict comprehensions as proposed in +PEP 572. I.e. in ``{k: v for ...}``, ``k`` will be evaluated before ``v``. + +.. + +.. bpo: 37316 +.. date: 2019-06-17-03-53-16 +.. nonce: LytDX_ +.. section: Core and Builtins + +Fix the :c:func:`PySys_Audit` call in :class:`mmap.mmap`. + +.. + +.. bpo: 37269 +.. date: 2019-06-14-06-32-33 +.. nonce: SjVVAe +.. section: Core and Builtins + +Fix a bug in the peephole optimizer that was not treating correctly constant +conditions with binary operators. Patch by Pablo Galindo. + +.. + +.. bpo: 37213 +.. date: 2019-06-11-11-15-19 +.. nonce: UPii5K +.. section: Core and Builtins + +Handle correctly negative line offsets in the peephole optimizer. Patch by +Pablo Galindo. + +.. + +.. bpo: 37219 +.. date: 2019-06-10-23-18-31 +.. nonce: jPSufq +.. section: Core and Builtins + +Remove errorneous optimization for empty set differences. + +.. + +.. bpo: 36922 +.. date: 2019-06-06-13-59-52 +.. nonce: EMZ3TF +.. section: Core and Builtins + +Slot functions optimize any callable with ``Py_TPFLAGS_METHOD_DESCRIPTOR`` +instead of only instances of ``function``. + +.. + +.. bpo: 36974 +.. date: 2019-06-06-11-00-55 +.. nonce: wdzzym +.. section: Core and Builtins + +The slot ``tp_vectorcall_offset`` is inherited unconditionally to support +``super().__call__()`` when the base class uses vectorcall. + +.. + +.. bpo: 37160 +.. date: 2019-06-05-09-24-17 +.. nonce: O3IAY3 +.. section: Core and Builtins + +:func:`threading.get_native_id` now also supports NetBSD. + +.. + +.. bpo: 37077 +.. date: 2019-05-28-11-47-44 +.. nonce: S1h0Fc +.. section: Core and Builtins + +Add :func:`threading.get_native_id` support for AIX. Patch by M. Felt + +.. + +.. bpo: 37440 +.. date: 2019-06-28-16-40-17 +.. nonce: t3wX-N +.. section: Library + +http.client now enables TLS 1.3 post-handshake authentication for default +context or if a cert_file is passed to HTTPSConnection. + +.. + +.. bpo: 37437 +.. date: 2019-06-27-20-33-50 +.. nonce: du39_A +.. section: Library + +Update vendorized expat version to 2.2.7. + +.. + +.. bpo: 37428 +.. date: 2019-06-27-13-27-02 +.. nonce: _wcwUd +.. section: Library + +SSLContext.post_handshake_auth =3D True no longer sets +SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the +option is documented as ignored for clients, OpenSSL implicitly enables cert +chain validation when the flag is set. + +.. + +.. bpo: 37420 +.. date: 2019-06-26-22-25-05 +.. nonce: CxFJ09 +.. section: Library + +:func:`os.sched_setaffinity` now correctly handles errors that arise during +iteration over its ``mask`` argument. Patch by Brandt Bucher. + +.. + +.. bpo: 37412 +.. date: 2019-06-26-16-28-59 +.. nonce: lx0VjC +.. section: Library + +The :func:`os.getcwdb` function now uses the UTF-8 encoding on Windows, +rather than the ANSI code page: see :pep:`529` for the rationale. The +function is no longer deprecated on Windows. + +.. + +.. bpo: 29412 +.. date: 2019-06-25-19-27-25 +.. nonce: n4Zqdh +.. section: Library + +Fix IndexError in parsing a header value ending unexpectedly. Patch by +Abhilash Raj. + +.. + +.. bpo: 36546 +.. date: 2019-06-25-05-07-48 +.. nonce: RUcxaK +.. section: Library + +The *dist* argument for statistics.quantiles() is now positional only. The +current name doesn't reflect that the argument can be either a dataset or a +distribution. Marking the parameter as positional avoids confusion and +makes it possible to change the name later. + +.. + +.. bpo: 37394 +.. date: 2019-06-25-02-10-00 +.. nonce: srZ1zx +.. section: Library + +Fix a bug that was causing the :mod:`queue` module to fail if the +accelerator module was not available. Patch by Pablo Galindo. + +.. + +.. bpo: 33972 +.. date: 2019-06-15-14-39-50 +.. nonce: XxnNPw +.. section: Library + +Email with single part but content-type set to ``multipart/*`` doesn't raise +AttributeError anymore. + +.. + +.. bpo: 37280 +.. date: 2019-06-14-13-30-47 +.. nonce: Fxur0F +.. section: Library + +Use threadpool for reading from file for sendfile fallback mode. + +.. + +.. bpo: 37279 +.. date: 2019-06-14-13-25-56 +.. nonce: OHlW6l +.. section: Library + +Fix asyncio sendfile support when sendfile sends extra data in fallback +mode. + +.. + +.. bpo: 19865 +.. date: 2019-06-14-08-30-16 +.. nonce: FRGH4I +.. section: Library + +:func:`ctypes.create_unicode_buffer()` now also supports non-BMP characters +on platforms with 16-bit :c:type:`wchar_t` (for example, Windows and AIX). + +.. + +.. bpo: 37210 +.. date: 2019-06-12-16-10-50 +.. nonce: r4yMg6 +.. section: Library + +Allow pure Python implementation of :mod:`pickle` to work even when the C +:mod:`_pickle` module is unavailable. + +.. + +.. bpo: 35922 +.. date: 2019-06-11-19-34-29 +.. nonce: rxpzWr +.. section: Library + +Fix :meth:`RobotFileParser.crawl_delay` and +:meth:`RobotFileParser.request_rate` to return ``None`` rather than raise +:exc:`AttributeError` when no relevant rule is defined in the robots.txt +file. Patch by R=C3=A9mi Lapeyre. + +.. + +.. bpo: 35766 +.. date: 2019-06-11-16-41-40 +.. nonce: v1Kj-T +.. section: Library + +Change the format of feature_version to be a (major, minor) tuple. + +.. + +.. bpo: 36607 +.. date: 2019-06-11-13-52-04 +.. nonce: 5_mJkQ +.. section: Library + +Eliminate :exc:`RuntimeError` raised by :func:`asyncio.all_tasks()` if +internal tasks weak set is changed by another thread during iteration. + +.. + +.. bpo: 18748 +.. date: 2019-06-11-01-54-19 +.. nonce: ADqCkq +.. section: Library + +:class:`_pyio.IOBase` destructor now does nothing if getting the ``closed`` +attribute fails to better mimick :class:`_io.IOBase` finalizer. + +.. + +.. bpo: 36402 +.. date: 2019-06-11-00-35-02 +.. nonce: b0IJVp +.. section: Library + +Fix a race condition at Python shutdown when waiting for threads. Wait until +the Python thread state of all non-daemon threads get deleted (join all +non-daemon threads), rather than just wait until non-daemon Python threads +complete. + +.. + +.. bpo: 34886 +.. date: 2019-06-08-16-03-19 +.. nonce: Ov-pc9 +.. section: Library + +Fix an unintended ValueError from :func:`subprocess.run` when checking for +conflicting `input` and `stdin` or `capture_output` and `stdout` or `stderr` +args when they were explicitly provided but with `None` values within a +passed in `**kwargs` dict rather than as passed directly by name. Patch +contributed by R=C3=A9mi Lapeyre. + +.. + +.. bpo: 37173 +.. date: 2019-06-08-11-33-48 +.. nonce: 0e_8gS +.. section: Library + +The exception message for ``inspect.getfile()`` now correctly reports the +passed class rather than the builtins module. + +.. + +.. bpo: 37178 +.. date: 2019-06-07-17-16-09 +.. nonce: Day_oB +.. section: Library + +Give math.perm() a one argument form that means the same as +math.factorial(). + +.. + +.. bpo: 37178 +.. date: 2019-06-07-17-11-34 +.. nonce: b1StSv +.. section: Library + +For math.perm(n, k), let k default to n, giving the same result as +factorial. + +.. + +.. bpo: 37163 +.. date: 2019-06-07-08-18-05 +.. nonce: 36JkUh +.. section: Library + +Deprecated passing ``obj`` argument of :func:`dataclasses.replace` as +keyword argument. + +.. + +.. bpo: 37165 +.. date: 2019-06-05-11-48-19 +.. nonce: V_rwfE +.. section: Library + +Converted _collections._count_elements to use the Argument Clinic. + +.. + +.. bpo: 34767 +.. date: 2019-06-04-23-44-52 +.. nonce: BpDShN +.. section: Library + +Do not always create a :class:`collections.deque` in :class:`asyncio.Lock`. + +.. + +.. bpo: 37158 +.. date: 2019-06-04-22-25-38 +.. nonce: JKm15S +.. section: Library + +Speed-up statistics.fmean() by switching from a function to a generator. + +.. + +.. bpo: 37150 +.. date: 2019-06-04-14-44-41 +.. nonce: TTzHxj +.. section: Library + +`argparse._ActionsContainer.add_argument` now throws error, if someone +accidentally pass FileType class object instead of instance of FileType as +`type` argument + +.. + +.. bpo: 35621 +.. date: 2019-05-28-19-03-46 +.. nonce: Abc1lf +.. section: Library + +Support running asyncio subprocesses when execution event loop in a thread +on UNIX. + +.. + +.. bpo: 36520 +.. date: 2019-05-28-02-37-00 +.. nonce: W4tday +.. section: Library + +Lengthy email headers with UTF-8 characters are now properly encoded when +they are folded. Patch by Jeffrey Kintscher. + +.. + +.. bpo: 30835 +.. date: 2019-05-27-15-29-46 +.. nonce: 3FoaWH +.. section: Library + +Fixed a bug in email parsing where a message with invalid bytes in +content-transfer-encoding of a multipart message can cause an +AttributeError. Patch by Andrew Donnellan. + +.. + +.. bpo: 35805 +.. date: 2019-05-17-15-11-08 +.. nonce: E4YwYz +.. section: Library + +Add parser for Message-ID header and add it to default HeaderRegistry. This +should prevent folding of Message-ID using RFC 2048 encoded words. + +.. + +.. bpo: 35070 +.. date: 2019-05-09-18-50-55 +.. nonce: 4vaqNL +.. section: Library + +posix.getgrouplist() now works correctly when the user belongs to +NGROUPS_MAX supplemental groups. Patch by Jeffrey Kintscher. + +.. + +.. bpo: 32627 +.. date: 2019-02-03-19-13-08 +.. nonce: b68f64 +.. section: Library + +Fix compile error when ``_uuid`` headers conflicting included. + +.. + +.. bpo: 11122 +.. date: 2018-11-12-19-08-50 +.. nonce: Gj7BQn +.. section: Library + +Distutils won't check for rpmbuild in specified paths only. + +.. + +.. bpo: 4963 +.. date: 2017-08-15-11-24-41 +.. nonce: LRYres +.. section: Library + +Fixed non-deterministic behavior related to mimetypes extension mapping and +module reinitialization. + +.. + +.. bpo: 34903 +.. date: 2019-06-17-09-36-46 +.. nonce: r_wGRc +.. section: Documentation + +Documented that in :meth:`datetime.datetime.strptime()`, the leading zero in +some two-digit formats is optional. Patch by Mike Gleen. + +.. + +.. bpo: 37421 +.. date: 2019-07-03-00-05-28 +.. nonce: ORGRSG +.. section: Tests + +test_distutils.test_build_ext() is now able to remove the temporary +directory on Windows: don't import the newly built C extension ("xx") in the +current process, but test it in a separated process. + +.. + +.. bpo: 37421 +.. date: 2019-07-02-23-29-06 +.. nonce: WEfc5A +.. section: Tests + +test_concurrent_futures now cleans up multiprocessing to remove immediately +temporary directories created by multiprocessing.util.get_temp_dir(). + +.. + +.. bpo: 37421 +.. date: 2019-07-02-23-20-35 +.. nonce: HCkKWz +.. section: Tests + +test_winconsoleio doesn't leak a temporary file anymore: use +tempfile.TemporaryFile() to remove it when the test completes. + +.. + +.. bpo: 37421 +.. date: 2019-07-01-19-57-26 +.. nonce: NFH1f0 +.. section: Tests + +multiprocessing tests now explicitly call ``_run_finalizers()`` to +immediately remove temporary directories created by tests. + +.. + +.. bpo: 37199 +.. date: 2019-06-29-23-56-28 +.. nonce: FHDsLf +.. section: Tests + +Fix test failures when IPv6 is unavailable or disabled. + +.. + +.. bpo: 37335 +.. date: 2019-06-28-16-37-52 +.. nonce: o5S2hY +.. section: Tests + +Remove no longer necessary code from c locale coercion tests + +.. + +.. bpo: 37421 +.. date: 2019-06-27-00-37-59 +.. nonce: rVJb3x +.. section: Tests + +Fix test_shutil to no longer leak temporary files. + +.. + +.. bpo: 37411 +.. date: 2019-06-26-15-28-45 +.. nonce: 5lGNhM +.. section: Tests + +Fix test_wsgiref.testEnviron() to no longer depend on the environment +variables (don't fail if "X" variable is set). + +.. + +.. bpo: 37400 +.. date: 2019-06-25-16-02-43 +.. nonce: cx_EWv +.. section: Tests + +Fix test_os.test_chown(): use os.getgroups() rather than grp.getgrall() to +get groups. Rename also the test to test_chown_gid(). + +.. + +.. bpo: 37359 +.. date: 2019-06-24-10-47-07 +.. nonce: CkdtyO +.. section: Tests + +Add --cleanup option to python3 -m test to remove ``test_python_*`` +directories of previous failed jobs. Add "make cleantest" to run ``python3 +-m test --cleanup``. + +.. + +.. bpo: 37362 +.. date: 2019-06-21-15-47-33 +.. nonce: D3xppx +.. section: Tests + +test_gdb no longer fails if it gets an "unexpected" message on stderr: it +now ignores stderr. The purpose of test_gdb is to test that python-gdb.py +commands work as expected, not to test gdb. + +.. + +.. bpo: 35998 +.. date: 2019-06-14-17-05-49 +.. nonce: yX82oD +.. section: Tests + +Avoid TimeoutError in test_asyncio: test_start_tls_server_1() + +.. + +.. bpo: 37278 +.. date: 2019-06-14-12-21-47 +.. nonce: z0HUOr +.. section: Tests + +Fix test_asyncio ProactorLoopCtrlC: join the thread to prevent leaking a +running thread and leaking a reference. + +.. + +.. bpo: 37261 +.. date: 2019-06-13-12-19-56 +.. nonce: NuKFVo +.. section: Tests + +Fix :func:`test.support.catch_unraisable_exception`: its __exit__() method +now ignores unraisable exception raised when clearing its ``unraisable`` +attribute. + +.. + +.. bpo: 37169 +.. date: 2019-06-07-12-23-15 +.. nonce: yfXTFg +.. section: Tests + +Rewrite ``_PyObject_IsFreed()`` unit tests. + +.. + +.. bpo: 37153 +.. date: 2019-06-04-18-30-39 +.. nonce: 711INB +.. section: Tests + +``test_venv.test_mutiprocessing()`` now explicitly calls +``pool.terminate()`` to wait until the pool completes. + +.. + +.. bpo: 28009 +.. date: 2019-04-11-07-59-43 +.. nonce: s85urF +.. section: Tests + +Modify the test_uuid logic to test when a program is available AND can be +used to obtain a MACADDR as basis for an UUID. Patch by M. Felt + +.. + +.. bpo: 37189 +.. date: 2019-06-17-09-40-59 +.. nonce: j5ebdT +.. section: Build + +Many ``PyRun_XXX()`` functions like :c:func:`PyRun_String` were no longer +exported in ``libpython38.dll`` by mistake. Export them again to fix the ABI +compatibiliy. + +.. + +.. bpo: 10945 +.. date: 2019-07-01-12-38-48 +.. nonce: s0YBHG +.. section: Windows + +Officially drop support for creating bdist_wininst installers on non-Windows +systems. + +.. + +.. bpo: 37369 +.. date: 2019-06-28-09-44-08 +.. nonce: 1iVpxq +.. section: Windows + +Fixes path for :data:`sys.executable` when running from the Microsoft Store. + +.. + +.. bpo: 37351 +.. date: 2019-06-20-12-50-32 +.. nonce: asTnVW +.. section: Windows + +Removes libpython38.a from standard Windows distribution. + +.. + +.. bpo: 35360 +.. date: 2019-06-18-09-05-08 +.. nonce: tdqSmo +.. section: Windows + +Update Windows builds to use SQLite 3.28.0. + +.. + +.. bpo: 37267 +.. date: 2019-06-13-04-15-51 +.. nonce: Ygo5ef +.. section: Windows + +On Windows, :func:`os.dup` no longer creates an inheritable fd when handling +a character file. + +.. + +.. bpo: 36779 +.. date: 2019-06-11-15-41-34 +.. nonce: 0TMw6f +.. section: Windows + +Ensure ``time.tzname`` is correct on Windows when the active code page is +set to CP_UTF7 or CP_UTF8. + +.. + +.. bpo: 34602 +.. date: 2019-07-02-01-06-47 +.. nonce: 10d4wl +.. section: macOS + +Avoid test suite failures on macOS by no longer calling resource.setrlimit +to increase the process stack size limit at runtime. The runtime change is +no longer needed since the interpreter is being built with a larger default +stack size. + +.. + +.. bpo: 35360 +.. date: 2019-06-18-08-58-30 +.. nonce: -CWbfy +.. section: macOS + +Update macOS installer to use SQLite 3.28.0. + +.. + +.. bpo: 34631 +.. date: 2019-06-18-00-30-40 +.. nonce: vSifcv +.. section: macOS + +Updated OpenSSL to 1.1.1c in macOS installer. + +.. + +.. bpo: 37325 +.. date: 2019-06-18-16-40-05 +.. nonce: GssOf1 +.. section: IDLE + +Fix tab focus traversal order for help source and custom run dialogs. + +.. + +.. bpo: 37321 +.. date: 2019-06-17-16-35-30 +.. nonce: zVTTGS +.. section: IDLE + +Both subprocess connection error messages now refer to the 'Startup failure' +section of the IDLE doc. + +.. + +.. bpo: 37177 +.. date: 2019-06-07-00-17-41 +.. nonce: voU6pQ +.. section: IDLE + +Properly 'attach' search dialogs to their main window so that they behave +like other dialogs and do not get hidden behind their main window. + +.. + +.. bpo: 37039 +.. date: 2019-06-04-23-27-33 +.. nonce: FN_fBf +.. section: IDLE + +Adjust "Zoom Height" to individual screens by momemtarily maximizing the +window on first use with a particular screen. Changing screen settings may +invalidate the saved height. While a window is maximized, "Zoom Height" has +no effect. + +.. + +.. bpo: 35763 +.. date: 2019-06-04-20-36-24 +.. nonce: 7XdoWz +.. section: IDLE + +Make calltip reminder about '/' meaning positional-only less obtrusive by +only adding it when there is room on the first line. + +.. + +.. bpo: 5680 +.. date: 2019-06-03-00-39-29 +.. nonce: VCQfOO +.. section: IDLE + +Add 'Run... Customized' to the Run menu to run a module with customized +settings. Any 'command line arguments' entered are added to sys.argv. One +can suppress the normal Shell main module restart. + +.. + +.. bpo: 36763 +.. date: 2019-06-28-15-49-16 +.. nonce: zrmgki +.. section: C API + +Add :func:`PyConfig_SetWideStringList` function. + +.. + +.. bpo: 28805 +.. date: 2019-06-14-14-03-51 +.. nonce: qZC0N_ +.. section: C API + +The :const:`METH_FASTCALL` calling convention has been documented. + +.. + +.. bpo: 37221 +.. date: 2019-06-12-11-45-36 +.. nonce: RhP1E7 +.. section: C API + +``tp_print`` is put back at the end of the ``PyTypeObject`` structure to +restore support for old code (in particular generated by Cython) setting +``tp_print =3D 0``. Note that ``tp_print`` will be removed entirely in Python +3.9. + +.. + +.. bpo: 37221 +.. date: 2019-06-11-02-50-38 +.. nonce: 4tClQT +.. section: C API + +The new function :c:func:`PyCode_NewWithPosOnlyArgs` allows to create code +objects like :c:func:`PyCode_New`, but with an extra *posonlyargcount* +parameter for indicating the number of positonal-only arguments. + +.. + +.. bpo: 37215 +.. date: 2019-06-10-15-32-34 +.. nonce: yzoNyU +.. section: C API + +Fix dtrace issue introduce by bpo-36842 + +.. + +.. bpo: 37191 +.. date: 2019-06-07-10-47-37 +.. nonce: iGL1_K +.. section: C API + +Python.h does not need compiler support for intermingled declarations (GCC's +``-Wdeclaration-after-statement``), which were added in 3.8.0 Beta 1. Note +that in Python 3.9, intermingled declarations will be needed again. + +.. + +.. bpo: 37170 +.. date: 2019-06-06-08-47-04 +.. nonce: hO_fpM +.. section: C API + +Fix the cast on error in :c:func:`PyLong_AsUnsignedLongLongMask()`. diff --git a/Misc/NEWS.d/next/Build/2019-06-17-09-40-59.bpo-37189.j5ebdT.rst = b/Misc/NEWS.d/next/Build/2019-06-17-09-40-59.bpo-37189.j5ebdT.rst deleted file mode 100644 index f11f2746e5a0..000000000000 --- a/Misc/NEWS.d/next/Build/2019-06-17-09-40-59.bpo-37189.j5ebdT.rst +++ /dev/null @@ -1,3 +0,0 @@ -Many ``PyRun_XXX()`` functions like :c:func:`PyRun_String` were no longer -exported in ``libpython38.dll`` by mistake. Export them again to fix the ABI -compatibiliy. diff --git a/Misc/NEWS.d/next/C API/2019-06-06-08-47-04.bpo-37170.hO_fpM.rst = b/Misc/NEWS.d/next/C API/2019-06-06-08-47-04.bpo-37170.hO_fpM.rst deleted file mode 100644 index 7a35c9583d69..000000000000 --- a/Misc/NEWS.d/next/C API/2019-06-06-08-47-04.bpo-37170.hO_fpM.rst=09 +++ /dev/null @@ -1 +0,0 @@ -Fix the cast on error in :c:func:`PyLong_AsUnsignedLongLongMask()`. diff --git a/Misc/NEWS.d/next/C API/2019-06-07-10-47-37.bpo-37191.iGL1_K.rst = b/Misc/NEWS.d/next/C API/2019-06-07-10-47-37.bpo-37191.iGL1_K.rst deleted file mode 100644 index 7cb296d33bb5..000000000000 --- a/Misc/NEWS.d/next/C API/2019-06-07-10-47-37.bpo-37191.iGL1_K.rst=09 +++ /dev/null @@ -1,3 +0,0 @@ -Python.h does not need compiler support for intermingled declarations (GCC's -``-Wdeclaration-after-statement``), which were added in 3.8.0 Beta 1. Note -that in Python 3.9, intermingled declarations will be needed again. diff --git a/Misc/NEWS.d/next/C API/2019-06-10-15-32-34.bpo-37215.yzoNyU.rst = b/Misc/NEWS.d/next/C API/2019-06-10-15-32-34.bpo-37215.yzoNyU.rst deleted file mode 100644 index 58038b21729b..000000000000 --- a/Misc/NEWS.d/next/C API/2019-06-10-15-32-34.bpo-37215.yzoNyU.rst=09 +++ /dev/null @@ -1 +0,0 @@ -Fix dtrace issue introduce by bpo-36842 diff --git a/Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst = b/Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst deleted file mode 100644 index 0ea8b9b86788..000000000000 --- a/Misc/NEWS.d/next/C API/2019-06-11-02-50-38.bpo-37221.4tClQT.rst=09 +++ /dev/null @@ -1,3 +0,0 @@ -The new function :c:func:`PyCode_NewWithPosOnlyArgs` allows to create -code objects like :c:func:`PyCode_New`, but with an extra *posonlyargcount* -parameter for indicating the number of positonal-only arguments. diff --git a/Misc/NEWS.d/next/C API/2019-06-12-11-45-36.bpo-37221.RhP1E7.rst = b/Misc/NEWS.d/next/C API/2019-06-12-11-45-36.bpo-37221.RhP1E7.rst deleted file mode 100644 index 96b61dea0d9c..000000000000 --- a/Misc/NEWS.d/next/C API/2019-06-12-11-45-36.bpo-37221.RhP1E7.rst=09 +++ /dev/null @@ -1,4 +0,0 @@ -``tp_print`` is put back at the end of the ``PyTypeObject`` structure -to restore support for old code (in particular generated by Cython) -setting ``tp_print =3D 0``. -Note that ``tp_print`` will be removed entirely in Python 3.9. diff --git a/Misc/NEWS.d/next/C API/2019-06-14-14-03-51.bpo-28805.qZC0N_.rst = b/Misc/NEWS.d/next/C API/2019-06-14-14-03-51.bpo-28805.qZC0N_.rst deleted file mode 100644 index 6d6c4ad4af60..000000000000 --- a/Misc/NEWS.d/next/C API/2019-06-14-14-03-51.bpo-28805.qZC0N_.rst=09 +++ /dev/null @@ -1 +0,0 @@ -The :const:`METH_FASTCALL` calling convention has been documented. diff --git a/Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst = b/Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst deleted file mode 100644 index 095d58116385..000000000000 --- a/Misc/NEWS.d/next/C API/2019-06-28-15-49-16.bpo-36763.zrmgki.rst=09 +++ /dev/null @@ -1 +0,0 @@ -Add :func:`PyConfig_SetWideStringList` function. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-28-11-47-44.bpo-37077= .S1h0Fc.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-28-11-47-44.bpo-3707= 7.S1h0Fc.rst deleted file mode 100644 index 832dfc94ac49..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-05-28-11-47-44.bpo-37077.S1h0Fc= .rst=09 +++ /dev/null @@ -1,2 +0,0 @@ -Add :func:`threading.get_native_id` support for AIX. -Patch by M. Felt diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-05-09-24-17.bpo-37160= .O3IAY3.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-05-09-24-17.bpo-3716= 0.O3IAY3.rst deleted file mode 100644 index c2fc6804a126..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-05-09-24-17.bpo-37160.O3IAY3= .rst=09 +++ /dev/null @@ -1 +0,0 @@ -:func:`threading.get_native_id` now also supports NetBSD. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-06-11-00-55.bpo-36974= .wdzzym.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-06-11-00-55.bpo-3697= 4.wdzzym.rst deleted file mode 100644 index 035cdd3d6a9e..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-06-11-00-55.bpo-36974.wdzzym= .rst=09 +++ /dev/null @@ -1,2 +0,0 @@ -The slot ``tp_vectorcall_offset`` is inherited unconditionally to support -``super().__call__()`` when the base class uses vectorcall. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-06-13-59-52.bpo-36922= .EMZ3TF.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-06-13-59-52.bpo-3692= 2.EMZ3TF.rst deleted file mode 100644 index c2a2ad4e8e73..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-06-13-59-52.bpo-36922.EMZ3TF= .rst=09 +++ /dev/null @@ -1 +0,0 @@ -Slot functions optimize any callable with ``Py_TPFLAGS_METHOD_DESCRIPTOR`` i= nstead of only instances of ``function``. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-10-23-18-31.bpo-37219= .jPSufq.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-10-23-18-31.bpo-3721= 9.jPSufq.rst deleted file mode 100644 index ef8f52dce678..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-10-23-18-31.bpo-37219.jPSufq= .rst=09 +++ /dev/null @@ -1 +0,0 @@ -Remove errorneous optimization for empty set differences. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-11-11-15-19.bpo-37213= .UPii5K.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-11-11-15-19.bpo-3721= 3.UPii5K.rst deleted file mode 100644 index b949883da9c2..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-11-11-15-19.bpo-37213.UPii5K= .rst=09 +++ /dev/null @@ -1,2 +0,0 @@ -Handle correctly negative line offsets in the peephole optimizer. Patch by -Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-14-06-32-33.bpo-37269= .SjVVAe.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-14-06-32-33.bpo-3726= 9.SjVVAe.rst deleted file mode 100644 index b9b79066774f..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-14-06-32-33.bpo-37269.SjVVAe= .rst=09 +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug in the peephole optimizer that was not treating correctly constant -conditions with binary operators. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-17-03-53-16.bpo-37316= .LytDX_.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-17-03-53-16.bpo-3731= 6.LytDX_.rst deleted file mode 100644 index 40436d467c85..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-17-03-53-16.bpo-37316.LytDX_= .rst=09 +++ /dev/null @@ -1 +0,0 @@ -Fix the :c:func:`PySys_Audit` call in :class:`mmap.mmap`. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-17-06-03-55.bpo-35224= .FHWPGv.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-17-06-03-55.bpo-3522= 4.FHWPGv.rst deleted file mode 100644 index 5a1a79be098a..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-17-06-03-55.bpo-35224.FHWPGv= .rst=09 +++ /dev/null @@ -1,2 +0,0 @@ -Reverse evaluation order of key: value in dict comprehensions as proposed in= PEP 572. -I.e. in ``{k: v for ...}``, ``k`` will be evaluated before ``v``. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-22-12-45-20.bpo-24214= .hIiHeD.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-22-12-45-20.bpo-2421= 4.hIiHeD.rst deleted file mode 100644 index 2d70ce05deae..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-22-12-45-20.bpo-24214.hIiHeD= .rst=09 +++ /dev/null @@ -1,2 +0,0 @@ -Improved support of the surrogatepass error handler in the UTF-8 and UTF-16 -incremental decoders. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-26-18-41-00.bpo-37417= .VsZeHL.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-26-18-41-00.bpo-3741= 7.VsZeHL.rst deleted file mode 100644 index f004631e2361..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-26-18-41-00.bpo-37417.VsZeHL= .rst=09 +++ /dev/null @@ -1,2 +0,0 @@ -:meth:`bytearray.extend` now correctly handles errors that arise during iter= ation. -Patch by Brandt Bucher. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467= .u-XyEu.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-3746= 7.u-XyEu.rst deleted file mode 100644 index 5e809646b4b8..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-07-01-12-22-44.bpo-37467.u-XyEu= .rst=09 +++ /dev/null @@ -1,3 +0,0 @@ -Fix :func:`sys.excepthook` and :c:func:`PyErr_Display` if a filename is a -bytes string. For example, for a SyntaxError exception where the filename -attribute is a bytes string. diff --git a/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_w= GRc.rst b/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc= .rst deleted file mode 100644 index 7e277f4ec1ae..000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst +++ /dev/null @@ -1 +0,0 @@ -Documented that in :meth:`datetime.datetime.strptime()`, the leading zero in= some two-digit formats is optional. Patch by Mike Gleen. diff --git a/Misc/NEWS.d/next/IDLE/2019-06-03-00-39-29.bpo-5680.VCQfOO.rst b/= Misc/NEWS.d/next/IDLE/2019-06-03-00-39-29.bpo-5680.VCQfOO.rst deleted file mode 100644 index 9fc642077488..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-06-03-00-39-29.bpo-5680.VCQfOO.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add 'Run... Customized' to the Run menu to run a module with customized -settings. Any 'command line arguments' entered are added to sys.argv. -One can suppress the normal Shell main module restart. diff --git a/Misc/NEWS.d/next/IDLE/2019-06-04-20-36-24.bpo-35763.7XdoWz.rst b= /Misc/NEWS.d/next/IDLE/2019-06-04-20-36-24.bpo-35763.7XdoWz.rst deleted file mode 100644 index c2b6594cc350..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-06-04-20-36-24.bpo-35763.7XdoWz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make calltip reminder about '/' meaning positional-only less obtrusive by -only adding it when there is room on the first line. diff --git a/Misc/NEWS.d/next/IDLE/2019-06-04-23-27-33.bpo-37039.FN_fBf.rst b= /Misc/NEWS.d/next/IDLE/2019-06-04-23-27-33.bpo-37039.FN_fBf.rst deleted file mode 100644 index 71c8c892ba6a..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-06-04-23-27-33.bpo-37039.FN_fBf.rst +++ /dev/null @@ -1,4 +0,0 @@ -Adjust "Zoom Height" to individual screens by momemtarily maximizing the -window on first use with a particular screen. Changing screen settings -may invalidate the saved height. While a window is maximized, -"Zoom Height" has no effect. diff --git a/Misc/NEWS.d/next/IDLE/2019-06-07-00-17-41.bpo-37177.voU6pQ.rst b= /Misc/NEWS.d/next/IDLE/2019-06-07-00-17-41.bpo-37177.voU6pQ.rst deleted file mode 100644 index 74e48eef89a6..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-06-07-00-17-41.bpo-37177.voU6pQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Properly 'attach' search dialogs to their main window so that they behave -like other dialogs and do not get hidden behind their main window. diff --git a/Misc/NEWS.d/next/IDLE/2019-06-17-16-35-30.bpo-37321.zVTTGS.rst b= /Misc/NEWS.d/next/IDLE/2019-06-17-16-35-30.bpo-37321.zVTTGS.rst deleted file mode 100644 index 1321986c5a2c..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-06-17-16-35-30.bpo-37321.zVTTGS.rst +++ /dev/null @@ -1,2 +0,0 @@ -Both subprocess connection error messages now refer to the 'Startup failure' -section of the IDLE doc. diff --git a/Misc/NEWS.d/next/IDLE/2019-06-18-16-40-05.bpo-37325.GssOf1.rst b= /Misc/NEWS.d/next/IDLE/2019-06-18-16-40-05.bpo-37325.GssOf1.rst deleted file mode 100644 index edfffbefe884..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-06-18-16-40-05.bpo-37325.GssOf1.rst +++ /dev/null @@ -1 +0,0 @@ -Fix tab focus traversal order for help source and custom run dialogs. diff --git a/Misc/NEWS.d/next/Library/2017-08-15-11-24-41.bpo-4963.LRYres.rst= b/Misc/NEWS.d/next/Library/2017-08-15-11-24-41.bpo-4963.LRYres.rst deleted file mode 100644 index 3b060052fd35..000000000000 --- a/Misc/NEWS.d/next/Library/2017-08-15-11-24-41.bpo-4963.LRYres.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed non-deterministic behavior related to mimetypes extension mapping and -module reinitialization. diff --git a/Misc/NEWS.d/next/Library/2018-11-12-19-08-50.bpo-11122.Gj7BQn.rs= t b/Misc/NEWS.d/next/Library/2018-11-12-19-08-50.bpo-11122.Gj7BQn.rst deleted file mode 100644 index 483906613801..000000000000 --- a/Misc/NEWS.d/next/Library/2018-11-12-19-08-50.bpo-11122.Gj7BQn.rst +++ /dev/null @@ -1 +0,0 @@ -Distutils won't check for rpmbuild in specified paths only. diff --git a/Misc/NEWS.d/next/Library/2019-02-03-19-13-08.bpo-32627.b68f64.rs= t b/Misc/NEWS.d/next/Library/2019-02-03-19-13-08.bpo-32627.b68f64.rst deleted file mode 100644 index 16708aa5ee54..000000000000 --- a/Misc/NEWS.d/next/Library/2019-02-03-19-13-08.bpo-32627.b68f64.rst +++ /dev/null @@ -1 +0,0 @@ -Fix compile error when ``_uuid`` headers conflicting included. diff --git a/Misc/NEWS.d/next/Library/2019-05-09-18-50-55.bpo-35070.4vaqNL.rs= t b/Misc/NEWS.d/next/Library/2019-05-09-18-50-55.bpo-35070.4vaqNL.rst deleted file mode 100644 index e823f1d469cc..000000000000 --- a/Misc/NEWS.d/next/Library/2019-05-09-18-50-55.bpo-35070.4vaqNL.rst +++ /dev/null @@ -1,2 +0,0 @@ -posix.getgrouplist() now works correctly when the user belongs to -NGROUPS_MAX supplemental groups. Patch by Jeffrey Kintscher. diff --git a/Misc/NEWS.d/next/Library/2019-05-17-15-11-08.bpo-35805.E4YwYz.rs= t b/Misc/NEWS.d/next/Library/2019-05-17-15-11-08.bpo-35805.E4YwYz.rst deleted file mode 100644 index 2d8c8b3222d0..000000000000 --- a/Misc/NEWS.d/next/Library/2019-05-17-15-11-08.bpo-35805.E4YwYz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add parser for Message-ID header and add it to default HeaderRegistry. This -should prevent folding of Message-ID using RFC 2048 encoded words. diff --git a/Misc/NEWS.d/next/Library/2019-05-27-15-29-46.bpo-30835.3FoaWH.rs= t b/Misc/NEWS.d/next/Library/2019-05-27-15-29-46.bpo-30835.3FoaWH.rst deleted file mode 100644 index 019321d6f1d7..000000000000 --- a/Misc/NEWS.d/next/Library/2019-05-27-15-29-46.bpo-30835.3FoaWH.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed a bug in email parsing where a message with invalid bytes in -content-transfer-encoding of a multipart message can cause an AttributeError. -Patch by Andrew Donnellan. diff --git a/Misc/NEWS.d/next/Library/2019-05-28-02-37-00.bpo-36520.W4tday.rs= t b/Misc/NEWS.d/next/Library/2019-05-28-02-37-00.bpo-36520.W4tday.rst deleted file mode 100644 index 8171bfe9e2df..000000000000 --- a/Misc/NEWS.d/next/Library/2019-05-28-02-37-00.bpo-36520.W4tday.rst +++ /dev/null @@ -1 +0,0 @@ -Lengthy email headers with UTF-8 characters are now properly encoded when th= ey are folded. Patch by Jeffrey Kintscher. diff --git a/Misc/NEWS.d/next/Library/2019-05-28-19-03-46.bpo-35621.Abc1lf.rs= t b/Misc/NEWS.d/next/Library/2019-05-28-19-03-46.bpo-35621.Abc1lf.rst deleted file mode 100644 index c492e1de6d5c..000000000000 --- a/Misc/NEWS.d/next/Library/2019-05-28-19-03-46.bpo-35621.Abc1lf.rst +++ /dev/null @@ -1,2 +0,0 @@ -Support running asyncio subprocesses when execution event loop in a thread -on UNIX. diff --git a/Misc/NEWS.d/next/Library/2019-06-04-14-44-41.bpo-37150.TTzHxj.rs= t b/Misc/NEWS.d/next/Library/2019-06-04-14-44-41.bpo-37150.TTzHxj.rst deleted file mode 100644 index c5be46e1e5d3..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-04-14-44-41.bpo-37150.TTzHxj.rst +++ /dev/null @@ -1 +0,0 @@ -`argparse._ActionsContainer.add_argument` now throws error, if someone accid= entally pass FileType class object instead of instance of FileType as `type` = argument \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2019-06-04-22-25-38.bpo-37158.JKm15S.rs= t b/Misc/NEWS.d/next/Library/2019-06-04-22-25-38.bpo-37158.JKm15S.rst deleted file mode 100644 index 4a5ec4122f94..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-04-22-25-38.bpo-37158.JKm15S.rst +++ /dev/null @@ -1 +0,0 @@ -Speed-up statistics.fmean() by switching from a function to a generator. diff --git a/Misc/NEWS.d/next/Library/2019-06-04-23-44-52.bpo-34767.BpDShN.rs= t b/Misc/NEWS.d/next/Library/2019-06-04-23-44-52.bpo-34767.BpDShN.rst deleted file mode 100644 index b46bc44506f4..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-04-23-44-52.bpo-34767.BpDShN.rst +++ /dev/null @@ -1 +0,0 @@ -Do not always create a :class:`collections.deque` in :class:`asyncio.Lock`. diff --git a/Misc/NEWS.d/next/Library/2019-06-05-11-48-19.bpo-37165.V_rwfE.rs= t b/Misc/NEWS.d/next/Library/2019-06-05-11-48-19.bpo-37165.V_rwfE.rst deleted file mode 100644 index 5430a57ef54c..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-05-11-48-19.bpo-37165.V_rwfE.rst +++ /dev/null @@ -1 +0,0 @@ -Converted _collections._count_elements to use the Argument Clinic. diff --git a/Misc/NEWS.d/next/Library/2019-06-07-08-18-05.bpo-37163.36JkUh.rs= t b/Misc/NEWS.d/next/Library/2019-06-07-08-18-05.bpo-37163.36JkUh.rst deleted file mode 100644 index 04cf61d3e099..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-07-08-18-05.bpo-37163.36JkUh.rst +++ /dev/null @@ -1,2 +0,0 @@ -Deprecated passing ``obj`` argument of :func:`dataclasses.replace` as -keyword argument. diff --git a/Misc/NEWS.d/next/Library/2019-06-07-17-11-34.bpo-37178.b1StSv.rs= t b/Misc/NEWS.d/next/Library/2019-06-07-17-11-34.bpo-37178.b1StSv.rst deleted file mode 100644 index 77b872319a91..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-07-17-11-34.bpo-37178.b1StSv.rst +++ /dev/null @@ -1,2 +0,0 @@ -For math.perm(n, k), let k default to n, giving the same result as -factorial. diff --git a/Misc/NEWS.d/next/Library/2019-06-07-17-16-09.bpo-37178.Day_oB.rs= t b/Misc/NEWS.d/next/Library/2019-06-07-17-16-09.bpo-37178.Day_oB.rst deleted file mode 100644 index 500ef54fd615..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-07-17-16-09.bpo-37178.Day_oB.rst +++ /dev/null @@ -1,2 +0,0 @@ -Give math.perm() a one argument form that means the same as -math.factorial(). diff --git a/Misc/NEWS.d/next/Library/2019-06-08-11-33-48.bpo-37173.0e_8gS.rs= t b/Misc/NEWS.d/next/Library/2019-06-08-11-33-48.bpo-37173.0e_8gS.rst deleted file mode 100644 index e996f97ecddd..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-08-11-33-48.bpo-37173.0e_8gS.rst +++ /dev/null @@ -1 +0,0 @@ -The exception message for ``inspect.getfile()`` now correctly reports the pa= ssed class rather than the builtins module. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2019-06-08-16-03-19.bpo-34886.Ov-pc9.rs= t b/Misc/NEWS.d/next/Library/2019-06-08-16-03-19.bpo-34886.Ov-pc9.rst deleted file mode 100644 index 1b5fc3702521..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-08-16-03-19.bpo-34886.Ov-pc9.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix an unintended ValueError from :func:`subprocess.run` when checking for -conflicting `input` and `stdin` or `capture_output` and `stdout` or `stderr` -args when they were explicitly provided but with `None` values within a -passed in `**kwargs` dict rather than as passed directly by name. Patch -contributed by R=C3=A9mi Lapeyre. diff --git a/Misc/NEWS.d/next/Library/2019-06-11-00-35-02.bpo-36402.b0IJVp.rs= t b/Misc/NEWS.d/next/Library/2019-06-11-00-35-02.bpo-36402.b0IJVp.rst deleted file mode 100644 index 3bc537e40ff6..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-11-00-35-02.bpo-36402.b0IJVp.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix a race condition at Python shutdown when waiting for threads. Wait until -the Python thread state of all non-daemon threads get deleted (join all -non-daemon threads), rather than just wait until non-daemon Python threads -complete. diff --git a/Misc/NEWS.d/next/Library/2019-06-11-01-54-19.bpo-18748.ADqCkq.rs= t b/Misc/NEWS.d/next/Library/2019-06-11-01-54-19.bpo-18748.ADqCkq.rst deleted file mode 100644 index 295ddebb2a40..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-11-01-54-19.bpo-18748.ADqCkq.rst +++ /dev/null @@ -1,2 +0,0 @@ -:class:`_pyio.IOBase` destructor now does nothing if getting the ``closed`` -attribute fails to better mimick :class:`_io.IOBase` finalizer. diff --git a/Misc/NEWS.d/next/Library/2019-06-11-13-52-04.bpo-36607.5_mJkQ.rs= t b/Misc/NEWS.d/next/Library/2019-06-11-13-52-04.bpo-36607.5_mJkQ.rst deleted file mode 100644 index 337c8e2668c4..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-11-13-52-04.bpo-36607.5_mJkQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Eliminate :exc:`RuntimeError` raised by :func:`asyncio.all_tasks()` if -internal tasks weak set is changed by another thread during iteration. diff --git a/Misc/NEWS.d/next/Library/2019-06-11-16-41-40.bpo-35766.v1Kj-T.rs= t b/Misc/NEWS.d/next/Library/2019-06-11-16-41-40.bpo-35766.v1Kj-T.rst deleted file mode 100644 index 6dd49998cb4c..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-11-16-41-40.bpo-35766.v1Kj-T.rst +++ /dev/null @@ -1 +0,0 @@ -Change the format of feature_version to be a (major, minor) tuple. diff --git a/Misc/NEWS.d/next/Library/2019-06-11-19-34-29.bpo-35922.rxpzWr.rs= t b/Misc/NEWS.d/next/Library/2019-06-11-19-34-29.bpo-35922.rxpzWr.rst deleted file mode 100644 index 5271a495624d..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-11-19-34-29.bpo-35922.rxpzWr.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix :meth:`RobotFileParser.crawl_delay` and -:meth:`RobotFileParser.request_rate` to return ``None`` rather than -raise :exc:`AttributeError` when no relevant rule is defined in the -robots.txt file. Patch by R=C3=A9mi Lapeyre. diff --git a/Misc/NEWS.d/next/Library/2019-06-12-16-10-50.bpo-37210.r4yMg6.rs= t b/Misc/NEWS.d/next/Library/2019-06-12-16-10-50.bpo-37210.r4yMg6.rst deleted file mode 100644 index 58fc66b59059..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-12-16-10-50.bpo-37210.r4yMg6.rst +++ /dev/null @@ -1 +0,0 @@ -Allow pure Python implementation of :mod:`pickle` to work even when the C :m= od:`_pickle` module is unavailable. diff --git a/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rs= t b/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst deleted file mode 100644 index efd1f55c0135..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-14-08-30-16.bpo-19865.FRGH4I.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`ctypes.create_unicode_buffer()` now also supports non-BMP characters -on platforms with 16-bit :c:type:`wchar_t` (for example, Windows and AIX). diff --git a/Misc/NEWS.d/next/Library/2019-06-14-13-25-56.bpo-37279.OHlW6l.rs= t b/Misc/NEWS.d/next/Library/2019-06-14-13-25-56.bpo-37279.OHlW6l.rst deleted file mode 100644 index d740b9b62b08..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-14-13-25-56.bpo-37279.OHlW6l.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix asyncio sendfile support when sendfile sends extra data in fallback -mode. diff --git a/Misc/NEWS.d/next/Library/2019-06-14-13-30-47.bpo-37280.Fxur0F.rs= t b/Misc/NEWS.d/next/Library/2019-06-14-13-30-47.bpo-37280.Fxur0F.rst deleted file mode 100644 index 7cdc56a72ce4..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-14-13-30-47.bpo-37280.Fxur0F.rst +++ /dev/null @@ -1 +0,0 @@ -Use threadpool for reading from file for sendfile fallback mode. diff --git a/Misc/NEWS.d/next/Library/2019-06-15-14-39-50.bpo-33972.XxnNPw.rs= t b/Misc/NEWS.d/next/Library/2019-06-15-14-39-50.bpo-33972.XxnNPw.rst deleted file mode 100644 index ded1570b308f..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-15-14-39-50.bpo-33972.XxnNPw.rst +++ /dev/null @@ -1,2 +0,0 @@ -Email with single part but content-type set to ``multipart/*`` doesn't raise -AttributeError anymore. diff --git a/Misc/NEWS.d/next/Library/2019-06-25-02-10-00.bpo-37394.srZ1zx.rs= t b/Misc/NEWS.d/next/Library/2019-06-25-02-10-00.bpo-37394.srZ1zx.rst deleted file mode 100644 index 8d0d2fa311bc..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-25-02-10-00.bpo-37394.srZ1zx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug that was causing the :mod:`queue` module to fail if the -accelerator module was not available. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Library/2019-06-25-05-07-48.bpo-36546.RUcxaK.rs= t b/Misc/NEWS.d/next/Library/2019-06-25-05-07-48.bpo-36546.RUcxaK.rst deleted file mode 100644 index f6829fb1727e..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-25-05-07-48.bpo-36546.RUcxaK.rst +++ /dev/null @@ -1,4 +0,0 @@ -The *dist* argument for statistics.quantiles() is now positional only. The -current name doesn't reflect that the argument can be either a dataset or a -distribution. Marking the parameter as positional avoids confusion and -makes it possible to change the name later. diff --git a/Misc/NEWS.d/next/Library/2019-06-25-19-27-25.bpo-29412.n4Zqdh.rs= t b/Misc/NEWS.d/next/Library/2019-06-25-19-27-25.bpo-29412.n4Zqdh.rst deleted file mode 100644 index b8fac4673686..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-25-19-27-25.bpo-29412.n4Zqdh.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix IndexError in parsing a header value ending unexpectedly. Patch by Abhil= ash -Raj. diff --git a/Misc/NEWS.d/next/Library/2019-06-26-16-28-59.bpo-37412.lx0VjC.rs= t b/Misc/NEWS.d/next/Library/2019-06-26-16-28-59.bpo-37412.lx0VjC.rst deleted file mode 100644 index 3ce4102129f0..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-26-16-28-59.bpo-37412.lx0VjC.rst +++ /dev/null @@ -1,3 +0,0 @@ -The :func:`os.getcwdb` function now uses the UTF-8 encoding on Windows, -rather than the ANSI code page: see :pep:`529` for the rationale. The functi= on -is no longer deprecated on Windows. diff --git a/Misc/NEWS.d/next/Library/2019-06-26-22-25-05.bpo-37420.CxFJ09.rs= t b/Misc/NEWS.d/next/Library/2019-06-26-22-25-05.bpo-37420.CxFJ09.rst deleted file mode 100644 index dea1a2925014..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-26-22-25-05.bpo-37420.CxFJ09.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`os.sched_setaffinity` now correctly handles errors that arise during = iteration over its ``mask`` argument. -Patch by Brandt Bucher. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rs= t b/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst deleted file mode 100644 index 2cdce6b24dc6..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-27-13-27-02.bpo-37428._wcwUd.rst +++ /dev/null @@ -1,4 +0,0 @@ -SSLContext.post_handshake_auth =3D True no longer sets -SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the -option is documented as ignored for clients, OpenSSL implicitly enables cert -chain validation when the flag is set. diff --git a/Misc/NEWS.d/next/Library/2019-06-27-20-33-50.bpo-37437.du39_A.rs= t b/Misc/NEWS.d/next/Library/2019-06-27-20-33-50.bpo-37437.du39_A.rst deleted file mode 100644 index 80719ee152d7..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-27-20-33-50.bpo-37437.du39_A.rst +++ /dev/null @@ -1 +0,0 @@ -Update vendorized expat version to 2.2.7. diff --git a/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rs= t b/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst deleted file mode 100644 index b336e061e15e..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-28-16-40-17.bpo-37440.t3wX-N.rst +++ /dev/null @@ -1,2 +0,0 @@ -http.client now enables TLS 1.3 post-handshake authentication for default -context or if a cert_file is passed to HTTPSConnection. diff --git a/Misc/NEWS.d/next/Security/2019-06-17-09-34-25.bpo-34631.DBfM4j.r= st b/Misc/NEWS.d/next/Security/2019-06-17-09-34-25.bpo-34631.DBfM4j.rst deleted file mode 100644 index 90aa30192fc8..000000000000 --- a/Misc/NEWS.d/next/Security/2019-06-17-09-34-25.bpo-34631.DBfM4j.rst +++ /dev/null @@ -1 +0,0 @@ -Updated OpenSSL to 1.1.1c in Windows installer diff --git a/Misc/NEWS.d/next/Security/2019-06-21-14-42-53.bpo-37364.IIRc2s.r= st b/Misc/NEWS.d/next/Security/2019-06-21-14-42-53.bpo-37364.IIRc2s.rst deleted file mode 100644 index 7506fa9064b1..000000000000 --- a/Misc/NEWS.d/next/Security/2019-06-21-14-42-53.bpo-37364.IIRc2s.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`io.open_code` is now used when reading :file:`.pth` files. diff --git a/Misc/NEWS.d/next/Security/2019-06-21-15-58-59.bpo-37363.diouyl.r= st b/Misc/NEWS.d/next/Security/2019-06-21-15-58-59.bpo-37363.diouyl.rst deleted file mode 100644 index 1b724ff559e2..000000000000 --- a/Misc/NEWS.d/next/Security/2019-06-21-15-58-59.bpo-37363.diouyl.rst +++ /dev/null @@ -1,5 +0,0 @@ -Adds audit events for :mod:`ensurepip`, :mod:`ftplib`, :mod:`glob`, -:mod:`imaplib`, :mod:`nntplib`, :mod:`pdb`, :mod:`poplib`, :mod:`shutil`, -:mod:`smtplib`, :mod:`sqlite3`, :mod:`subprocess`, :mod:`telnetlib`, -:mod:`tempfile` and :mod:`webbrowser`, as well as :func:`os.listdir`, -:func:`os.scandir` and :func:`breakpoint`. diff --git a/Misc/NEWS.d/next/Security/2019-07-01-08-46-14.bpo-37463.1CHwjE.r= st b/Misc/NEWS.d/next/Security/2019-07-01-08-46-14.bpo-37463.1CHwjE.rst deleted file mode 100644 index 4f4a62e78374..000000000000 --- a/Misc/NEWS.d/next/Security/2019-07-01-08-46-14.bpo-37463.1CHwjE.rst +++ /dev/null @@ -1,4 +0,0 @@ -ssl.match_hostname() no longer accepts IPv4 addresses with additional text -after the address and only quad-dotted notation without trailing -whitespaces. Some inet_aton() implementations ignore whitespace and all data -after whitespace, e.g. '127.0.0.1 whatever'. diff --git a/Misc/NEWS.d/next/Security/2019-07-01-10-31-14.bpo-37363.fSjatj.r= st b/Misc/NEWS.d/next/Security/2019-07-01-10-31-14.bpo-37363.fSjatj.rst deleted file mode 100644 index a8bde90db496..000000000000 --- a/Misc/NEWS.d/next/Security/2019-07-01-10-31-14.bpo-37363.fSjatj.rst +++ /dev/null @@ -1,2 +0,0 @@ -Adds audit events for the range of supported run commands (see -:ref:`using-on-general`). diff --git a/Misc/NEWS.d/next/Tests/2019-04-11-07-59-43.bpo-28009.s85urF.rst = b/Misc/NEWS.d/next/Tests/2019-04-11-07-59-43.bpo-28009.s85urF.rst deleted file mode 100644 index 233640716d15..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-04-11-07-59-43.bpo-28009.s85urF.rst +++ /dev/null @@ -1,3 +0,0 @@ -Modify the test_uuid logic to test when a program is available -AND can be used to obtain a MACADDR as basis for an UUID. -Patch by M. Felt diff --git a/Misc/NEWS.d/next/Tests/2019-06-04-18-30-39.bpo-37153.711INB.rst = b/Misc/NEWS.d/next/Tests/2019-06-04-18-30-39.bpo-37153.711INB.rst deleted file mode 100644 index 138a22f6acc2..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-04-18-30-39.bpo-37153.711INB.rst +++ /dev/null @@ -1,2 +0,0 @@ -``test_venv.test_mutiprocessing()`` now explicitly calls -``pool.terminate()`` to wait until the pool completes. diff --git a/Misc/NEWS.d/next/Tests/2019-06-07-12-23-15.bpo-37169.yfXTFg.rst = b/Misc/NEWS.d/next/Tests/2019-06-07-12-23-15.bpo-37169.yfXTFg.rst deleted file mode 100644 index f2f0a8b8d8ea..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-07-12-23-15.bpo-37169.yfXTFg.rst +++ /dev/null @@ -1 +0,0 @@ -Rewrite ``_PyObject_IsFreed()`` unit tests. diff --git a/Misc/NEWS.d/next/Tests/2019-06-13-12-19-56.bpo-37261.NuKFVo.rst = b/Misc/NEWS.d/next/Tests/2019-06-13-12-19-56.bpo-37261.NuKFVo.rst deleted file mode 100644 index 27ce78a70f2b..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-13-12-19-56.bpo-37261.NuKFVo.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix :func:`test.support.catch_unraisable_exception`: its __exit__() method -now ignores unraisable exception raised when clearing its ``unraisable`` -attribute. diff --git a/Misc/NEWS.d/next/Tests/2019-06-14-12-21-47.bpo-37278.z0HUOr.rst = b/Misc/NEWS.d/next/Tests/2019-06-14-12-21-47.bpo-37278.z0HUOr.rst deleted file mode 100644 index 3d3011b51c5b..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-14-12-21-47.bpo-37278.z0HUOr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix test_asyncio ProactorLoopCtrlC: join the thread to prevent leaking a -running thread and leaking a reference. diff --git a/Misc/NEWS.d/next/Tests/2019-06-14-17-05-49.bpo-35998.yX82oD.rst = b/Misc/NEWS.d/next/Tests/2019-06-14-17-05-49.bpo-35998.yX82oD.rst deleted file mode 100644 index 23b6d00f42c5..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-14-17-05-49.bpo-35998.yX82oD.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid TimeoutError in test_asyncio: test_start_tls_server_1() diff --git a/Misc/NEWS.d/next/Tests/2019-06-21-15-47-33.bpo-37362.D3xppx.rst = b/Misc/NEWS.d/next/Tests/2019-06-21-15-47-33.bpo-37362.D3xppx.rst deleted file mode 100644 index 43fdc1030c57..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-21-15-47-33.bpo-37362.D3xppx.rst +++ /dev/null @@ -1,3 +0,0 @@ -test_gdb no longer fails if it gets an "unexpected" message on stderr: it now -ignores stderr. The purpose of test_gdb is to test that python-gdb.py comman= ds -work as expected, not to test gdb. diff --git a/Misc/NEWS.d/next/Tests/2019-06-24-10-47-07.bpo-37359.CkdtyO.rst = b/Misc/NEWS.d/next/Tests/2019-06-24-10-47-07.bpo-37359.CkdtyO.rst deleted file mode 100644 index 3d5350de4f43..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-24-10-47-07.bpo-37359.CkdtyO.rst +++ /dev/null @@ -1,4 +0,0 @@ -Add --cleanup option to python3 -m test to remove ``test_python_*`` -directories of previous failed jobs. Add "make cleantest" to run -``python3 -m test --cleanup``. - diff --git a/Misc/NEWS.d/next/Tests/2019-06-25-16-02-43.bpo-37400.cx_EWv.rst = b/Misc/NEWS.d/next/Tests/2019-06-25-16-02-43.bpo-37400.cx_EWv.rst deleted file mode 100644 index 737c78189009..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-25-16-02-43.bpo-37400.cx_EWv.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix test_os.test_chown(): use os.getgroups() rather than grp.getgrall() -to get groups. Rename also the test to test_chown_gid(). diff --git a/Misc/NEWS.d/next/Tests/2019-06-26-15-28-45.bpo-37411.5lGNhM.rst = b/Misc/NEWS.d/next/Tests/2019-06-26-15-28-45.bpo-37411.5lGNhM.rst deleted file mode 100644 index 20e52d3c74f7..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-26-15-28-45.bpo-37411.5lGNhM.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix test_wsgiref.testEnviron() to no longer depend on the environment -variables (don't fail if "X" variable is set). diff --git a/Misc/NEWS.d/next/Tests/2019-06-27-00-37-59.bpo-37421.rVJb3x.rst = b/Misc/NEWS.d/next/Tests/2019-06-27-00-37-59.bpo-37421.rVJb3x.rst deleted file mode 100644 index 9f4033831d68..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-27-00-37-59.bpo-37421.rVJb3x.rst +++ /dev/null @@ -1 +0,0 @@ -Fix test_shutil to no longer leak temporary files. diff --git a/Misc/NEWS.d/next/Tests/2019-06-28-16-37-52.bpo-37335.o5S2hY.rst = b/Misc/NEWS.d/next/Tests/2019-06-28-16-37-52.bpo-37335.o5S2hY.rst deleted file mode 100644 index cb884d926590..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-28-16-37-52.bpo-37335.o5S2hY.rst +++ /dev/null @@ -1 +0,0 @@ -Remove no longer necessary code from c locale coercion tests diff --git a/Misc/NEWS.d/next/Tests/2019-06-29-23-56-28.bpo-37199.FHDsLf.rst = b/Misc/NEWS.d/next/Tests/2019-06-29-23-56-28.bpo-37199.FHDsLf.rst deleted file mode 100644 index b05209159cc8..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-06-29-23-56-28.bpo-37199.FHDsLf.rst +++ /dev/null @@ -1 +0,0 @@ -Fix test failures when IPv6 is unavailable or disabled. diff --git a/Misc/NEWS.d/next/Tests/2019-07-01-19-57-26.bpo-37421.NFH1f0.rst = b/Misc/NEWS.d/next/Tests/2019-07-01-19-57-26.bpo-37421.NFH1f0.rst deleted file mode 100644 index c379b504ba8a..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-07-01-19-57-26.bpo-37421.NFH1f0.rst +++ /dev/null @@ -1,2 +0,0 @@ -multiprocessing tests now explicitly call ``_run_finalizers()`` to -immediately remove temporary directories created by tests. diff --git a/Misc/NEWS.d/next/Tests/2019-07-02-23-20-35.bpo-37421.HCkKWz.rst = b/Misc/NEWS.d/next/Tests/2019-07-02-23-20-35.bpo-37421.HCkKWz.rst deleted file mode 100644 index 6671ffe922fd..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-07-02-23-20-35.bpo-37421.HCkKWz.rst +++ /dev/null @@ -1,2 +0,0 @@ -test_winconsoleio doesn't leak a temporary file anymore: use -tempfile.TemporaryFile() to remove it when the test completes. diff --git a/Misc/NEWS.d/next/Tests/2019-07-02-23-29-06.bpo-37421.WEfc5A.rst = b/Misc/NEWS.d/next/Tests/2019-07-02-23-29-06.bpo-37421.WEfc5A.rst deleted file mode 100644 index 215a0a144459..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-07-02-23-29-06.bpo-37421.WEfc5A.rst +++ /dev/null @@ -1,2 +0,0 @@ -test_concurrent_futures now cleans up multiprocessing to remove immediately -temporary directories created by multiprocessing.util.get_temp_dir(). diff --git a/Misc/NEWS.d/next/Tests/2019-07-03-00-05-28.bpo-37421.ORGRSG.rst = b/Misc/NEWS.d/next/Tests/2019-07-03-00-05-28.bpo-37421.ORGRSG.rst deleted file mode 100644 index 8610509e4b72..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-07-03-00-05-28.bpo-37421.ORGRSG.rst +++ /dev/null @@ -1,3 +0,0 @@ -test_distutils.test_build_ext() is now able to remove the temporary -directory on Windows: don't import the newly built C extension ("xx") in the -current process, but test it in a separated process. diff --git a/Misc/NEWS.d/next/Windows/2019-06-11-15-41-34.bpo-36779.0TMw6f.rs= t b/Misc/NEWS.d/next/Windows/2019-06-11-15-41-34.bpo-36779.0TMw6f.rst deleted file mode 100644 index 618cfcae7b89..000000000000 --- a/Misc/NEWS.d/next/Windows/2019-06-11-15-41-34.bpo-36779.0TMw6f.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure ``time.tzname`` is correct on Windows when the active code page is -set to CP_UTF7 or CP_UTF8. diff --git a/Misc/NEWS.d/next/Windows/2019-06-13-04-15-51.bpo-37267.Ygo5ef.rs= t b/Misc/NEWS.d/next/Windows/2019-06-13-04-15-51.bpo-37267.Ygo5ef.rst deleted file mode 100644 index a4dcfcde35b0..000000000000 --- a/Misc/NEWS.d/next/Windows/2019-06-13-04-15-51.bpo-37267.Ygo5ef.rst +++ /dev/null @@ -1,2 +0,0 @@ -On Windows, :func:`os.dup` no longer creates an inheritable fd when handling -a character file. diff --git a/Misc/NEWS.d/next/Windows/2019-06-18-09-05-08.bpo-35360.tdqSmo.rs= t b/Misc/NEWS.d/next/Windows/2019-06-18-09-05-08.bpo-35360.tdqSmo.rst deleted file mode 100644 index 8fea56a00fdc..000000000000 --- a/Misc/NEWS.d/next/Windows/2019-06-18-09-05-08.bpo-35360.tdqSmo.rst +++ /dev/null @@ -1 +0,0 @@ -Update Windows builds to use SQLite 3.28.0. diff --git a/Misc/NEWS.d/next/Windows/2019-06-20-12-50-32.bpo-37351.asTnVW.rs= t b/Misc/NEWS.d/next/Windows/2019-06-20-12-50-32.bpo-37351.asTnVW.rst deleted file mode 100644 index df2efd4a9a41..000000000000 --- a/Misc/NEWS.d/next/Windows/2019-06-20-12-50-32.bpo-37351.asTnVW.rst +++ /dev/null @@ -1 +0,0 @@ -Removes libpython38.a from standard Windows distribution. diff --git a/Misc/NEWS.d/next/Windows/2019-06-28-09-44-08.bpo-37369.1iVpxq.rs= t b/Misc/NEWS.d/next/Windows/2019-06-28-09-44-08.bpo-37369.1iVpxq.rst deleted file mode 100644 index 5eaed61a9261..000000000000 --- a/Misc/NEWS.d/next/Windows/2019-06-28-09-44-08.bpo-37369.1iVpxq.rst +++ /dev/null @@ -1 +0,0 @@ -Fixes path for :data:`sys.executable` when running from the Microsoft Store. diff --git a/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rs= t b/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst deleted file mode 100644 index d39576545efa..000000000000 --- a/Misc/NEWS.d/next/Windows/2019-07-01-12-38-48.bpo-10945.s0YBHG.rst +++ /dev/null @@ -1,2 +0,0 @@ -Officially drop support for creating bdist_wininst installers on non-Windows -systems. diff --git a/Misc/NEWS.d/next/macOS/2019-06-18-00-30-40.bpo-34631.vSifcv.rst = b/Misc/NEWS.d/next/macOS/2019-06-18-00-30-40.bpo-34631.vSifcv.rst deleted file mode 100644 index 164950a37c83..000000000000 --- a/Misc/NEWS.d/next/macOS/2019-06-18-00-30-40.bpo-34631.vSifcv.rst +++ /dev/null @@ -1 +0,0 @@ -Updated OpenSSL to 1.1.1c in macOS installer. diff --git a/Misc/NEWS.d/next/macOS/2019-06-18-08-58-30.bpo-35360.-CWbfy.rst = b/Misc/NEWS.d/next/macOS/2019-06-18-08-58-30.bpo-35360.-CWbfy.rst deleted file mode 100644 index 5e05ac56b095..000000000000 --- a/Misc/NEWS.d/next/macOS/2019-06-18-08-58-30.bpo-35360.-CWbfy.rst +++ /dev/null @@ -1 +0,0 @@ -Update macOS installer to use SQLite 3.28.0. diff --git a/Misc/NEWS.d/next/macOS/2019-07-02-01-06-47.bpo-34602.10d4wl.rst = b/Misc/NEWS.d/next/macOS/2019-07-02-01-06-47.bpo-34602.10d4wl.rst deleted file mode 100644 index c50ac638df57..000000000000 --- a/Misc/NEWS.d/next/macOS/2019-07-02-01-06-47.bpo-34602.10d4wl.rst +++ /dev/null @@ -1,4 +0,0 @@ -Avoid test suite failures on macOS by no longer calling resource.setrlimit -to increase the process stack size limit at runtime. The runtime change is -no longer needed since the interpreter is being built with a larger default -stack size. diff --git a/README.rst b/README.rst index b0a0ce7ec287..e1d89b3d811a 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.8.0 beta 1 +This is Python version 3.8.0 beta 2 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 .. image:: https://travis-ci.org/python/cpython.svg?branch=3Dmaster From webhook-mailer at python.org Fri Jul 12 17:22:26 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 12 Jul 2019 21:22:26 -0000 Subject: [Python-checkins] bpo-19696: Move threaded_import_hangers (GH-14655) Message-ID: https://github.com/python/cpython/commit/a65c977552507dd19d2cc073fc91ae22cc66bbff commit: a65c977552507dd19d2cc073fc91ae22cc66bbff branch: master author: Kyle Stanley committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-12T14:22:05-07:00 summary: bpo-19696: Move threaded_import_hangers (GH-14655) Move `threaded_import_hangers`, a dependency of `test_threaded_import`, to the directory `test_importlib/`. Also update the import references for `threaded_import_hangers` in `test_threaded_import`. https://bugs.python.org/issue19696 files: A Lib/test/test_importlib/threaded_import_hangers.py D Lib/test/threaded_import_hangers.py M Lib/test/test_importlib/test_threaded_import.py diff --git a/Lib/test/test_importlib/test_threaded_import.py b/Lib/test/test_importlib/test_threaded_import.py index 8607f363db21..d1f64c70fac8 100644 --- a/Lib/test/test_importlib/test_threaded_import.py +++ b/Lib/test/test_importlib/test_threaded_import.py @@ -178,11 +178,11 @@ def test_import_hangers(self): # In case this test is run again, make sure the helper module # gets loaded from scratch again. try: - del sys.modules['test.threaded_import_hangers'] + del sys.modules['test.test_importlib.threaded_import_hangers'] except KeyError: pass - import test.threaded_import_hangers - self.assertFalse(test.threaded_import_hangers.errors) + import test.test_importlib.threaded_import_hangers + self.assertFalse(test.test_importlib.threaded_import_hangers.errors) def test_circular_imports(self): # The goal of this test is to exercise implementations of the import diff --git a/Lib/test/threaded_import_hangers.py b/Lib/test/test_importlib/threaded_import_hangers.py similarity index 100% rename from Lib/test/threaded_import_hangers.py rename to Lib/test/test_importlib/threaded_import_hangers.py From webhook-mailer at python.org Fri Jul 12 18:35:43 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 12 Jul 2019 22:35:43 -0000 Subject: [Python-checkins] bpo-37521: No longer treat insertion into sys.modules as optional in importlib examples (GH-14723) Message-ID: https://github.com/python/cpython/commit/0827064c955f88df8ba5621d8e3d81be3cfc26a9 commit: 0827064c955f88df8ba5621d8e3d81be3cfc26a9 branch: master author: Brett Cannon <54418+brettcannon at users.noreply.github.com> committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-12T15:35:34-07:00 summary: bpo-37521: No longer treat insertion into sys.modules as optional in importlib examples (GH-14723) Fix importlib examples to insert any newly created modules via importlib.util.module_from_spec() immediately into sys.modules instead of after calling loader.exec_module(). Thanks to Benjamin Mintz for finding the bug. https://bugs.python.org/issue37521 files: A Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst M Doc/library/importlib.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index df184b33d0e7..9fab0779aa74 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1638,15 +1638,16 @@ import, then you should use :func:`importlib.util.find_spec`. # For illustrative purposes. name = 'itertools' - spec = importlib.util.find_spec(name) - if spec is None: - print("can't find the itertools module") + if name in sys.modules: + print(f"{name!r} already in sys.modules") + elif (spec := importlib.util.find_spec(name)) is None: + print(f"can't find the {name!r} module") else: # If you chose to perform the actual import ... module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - # Adding the module to sys.modules is optional. sys.modules[name] = module + spec.loader.exec_module(module) + print(f"{name!r} has been imported") Importing a source file directly @@ -1665,10 +1666,9 @@ To import a Python source file directly, use the following recipe spec = importlib.util.spec_from_file_location(module_name, file_path) module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - # Optional; only necessary if you want to be able to import the module - # by name later. sys.modules[module_name] = module + spec.loader.exec_module(module) + Setting up an importer @@ -1740,8 +1740,8 @@ Python 3.6 and newer for other parts of the code). msg = f'No module named {absolute_name!r}' raise ModuleNotFoundError(msg, name=absolute_name) module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) sys.modules[absolute_name] = module + spec.loader.exec_module(module) if path is not None: setattr(parent_module, child_name, module) return module diff --git a/Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst b/Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst new file mode 100644 index 000000000000..35d7f56f732f --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst @@ -0,0 +1,5 @@ +Fix `importlib` examples to insert any newly created modules via +importlib.util.module_from_spec() immediately into sys.modules instead of +after calling loader.exec_module(). + +Thanks to Benjamin Mintz for finding the bug. From webhook-mailer at python.org Fri Jul 12 18:51:52 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 12 Jul 2019 22:51:52 -0000 Subject: [Python-checkins] [3.8] bpo-37521: No longer treat insertion into sys.modules as optional in importlib examples (GH-14723) (GH-14724) Message-ID: https://github.com/python/cpython/commit/bfb709b771230e7e466961b5ddf4852962602450 commit: bfb709b771230e7e466961b5ddf4852962602450 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-12T15:51:48-07:00 summary: [3.8] bpo-37521: No longer treat insertion into sys.modules as optional in importlib examples (GH-14723) (GH-14724) Fix importlib examples to insert any newly created modules via importlib.util.module_from_spec() immediately into sys.modules instead of after calling loader.exec_module(). Thanks to Benjamin Mintz for finding the bug. https://bugs.python.org/issue37521 (cherry picked from commit 0827064c955f88df8ba5621d8e3d81be3cfc26a9) Co-authored-by: Brett Cannon <54418+brettcannon at users.noreply.github.com> https://bugs.python.org/issue37521 files: A Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst M Doc/library/importlib.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index df184b33d0e7..9fab0779aa74 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1638,15 +1638,16 @@ import, then you should use :func:`importlib.util.find_spec`. # For illustrative purposes. name = 'itertools' - spec = importlib.util.find_spec(name) - if spec is None: - print("can't find the itertools module") + if name in sys.modules: + print(f"{name!r} already in sys.modules") + elif (spec := importlib.util.find_spec(name)) is None: + print(f"can't find the {name!r} module") else: # If you chose to perform the actual import ... module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - # Adding the module to sys.modules is optional. sys.modules[name] = module + spec.loader.exec_module(module) + print(f"{name!r} has been imported") Importing a source file directly @@ -1665,10 +1666,9 @@ To import a Python source file directly, use the following recipe spec = importlib.util.spec_from_file_location(module_name, file_path) module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - # Optional; only necessary if you want to be able to import the module - # by name later. sys.modules[module_name] = module + spec.loader.exec_module(module) + Setting up an importer @@ -1740,8 +1740,8 @@ Python 3.6 and newer for other parts of the code). msg = f'No module named {absolute_name!r}' raise ModuleNotFoundError(msg, name=absolute_name) module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) sys.modules[absolute_name] = module + spec.loader.exec_module(module) if path is not None: setattr(parent_module, child_name, module) return module diff --git a/Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst b/Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst new file mode 100644 index 000000000000..35d7f56f732f --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst @@ -0,0 +1,5 @@ +Fix `importlib` examples to insert any newly created modules via +importlib.util.module_from_spec() immediately into sys.modules instead of +after calling loader.exec_module(). + +Thanks to Benjamin Mintz for finding the bug. From webhook-mailer at python.org Fri Jul 12 23:16:13 2019 From: webhook-mailer at python.org (Benjamin Peterson) Date: Sat, 13 Jul 2019 03:16:13 -0000 Subject: [Python-checkins] closes bpo-37347: Fix refcount problem in sqlite3. (GH-14268) Message-ID: https://github.com/python/cpython/commit/b9a0376b0dedf16a2f82fa43d851119d1f7a2707 commit: b9a0376b0dedf16a2f82fa43d851119d1f7a2707 branch: master author: gescheit committer: Benjamin Peterson date: 2019-07-12T20:15:48-07:00 summary: closes bpo-37347: Fix refcount problem in sqlite3. (GH-14268) files: A Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst M Lib/sqlite3/test/regression.py M Misc/ACKS M Modules/_sqlite/connection.c M Modules/_sqlite/connection.h M setup.py diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index ee326168bc12..c714116ac492 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -25,6 +25,7 @@ import unittest import sqlite3 as sqlite import weakref +import functools from test import support class RegressionTests(unittest.TestCase): @@ -383,72 +384,26 @@ def CheckDelIsolation_levelSegfault(self): with self.assertRaises(AttributeError): del self.con.isolation_level + def CheckBpo37347(self): + class Printer: + def log(self, *args): + return sqlite.SQLITE_OK -class UnhashableFunc: - __hash__ = None + for method in [self.con.set_trace_callback, + functools.partial(self.con.set_progress_handler, n=1), + self.con.set_authorizer]: + printer_instance = Printer() + method(printer_instance.log) + method(printer_instance.log) + self.con.execute("select 1") # trigger seg fault + method(None) - def __init__(self, return_value=None): - self.calls = 0 - self.return_value = return_value - - def __call__(self, *args, **kwargs): - self.calls += 1 - return self.return_value - - -class UnhashableCallbacksTestCase(unittest.TestCase): - """ - https://bugs.python.org/issue34052 - - Registering unhashable callbacks raises TypeError, callbacks are not - registered in SQLite after such registration attempt. - """ - def setUp(self): - self.con = sqlite.connect(':memory:') - - def tearDown(self): - self.con.close() - - def test_progress_handler(self): - f = UnhashableFunc(return_value=0) - with self.assertRaisesRegex(TypeError, 'unhashable type'): - self.con.set_progress_handler(f, 1) - self.con.execute('SELECT 1') - self.assertFalse(f.calls) - - def test_func(self): - func_name = 'func_name' - f = UnhashableFunc() - with self.assertRaisesRegex(TypeError, 'unhashable type'): - self.con.create_function(func_name, 0, f) - msg = 'no such function: %s' % func_name - with self.assertRaisesRegex(sqlite.OperationalError, msg): - self.con.execute('SELECT %s()' % func_name) - self.assertFalse(f.calls) - - def test_authorizer(self): - f = UnhashableFunc(return_value=sqlite.SQLITE_DENY) - with self.assertRaisesRegex(TypeError, 'unhashable type'): - self.con.set_authorizer(f) - self.con.execute('SELECT 1') - self.assertFalse(f.calls) - - def test_aggr(self): - class UnhashableType(type): - __hash__ = None - aggr_name = 'aggr_name' - with self.assertRaisesRegex(TypeError, 'unhashable type'): - self.con.create_aggregate(aggr_name, 0, UnhashableType('Aggr', (), {})) - msg = 'no such function: %s' % aggr_name - with self.assertRaisesRegex(sqlite.OperationalError, msg): - self.con.execute('SELECT %s()' % aggr_name) def suite(): regression_suite = unittest.makeSuite(RegressionTests, "Check") return unittest.TestSuite(( regression_suite, - unittest.makeSuite(UnhashableCallbacksTestCase), )) def test(): diff --git a/Misc/ACKS b/Misc/ACKS index d916c45a8e44..c0119992cffe 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1870,3 +1870,4 @@ Diego Rojas Edison Abahurire Geoff Shannon Batuhan Taskaya +Aleksandr Balezin diff --git a/Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst b/Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst new file mode 100644 index 000000000000..1e61f5e0b3db --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst @@ -0,0 +1,6 @@ +:meth:`sqlite3.Connection.create_aggregate`, +:meth:`sqlite3.Connection.create_function`, +:meth:`sqlite3.Connection.set_authorizer`, +:meth:`sqlite3.Connection.set_progress_handler` +:meth:`sqlite3.Connection.set_trace_callback` +methods lead to segfaults if some of these methods are called twice with an equal object but not the same. Now callbacks are stored more carefully. Patch by Aleksandr Balezin. \ No newline at end of file diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 47b8b62cea26..ae03aed8f0b8 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -186,10 +186,9 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject } self->check_same_thread = check_same_thread; - Py_XSETREF(self->function_pinboard, PyDict_New()); - if (!self->function_pinboard) { - return -1; - } + self->function_pinboard_trace_callback = NULL; + self->function_pinboard_progress_handler = NULL; + self->function_pinboard_authorizer_cb = NULL; Py_XSETREF(self->collations, PyDict_New()); if (!self->collations) { @@ -249,19 +248,18 @@ void pysqlite_connection_dealloc(pysqlite_Connection* self) /* Clean up if user has not called .close() explicitly. */ if (self->db) { - Py_BEGIN_ALLOW_THREADS SQLITE3_CLOSE(self->db); - Py_END_ALLOW_THREADS } Py_XDECREF(self->isolation_level); - Py_XDECREF(self->function_pinboard); + Py_XDECREF(self->function_pinboard_trace_callback); + Py_XDECREF(self->function_pinboard_progress_handler); + Py_XDECREF(self->function_pinboard_authorizer_cb); Py_XDECREF(self->row_factory); Py_XDECREF(self->text_factory); Py_XDECREF(self->collations); Py_XDECREF(self->statements); Py_XDECREF(self->cursors); - Py_TYPE(self)->tp_free((PyObject*)self); } @@ -342,9 +340,7 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args) pysqlite_do_all_statements(self, ACTION_FINALIZE, 1); if (self->db) { - Py_BEGIN_ALLOW_THREADS rc = SQLITE3_CLOSE(self->db); - Py_END_ALLOW_THREADS if (rc != SQLITE_OK) { _pysqlite_seterror(self->db, NULL); @@ -808,6 +804,11 @@ static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self) Py_SETREF(self->cursors, new_list); } +static void _destructor(void* args) +{ + Py_DECREF((PyObject*)args); +} + PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) { static char *kwlist[] = {"name", "narg", "func", "deterministic", NULL}; @@ -843,17 +844,16 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec flags |= SQLITE_DETERMINISTIC; #endif } - if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) { - return NULL; - } - rc = sqlite3_create_function(self->db, - name, - narg, - flags, - (void*)func, - _pysqlite_func_callback, - NULL, - NULL); + Py_INCREF(func); + rc = sqlite3_create_function_v2(self->db, + name, + narg, + flags, + (void*)func, + _pysqlite_func_callback, + NULL, + NULL, + &_destructor); // will decref func if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ @@ -880,11 +880,16 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje kwlist, &name, &n_arg, &aggregate_class)) { return NULL; } - - if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) { - return NULL; - } - rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_pysqlite_step_callback, &_pysqlite_final_callback); + Py_INCREF(aggregate_class); + rc = sqlite3_create_function_v2(self->db, + name, + n_arg, + SQLITE_UTF8, + (void*)aggregate_class, + 0, + &_pysqlite_step_callback, + &_pysqlite_final_callback, + &_destructor); // will decref func if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate"); @@ -1003,13 +1008,14 @@ static PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, P return NULL; } - if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) { - return NULL; - } rc = sqlite3_set_authorizer(self->db, _authorizer_callback, (void*)authorizer_cb); if (rc != SQLITE_OK) { PyErr_SetString(pysqlite_OperationalError, "Error setting authorizer callback"); + Py_XSETREF(self->function_pinboard_authorizer_cb, NULL); return NULL; + } else { + Py_INCREF(authorizer_cb); + Py_XSETREF(self->function_pinboard_authorizer_cb, authorizer_cb); } Py_RETURN_NONE; } @@ -1033,12 +1039,12 @@ static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* s if (progress_handler == Py_None) { /* None clears the progress handler previously set */ sqlite3_progress_handler(self->db, 0, 0, (void*)0); + Py_XSETREF(self->function_pinboard_progress_handler, NULL); } else { - if (PyDict_SetItem(self->function_pinboard, progress_handler, Py_None) == -1) - return NULL; sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); + Py_INCREF(progress_handler); + Py_XSETREF(self->function_pinboard_progress_handler, progress_handler); } - Py_RETURN_NONE; } @@ -1060,10 +1066,11 @@ static PyObject* pysqlite_connection_set_trace_callback(pysqlite_Connection* sel if (trace_callback == Py_None) { /* None clears the trace callback previously set */ sqlite3_trace(self->db, 0, (void*)0); + Py_XSETREF(self->function_pinboard_trace_callback, NULL); } else { - if (PyDict_SetItem(self->function_pinboard, trace_callback, Py_None) == -1) - return NULL; sqlite3_trace(self->db, _trace_callback, trace_callback); + Py_INCREF(trace_callback); + Py_XSETREF(self->function_pinboard_trace_callback, trace_callback); } Py_RETURN_NONE; diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h index 4e9d94c5f308..206085e00a00 100644 --- a/Modules/_sqlite/connection.h +++ b/Modules/_sqlite/connection.h @@ -85,11 +85,10 @@ typedef struct */ PyObject* text_factory; - /* remember references to functions/classes used in - * create_function/create/aggregate, use these as dictionary keys, so we - * can keep the total system refcount constant by clearing that dictionary - * in connection_dealloc */ - PyObject* function_pinboard; + /* remember references to object used in trace_callback/progress_handler/authorizer_cb */ + PyObject* function_pinboard_trace_callback; + PyObject* function_pinboard_progress_handler; + PyObject* function_pinboard_authorizer_cb; /* a dictionary of registered collation name => collation callable mappings */ PyObject* collations; diff --git a/setup.py b/setup.py index 3ec89cedfd57..7cd77257ae35 100644 --- a/setup.py +++ b/setup.py @@ -1357,7 +1357,7 @@ def detect_sqlite(self): ] if CROSS_COMPILING: sqlite_inc_paths = [] - MIN_SQLITE_VERSION_NUMBER = (3, 3, 9) + MIN_SQLITE_VERSION_NUMBER = (3, 7, 2) MIN_SQLITE_VERSION = ".".join([str(x) for x in MIN_SQLITE_VERSION_NUMBER]) From webhook-mailer at python.org Fri Jul 12 23:33:58 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 03:33:58 -0000 Subject: [Python-checkins] closes bpo-37347: Fix refcount problem in sqlite3. (GH-14268) Message-ID: https://github.com/python/cpython/commit/36101c2c5daf692d1716e17720c6c5197f28e25d commit: 36101c2c5daf692d1716e17720c6c5197f28e25d branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-12T20:33:53-07:00 summary: closes bpo-37347: Fix refcount problem in sqlite3. (GH-14268) (cherry picked from commit b9a0376b0dedf16a2f82fa43d851119d1f7a2707) Co-authored-by: gescheit files: A Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst M Lib/sqlite3/test/regression.py M Misc/ACKS M Modules/_sqlite/connection.c M Modules/_sqlite/connection.h M setup.py diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 865bd88f74f1..dd5aec50d186 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -25,6 +25,7 @@ import unittest import sqlite3 as sqlite import weakref +import functools from test import support class RegressionTests(unittest.TestCase): @@ -383,72 +384,26 @@ def CheckDelIsolation_levelSegfault(self): with self.assertRaises(AttributeError): del self.con.isolation_level + def CheckBpo37347(self): + class Printer: + def log(self, *args): + return sqlite.SQLITE_OK -class UnhashableFunc: - __hash__ = None + for method in [self.con.set_trace_callback, + functools.partial(self.con.set_progress_handler, n=1), + self.con.set_authorizer]: + printer_instance = Printer() + method(printer_instance.log) + method(printer_instance.log) + self.con.execute("select 1") # trigger seg fault + method(None) - def __init__(self, return_value=None): - self.calls = 0 - self.return_value = return_value - - def __call__(self, *args, **kwargs): - self.calls += 1 - return self.return_value - - -class UnhashableCallbacksTestCase(unittest.TestCase): - """ - https://bugs.python.org/issue34052 - - Registering unhashable callbacks raises TypeError, callbacks are not - registered in SQLite after such registration attempt. - """ - def setUp(self): - self.con = sqlite.connect(':memory:') - - def tearDown(self): - self.con.close() - - def test_progress_handler(self): - f = UnhashableFunc(return_value=0) - with self.assertRaisesRegex(TypeError, 'unhashable type'): - self.con.set_progress_handler(f, 1) - self.con.execute('SELECT 1') - self.assertFalse(f.calls) - - def test_func(self): - func_name = 'func_name' - f = UnhashableFunc() - with self.assertRaisesRegex(TypeError, 'unhashable type'): - self.con.create_function(func_name, 0, f) - msg = 'no such function: %s' % func_name - with self.assertRaisesRegex(sqlite.OperationalError, msg): - self.con.execute('SELECT %s()' % func_name) - self.assertFalse(f.calls) - - def test_authorizer(self): - f = UnhashableFunc(return_value=sqlite.SQLITE_DENY) - with self.assertRaisesRegex(TypeError, 'unhashable type'): - self.con.set_authorizer(f) - self.con.execute('SELECT 1') - self.assertFalse(f.calls) - - def test_aggr(self): - class UnhashableType(type): - __hash__ = None - aggr_name = 'aggr_name' - with self.assertRaisesRegex(TypeError, 'unhashable type'): - self.con.create_aggregate(aggr_name, 0, UnhashableType('Aggr', (), {})) - msg = 'no such function: %s' % aggr_name - with self.assertRaisesRegex(sqlite.OperationalError, msg): - self.con.execute('SELECT %s()' % aggr_name) def suite(): regression_suite = unittest.makeSuite(RegressionTests, "Check") return unittest.TestSuite(( regression_suite, - unittest.makeSuite(UnhashableCallbacksTestCase), )) def test(): diff --git a/Misc/ACKS b/Misc/ACKS index f427d8b5a929..53285403e695 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1866,3 +1866,4 @@ Diego Rojas Edison Abahurire Geoff Shannon Batuhan Taskaya +Aleksandr Balezin diff --git a/Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst b/Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst new file mode 100644 index 000000000000..1e61f5e0b3db --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst @@ -0,0 +1,6 @@ +:meth:`sqlite3.Connection.create_aggregate`, +:meth:`sqlite3.Connection.create_function`, +:meth:`sqlite3.Connection.set_authorizer`, +:meth:`sqlite3.Connection.set_progress_handler` +:meth:`sqlite3.Connection.set_trace_callback` +methods lead to segfaults if some of these methods are called twice with an equal object but not the same. Now callbacks are stored more carefully. Patch by Aleksandr Balezin. \ No newline at end of file diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 0d6462ef7dc2..ebe073f644aa 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -186,10 +186,9 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject } self->check_same_thread = check_same_thread; - Py_XSETREF(self->function_pinboard, PyDict_New()); - if (!self->function_pinboard) { - return -1; - } + self->function_pinboard_trace_callback = NULL; + self->function_pinboard_progress_handler = NULL; + self->function_pinboard_authorizer_cb = NULL; Py_XSETREF(self->collations, PyDict_New()); if (!self->collations) { @@ -249,19 +248,18 @@ void pysqlite_connection_dealloc(pysqlite_Connection* self) /* Clean up if user has not called .close() explicitly. */ if (self->db) { - Py_BEGIN_ALLOW_THREADS SQLITE3_CLOSE(self->db); - Py_END_ALLOW_THREADS } Py_XDECREF(self->isolation_level); - Py_XDECREF(self->function_pinboard); + Py_XDECREF(self->function_pinboard_trace_callback); + Py_XDECREF(self->function_pinboard_progress_handler); + Py_XDECREF(self->function_pinboard_authorizer_cb); Py_XDECREF(self->row_factory); Py_XDECREF(self->text_factory); Py_XDECREF(self->collations); Py_XDECREF(self->statements); Py_XDECREF(self->cursors); - Py_TYPE(self)->tp_free((PyObject*)self); } @@ -342,9 +340,7 @@ PyObject* pysqlite_connection_close(pysqlite_Connection* self, PyObject* args) pysqlite_do_all_statements(self, ACTION_FINALIZE, 1); if (self->db) { - Py_BEGIN_ALLOW_THREADS rc = SQLITE3_CLOSE(self->db); - Py_END_ALLOW_THREADS if (rc != SQLITE_OK) { _pysqlite_seterror(self->db, NULL); @@ -808,6 +804,11 @@ static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self) Py_SETREF(self->cursors, new_list); } +static void _destructor(void* args) +{ + Py_DECREF((PyObject*)args); +} + PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) { static char *kwlist[] = {"name", "narg", "func", "deterministic", NULL}; @@ -843,17 +844,16 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec flags |= SQLITE_DETERMINISTIC; #endif } - if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) { - return NULL; - } - rc = sqlite3_create_function(self->db, - name, - narg, - flags, - (void*)func, - _pysqlite_func_callback, - NULL, - NULL); + Py_INCREF(func); + rc = sqlite3_create_function_v2(self->db, + name, + narg, + flags, + (void*)func, + _pysqlite_func_callback, + NULL, + NULL, + &_destructor); // will decref func if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ @@ -880,11 +880,16 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje kwlist, &name, &n_arg, &aggregate_class)) { return NULL; } - - if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) { - return NULL; - } - rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_pysqlite_step_callback, &_pysqlite_final_callback); + Py_INCREF(aggregate_class); + rc = sqlite3_create_function_v2(self->db, + name, + n_arg, + SQLITE_UTF8, + (void*)aggregate_class, + 0, + &_pysqlite_step_callback, + &_pysqlite_final_callback, + &_destructor); // will decref func if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate"); @@ -1003,13 +1008,14 @@ static PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, P return NULL; } - if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) { - return NULL; - } rc = sqlite3_set_authorizer(self->db, _authorizer_callback, (void*)authorizer_cb); if (rc != SQLITE_OK) { PyErr_SetString(pysqlite_OperationalError, "Error setting authorizer callback"); + Py_XSETREF(self->function_pinboard_authorizer_cb, NULL); return NULL; + } else { + Py_INCREF(authorizer_cb); + Py_XSETREF(self->function_pinboard_authorizer_cb, authorizer_cb); } Py_RETURN_NONE; } @@ -1033,12 +1039,12 @@ static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* s if (progress_handler == Py_None) { /* None clears the progress handler previously set */ sqlite3_progress_handler(self->db, 0, 0, (void*)0); + Py_XSETREF(self->function_pinboard_progress_handler, NULL); } else { - if (PyDict_SetItem(self->function_pinboard, progress_handler, Py_None) == -1) - return NULL; sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); + Py_INCREF(progress_handler); + Py_XSETREF(self->function_pinboard_progress_handler, progress_handler); } - Py_RETURN_NONE; } @@ -1060,10 +1066,11 @@ static PyObject* pysqlite_connection_set_trace_callback(pysqlite_Connection* sel if (trace_callback == Py_None) { /* None clears the trace callback previously set */ sqlite3_trace(self->db, 0, (void*)0); + Py_XSETREF(self->function_pinboard_trace_callback, NULL); } else { - if (PyDict_SetItem(self->function_pinboard, trace_callback, Py_None) == -1) - return NULL; sqlite3_trace(self->db, _trace_callback, trace_callback); + Py_INCREF(trace_callback); + Py_XSETREF(self->function_pinboard_trace_callback, trace_callback); } Py_RETURN_NONE; diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h index 4e9d94c5f308..206085e00a00 100644 --- a/Modules/_sqlite/connection.h +++ b/Modules/_sqlite/connection.h @@ -85,11 +85,10 @@ typedef struct */ PyObject* text_factory; - /* remember references to functions/classes used in - * create_function/create/aggregate, use these as dictionary keys, so we - * can keep the total system refcount constant by clearing that dictionary - * in connection_dealloc */ - PyObject* function_pinboard; + /* remember references to object used in trace_callback/progress_handler/authorizer_cb */ + PyObject* function_pinboard_trace_callback; + PyObject* function_pinboard_progress_handler; + PyObject* function_pinboard_authorizer_cb; /* a dictionary of registered collation name => collation callable mappings */ PyObject* collations; diff --git a/setup.py b/setup.py index edc343424297..6cbbec9e1212 100644 --- a/setup.py +++ b/setup.py @@ -1348,7 +1348,7 @@ def detect_sqlite(self): ] if CROSS_COMPILING: sqlite_inc_paths = [] - MIN_SQLITE_VERSION_NUMBER = (3, 3, 9) + MIN_SQLITE_VERSION_NUMBER = (3, 7, 2) MIN_SQLITE_VERSION = ".".join([str(x) for x in MIN_SQLITE_VERSION_NUMBER]) From webhook-mailer at python.org Sat Jul 13 04:35:16 2019 From: webhook-mailer at python.org (Xiang Zhang) Date: Sat, 13 Jul 2019 08:35:16 -0000 Subject: [Python-checkins] Fix typo in re.escape documentation (GH-14722) Message-ID: https://github.com/python/cpython/commit/fb6c1f8d3b116c7e3e3f814bf750d984a2f2ecbf commit: fb6c1f8d3b116c7e3e3f814bf750d984a2f2ecbf branch: master author: Robert DiPietro committer: Xiang Zhang date: 2019-07-13T16:35:04+08:00 summary: Fix typo in re.escape documentation (GH-14722) files: M Doc/library/re.rst diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 5ef72b535ce8..158248c3d147 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -942,7 +942,7 @@ form. >>> print('|'.join(map(re.escape, sorted(operators, reverse=True)))) /|\-|\+|\*\*|\* - This functions must not be used for the replacement string in :func:`sub` + This function must not be used for the replacement string in :func:`sub` and :func:`subn`, only backslashes should be escaped. For example:: >>> digits_re = r'\d+' From webhook-mailer at python.org Sat Jul 13 04:40:59 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 08:40:59 -0000 Subject: [Python-checkins] Fix typo in re.escape documentation (GH-14722) Message-ID: https://github.com/python/cpython/commit/184d06b354d799b7d862040ed41274cf0924100a commit: 184d06b354d799b7d862040ed41274cf0924100a branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-13T01:40:54-07:00 summary: Fix typo in re.escape documentation (GH-14722) (cherry picked from commit fb6c1f8d3b116c7e3e3f814bf750d984a2f2ecbf) Co-authored-by: Robert DiPietro files: M Doc/library/re.rst diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 2e6c7f715d20..23deb1f01b86 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -936,7 +936,7 @@ form. >>> print('|'.join(map(re.escape, sorted(operators, reverse=True)))) /|\-|\+|\*\*|\* - This functions must not be used for the replacement string in :func:`sub` + This function must not be used for the replacement string in :func:`sub` and :func:`subn`, only backslashes should be escaped. For example:: >>> digits_re = r'\d+' From webhook-mailer at python.org Sat Jul 13 04:42:29 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 08:42:29 -0000 Subject: [Python-checkins] Fix typo in re.escape documentation (GH-14722) Message-ID: https://github.com/python/cpython/commit/25d371ab74b9413231b2e8ea47be0fa49a04136d commit: 25d371ab74b9413231b2e8ea47be0fa49a04136d branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-13T01:42:25-07:00 summary: Fix typo in re.escape documentation (GH-14722) (cherry picked from commit fb6c1f8d3b116c7e3e3f814bf750d984a2f2ecbf) Co-authored-by: Robert DiPietro files: M Doc/library/re.rst diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 5ef72b535ce8..158248c3d147 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -942,7 +942,7 @@ form. >>> print('|'.join(map(re.escape, sorted(operators, reverse=True)))) /|\-|\+|\*\*|\* - This functions must not be used for the replacement string in :func:`sub` + This function must not be used for the replacement string in :func:`sub` and :func:`subn`, only backslashes should be escaped. For example:: >>> digits_re = r'\d+' From webhook-mailer at python.org Sat Jul 13 05:46:29 2019 From: webhook-mailer at python.org (Steve Dower) Date: Sat, 13 Jul 2019 09:46:29 -0000 Subject: [Python-checkins] Enable publish of Windows releases through Azure Pipelines (GH-14720) Message-ID: https://github.com/python/cpython/commit/994a3b88dca852696351358e2743313e546b5ecf commit: 994a3b88dca852696351358e2743313e546b5ecf branch: master author: Steve Dower committer: GitHub date: 2019-07-13T11:46:16+02:00 summary: Enable publish of Windows releases through Azure Pipelines (GH-14720) files: A .azure-pipelines/windows-release/gpg-sign.yml M .azure-pipelines/windows-release.yml M .azure-pipelines/windows-release/stage-publish-nugetorg.yml M .azure-pipelines/windows-release/stage-publish-pythonorg.yml M .azure-pipelines/windows-release/stage-publish-store.yml M Tools/msi/uploadrelease.ps1 diff --git a/.azure-pipelines/windows-release.yml b/.azure-pipelines/windows-release.yml index 774585792484..3d072e3b43e1 100644 --- a/.azure-pipelines/windows-release.yml +++ b/.azure-pipelines/windows-release.yml @@ -1,7 +1,8 @@ name: Release_$(Build.SourceBranchName)_$(SourceTag)_$(Date:yyyyMMdd)$(Rev:.rr) +variables: + __RealSigningCertificate: 'Python Software Foundation' # QUEUE TIME VARIABLES -# variables: # GitRemote: python # SourceTag: # DoPGO: true @@ -13,6 +14,9 @@ name: Release_$(Build.SourceBranchName)_$(SourceTag)_$(Date:yyyyMMdd)$(Rev:.rr) # DoEmbed: true # DoMSI: true # DoPublish: false +# PyDotOrgUsername: '' +# PyDotOrgServer: '' +# BuildToPublish: '' trigger: none pr: none @@ -20,18 +24,21 @@ pr: none stages: - stage: Build displayName: Build binaries + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-build.yml - stage: Sign displayName: Sign binaries dependsOn: Build + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-sign.yml - stage: Layout displayName: Generate layouts dependsOn: Sign + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-layout-full.yml - template: windows-release/stage-layout-embed.yml @@ -39,11 +46,13 @@ stages: - stage: Pack dependsOn: Layout + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-pack-nuget.yml - stage: Test dependsOn: Pack + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-test-embed.yml - template: windows-release/stage-test-nuget.yml @@ -51,46 +60,70 @@ stages: - stage: Layout_MSIX displayName: Generate MSIX layouts dependsOn: Sign - condition: and(succeeded(), eq(variables['DoMSIX'], 'true')) + condition: and(succeeded(), and(eq(variables['DoMSIX'], 'true'), not(variables['BuildToPublish']))) jobs: - template: windows-release/stage-layout-msix.yml - stage: Pack_MSIX displayName: Package MSIX dependsOn: Layout_MSIX + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-pack-msix.yml - stage: Build_MSI displayName: Build MSI installer dependsOn: Sign - condition: and(succeeded(), eq(variables['DoMSI'], 'true')) + condition: and(succeeded(), and(eq(variables['DoMSI'], 'true'), not(variables['BuildToPublish']))) jobs: - template: windows-release/stage-msi.yml - stage: Test_MSI displayName: Test MSI installer dependsOn: Build_MSI + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-test-msi.yml - stage: PublishPyDotOrg displayName: Publish to python.org dependsOn: ['Test_MSI', 'Test'] - condition: and(succeeded(), eq(variables['DoPublish'], 'true')) + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), not(variables['BuildToPublish']))) jobs: - template: windows-release/stage-publish-pythonorg.yml - stage: PublishNuget displayName: Publish to nuget.org dependsOn: Test - condition: and(succeeded(), eq(variables['DoPublish'], 'true')) + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), not(variables['BuildToPublish']))) jobs: - template: windows-release/stage-publish-nugetorg.yml - stage: PublishStore displayName: Publish to Store dependsOn: Pack_MSIX - condition: and(succeeded(), eq(variables['DoPublish'], 'true')) + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), not(variables['BuildToPublish']))) + jobs: + - template: windows-release/stage-publish-store.yml + + +- stage: PublishExistingPyDotOrg + displayName: Publish existing build to python.org + dependsOn: [] + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), variables['BuildToPublish'])) + jobs: + - template: windows-release/stage-publish-pythonorg.yml + +- stage: PublishExistingNuget + displayName: Publish existing build to nuget.org + dependsOn: [] + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), variables['BuildToPublish'])) + jobs: + - template: windows-release/stage-publish-nugetorg.yml + +- stage: PublishExistingStore + displayName: Publish existing build to Store + dependsOn: [] + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), variables['BuildToPublish'])) jobs: - template: windows-release/stage-publish-store.yml diff --git a/.azure-pipelines/windows-release/gpg-sign.yml b/.azure-pipelines/windows-release/gpg-sign.yml new file mode 100644 index 000000000000..0855af8d703d --- /dev/null +++ b/.azure-pipelines/windows-release/gpg-sign.yml @@ -0,0 +1,28 @@ +parameters: + GPGKeyFile: $(GPGKey) + GPGPassphrase: $(GPGPassphrase) + Files: '*' + WorkingDirectory: $(Build.BinariesDirectory) + +steps: +- task: DownloadSecureFile at 1 + name: gpgkey + inputs: + secureFile: ${{ parameters.GPGKeyFile }} + displayName: 'Download GPG key' + +- powershell: | + git clone https://github.com/python/cpython-bin-deps --branch gpg --single-branch --depth 1 --progress -v "gpg" + gpg/gpg2.exe --import "$(gpgkey.secureFilePath)" + (gci -File ${{ parameters.Files }}).FullName | %{ + gpg/gpg2.exe -ba --batch --passphrase ${{ parameters.GPGPassphrase }} $_ + "Made signature for $_" + } + displayName: 'Generate GPG signatures' + workingDirectory: ${{ parameters.WorkingDirectory }} + +- powershell: | + $p = gps "gpg-agent" -EA 0 + if ($p) { $p.Kill() } + displayName: 'Kill GPG agent' + condition: true diff --git a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml index 7586d850f340..296eb28648b9 100644 --- a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml +++ b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml @@ -14,13 +14,26 @@ jobs: - task: DownloadBuildArtifacts at 0 displayName: 'Download artifact: nuget' + condition: and(succeeded(), not(variables['BuildToPublish'])) inputs: artifactName: nuget downloadPath: $(Build.BinariesDirectory) + - task: DownloadBuildArtifacts at 0 + displayName: 'Download artifact: nuget' + condition: and(succeeded(), variables['BuildToPublish']) + inputs: + artifactName: nuget + downloadPath: $(Build.BinariesDirectory) + buildType: specific + project: cpython + pipeline: Windows-Release + buildVersionToDownload: specific + buildId: $(BuildToPublish) + - task: NuGetCommand at 2 displayName: Push packages - condition: and(succeeded(), eq(variables['SigningCertificate'], 'Python Software Foundation')) + condition: and(succeeded(), eq(variables['SigningCertificate'], variables['__RealSigningCertificate'])) inputs: command: push packagesToPush: $(Build.BinariesDirectory)\nuget\*.nupkg' diff --git a/.azure-pipelines/windows-release/stage-publish-pythonorg.yml b/.azure-pipelines/windows-release/stage-publish-pythonorg.yml index 2215a56d4bc2..2dd354a8c276 100644 --- a/.azure-pipelines/windows-release/stage-publish-pythonorg.yml +++ b/.azure-pipelines/windows-release/stage-publish-pythonorg.yml @@ -4,31 +4,151 @@ jobs: condition: and(succeeded(), and(eq(variables['DoMSI'], 'true'), eq(variables['DoEmbed'], 'true'))) pool: - vmName: win2016-vs2017 + #vmName: win2016-vs2017 + name: 'Windows Release' workspace: clean: all steps: - - checkout: none + - template: ./checkout.yml - - task: DownloadBuildArtifacts at 0 + - task: UsePythonVersion at 0 + displayName: 'Use Python 3.6 or later' + inputs: + versionSpec: '>=3.6' + + - task: DownloadPipelineArtifact at 1 displayName: 'Download artifact: Doc' + condition: and(succeeded(), not(variables['BuildToPublish'])) inputs: artifactName: Doc - downloadPath: $(Build.BinariesDirectory) + targetPath: $(Build.BinariesDirectory)\Doc - - task: DownloadBuildArtifacts at 0 + - task: DownloadPipelineArtifact at 1 displayName: 'Download artifact: msi' + condition: and(succeeded(), not(variables['BuildToPublish'])) inputs: artifactName: msi - downloadPath: $(Build.BinariesDirectory) + targetPath: $(Build.BinariesDirectory)\msi - task: DownloadBuildArtifacts at 0 displayName: 'Download artifact: embed' + condition: and(succeeded(), not(variables['BuildToPublish'])) + inputs: + artifactName: embed + downloadPath: $(Build.BinariesDirectory) + + + - task: DownloadPipelineArtifact at 1 + displayName: 'Download artifact from $(BuildToPublish): Doc' + condition: and(succeeded(), variables['BuildToPublish']) + inputs: + artifactName: Doc + targetPath: $(Build.BinariesDirectory)\Doc + buildType: specific + project: cpython + pipeline: 21 + buildVersionToDownload: specific + buildId: $(BuildToPublish) + + - task: DownloadPipelineArtifact at 1 + displayName: 'Download artifact from $(BuildToPublish): msi' + condition: and(succeeded(), variables['BuildToPublish']) + inputs: + artifactName: msi + targetPath: $(Build.BinariesDirectory)\msi + buildType: specific + project: cpython + pipeline: 21 + buildVersionToDownload: specific + buildId: $(BuildToPublish) + + - task: DownloadBuildArtifacts at 0 + displayName: 'Download artifact from $(BuildToPublish): embed' + condition: and(succeeded(), variables['BuildToPublish']) inputs: artifactName: embed downloadPath: $(Build.BinariesDirectory) + buildType: specific + project: cpython + pipeline: Windows-Release + buildVersionToDownload: specific + buildId: $(BuildToPublish) + + + - template: ./gpg-sign.yml + parameters: + GPGKeyFile: 'python-signing.key' + Files: 'doc\htmlhelp\*.chm, msi\*\*, embed\*.zip' - # TODO: eq(variables['SigningCertificate'], 'Python Software Foundation') - # If we are not real-signed, DO NOT PUBLISH + - powershell: > + $(Build.SourcesDirectory)\Tools\msi\uploadrelease.ps1 + -build msi + -user $(PyDotOrgUsername) + -server $(PyDotOrgServer) + -doc_htmlhelp doc\htmlhelp + -embed embed + -skippurge + -skiptest + -skiphash + condition: and(succeeded(), eq(variables['SigningCertificate'], variables['__RealSigningCertificate'])) + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Upload files to python.org' + + - powershell: > + python + "$(Build.SourcesDirectory)\Tools\msi\purge.py" + (gci msi\*\python-*.exe | %{ $_.Name -replace 'python-(.+?)(-|\.exe).+', '$1' } | select -First 1) + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Purge CDN' + + - powershell: | + $failures = 0 + gci "msi\*\*-webinstall.exe" -File | %{ + $d = mkdir "tests\$($_.BaseName)" -Force + gci $d -r -File | del + $ic = copy $_ $d -PassThru + "Checking layout for $($ic.Name)" + Start-Process -wait $ic "/passive", "/layout", "$d\layout", "/log", "$d\log\install.log" + if (-not $?) { + Write-Error "Failed to validate layout of $($inst.Name)" + $failures += 1 + } + } + if ($failures) { + Write-Error "Failed to validate $failures installers" + exit 1 + } + #condition: and(succeeded(), eq(variables['SigningCertificate'], variables['__RealSigningCertificate'])) + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Test layouts' + + - powershell: | + $hashes = gci doc\htmlhelp\python*.chm, msi\*\*.exe, embed\*.zip | ` + Sort-Object Name | ` + Format-Table Name, @{ + Label="MD5"; + Expression={(Get-FileHash $_ -Algorithm MD5).Hash} + }, Length -AutoSize | ` + Out-String -Width 4096 + $d = mkdir "$(Build.ArtifactStagingDirectory)\hashes" -Force + $hashes | Out-File "$d\hashes.txt" -Encoding ascii + $hashes + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Generate hashes' + + - powershell: | + "Copying:" + (gci msi\*\python*.asc, doc\htmlhelp\*.asc, embed\*.asc).FullName + $d = mkdir "$(Build.ArtifactStagingDirectory)\hashes" -Force + move msi\*\python*.asc, doc\htmlhelp\*.asc, embed\*.asc $d -Force + gci msi -Directory | %{ move "msi\$_\*.asc" (mkdir "$d\$_" -Force) } + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Copy GPG signatures for build' + + - task: PublishPipelineArtifact at 0 + displayName: 'Publish Artifact: hashes' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)\hashes' + artifactName: hashes diff --git a/.azure-pipelines/windows-release/stage-publish-store.yml b/.azure-pipelines/windows-release/stage-publish-store.yml index 06884c4f35b7..b22147b1ab45 100644 --- a/.azure-pipelines/windows-release/stage-publish-store.yml +++ b/.azure-pipelines/windows-release/stage-publish-store.yml @@ -14,9 +14,22 @@ jobs: - task: DownloadBuildArtifacts at 0 displayName: 'Download artifact: msixupload' + condition: and(succeeded(), not(variables['BuildToPublish'])) inputs: artifactName: msixupload downloadPath: $(Build.BinariesDirectory) - # TODO: eq(variables['SigningCertificate'], 'Python Software Foundation') + - task: DownloadBuildArtifacts at 0 + displayName: 'Download artifact: msixupload' + condition: and(succeeded(), variables['BuildToPublish']) + inputs: + artifactName: msixupload + downloadPath: $(Build.BinariesDirectory) + buildType: specific + project: cpython + pipeline: Windows-Release + buildVersionToDownload: specific + buildId: $(BuildToPublish) + + # TODO: eq(variables['SigningCertificate'], variables['__RealSigningCertificate']) # If we are not real-signed, DO NOT PUBLISH diff --git a/Tools/msi/uploadrelease.ps1 b/Tools/msi/uploadrelease.ps1 index b6fbeea29810..469a96818a1f 100644 --- a/Tools/msi/uploadrelease.ps1 +++ b/Tools/msi/uploadrelease.ps1 @@ -164,5 +164,4 @@ if (-not $skiphash) { Out-String -Width 4096 $hashes | clip $hashes - popd } From webhook-mailer at python.org Sat Jul 13 06:10:40 2019 From: webhook-mailer at python.org (Steve Dower) Date: Sat, 13 Jul 2019 10:10:40 -0000 Subject: [Python-checkins] Enable publish of Windows releases through Azure Pipelines (GH-14720) Message-ID: https://github.com/python/cpython/commit/12f3312aa20b8012659ad47f636931d983c04cc9 commit: 12f3312aa20b8012659ad47f636931d983c04cc9 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Steve Dower date: 2019-07-13T12:10:33+02:00 summary: Enable publish of Windows releases through Azure Pipelines (GH-14720) (cherry picked from commit 994a3b88dca852696351358e2743313e546b5ecf) Co-authored-by: Steve Dower files: A .azure-pipelines/windows-release/gpg-sign.yml M .azure-pipelines/windows-release.yml M .azure-pipelines/windows-release/stage-publish-nugetorg.yml M .azure-pipelines/windows-release/stage-publish-pythonorg.yml M .azure-pipelines/windows-release/stage-publish-store.yml M Tools/msi/uploadrelease.ps1 diff --git a/.azure-pipelines/windows-release.yml b/.azure-pipelines/windows-release.yml index 774585792484..3d072e3b43e1 100644 --- a/.azure-pipelines/windows-release.yml +++ b/.azure-pipelines/windows-release.yml @@ -1,7 +1,8 @@ name: Release_$(Build.SourceBranchName)_$(SourceTag)_$(Date:yyyyMMdd)$(Rev:.rr) +variables: + __RealSigningCertificate: 'Python Software Foundation' # QUEUE TIME VARIABLES -# variables: # GitRemote: python # SourceTag: # DoPGO: true @@ -13,6 +14,9 @@ name: Release_$(Build.SourceBranchName)_$(SourceTag)_$(Date:yyyyMMdd)$(Rev:.rr) # DoEmbed: true # DoMSI: true # DoPublish: false +# PyDotOrgUsername: '' +# PyDotOrgServer: '' +# BuildToPublish: '' trigger: none pr: none @@ -20,18 +24,21 @@ pr: none stages: - stage: Build displayName: Build binaries + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-build.yml - stage: Sign displayName: Sign binaries dependsOn: Build + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-sign.yml - stage: Layout displayName: Generate layouts dependsOn: Sign + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-layout-full.yml - template: windows-release/stage-layout-embed.yml @@ -39,11 +46,13 @@ stages: - stage: Pack dependsOn: Layout + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-pack-nuget.yml - stage: Test dependsOn: Pack + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-test-embed.yml - template: windows-release/stage-test-nuget.yml @@ -51,46 +60,70 @@ stages: - stage: Layout_MSIX displayName: Generate MSIX layouts dependsOn: Sign - condition: and(succeeded(), eq(variables['DoMSIX'], 'true')) + condition: and(succeeded(), and(eq(variables['DoMSIX'], 'true'), not(variables['BuildToPublish']))) jobs: - template: windows-release/stage-layout-msix.yml - stage: Pack_MSIX displayName: Package MSIX dependsOn: Layout_MSIX + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-pack-msix.yml - stage: Build_MSI displayName: Build MSI installer dependsOn: Sign - condition: and(succeeded(), eq(variables['DoMSI'], 'true')) + condition: and(succeeded(), and(eq(variables['DoMSI'], 'true'), not(variables['BuildToPublish']))) jobs: - template: windows-release/stage-msi.yml - stage: Test_MSI displayName: Test MSI installer dependsOn: Build_MSI + condition: and(succeeded(), not(variables['BuildToPublish'])) jobs: - template: windows-release/stage-test-msi.yml - stage: PublishPyDotOrg displayName: Publish to python.org dependsOn: ['Test_MSI', 'Test'] - condition: and(succeeded(), eq(variables['DoPublish'], 'true')) + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), not(variables['BuildToPublish']))) jobs: - template: windows-release/stage-publish-pythonorg.yml - stage: PublishNuget displayName: Publish to nuget.org dependsOn: Test - condition: and(succeeded(), eq(variables['DoPublish'], 'true')) + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), not(variables['BuildToPublish']))) jobs: - template: windows-release/stage-publish-nugetorg.yml - stage: PublishStore displayName: Publish to Store dependsOn: Pack_MSIX - condition: and(succeeded(), eq(variables['DoPublish'], 'true')) + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), not(variables['BuildToPublish']))) + jobs: + - template: windows-release/stage-publish-store.yml + + +- stage: PublishExistingPyDotOrg + displayName: Publish existing build to python.org + dependsOn: [] + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), variables['BuildToPublish'])) + jobs: + - template: windows-release/stage-publish-pythonorg.yml + +- stage: PublishExistingNuget + displayName: Publish existing build to nuget.org + dependsOn: [] + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), variables['BuildToPublish'])) + jobs: + - template: windows-release/stage-publish-nugetorg.yml + +- stage: PublishExistingStore + displayName: Publish existing build to Store + dependsOn: [] + condition: and(succeeded(), and(eq(variables['DoPublish'], 'true'), variables['BuildToPublish'])) jobs: - template: windows-release/stage-publish-store.yml diff --git a/.azure-pipelines/windows-release/gpg-sign.yml b/.azure-pipelines/windows-release/gpg-sign.yml new file mode 100644 index 000000000000..0855af8d703d --- /dev/null +++ b/.azure-pipelines/windows-release/gpg-sign.yml @@ -0,0 +1,28 @@ +parameters: + GPGKeyFile: $(GPGKey) + GPGPassphrase: $(GPGPassphrase) + Files: '*' + WorkingDirectory: $(Build.BinariesDirectory) + +steps: +- task: DownloadSecureFile at 1 + name: gpgkey + inputs: + secureFile: ${{ parameters.GPGKeyFile }} + displayName: 'Download GPG key' + +- powershell: | + git clone https://github.com/python/cpython-bin-deps --branch gpg --single-branch --depth 1 --progress -v "gpg" + gpg/gpg2.exe --import "$(gpgkey.secureFilePath)" + (gci -File ${{ parameters.Files }}).FullName | %{ + gpg/gpg2.exe -ba --batch --passphrase ${{ parameters.GPGPassphrase }} $_ + "Made signature for $_" + } + displayName: 'Generate GPG signatures' + workingDirectory: ${{ parameters.WorkingDirectory }} + +- powershell: | + $p = gps "gpg-agent" -EA 0 + if ($p) { $p.Kill() } + displayName: 'Kill GPG agent' + condition: true diff --git a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml index 7586d850f340..296eb28648b9 100644 --- a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml +++ b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml @@ -14,13 +14,26 @@ jobs: - task: DownloadBuildArtifacts at 0 displayName: 'Download artifact: nuget' + condition: and(succeeded(), not(variables['BuildToPublish'])) inputs: artifactName: nuget downloadPath: $(Build.BinariesDirectory) + - task: DownloadBuildArtifacts at 0 + displayName: 'Download artifact: nuget' + condition: and(succeeded(), variables['BuildToPublish']) + inputs: + artifactName: nuget + downloadPath: $(Build.BinariesDirectory) + buildType: specific + project: cpython + pipeline: Windows-Release + buildVersionToDownload: specific + buildId: $(BuildToPublish) + - task: NuGetCommand at 2 displayName: Push packages - condition: and(succeeded(), eq(variables['SigningCertificate'], 'Python Software Foundation')) + condition: and(succeeded(), eq(variables['SigningCertificate'], variables['__RealSigningCertificate'])) inputs: command: push packagesToPush: $(Build.BinariesDirectory)\nuget\*.nupkg' diff --git a/.azure-pipelines/windows-release/stage-publish-pythonorg.yml b/.azure-pipelines/windows-release/stage-publish-pythonorg.yml index 2215a56d4bc2..2dd354a8c276 100644 --- a/.azure-pipelines/windows-release/stage-publish-pythonorg.yml +++ b/.azure-pipelines/windows-release/stage-publish-pythonorg.yml @@ -4,31 +4,151 @@ jobs: condition: and(succeeded(), and(eq(variables['DoMSI'], 'true'), eq(variables['DoEmbed'], 'true'))) pool: - vmName: win2016-vs2017 + #vmName: win2016-vs2017 + name: 'Windows Release' workspace: clean: all steps: - - checkout: none + - template: ./checkout.yml - - task: DownloadBuildArtifacts at 0 + - task: UsePythonVersion at 0 + displayName: 'Use Python 3.6 or later' + inputs: + versionSpec: '>=3.6' + + - task: DownloadPipelineArtifact at 1 displayName: 'Download artifact: Doc' + condition: and(succeeded(), not(variables['BuildToPublish'])) inputs: artifactName: Doc - downloadPath: $(Build.BinariesDirectory) + targetPath: $(Build.BinariesDirectory)\Doc - - task: DownloadBuildArtifacts at 0 + - task: DownloadPipelineArtifact at 1 displayName: 'Download artifact: msi' + condition: and(succeeded(), not(variables['BuildToPublish'])) inputs: artifactName: msi - downloadPath: $(Build.BinariesDirectory) + targetPath: $(Build.BinariesDirectory)\msi - task: DownloadBuildArtifacts at 0 displayName: 'Download artifact: embed' + condition: and(succeeded(), not(variables['BuildToPublish'])) + inputs: + artifactName: embed + downloadPath: $(Build.BinariesDirectory) + + + - task: DownloadPipelineArtifact at 1 + displayName: 'Download artifact from $(BuildToPublish): Doc' + condition: and(succeeded(), variables['BuildToPublish']) + inputs: + artifactName: Doc + targetPath: $(Build.BinariesDirectory)\Doc + buildType: specific + project: cpython + pipeline: 21 + buildVersionToDownload: specific + buildId: $(BuildToPublish) + + - task: DownloadPipelineArtifact at 1 + displayName: 'Download artifact from $(BuildToPublish): msi' + condition: and(succeeded(), variables['BuildToPublish']) + inputs: + artifactName: msi + targetPath: $(Build.BinariesDirectory)\msi + buildType: specific + project: cpython + pipeline: 21 + buildVersionToDownload: specific + buildId: $(BuildToPublish) + + - task: DownloadBuildArtifacts at 0 + displayName: 'Download artifact from $(BuildToPublish): embed' + condition: and(succeeded(), variables['BuildToPublish']) inputs: artifactName: embed downloadPath: $(Build.BinariesDirectory) + buildType: specific + project: cpython + pipeline: Windows-Release + buildVersionToDownload: specific + buildId: $(BuildToPublish) + + + - template: ./gpg-sign.yml + parameters: + GPGKeyFile: 'python-signing.key' + Files: 'doc\htmlhelp\*.chm, msi\*\*, embed\*.zip' - # TODO: eq(variables['SigningCertificate'], 'Python Software Foundation') - # If we are not real-signed, DO NOT PUBLISH + - powershell: > + $(Build.SourcesDirectory)\Tools\msi\uploadrelease.ps1 + -build msi + -user $(PyDotOrgUsername) + -server $(PyDotOrgServer) + -doc_htmlhelp doc\htmlhelp + -embed embed + -skippurge + -skiptest + -skiphash + condition: and(succeeded(), eq(variables['SigningCertificate'], variables['__RealSigningCertificate'])) + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Upload files to python.org' + + - powershell: > + python + "$(Build.SourcesDirectory)\Tools\msi\purge.py" + (gci msi\*\python-*.exe | %{ $_.Name -replace 'python-(.+?)(-|\.exe).+', '$1' } | select -First 1) + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Purge CDN' + + - powershell: | + $failures = 0 + gci "msi\*\*-webinstall.exe" -File | %{ + $d = mkdir "tests\$($_.BaseName)" -Force + gci $d -r -File | del + $ic = copy $_ $d -PassThru + "Checking layout for $($ic.Name)" + Start-Process -wait $ic "/passive", "/layout", "$d\layout", "/log", "$d\log\install.log" + if (-not $?) { + Write-Error "Failed to validate layout of $($inst.Name)" + $failures += 1 + } + } + if ($failures) { + Write-Error "Failed to validate $failures installers" + exit 1 + } + #condition: and(succeeded(), eq(variables['SigningCertificate'], variables['__RealSigningCertificate'])) + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Test layouts' + + - powershell: | + $hashes = gci doc\htmlhelp\python*.chm, msi\*\*.exe, embed\*.zip | ` + Sort-Object Name | ` + Format-Table Name, @{ + Label="MD5"; + Expression={(Get-FileHash $_ -Algorithm MD5).Hash} + }, Length -AutoSize | ` + Out-String -Width 4096 + $d = mkdir "$(Build.ArtifactStagingDirectory)\hashes" -Force + $hashes | Out-File "$d\hashes.txt" -Encoding ascii + $hashes + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Generate hashes' + + - powershell: | + "Copying:" + (gci msi\*\python*.asc, doc\htmlhelp\*.asc, embed\*.asc).FullName + $d = mkdir "$(Build.ArtifactStagingDirectory)\hashes" -Force + move msi\*\python*.asc, doc\htmlhelp\*.asc, embed\*.asc $d -Force + gci msi -Directory | %{ move "msi\$_\*.asc" (mkdir "$d\$_" -Force) } + workingDirectory: $(Build.BinariesDirectory) + displayName: 'Copy GPG signatures for build' + + - task: PublishPipelineArtifact at 0 + displayName: 'Publish Artifact: hashes' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)\hashes' + artifactName: hashes diff --git a/.azure-pipelines/windows-release/stage-publish-store.yml b/.azure-pipelines/windows-release/stage-publish-store.yml index 06884c4f35b7..b22147b1ab45 100644 --- a/.azure-pipelines/windows-release/stage-publish-store.yml +++ b/.azure-pipelines/windows-release/stage-publish-store.yml @@ -14,9 +14,22 @@ jobs: - task: DownloadBuildArtifacts at 0 displayName: 'Download artifact: msixupload' + condition: and(succeeded(), not(variables['BuildToPublish'])) inputs: artifactName: msixupload downloadPath: $(Build.BinariesDirectory) - # TODO: eq(variables['SigningCertificate'], 'Python Software Foundation') + - task: DownloadBuildArtifacts at 0 + displayName: 'Download artifact: msixupload' + condition: and(succeeded(), variables['BuildToPublish']) + inputs: + artifactName: msixupload + downloadPath: $(Build.BinariesDirectory) + buildType: specific + project: cpython + pipeline: Windows-Release + buildVersionToDownload: specific + buildId: $(BuildToPublish) + + # TODO: eq(variables['SigningCertificate'], variables['__RealSigningCertificate']) # If we are not real-signed, DO NOT PUBLISH diff --git a/Tools/msi/uploadrelease.ps1 b/Tools/msi/uploadrelease.ps1 index b6fbeea29810..469a96818a1f 100644 --- a/Tools/msi/uploadrelease.ps1 +++ b/Tools/msi/uploadrelease.ps1 @@ -164,5 +164,4 @@ if (-not $skiphash) { Out-String -Width 4096 $hashes | clip $hashes - popd } From webhook-mailer at python.org Sat Jul 13 06:17:21 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 10:17:21 -0000 Subject: [Python-checkins] bpo-37580: Fix typo in http.cookiejar documentation (GH-14731) Message-ID: https://github.com/python/cpython/commit/b5bbb8a740eaf46c78d122185de8b072e9deea2a commit: b5bbb8a740eaf46c78d122185de8b072e9deea2a branch: master author: Milan Oberkirch committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-13T03:17:16-07:00 summary: bpo-37580: Fix typo in http.cookiejar documentation (GH-14731) [bpo-37580](https://bugs.python.org/issue37580): Markup typo in http.cookiejar doc https://bugs.python.org/issue37580 files: M Doc/library/http.cookiejar.rst diff --git a/Doc/library/http.cookiejar.rst b/Doc/library/http.cookiejar.rst index a9d7321a42c4..1788bd8f833d 100644 --- a/Doc/library/http.cookiejar.rst +++ b/Doc/library/http.cookiejar.rst @@ -159,7 +159,7 @@ contained :class:`Cookie` objects. the :class:`CookieJar`'s :class:`CookiePolicy` instance are true and false respectively), the :mailheader:`Cookie2` header is also added when appropriate. - The *request* object (usually a :class:`urllib.request..Request` instance) + The *request* object (usually a :class:`urllib.request.Request` instance) must support the methods :meth:`get_full_url`, :meth:`get_host`, :meth:`get_type`, :meth:`unverifiable`, :meth:`has_header`, :meth:`get_header`, :meth:`header_items`, :meth:`add_unredirected_header` From webhook-mailer at python.org Sat Jul 13 06:23:14 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 10:23:14 -0000 Subject: [Python-checkins] bpo-37580: Fix typo in http.cookiejar documentation (GH-14731) Message-ID: https://github.com/python/cpython/commit/36494a94914c4d3f249ca10b6a2d6194f6093cc6 commit: 36494a94914c4d3f249ca10b6a2d6194f6093cc6 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-13T03:23:10-07:00 summary: bpo-37580: Fix typo in http.cookiejar documentation (GH-14731) [bpo-37580](https://bugs.python.org/issue37580): Markup typo in http.cookiejar doc https://bugs.python.org/issue37580 (cherry picked from commit b5bbb8a740eaf46c78d122185de8b072e9deea2a) Co-authored-by: Milan Oberkirch files: M Doc/library/http.cookiejar.rst diff --git a/Doc/library/http.cookiejar.rst b/Doc/library/http.cookiejar.rst index d8da6683a3a5..8bacd72c811a 100644 --- a/Doc/library/http.cookiejar.rst +++ b/Doc/library/http.cookiejar.rst @@ -153,7 +153,7 @@ contained :class:`Cookie` objects. the :class:`CookieJar`'s :class:`CookiePolicy` instance are true and false respectively), the :mailheader:`Cookie2` header is also added when appropriate. - The *request* object (usually a :class:`urllib.request..Request` instance) + The *request* object (usually a :class:`urllib.request.Request` instance) must support the methods :meth:`get_full_url`, :meth:`get_host`, :meth:`get_type`, :meth:`unverifiable`, :meth:`has_header`, :meth:`get_header`, :meth:`header_items`, :meth:`add_unredirected_header` From webhook-mailer at python.org Sat Jul 13 06:24:52 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 10:24:52 -0000 Subject: [Python-checkins] bpo-37580: Fix typo in http.cookiejar documentation (GH-14731) Message-ID: https://github.com/python/cpython/commit/5da83b417e48aecd7698387d3f37c603162fd46e commit: 5da83b417e48aecd7698387d3f37c603162fd46e branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-13T03:24:48-07:00 summary: bpo-37580: Fix typo in http.cookiejar documentation (GH-14731) [bpo-37580](https://bugs.python.org/issue37580): Markup typo in http.cookiejar doc https://bugs.python.org/issue37580 (cherry picked from commit b5bbb8a740eaf46c78d122185de8b072e9deea2a) Co-authored-by: Milan Oberkirch files: M Doc/library/http.cookiejar.rst diff --git a/Doc/library/http.cookiejar.rst b/Doc/library/http.cookiejar.rst index a9d7321a42c4..1788bd8f833d 100644 --- a/Doc/library/http.cookiejar.rst +++ b/Doc/library/http.cookiejar.rst @@ -159,7 +159,7 @@ contained :class:`Cookie` objects. the :class:`CookieJar`'s :class:`CookiePolicy` instance are true and false respectively), the :mailheader:`Cookie2` header is also added when appropriate. - The *request* object (usually a :class:`urllib.request..Request` instance) + The *request* object (usually a :class:`urllib.request.Request` instance) must support the methods :meth:`get_full_url`, :meth:`get_host`, :meth:`get_type`, :meth:`unverifiable`, :meth:`has_header`, :meth:`get_header`, :meth:`header_items`, :meth:`add_unredirected_header` From webhook-mailer at python.org Sat Jul 13 09:11:47 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sat, 13 Jul 2019 13:11:47 -0000 Subject: [Python-checkins] bpo-28269: Replace strcasecmp with system function _stricmp. (GH-13095) Message-ID: https://github.com/python/cpython/commit/05f2d84cae4ba1ff15b7a1d0347305393f4bdcc5 commit: 05f2d84cae4ba1ff15b7a1d0347305393f4bdcc5 branch: master author: Minmin Gong committer: Serhiy Storchaka date: 2019-07-13T16:11:43+03:00 summary: bpo-28269: Replace strcasecmp with system function _stricmp. (GH-13095) files: A Misc/NEWS.d/next/Windows/2019-05-05-05-23-34.bpo-28269.-MOHI7.rst M Python/dynload_win.c diff --git a/Misc/NEWS.d/next/Windows/2019-05-05-05-23-34.bpo-28269.-MOHI7.rst b/Misc/NEWS.d/next/Windows/2019-05-05-05-23-34.bpo-28269.-MOHI7.rst new file mode 100644 index 000000000000..a531b98118a2 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2019-05-05-05-23-34.bpo-28269.-MOHI7.rst @@ -0,0 +1 @@ +Replace use of :c:func:`strcasecmp` for the system function :c:func:`_stricmp`. Patch by Minmin Gong. diff --git a/Python/dynload_win.c b/Python/dynload_win.c index 457d47f5eed5..5096555e701c 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -38,24 +38,6 @@ const char *_PyImport_DynLoadFiletab[] = { NULL }; -/* Case insensitive string compare, to avoid any dependencies on particular - C RTL implementations */ - -static int strcasecmp (const char *string1, const char *string2) -{ - int first, second; - - do { - first = tolower(*string1); - second = tolower(*string2); - string1++; - string2++; - } while (first && first == second); - - return (first - second); -} - - /* Function to return the name of the "python" DLL that the supplied module directly imports. Looks through the list of imported modules and returns the first entry that starts with "python" (case sensitive) and @@ -297,7 +279,7 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, import_python = GetPythonImport(hDLL); if (import_python && - strcasecmp(buffer,import_python)) { + _stricmp(buffer,import_python)) { PyErr_Format(PyExc_ImportError, "Module use of %.150s conflicts " "with this version of Python.", From webhook-mailer at python.org Sat Jul 13 09:22:26 2019 From: webhook-mailer at python.org (Paul Ganssle) Date: Sat, 13 Jul 2019 13:22:26 -0000 Subject: [Python-checkins] bpo-37579: Improve equality behavior for pure Python datetime and time (GH-14726) Message-ID: https://github.com/python/cpython/commit/e6b46aafad3427463d6264a68824df4797e682f1 commit: e6b46aafad3427463d6264a68824df4797e682f1 branch: master author: Xtreak committer: Paul Ganssle date: 2019-07-13T15:22:21+02:00 summary: bpo-37579: Improve equality behavior for pure Python datetime and time (GH-14726) Returns NotImplemented for timedelta and time in __eq__ for different types in Python implementation, which matches the C implementation. This also adds tests to enforce that these objects will fall back to the right hand side's __eq__ and/or __ne__ implementation. bpo-37579 files: A Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst M Lib/datetime.py M Lib/test/datetimetester.py diff --git a/Lib/datetime.py b/Lib/datetime.py index 0e64815944db..e35ee0554c1f 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -733,7 +733,7 @@ def __eq__(self, other): if isinstance(other, timedelta): return self._cmp(other) == 0 else: - return False + return NotImplemented def __le__(self, other): if isinstance(other, timedelta): @@ -1310,7 +1310,7 @@ def __eq__(self, other): if isinstance(other, time): return self._cmp(other, allow_mixed=True) == 0 else: - return False + return NotImplemented def __le__(self, other): if isinstance(other, time): diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 53de0658773c..37ddd4b1a8bc 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -53,6 +53,19 @@ INF = float("inf") NAN = float("nan") + +class ComparesEqualClass(object): + """ + A class that is always equal to whatever you compare it to. + """ + + def __eq__(self, other): + return True + + def __ne__(self, other): + return False + + ############################################################################# # module tests @@ -399,6 +412,13 @@ def test_harmless_mixed_comparison(self): self.assertIn(me, [1, 20, [], me]) self.assertIn([], [me, 1, 20, []]) + # Comparison to objects of unsupported types should return + # NotImplemented which falls back to the right hand side's __eq__ + # method. In this case, ComparesEqualClass.__eq__ always returns True. + # ComparesEqualClass.__ne__ always returns False. + self.assertTrue(me == ComparesEqualClass()) + self.assertFalse(me != ComparesEqualClass()) + def test_harmful_mixed_comparison(self): me = self.theclass(1, 1, 1) diff --git a/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst b/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst new file mode 100644 index 000000000000..ad52cf2a06cc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst @@ -0,0 +1,4 @@ +Return :exc:`NotImplemented` in Python implementation of ``__eq__`` for +:class:`~datetime.timedelta` and :class:`~datetime.time` when the other +object being compared is not of the same type to match C implementation. +Patch by Karthikeyan Singaravelan. From webhook-mailer at python.org Sat Jul 13 09:59:40 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 13:59:40 -0000 Subject: [Python-checkins] bpo-37579: Improve equality behavior for pure Python datetime and time (GH-14726) Message-ID: https://github.com/python/cpython/commit/143672cf028740fc549e532c049559c522930c95 commit: 143672cf028740fc549e532c049559c522930c95 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-13T06:59:37-07:00 summary: bpo-37579: Improve equality behavior for pure Python datetime and time (GH-14726) Returns NotImplemented for timedelta and time in __eq__ for different types in Python implementation, which matches the C implementation. This also adds tests to enforce that these objects will fall back to the right hand side's __eq__ and/or __ne__ implementation. bpo-37579 (cherry picked from commit e6b46aafad3427463d6264a68824df4797e682f1) Co-authored-by: Xtreak files: A Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst M Lib/datetime.py M Lib/test/datetimetester.py diff --git a/Lib/datetime.py b/Lib/datetime.py index 0e64815944db..e35ee0554c1f 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -733,7 +733,7 @@ def __eq__(self, other): if isinstance(other, timedelta): return self._cmp(other) == 0 else: - return False + return NotImplemented def __le__(self, other): if isinstance(other, timedelta): @@ -1310,7 +1310,7 @@ def __eq__(self, other): if isinstance(other, time): return self._cmp(other, allow_mixed=True) == 0 else: - return False + return NotImplemented def __le__(self, other): if isinstance(other, time): diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 53de0658773c..37ddd4b1a8bc 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -53,6 +53,19 @@ INF = float("inf") NAN = float("nan") + +class ComparesEqualClass(object): + """ + A class that is always equal to whatever you compare it to. + """ + + def __eq__(self, other): + return True + + def __ne__(self, other): + return False + + ############################################################################# # module tests @@ -399,6 +412,13 @@ def test_harmless_mixed_comparison(self): self.assertIn(me, [1, 20, [], me]) self.assertIn([], [me, 1, 20, []]) + # Comparison to objects of unsupported types should return + # NotImplemented which falls back to the right hand side's __eq__ + # method. In this case, ComparesEqualClass.__eq__ always returns True. + # ComparesEqualClass.__ne__ always returns False. + self.assertTrue(me == ComparesEqualClass()) + self.assertFalse(me != ComparesEqualClass()) + def test_harmful_mixed_comparison(self): me = self.theclass(1, 1, 1) diff --git a/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst b/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst new file mode 100644 index 000000000000..ad52cf2a06cc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst @@ -0,0 +1,4 @@ +Return :exc:`NotImplemented` in Python implementation of ``__eq__`` for +:class:`~datetime.timedelta` and :class:`~datetime.time` when the other +object being compared is not of the same type to match C implementation. +Patch by Karthikeyan Singaravelan. From webhook-mailer at python.org Sat Jul 13 09:59:58 2019 From: webhook-mailer at python.org (Mark Dickinson) Date: Sat, 13 Jul 2019 13:59:58 -0000 Subject: [Python-checkins] bpo-37548: Document range of atan, acos and asin (GH-14717) Message-ID: https://github.com/python/cpython/commit/dc3f99fa77f415077c20a9c2b3e953e5cd894076 commit: dc3f99fa77f415077c20a9c2b3e953e5cd894076 branch: master author: Giovanni Cappellotto committer: Mark Dickinson date: 2019-07-13T14:59:55+01:00 summary: bpo-37548: Document range of atan, acos and asin (GH-14717) files: M Doc/library/math.rst M Misc/ACKS M Modules/mathmodule.c diff --git a/Doc/library/math.rst b/Doc/library/math.rst index bfce41a7f4c4..be953cfe9599 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -366,17 +366,20 @@ Trigonometric functions .. function:: acos(x) - Return the arc cosine of *x*, in radians. + Return the arc cosine of *x*, in radians. The result is between ``0`` and + ``pi``. .. function:: asin(x) - Return the arc sine of *x*, in radians. + Return the arc sine of *x*, in radians. The result is between ``-pi/2`` and + ``pi/2``. .. function:: atan(x) - Return the arc tangent of *x*, in radians. + Return the arc tangent of *x*, in radians. The result is between ``-pi/2`` and + ``pi/2``. .. function:: atan2(y, x) diff --git a/Misc/ACKS b/Misc/ACKS index c0119992cffe..31f8dda888c8 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -251,6 +251,7 @@ Jp Calderone Arnaud Calmettes Daniel Calvelo Tony Campbell +Giovanni Cappellotto Brett Cannon Tristan Carel Mike Carlton diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 552cb78c88d1..92c40b3a9ff1 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -1072,19 +1072,22 @@ math_2(PyObject *const *args, Py_ssize_t nargs, FUNC1(acos, acos, 0, "acos($module, x, /)\n--\n\n" - "Return the arc cosine (measured in radians) of x.") + "Return the arc cosine (measured in radians) of x.\n\n" + "The result is between 0 and pi.") FUNC1(acosh, m_acosh, 0, "acosh($module, x, /)\n--\n\n" "Return the inverse hyperbolic cosine of x.") FUNC1(asin, asin, 0, "asin($module, x, /)\n--\n\n" - "Return the arc sine (measured in radians) of x.") + "Return the arc sine (measured in radians) of x.\n\n" + "The result is between -pi/2 and pi/2.") FUNC1(asinh, m_asinh, 0, "asinh($module, x, /)\n--\n\n" "Return the inverse hyperbolic sine of x.") FUNC1(atan, atan, 0, "atan($module, x, /)\n--\n\n" - "Return the arc tangent (measured in radians) of x.") + "Return the arc tangent (measured in radians) of x.\n\n" + "The result is between -pi/2 and pi/2.") FUNC2(atan2, m_atan2, "atan2($module, y, x, /)\n--\n\n" "Return the arc tangent (measured in radians) of y/x.\n\n" From webhook-mailer at python.org Sat Jul 13 10:39:22 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 14:39:22 -0000 Subject: [Python-checkins] bpo-37358: Use vectorcall for functools.partial (GH-14284) Message-ID: https://github.com/python/cpython/commit/ed184c06e2e610e12050c5d5c9b0c1c2ecabb930 commit: ed184c06e2e610e12050c5d5c9b0c1c2ecabb930 branch: master author: Jeroen Demeyer committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-13T07:39:18-07:00 summary: bpo-37358: Use vectorcall for functools.partial (GH-14284) https://bugs.python.org/issue37358 files: A Misc/NEWS.d/next/Library/2019-06-21-14-54-02.bpo-37358.RsASpn.rst M Modules/_functoolsmodule.c diff --git a/Misc/NEWS.d/next/Library/2019-06-21-14-54-02.bpo-37358.RsASpn.rst b/Misc/NEWS.d/next/Library/2019-06-21-14-54-02.bpo-37358.RsASpn.rst new file mode 100644 index 000000000000..bc37631020f6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-21-14-54-02.bpo-37358.RsASpn.rst @@ -0,0 +1 @@ +Optimized ``functools.partial`` by using vectorcall. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index a101363bf02a..17d49ac5b342 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -18,13 +18,15 @@ typedef struct { PyObject *fn; PyObject *args; PyObject *kw; - PyObject *dict; + PyObject *dict; /* __dict__ */ PyObject *weakreflist; /* List of weak references */ - int use_fastcall; + vectorcallfunc vectorcall; } partialobject; static PyTypeObject partial_type; +static void partial_setvectorcall(partialobject *pto); + static PyObject * partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) { @@ -107,8 +109,7 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) return NULL; } - pto->use_fastcall = (_PyVectorcall_Function(func) != NULL); - + partial_setvectorcall(pto); return (PyObject *)pto; } @@ -126,77 +127,107 @@ partial_dealloc(partialobject *pto) Py_TYPE(pto)->tp_free(pto); } + +/* Merging keyword arguments using the vectorcall convention is messy, so + * if we would need to do that, we stop using vectorcall and fall back + * to using partial_call() instead. */ +_Py_NO_INLINE static PyObject * +partial_vectorcall_fallback(partialobject *pto, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + pto->vectorcall = NULL; + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + return _PyObject_MakeTpCall((PyObject *)pto, args, nargs, kwnames); +} + static PyObject * -partial_fastcall(partialobject *pto, PyObject **args, Py_ssize_t nargs, - PyObject *kwargs) +partial_vectorcall(partialobject *pto, PyObject *const *args, + size_t nargsf, PyObject *kwnames) { - PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; - PyObject *ret; - PyObject **stack, **stack_buf = NULL; - Py_ssize_t nargs2, pto_nargs; + /* pto->kw is mutable, so need to check every time */ + if (PyDict_GET_SIZE(pto->kw)) { + return partial_vectorcall_fallback(pto, args, nargsf, kwnames); + } + + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + Py_ssize_t nargs_total = nargs; + if (kwnames != NULL) { + nargs_total += PyTuple_GET_SIZE(kwnames); + } + + PyObject **pto_args = _PyTuple_ITEMS(pto->args); + Py_ssize_t pto_nargs = PyTuple_GET_SIZE(pto->args); - pto_nargs = PyTuple_GET_SIZE(pto->args); - nargs2 = pto_nargs + nargs; + /* Fast path if we're called without arguments */ + if (nargs_total == 0) { + return _PyObject_Vectorcall(pto->fn, pto_args, pto_nargs, NULL); + } - if (pto_nargs == 0) { - stack = args; + /* Fast path using PY_VECTORCALL_ARGUMENTS_OFFSET to prepend a single + * positional argument */ + if (pto_nargs == 1 && (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)) { + PyObject **newargs = (PyObject **)args - 1; + PyObject *tmp = newargs[0]; + newargs[0] = pto_args[0]; + PyObject *ret = _PyObject_Vectorcall(pto->fn, newargs, nargs + 1, kwnames); + newargs[0] = tmp; + return ret; } - else if (nargs == 0) { - stack = _PyTuple_ITEMS(pto->args); + + Py_ssize_t newnargs_total = pto_nargs + nargs_total; + + PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; + PyObject *ret; + PyObject **stack; + + if (newnargs_total <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { + stack = small_stack; } else { - if (nargs2 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { - stack = small_stack; - } - else { - stack_buf = PyMem_Malloc(nargs2 * sizeof(PyObject *)); - if (stack_buf == NULL) { - PyErr_NoMemory(); - return NULL; - } - stack = stack_buf; + stack = PyMem_Malloc(newnargs_total * sizeof(PyObject *)); + if (stack == NULL) { + PyErr_NoMemory(); + return NULL; } - - /* use borrowed references */ - memcpy(stack, - _PyTuple_ITEMS(pto->args), - pto_nargs * sizeof(PyObject*)); - memcpy(&stack[pto_nargs], - args, - nargs * sizeof(PyObject*)); } - ret = _PyObject_FastCallDict(pto->fn, stack, nargs2, kwargs); - PyMem_Free(stack_buf); + /* Copy to new stack, using borrowed references */ + memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); + memcpy(stack + pto_nargs, args, nargs_total * sizeof(PyObject*)); + + ret = _PyObject_Vectorcall(pto->fn, stack, pto_nargs + nargs, kwnames); + if (stack != small_stack) { + PyMem_Free(stack); + } return ret; } -static PyObject * -partial_call_impl(partialobject *pto, PyObject *args, PyObject *kwargs) +/* Set pto->vectorcall depending on the parameters of the partial object */ +static void +partial_setvectorcall(partialobject *pto) { - PyObject *ret, *args2; - - /* Note: tupleconcat() is optimized for empty tuples */ - args2 = PySequence_Concat(pto->args, args); - if (args2 == NULL) { - return NULL; + if (_PyVectorcall_Function(pto->fn) == NULL) { + /* Don't use vectorcall if the underlying function doesn't support it */ + pto->vectorcall = NULL; + } + /* We could have a special case if there are no arguments, + * but that is unlikely (why use partial without arguments?), + * so we don't optimize that */ + else { + pto->vectorcall = (vectorcallfunc)partial_vectorcall; } - assert(PyTuple_Check(args2)); - - ret = PyObject_Call(pto->fn, args2, kwargs); - Py_DECREF(args2); - return ret; } + static PyObject * partial_call(partialobject *pto, PyObject *args, PyObject *kwargs) { - PyObject *kwargs2, *res; - - assert (PyCallable_Check(pto->fn)); - assert (PyTuple_Check(pto->args)); - assert (PyDict_Check(pto->kw)); + assert(PyCallable_Check(pto->fn)); + assert(PyTuple_Check(pto->args)); + assert(PyDict_Check(pto->kw)); + /* Merge keywords */ + PyObject *kwargs2; if (PyDict_GET_SIZE(pto->kw) == 0) { /* kwargs can be NULL */ kwargs2 = kwargs; @@ -219,16 +250,16 @@ partial_call(partialobject *pto, PyObject *args, PyObject *kwargs) } } - - if (pto->use_fastcall) { - res = partial_fastcall(pto, - _PyTuple_ITEMS(args), - PyTuple_GET_SIZE(args), - kwargs2); - } - else { - res = partial_call_impl(pto, args, kwargs2); + /* Merge positional arguments */ + /* Note: tupleconcat() is optimized for empty tuples */ + PyObject *args2 = PySequence_Concat(pto->args, args); + if (args2 == NULL) { + Py_XDECREF(kwargs2); + return NULL; } + + PyObject *res = PyObject_Call(pto->fn, args2, kwargs2); + Py_DECREF(args2); Py_XDECREF(kwargs2); return res; } @@ -365,11 +396,11 @@ partial_setstate(partialobject *pto, PyObject *state) Py_INCREF(dict); Py_INCREF(fn); - pto->use_fastcall = (_PyVectorcall_Function(fn) != NULL); Py_SETREF(pto->fn, fn); Py_SETREF(pto->args, fnargs); Py_SETREF(pto->kw, kw); Py_XSETREF(pto->dict, dict); + partial_setvectorcall(pto); Py_RETURN_NONE; } @@ -386,7 +417,7 @@ static PyTypeObject partial_type = { 0, /* tp_itemsize */ /* methods */ (destructor)partial_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + offsetof(partialobject, vectorcall),/* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ @@ -401,7 +432,8 @@ static PyTypeObject partial_type = { PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_BASETYPE | + _Py_TPFLAGS_HAVE_VECTORCALL, /* tp_flags */ partial_doc, /* tp_doc */ (traverseproc)partial_traverse, /* tp_traverse */ 0, /* tp_clear */ From webhook-mailer at python.org Sat Jul 13 10:47:00 2019 From: webhook-mailer at python.org (larryhastings) Date: Sat, 13 Jul 2019 14:47:00 -0000 Subject: [Python-checkins] [3.5] Doc: Add an optional obsolete header. (GH-13638). (#13658) Message-ID: https://github.com/python/cpython/commit/84867166ec13d75c07809813588344054095d128 commit: 84867166ec13d75c07809813588344054095d128 branch: 3.5 author: Julien Palard committer: larryhastings date: 2019-07-13T16:46:56+02:00 summary: [3.5] Doc: Add an optional obsolete header. (GH-13638). (#13658) * [3.5] Doc: Add an optional obsolete header. (GH-13638). (cherry picked from commit 46ed90dd014010703c7a3b2a61c4927644fa8210) Co-authored-by: Julien Palard files: M Doc/README.txt M Doc/tools/templates/layout.html diff --git a/Doc/README.txt b/Doc/README.txt index 4f8e9f8f1417..f245d6164f5b 100644 --- a/Doc/README.txt +++ b/Doc/README.txt @@ -104,6 +104,15 @@ Then, from the ``Doc`` directory, run :: where ```` is one of html, text, latex, or htmlhelp (for explanations see the make targets above). +Deprecation header +================== + +You can define the ``outdated`` variable in ``html_context`` to show a +red banner on each page redirecting to the "latest" version. + +The link points to the same page on ``/3/``, sadly for the moment the +language is lost during the process. + Contributing ============ diff --git a/Doc/tools/templates/layout.html b/Doc/tools/templates/layout.html index c2106678ac60..7a7feb52e1a3 100644 --- a/Doc/tools/templates/layout.html +++ b/Doc/tools/templates/layout.html @@ -1,4 +1,15 @@ {% extends "!layout.html" %} + +{% block header %} +{%- if outdated %} +
+ {% trans %}This document is for an old version of Python that is no longer supported. + You should upgrade, and read the {% endtrans %} + {% trans %} Python documentation for the current stable release{% endtrans %}. +
+{%- endif %} +{% endblock %} + {% block rootrellink %}
  • From webhook-mailer at python.org Sat Jul 13 10:47:18 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 14:47:18 -0000 Subject: [Python-checkins] bpo-30088: Document that existing dir structure isn't verified by mailbox.Maildir (GH-1163) Message-ID: https://github.com/python/cpython/commit/e44184749c2fd0921867ea5cd20b8e226c2146c2 commit: e44184749c2fd0921867ea5cd20b8e226c2146c2 branch: master author: Sviatoslav Sydorenko committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-13T07:47:14-07:00 summary: bpo-30088: Document that existing dir structure isn't verified by mailbox.Maildir (GH-1163) Hi, I've faced an issue w/ `mailbox.Maildir()`. The case is following: 1. I create a folder with `tempfile.TemporaryDirectory()`, so it's empty 2. I pass that folder path as an argument when instantiating `mailbox.Maildir()` 3. Then I receive an exception happening because "there's no such file or directory" (namely `cur`, `tmp` or `new`) during interaction with Maildir **Expected result:** subdirs are created during `Maildir()` instance creation. **Actual result:** subdirs are assumed as existing which leads to exceptions during use. **Workaround:** remove the actual dir before passing the path to `Maildir()`. It will be created automatically with all subdirs needed. **Fix:** This PR. Basically it adds creation of subdirs regardless of whether the base dir existed before. https://bugs.python.org/issue30088 files: A Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst M Doc/library/mailbox.rst M Misc/ACKS diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index d901ad2cc1c4..f82a3b200deb 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -308,6 +308,9 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. representation. If *create* is ``True``, the mailbox is created if it does not exist. + If *create* is ``True`` and the *dirname* path exists, it will be treated as + an existing maildir without attempting to verify its directory layout. + It is for historical reasons that *dirname* is named as such rather than *path*. Maildir is a directory-based mailbox format invented for the qmail mail diff --git a/Misc/ACKS b/Misc/ACKS index 31f8dda888c8..b2a6011019c2 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1613,6 +1613,7 @@ Kalle Svensson Andrew Svetlov Paul Swartz Al Sweigart +Sviatoslav Sydorenko Thenault Sylvain P?ter Szab? John Szakmeister diff --git a/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst b/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst new file mode 100644 index 000000000000..a39fe3fbbcb6 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst @@ -0,0 +1 @@ +Documented that :class:`mailbox.Maildir` constructor doesn't attempt to verify the maildir folder layout correctness. Patch by Sviatoslav Sydorenko. \ No newline at end of file From webhook-mailer at python.org Sat Jul 13 10:59:36 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 14:59:36 -0000 Subject: [Python-checkins] bpo-30088: Document that existing dir structure isn't verified by mailbox.Maildir (GH-1163) Message-ID: https://github.com/python/cpython/commit/d1524148cd08f00c0b7c1dfdf698bf96c246350d commit: d1524148cd08f00c0b7c1dfdf698bf96c246350d branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-13T07:59:32-07:00 summary: bpo-30088: Document that existing dir structure isn't verified by mailbox.Maildir (GH-1163) Hi, I've faced an issue w/ `mailbox.Maildir()`. The case is following: 1. I create a folder with `tempfile.TemporaryDirectory()`, so it's empty 2. I pass that folder path as an argument when instantiating `mailbox.Maildir()` 3. Then I receive an exception happening because "there's no such file or directory" (namely `cur`, `tmp` or `new`) during interaction with Maildir **Expected result:** subdirs are created during `Maildir()` instance creation. **Actual result:** subdirs are assumed as existing which leads to exceptions during use. **Workaround:** remove the actual dir before passing the path to `Maildir()`. It will be created automatically with all subdirs needed. **Fix:** This PR. Basically it adds creation of subdirs regardless of whether the base dir existed before. https://bugs.python.org/issue30088 (cherry picked from commit e44184749c2fd0921867ea5cd20b8e226c2146c2) Co-authored-by: Sviatoslav Sydorenko files: A Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst M Doc/library/mailbox.rst M Misc/ACKS diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index d901ad2cc1c4..f82a3b200deb 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -308,6 +308,9 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. representation. If *create* is ``True``, the mailbox is created if it does not exist. + If *create* is ``True`` and the *dirname* path exists, it will be treated as + an existing maildir without attempting to verify its directory layout. + It is for historical reasons that *dirname* is named as such rather than *path*. Maildir is a directory-based mailbox format invented for the qmail mail diff --git a/Misc/ACKS b/Misc/ACKS index f7fc70afd173..4e2810300e6b 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1582,6 +1582,7 @@ Kalle Svensson Andrew Svetlov Paul Swartz Al Sweigart +Sviatoslav Sydorenko Thenault Sylvain P?ter Szab? John Szakmeister diff --git a/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst b/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst new file mode 100644 index 000000000000..a39fe3fbbcb6 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst @@ -0,0 +1 @@ +Documented that :class:`mailbox.Maildir` constructor doesn't attempt to verify the maildir folder layout correctness. Patch by Sviatoslav Sydorenko. \ No newline at end of file From webhook-mailer at python.org Sat Jul 13 10:59:52 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 13 Jul 2019 14:59:52 -0000 Subject: [Python-checkins] bpo-30088: Document that existing dir structure isn't verified by mailbox.Maildir (GH-1163) Message-ID: https://github.com/python/cpython/commit/b815669c833c543b0f6696c3121a179f6b2383a6 commit: b815669c833c543b0f6696c3121a179f6b2383a6 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-13T07:59:48-07:00 summary: bpo-30088: Document that existing dir structure isn't verified by mailbox.Maildir (GH-1163) Hi, I've faced an issue w/ `mailbox.Maildir()`. The case is following: 1. I create a folder with `tempfile.TemporaryDirectory()`, so it's empty 2. I pass that folder path as an argument when instantiating `mailbox.Maildir()` 3. Then I receive an exception happening because "there's no such file or directory" (namely `cur`, `tmp` or `new`) during interaction with Maildir **Expected result:** subdirs are created during `Maildir()` instance creation. **Actual result:** subdirs are assumed as existing which leads to exceptions during use. **Workaround:** remove the actual dir before passing the path to `Maildir()`. It will be created automatically with all subdirs needed. **Fix:** This PR. Basically it adds creation of subdirs regardless of whether the base dir existed before. https://bugs.python.org/issue30088 (cherry picked from commit e44184749c2fd0921867ea5cd20b8e226c2146c2) Co-authored-by: Sviatoslav Sydorenko files: A Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst M Doc/library/mailbox.rst M Misc/ACKS diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index d901ad2cc1c4..f82a3b200deb 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -308,6 +308,9 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. representation. If *create* is ``True``, the mailbox is created if it does not exist. + If *create* is ``True`` and the *dirname* path exists, it will be treated as + an existing maildir without attempting to verify its directory layout. + It is for historical reasons that *dirname* is named as such rather than *path*. Maildir is a directory-based mailbox format invented for the qmail mail diff --git a/Misc/ACKS b/Misc/ACKS index 53285403e695..f7032a1a96a2 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1608,6 +1608,7 @@ Kalle Svensson Andrew Svetlov Paul Swartz Al Sweigart +Sviatoslav Sydorenko Thenault Sylvain P?ter Szab? John Szakmeister diff --git a/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst b/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst new file mode 100644 index 000000000000..a39fe3fbbcb6 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst @@ -0,0 +1 @@ +Documented that :class:`mailbox.Maildir` constructor doesn't attempt to verify the maildir folder layout correctness. Patch by Sviatoslav Sydorenko. \ No newline at end of file From webhook-mailer at python.org Sat Jul 13 11:35:40 2019 From: webhook-mailer at python.org (larryhastings) Date: Sat, 13 Jul 2019 15:35:40 -0000 Subject: [Python-checkins] [3.5] bpo-36816: Update the self-signed.pythontest.net cert (GH-13192) (#13200) Message-ID: https://github.com/python/cpython/commit/221178aea686abf13ff92b7e2b5ed3e739a53b3f commit: 221178aea686abf13ff92b7e2b5ed3e739a53b3f branch: 3.5 author: Gregory P. Smith committer: larryhastings date: 2019-07-13T17:35:34+02:00 summary: [3.5] bpo-36816: Update the self-signed.pythontest.net cert (GH-13192) (#13200) * [3.5] bpo-36816: Update the self-signed.pythontest.net cert (GH-13192) We updated the server, our testsuite must match. https://bugs.python.org/issue36816 ?? CLE -> DEN ?? GH-pycon2019 (cherry picked from commit 6bd81734de0b73f1431880d6a75fb71bcbc65fa1) Co-authored-by: Gregory P. Smith files: A Lib/test/capath/efa5f9c3.0 A Misc/NEWS.d/next/Tests/2019-05-08-15-55-46.bpo-36816.WBKRGZ.rst M Lib/test/selfsigned_pythontestdotnet.pem diff --git a/Lib/test/capath/efa5f9c3.0 b/Lib/test/capath/efa5f9c3.0 new file mode 100644 index 000000000000..2b1760747bce --- /dev/null +++ b/Lib/test/capath/efa5f9c3.0 @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9zCCA9+gAwIBAgIUH98b4Fw/DyugC9cV7VK7ZODzHsIwDQYJKoZIhvcNAQEL +BQAwgYoxCzAJBgNVBAYTAlhZMRcwFQYDVQQIDA5DYXN0bGUgQW50aHJheDEYMBYG +A1UEBwwPQXJndW1lbnQgQ2xpbmljMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0aG9udGVzdC5uZXQw +HhcNMTkwNTA4MDEwMjQzWhcNMjcwNzI0MDEwMjQzWjCBijELMAkGA1UEBhMCWFkx +FzAVBgNVBAgMDkNhc3RsZSBBbnRocmF4MRgwFgYDVQQHDA9Bcmd1bWVudCBDbGlu +aWMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQD +DBpzZWxmLXNpZ25lZC5weXRob250ZXN0Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMKdJlyCThkahwoBb7pl5q64Pe9Fn5jrIvzsveHTc97TpjV2 +RLfICnXKrltPk/ohkVl6K5SUZQZwMVzFubkyxE0nZPHYHlpiKWQxbsYVkYv01rix +IFdLvaxxbGYke2jwQao31s4o61AdlsfK1SdpHQUynBBMssqI3SB4XPmcA7e+wEEx +jxjVish4ixA1vuIZOx8yibu+CFCf/geEjoBMF3QPdzULzlrCSw8k/45iZCSoNbvK +DoL4TVV07PHOxpheDh8ZQmepGvU6pVqhb9m4lgmV0OGWHgozd5Ur9CbTVDmxIEz3 +TSoRtNJK7qtyZdGNqwjksQxgZTjM/d/Lm/BJG99AiOmYOjsl9gbQMZgvQmMAtUsI +aMJnQuZ6R+KEpW/TR5qSKLWZSG45z/op+tzI2m+cE6HwTRVAWbcuJxcAA55MZjqU +OOOu3BBYMjS5nf2sQ9uoXsVBFH7i0mQqoW1SLzr9opI8KsWwFxQmO2vBxWYaN+lH +OmwBZBwyODIsmI1YGXmTp09NxRYz3Qe5GCgFzYowpMrcxUC24iduIdMwwhRM7rKg +7GtIWMSrFfuI1XCLRmSlhDbhNN6fVg2f8Bo9PdH9ihiIyxSrc+FOUasUYCCJvlSZ +8hFUlLvcmrZlWuazohm0lsXuMK1JflmQr/DA/uXxP9xzFfRy+RU3jDyxJbRHAgMB +AAGjUzBRMB0GA1UdDgQWBBSQJyxiPMRK01i+0BsV9zUwDiBaHzAfBgNVHSMEGDAW +gBSQJyxiPMRK01i+0BsV9zUwDiBaHzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4ICAQCR+7a7N/m+WLkxPPIA/CB4MOr2Uf8ixTv435Nyv6rXOun0+lTP +ExSZ0uYQ+L0WylItI3cQHULldDueD+s8TGzxf5woaLKf6tqyr0NYhKs+UeNEzDnN +9PHQIhX0SZw3XyXGUgPNBfRCg2ZDdtMMdOU4XlQN/IN/9hbYTrueyY7eXq9hmtI9 +1srftAMqr9SR1JP7aHI6DVgrEsZVMTDnfT8WmLSGLlY1HmGfdEn1Ip5sbo9uSkiH +AEPgPfjYIvR5LqTOMn4KsrlZyBbFIDh9Sl99M1kZzgH6zUGVLCDg1y6Cms69fx/e +W1HoIeVkY4b4TY7Bk7JsqyNhIuqu7ARaxkdaZWhYaA2YyknwANdFfNpfH+elCLIk +BUt5S3f4i7DaUePTvKukCZiCq4Oyln7RcOn5If73wCeLB/ZM9Ei1HforyLWP1CN8 +XLfpHaoeoPSWIveI0XHUl65LsPN2UbMbul/F23hwl+h8+BLmyAS680Yhn4zEN6Ku +B7Po90HoFa1Du3bmx4jsN73UkT/dwMTi6K072FbipnC1904oGlWmLwvAHvrtxxmL +Pl3pvEaZIu8wa/PNF6Y7J7VIewikIJq6Ta6FrWeFfzMWOj2qA1ZZi6fUaDSNYvuV +J5quYKCc/O+I/yDDf8wyBbZ/gvUXzUHTMYGG+bFrn1p7XDbYYeEJ6R/xEg== +-----END CERTIFICATE----- diff --git a/Lib/test/selfsigned_pythontestdotnet.pem b/Lib/test/selfsigned_pythontestdotnet.pem index b6d259bcb236..2b1760747bce 100644 --- a/Lib/test/selfsigned_pythontestdotnet.pem +++ b/Lib/test/selfsigned_pythontestdotnet.pem @@ -1,16 +1,34 @@ -----BEGIN CERTIFICATE----- -MIIClTCCAf6gAwIBAgIJAKGU95wKR8pTMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV -BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAMMGnNlbGYtc2lnbmVkLnB5dGhv -bnRlc3QubmV0MB4XDTE0MTEwMjE4MDkyOVoXDTI0MTAzMDE4MDkyOVowcDELMAkG -A1UEBhMCWFkxFzAVBgNVBAcMDkNhc3RsZSBBbnRocmF4MSMwIQYDVQQKDBpQeXRo -b24gU29mdHdhcmUgRm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0 -aG9udGVzdC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANDXQXW9tjyZ -Xt0Iv2tLL1+jinr4wGg36ioLDLFkMf+2Y1GL0v0BnKYG4N1OKlAU15LXGeGer8vm -Sv/yIvmdrELvhAbbo3w4a9TMYQA4XkIVLdvu3mvNOAet+8PMJxn26dbDhG809ALv -EHY57lQsBS3G59RZyBPVqAqmImWNJnVzAgMBAAGjNzA1MCUGA1UdEQQeMByCGnNl -bGYtc2lnbmVkLnB5dGhvbnRlc3QubmV0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN -AQEFBQADgYEAIuzAhgMouJpNdf3URCHIineyoSt6WK/9+eyUcjlKOrDoXNZaD72h -TXMeKYoWvJyVcSLKL8ckPtDobgP2OTt0UkyAaj0n+ZHaqq1lH2yVfGUA1ILJv515 -C8BqbvVZuqm3i7ygmw3bqE/lYMgOrYtXXnqOrz6nvsE6Yc9V9rFflOM= +MIIF9zCCA9+gAwIBAgIUH98b4Fw/DyugC9cV7VK7ZODzHsIwDQYJKoZIhvcNAQEL +BQAwgYoxCzAJBgNVBAYTAlhZMRcwFQYDVQQIDA5DYXN0bGUgQW50aHJheDEYMBYG +A1UEBwwPQXJndW1lbnQgQ2xpbmljMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0aG9udGVzdC5uZXQw +HhcNMTkwNTA4MDEwMjQzWhcNMjcwNzI0MDEwMjQzWjCBijELMAkGA1UEBhMCWFkx +FzAVBgNVBAgMDkNhc3RsZSBBbnRocmF4MRgwFgYDVQQHDA9Bcmd1bWVudCBDbGlu +aWMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQD +DBpzZWxmLXNpZ25lZC5weXRob250ZXN0Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMKdJlyCThkahwoBb7pl5q64Pe9Fn5jrIvzsveHTc97TpjV2 +RLfICnXKrltPk/ohkVl6K5SUZQZwMVzFubkyxE0nZPHYHlpiKWQxbsYVkYv01rix +IFdLvaxxbGYke2jwQao31s4o61AdlsfK1SdpHQUynBBMssqI3SB4XPmcA7e+wEEx +jxjVish4ixA1vuIZOx8yibu+CFCf/geEjoBMF3QPdzULzlrCSw8k/45iZCSoNbvK +DoL4TVV07PHOxpheDh8ZQmepGvU6pVqhb9m4lgmV0OGWHgozd5Ur9CbTVDmxIEz3 +TSoRtNJK7qtyZdGNqwjksQxgZTjM/d/Lm/BJG99AiOmYOjsl9gbQMZgvQmMAtUsI +aMJnQuZ6R+KEpW/TR5qSKLWZSG45z/op+tzI2m+cE6HwTRVAWbcuJxcAA55MZjqU +OOOu3BBYMjS5nf2sQ9uoXsVBFH7i0mQqoW1SLzr9opI8KsWwFxQmO2vBxWYaN+lH +OmwBZBwyODIsmI1YGXmTp09NxRYz3Qe5GCgFzYowpMrcxUC24iduIdMwwhRM7rKg +7GtIWMSrFfuI1XCLRmSlhDbhNN6fVg2f8Bo9PdH9ihiIyxSrc+FOUasUYCCJvlSZ +8hFUlLvcmrZlWuazohm0lsXuMK1JflmQr/DA/uXxP9xzFfRy+RU3jDyxJbRHAgMB +AAGjUzBRMB0GA1UdDgQWBBSQJyxiPMRK01i+0BsV9zUwDiBaHzAfBgNVHSMEGDAW +gBSQJyxiPMRK01i+0BsV9zUwDiBaHzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4ICAQCR+7a7N/m+WLkxPPIA/CB4MOr2Uf8ixTv435Nyv6rXOun0+lTP +ExSZ0uYQ+L0WylItI3cQHULldDueD+s8TGzxf5woaLKf6tqyr0NYhKs+UeNEzDnN +9PHQIhX0SZw3XyXGUgPNBfRCg2ZDdtMMdOU4XlQN/IN/9hbYTrueyY7eXq9hmtI9 +1srftAMqr9SR1JP7aHI6DVgrEsZVMTDnfT8WmLSGLlY1HmGfdEn1Ip5sbo9uSkiH +AEPgPfjYIvR5LqTOMn4KsrlZyBbFIDh9Sl99M1kZzgH6zUGVLCDg1y6Cms69fx/e +W1HoIeVkY4b4TY7Bk7JsqyNhIuqu7ARaxkdaZWhYaA2YyknwANdFfNpfH+elCLIk +BUt5S3f4i7DaUePTvKukCZiCq4Oyln7RcOn5If73wCeLB/ZM9Ei1HforyLWP1CN8 +XLfpHaoeoPSWIveI0XHUl65LsPN2UbMbul/F23hwl+h8+BLmyAS680Yhn4zEN6Ku +B7Po90HoFa1Du3bmx4jsN73UkT/dwMTi6K072FbipnC1904oGlWmLwvAHvrtxxmL +Pl3pvEaZIu8wa/PNF6Y7J7VIewikIJq6Ta6FrWeFfzMWOj2qA1ZZi6fUaDSNYvuV +J5quYKCc/O+I/yDDf8wyBbZ/gvUXzUHTMYGG+bFrn1p7XDbYYeEJ6R/xEg== -----END CERTIFICATE----- diff --git a/Misc/NEWS.d/next/Tests/2019-05-08-15-55-46.bpo-36816.WBKRGZ.rst b/Misc/NEWS.d/next/Tests/2019-05-08-15-55-46.bpo-36816.WBKRGZ.rst new file mode 100644 index 000000000000..420dfe832366 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2019-05-08-15-55-46.bpo-36816.WBKRGZ.rst @@ -0,0 +1 @@ +Update Lib/test/selfsigned_pythontestdotnet.pem to match self-signed.pythontest.net's new TLS certificate. \ No newline at end of file From webhook-mailer at python.org Sat Jul 13 11:50:07 2019 From: webhook-mailer at python.org (Mark Dickinson) Date: Sat, 13 Jul 2019 15:50:07 -0000 Subject: [Python-checkins] Fix inconsequential typo in math.remainder algorithm comments. (#14746) Message-ID: https://github.com/python/cpython/commit/014847034bfc67e7b7d7e9e01d664e1e8864be9d commit: 014847034bfc67e7b7d7e9e01d664e1e8864be9d branch: master author: Mark Dickinson committer: GitHub date: 2019-07-13T16:50:03+01:00 summary: Fix inconsequential typo in math.remainder algorithm comments. (#14746) files: M Modules/mathmodule.c diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 92c40b3a9ff1..4c1dbbe15ecc 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -646,7 +646,7 @@ m_remainder(double x, double y) Warning: some subtlety here. What we *want* to know at this point is whether the remainder m is less than, equal to, or greater than half of absy. However, we can't do that comparison directly because we - can't be sure that 0.5*absy is representable (the mutiplication + can't be sure that 0.5*absy is representable (the multiplication might incur precision loss due to underflow). So instead we compare m with the complement c = absy - m: m < 0.5*absy if and only if m < c, and so on. The catch is that absy - m might also not be From webhook-mailer at python.org Sat Jul 13 18:12:49 2019 From: webhook-mailer at python.org (larryhastings) Date: Sat, 13 Jul 2019 22:12:49 -0000 Subject: [Python-checkins] Fix compatibility with ISO C89 needed by "gnu89" standard of GCC 4.8: use C89 for loops in backported pickle patch (#12622) Message-ID: https://github.com/python/cpython/commit/43a0ae920bb8962d20148cfbdf37a60c1ad45f5b commit: 43a0ae920bb8962d20148cfbdf37a60c1ad45f5b branch: 3.5 author: Anthony Sottile committer: larryhastings date: 2019-07-14T00:12:44+02:00 summary: Fix compatibility with ISO C89 needed by "gnu89" standard of GCC 4.8: use C89 for loops in backported pickle patch (#12622) files: A Misc/NEWS.d/next/Build/2019-03-29-14-29-06.bpo-36478.hzyneF.rst M Modules/_pickle.c diff --git a/Misc/NEWS.d/next/Build/2019-03-29-14-29-06.bpo-36478.hzyneF.rst b/Misc/NEWS.d/next/Build/2019-03-29-14-29-06.bpo-36478.hzyneF.rst new file mode 100644 index 000000000000..942ad9a31656 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2019-03-29-14-29-06.bpo-36478.hzyneF.rst @@ -0,0 +1 @@ +Fix compatibility with ISO C89 needed by "gnu89" standard of GCC 4.8: use C89 for loops in backported pickle patch. Patch by Anthony Sottile. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index fcb9e8789951..3b4003f5cacf 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -658,6 +658,7 @@ PyMemoTable_New(void) static PyMemoTable * PyMemoTable_Copy(PyMemoTable *self) { + size_t i; PyMemoTable *new = PyMemoTable_New(); if (new == NULL) return NULL; @@ -674,7 +675,7 @@ PyMemoTable_Copy(PyMemoTable *self) PyErr_NoMemory(); return NULL; } - for (size_t i = 0; i < self->mt_allocated; i++) { + for (i = 0; i < self->mt_allocated; i++) { Py_XINCREF(self->mt_table[i].me_key); } memcpy(new->mt_table, self->mt_table, @@ -4198,13 +4199,14 @@ static PyObject * _pickle_PicklerMemoProxy_copy_impl(PicklerMemoProxyObject *self) /*[clinic end generated code: output=bb83a919d29225ef input=b73043485ac30b36]*/ { + size_t i; PyMemoTable *memo; PyObject *new_memo = PyDict_New(); if (new_memo == NULL) return NULL; memo = self->pickler->memo; - for (size_t i = 0; i < memo->mt_allocated; ++i) { + for (i = 0; i < memo->mt_allocated; ++i) { PyMemoEntry entry = memo->mt_table[i]; if (entry.me_key != NULL) { int status; @@ -6773,6 +6775,7 @@ Unpickler_get_memo(UnpicklerObject *self) static int Unpickler_set_memo(UnpicklerObject *self, PyObject *obj) { + size_t i; PyObject **new_memo; size_t new_memo_size = 0; @@ -6791,7 +6794,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj) if (new_memo == NULL) return -1; - for (size_t i = 0; i < new_memo_size; i++) { + for (i = 0; i < new_memo_size; i++) { Py_XINCREF(unpickler->memo[i]); new_memo[i] = unpickler->memo[i]; } @@ -6839,7 +6842,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj) error: if (new_memo_size) { - for (size_t i = new_memo_size - 1; i != SIZE_MAX; i--) { + for (i = new_memo_size - 1; i != SIZE_MAX; i--) { Py_XDECREF(new_memo[i]); } PyMem_FREE(new_memo); From webhook-mailer at python.org Sun Jul 14 01:36:03 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 05:36:03 -0000 Subject: [Python-checkins] bpo-26967: fix flag grouping with allow_abbrev=False (GH-14316) Message-ID: https://github.com/python/cpython/commit/dffca9e925ee5c3072663cbe8d4d4768406d5307 commit: dffca9e925ee5c3072663cbe8d4d4768406d5307 branch: master author: Zac Hatfield-Dodds committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-13T22:35:58-07:00 summary: bpo-26967: fix flag grouping with allow_abbrev=False (GH-14316) The `allow_abbrev` option for ArgumentParser is documented and intended to disable support for unique prefixes of --options, which may sometimes be ambiguous due to deferred parsing. However, the initial implementation also broke parsing of grouped short flags, such as `-ab` meaning `-a -b` (or `-a=b`). Checking the argument for a leading `--` before rejecting it fixes this. This was prompted by pytest-dev/pytest#5469, so a backport to at least 3.8 would be great :smile: And this is my first PR to CPython, so please let me know if I've missed anything! https://bugs.python.org/issue26967 files: A Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst M Doc/library/argparse.rst M Lib/argparse.py M Lib/test/test_argparse.py M Misc/ACKS diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index b77a38ccd485..ef2fd42783c8 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -182,6 +182,10 @@ ArgumentParser objects .. versionchanged:: 3.5 *allow_abbrev* parameter was added. + .. versionchanged:: 3.8 + In previous versions, *allow_abbrev* also disabled grouping of short + flags such as ``-vv`` to mean ``-v -v``. + The following sections describe how each of these are used. diff --git a/Lib/argparse.py b/Lib/argparse.py index 4f3aea928bf6..e45b67b67704 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2130,7 +2130,7 @@ def _parse_optional(self, arg_string): action = self._option_string_actions[option_string] return action, option_string, explicit_arg - if self.allow_abbrev: + if self.allow_abbrev or not arg_string.startswith('--'): # search through all possible prefixes of the option string # and all actions in the parser for possible interpretations option_tuples = self._get_option_tuples(arg_string) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 9079d4bc7aa7..5128dc5e1be4 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -785,6 +785,25 @@ class TestOptionalsDisallowLongAbbreviation(ParserTestCase): ('--foonly 7 --foodle --foo 2', NS(foo='2', foodle=True, foonly='7')), ] + +class TestDisallowLongAbbreviationAllowsShortGrouping(ParserTestCase): + """Do not allow abbreviations of long options at all""" + + parser_signature = Sig(allow_abbrev=False) + argument_signatures = [ + Sig('-r'), + Sig('-c', action='count'), + ] + failures = ['-r', '-c -r'] + successes = [ + ('', NS(r=None, c=None)), + ('-ra', NS(r='a', c=None)), + ('-rcc', NS(r='cc', c=None)), + ('-cc', NS(r=None, c=2)), + ('-cc -ra', NS(r='a', c=2)), + ('-ccrcc', NS(r='cc', c=2)), + ] + # ================ # Positional tests # ================ diff --git a/Misc/ACKS b/Misc/ACKS index b2a6011019c2..3e6a0aca3573 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -645,6 +645,7 @@ Travis B. Hartwell Shane Harvey Larry Hastings Tim Hatch +Zac Hatfield-Dodds Shane Hathaway Michael Haubenwallner Janko Hauser diff --git a/Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst b/Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst new file mode 100644 index 000000000000..c5852f614215 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst @@ -0,0 +1,3 @@ +An :class:`~argparse.ArgumentParser` with ``allow_abbrev=False`` no longer +disables grouping of short flags, such as ``-vv``, but only disables +abbreviation of long flags as documented. Patch by Zac Hatfield-Dodds. From webhook-mailer at python.org Sun Jul 14 02:00:05 2019 From: webhook-mailer at python.org (Petr Viktorin) Date: Sun, 14 Jul 2019 06:00:05 -0000 Subject: [Python-checkins] bpo-26967: fix flag grouping with allow_abbrev=False (GH-14316) (GH-14759) Message-ID: https://github.com/python/cpython/commit/b1e4d1b6032d4c82b549233fa08a2c7cfe7e818b commit: b1e4d1b6032d4c82b549233fa08a2c7cfe7e818b branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Petr Viktorin date: 2019-07-14T07:59:56+02:00 summary: bpo-26967: fix flag grouping with allow_abbrev=False (GH-14316) (GH-14759) The `allow_abbrev` option for ArgumentParser is documented and intended to disable support for unique prefixes of --options, which may sometimes be ambiguous due to deferred parsing. However, the initial implementation also broke parsing of grouped short flags, such as `-ab` meaning `-a -b` (or `-a=b`). Checking the argument for a leading `--` before rejecting it fixes this. This was prompted by pytest-dev/pytestGH-5469, so a backport to at least 3.8 would be great :smile: And this is my first PR to CPython, so please let me know if I've missed anything! https://bugs.python.org/issue26967 (cherry picked from commit dffca9e925ee5c3072663cbe8d4d4768406d5307) Co-authored-by: Zac Hatfield-Dodds files: A Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst M Doc/library/argparse.rst M Lib/argparse.py M Lib/test/test_argparse.py M Misc/ACKS diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index b77a38ccd485..ef2fd42783c8 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -182,6 +182,10 @@ ArgumentParser objects .. versionchanged:: 3.5 *allow_abbrev* parameter was added. + .. versionchanged:: 3.8 + In previous versions, *allow_abbrev* also disabled grouping of short + flags such as ``-vv`` to mean ``-v -v``. + The following sections describe how each of these are used. diff --git a/Lib/argparse.py b/Lib/argparse.py index 9a67b41ae00e..5820d0d8ca99 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2132,7 +2132,7 @@ def _parse_optional(self, arg_string): action = self._option_string_actions[option_string] return action, option_string, explicit_arg - if self.allow_abbrev: + if self.allow_abbrev or not arg_string.startswith('--'): # search through all possible prefixes of the option string # and all actions in the parser for possible interpretations option_tuples = self._get_option_tuples(arg_string) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 9079d4bc7aa7..5128dc5e1be4 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -785,6 +785,25 @@ class TestOptionalsDisallowLongAbbreviation(ParserTestCase): ('--foonly 7 --foodle --foo 2', NS(foo='2', foodle=True, foonly='7')), ] + +class TestDisallowLongAbbreviationAllowsShortGrouping(ParserTestCase): + """Do not allow abbreviations of long options at all""" + + parser_signature = Sig(allow_abbrev=False) + argument_signatures = [ + Sig('-r'), + Sig('-c', action='count'), + ] + failures = ['-r', '-c -r'] + successes = [ + ('', NS(r=None, c=None)), + ('-ra', NS(r='a', c=None)), + ('-rcc', NS(r='cc', c=None)), + ('-cc', NS(r=None, c=2)), + ('-cc -ra', NS(r='a', c=2)), + ('-ccrcc', NS(r='cc', c=2)), + ] + # ================ # Positional tests # ================ diff --git a/Misc/ACKS b/Misc/ACKS index f7032a1a96a2..5c7ec998a547 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -641,6 +641,7 @@ Travis B. Hartwell Shane Harvey Larry Hastings Tim Hatch +Zac Hatfield-Dodds Shane Hathaway Michael Haubenwallner Janko Hauser diff --git a/Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst b/Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst new file mode 100644 index 000000000000..c5852f614215 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst @@ -0,0 +1,3 @@ +An :class:`~argparse.ArgumentParser` with ``allow_abbrev=False`` no longer +disables grouping of short flags, such as ``-vv``, but only disables +abbreviation of long flags as documented. Patch by Zac Hatfield-Dodds. From webhook-mailer at python.org Sun Jul 14 03:04:23 2019 From: webhook-mailer at python.org (larryhastings) Date: Sun, 14 Jul 2019 07:04:23 -0000 Subject: [Python-checkins] bpo-35907, CVE-2019-9948: urllib rejects local_file:// scheme (GH-13474) (GH-13505) (#13510) Message-ID: https://github.com/python/cpython/commit/4fe82a8eef7aed60de05bfca0f2c322730ea921e commit: 4fe82a8eef7aed60de05bfca0f2c322730ea921e branch: 3.5 author: Victor Stinner committer: larryhastings date: 2019-07-14T09:04:15+02:00 summary: bpo-35907, CVE-2019-9948: urllib rejects local_file:// scheme (GH-13474) (GH-13505) (#13510) CVE-2019-9948: Avoid file reading by disallowing local-file:// and local_file:// URL schemes in URLopener().open() and URLopener().retrieve() of urllib.request. Co-Authored-By: SH files: A Misc/NEWS.d/next/Security/2019-05-21-23-20-18.bpo-35907.NC_zNK.rst M Lib/test/test_urllib.py M Lib/urllib/request.py diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 1a28c9a21d14..3afb1312de32 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -16,6 +16,7 @@ ssl = None import sys import tempfile +import warnings from nturl2path import url2pathname, pathname2url from base64 import b64encode @@ -1409,6 +1410,23 @@ def open_spam(self, url): "spam://c:|windows%/:=&?~#+!$,;'@()*[]|/path/"), "//c:|windows%/:=&?~#+!$,;'@()*[]|/path/") + def test_local_file_open(self): + # bpo-35907, CVE-2019-9948: urllib must reject local_file:// scheme + class DummyURLopener(urllib.request.URLopener): + def open_local_file(self, url): + return url + + with warnings.catch_warnings(record=True): + warnings.simplefilter("ignore", DeprecationWarning) + + for url in ('local_file://example', 'local-file://example'): + self.assertRaises(OSError, urllib.request.urlopen, url) + self.assertRaises(OSError, urllib.request.URLopener().open, url) + self.assertRaises(OSError, urllib.request.URLopener().retrieve, url) + self.assertRaises(OSError, DummyURLopener().open, url) + self.assertRaises(OSError, DummyURLopener().retrieve, url) + + # Just commented them out. # Can't really tell why keep failing in windows and sparc. # Everywhere else they work ok, but on those machines, sometimes diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index e98be0cde1d3..3184ddab24a0 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1683,7 +1683,7 @@ def open(self, fullurl, data=None): name = 'open_' + urltype self.type = urltype name = name.replace('-', '_') - if not hasattr(self, name): + if not hasattr(self, name) or name == 'open_local_file': if proxy: return self.open_unknown_proxy(proxy, fullurl, data) else: diff --git a/Misc/NEWS.d/next/Security/2019-05-21-23-20-18.bpo-35907.NC_zNK.rst b/Misc/NEWS.d/next/Security/2019-05-21-23-20-18.bpo-35907.NC_zNK.rst new file mode 100644 index 000000000000..37b567a5b6f9 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2019-05-21-23-20-18.bpo-35907.NC_zNK.rst @@ -0,0 +1,3 @@ +CVE-2019-9948: Avoid file reading by disallowing ``local-file://`` and +``local_file://`` URL schemes in ``URLopener().open()`` and +``URLopener().retrieve()`` of :mod:`urllib.request`. From webhook-mailer at python.org Sun Jul 14 03:46:29 2019 From: webhook-mailer at python.org (Steve Dower) Date: Sun, 14 Jul 2019 07:46:29 -0000 Subject: [Python-checkins] bpo-36261: Improve example of the preamble field in email docs (GH-14751) Message-ID: https://github.com/python/cpython/commit/8efade91b12a13102a09a3856179021e579da5e9 commit: 8efade91b12a13102a09a3856179021e579da5e9 branch: master author: Carl Bordum Hansen committer: Steve Dower date: 2019-07-14T09:46:18+02:00 summary: bpo-36261: Improve example of the preamble field in email docs (GH-14751) files: M Doc/includes/email-mime.py diff --git a/Doc/includes/email-mime.py b/Doc/includes/email-mime.py index c610242f11f8..6af2be0b08a4 100644 --- a/Doc/includes/email-mime.py +++ b/Doc/includes/email-mime.py @@ -14,7 +14,7 @@ # family = the list of all recipients' email addresses msg['From'] = me msg['To'] = ', '.join(family) -msg.preamble = 'Our family reunion' +msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' # Open the files in binary mode. Use imghdr to figure out the # MIME subtype for each specific image. From webhook-mailer at python.org Sun Jul 14 03:51:54 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 07:51:54 -0000 Subject: [Python-checkins] bpo-36261: Improve example of the preamble field in email docs (GH-14751) Message-ID: https://github.com/python/cpython/commit/fb58024688050385ed2fd93caf02a5807d1ccfed commit: fb58024688050385ed2fd93caf02a5807d1ccfed branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-14T00:51:49-07:00 summary: bpo-36261: Improve example of the preamble field in email docs (GH-14751) (cherry picked from commit 8efade91b12a13102a09a3856179021e579da5e9) Co-authored-by: Carl Bordum Hansen files: M Doc/includes/email-mime.py diff --git a/Doc/includes/email-mime.py b/Doc/includes/email-mime.py index c610242f11f8..6af2be0b08a4 100644 --- a/Doc/includes/email-mime.py +++ b/Doc/includes/email-mime.py @@ -14,7 +14,7 @@ # family = the list of all recipients' email addresses msg['From'] = me msg['To'] = ', '.join(family) -msg.preamble = 'Our family reunion' +msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' # Open the files in binary mode. Use imghdr to figure out the # MIME subtype for each specific image. From webhook-mailer at python.org Sun Jul 14 03:53:19 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 07:53:19 -0000 Subject: [Python-checkins] bpo-36261: Improve example of the preamble field in email docs (GH-14751) Message-ID: https://github.com/python/cpython/commit/262779fb5038db7574180c6a89b4f0968f29bd79 commit: 262779fb5038db7574180c6a89b4f0968f29bd79 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-14T00:53:15-07:00 summary: bpo-36261: Improve example of the preamble field in email docs (GH-14751) (cherry picked from commit 8efade91b12a13102a09a3856179021e579da5e9) Co-authored-by: Carl Bordum Hansen files: M Doc/includes/email-mime.py diff --git a/Doc/includes/email-mime.py b/Doc/includes/email-mime.py index c610242f11f8..6af2be0b08a4 100644 --- a/Doc/includes/email-mime.py +++ b/Doc/includes/email-mime.py @@ -14,7 +14,7 @@ # family = the list of all recipients' email addresses msg['From'] = me msg['To'] = ', '.join(family) -msg.preamble = 'Our family reunion' +msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' # Open the files in binary mode. Use imghdr to figure out the # MIME subtype for each specific image. From webhook-mailer at python.org Sun Jul 14 03:55:14 2019 From: webhook-mailer at python.org (Steve Dower) Date: Sun, 14 Jul 2019 07:55:14 -0000 Subject: [Python-checkins] bpo-37571: Add 'b' to prevent the TypeError exception. (GH-14721) Message-ID: https://github.com/python/cpython/commit/6b929580eb018cfef386db7f7f66b3a58532eada commit: 6b929580eb018cfef386db7f7f66b3a58532eada branch: master author: Michele Angrisano committer: Steve Dower date: 2019-07-14T09:55:11+02:00 summary: bpo-37571: Add 'b' to prevent the TypeError exception. (GH-14721) Co-Authored-By: Kyle Stanley files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 680703d4483f..5507cc6aad16 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1178,12 +1178,17 @@ the root-object's underlying buffer. Another example that may behave different from what one would expect is this:: >>> s = c_char_p() - >>> s.value = "abc def ghi" + >>> s.value = b"abc def ghi" >>> s.value - 'abc def ghi' + b'abc def ghi' >>> s.value is s.value False - >>> + >>> + +.. note:: + + Objects instantiated from :class:`c_char_p` can only have their value set to bytes + or integers. Why is it printing ``False``? ctypes instances are objects containing a memory block plus some :term:`descriptor`\s accessing the contents of the memory. From webhook-mailer at python.org Sun Jul 14 04:07:10 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 08:07:10 -0000 Subject: [Python-checkins] bpo-37571: Add 'b' to prevent the TypeError exception. (GH-14721) Message-ID: https://github.com/python/cpython/commit/e7c114df38eaef0cec3457f55835a2276eccbff6 commit: e7c114df38eaef0cec3457f55835a2276eccbff6 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-14T01:07:06-07:00 summary: bpo-37571: Add 'b' to prevent the TypeError exception. (GH-14721) Co-Authored-By: Kyle Stanley (cherry picked from commit 6b929580eb018cfef386db7f7f66b3a58532eada) Co-authored-by: Michele Angrisano files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 680703d4483f..5507cc6aad16 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1178,12 +1178,17 @@ the root-object's underlying buffer. Another example that may behave different from what one would expect is this:: >>> s = c_char_p() - >>> s.value = "abc def ghi" + >>> s.value = b"abc def ghi" >>> s.value - 'abc def ghi' + b'abc def ghi' >>> s.value is s.value False - >>> + >>> + +.. note:: + + Objects instantiated from :class:`c_char_p` can only have their value set to bytes + or integers. Why is it printing ``False``? ctypes instances are objects containing a memory block plus some :term:`descriptor`\s accessing the contents of the memory. From webhook-mailer at python.org Sun Jul 14 04:08:52 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 08:08:52 -0000 Subject: [Python-checkins] bpo-37571: Add 'b' to prevent the TypeError exception. (GH-14721) Message-ID: https://github.com/python/cpython/commit/d7caf75c73626d7df4c0628c63761738b7063463 commit: d7caf75c73626d7df4c0628c63761738b7063463 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-14T01:08:48-07:00 summary: bpo-37571: Add 'b' to prevent the TypeError exception. (GH-14721) Co-Authored-By: Kyle Stanley (cherry picked from commit 6b929580eb018cfef386db7f7f66b3a58532eada) Co-authored-by: Michele Angrisano files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 46a9d23ac392..b5dfaa00c1d6 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1178,12 +1178,17 @@ the root-object's underlying buffer. Another example that may behave different from what one would expect is this:: >>> s = c_char_p() - >>> s.value = "abc def ghi" + >>> s.value = b"abc def ghi" >>> s.value - 'abc def ghi' + b'abc def ghi' >>> s.value is s.value False - >>> + >>> + +.. note:: + + Objects instantiated from :class:`c_char_p` can only have their value set to bytes + or integers. Why is it printing ``False``? ctypes instances are objects containing a memory block plus some :term:`descriptor`\s accessing the contents of the memory. From webhook-mailer at python.org Sun Jul 14 04:09:48 2019 From: webhook-mailer at python.org (Steve Dower) Date: Sun, 14 Jul 2019 08:09:48 -0000 Subject: [Python-checkins] bpo-37571: Remove extra space in ctypes docs (GH14764) Message-ID: https://github.com/python/cpython/commit/68c74d05c1fdaf59d8711431884af975ac2ac5f8 commit: 68c74d05c1fdaf59d8711431884af975ac2ac5f8 branch: master author: Steve Dower committer: GitHub date: 2019-07-14T10:09:45+02:00 summary: bpo-37571: Remove extra space in ctypes docs (GH14764) files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 5507cc6aad16..02a5500e8115 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1183,7 +1183,7 @@ Another example that may behave different from what one would expect is this:: b'abc def ghi' >>> s.value is s.value False - >>> + >>> .. note:: From webhook-mailer at python.org Sun Jul 14 04:16:23 2019 From: webhook-mailer at python.org (larryhastings) Date: Sun, 14 Jul 2019 08:16:23 -0000 Subject: [Python-checkins] bpo-36742: Fixes handling of pre-normalization characters in urlsplit() (GH-13017) (#13042) Message-ID: https://github.com/python/cpython/commit/4655d576141ee56a69d2052431c636858fcb916a commit: 4655d576141ee56a69d2052431c636858fcb916a branch: 3.5 author: Steve Dower committer: larryhastings date: 2019-07-14T10:16:19+02:00 summary: bpo-36742: Fixes handling of pre-normalization characters in urlsplit() (GH-13017) (#13042) files: A Misc/NEWS.d/next/Security/2019-04-29-15-34-59.bpo-36742.QCUY0i.rst M Lib/test/test_urlparse.py M Lib/urllib/parse.py diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py index d0420b0e742d..1e90e18609b2 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -987,6 +987,12 @@ def test_urlsplit_normalization(self): self.assertIn('\u2100', denorm_chars) self.assertIn('\uFF03', denorm_chars) + # bpo-36742: Verify port separators are ignored when they + # existed prior to decomposition + urllib.parse.urlsplit('http://\u30d5\u309a:80') + with self.assertRaises(ValueError): + urllib.parse.urlsplit('http://\u30d5\u309a\ufe1380') + for scheme in ["http", "https", "ftp"]: for c in denorm_chars: url = "{}://netloc{}false.netloc/path".format(scheme, c) diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py index 7ba2b445f5cd..7405d660fc4e 100644 --- a/Lib/urllib/parse.py +++ b/Lib/urllib/parse.py @@ -333,13 +333,16 @@ def _checknetloc(netloc): # looking for characters like \u2100 that expand to 'a/c' # IDNA uses NFKC equivalence, so normalize for this check import unicodedata - netloc2 = unicodedata.normalize('NFKC', netloc) - if netloc == netloc2: + n = netloc.rpartition('@')[2] # ignore anything to the left of '@' + n = n.replace(':', '') # ignore characters already included + n = n.replace('#', '') # but not the surrounding text + n = n.replace('?', '') + netloc2 = unicodedata.normalize('NFKC', n) + if n == netloc2: return - _, _, netloc = netloc.rpartition('@') # anything to the left of '@' is okay for c in '/?#@:': if c in netloc2: - raise ValueError("netloc '" + netloc2 + "' contains invalid " + + raise ValueError("netloc '" + netloc + "' contains invalid " + "characters under NFKC normalization") def urlsplit(url, scheme='', allow_fragments=True): diff --git a/Misc/NEWS.d/next/Security/2019-04-29-15-34-59.bpo-36742.QCUY0i.rst b/Misc/NEWS.d/next/Security/2019-04-29-15-34-59.bpo-36742.QCUY0i.rst new file mode 100644 index 000000000000..d729ed2f3cd5 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2019-04-29-15-34-59.bpo-36742.QCUY0i.rst @@ -0,0 +1 @@ +Fixes mishandling of pre-normalization characters in urlsplit(). From webhook-mailer at python.org Sun Jul 14 04:21:00 2019 From: webhook-mailer at python.org (Steve Dower) Date: Sun, 14 Jul 2019 08:21:00 -0000 Subject: [Python-checkins] Remove redundant docs of PyEval_EvalFrameEx (GH-14765) Message-ID: https://github.com/python/cpython/commit/40d2226a69aed6252023d365731bd4ed39dc1a4f commit: 40d2226a69aed6252023d365731bd4ed39dc1a4f branch: master author: Hai Shi committer: Steve Dower date: 2019-07-14T10:20:56+02:00 summary: Remove redundant docs of PyEval_EvalFrameEx (GH-14765) files: M Doc/c-api/veryhigh.rst diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst index e6704ddeca09..67dc11dfa9a5 100644 --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -335,12 +335,12 @@ the same library that the Python runtime is using. .. c:function:: PyObject* PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) - This is the main, unvarnished function of Python interpretation. It is - literally 2000 lines long. The code object associated with the execution - frame *f* is executed, interpreting bytecode and executing calls as needed. - The additional *throwflag* parameter can mostly be ignored - if true, then - it causes an exception to immediately be thrown; this is used for the - :meth:`~generator.throw` methods of generator objects. + This is the main, unvarnished function of Python interpretation. The code + object associated with the execution frame *f* is executed, interpreting + bytecode and executing calls as needed. The additional *throwflag* + parameter can mostly be ignored - if true, then it causes an exception + to immediately be thrown; this is used for the :meth:`~generator.throw` + methods of generator objects. .. versionchanged:: 3.4 This function now includes a debug assertion to help ensure that it From webhook-mailer at python.org Sun Jul 14 04:23:00 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 08:23:00 -0000 Subject: [Python-checkins] bpo-37571: Remove extra space in ctypes docs (GH14764) Message-ID: https://github.com/python/cpython/commit/4f733f48b48735231b79cd0f6605d3d0a2d5b511 commit: 4f733f48b48735231b79cd0f6605d3d0a2d5b511 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-14T01:22:57-07:00 summary: bpo-37571: Remove extra space in ctypes docs (GH14764) (cherry picked from commit 68c74d05c1fdaf59d8711431884af975ac2ac5f8) Co-authored-by: Steve Dower files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 5507cc6aad16..02a5500e8115 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1183,7 +1183,7 @@ Another example that may behave different from what one would expect is this:: b'abc def ghi' >>> s.value is s.value False - >>> + >>> .. note:: From webhook-mailer at python.org Sun Jul 14 04:24:39 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 08:24:39 -0000 Subject: [Python-checkins] bpo-37571: Remove extra space in ctypes docs (GH14764) Message-ID: https://github.com/python/cpython/commit/13c89f3c876363c44d35ec5f8dc374aecbca62a1 commit: 13c89f3c876363c44d35ec5f8dc374aecbca62a1 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-14T01:24:35-07:00 summary: bpo-37571: Remove extra space in ctypes docs (GH14764) (cherry picked from commit 68c74d05c1fdaf59d8711431884af975ac2ac5f8) Co-authored-by: Steve Dower files: M Doc/library/ctypes.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index b5dfaa00c1d6..6cd4f9779f6d 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1183,7 +1183,7 @@ Another example that may behave different from what one would expect is this:: b'abc def ghi' >>> s.value is s.value False - >>> + >>> .. note:: From webhook-mailer at python.org Sun Jul 14 04:30:32 2019 From: webhook-mailer at python.org (Steve Dower) Date: Sun, 14 Jul 2019 08:30:32 -0000 Subject: [Python-checkins] bpo-37553: SendfileUsingSendTest tests timeout too short for Windows ARM32 (GH-14716) Message-ID: https://github.com/python/cpython/commit/52c8c090870c4e45dc48d1991d7ef7de2e40b2a8 commit: 52c8c090870c4e45dc48d1991d7ef7de2e40b2a8 branch: master author: Paul Monson committer: Steve Dower date: 2019-07-14T10:30:28+02:00 summary: bpo-37553: SendfileUsingSendTest tests timeout too short for Windows ARM32 (GH-14716) files: M Lib/test/test_socket.py diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index e92f871880a9..2705eff4794e 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5769,7 +5769,8 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): FILESIZE = (10 * 1024 * 1024) # 10 MiB BUFSIZE = 8192 FILEDATA = b"" - TIMEOUT = 2 + # bpo-37553: This is taking longer than 2 seconds on Windows ARM32 buildbot + TIMEOUT = 10 if sys.platform == 'win32' and platform.machine() == 'ARM' else 2 @classmethod def setUpClass(cls): From webhook-mailer at python.org Sun Jul 14 04:53:22 2019 From: webhook-mailer at python.org (Steve Dower) Date: Sun, 14 Jul 2019 08:53:22 -0000 Subject: [Python-checkins] bpo-37590: Remove redundant docs of PyEval_EvalFrameEx (GH-14765) Message-ID: https://github.com/python/cpython/commit/7a430109b983806c57babf229c60d0245d0b541c commit: 7a430109b983806c57babf229c60d0245d0b541c branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Steve Dower date: 2019-07-14T10:53:18+02:00 summary: bpo-37590: Remove redundant docs of PyEval_EvalFrameEx (GH-14765) (cherry picked from commit 40d2226a69aed6252023d365731bd4ed39dc1a4f) Co-authored-by: Hai Shi files: M Doc/c-api/veryhigh.rst diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst index e6704ddeca09..67dc11dfa9a5 100644 --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -335,12 +335,12 @@ the same library that the Python runtime is using. .. c:function:: PyObject* PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) - This is the main, unvarnished function of Python interpretation. It is - literally 2000 lines long. The code object associated with the execution - frame *f* is executed, interpreting bytecode and executing calls as needed. - The additional *throwflag* parameter can mostly be ignored - if true, then - it causes an exception to immediately be thrown; this is used for the - :meth:`~generator.throw` methods of generator objects. + This is the main, unvarnished function of Python interpretation. The code + object associated with the execution frame *f* is executed, interpreting + bytecode and executing calls as needed. The additional *throwflag* + parameter can mostly be ignored - if true, then it causes an exception + to immediately be thrown; this is used for the :meth:`~generator.throw` + methods of generator objects. .. versionchanged:: 3.4 This function now includes a debug assertion to help ensure that it From webhook-mailer at python.org Sun Jul 14 05:01:52 2019 From: webhook-mailer at python.org (Ronald Oussoren) Date: Sun, 14 Jul 2019 09:01:52 -0000 Subject: [Python-checkins] Clarify that plistlib's load and dump functions take a binary file object (GH-9825) Message-ID: https://github.com/python/cpython/commit/0d4f4352efecf1b044c88e234e71774fe04b7d6c commit: 0d4f4352efecf1b044c88e234e71774fe04b7d6c branch: master author: Collin Styles committer: Ronald Oussoren date: 2019-07-14T11:01:48+02:00 summary: Clarify that plistlib's load and dump functions take a binary file object (GH-9825) The documentation says that the fp parameter to plistlib.load "should be a readable and binary file object" but the docstring only mentions that it should be readable. Similarly, plistlib.dump's docstring only mentions "writable". This commit clarifies that fp should also be binary. https://docs.python.org/3/library/plistlib.html#plistlib.load https://docs.python.org/3/library/plistlib.html#plistlib.dump files: M Lib/plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 0133c89bdc66..04f8a87634d3 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -971,7 +971,7 @@ def _is_fmt_binary(header): def load(fp, *, fmt=None, use_builtin_types=True, dict_type=dict): - """Read a .plist file. 'fp' should be (readable) file object. + """Read a .plist file. 'fp' should be a readable and binary file object. Return the unpacked root object (which usually is a dictionary). """ if fmt is None: @@ -1002,8 +1002,8 @@ def loads(value, *, fmt=None, use_builtin_types=True, dict_type=dict): def dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False): - """Write 'value' to a .plist file. 'fp' should be a (writable) - file object. + """Write 'value' to a .plist file. 'fp' should be a writable, + binary file object. """ if fmt not in _FORMATS: raise ValueError("Unsupported format: %r"%(fmt,)) From webhook-mailer at python.org Sun Jul 14 05:07:15 2019 From: webhook-mailer at python.org (larryhastings) Date: Sun, 14 Jul 2019 09:07:15 -0000 Subject: [Python-checkins] bpo-30458: Disallow control chars in http URLs. (GH-12755) (#13207) Message-ID: https://github.com/python/cpython/commit/afe3a4975cf93c97e5d6eb8800e48f368011d37a commit: afe3a4975cf93c97e5d6eb8800e48f368011d37a branch: 3.5 author: Miro Hron?ok committer: larryhastings date: 2019-07-14T11:07:11+02:00 summary: bpo-30458: Disallow control chars in http URLs. (GH-12755) (#13207) Disallow control chars in http URLs in urllib.urlopen. This addresses a potential security problem for applications that do not sanity check their URLs where http request headers could be injected. Disable https related urllib tests on a build without ssl (GH-13032) These tests require an SSL enabled build. Skip these tests when python is built without SSL to fix test failures. Use http.client.InvalidURL instead of ValueError as the new error case's exception. (GH-13044) Co-Authored-By: Miro Hron?ok files: A Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst M Lib/http/client.py M Lib/test/test_urllib.py M Lib/test/test_xmlrpc.py diff --git a/Lib/http/client.py b/Lib/http/client.py index 352c1017adce..76b9be69a374 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -141,6 +141,16 @@ _is_legal_header_name = re.compile(rb'[^:\s][^:\r\n]*').fullmatch _is_illegal_header_value = re.compile(rb'\n(?![ \t])|\r(?![ \t\n])').search +# These characters are not allowed within HTTP URL paths. +# See https://tools.ietf.org/html/rfc3986#section-3.3 and the +# https://tools.ietf.org/html/rfc3986#appendix-A pchar definition. +# Prevents CVE-2019-9740. Includes control characters such as \r\n. +# We don't restrict chars above \x7f as putrequest() limits us to ASCII. +_contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f]') +# Arguably only these _should_ allowed: +# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$") +# We are more lenient for assumed real world compatibility purposes. + # We always set the Content-Length header for these methods because some # servers will otherwise respond with a 411 _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'} @@ -978,6 +988,12 @@ def putrequest(self, method, url, skip_host=False, self._method = method if not url: url = '/' + # Prevent CVE-2019-9740. + match = _contains_disallowed_url_pchar_re.search(url) + if match: + raise InvalidURL("URL can't contain control characters. {!r} " + "(found at least {!r})".format(url, + match.group())) request = '%s %s %s' % (method, url, self._http_vsn_str) # Non-ASCII characters should have been eliminated earlier diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 3afb1312de32..1e2c622e29fd 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -330,6 +330,61 @@ def test_willclose(self): finally: self.unfakehttp() + @unittest.skipUnless(ssl, "ssl module required") + def test_url_with_control_char_rejected(self): + for char_no in list(range(0, 0x21)) + [0x7f]: + char = chr(char_no) + schemeless_url = "//localhost:7777/test{}/".format(char) + self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.") + try: + # We explicitly test urllib.request.urlopen() instead of the top + # level 'def urlopen()' function defined in this... (quite ugly) + # test suite. They use different url opening codepaths. Plain + # urlopen uses FancyURLOpener which goes via a codepath that + # calls urllib.parse.quote() on the URL which makes all of the + # above attempts at injection within the url _path_ safe. + escaped_char_repr = repr(char).replace('\\', r'\\') + InvalidURL = http.client.InvalidURL + with self.assertRaisesRegex( + InvalidURL, + "contain control.*{}".format(escaped_char_repr)): + urllib.request.urlopen("http:{}".format(schemeless_url)) + with self.assertRaisesRegex( + InvalidURL, + "contain control.*{}".format(escaped_char_repr)): + urllib.request.urlopen("https:{}".format(schemeless_url)) + # This code path quotes the URL so there is no injection. + resp = urlopen("http:{}".format(schemeless_url)) + self.assertNotIn(char, resp.geturl()) + finally: + self.unfakehttp() + + @unittest.skipUnless(ssl, "ssl module required") + def test_url_with_newline_header_injection_rejected(self): + self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.") + host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123" + schemeless_url = "//" + host + ":8080/test/?test=a" + try: + # We explicitly test urllib.request.urlopen() instead of the top + # level 'def urlopen()' function defined in this... (quite ugly) + # test suite. They use different url opening codepaths. Plain + # urlopen uses FancyURLOpener which goes via a codepath that + # calls urllib.parse.quote() on the URL which makes all of the + # above attempts at injection within the url _path_ safe. + InvalidURL = http.client.InvalidURL + with self.assertRaisesRegex( + InvalidURL, r"contain control.*\\r.*(found at least . .)"): + urllib.request.urlopen("http:{}".format(schemeless_url)) + with self.assertRaisesRegex(InvalidURL, r"contain control.*\\n"): + urllib.request.urlopen("https:{}".format(schemeless_url)) + # This code path quotes the URL so there is no injection. + resp = urlopen("http:{}".format(schemeless_url)) + self.assertNotIn(' ', resp.geturl()) + self.assertNotIn('\r', resp.geturl()) + self.assertNotIn('\n', resp.geturl()) + finally: + self.unfakehttp() + def test_read_0_9(self): # "0.9" response accepted (but not "simple responses" without # a status line) diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index c2de057ecbfa..99e510fcee86 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -896,7 +896,13 @@ def test_unicode_host(self): def test_partial_post(self): # Check that a partial POST doesn't make the server loop: issue #14001. conn = http.client.HTTPConnection(ADDR, PORT) - conn.request('POST', '/RPC2 HTTP/1.0\r\nContent-Length: 100\r\n\r\nbye') + conn.send('POST /RPC2 HTTP/1.0\r\n' + 'Content-Length: 100\r\n\r\n' + 'bye HTTP/1.1\r\n' + 'Host: {}:{}\r\n' + 'Accept-Encoding: identity\r\n' + 'Content-Length: 0\r\n\r\n' + .format(ADDR, PORT).encode('ascii')) conn.close() def test_context_manager(self): diff --git a/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst b/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst new file mode 100644 index 000000000000..ed8027fb4d64 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst @@ -0,0 +1 @@ +Address CVE-2019-9740 by disallowing URL paths with embedded whitespace or control characters through into the underlying http client request. Such potentially malicious header injection URLs now cause an http.client.InvalidURL exception to be raised. From webhook-mailer at python.org Sun Jul 14 05:23:42 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 09:23:42 -0000 Subject: [Python-checkins] Clarify that plistlib's load and dump functions take a binary file object (GH-9825) Message-ID: https://github.com/python/cpython/commit/5631e381a6fbc471896b5562d542be0a6befa797 commit: 5631e381a6fbc471896b5562d542be0a6befa797 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-14T02:23:38-07:00 summary: Clarify that plistlib's load and dump functions take a binary file object (GH-9825) The documentation says that the fp parameter to plistlib.load "should be a readable and binary file object" but the docstring only mentions that it should be readable. Similarly, plistlib.dump's docstring only mentions "writable". This commit clarifies that fp should also be binary. https://docs.python.org/3/library/plistlib.htmlGH-plistlib.load https://docs.python.org/3/library/plistlib.htmlGH-plistlib.dump (cherry picked from commit 0d4f4352efecf1b044c88e234e71774fe04b7d6c) Co-authored-by: Collin Styles files: M Lib/plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 21ebec3f0045..33b79a133b0a 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -929,7 +929,7 @@ def _is_fmt_binary(header): def load(fp, *, fmt=None, use_builtin_types=True, dict_type=dict): - """Read a .plist file. 'fp' should be (readable) file object. + """Read a .plist file. 'fp' should be a readable and binary file object. Return the unpacked root object (which usually is a dictionary). """ if fmt is None: @@ -960,8 +960,8 @@ def loads(value, *, fmt=None, use_builtin_types=True, dict_type=dict): def dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False): - """Write 'value' to a .plist file. 'fp' should be a (writable) - file object. + """Write 'value' to a .plist file. 'fp' should be a writable, + binary file object. """ if fmt not in _FORMATS: raise ValueError("Unsupported format: %r"%(fmt,)) From webhook-mailer at python.org Sun Jul 14 05:25:47 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 09:25:47 -0000 Subject: [Python-checkins] Clarify that plistlib's load and dump functions take a binary file object (GH-9825) Message-ID: https://github.com/python/cpython/commit/3958b7aae8cc7c571a842ff43c1dea1da9abdd9d commit: 3958b7aae8cc7c571a842ff43c1dea1da9abdd9d branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-14T02:25:43-07:00 summary: Clarify that plistlib's load and dump functions take a binary file object (GH-9825) The documentation says that the fp parameter to plistlib.load "should be a readable and binary file object" but the docstring only mentions that it should be readable. Similarly, plistlib.dump's docstring only mentions "writable". This commit clarifies that fp should also be binary. https://docs.python.org/3/library/plistlib.htmlGH-plistlib.load https://docs.python.org/3/library/plistlib.htmlGH-plistlib.dump (cherry picked from commit 0d4f4352efecf1b044c88e234e71774fe04b7d6c) Co-authored-by: Collin Styles files: M Lib/plistlib.py diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 0133c89bdc66..04f8a87634d3 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -971,7 +971,7 @@ def _is_fmt_binary(header): def load(fp, *, fmt=None, use_builtin_types=True, dict_type=dict): - """Read a .plist file. 'fp' should be (readable) file object. + """Read a .plist file. 'fp' should be a readable and binary file object. Return the unpacked root object (which usually is a dictionary). """ if fmt is None: @@ -1002,8 +1002,8 @@ def loads(value, *, fmt=None, use_builtin_types=True, dict_type=dict): def dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False): - """Write 'value' to a .plist file. 'fp' should be a (writable) - file object. + """Write 'value' to a .plist file. 'fp' should be a writable, + binary file object. """ if fmt not in _FORMATS: raise ValueError("Unsupported format: %r"%(fmt,)) From webhook-mailer at python.org Sun Jul 14 06:14:03 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 10:14:03 -0000 Subject: [Python-checkins] [3.7] bpo-37579: Improve equality behavior for pure Python datetime and time (GH-14726) (GH-14745) Message-ID: https://github.com/python/cpython/commit/c6b31061997526b31961ec34328408ca421f51fc commit: c6b31061997526b31961ec34328408ca421f51fc branch: 3.7 author: Xtreak committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-14T03:13:59-07:00 summary: [3.7] bpo-37579: Improve equality behavior for pure Python datetime and time (GH-14726) (GH-14745) Returns NotImplemented for timedelta and time in __eq__ for different types in Python implementation, which matches the C implementation. This also adds tests to enforce that these objects will fall back to the right hand side's __eq__ and/or __ne__ implementation. [bpo-37579](https://bugs.python.org/issue37579) (cherry picked from commit e6b46aafad3427463d6264a68824df4797e682f1) Co-authored-by: Xtreak https://bugs.python.org/issue37579 files: A Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst M Lib/datetime.py M Lib/test/datetimetester.py diff --git a/Lib/datetime.py b/Lib/datetime.py index a964b202e3c7..03beb055a00b 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -718,7 +718,7 @@ def __eq__(self, other): if isinstance(other, timedelta): return self._cmp(other) == 0 else: - return False + return NotImplemented def __le__(self, other): if isinstance(other, timedelta): @@ -1261,7 +1261,7 @@ def __eq__(self, other): if isinstance(other, time): return self._cmp(other, allow_mixed=True) == 0 else: - return False + return NotImplemented def __le__(self, other): if isinstance(other, time): diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 2f8975d8c07f..581441738658 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -53,6 +53,19 @@ INF = float("inf") NAN = float("nan") + +class ComparesEqualClass(object): + """ + A class that is always equal to whatever you compare it to. + """ + + def __eq__(self, other): + return True + + def __ne__(self, other): + return False + + ############################################################################# # module tests @@ -399,6 +412,13 @@ def test_harmless_mixed_comparison(self): self.assertIn(me, [1, 20, [], me]) self.assertIn([], [me, 1, 20, []]) + # Comparison to objects of unsupported types should return + # NotImplemented which falls back to the right hand side's __eq__ + # method. In this case, ComparesEqualClass.__eq__ always returns True. + # ComparesEqualClass.__ne__ always returns False. + self.assertTrue(me == ComparesEqualClass()) + self.assertFalse(me != ComparesEqualClass()) + def test_harmful_mixed_comparison(self): me = self.theclass(1, 1, 1) diff --git a/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst b/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst new file mode 100644 index 000000000000..ad52cf2a06cc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst @@ -0,0 +1,4 @@ +Return :exc:`NotImplemented` in Python implementation of ``__eq__`` for +:class:`~datetime.timedelta` and :class:`~datetime.time` when the other +object being compared is not of the same type to match C implementation. +Patch by Karthikeyan Singaravelan. From webhook-mailer at python.org Sun Jul 14 08:15:36 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 12:15:36 -0000 Subject: [Python-checkins] bpo-34749: Improved performance of binascii.a2b_base64(). (GH-9444) Message-ID: https://github.com/python/cpython/commit/1c5e68e7145f0825f9b952389141edb9436eb43d commit: 1c5e68e7145f0825f9b952389141edb9436eb43d branch: master author: Sergey Fedoseev committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-14T05:15:32-07:00 summary: bpo-34749: Improved performance of binascii.a2b_base64(). (GH-9444) https://bugs.python.org/issue34749 files: A Misc/NEWS.d/next/Library/2018-09-21-13-23-29.bpo-34749.B0k819.rst M Modules/binascii.c diff --git a/Misc/NEWS.d/next/Library/2018-09-21-13-23-29.bpo-34749.B0k819.rst b/Misc/NEWS.d/next/Library/2018-09-21-13-23-29.bpo-34749.B0k819.rst new file mode 100644 index 000000000000..5a5e5b492c0b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-21-13-23-29.bpo-34749.B0k819.rst @@ -0,0 +1,2 @@ +:func:`binascii.a2b_base64` is now up to 2 times faster. Patch by Sergey +Fedoseev. diff --git a/Modules/binascii.c b/Modules/binascii.c index 1c7dc35882de..94b0732c12c8 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -130,7 +130,7 @@ static const unsigned char table_a2b_hqx[256] = { static const unsigned char table_b2a_hqx[] = "!\"#$%&'()*+,-012345689 at ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr"; -static const char table_a2b_base64[] = { +static const unsigned char table_a2b_base64[] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, @@ -138,7 +138,16 @@ static const char table_a2b_base64[] = { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, - 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1, + + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, }; #define BASE64_PAD '=' @@ -413,32 +422,6 @@ binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick) return _PyBytesWriter_Finish(&writer, ascii_data); } - -static int -binascii_find_valid(const unsigned char *s, Py_ssize_t slen, int num) -{ - /* Finds & returns the (num+1)th - ** valid character for base64, or -1 if none. - */ - - int ret = -1; - unsigned char c, b64val; - - while ((slen > 0) && (ret == -1)) { - c = *s; - b64val = table_a2b_base64[c & 0x7f]; - if ( ((c <= 0x7f) && (b64val != (unsigned char)-1)) ) { - if (num == 0) - ret = *s; - num--; - } - - s++; - slen--; - } - return ret; -} - /*[clinic input] binascii.a2b_base64 @@ -452,88 +435,74 @@ static PyObject * binascii_a2b_base64_impl(PyObject *module, Py_buffer *data) /*[clinic end generated code: output=0628223f19fd3f9b input=5872acf6e1cac243]*/ { - const unsigned char *ascii_data; - unsigned char *bin_data; - unsigned char *bin_data_start; - int leftbits = 0; - unsigned char this_ch; - unsigned int leftchar = 0; - Py_ssize_t ascii_len, bin_len; - int quad_pos = 0; - _PyBytesWriter writer; - binascii_state *state; - - ascii_data = data->buf; - ascii_len = data->len; + assert(data->len >= 0); - assert(ascii_len >= 0); - - if (ascii_len > PY_SSIZE_T_MAX - 3) - return PyErr_NoMemory(); - - bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */ - - _PyBytesWriter_Init(&writer); + const unsigned char *ascii_data = data->buf; + size_t ascii_len = data->len; /* Allocate the buffer */ - bin_data = _PyBytesWriter_Alloc(&writer, bin_len); + Py_ssize_t bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */ + _PyBytesWriter writer; + _PyBytesWriter_Init(&writer); + unsigned char *bin_data = _PyBytesWriter_Alloc(&writer, bin_len); if (bin_data == NULL) return NULL; - bin_data_start = bin_data; - - for( ; ascii_len > 0; ascii_len--, ascii_data++) { - this_ch = *ascii_data; + unsigned char *bin_data_start = bin_data; - if (this_ch > 0x7f || - this_ch == '\r' || this_ch == '\n' || this_ch == ' ') - continue; + int quad_pos = 0; + unsigned char leftchar = 0; + int pads = 0; + for (size_t i = 0; i < ascii_len; i++) { + unsigned char this_ch = ascii_data[i]; /* Check for pad sequences and ignore ** the invalid ones. */ if (this_ch == BASE64_PAD) { - if ( (quad_pos < 2) || - ((quad_pos == 2) && - (binascii_find_valid(ascii_data, ascii_len, 1) - != BASE64_PAD)) ) - { - continue; - } - else { + if (quad_pos >= 2 && quad_pos + ++pads >= 4) { /* A pad sequence means no more input. ** We've already interpreted the data ** from the quad at this point. */ - leftbits = 0; - break; + goto done; } + continue; } - this_ch = table_a2b_base64[*ascii_data]; - if ( this_ch == (unsigned char) -1 ) + this_ch = table_a2b_base64[this_ch]; + if (this_ch >= 64) { continue; + } + pads = 0; - /* - ** Shift it in on the low end, and see if there's - ** a byte ready for output. - */ - quad_pos = (quad_pos + 1) & 0x03; - leftchar = (leftchar << 6) | (this_ch); - leftbits += 6; - - if ( leftbits >= 8 ) { - leftbits -= 8; - *bin_data++ = (leftchar >> leftbits) & 0xff; - leftchar &= ((1 << leftbits) - 1); + switch (quad_pos) { + case 0: + quad_pos = 1; + leftchar = this_ch; + break; + case 1: + quad_pos = 2; + *bin_data++ = (leftchar << 2) | (this_ch >> 4); + leftchar = this_ch & 0x0f; + break; + case 2: + quad_pos = 3; + *bin_data++ = (leftchar << 4) | (this_ch >> 2); + leftchar = this_ch & 0x03; + break; + case 3: + quad_pos = 0; + *bin_data++ = (leftchar << 6) | (this_ch); + leftchar = 0; + break; } } - if (leftbits != 0) { - state = PyModule_GetState(module); + if (quad_pos != 0) { + binascii_state *state = PyModule_GetState(module); if (state == NULL) { - return NULL; - } - if (leftbits == 6) { + /* error already set, from PyModule_GetState */ + } else if (quad_pos == 1) { /* ** There is exactly one extra valid, non-padding, base64 character. ** This is an invalid length, as there is no possible input that @@ -551,6 +520,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data) return NULL; } +done: return _PyBytesWriter_Finish(&writer, bin_data); } From webhook-mailer at python.org Sun Jul 14 13:31:25 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Sun, 14 Jul 2019 17:31:25 -0000 Subject: [Python-checkins] bpo-37473: Don't import importlib ASAP in tests (GH-14661) Message-ID: https://github.com/python/cpython/commit/8b7db5a1114e2113a756bdf8877fbe366055c69a commit: 8b7db5a1114e2113a756bdf8877fbe366055c69a branch: master author: Victor Stinner committer: GitHub date: 2019-07-14T19:31:12+02:00 summary: bpo-37473: Don't import importlib ASAP in tests (GH-14661) bpo-15386, bpo-37473: test_import, regrtest and libregrtest no longer import importlib as soon as possible, as the first import, "to test bpo-15386". It is tested by test_import.test_there_can_be_only_one(). Sort test_import imports. files: M Lib/test/libregrtest/__init__.py M Lib/test/regrtest.py M Lib/test/test_import/__init__.py diff --git a/Lib/test/libregrtest/__init__.py b/Lib/test/libregrtest/__init__.py index 3427b51b60af..5e8dba5dbde7 100644 --- a/Lib/test/libregrtest/__init__.py +++ b/Lib/test/libregrtest/__init__.py @@ -1,5 +1,2 @@ -# We import importlib *ASAP* in order to test #15386 -import importlib - from test.libregrtest.cmdline import _parse_args, RESOURCE_NAMES, ALL_RESOURCES from test.libregrtest.main import main diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 21b0edfd073d..0ffb3ed454ed 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -6,9 +6,6 @@ Run this script with -h or --help for documentation. """ -# We import importlib *ASAP* in order to test #15386 -import importlib - import os import sys from test.libregrtest import main diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 50406d9aa1d9..88746b4315f9 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1,24 +1,22 @@ -# We import importlib *ASAP* in order to test #15386 -import importlib +import builtins +import contextlib +import errno +import glob import importlib.util from importlib._bootstrap_external import _get_sourcefile -import builtins import marshal import os import py_compile import random import shutil -import subprocess import stat +import subprocess import sys +import textwrap import threading import time import unittest -import unittest.mock as mock -import textwrap -import errno -import contextlib -import glob +from unittest import mock import test.support from test.support import ( From webhook-mailer at python.org Sun Jul 14 19:32:22 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 23:32:22 -0000 Subject: [Python-checkins] bpo-37593: Swap the positions of posonlyargs and args in the constructor of ast.parameters nodes (GH-14778) Message-ID: https://github.com/python/cpython/commit/cd6e83b4810549c308ab2d7315dbab526e35ccf6 commit: cd6e83b4810549c308ab2d7315dbab526e35ccf6 branch: master author: Pablo Galindo committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-14T16:32:18-07:00 summary: bpo-37593: Swap the positions of posonlyargs and args in the constructor of ast.parameters nodes (GH-14778) https://bugs.python.org/issue37593 files: A Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH.rst M Include/Python-ast.h M Lib/importlib/_bootstrap_external.py M Lib/test/test_ast.py M Parser/Python.asdl M Python/Python-ast.c M Python/ast.c M Python/importlib_external.h diff --git a/Include/Python-ast.h b/Include/Python-ast.h index 62626503403b..5fe4f2b4314c 100644 --- a/Include/Python-ast.h +++ b/Include/Python-ast.h @@ -426,8 +426,8 @@ struct _excepthandler { }; struct _arguments { - asdl_seq *args; asdl_seq *posonlyargs; + asdl_seq *args; arg_ty vararg; asdl_seq *kwonlyargs; asdl_seq *kw_defaults; @@ -687,7 +687,7 @@ excepthandler_ty _Py_ExceptHandler(expr_ty type, identifier name, asdl_seq * end_lineno, int end_col_offset, PyArena *arena); #define arguments(a0, a1, a2, a3, a4, a5, a6, a7) _Py_arguments(a0, a1, a2, a3, a4, a5, a6, a7) -arguments_ty _Py_arguments(asdl_seq * args, asdl_seq * posonlyargs, arg_ty +arguments_ty _Py_arguments(asdl_seq * posonlyargs, asdl_seq * args, arg_ty vararg, asdl_seq * kwonlyargs, asdl_seq * kw_defaults, arg_ty kwarg, asdl_seq * defaults, PyArena *arena); diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 64185771abc8..1bafc242c518 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -268,6 +268,8 @@ def _write_atomic(path, data, mode=0o666): # Python 3.8a1 3410 (PEP570 Python Positional-Only Parameters #36540) # Python 3.8b2 3411 (Reverse evaluation order of key: value in dict # comprehensions #35224) +# Python 3.8b2 3412 (Swap the position of positional args and positional +# only args in ast.arguments #37593) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -276,7 +278,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3411).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3412).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 6bd27ce12a62..5c37a5fbed6e 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -331,8 +331,8 @@ def test_field_attr_existence(self): def test_arguments(self): x = ast.arguments() - self.assertEqual(x._fields, ('args', 'posonlyargs', 'vararg', 'kwonlyargs', - 'kw_defaults', 'kwarg', 'defaults')) + self.assertEqual(x._fields, ('posonlyargs', 'args', 'vararg', 'kwonlyargs', + 'kw_defaults', 'kwarg', 'defaults')) with self.assertRaises(AttributeError): x.vararg @@ -1683,11 +1683,11 @@ def main(): ('Module', [('Expr', (1, 0), ('Constant', (1, 0), 'module docstring', None))], []), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 9))], [], None, None)], []), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9), ('Constant', (1, 9), 'function docstring', None))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, []), [('Pass', (1, 10))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, [('Constant', (1, 8), 0, None)]), [('Pass', (1, 12))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 10))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None)], None, [], [], None, [('Constant', (1, 8), 0, None)]), [('Pass', (1, 12))], [], None, None)], []), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], ('arg', (1, 7), 'args', None, None), [], [], None, []), [('Pass', (1, 14))], [], None, None)], []), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], ('arg', (1, 8), 'kwargs', None, None), []), [('Pass', (1, 17))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None), ('arg', (1, 9), 'b', None, None), ('arg', (1, 14), 'c', None, None), ('arg', (1, 22), 'd', None, None), ('arg', (1, 28), 'e', None, None)], [], ('arg', (1, 35), 'args', None, None), [('arg', (1, 41), 'f', None, None)], [('Constant', (1, 43), 42, None)], ('arg', (1, 49), 'kwargs', None, None), [('Constant', (1, 11), 1, None), ('Constant', (1, 16), None, None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [('Expr', (1, 58), ('Constant', (1, 58), 'doc for f()', None))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None), ('arg', (1, 9), 'b', None, None), ('arg', (1, 14), 'c', None, None), ('arg', (1, 22), 'd', None, None), ('arg', (1, 28), 'e', None, None)], ('arg', (1, 35), 'args', None, None), [('arg', (1, 41), 'f', None, None)], [('Constant', (1, 43), 42, None)], ('arg', (1, 49), 'kwargs', None, None), [('Constant', (1, 11), 1, None), ('Constant', (1, 16), None, None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [('Expr', (1, 58), ('Constant', (1, 58), 'doc for f()', None))], [], None, None)], []), ('Module', [('ClassDef', (1, 0), 'C', [], [], [('Pass', (1, 8))], [])], []), ('Module', [('ClassDef', (1, 0), 'C', [], [], [('Expr', (1, 9), ('Constant', (1, 9), 'docstring for class C', None))], [])], []), ('Module', [('ClassDef', (1, 0), 'C', [('Name', (1, 8), 'object', ('Load',))], [], [('Pass', (1, 17))], [])], []), @@ -1733,16 +1733,16 @@ def main(): ('Module', [('ClassDef', (3, 0), 'C', [], [], [('Pass', (3, 9))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])])], []), ('Module', [('FunctionDef', (2, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9))], [('Call', (1, 1), ('Name', (1, 1), 'deco', ('Load',)), [('GeneratorExp', (1, 5), ('Name', (1, 6), 'a', ('Load',)), [('comprehension', ('Name', (1, 12), 'a', ('Store',)), ('Name', (1, 17), 'b', ('Load',)), [], 0)])], [])], None, None)], []), ('Module', [('Expr', (1, 0), ('NamedExpr', (1, 1), ('Name', (1, 1), 'a', ('Store',)), ('Constant', (1, 6), 1, None)))], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 14))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 12), 'c', None, None), ('arg', (1, 15), 'd', None, None), ('arg', (1, 18), 'e', None, None)], [('arg', (1, 6), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 22))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 12), 'c', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 18), 'd', None, None), ('arg', (1, 21), 'e', None, None)], [None, None], None, []), [('Pass', (1, 25))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 12), 'c', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 18), 'd', None, None), ('arg', (1, 21), 'e', None, None)], [None, None], ('arg', (1, 26), 'kwargs', None, None), []), [('Pass', (1, 35))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None)], None, [], [], None, [('Constant', (1, 8), 1, None)]), [('Pass', (1, 16))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None), ('arg', (1, 19), 'c', None, None)], [('arg', (1, 6), 'a', None, None)], None, [], [], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None), ('Constant', (1, 21), 4, None)]), [('Pass', (1, 25))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 22), 'c', None, None)], [('Constant', (1, 24), 4, None)], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 28))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 22), 'c', None, None)], [None], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 26))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 22), 'c', None, None)], [('Constant', (1, 24), 4, None)], ('arg', (1, 29), 'kwargs', None, None), [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 38))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 22), 'c', None, None)], [None], ('arg', (1, 27), 'kwargs', None, None), [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 36))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, []), [('Pass', (1, 14))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 12), 'c', None, None), ('arg', (1, 15), 'd', None, None), ('arg', (1, 18), 'e', None, None)], None, [], [], None, []), [('Pass', (1, 22))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 12), 'c', None, None)], None, [('arg', (1, 18), 'd', None, None), ('arg', (1, 21), 'e', None, None)], [None, None], None, []), [('Pass', (1, 25))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 12), 'c', None, None)], None, [('arg', (1, 18), 'd', None, None), ('arg', (1, 21), 'e', None, None)], [None, None], ('arg', (1, 26), 'kwargs', None, None), []), [('Pass', (1, 35))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, [('Constant', (1, 8), 1, None)]), [('Pass', (1, 16))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None), ('arg', (1, 19), 'c', None, None)], None, [], [], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None), ('Constant', (1, 21), 4, None)]), [('Pass', (1, 25))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None)], None, [('arg', (1, 22), 'c', None, None)], [('Constant', (1, 24), 4, None)], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 28))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None)], None, [('arg', (1, 22), 'c', None, None)], [None], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 26))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None)], None, [('arg', (1, 22), 'c', None, None)], [('Constant', (1, 24), 4, None)], ('arg', (1, 29), 'kwargs', None, None), [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 38))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None)], None, [('arg', (1, 22), 'c', None, None)], [None], ('arg', (1, 27), 'kwargs', None, None), [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 36))], [], None, None)], []), ] single_results = [ ('Interactive', [('Expr', (1, 0), ('BinOp', (1, 0), ('Constant', (1, 0), 1, None), ('Add',), ('Constant', (1, 2), 2, None)))]), diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH.rst new file mode 100644 index 000000000000..5ec9bba2d2b9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH.rst @@ -0,0 +1,2 @@ +Swap the positions of the *posonlyargs* and *args* parameters in the +constructor of :class:`ast.parameters` nodes. diff --git a/Parser/Python.asdl b/Parser/Python.asdl index 0c00d398b461..126d478975bb 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -111,7 +111,7 @@ module Python excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body) attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset) - arguments = (arg* args, arg* posonlyargs, arg? vararg, arg* kwonlyargs, + arguments = (arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults, arg? kwarg, expr* defaults) arg = (identifier arg, expr? annotation, string? type_comment) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 0774c58e2d23..fd96964a1e77 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -476,8 +476,8 @@ _Py_IDENTIFIER(kw_defaults); _Py_IDENTIFIER(kwarg); _Py_IDENTIFIER(defaults); static char *arguments_fields[]={ - "args", "posonlyargs", + "args", "vararg", "kwonlyargs", "kw_defaults", @@ -2573,7 +2573,7 @@ ExceptHandler(expr_ty type, identifier name, asdl_seq * body, int lineno, int } arguments_ty -arguments(asdl_seq * args, asdl_seq * posonlyargs, arg_ty vararg, asdl_seq * +arguments(asdl_seq * posonlyargs, asdl_seq * args, arg_ty vararg, asdl_seq * kwonlyargs, asdl_seq * kw_defaults, arg_ty kwarg, asdl_seq * defaults, PyArena *arena) { @@ -2581,8 +2581,8 @@ arguments(asdl_seq * args, asdl_seq * posonlyargs, arg_ty vararg, asdl_seq * p = (arguments_ty)PyArena_Malloc(arena, sizeof(*p)); if (!p) return NULL; - p->args = args; p->posonlyargs = posonlyargs; + p->args = args; p->vararg = vararg; p->kwonlyargs = kwonlyargs; p->kw_defaults = kw_defaults; @@ -3961,14 +3961,14 @@ ast2obj_arguments(void* _o) result = PyType_GenericNew(arguments_type, NULL, NULL); if (!result) return NULL; - value = ast2obj_list(o->args, ast2obj_arg); + value = ast2obj_list(o->posonlyargs, ast2obj_arg); if (!value) goto failed; - if (_PyObject_SetAttrId(result, &PyId_args, value) == -1) + if (_PyObject_SetAttrId(result, &PyId_posonlyargs, value) == -1) goto failed; Py_DECREF(value); - value = ast2obj_list(o->posonlyargs, ast2obj_arg); + value = ast2obj_list(o->args, ast2obj_arg); if (!value) goto failed; - if (_PyObject_SetAttrId(result, &PyId_posonlyargs, value) == -1) + if (_PyObject_SetAttrId(result, &PyId_args, value) == -1) goto failed; Py_DECREF(value); value = ast2obj_arg(o->vararg); @@ -8288,19 +8288,19 @@ int obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena) { PyObject* tmp = NULL; - asdl_seq* args; asdl_seq* posonlyargs; + asdl_seq* args; arg_ty vararg; asdl_seq* kwonlyargs; asdl_seq* kw_defaults; arg_ty kwarg; asdl_seq* defaults; - if (_PyObject_LookupAttrId(obj, &PyId_args, &tmp) < 0) { + if (_PyObject_LookupAttrId(obj, &PyId_posonlyargs, &tmp) < 0) { return 1; } if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from arguments"); + PyErr_SetString(PyExc_TypeError, "required field \"posonlyargs\" missing from arguments"); return 1; } else { @@ -8308,29 +8308,29 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena) Py_ssize_t len; Py_ssize_t i; if (!PyList_Check(tmp)) { - PyErr_Format(PyExc_TypeError, "arguments field \"args\" must be a list, not a %.200s", tmp->ob_type->tp_name); + PyErr_Format(PyExc_TypeError, "arguments field \"posonlyargs\" must be a list, not a %.200s", tmp->ob_type->tp_name); goto failed; } len = PyList_GET_SIZE(tmp); - args = _Py_asdl_seq_new(len, arena); - if (args == NULL) goto failed; + posonlyargs = _Py_asdl_seq_new(len, arena); + if (posonlyargs == NULL) goto failed; for (i = 0; i < len; i++) { arg_ty val; res = obj2ast_arg(PyList_GET_ITEM(tmp, i), &val, arena); if (res != 0) goto failed; if (len != PyList_GET_SIZE(tmp)) { - PyErr_SetString(PyExc_RuntimeError, "arguments field \"args\" changed size during iteration"); + PyErr_SetString(PyExc_RuntimeError, "arguments field \"posonlyargs\" changed size during iteration"); goto failed; } - asdl_seq_SET(args, i, val); + asdl_seq_SET(posonlyargs, i, val); } Py_CLEAR(tmp); } - if (_PyObject_LookupAttrId(obj, &PyId_posonlyargs, &tmp) < 0) { + if (_PyObject_LookupAttrId(obj, &PyId_args, &tmp) < 0) { return 1; } if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"posonlyargs\" missing from arguments"); + PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from arguments"); return 1; } else { @@ -8338,21 +8338,21 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena) Py_ssize_t len; Py_ssize_t i; if (!PyList_Check(tmp)) { - PyErr_Format(PyExc_TypeError, "arguments field \"posonlyargs\" must be a list, not a %.200s", tmp->ob_type->tp_name); + PyErr_Format(PyExc_TypeError, "arguments field \"args\" must be a list, not a %.200s", tmp->ob_type->tp_name); goto failed; } len = PyList_GET_SIZE(tmp); - posonlyargs = _Py_asdl_seq_new(len, arena); - if (posonlyargs == NULL) goto failed; + args = _Py_asdl_seq_new(len, arena); + if (args == NULL) goto failed; for (i = 0; i < len; i++) { arg_ty val; res = obj2ast_arg(PyList_GET_ITEM(tmp, i), &val, arena); if (res != 0) goto failed; if (len != PyList_GET_SIZE(tmp)) { - PyErr_SetString(PyExc_RuntimeError, "arguments field \"posonlyargs\" changed size during iteration"); + PyErr_SetString(PyExc_RuntimeError, "arguments field \"args\" changed size during iteration"); goto failed; } - asdl_seq_SET(posonlyargs, i, val); + asdl_seq_SET(args, i, val); } Py_CLEAR(tmp); } @@ -8472,7 +8472,7 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena) } Py_CLEAR(tmp); } - *out = arguments(args, posonlyargs, vararg, kwonlyargs, kw_defaults, kwarg, + *out = arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults, arena); return 0; failed: diff --git a/Python/ast.c b/Python/ast.c index 8dc86c23d62d..ce3b4927bc04 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -1688,7 +1688,7 @@ ast_for_arguments(struct compiling *c, const node *n) return NULL; } } - return arguments(posargs, posonlyargs, vararg, kwonlyargs, kwdefaults, kwarg, posdefaults, c->c_arena); + return arguments(posonlyargs, posargs, vararg, kwonlyargs, kwdefaults, kwarg, posdefaults, c->c_arena); } static expr_ty diff --git a/Python/importlib_external.h b/Python/importlib_external.h index 33b2ca6820f1..4a3cb24e37b9 100644 --- a/Python/importlib_external.h +++ b/Python/importlib_external.h @@ -278,7 +278,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 95,97,116,111,109,105,99,120,0,0,0,115,30,0,0,0, 0,5,16,1,6,1,16,0,6,255,4,2,2,3,14,1, 20,1,16,1,14,1,2,1,14,1,14,1,6,1,114,69, - 0,0,0,105,83,13,0,0,114,28,0,0,0,114,16,0, + 0,0,0,105,84,13,0,0,114,28,0,0,0,114,16,0, 0,0,115,2,0,0,0,13,10,90,11,95,95,112,121,99, 97,99,104,101,95,95,122,4,111,112,116,45,122,3,46,112, 121,122,4,46,112,121,99,78,41,1,218,12,111,112,116,105, @@ -392,7 +392,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 111,115,116,95,102,105,108,101,110,97,109,101,218,8,102,105, 108,101,110,97,109,101,114,3,0,0,0,114,3,0,0,0, 114,6,0,0,0,218,17,99,97,99,104,101,95,102,114,111, - 109,95,115,111,117,114,99,101,35,1,0,0,115,72,0,0, + 109,95,115,111,117,114,99,101,37,1,0,0,115,72,0,0, 0,0,18,8,1,6,1,2,255,4,2,8,1,4,1,8, 1,12,1,10,1,12,1,16,1,8,1,8,1,8,1,24, 1,8,1,12,1,6,2,8,1,8,1,8,1,8,1,14, @@ -473,7 +473,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 112,116,95,108,101,118,101,108,90,13,98,97,115,101,95,102, 105,108,101,110,97,109,101,114,3,0,0,0,114,3,0,0, 0,114,6,0,0,0,218,17,115,111,117,114,99,101,95,102, - 114,111,109,95,99,97,99,104,101,106,1,0,0,115,52,0, + 114,111,109,95,99,97,99,104,101,108,1,0,0,115,52,0, 0,0,0,9,12,1,8,1,10,1,12,1,4,1,10,1, 12,1,14,1,16,1,4,1,4,1,12,1,8,1,18,2, 10,1,8,1,16,1,10,1,16,1,10,1,14,2,16,1, @@ -508,7 +508,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,90,9,101,120,116,101,110,115,105,111,110,218,11,115, 111,117,114,99,101,95,112,97,116,104,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,15,95,103,101,116,95, - 115,111,117,114,99,101,102,105,108,101,146,1,0,0,115,20, + 115,111,117,114,99,101,102,105,108,101,148,1,0,0,115,20, 0,0,0,0,7,12,1,4,1,16,1,24,1,4,1,2, 1,12,1,18,1,18,1,114,109,0,0,0,99,1,0,0, 0,0,0,0,0,0,0,0,0,1,0,0,0,8,0,0, @@ -522,7 +522,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 98,0,0,0,114,82,0,0,0,114,89,0,0,0,41,1, 114,97,0,0,0,114,3,0,0,0,114,3,0,0,0,114, 6,0,0,0,218,11,95,103,101,116,95,99,97,99,104,101, - 100,165,1,0,0,115,16,0,0,0,0,1,14,1,2,1, + 100,167,1,0,0,115,16,0,0,0,0,1,14,1,2,1, 10,1,14,1,8,1,14,1,4,2,114,113,0,0,0,99, 1,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, 8,0,0,0,67,0,0,0,115,52,0,0,0,122,14,116, @@ -536,7 +536,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,41,3,114,49,0,0,0,114,51,0,0,0,114,50, 0,0,0,41,2,114,44,0,0,0,114,52,0,0,0,114, 3,0,0,0,114,3,0,0,0,114,6,0,0,0,218,10, - 95,99,97,108,99,95,109,111,100,101,177,1,0,0,115,12, + 95,99,97,108,99,95,109,111,100,101,179,1,0,0,115,12, 0,0,0,0,2,2,1,14,1,14,1,10,3,8,1,114, 115,0,0,0,99,1,0,0,0,0,0,0,0,0,0,0, 0,3,0,0,0,8,0,0,0,3,0,0,0,115,68,0, @@ -574,7 +574,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,218,4,97,114,103,115,90,6,107,119,97,114,103, 115,169,1,218,6,109,101,116,104,111,100,114,3,0,0,0, 114,6,0,0,0,218,19,95,99,104,101,99,107,95,110,97, - 109,101,95,119,114,97,112,112,101,114,197,1,0,0,115,18, + 109,101,95,119,114,97,112,112,101,114,199,1,0,0,115,18, 0,0,0,0,1,8,1,8,1,10,1,4,1,8,255,2, 1,2,255,6,2,122,40,95,99,104,101,99,107,95,110,97, 109,101,46,60,108,111,99,97,108,115,62,46,95,99,104,101, @@ -592,7 +592,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,116,116,114,218,8,95,95,100,105,99,116,95,95,218,6, 117,112,100,97,116,101,41,3,90,3,110,101,119,90,3,111, 108,100,114,67,0,0,0,114,3,0,0,0,114,3,0,0, - 0,114,6,0,0,0,218,5,95,119,114,97,112,208,1,0, + 0,114,6,0,0,0,218,5,95,119,114,97,112,210,1,0, 0,115,8,0,0,0,0,1,8,1,10,1,20,1,122,26, 95,99,104,101,99,107,95,110,97,109,101,46,60,108,111,99, 97,108,115,62,46,95,119,114,97,112,41,1,78,41,3,218, @@ -600,7 +600,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 218,9,78,97,109,101,69,114,114,111,114,41,3,114,122,0, 0,0,114,123,0,0,0,114,133,0,0,0,114,3,0,0, 0,114,121,0,0,0,114,6,0,0,0,218,11,95,99,104, - 101,99,107,95,110,97,109,101,189,1,0,0,115,14,0,0, + 101,99,107,95,110,97,109,101,191,1,0,0,115,14,0,0, 0,0,8,14,7,2,1,10,1,14,2,14,5,10,1,114, 136,0,0,0,99,2,0,0,0,0,0,0,0,0,0,0, 0,5,0,0,0,6,0,0,0,67,0,0,0,115,60,0, @@ -628,7 +628,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 110,97,109,101,218,6,108,111,97,100,101,114,218,8,112,111, 114,116,105,111,110,115,218,3,109,115,103,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,17,95,102,105,110, - 100,95,109,111,100,117,108,101,95,115,104,105,109,217,1,0, + 100,95,109,111,100,117,108,101,95,115,104,105,109,219,1,0, 0,115,10,0,0,0,0,10,14,1,16,1,4,1,22,1, 114,143,0,0,0,99,3,0,0,0,0,0,0,0,0,0, 0,0,6,0,0,0,4,0,0,0,67,0,0,0,115,158, @@ -695,7 +695,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 95,100,101,116,97,105,108,115,90,5,109,97,103,105,99,114, 93,0,0,0,114,83,0,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,13,95,99,108,97,115,115, - 105,102,121,95,112,121,99,234,1,0,0,115,28,0,0,0, + 105,102,121,95,112,121,99,236,1,0,0,115,28,0,0,0, 0,16,12,1,8,1,16,1,12,1,12,1,12,1,10,1, 12,1,8,1,16,2,8,1,16,1,12,1,114,152,0,0, 0,99,5,0,0,0,0,0,0,0,0,0,0,0,6,0, @@ -749,7 +749,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 99,101,95,115,105,122,101,114,117,0,0,0,114,151,0,0, 0,114,93,0,0,0,114,3,0,0,0,114,3,0,0,0, 114,6,0,0,0,218,23,95,118,97,108,105,100,97,116,101, - 95,116,105,109,101,115,116,97,109,112,95,112,121,99,11,2, + 95,116,105,109,101,115,116,97,109,112,95,112,121,99,13,2, 0,0,115,16,0,0,0,0,19,24,1,10,1,12,1,12, 1,8,1,22,255,2,2,114,156,0,0,0,99,4,0,0, 0,0,0,0,0,0,0,0,0,4,0,0,0,3,0,0, @@ -795,7 +795,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,218,11,115,111,117,114,99,101,95,104,97,115,104, 114,117,0,0,0,114,151,0,0,0,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,18,95,118,97,108,105, - 100,97,116,101,95,104,97,115,104,95,112,121,99,39,2,0, + 100,97,116,101,95,104,97,115,104,95,112,121,99,41,2,0, 0,115,12,0,0,0,0,17,16,1,2,1,8,255,2,2, 2,254,114,158,0,0,0,99,4,0,0,0,0,0,0,0, 0,0,0,0,5,0,0,0,5,0,0,0,67,0,0,0, @@ -819,7 +819,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 5,114,26,0,0,0,114,117,0,0,0,114,107,0,0,0, 114,108,0,0,0,218,4,99,111,100,101,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,17,95,99,111,109, - 112,105,108,101,95,98,121,116,101,99,111,100,101,63,2,0, + 112,105,108,101,95,98,121,116,101,99,111,100,101,65,2,0, 0,115,20,0,0,0,0,2,10,1,10,1,12,1,8,1, 12,1,4,2,10,1,2,0,2,255,114,165,0,0,0,114, 73,0,0,0,99,3,0,0,0,0,0,0,0,0,0,0, @@ -838,7 +838,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 109,116,105,109,101,114,155,0,0,0,114,26,0,0,0,114, 3,0,0,0,114,3,0,0,0,114,6,0,0,0,218,22, 95,99,111,100,101,95,116,111,95,116,105,109,101,115,116,97, - 109,112,95,112,121,99,76,2,0,0,115,12,0,0,0,0, + 109,112,95,112,121,99,78,2,0,0,115,12,0,0,0,0, 2,8,1,14,1,14,1,14,1,16,1,114,170,0,0,0, 84,99,3,0,0,0,0,0,0,0,0,0,0,0,5,0, 0,0,5,0,0,0,67,0,0,0,115,80,0,0,0,116, @@ -856,7 +856,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 164,0,0,0,114,157,0,0,0,90,7,99,104,101,99,107, 101,100,114,26,0,0,0,114,83,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,17,95,99,111, - 100,101,95,116,111,95,104,97,115,104,95,112,121,99,86,2, + 100,101,95,116,111,95,104,97,115,104,95,112,121,99,88,2, 0,0,115,14,0,0,0,0,2,8,1,12,1,14,1,16, 1,10,1,16,1,114,171,0,0,0,99,1,0,0,0,0, 0,0,0,0,0,0,0,5,0,0,0,6,0,0,0,67, @@ -884,7 +884,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 101,110,99,111,100,105,110,103,90,15,110,101,119,108,105,110, 101,95,100,101,99,111,100,101,114,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,13,100,101,99,111,100,101, - 95,115,111,117,114,99,101,97,2,0,0,115,10,0,0,0, + 95,115,111,117,114,99,101,99,2,0,0,115,10,0,0,0, 0,5,8,1,12,1,10,1,12,1,114,176,0,0,0,169, 2,114,140,0,0,0,218,26,115,117,98,109,111,100,117,108, 101,95,115,101,97,114,99,104,95,108,111,99,97,116,105,111, @@ -946,7 +946,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,90,7,100,105,114,110,97,109,101,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,23,115,112,101,99,95, 102,114,111,109,95,102,105,108,101,95,108,111,99,97,116,105, - 111,110,114,2,0,0,115,62,0,0,0,0,12,8,4,4, + 111,110,116,2,0,0,115,62,0,0,0,0,12,8,4,4, 1,10,2,2,1,14,1,14,1,8,2,10,8,16,1,6, 3,8,1,14,1,14,1,10,1,6,1,6,2,4,3,8, 2,10,1,2,1,14,1,14,1,6,2,4,1,8,2,6, @@ -983,7 +983,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 75,69,89,95,76,79,67,65,76,95,77,65,67,72,73,78, 69,41,2,218,3,99,108,115,114,5,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,218,14,95,111, - 112,101,110,95,114,101,103,105,115,116,114,121,194,2,0,0, + 112,101,110,95,114,101,103,105,115,116,114,121,196,2,0,0, 115,8,0,0,0,0,2,2,1,16,1,14,1,122,36,87, 105,110,100,111,119,115,82,101,103,105,115,116,114,121,70,105, 110,100,101,114,46,95,111,112,101,110,95,114,101,103,105,115, @@ -1009,7 +1009,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 105,115,116,114,121,95,107,101,121,114,5,0,0,0,90,4, 104,107,101,121,218,8,102,105,108,101,112,97,116,104,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,218,16,95, - 115,101,97,114,99,104,95,114,101,103,105,115,116,114,121,201, + 115,101,97,114,99,104,95,114,101,103,105,115,116,114,121,203, 2,0,0,115,24,0,0,0,0,2,6,1,8,2,6,1, 6,1,16,255,6,2,2,1,12,1,26,1,14,1,8,1, 122,38,87,105,110,100,111,119,115,82,101,103,105,115,116,114, @@ -1032,7 +1032,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,218,6,116,97,114,103,101,116,114,199,0,0,0,114,140, 0,0,0,114,189,0,0,0,114,187,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,218,9,102,105, - 110,100,95,115,112,101,99,216,2,0,0,115,28,0,0,0, + 110,100,95,115,112,101,99,218,2,0,0,115,28,0,0,0, 0,2,10,1,8,1,4,1,2,1,12,1,14,1,8,1, 14,1,14,1,6,1,8,1,2,254,6,3,122,31,87,105, 110,100,111,119,115,82,101,103,105,115,116,114,121,70,105,110, @@ -1051,7 +1051,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,203,0,0,0,114,140,0,0,0,169,4,114,193,0,0, 0,114,139,0,0,0,114,44,0,0,0,114,187,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 11,102,105,110,100,95,109,111,100,117,108,101,232,2,0,0, + 11,102,105,110,100,95,109,111,100,117,108,101,234,2,0,0, 115,8,0,0,0,0,7,12,1,8,1,6,2,122,33,87, 105,110,100,111,119,115,82,101,103,105,115,116,114,121,70,105, 110,100,101,114,46,102,105,110,100,95,109,111,100,117,108,101, @@ -1061,7 +1061,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,115,115,109,101,116,104,111,100,114,194,0,0,0,114,200, 0,0,0,114,203,0,0,0,114,206,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,191,0,0,0,182,2,0,0,115,28,0,0,0,8, + 0,114,191,0,0,0,184,2,0,0,115,28,0,0,0,8, 2,4,3,2,255,2,4,2,255,2,3,4,2,2,1,10, 6,2,1,10,14,2,1,12,15,2,1,114,191,0,0,0, 99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -1097,7 +1097,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,97,0,0,0,90,13,102,105,108,101,110,97, 109,101,95,98,97,115,101,90,9,116,97,105,108,95,110,97, 109,101,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,182,0,0,0,251,2,0,0,115,8,0,0,0,0, + 0,114,182,0,0,0,253,2,0,0,115,8,0,0,0,0, 3,18,1,16,1,14,1,122,24,95,76,111,97,100,101,114, 66,97,115,105,99,115,46,105,115,95,112,97,99,107,97,103, 101,99,2,0,0,0,0,0,0,0,0,0,0,0,2,0, @@ -1108,7 +1108,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 46,78,114,3,0,0,0,169,2,114,119,0,0,0,114,187, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, 0,0,218,13,99,114,101,97,116,101,95,109,111,100,117,108, - 101,3,3,0,0,115,2,0,0,0,0,1,122,27,95,76, + 101,5,3,0,0,115,2,0,0,0,0,1,122,27,95,76, 111,97,100,101,114,66,97,115,105,99,115,46,99,114,101,97, 116,101,95,109,111,100,117,108,101,99,2,0,0,0,0,0, 0,0,0,0,0,0,3,0,0,0,5,0,0,0,67,0, @@ -1128,7 +1128,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,41,3,114,119,0,0,0,218,6,109,111,100,117, 108,101,114,164,0,0,0,114,3,0,0,0,114,3,0,0, 0,114,6,0,0,0,218,11,101,120,101,99,95,109,111,100, - 117,108,101,6,3,0,0,115,12,0,0,0,0,2,12,1, + 117,108,101,8,3,0,0,115,12,0,0,0,0,2,12,1, 8,1,6,1,4,255,6,2,122,25,95,76,111,97,100,101, 114,66,97,115,105,99,115,46,101,120,101,99,95,109,111,100, 117,108,101,99,2,0,0,0,0,0,0,0,0,0,0,0, @@ -1139,14 +1139,14 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,218,17,95,108,111,97,100,95,109,111,100,117,108,101, 95,115,104,105,109,169,2,114,119,0,0,0,114,139,0,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 218,11,108,111,97,100,95,109,111,100,117,108,101,14,3,0, + 218,11,108,111,97,100,95,109,111,100,117,108,101,16,3,0, 0,115,2,0,0,0,0,2,122,25,95,76,111,97,100,101, 114,66,97,115,105,99,115,46,108,111,97,100,95,109,111,100, 117,108,101,78,41,8,114,125,0,0,0,114,124,0,0,0, 114,126,0,0,0,114,127,0,0,0,114,182,0,0,0,114, 212,0,0,0,114,217,0,0,0,114,220,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,114,208,0,0,0,246,2,0,0,115,10,0,0,0, + 0,0,114,208,0,0,0,248,2,0,0,115,10,0,0,0, 8,2,4,3,8,8,8,3,8,8,114,208,0,0,0,99, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 3,0,0,0,64,0,0,0,115,74,0,0,0,101,0,90, @@ -1171,7 +1171,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 32,32,32,32,32,32,78,41,1,114,50,0,0,0,169,2, 114,119,0,0,0,114,44,0,0,0,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,10,112,97,116,104,95, - 109,116,105,109,101,21,3,0,0,115,2,0,0,0,0,6, + 109,116,105,109,101,23,3,0,0,115,2,0,0,0,0,6, 122,23,83,111,117,114,99,101,76,111,97,100,101,114,46,112, 97,116,104,95,109,116,105,109,101,99,2,0,0,0,0,0, 0,0,0,0,0,0,2,0,0,0,4,0,0,0,67,0, @@ -1205,7 +1205,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 46,10,32,32,32,32,32,32,32,32,114,169,0,0,0,41, 1,114,223,0,0,0,114,222,0,0,0,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,10,112,97,116,104, - 95,115,116,97,116,115,29,3,0,0,115,2,0,0,0,0, + 95,115,116,97,116,115,31,3,0,0,115,2,0,0,0,0, 12,122,23,83,111,117,114,99,101,76,111,97,100,101,114,46, 112,97,116,104,95,115,116,97,116,115,99,4,0,0,0,0, 0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,67, @@ -1229,7 +1229,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 108,0,0,0,90,10,99,97,99,104,101,95,112,97,116,104, 114,26,0,0,0,114,3,0,0,0,114,3,0,0,0,114, 6,0,0,0,218,15,95,99,97,99,104,101,95,98,121,116, - 101,99,111,100,101,43,3,0,0,115,2,0,0,0,0,8, + 101,99,111,100,101,45,3,0,0,115,2,0,0,0,0,8, 122,28,83,111,117,114,99,101,76,111,97,100,101,114,46,95, 99,97,99,104,101,95,98,121,116,101,99,111,100,101,99,3, 0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,1, @@ -1246,7 +1246,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 46,10,32,32,32,32,32,32,32,32,78,114,3,0,0,0, 41,3,114,119,0,0,0,114,44,0,0,0,114,26,0,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,225,0,0,0,53,3,0,0,115,2,0,0,0,0,1, + 114,225,0,0,0,55,3,0,0,115,2,0,0,0,0,1, 122,21,83,111,117,114,99,101,76,111,97,100,101,114,46,115, 101,116,95,100,97,116,97,99,2,0,0,0,0,0,0,0, 0,0,0,0,5,0,0,0,10,0,0,0,67,0,0,0, @@ -1267,7 +1267,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,119,0,0,0,114,139,0,0,0,114,44,0,0,0,114, 174,0,0,0,218,3,101,120,99,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,10,103,101,116,95,115,111, - 117,114,99,101,60,3,0,0,115,20,0,0,0,0,2,10, + 117,114,99,101,62,3,0,0,115,20,0,0,0,0,2,10, 1,2,1,14,1,16,1,4,1,2,255,4,1,2,255,20, 2,122,23,83,111,117,114,99,101,76,111,97,100,101,114,46, 103,101,116,95,115,111,117,114,99,101,114,105,0,0,0,41, @@ -1289,7 +1289,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 111,109,112,105,108,101,41,4,114,119,0,0,0,114,26,0, 0,0,114,44,0,0,0,114,230,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,14,115,111,117, - 114,99,101,95,116,111,95,99,111,100,101,70,3,0,0,115, + 114,99,101,95,116,111,95,99,111,100,101,72,3,0,0,115, 8,0,0,0,0,5,12,1,2,0,2,255,122,27,83,111, 117,114,99,101,76,111,97,100,101,114,46,115,111,117,114,99, 101,95,116,111,95,99,111,100,101,99,2,0,0,0,0,0, @@ -1367,7 +1367,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,83,0,0,0,90,10,98,121,116,101,115,95, 100,97,116,97,90,11,99,111,100,101,95,111,98,106,101,99, 116,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,213,0,0,0,78,3,0,0,115,152,0,0,0,0,7, + 114,213,0,0,0,80,3,0,0,115,152,0,0,0,0,7, 10,1,4,1,4,1,4,1,4,1,4,1,2,1,12,1, 14,1,12,2,2,1,14,1,14,1,8,2,12,1,2,1, 14,1,14,1,6,3,2,1,2,254,6,4,2,1,12,1, @@ -1383,7 +1383,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 223,0,0,0,114,224,0,0,0,114,226,0,0,0,114,225, 0,0,0,114,229,0,0,0,114,233,0,0,0,114,213,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,3,0,0, - 0,114,6,0,0,0,114,221,0,0,0,19,3,0,0,115, + 0,114,6,0,0,0,114,221,0,0,0,21,3,0,0,115, 14,0,0,0,8,2,8,8,8,14,8,10,8,7,8,10, 14,8,114,221,0,0,0,99,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0, @@ -1413,7 +1413,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 110,100,101,114,46,78,114,159,0,0,0,41,3,114,119,0, 0,0,114,139,0,0,0,114,44,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,114,209,0,0,0, - 168,3,0,0,115,4,0,0,0,0,3,6,1,122,19,70, + 170,3,0,0,115,4,0,0,0,0,3,6,1,122,19,70, 105,108,101,76,111,97,100,101,114,46,95,95,105,110,105,116, 95,95,99,2,0,0,0,0,0,0,0,0,0,0,0,2, 0,0,0,2,0,0,0,67,0,0,0,115,24,0,0,0, @@ -1422,7 +1422,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 9,95,95,99,108,97,115,115,95,95,114,131,0,0,0,169, 2,114,119,0,0,0,90,5,111,116,104,101,114,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,218,6,95,95, - 101,113,95,95,174,3,0,0,115,6,0,0,0,0,1,12, + 101,113,95,95,176,3,0,0,115,6,0,0,0,0,1,12, 1,10,255,122,17,70,105,108,101,76,111,97,100,101,114,46, 95,95,101,113,95,95,99,1,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,0,3,0,0,0,67,0,0,0,115, @@ -1430,7 +1430,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 106,2,131,1,65,0,83,0,114,110,0,0,0,169,3,218, 4,104,97,115,104,114,117,0,0,0,114,44,0,0,0,169, 1,114,119,0,0,0,114,3,0,0,0,114,3,0,0,0, - 114,6,0,0,0,218,8,95,95,104,97,115,104,95,95,178, + 114,6,0,0,0,218,8,95,95,104,97,115,104,95,95,180, 3,0,0,115,2,0,0,0,0,1,122,19,70,105,108,101, 76,111,97,100,101,114,46,95,95,104,97,115,104,95,95,99, 2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, @@ -1445,7 +1445,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 32,32,32,32,32,41,3,218,5,115,117,112,101,114,114,239, 0,0,0,114,220,0,0,0,114,219,0,0,0,169,1,114, 241,0,0,0,114,3,0,0,0,114,6,0,0,0,114,220, - 0,0,0,181,3,0,0,115,2,0,0,0,0,10,122,22, + 0,0,0,183,3,0,0,115,2,0,0,0,0,10,122,22, 70,105,108,101,76,111,97,100,101,114,46,108,111,97,100,95, 109,111,100,117,108,101,99,2,0,0,0,0,0,0,0,0, 0,0,0,2,0,0,0,1,0,0,0,67,0,0,0,115, @@ -1455,7 +1455,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 32,97,115,32,102,111,117,110,100,32,98,121,32,116,104,101, 32,102,105,110,100,101,114,46,114,48,0,0,0,114,219,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,179,0,0,0,193,3,0,0,115,2,0,0,0,0, + 0,114,179,0,0,0,195,3,0,0,115,2,0,0,0,0, 3,122,23,70,105,108,101,76,111,97,100,101,114,46,103,101, 116,95,102,105,108,101,110,97,109,101,99,2,0,0,0,0, 0,0,0,0,0,0,0,3,0,0,0,10,0,0,0,67, @@ -1475,7 +1475,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,90,4,114,101,97,100,114,65,0,0,0,41,3, 114,119,0,0,0,114,44,0,0,0,114,68,0,0,0,114, 3,0,0,0,114,3,0,0,0,114,6,0,0,0,114,227, - 0,0,0,198,3,0,0,115,10,0,0,0,0,2,14,1, + 0,0,0,200,3,0,0,115,10,0,0,0,0,2,14,1, 16,1,28,2,14,1,122,19,70,105,108,101,76,111,97,100, 101,114,46,103,101,116,95,100,97,116,97,99,2,0,0,0, 0,0,0,0,0,0,0,0,2,0,0,0,3,0,0,0, @@ -1484,7 +1484,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,1,114,182,0,0,0,169,2,114,119,0,0,0,114,216, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, 0,0,218,19,103,101,116,95,114,101,115,111,117,114,99,101, - 95,114,101,97,100,101,114,209,3,0,0,115,6,0,0,0, + 95,114,101,97,100,101,114,211,3,0,0,115,6,0,0,0, 0,2,10,1,4,1,122,30,70,105,108,101,76,111,97,100, 101,114,46,103,101,116,95,114,101,115,111,117,114,99,101,95, 114,101,97,100,101,114,99,2,0,0,0,0,0,0,0,0, @@ -1497,7 +1497,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,90,8,114,101,115,111,117,114,99,101,114,44,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, 0,218,13,111,112,101,110,95,114,101,115,111,117,114,99,101, - 215,3,0,0,115,4,0,0,0,0,1,20,1,122,24,70, + 217,3,0,0,115,4,0,0,0,0,1,20,1,122,24,70, 105,108,101,76,111,97,100,101,114,46,111,112,101,110,95,114, 101,115,111,117,114,99,101,99,2,0,0,0,0,0,0,0, 0,0,0,0,3,0,0,0,3,0,0,0,67,0,0,0, @@ -1509,7 +1509,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 69,114,114,111,114,114,38,0,0,0,114,47,0,0,0,114, 44,0,0,0,114,255,0,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,13,114,101,115,111,117,114, - 99,101,95,112,97,116,104,219,3,0,0,115,8,0,0,0, + 99,101,95,112,97,116,104,221,3,0,0,115,8,0,0,0, 0,1,10,1,4,1,20,1,122,24,70,105,108,101,76,111, 97,100,101,114,46,114,101,115,111,117,114,99,101,95,112,97, 116,104,99,2,0,0,0,0,0,0,0,0,0,0,0,3, @@ -1521,7 +1521,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,44,0,0,0,114,54,0,0,0,169,3,114,119, 0,0,0,114,117,0,0,0,114,44,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,114,2,1,0, - 0,225,3,0,0,115,8,0,0,0,0,1,8,1,4,1, + 0,227,3,0,0,115,8,0,0,0,0,1,8,1,4,1, 20,1,122,22,70,105,108,101,76,111,97,100,101,114,46,105, 115,95,114,101,115,111,117,114,99,101,99,1,0,0,0,0, 0,0,0,0,0,0,0,1,0,0,0,5,0,0,0,67, @@ -1531,7 +1531,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,218,7,108,105,115,116,100,105,114,114,47,0,0,0, 114,44,0,0,0,114,246,0,0,0,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,8,99,111,110,116,101, - 110,116,115,231,3,0,0,115,2,0,0,0,0,1,122,19, + 110,116,115,233,3,0,0,115,2,0,0,0,0,1,122,19, 70,105,108,101,76,111,97,100,101,114,46,99,111,110,116,101, 110,116,115,41,17,114,125,0,0,0,114,124,0,0,0,114, 126,0,0,0,114,127,0,0,0,114,209,0,0,0,114,243, @@ -1540,7 +1540,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,0,1,0,0,114,4,1,0,0,114,2,1,0,0, 114,8,1,0,0,90,13,95,95,99,108,97,115,115,99,101, 108,108,95,95,114,3,0,0,0,114,3,0,0,0,114,249, - 0,0,0,114,6,0,0,0,114,239,0,0,0,163,3,0, + 0,0,0,114,6,0,0,0,114,239,0,0,0,165,3,0, 0,115,30,0,0,0,8,2,4,3,8,6,8,4,8,3, 2,1,14,11,2,1,10,4,8,11,2,1,10,5,8,4, 8,6,8,6,114,239,0,0,0,99,0,0,0,0,0,0, @@ -1563,7 +1563,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,49,0,0,0,218,8,115,116,95,109,116,105,109,101,90, 7,115,116,95,115,105,122,101,41,3,114,119,0,0,0,114, 44,0,0,0,114,238,0,0,0,114,3,0,0,0,114,3, - 0,0,0,114,6,0,0,0,114,224,0,0,0,239,3,0, + 0,0,0,114,6,0,0,0,114,224,0,0,0,241,3,0, 0,115,4,0,0,0,0,2,8,1,122,27,83,111,117,114, 99,101,70,105,108,101,76,111,97,100,101,114,46,112,97,116, 104,95,115,116,97,116,115,99,4,0,0,0,0,0,0,0, @@ -1574,7 +1574,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,225,0,0,0,41,5,114,119,0,0,0,114,108,0,0, 0,114,107,0,0,0,114,26,0,0,0,114,52,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 226,0,0,0,244,3,0,0,115,4,0,0,0,0,2,8, + 226,0,0,0,246,3,0,0,115,4,0,0,0,0,2,8, 1,122,32,83,111,117,114,99,101,70,105,108,101,76,111,97, 100,101,114,46,95,99,97,99,104,101,95,98,121,116,101,99, 111,100,101,114,60,0,0,0,114,11,1,0,0,99,3,0, @@ -1609,7 +1609,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 12,1,0,0,218,6,112,97,114,101,110,116,114,97,0,0, 0,114,37,0,0,0,114,33,0,0,0,114,228,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 225,0,0,0,249,3,0,0,115,48,0,0,0,0,2,12, + 225,0,0,0,251,3,0,0,115,48,0,0,0,0,2,12, 1,4,2,12,1,12,1,12,2,12,1,10,1,2,1,14, 1,14,2,8,1,16,3,6,1,2,0,2,255,4,2,28, 1,2,1,12,1,16,1,16,2,8,1,2,255,122,25,83, @@ -1618,7 +1618,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,124,0,0,0,114,126,0,0,0,114,127,0,0,0,114, 224,0,0,0,114,226,0,0,0,114,225,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,114,9,1,0,0,235,3,0,0,115,8,0,0,0, + 0,0,114,9,1,0,0,237,3,0,0,115,8,0,0,0, 8,2,4,2,8,5,8,5,114,9,1,0,0,99,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, 0,0,64,0,0,0,115,32,0,0,0,101,0,90,1,100, @@ -1640,7 +1640,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,235,0,0,0,41,5,114,119,0,0,0,114, 139,0,0,0,114,44,0,0,0,114,26,0,0,0,114,151, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,114,213,0,0,0,28,4,0,0,115,22,0,0,0, + 0,0,114,213,0,0,0,30,4,0,0,115,22,0,0,0, 0,1,10,1,10,4,2,1,2,254,6,4,12,1,2,1, 14,1,2,1,2,253,122,29,83,111,117,114,99,101,108,101, 115,115,70,105,108,101,76,111,97,100,101,114,46,103,101,116, @@ -1651,13 +1651,13 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 105,115,32,110,111,32,115,111,117,114,99,101,32,99,111,100, 101,46,78,114,3,0,0,0,114,219,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,114,229,0,0, - 0,44,4,0,0,115,2,0,0,0,0,2,122,31,83,111, + 0,46,4,0,0,115,2,0,0,0,0,2,122,31,83,111, 117,114,99,101,108,101,115,115,70,105,108,101,76,111,97,100, 101,114,46,103,101,116,95,115,111,117,114,99,101,78,41,6, 114,125,0,0,0,114,124,0,0,0,114,126,0,0,0,114, 127,0,0,0,114,213,0,0,0,114,229,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,114,15,1,0,0,24,4,0,0,115,6,0,0,0, + 0,0,114,15,1,0,0,26,4,0,0,115,6,0,0,0, 8,2,4,2,8,16,114,15,1,0,0,99,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0, 64,0,0,0,115,92,0,0,0,101,0,90,1,100,0,90, @@ -1677,7 +1677,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 67,0,0,0,115,16,0,0,0,124,1,124,0,95,0,124, 2,124,0,95,1,100,0,83,0,114,110,0,0,0,114,159, 0,0,0,114,5,1,0,0,114,3,0,0,0,114,3,0, - 0,0,114,6,0,0,0,114,209,0,0,0,61,4,0,0, + 0,0,114,6,0,0,0,114,209,0,0,0,63,4,0,0, 115,4,0,0,0,0,1,6,1,122,28,69,120,116,101,110, 115,105,111,110,70,105,108,101,76,111,97,100,101,114,46,95, 95,105,110,105,116,95,95,99,2,0,0,0,0,0,0,0, @@ -1686,7 +1686,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 22,124,0,106,1,124,1,106,1,107,2,83,0,114,110,0, 0,0,114,240,0,0,0,114,242,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,114,243,0,0,0, - 65,4,0,0,115,6,0,0,0,0,1,12,1,10,255,122, + 67,4,0,0,115,6,0,0,0,0,1,12,1,10,255,122, 26,69,120,116,101,110,115,105,111,110,70,105,108,101,76,111, 97,100,101,114,46,95,95,101,113,95,95,99,1,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0, @@ -1694,7 +1694,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 1,116,0,124,0,106,2,131,1,65,0,83,0,114,110,0, 0,0,114,244,0,0,0,114,246,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,114,247,0,0,0, - 69,4,0,0,115,2,0,0,0,0,1,122,28,69,120,116, + 71,4,0,0,115,2,0,0,0,0,1,122,28,69,120,116, 101,110,115,105,111,110,70,105,108,101,76,111,97,100,101,114, 46,95,95,104,97,115,104,95,95,99,2,0,0,0,0,0, 0,0,0,0,0,0,3,0,0,0,5,0,0,0,67,0, @@ -1711,7 +1711,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,117,0,0,0,114,44,0,0,0,41,3,114, 119,0,0,0,114,187,0,0,0,114,216,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,114,212,0, - 0,0,72,4,0,0,115,18,0,0,0,0,2,4,1,4, + 0,0,74,4,0,0,115,18,0,0,0,0,2,4,1,4, 0,2,255,4,2,6,1,4,0,4,255,4,2,122,33,69, 120,116,101,110,115,105,111,110,70,105,108,101,76,111,97,100, 101,114,46,99,114,101,97,116,101,95,109,111,100,117,108,101, @@ -1728,7 +1728,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 90,12,101,120,101,99,95,100,121,110,97,109,105,99,114,149, 0,0,0,114,117,0,0,0,114,44,0,0,0,114,253,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,217,0,0,0,80,4,0,0,115,10,0,0,0,0, + 0,114,217,0,0,0,82,4,0,0,115,10,0,0,0,0, 2,14,1,6,1,4,0,4,255,122,31,69,120,116,101,110, 115,105,111,110,70,105,108,101,76,111,97,100,101,114,46,101, 120,101,99,95,109,111,100,117,108,101,99,2,0,0,0,0, @@ -1746,7 +1746,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,209,0,0,0,78,114,3,0,0,0,169,2,114,32,0, 0,0,218,6,115,117,102,102,105,120,169,1,90,9,102,105, 108,101,95,110,97,109,101,114,3,0,0,0,114,6,0,0, - 0,218,9,60,103,101,110,101,120,112,114,62,89,4,0,0, + 0,218,9,60,103,101,110,101,120,112,114,62,91,4,0,0, 115,4,0,0,0,4,1,2,255,122,49,69,120,116,101,110, 115,105,111,110,70,105,108,101,76,111,97,100,101,114,46,105, 115,95,112,97,99,107,97,103,101,46,60,108,111,99,97,108, @@ -1754,7 +1754,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,44,0,0,0,218,3,97,110,121,218,18,69, 88,84,69,78,83,73,79,78,95,83,85,70,70,73,88,69, 83,114,219,0,0,0,114,3,0,0,0,114,18,1,0,0, - 114,6,0,0,0,114,182,0,0,0,86,4,0,0,115,8, + 114,6,0,0,0,114,182,0,0,0,88,4,0,0,115,8, 0,0,0,0,2,14,1,12,1,2,255,122,30,69,120,116, 101,110,115,105,111,110,70,105,108,101,76,111,97,100,101,114, 46,105,115,95,112,97,99,107,97,103,101,99,2,0,0,0, @@ -1765,7 +1765,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 117,108,101,32,99,97,110,110,111,116,32,99,114,101,97,116, 101,32,97,32,99,111,100,101,32,111,98,106,101,99,116,46, 78,114,3,0,0,0,114,219,0,0,0,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,213,0,0,0,92, + 114,3,0,0,0,114,6,0,0,0,114,213,0,0,0,94, 4,0,0,115,2,0,0,0,0,2,122,28,69,120,116,101, 110,115,105,111,110,70,105,108,101,76,111,97,100,101,114,46, 103,101,116,95,99,111,100,101,99,2,0,0,0,0,0,0, @@ -1776,14 +1776,14 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,118,101,32,110,111,32,115,111,117,114,99,101,32,99,111, 100,101,46,78,114,3,0,0,0,114,219,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,114,229,0, - 0,0,96,4,0,0,115,2,0,0,0,0,2,122,30,69, + 0,0,98,4,0,0,115,2,0,0,0,0,2,122,30,69, 120,116,101,110,115,105,111,110,70,105,108,101,76,111,97,100, 101,114,46,103,101,116,95,115,111,117,114,99,101,99,2,0, 0,0,0,0,0,0,0,0,0,0,2,0,0,0,1,0, 0,0,67,0,0,0,115,6,0,0,0,124,0,106,0,83, 0,114,250,0,0,0,114,48,0,0,0,114,219,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 179,0,0,0,100,4,0,0,115,2,0,0,0,0,3,122, + 179,0,0,0,102,4,0,0,115,2,0,0,0,0,3,122, 32,69,120,116,101,110,115,105,111,110,70,105,108,101,76,111, 97,100,101,114,46,103,101,116,95,102,105,108,101,110,97,109, 101,78,41,14,114,125,0,0,0,114,124,0,0,0,114,126, @@ -1792,7 +1792,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,182,0,0,0,114,213,0,0,0,114,229,0,0,0, 114,136,0,0,0,114,179,0,0,0,114,3,0,0,0,114, 3,0,0,0,114,3,0,0,0,114,6,0,0,0,114,252, - 0,0,0,53,4,0,0,115,22,0,0,0,8,2,4,6, + 0,0,0,55,4,0,0,115,22,0,0,0,8,2,4,6, 8,4,8,4,8,3,8,8,8,6,8,6,8,4,8,4, 2,1,114,252,0,0,0,99,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,2,0,0,0,64,0,0,0, @@ -1834,7 +1834,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 116,104,95,102,105,110,100,101,114,169,4,114,119,0,0,0, 114,117,0,0,0,114,44,0,0,0,90,11,112,97,116,104, 95,102,105,110,100,101,114,114,3,0,0,0,114,3,0,0, - 0,114,6,0,0,0,114,209,0,0,0,113,4,0,0,115, + 0,114,6,0,0,0,114,209,0,0,0,115,4,0,0,115, 8,0,0,0,0,1,6,1,6,1,14,1,122,23,95,78, 97,109,101,115,112,97,99,101,80,97,116,104,46,95,95,105, 110,105,116,95,95,99,1,0,0,0,0,0,0,0,0,0, @@ -1852,7 +1852,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 1,0,0,218,3,100,111,116,90,2,109,101,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,23,95,102,105, 110,100,95,112,97,114,101,110,116,95,112,97,116,104,95,110, - 97,109,101,115,119,4,0,0,115,8,0,0,0,0,2,18, + 97,109,101,115,121,4,0,0,115,8,0,0,0,0,2,18, 1,8,2,4,3,122,38,95,78,97,109,101,115,112,97,99, 101,80,97,116,104,46,95,102,105,110,100,95,112,97,114,101, 110,116,95,112,97,116,104,95,110,97,109,101,115,99,1,0, @@ -1865,7 +1865,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,114,101,110,116,95,109,111,100,117,108,101,95,110,97,109, 101,90,14,112,97,116,104,95,97,116,116,114,95,110,97,109, 101,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,25,1,0,0,129,4,0,0,115,4,0,0,0,0,1, + 114,25,1,0,0,131,4,0,0,115,4,0,0,0,0,1, 12,1,122,31,95,78,97,109,101,115,112,97,99,101,80,97, 116,104,46,95,103,101,116,95,112,97,114,101,110,116,95,112, 97,116,104,99,1,0,0,0,0,0,0,0,0,0,0,0, @@ -1881,7 +1881,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,3,114,119,0,0,0,90,11,112,97,114,101,110,116,95, 112,97,116,104,114,187,0,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,12,95,114,101,99,97,108, - 99,117,108,97,116,101,133,4,0,0,115,16,0,0,0,0, + 99,117,108,97,116,101,135,4,0,0,115,16,0,0,0,0, 2,12,1,10,1,14,3,18,1,6,1,8,1,6,1,122, 27,95,78,97,109,101,115,112,97,99,101,80,97,116,104,46, 95,114,101,99,97,108,99,117,108,97,116,101,99,1,0,0, @@ -1890,7 +1890,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 161,0,131,1,83,0,114,110,0,0,0,41,2,114,6,1, 0,0,114,32,1,0,0,114,246,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,8,95,95,105, - 116,101,114,95,95,146,4,0,0,115,2,0,0,0,0,1, + 116,101,114,95,95,148,4,0,0,115,2,0,0,0,0,1, 122,23,95,78,97,109,101,115,112,97,99,101,80,97,116,104, 46,95,95,105,116,101,114,95,95,99,2,0,0,0,0,0, 0,0,0,0,0,0,2,0,0,0,2,0,0,0,67,0, @@ -1898,7 +1898,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,83,0,114,110,0,0,0,169,1,114,32,1,0,0,41, 2,114,119,0,0,0,218,5,105,110,100,101,120,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,218,11,95,95, - 103,101,116,105,116,101,109,95,95,149,4,0,0,115,2,0, + 103,101,116,105,116,101,109,95,95,151,4,0,0,115,2,0, 0,0,0,1,122,26,95,78,97,109,101,115,112,97,99,101, 80,97,116,104,46,95,95,103,101,116,105,116,101,109,95,95, 99,3,0,0,0,0,0,0,0,0,0,0,0,3,0,0, @@ -1907,7 +1907,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,41,1,114,24,1,0,0,41,3,114,119,0,0,0,114, 35,1,0,0,114,44,0,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,11,95,95,115,101,116,105, - 116,101,109,95,95,152,4,0,0,115,2,0,0,0,0,1, + 116,101,109,95,95,154,4,0,0,115,2,0,0,0,0,1, 122,26,95,78,97,109,101,115,112,97,99,101,80,97,116,104, 46,95,95,115,101,116,105,116,101,109,95,95,99,1,0,0, 0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0, @@ -1915,7 +1915,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 161,0,131,1,83,0,114,110,0,0,0,41,2,114,22,0, 0,0,114,32,1,0,0,114,246,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,7,95,95,108, - 101,110,95,95,155,4,0,0,115,2,0,0,0,0,1,122, + 101,110,95,95,157,4,0,0,115,2,0,0,0,0,1,122, 22,95,78,97,109,101,115,112,97,99,101,80,97,116,104,46, 95,95,108,101,110,95,95,99,1,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,3,0,0,0,67,0,0,0, @@ -1924,7 +1924,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 80,97,116,104,40,123,33,114,125,41,41,2,114,62,0,0, 0,114,24,1,0,0,114,246,0,0,0,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,8,95,95,114,101, - 112,114,95,95,158,4,0,0,115,2,0,0,0,0,1,122, + 112,114,95,95,160,4,0,0,115,2,0,0,0,0,1,122, 23,95,78,97,109,101,115,112,97,99,101,80,97,116,104,46, 95,95,114,101,112,114,95,95,99,2,0,0,0,0,0,0, 0,0,0,0,0,2,0,0,0,3,0,0,0,67,0,0, @@ -1932,7 +1932,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 83,0,114,110,0,0,0,114,34,1,0,0,169,2,114,119, 0,0,0,218,4,105,116,101,109,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,12,95,95,99,111,110,116, - 97,105,110,115,95,95,161,4,0,0,115,2,0,0,0,0, + 97,105,110,115,95,95,163,4,0,0,115,2,0,0,0,0, 1,122,27,95,78,97,109,101,115,112,97,99,101,80,97,116, 104,46,95,95,99,111,110,116,97,105,110,115,95,95,99,2, 0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,3, @@ -1940,7 +1940,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 160,1,124,1,161,1,1,0,100,0,83,0,114,110,0,0, 0,41,2,114,24,1,0,0,114,186,0,0,0,114,40,1, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,186,0,0,0,164,4,0,0,115,2,0,0,0,0, + 0,114,186,0,0,0,166,4,0,0,115,2,0,0,0,0, 1,122,21,95,78,97,109,101,115,112,97,99,101,80,97,116, 104,46,97,112,112,101,110,100,78,41,15,114,125,0,0,0, 114,124,0,0,0,114,126,0,0,0,114,127,0,0,0,114, @@ -1948,7 +1948,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 1,0,0,114,33,1,0,0,114,36,1,0,0,114,37,1, 0,0,114,38,1,0,0,114,39,1,0,0,114,42,1,0, 0,114,186,0,0,0,114,3,0,0,0,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,22,1,0,0,106, + 114,3,0,0,0,114,6,0,0,0,114,22,1,0,0,108, 4,0,0,115,24,0,0,0,8,1,4,6,8,6,8,10, 8,4,8,13,8,3,8,3,8,3,8,3,8,3,8,3, 114,22,1,0,0,99,0,0,0,0,0,0,0,0,0,0, @@ -1965,7 +1965,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,95,1,100,0,83,0,114,110,0,0,0,41,2,114,22, 1,0,0,114,24,1,0,0,114,28,1,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,114,209,0,0, - 0,170,4,0,0,115,2,0,0,0,0,1,122,25,95,78, + 0,172,4,0,0,115,2,0,0,0,0,1,122,25,95,78, 97,109,101,115,112,97,99,101,76,111,97,100,101,114,46,95, 95,105,110,105,116,95,95,99,2,0,0,0,0,0,0,0, 0,0,0,0,2,0,0,0,3,0,0,0,67,0,0,0, @@ -1982,21 +1982,21 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 101,41,62,41,2,114,62,0,0,0,114,125,0,0,0,41, 2,114,193,0,0,0,114,216,0,0,0,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,11,109,111,100,117, - 108,101,95,114,101,112,114,173,4,0,0,115,2,0,0,0, + 108,101,95,114,101,112,114,175,4,0,0,115,2,0,0,0, 0,7,122,28,95,78,97,109,101,115,112,97,99,101,76,111, 97,100,101,114,46,109,111,100,117,108,101,95,114,101,112,114, 99,2,0,0,0,0,0,0,0,0,0,0,0,2,0,0, 0,1,0,0,0,67,0,0,0,115,4,0,0,0,100,1, 83,0,41,2,78,84,114,3,0,0,0,114,219,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 182,0,0,0,182,4,0,0,115,2,0,0,0,0,1,122, + 182,0,0,0,184,4,0,0,115,2,0,0,0,0,1,122, 27,95,78,97,109,101,115,112,97,99,101,76,111,97,100,101, 114,46,105,115,95,112,97,99,107,97,103,101,99,2,0,0, 0,0,0,0,0,0,0,0,0,2,0,0,0,1,0,0, 0,67,0,0,0,115,4,0,0,0,100,1,83,0,41,2, 78,114,40,0,0,0,114,3,0,0,0,114,219,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 229,0,0,0,185,4,0,0,115,2,0,0,0,0,1,122, + 229,0,0,0,187,4,0,0,115,2,0,0,0,0,1,122, 27,95,78,97,109,101,115,112,97,99,101,76,111,97,100,101, 114,46,103,101,116,95,115,111,117,114,99,101,99,2,0,0, 0,0,0,0,0,0,0,0,0,2,0,0,0,6,0,0, @@ -2005,21 +2005,21 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,122,8,60,115,116,114,105,110,103,62,114,215,0,0, 0,84,41,1,114,231,0,0,0,41,1,114,232,0,0,0, 114,219,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,114,213,0,0,0,188,4,0,0,115,2,0, + 6,0,0,0,114,213,0,0,0,190,4,0,0,115,2,0, 0,0,0,1,122,25,95,78,97,109,101,115,112,97,99,101, 76,111,97,100,101,114,46,103,101,116,95,99,111,100,101,99, 2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, 1,0,0,0,67,0,0,0,115,4,0,0,0,100,1,83, 0,114,210,0,0,0,114,3,0,0,0,114,211,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 212,0,0,0,191,4,0,0,115,2,0,0,0,0,1,122, + 212,0,0,0,193,4,0,0,115,2,0,0,0,0,1,122, 30,95,78,97,109,101,115,112,97,99,101,76,111,97,100,101, 114,46,99,114,101,97,116,101,95,109,111,100,117,108,101,99, 2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, 1,0,0,0,67,0,0,0,115,4,0,0,0,100,0,83, 0,114,110,0,0,0,114,3,0,0,0,114,253,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 217,0,0,0,194,4,0,0,115,2,0,0,0,0,1,122, + 217,0,0,0,196,4,0,0,115,2,0,0,0,0,1,122, 28,95,78,97,109,101,115,112,97,99,101,76,111,97,100,101, 114,46,101,120,101,99,95,109,111,100,117,108,101,99,2,0, 0,0,0,0,0,0,0,0,0,0,2,0,0,0,4,0, @@ -2037,7 +2037,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 33,114,125,41,4,114,134,0,0,0,114,149,0,0,0,114, 24,1,0,0,114,218,0,0,0,114,219,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,114,220,0, - 0,0,197,4,0,0,115,8,0,0,0,0,7,6,1,4, + 0,0,199,4,0,0,115,8,0,0,0,0,7,6,1,4, 255,4,2,122,28,95,78,97,109,101,115,112,97,99,101,76, 111,97,100,101,114,46,108,111,97,100,95,109,111,100,117,108, 101,78,41,12,114,125,0,0,0,114,124,0,0,0,114,126, @@ -2045,7 +2045,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,182,0,0,0,114,229,0,0,0,114,213,0,0, 0,114,212,0,0,0,114,217,0,0,0,114,220,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,114,43,1,0,0,169,4,0,0,115,18,0, + 6,0,0,0,114,43,1,0,0,171,4,0,0,115,18,0, 0,0,8,1,8,3,2,1,10,8,8,3,8,3,8,3, 8,3,8,3,114,43,1,0,0,99,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,4,0,0,0,64,0, @@ -2085,7 +2085,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 95,99,97,99,104,101,218,5,105,116,101,109,115,114,128,0, 0,0,114,46,1,0,0,41,3,114,193,0,0,0,114,117, 0,0,0,218,6,102,105,110,100,101,114,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,46,1,0,0,215, + 114,3,0,0,0,114,6,0,0,0,114,46,1,0,0,217, 4,0,0,115,10,0,0,0,0,4,22,1,8,1,10,1, 10,1,122,28,80,97,116,104,70,105,110,100,101,114,46,105, 110,118,97,108,105,100,97,116,101,95,99,97,99,104,101,115, @@ -2106,7 +2106,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,118,0,0,0,41,3,114,193,0,0,0,114,44,0,0, 0,90,4,104,111,111,107,114,3,0,0,0,114,3,0,0, 0,114,6,0,0,0,218,11,95,112,97,116,104,95,104,111, - 111,107,115,225,4,0,0,115,16,0,0,0,0,3,16,1, + 111,107,115,227,4,0,0,115,16,0,0,0,0,3,16,1, 12,1,10,1,2,1,14,1,14,1,12,2,122,22,80,97, 116,104,70,105,110,100,101,114,46,95,112,97,116,104,95,104, 111,111,107,115,99,2,0,0,0,0,0,0,0,0,0,0, @@ -2137,7 +2137,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,3,114,193,0,0,0,114,44,0,0,0,114,50,1,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, 218,20,95,112,97,116,104,95,105,109,112,111,114,116,101,114, - 95,99,97,99,104,101,238,4,0,0,115,22,0,0,0,0, + 95,99,97,99,104,101,240,4,0,0,115,22,0,0,0,0, 8,8,1,2,1,12,1,14,3,8,1,2,1,14,1,14, 1,10,1,16,1,122,31,80,97,116,104,70,105,110,100,101, 114,46,95,112,97,116,104,95,105,109,112,111,114,116,101,114, @@ -2155,7 +2155,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 50,1,0,0,114,140,0,0,0,114,141,0,0,0,114,187, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, 0,0,218,16,95,108,101,103,97,99,121,95,103,101,116,95, - 115,112,101,99,4,5,0,0,115,18,0,0,0,0,4,10, + 115,112,101,99,6,5,0,0,115,18,0,0,0,0,4,10, 1,16,2,10,1,4,1,8,1,12,1,12,1,6,1,122, 27,80,97,116,104,70,105,110,100,101,114,46,95,108,101,103, 97,99,121,95,103,101,116,95,115,112,101,99,78,99,4,0, @@ -2186,7 +2186,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 115,112,97,99,101,95,112,97,116,104,90,5,101,110,116,114, 121,114,50,1,0,0,114,187,0,0,0,114,141,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 9,95,103,101,116,95,115,112,101,99,19,5,0,0,115,40, + 9,95,103,101,116,95,115,112,101,99,21,5,0,0,115,40, 0,0,0,0,5,4,1,8,1,14,1,2,1,10,1,8, 1,10,1,14,2,12,1,8,1,2,1,10,1,8,1,6, 1,8,1,8,5,12,2,12,1,6,1,122,20,80,97,116, @@ -2213,7 +2213,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,181,0,0,0,114,22,1,0,0,41,6,114,193,0,0, 0,114,139,0,0,0,114,44,0,0,0,114,202,0,0,0, 114,187,0,0,0,114,57,1,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,203,0,0,0,51,5, + 3,0,0,0,114,6,0,0,0,114,203,0,0,0,53,5, 0,0,115,26,0,0,0,0,6,8,1,6,1,14,1,8, 1,4,1,10,1,6,1,4,3,6,1,16,1,4,2,6, 2,122,20,80,97,116,104,70,105,110,100,101,114,46,102,105, @@ -2234,7 +2234,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 110,115,116,101,97,100,46,10,10,32,32,32,32,32,32,32, 32,78,114,204,0,0,0,114,205,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,114,206,0,0,0, - 75,5,0,0,115,8,0,0,0,0,8,12,1,8,1,4, + 77,5,0,0,115,8,0,0,0,0,8,12,1,8,1,4, 1,122,22,80,97,116,104,70,105,110,100,101,114,46,102,105, 110,100,95,109,111,100,117,108,101,122,45,40,63,58,123,112, 97,116,116,101,114,110,125,40,45,46,42,41,63,92,46,40, @@ -2276,7 +2276,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 112,97,116,116,101,114,110,90,5,102,111,117,110,100,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,218,18,102, 105,110,100,95,100,105,115,116,114,105,98,117,116,105,111,110, - 115,90,5,0,0,115,14,0,0,0,0,10,8,1,12,1, + 115,92,5,0,0,115,14,0,0,0,0,10,8,1,12,1, 8,1,6,1,22,1,12,1,122,29,80,97,116,104,70,105, 110,100,101,114,46,102,105,110,100,95,100,105,115,116,114,105, 98,117,116,105,111,110,115,99,3,0,0,0,0,0,0,0, @@ -2295,7 +2295,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,114,99,104,95,112,97,116,104,41,2,114,32,0,0,0, 114,44,0,0,0,169,2,114,193,0,0,0,114,63,1,0, 0,114,3,0,0,0,114,6,0,0,0,114,19,1,0,0, - 112,5,0,0,115,4,0,0,0,4,2,2,255,122,43,80, + 114,5,0,0,115,4,0,0,0,4,2,2,255,122,43,80, 97,116,104,70,105,110,100,101,114,46,95,115,101,97,114,99, 104,95,112,97,116,104,115,46,60,108,111,99,97,108,115,62, 46,60,103,101,110,101,120,112,114,62,41,5,218,9,105,116, @@ -2304,7 +2304,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,218,12,95,115,119,105,116,99,104,95,112,97,116,104, 41,4,114,193,0,0,0,114,63,1,0,0,90,5,112,97, 116,104,115,114,67,1,0,0,114,3,0,0,0,114,66,1, - 0,0,114,6,0,0,0,114,61,1,0,0,108,5,0,0, + 0,0,114,6,0,0,0,114,61,1,0,0,110,5,0,0, 115,8,0,0,0,0,3,8,1,18,2,10,254,122,24,80, 97,116,104,70,105,110,100,101,114,46,95,115,101,97,114,99, 104,95,112,97,116,104,115,99,1,0,0,0,0,0,0,0, @@ -2321,7 +2321,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 104,108,105,98,114,70,1,0,0,218,9,69,120,99,101,112, 116,105,111,110,41,4,114,44,0,0,0,114,69,1,0,0, 114,71,1,0,0,114,70,1,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,68,1,0,0,117,5, + 3,0,0,0,114,6,0,0,0,114,68,1,0,0,119,5, 0,0,115,12,0,0,0,0,2,12,1,8,1,12,1,10, 1,28,1,122,23,80,97,116,104,70,105,110,100,101,114,46, 95,115,119,105,116,99,104,95,112,97,116,104,99,4,0,0, @@ -2335,7 +2335,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 193,0,0,0,114,63,1,0,0,218,4,114,111,111,116,114, 41,1,0,0,114,60,1,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,10,95,112,114,101,100,105, - 99,97,116,101,126,5,0,0,115,4,0,0,0,0,2,8, + 99,97,116,101,128,5,0,0,115,4,0,0,0,0,2,8, 1,122,21,80,97,116,104,70,105,110,100,101,114,46,95,112, 114,101,100,105,99,97,116,101,99,3,0,0,0,0,0,0, 0,0,0,0,0,4,0,0,0,4,0,0,0,3,0,0, @@ -2352,7 +2352,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,1,114,74,1,0,0,41,2,114,32,0,0,0,114,41, 1,0,0,169,3,114,193,0,0,0,90,7,109,97,116,99, 104,101,114,114,73,1,0,0,114,3,0,0,0,114,6,0, - 0,0,114,19,1,0,0,137,5,0,0,115,6,0,0,0, + 0,0,114,19,1,0,0,139,5,0,0,115,6,0,0,0, 4,0,2,1,14,255,122,42,80,97,116,104,70,105,110,100, 101,114,46,95,115,101,97,114,99,104,95,112,97,116,104,46, 60,108,111,99,97,108,115,62,46,60,103,101,110,101,120,112, @@ -2362,7 +2362,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,4,114,193,0,0,0,114,73,1,0,0,114,63,1,0, 0,90,10,110,111,114,109,97,108,105,122,101,100,114,3,0, 0,0,114,76,1,0,0,114,6,0,0,0,114,65,1,0, - 0,131,5,0,0,115,10,0,0,0,0,2,8,1,4,1, + 0,133,5,0,0,115,10,0,0,0,0,2,8,1,4,1, 12,1,14,1,122,23,80,97,116,104,70,105,110,100,101,114, 46,95,115,101,97,114,99,104,95,112,97,116,104,41,1,78, 41,2,78,78,41,1,78,41,2,78,78,41,19,114,125,0, @@ -2373,7 +2373,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 1,0,0,114,61,1,0,0,218,12,115,116,97,116,105,99, 109,101,116,104,111,100,114,68,1,0,0,114,74,1,0,0, 114,65,1,0,0,114,3,0,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,45,1,0,0,211,4, + 3,0,0,0,114,6,0,0,0,114,45,1,0,0,213,4, 0,0,115,52,0,0,0,8,2,4,2,2,1,10,9,2, 1,10,12,2,1,10,21,2,1,10,14,2,1,12,31,2, 1,12,23,2,1,12,12,4,2,2,1,12,17,2,1,10, @@ -2419,7 +2419,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,124,0,93,14,125,1,124,1,136,0,102,2,86,0, 1,0,113,2,100,0,83,0,114,110,0,0,0,114,3,0, 0,0,114,16,1,0,0,169,1,114,140,0,0,0,114,3, - 0,0,0,114,6,0,0,0,114,19,1,0,0,156,5,0, + 0,0,0,114,6,0,0,0,114,19,1,0,0,158,5,0, 0,115,4,0,0,0,4,0,2,0,122,38,70,105,108,101, 70,105,110,100,101,114,46,95,95,105,110,105,116,95,95,46, 60,108,111,99,97,108,115,62,46,60,103,101,110,101,120,112, @@ -2432,7 +2432,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,218,14,108,111,97,100,101,114,95,100,101,116,97, 105,108,115,90,7,108,111,97,100,101,114,115,114,189,0,0, 0,114,3,0,0,0,114,80,1,0,0,114,6,0,0,0, - 114,209,0,0,0,150,5,0,0,115,16,0,0,0,0,4, + 114,209,0,0,0,152,5,0,0,115,16,0,0,0,0,4, 4,1,12,1,26,1,6,2,10,1,6,1,8,1,122,19, 70,105,108,101,70,105,110,100,101,114,46,95,95,105,110,105, 116,95,95,99,1,0,0,0,0,0,0,0,0,0,0,0, @@ -2442,7 +2442,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,101,99,116,111,114,121,32,109,116,105,109,101,46,114,105, 0,0,0,78,41,1,114,82,1,0,0,114,246,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 46,1,0,0,164,5,0,0,115,2,0,0,0,0,2,122, + 46,1,0,0,166,5,0,0,115,2,0,0,0,0,2,122, 28,70,105,108,101,70,105,110,100,101,114,46,105,110,118,97, 108,105,100,97,116,101,95,99,97,99,104,101,115,99,2,0, 0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0, @@ -2465,7 +2465,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 3,114,203,0,0,0,114,140,0,0,0,114,178,0,0,0, 41,3,114,119,0,0,0,114,139,0,0,0,114,187,0,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,137,0,0,0,170,5,0,0,115,8,0,0,0,0,7, + 114,137,0,0,0,172,5,0,0,115,8,0,0,0,0,7, 10,1,8,1,8,1,122,22,70,105,108,101,70,105,110,100, 101,114,46,102,105,110,100,95,108,111,97,100,101,114,99,6, 0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,6, @@ -2475,7 +2475,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,190,0,0,0,41,7,114,119,0,0,0,114,188,0,0, 0,114,139,0,0,0,114,44,0,0,0,90,4,115,109,115, 108,114,202,0,0,0,114,140,0,0,0,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,58,1,0,0,182, + 114,3,0,0,0,114,6,0,0,0,114,58,1,0,0,184, 5,0,0,115,8,0,0,0,0,1,10,1,8,1,2,255, 122,20,70,105,108,101,70,105,110,100,101,114,46,95,103,101, 116,95,115,112,101,99,78,99,3,0,0,0,0,0,0,0, @@ -2530,7 +2530,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,188,0,0,0,90,13,105,110,105,116,95,102,105,108, 101,110,97,109,101,90,9,102,117,108,108,95,112,97,116,104, 114,187,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,114,203,0,0,0,187,5,0,0,115,74,0, + 6,0,0,0,114,203,0,0,0,189,5,0,0,115,74,0, 0,0,0,5,4,1,14,1,2,1,24,1,14,1,10,1, 10,1,8,1,6,2,6,1,6,1,10,2,6,1,4,2, 8,1,12,1,14,1,8,1,10,1,8,1,26,4,8,2, @@ -2562,7 +2562,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,146,2,113,4,83,0,114,3,0,0,0,41,1,114,106, 0,0,0,41,2,114,32,0,0,0,90,2,102,110,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,218,9,60, - 115,101,116,99,111,109,112,62,8,6,0,0,115,4,0,0, + 115,101,116,99,111,109,112,62,10,6,0,0,115,4,0,0, 0,6,0,2,0,122,41,70,105,108,101,70,105,110,100,101, 114,46,95,102,105,108,108,95,99,97,99,104,101,46,60,108, 111,99,97,108,115,62,46,60,115,101,116,99,111,109,112,62, @@ -2579,7 +2579,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,41,1,0,0,114,117,0,0,0,114,29,1,0,0,114, 17,1,0,0,90,8,110,101,119,95,110,97,109,101,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,114,87,1, - 0,0,235,5,0,0,115,34,0,0,0,0,2,6,1,2, + 0,0,237,5,0,0,115,34,0,0,0,0,2,6,1,2, 1,22,1,20,3,10,3,12,1,12,7,6,1,8,1,16, 1,4,1,18,2,4,1,12,1,6,1,12,1,122,22,70, 105,108,101,70,105,110,100,101,114,46,95,102,105,108,108,95, @@ -2617,14 +2617,14 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,48,0,0,0,169,2,114,193,0,0,0,114,86, 1,0,0,114,3,0,0,0,114,6,0,0,0,218,24,112, 97,116,104,95,104,111,111,107,95,102,111,114,95,70,105,108, - 101,70,105,110,100,101,114,20,6,0,0,115,6,0,0,0, + 101,70,105,110,100,101,114,22,6,0,0,115,6,0,0,0, 0,2,8,1,12,1,122,54,70,105,108,101,70,105,110,100, 101,114,46,112,97,116,104,95,104,111,111,107,46,60,108,111, 99,97,108,115,62,46,112,97,116,104,95,104,111,111,107,95, 102,111,114,95,70,105,108,101,70,105,110,100,101,114,114,3, 0,0,0,41,3,114,193,0,0,0,114,86,1,0,0,114, 93,1,0,0,114,3,0,0,0,114,92,1,0,0,114,6, - 0,0,0,218,9,112,97,116,104,95,104,111,111,107,10,6, + 0,0,0,218,9,112,97,116,104,95,104,111,111,107,12,6, 0,0,115,4,0,0,0,0,10,14,6,122,20,70,105,108, 101,70,105,110,100,101,114,46,112,97,116,104,95,104,111,111, 107,99,1,0,0,0,0,0,0,0,0,0,0,0,1,0, @@ -2633,7 +2633,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 70,105,108,101,70,105,110,100,101,114,40,123,33,114,125,41, 41,2,114,62,0,0,0,114,44,0,0,0,114,246,0,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,39,1,0,0,28,6,0,0,115,2,0,0,0,0,1, + 114,39,1,0,0,30,6,0,0,115,2,0,0,0,0,1, 122,19,70,105,108,101,70,105,110,100,101,114,46,95,95,114, 101,112,114,95,95,41,1,78,41,15,114,125,0,0,0,114, 124,0,0,0,114,126,0,0,0,114,127,0,0,0,114,209, @@ -2641,7 +2641,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,137,0,0,0,114,58,1,0,0,114,203,0,0, 0,114,87,1,0,0,114,207,0,0,0,114,94,1,0,0, 114,39,1,0,0,114,3,0,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,79,1,0,0,141,5, + 3,0,0,0,114,6,0,0,0,114,79,1,0,0,143,5, 0,0,115,22,0,0,0,8,2,4,7,8,14,8,4,4, 2,8,12,8,5,10,48,8,31,2,1,10,17,114,79,1, 0,0,99,4,0,0,0,0,0,0,0,0,0,0,0,6, @@ -2664,7 +2664,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,90,8,112,97,116,104,110,97,109,101,90,9,99,112,97, 116,104,110,97,109,101,114,140,0,0,0,114,187,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 14,95,102,105,120,95,117,112,95,109,111,100,117,108,101,34, + 14,95,102,105,120,95,117,112,95,109,111,100,117,108,101,36, 6,0,0,115,34,0,0,0,0,2,10,1,10,1,4,1, 4,1,8,1,8,1,12,2,10,1,4,1,14,1,2,1, 8,1,8,1,8,1,12,1,14,2,114,98,1,0,0,99, @@ -2684,7 +2684,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,15,1,0,0,114,89,0,0,0,41,3,90,10,101,120, 116,101,110,115,105,111,110,115,90,6,115,111,117,114,99,101, 90,8,98,121,116,101,99,111,100,101,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,184,0,0,0,57,6, + 3,0,0,0,114,6,0,0,0,114,184,0,0,0,59,6, 0,0,115,8,0,0,0,0,5,12,1,8,1,8,1,114, 184,0,0,0,99,1,0,0,0,0,0,0,0,0,0,0, 0,12,0,0,0,9,0,0,0,67,0,0,0,115,178,1, @@ -2737,7 +2737,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,1,0,113,2,100,1,83,0,41,2,114,39,0,0,0, 78,41,1,114,22,0,0,0,41,2,114,32,0,0,0,114, 95,0,0,0,114,3,0,0,0,114,3,0,0,0,114,6, - 0,0,0,114,19,1,0,0,93,6,0,0,115,4,0,0, + 0,0,0,114,19,1,0,0,95,6,0,0,115,4,0,0, 0,4,0,2,0,122,25,95,115,101,116,117,112,46,60,108, 111,99,97,108,115,62,46,60,103,101,110,101,120,112,114,62, 114,73,0,0,0,122,30,105,109,112,111,114,116,108,105,98, @@ -2749,7 +2749,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 93,14,125,1,100,0,124,1,155,0,157,2,146,2,113,4, 83,0,41,1,114,74,0,0,0,114,3,0,0,0,41,2, 114,32,0,0,0,218,1,115,114,3,0,0,0,114,3,0, - 0,0,114,6,0,0,0,114,88,1,0,0,109,6,0,0, + 0,0,114,6,0,0,0,114,88,1,0,0,111,6,0,0, 115,4,0,0,0,6,0,2,0,122,25,95,115,101,116,117, 112,46,60,108,111,99,97,108,115,62,46,60,115,101,116,99, 111,109,112,62,90,7,95,116,104,114,101,97,100,90,8,95, @@ -2773,7 +2773,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 111,100,117,108,101,90,14,119,101,97,107,114,101,102,95,109, 111,100,117,108,101,90,13,119,105,110,114,101,103,95,109,111, 100,117,108,101,114,3,0,0,0,114,3,0,0,0,114,6, - 0,0,0,218,6,95,115,101,116,117,112,68,6,0,0,115, + 0,0,0,218,6,95,115,101,116,117,112,70,6,0,0,115, 78,0,0,0,0,8,4,1,6,1,6,3,10,1,8,1, 10,1,12,2,10,1,14,3,22,1,12,2,22,1,8,1, 10,1,10,1,6,2,2,1,10,1,10,1,14,1,12,2, @@ -2794,7 +2794,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 2,114,105,1,0,0,90,17,115,117,112,112,111,114,116,101, 100,95,108,111,97,100,101,114,115,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,8,95,105,110,115,116,97, - 108,108,133,6,0,0,115,8,0,0,0,0,2,8,1,6, + 108,108,135,6,0,0,115,8,0,0,0,0,2,8,1,6, 1,20,1,114,108,1,0,0,41,1,114,60,0,0,0,41, 1,78,41,3,78,78,78,41,2,114,73,0,0,0,114,73, 0,0,0,41,1,84,41,1,78,41,1,78,41,63,114,127, @@ -2827,7 +2827,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 6,0,0,0,218,8,60,109,111,100,117,108,101,62,1,0, 0,0,115,126,0,0,0,4,22,4,1,4,1,2,1,2, 255,4,4,8,17,8,5,8,5,8,6,8,6,8,12,8, - 10,8,9,8,5,8,7,8,9,10,22,10,127,0,10,16, + 10,8,9,8,5,8,7,8,9,10,22,10,127,0,12,16, 1,12,2,4,1,4,2,6,2,6,2,8,2,16,71,8, 40,8,19,8,12,8,12,8,28,8,17,8,33,8,28,8, 24,10,13,10,10,10,11,8,14,6,3,4,1,2,255,12, From webhook-mailer at python.org Sun Jul 14 19:49:56 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 14 Jul 2019 23:49:56 -0000 Subject: [Python-checkins] bpo-37593: Swap the positions of posonlyargs and args in the constructor of ast.parameters nodes (GH-14778) Message-ID: https://github.com/python/cpython/commit/cf9a63c6c7e19f3d27cf3b5731d02cc216ef3dd1 commit: cf9a63c6c7e19f3d27cf3b5731d02cc216ef3dd1 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-14T16:49:52-07:00 summary: bpo-37593: Swap the positions of posonlyargs and args in the constructor of ast.parameters nodes (GH-14778) https://bugs.python.org/issue37593 (cherry picked from commit cd6e83b4810549c308ab2d7315dbab526e35ccf6) Co-authored-by: Pablo Galindo files: A Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH.rst M Include/Python-ast.h M Lib/importlib/_bootstrap_external.py M Lib/test/test_ast.py M Parser/Python.asdl M Python/Python-ast.c M Python/ast.c M Python/importlib_external.h diff --git a/Include/Python-ast.h b/Include/Python-ast.h index 62626503403b..5fe4f2b4314c 100644 --- a/Include/Python-ast.h +++ b/Include/Python-ast.h @@ -426,8 +426,8 @@ struct _excepthandler { }; struct _arguments { - asdl_seq *args; asdl_seq *posonlyargs; + asdl_seq *args; arg_ty vararg; asdl_seq *kwonlyargs; asdl_seq *kw_defaults; @@ -687,7 +687,7 @@ excepthandler_ty _Py_ExceptHandler(expr_ty type, identifier name, asdl_seq * end_lineno, int end_col_offset, PyArena *arena); #define arguments(a0, a1, a2, a3, a4, a5, a6, a7) _Py_arguments(a0, a1, a2, a3, a4, a5, a6, a7) -arguments_ty _Py_arguments(asdl_seq * args, asdl_seq * posonlyargs, arg_ty +arguments_ty _Py_arguments(asdl_seq * posonlyargs, asdl_seq * args, arg_ty vararg, asdl_seq * kwonlyargs, asdl_seq * kw_defaults, arg_ty kwarg, asdl_seq * defaults, PyArena *arena); diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 64185771abc8..1bafc242c518 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -268,6 +268,8 @@ def _write_atomic(path, data, mode=0o666): # Python 3.8a1 3410 (PEP570 Python Positional-Only Parameters #36540) # Python 3.8b2 3411 (Reverse evaluation order of key: value in dict # comprehensions #35224) +# Python 3.8b2 3412 (Swap the position of positional args and positional +# only args in ast.arguments #37593) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -276,7 +278,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3411).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3412).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 6bd27ce12a62..5c37a5fbed6e 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -331,8 +331,8 @@ def test_field_attr_existence(self): def test_arguments(self): x = ast.arguments() - self.assertEqual(x._fields, ('args', 'posonlyargs', 'vararg', 'kwonlyargs', - 'kw_defaults', 'kwarg', 'defaults')) + self.assertEqual(x._fields, ('posonlyargs', 'args', 'vararg', 'kwonlyargs', + 'kw_defaults', 'kwarg', 'defaults')) with self.assertRaises(AttributeError): x.vararg @@ -1683,11 +1683,11 @@ def main(): ('Module', [('Expr', (1, 0), ('Constant', (1, 0), 'module docstring', None))], []), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 9))], [], None, None)], []), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Expr', (1, 9), ('Constant', (1, 9), 'function docstring', None))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, []), [('Pass', (1, 10))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, [('Constant', (1, 8), 0, None)]), [('Pass', (1, 12))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 10))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None)], None, [], [], None, [('Constant', (1, 8), 0, None)]), [('Pass', (1, 12))], [], None, None)], []), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], ('arg', (1, 7), 'args', None, None), [], [], None, []), [('Pass', (1, 14))], [], None, None)], []), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [], None, [], [], ('arg', (1, 8), 'kwargs', None, None), []), [('Pass', (1, 17))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None), ('arg', (1, 9), 'b', None, None), ('arg', (1, 14), 'c', None, None), ('arg', (1, 22), 'd', None, None), ('arg', (1, 28), 'e', None, None)], [], ('arg', (1, 35), 'args', None, None), [('arg', (1, 41), 'f', None, None)], [('Constant', (1, 43), 42, None)], ('arg', (1, 49), 'kwargs', None, None), [('Constant', (1, 11), 1, None), ('Constant', (1, 16), None, None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [('Expr', (1, 58), ('Constant', (1, 58), 'doc for f()', None))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None), ('arg', (1, 9), 'b', None, None), ('arg', (1, 14), 'c', None, None), ('arg', (1, 22), 'd', None, None), ('arg', (1, 28), 'e', None, None)], ('arg', (1, 35), 'args', None, None), [('arg', (1, 41), 'f', None, None)], [('Constant', (1, 43), 42, None)], ('arg', (1, 49), 'kwargs', None, None), [('Constant', (1, 11), 1, None), ('Constant', (1, 16), None, None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [('Expr', (1, 58), ('Constant', (1, 58), 'doc for f()', None))], [], None, None)], []), ('Module', [('ClassDef', (1, 0), 'C', [], [], [('Pass', (1, 8))], [])], []), ('Module', [('ClassDef', (1, 0), 'C', [], [], [('Expr', (1, 9), ('Constant', (1, 9), 'docstring for class C', None))], [])], []), ('Module', [('ClassDef', (1, 0), 'C', [('Name', (1, 8), 'object', ('Load',))], [], [('Pass', (1, 17))], [])], []), @@ -1733,16 +1733,16 @@ def main(): ('Module', [('ClassDef', (3, 0), 'C', [], [], [('Pass', (3, 9))], [('Name', (1, 1), 'deco1', ('Load',)), ('Call', (2, 0), ('Name', (2, 1), 'deco2', ('Load',)), [], [])])], []), ('Module', [('FunctionDef', (2, 0), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (2, 9))], [('Call', (1, 1), ('Name', (1, 1), 'deco', ('Load',)), [('GeneratorExp', (1, 5), ('Name', (1, 6), 'a', ('Load',)), [('comprehension', ('Name', (1, 12), 'a', ('Store',)), ('Name', (1, 17), 'b', ('Load',)), [], 0)])], [])], None, None)], []), ('Module', [('Expr', (1, 0), ('NamedExpr', (1, 1), ('Name', (1, 1), 'a', ('Store',)), ('Constant', (1, 6), 1, None)))], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 14))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 12), 'c', None, None), ('arg', (1, 15), 'd', None, None), ('arg', (1, 18), 'e', None, None)], [('arg', (1, 6), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 22))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 12), 'c', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 18), 'd', None, None), ('arg', (1, 21), 'e', None, None)], [None, None], None, []), [('Pass', (1, 25))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 12), 'c', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 18), 'd', None, None), ('arg', (1, 21), 'e', None, None)], [None, None], ('arg', (1, 26), 'kwargs', None, None), []), [('Pass', (1, 35))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], [('arg', (1, 6), 'a', None, None)], None, [], [], None, [('Constant', (1, 8), 1, None)]), [('Pass', (1, 16))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None), ('arg', (1, 19), 'c', None, None)], [('arg', (1, 6), 'a', None, None)], None, [], [], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None), ('Constant', (1, 21), 4, None)]), [('Pass', (1, 25))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 22), 'c', None, None)], [('Constant', (1, 24), 4, None)], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 28))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 22), 'c', None, None)], [None], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 26))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 22), 'c', None, None)], [('Constant', (1, 24), 4, None)], ('arg', (1, 29), 'kwargs', None, None), [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 38))], [], None, None)], []), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 14), 'b', None, None)], [('arg', (1, 6), 'a', None, None)], None, [('arg', (1, 22), 'c', None, None)], [None], ('arg', (1, 27), 'kwargs', None, None), [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 36))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, []), [('Pass', (1, 14))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 12), 'c', None, None), ('arg', (1, 15), 'd', None, None), ('arg', (1, 18), 'e', None, None)], None, [], [], None, []), [('Pass', (1, 22))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 12), 'c', None, None)], None, [('arg', (1, 18), 'd', None, None), ('arg', (1, 21), 'e', None, None)], [None, None], None, []), [('Pass', (1, 25))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 12), 'c', None, None)], None, [('arg', (1, 18), 'd', None, None), ('arg', (1, 21), 'e', None, None)], [None, None], ('arg', (1, 26), 'kwargs', None, None), []), [('Pass', (1, 35))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [], None, [], [], None, [('Constant', (1, 8), 1, None)]), [('Pass', (1, 16))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None), ('arg', (1, 19), 'c', None, None)], None, [], [], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None), ('Constant', (1, 21), 4, None)]), [('Pass', (1, 25))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None)], None, [('arg', (1, 22), 'c', None, None)], [('Constant', (1, 24), 4, None)], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 28))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None)], None, [('arg', (1, 22), 'c', None, None)], [None], None, [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 26))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None)], None, [('arg', (1, 22), 'c', None, None)], [('Constant', (1, 24), 4, None)], ('arg', (1, 29), 'kwargs', None, None), [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 38))], [], None, None)], []), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None, None)], [('arg', (1, 14), 'b', None, None)], None, [('arg', (1, 22), 'c', None, None)], [None], ('arg', (1, 27), 'kwargs', None, None), [('Constant', (1, 8), 1, None), ('Constant', (1, 16), 2, None)]), [('Pass', (1, 36))], [], None, None)], []), ] single_results = [ ('Interactive', [('Expr', (1, 0), ('BinOp', (1, 0), ('Constant', (1, 0), 1, None), ('Add',), ('Constant', (1, 2), 2, None)))]), diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH.rst new file mode 100644 index 000000000000..5ec9bba2d2b9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH.rst @@ -0,0 +1,2 @@ +Swap the positions of the *posonlyargs* and *args* parameters in the +constructor of :class:`ast.parameters` nodes. diff --git a/Parser/Python.asdl b/Parser/Python.asdl index 0c00d398b461..126d478975bb 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -111,7 +111,7 @@ module Python excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body) attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset) - arguments = (arg* args, arg* posonlyargs, arg? vararg, arg* kwonlyargs, + arguments = (arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults, arg? kwarg, expr* defaults) arg = (identifier arg, expr? annotation, string? type_comment) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 0774c58e2d23..fd96964a1e77 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -476,8 +476,8 @@ _Py_IDENTIFIER(kw_defaults); _Py_IDENTIFIER(kwarg); _Py_IDENTIFIER(defaults); static char *arguments_fields[]={ - "args", "posonlyargs", + "args", "vararg", "kwonlyargs", "kw_defaults", @@ -2573,7 +2573,7 @@ ExceptHandler(expr_ty type, identifier name, asdl_seq * body, int lineno, int } arguments_ty -arguments(asdl_seq * args, asdl_seq * posonlyargs, arg_ty vararg, asdl_seq * +arguments(asdl_seq * posonlyargs, asdl_seq * args, arg_ty vararg, asdl_seq * kwonlyargs, asdl_seq * kw_defaults, arg_ty kwarg, asdl_seq * defaults, PyArena *arena) { @@ -2581,8 +2581,8 @@ arguments(asdl_seq * args, asdl_seq * posonlyargs, arg_ty vararg, asdl_seq * p = (arguments_ty)PyArena_Malloc(arena, sizeof(*p)); if (!p) return NULL; - p->args = args; p->posonlyargs = posonlyargs; + p->args = args; p->vararg = vararg; p->kwonlyargs = kwonlyargs; p->kw_defaults = kw_defaults; @@ -3961,14 +3961,14 @@ ast2obj_arguments(void* _o) result = PyType_GenericNew(arguments_type, NULL, NULL); if (!result) return NULL; - value = ast2obj_list(o->args, ast2obj_arg); + value = ast2obj_list(o->posonlyargs, ast2obj_arg); if (!value) goto failed; - if (_PyObject_SetAttrId(result, &PyId_args, value) == -1) + if (_PyObject_SetAttrId(result, &PyId_posonlyargs, value) == -1) goto failed; Py_DECREF(value); - value = ast2obj_list(o->posonlyargs, ast2obj_arg); + value = ast2obj_list(o->args, ast2obj_arg); if (!value) goto failed; - if (_PyObject_SetAttrId(result, &PyId_posonlyargs, value) == -1) + if (_PyObject_SetAttrId(result, &PyId_args, value) == -1) goto failed; Py_DECREF(value); value = ast2obj_arg(o->vararg); @@ -8288,19 +8288,19 @@ int obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena) { PyObject* tmp = NULL; - asdl_seq* args; asdl_seq* posonlyargs; + asdl_seq* args; arg_ty vararg; asdl_seq* kwonlyargs; asdl_seq* kw_defaults; arg_ty kwarg; asdl_seq* defaults; - if (_PyObject_LookupAttrId(obj, &PyId_args, &tmp) < 0) { + if (_PyObject_LookupAttrId(obj, &PyId_posonlyargs, &tmp) < 0) { return 1; } if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from arguments"); + PyErr_SetString(PyExc_TypeError, "required field \"posonlyargs\" missing from arguments"); return 1; } else { @@ -8308,29 +8308,29 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena) Py_ssize_t len; Py_ssize_t i; if (!PyList_Check(tmp)) { - PyErr_Format(PyExc_TypeError, "arguments field \"args\" must be a list, not a %.200s", tmp->ob_type->tp_name); + PyErr_Format(PyExc_TypeError, "arguments field \"posonlyargs\" must be a list, not a %.200s", tmp->ob_type->tp_name); goto failed; } len = PyList_GET_SIZE(tmp); - args = _Py_asdl_seq_new(len, arena); - if (args == NULL) goto failed; + posonlyargs = _Py_asdl_seq_new(len, arena); + if (posonlyargs == NULL) goto failed; for (i = 0; i < len; i++) { arg_ty val; res = obj2ast_arg(PyList_GET_ITEM(tmp, i), &val, arena); if (res != 0) goto failed; if (len != PyList_GET_SIZE(tmp)) { - PyErr_SetString(PyExc_RuntimeError, "arguments field \"args\" changed size during iteration"); + PyErr_SetString(PyExc_RuntimeError, "arguments field \"posonlyargs\" changed size during iteration"); goto failed; } - asdl_seq_SET(args, i, val); + asdl_seq_SET(posonlyargs, i, val); } Py_CLEAR(tmp); } - if (_PyObject_LookupAttrId(obj, &PyId_posonlyargs, &tmp) < 0) { + if (_PyObject_LookupAttrId(obj, &PyId_args, &tmp) < 0) { return 1; } if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"posonlyargs\" missing from arguments"); + PyErr_SetString(PyExc_TypeError, "required field \"args\" missing from arguments"); return 1; } else { @@ -8338,21 +8338,21 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena) Py_ssize_t len; Py_ssize_t i; if (!PyList_Check(tmp)) { - PyErr_Format(PyExc_TypeError, "arguments field \"posonlyargs\" must be a list, not a %.200s", tmp->ob_type->tp_name); + PyErr_Format(PyExc_TypeError, "arguments field \"args\" must be a list, not a %.200s", tmp->ob_type->tp_name); goto failed; } len = PyList_GET_SIZE(tmp); - posonlyargs = _Py_asdl_seq_new(len, arena); - if (posonlyargs == NULL) goto failed; + args = _Py_asdl_seq_new(len, arena); + if (args == NULL) goto failed; for (i = 0; i < len; i++) { arg_ty val; res = obj2ast_arg(PyList_GET_ITEM(tmp, i), &val, arena); if (res != 0) goto failed; if (len != PyList_GET_SIZE(tmp)) { - PyErr_SetString(PyExc_RuntimeError, "arguments field \"posonlyargs\" changed size during iteration"); + PyErr_SetString(PyExc_RuntimeError, "arguments field \"args\" changed size during iteration"); goto failed; } - asdl_seq_SET(posonlyargs, i, val); + asdl_seq_SET(args, i, val); } Py_CLEAR(tmp); } @@ -8472,7 +8472,7 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena) } Py_CLEAR(tmp); } - *out = arguments(args, posonlyargs, vararg, kwonlyargs, kw_defaults, kwarg, + *out = arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults, arena); return 0; failed: diff --git a/Python/ast.c b/Python/ast.c index 317a42b7f121..f6c2049ae2cf 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -1688,7 +1688,7 @@ ast_for_arguments(struct compiling *c, const node *n) return NULL; } } - return arguments(posargs, posonlyargs, vararg, kwonlyargs, kwdefaults, kwarg, posdefaults, c->c_arena); + return arguments(posonlyargs, posargs, vararg, kwonlyargs, kwdefaults, kwarg, posdefaults, c->c_arena); } static expr_ty diff --git a/Python/importlib_external.h b/Python/importlib_external.h index 33b2ca6820f1..4a3cb24e37b9 100644 --- a/Python/importlib_external.h +++ b/Python/importlib_external.h @@ -278,7 +278,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 95,97,116,111,109,105,99,120,0,0,0,115,30,0,0,0, 0,5,16,1,6,1,16,0,6,255,4,2,2,3,14,1, 20,1,16,1,14,1,2,1,14,1,14,1,6,1,114,69, - 0,0,0,105,83,13,0,0,114,28,0,0,0,114,16,0, + 0,0,0,105,84,13,0,0,114,28,0,0,0,114,16,0, 0,0,115,2,0,0,0,13,10,90,11,95,95,112,121,99, 97,99,104,101,95,95,122,4,111,112,116,45,122,3,46,112, 121,122,4,46,112,121,99,78,41,1,218,12,111,112,116,105, @@ -392,7 +392,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 111,115,116,95,102,105,108,101,110,97,109,101,218,8,102,105, 108,101,110,97,109,101,114,3,0,0,0,114,3,0,0,0, 114,6,0,0,0,218,17,99,97,99,104,101,95,102,114,111, - 109,95,115,111,117,114,99,101,35,1,0,0,115,72,0,0, + 109,95,115,111,117,114,99,101,37,1,0,0,115,72,0,0, 0,0,18,8,1,6,1,2,255,4,2,8,1,4,1,8, 1,12,1,10,1,12,1,16,1,8,1,8,1,8,1,24, 1,8,1,12,1,6,2,8,1,8,1,8,1,8,1,14, @@ -473,7 +473,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 112,116,95,108,101,118,101,108,90,13,98,97,115,101,95,102, 105,108,101,110,97,109,101,114,3,0,0,0,114,3,0,0, 0,114,6,0,0,0,218,17,115,111,117,114,99,101,95,102, - 114,111,109,95,99,97,99,104,101,106,1,0,0,115,52,0, + 114,111,109,95,99,97,99,104,101,108,1,0,0,115,52,0, 0,0,0,9,12,1,8,1,10,1,12,1,4,1,10,1, 12,1,14,1,16,1,4,1,4,1,12,1,8,1,18,2, 10,1,8,1,16,1,10,1,16,1,10,1,14,2,16,1, @@ -508,7 +508,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,90,9,101,120,116,101,110,115,105,111,110,218,11,115, 111,117,114,99,101,95,112,97,116,104,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,15,95,103,101,116,95, - 115,111,117,114,99,101,102,105,108,101,146,1,0,0,115,20, + 115,111,117,114,99,101,102,105,108,101,148,1,0,0,115,20, 0,0,0,0,7,12,1,4,1,16,1,24,1,4,1,2, 1,12,1,18,1,18,1,114,109,0,0,0,99,1,0,0, 0,0,0,0,0,0,0,0,0,1,0,0,0,8,0,0, @@ -522,7 +522,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 98,0,0,0,114,82,0,0,0,114,89,0,0,0,41,1, 114,97,0,0,0,114,3,0,0,0,114,3,0,0,0,114, 6,0,0,0,218,11,95,103,101,116,95,99,97,99,104,101, - 100,165,1,0,0,115,16,0,0,0,0,1,14,1,2,1, + 100,167,1,0,0,115,16,0,0,0,0,1,14,1,2,1, 10,1,14,1,8,1,14,1,4,2,114,113,0,0,0,99, 1,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, 8,0,0,0,67,0,0,0,115,52,0,0,0,122,14,116, @@ -536,7 +536,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,41,3,114,49,0,0,0,114,51,0,0,0,114,50, 0,0,0,41,2,114,44,0,0,0,114,52,0,0,0,114, 3,0,0,0,114,3,0,0,0,114,6,0,0,0,218,10, - 95,99,97,108,99,95,109,111,100,101,177,1,0,0,115,12, + 95,99,97,108,99,95,109,111,100,101,179,1,0,0,115,12, 0,0,0,0,2,2,1,14,1,14,1,10,3,8,1,114, 115,0,0,0,99,1,0,0,0,0,0,0,0,0,0,0, 0,3,0,0,0,8,0,0,0,3,0,0,0,115,68,0, @@ -574,7 +574,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,218,4,97,114,103,115,90,6,107,119,97,114,103, 115,169,1,218,6,109,101,116,104,111,100,114,3,0,0,0, 114,6,0,0,0,218,19,95,99,104,101,99,107,95,110,97, - 109,101,95,119,114,97,112,112,101,114,197,1,0,0,115,18, + 109,101,95,119,114,97,112,112,101,114,199,1,0,0,115,18, 0,0,0,0,1,8,1,8,1,10,1,4,1,8,255,2, 1,2,255,6,2,122,40,95,99,104,101,99,107,95,110,97, 109,101,46,60,108,111,99,97,108,115,62,46,95,99,104,101, @@ -592,7 +592,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,116,116,114,218,8,95,95,100,105,99,116,95,95,218,6, 117,112,100,97,116,101,41,3,90,3,110,101,119,90,3,111, 108,100,114,67,0,0,0,114,3,0,0,0,114,3,0,0, - 0,114,6,0,0,0,218,5,95,119,114,97,112,208,1,0, + 0,114,6,0,0,0,218,5,95,119,114,97,112,210,1,0, 0,115,8,0,0,0,0,1,8,1,10,1,20,1,122,26, 95,99,104,101,99,107,95,110,97,109,101,46,60,108,111,99, 97,108,115,62,46,95,119,114,97,112,41,1,78,41,3,218, @@ -600,7 +600,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 218,9,78,97,109,101,69,114,114,111,114,41,3,114,122,0, 0,0,114,123,0,0,0,114,133,0,0,0,114,3,0,0, 0,114,121,0,0,0,114,6,0,0,0,218,11,95,99,104, - 101,99,107,95,110,97,109,101,189,1,0,0,115,14,0,0, + 101,99,107,95,110,97,109,101,191,1,0,0,115,14,0,0, 0,0,8,14,7,2,1,10,1,14,2,14,5,10,1,114, 136,0,0,0,99,2,0,0,0,0,0,0,0,0,0,0, 0,5,0,0,0,6,0,0,0,67,0,0,0,115,60,0, @@ -628,7 +628,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 110,97,109,101,218,6,108,111,97,100,101,114,218,8,112,111, 114,116,105,111,110,115,218,3,109,115,103,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,17,95,102,105,110, - 100,95,109,111,100,117,108,101,95,115,104,105,109,217,1,0, + 100,95,109,111,100,117,108,101,95,115,104,105,109,219,1,0, 0,115,10,0,0,0,0,10,14,1,16,1,4,1,22,1, 114,143,0,0,0,99,3,0,0,0,0,0,0,0,0,0, 0,0,6,0,0,0,4,0,0,0,67,0,0,0,115,158, @@ -695,7 +695,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 95,100,101,116,97,105,108,115,90,5,109,97,103,105,99,114, 93,0,0,0,114,83,0,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,13,95,99,108,97,115,115, - 105,102,121,95,112,121,99,234,1,0,0,115,28,0,0,0, + 105,102,121,95,112,121,99,236,1,0,0,115,28,0,0,0, 0,16,12,1,8,1,16,1,12,1,12,1,12,1,10,1, 12,1,8,1,16,2,8,1,16,1,12,1,114,152,0,0, 0,99,5,0,0,0,0,0,0,0,0,0,0,0,6,0, @@ -749,7 +749,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 99,101,95,115,105,122,101,114,117,0,0,0,114,151,0,0, 0,114,93,0,0,0,114,3,0,0,0,114,3,0,0,0, 114,6,0,0,0,218,23,95,118,97,108,105,100,97,116,101, - 95,116,105,109,101,115,116,97,109,112,95,112,121,99,11,2, + 95,116,105,109,101,115,116,97,109,112,95,112,121,99,13,2, 0,0,115,16,0,0,0,0,19,24,1,10,1,12,1,12, 1,8,1,22,255,2,2,114,156,0,0,0,99,4,0,0, 0,0,0,0,0,0,0,0,0,4,0,0,0,3,0,0, @@ -795,7 +795,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,218,11,115,111,117,114,99,101,95,104,97,115,104, 114,117,0,0,0,114,151,0,0,0,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,18,95,118,97,108,105, - 100,97,116,101,95,104,97,115,104,95,112,121,99,39,2,0, + 100,97,116,101,95,104,97,115,104,95,112,121,99,41,2,0, 0,115,12,0,0,0,0,17,16,1,2,1,8,255,2,2, 2,254,114,158,0,0,0,99,4,0,0,0,0,0,0,0, 0,0,0,0,5,0,0,0,5,0,0,0,67,0,0,0, @@ -819,7 +819,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 5,114,26,0,0,0,114,117,0,0,0,114,107,0,0,0, 114,108,0,0,0,218,4,99,111,100,101,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,17,95,99,111,109, - 112,105,108,101,95,98,121,116,101,99,111,100,101,63,2,0, + 112,105,108,101,95,98,121,116,101,99,111,100,101,65,2,0, 0,115,20,0,0,0,0,2,10,1,10,1,12,1,8,1, 12,1,4,2,10,1,2,0,2,255,114,165,0,0,0,114, 73,0,0,0,99,3,0,0,0,0,0,0,0,0,0,0, @@ -838,7 +838,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 109,116,105,109,101,114,155,0,0,0,114,26,0,0,0,114, 3,0,0,0,114,3,0,0,0,114,6,0,0,0,218,22, 95,99,111,100,101,95,116,111,95,116,105,109,101,115,116,97, - 109,112,95,112,121,99,76,2,0,0,115,12,0,0,0,0, + 109,112,95,112,121,99,78,2,0,0,115,12,0,0,0,0, 2,8,1,14,1,14,1,14,1,16,1,114,170,0,0,0, 84,99,3,0,0,0,0,0,0,0,0,0,0,0,5,0, 0,0,5,0,0,0,67,0,0,0,115,80,0,0,0,116, @@ -856,7 +856,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 164,0,0,0,114,157,0,0,0,90,7,99,104,101,99,107, 101,100,114,26,0,0,0,114,83,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,17,95,99,111, - 100,101,95,116,111,95,104,97,115,104,95,112,121,99,86,2, + 100,101,95,116,111,95,104,97,115,104,95,112,121,99,88,2, 0,0,115,14,0,0,0,0,2,8,1,12,1,14,1,16, 1,10,1,16,1,114,171,0,0,0,99,1,0,0,0,0, 0,0,0,0,0,0,0,5,0,0,0,6,0,0,0,67, @@ -884,7 +884,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 101,110,99,111,100,105,110,103,90,15,110,101,119,108,105,110, 101,95,100,101,99,111,100,101,114,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,13,100,101,99,111,100,101, - 95,115,111,117,114,99,101,97,2,0,0,115,10,0,0,0, + 95,115,111,117,114,99,101,99,2,0,0,115,10,0,0,0, 0,5,8,1,12,1,10,1,12,1,114,176,0,0,0,169, 2,114,140,0,0,0,218,26,115,117,98,109,111,100,117,108, 101,95,115,101,97,114,99,104,95,108,111,99,97,116,105,111, @@ -946,7 +946,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,90,7,100,105,114,110,97,109,101,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,23,115,112,101,99,95, 102,114,111,109,95,102,105,108,101,95,108,111,99,97,116,105, - 111,110,114,2,0,0,115,62,0,0,0,0,12,8,4,4, + 111,110,116,2,0,0,115,62,0,0,0,0,12,8,4,4, 1,10,2,2,1,14,1,14,1,8,2,10,8,16,1,6, 3,8,1,14,1,14,1,10,1,6,1,6,2,4,3,8, 2,10,1,2,1,14,1,14,1,6,2,4,1,8,2,6, @@ -983,7 +983,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 75,69,89,95,76,79,67,65,76,95,77,65,67,72,73,78, 69,41,2,218,3,99,108,115,114,5,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,218,14,95,111, - 112,101,110,95,114,101,103,105,115,116,114,121,194,2,0,0, + 112,101,110,95,114,101,103,105,115,116,114,121,196,2,0,0, 115,8,0,0,0,0,2,2,1,16,1,14,1,122,36,87, 105,110,100,111,119,115,82,101,103,105,115,116,114,121,70,105, 110,100,101,114,46,95,111,112,101,110,95,114,101,103,105,115, @@ -1009,7 +1009,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 105,115,116,114,121,95,107,101,121,114,5,0,0,0,90,4, 104,107,101,121,218,8,102,105,108,101,112,97,116,104,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,218,16,95, - 115,101,97,114,99,104,95,114,101,103,105,115,116,114,121,201, + 115,101,97,114,99,104,95,114,101,103,105,115,116,114,121,203, 2,0,0,115,24,0,0,0,0,2,6,1,8,2,6,1, 6,1,16,255,6,2,2,1,12,1,26,1,14,1,8,1, 122,38,87,105,110,100,111,119,115,82,101,103,105,115,116,114, @@ -1032,7 +1032,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,218,6,116,97,114,103,101,116,114,199,0,0,0,114,140, 0,0,0,114,189,0,0,0,114,187,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,218,9,102,105, - 110,100,95,115,112,101,99,216,2,0,0,115,28,0,0,0, + 110,100,95,115,112,101,99,218,2,0,0,115,28,0,0,0, 0,2,10,1,8,1,4,1,2,1,12,1,14,1,8,1, 14,1,14,1,6,1,8,1,2,254,6,3,122,31,87,105, 110,100,111,119,115,82,101,103,105,115,116,114,121,70,105,110, @@ -1051,7 +1051,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,203,0,0,0,114,140,0,0,0,169,4,114,193,0,0, 0,114,139,0,0,0,114,44,0,0,0,114,187,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 11,102,105,110,100,95,109,111,100,117,108,101,232,2,0,0, + 11,102,105,110,100,95,109,111,100,117,108,101,234,2,0,0, 115,8,0,0,0,0,7,12,1,8,1,6,2,122,33,87, 105,110,100,111,119,115,82,101,103,105,115,116,114,121,70,105, 110,100,101,114,46,102,105,110,100,95,109,111,100,117,108,101, @@ -1061,7 +1061,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,115,115,109,101,116,104,111,100,114,194,0,0,0,114,200, 0,0,0,114,203,0,0,0,114,206,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,191,0,0,0,182,2,0,0,115,28,0,0,0,8, + 0,114,191,0,0,0,184,2,0,0,115,28,0,0,0,8, 2,4,3,2,255,2,4,2,255,2,3,4,2,2,1,10, 6,2,1,10,14,2,1,12,15,2,1,114,191,0,0,0, 99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -1097,7 +1097,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,97,0,0,0,90,13,102,105,108,101,110,97, 109,101,95,98,97,115,101,90,9,116,97,105,108,95,110,97, 109,101,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,182,0,0,0,251,2,0,0,115,8,0,0,0,0, + 0,114,182,0,0,0,253,2,0,0,115,8,0,0,0,0, 3,18,1,16,1,14,1,122,24,95,76,111,97,100,101,114, 66,97,115,105,99,115,46,105,115,95,112,97,99,107,97,103, 101,99,2,0,0,0,0,0,0,0,0,0,0,0,2,0, @@ -1108,7 +1108,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 46,78,114,3,0,0,0,169,2,114,119,0,0,0,114,187, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, 0,0,218,13,99,114,101,97,116,101,95,109,111,100,117,108, - 101,3,3,0,0,115,2,0,0,0,0,1,122,27,95,76, + 101,5,3,0,0,115,2,0,0,0,0,1,122,27,95,76, 111,97,100,101,114,66,97,115,105,99,115,46,99,114,101,97, 116,101,95,109,111,100,117,108,101,99,2,0,0,0,0,0, 0,0,0,0,0,0,3,0,0,0,5,0,0,0,67,0, @@ -1128,7 +1128,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,41,3,114,119,0,0,0,218,6,109,111,100,117, 108,101,114,164,0,0,0,114,3,0,0,0,114,3,0,0, 0,114,6,0,0,0,218,11,101,120,101,99,95,109,111,100, - 117,108,101,6,3,0,0,115,12,0,0,0,0,2,12,1, + 117,108,101,8,3,0,0,115,12,0,0,0,0,2,12,1, 8,1,6,1,4,255,6,2,122,25,95,76,111,97,100,101, 114,66,97,115,105,99,115,46,101,120,101,99,95,109,111,100, 117,108,101,99,2,0,0,0,0,0,0,0,0,0,0,0, @@ -1139,14 +1139,14 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,218,17,95,108,111,97,100,95,109,111,100,117,108,101, 95,115,104,105,109,169,2,114,119,0,0,0,114,139,0,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 218,11,108,111,97,100,95,109,111,100,117,108,101,14,3,0, + 218,11,108,111,97,100,95,109,111,100,117,108,101,16,3,0, 0,115,2,0,0,0,0,2,122,25,95,76,111,97,100,101, 114,66,97,115,105,99,115,46,108,111,97,100,95,109,111,100, 117,108,101,78,41,8,114,125,0,0,0,114,124,0,0,0, 114,126,0,0,0,114,127,0,0,0,114,182,0,0,0,114, 212,0,0,0,114,217,0,0,0,114,220,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,114,208,0,0,0,246,2,0,0,115,10,0,0,0, + 0,0,114,208,0,0,0,248,2,0,0,115,10,0,0,0, 8,2,4,3,8,8,8,3,8,8,114,208,0,0,0,99, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 3,0,0,0,64,0,0,0,115,74,0,0,0,101,0,90, @@ -1171,7 +1171,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 32,32,32,32,32,32,78,41,1,114,50,0,0,0,169,2, 114,119,0,0,0,114,44,0,0,0,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,10,112,97,116,104,95, - 109,116,105,109,101,21,3,0,0,115,2,0,0,0,0,6, + 109,116,105,109,101,23,3,0,0,115,2,0,0,0,0,6, 122,23,83,111,117,114,99,101,76,111,97,100,101,114,46,112, 97,116,104,95,109,116,105,109,101,99,2,0,0,0,0,0, 0,0,0,0,0,0,2,0,0,0,4,0,0,0,67,0, @@ -1205,7 +1205,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 46,10,32,32,32,32,32,32,32,32,114,169,0,0,0,41, 1,114,223,0,0,0,114,222,0,0,0,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,10,112,97,116,104, - 95,115,116,97,116,115,29,3,0,0,115,2,0,0,0,0, + 95,115,116,97,116,115,31,3,0,0,115,2,0,0,0,0, 12,122,23,83,111,117,114,99,101,76,111,97,100,101,114,46, 112,97,116,104,95,115,116,97,116,115,99,4,0,0,0,0, 0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,67, @@ -1229,7 +1229,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 108,0,0,0,90,10,99,97,99,104,101,95,112,97,116,104, 114,26,0,0,0,114,3,0,0,0,114,3,0,0,0,114, 6,0,0,0,218,15,95,99,97,99,104,101,95,98,121,116, - 101,99,111,100,101,43,3,0,0,115,2,0,0,0,0,8, + 101,99,111,100,101,45,3,0,0,115,2,0,0,0,0,8, 122,28,83,111,117,114,99,101,76,111,97,100,101,114,46,95, 99,97,99,104,101,95,98,121,116,101,99,111,100,101,99,3, 0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,1, @@ -1246,7 +1246,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 46,10,32,32,32,32,32,32,32,32,78,114,3,0,0,0, 41,3,114,119,0,0,0,114,44,0,0,0,114,26,0,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,225,0,0,0,53,3,0,0,115,2,0,0,0,0,1, + 114,225,0,0,0,55,3,0,0,115,2,0,0,0,0,1, 122,21,83,111,117,114,99,101,76,111,97,100,101,114,46,115, 101,116,95,100,97,116,97,99,2,0,0,0,0,0,0,0, 0,0,0,0,5,0,0,0,10,0,0,0,67,0,0,0, @@ -1267,7 +1267,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,119,0,0,0,114,139,0,0,0,114,44,0,0,0,114, 174,0,0,0,218,3,101,120,99,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,10,103,101,116,95,115,111, - 117,114,99,101,60,3,0,0,115,20,0,0,0,0,2,10, + 117,114,99,101,62,3,0,0,115,20,0,0,0,0,2,10, 1,2,1,14,1,16,1,4,1,2,255,4,1,2,255,20, 2,122,23,83,111,117,114,99,101,76,111,97,100,101,114,46, 103,101,116,95,115,111,117,114,99,101,114,105,0,0,0,41, @@ -1289,7 +1289,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 111,109,112,105,108,101,41,4,114,119,0,0,0,114,26,0, 0,0,114,44,0,0,0,114,230,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,14,115,111,117, - 114,99,101,95,116,111,95,99,111,100,101,70,3,0,0,115, + 114,99,101,95,116,111,95,99,111,100,101,72,3,0,0,115, 8,0,0,0,0,5,12,1,2,0,2,255,122,27,83,111, 117,114,99,101,76,111,97,100,101,114,46,115,111,117,114,99, 101,95,116,111,95,99,111,100,101,99,2,0,0,0,0,0, @@ -1367,7 +1367,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,83,0,0,0,90,10,98,121,116,101,115,95, 100,97,116,97,90,11,99,111,100,101,95,111,98,106,101,99, 116,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,213,0,0,0,78,3,0,0,115,152,0,0,0,0,7, + 114,213,0,0,0,80,3,0,0,115,152,0,0,0,0,7, 10,1,4,1,4,1,4,1,4,1,4,1,2,1,12,1, 14,1,12,2,2,1,14,1,14,1,8,2,12,1,2,1, 14,1,14,1,6,3,2,1,2,254,6,4,2,1,12,1, @@ -1383,7 +1383,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 223,0,0,0,114,224,0,0,0,114,226,0,0,0,114,225, 0,0,0,114,229,0,0,0,114,233,0,0,0,114,213,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,3,0,0, - 0,114,6,0,0,0,114,221,0,0,0,19,3,0,0,115, + 0,114,6,0,0,0,114,221,0,0,0,21,3,0,0,115, 14,0,0,0,8,2,8,8,8,14,8,10,8,7,8,10, 14,8,114,221,0,0,0,99,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0, @@ -1413,7 +1413,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 110,100,101,114,46,78,114,159,0,0,0,41,3,114,119,0, 0,0,114,139,0,0,0,114,44,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,114,209,0,0,0, - 168,3,0,0,115,4,0,0,0,0,3,6,1,122,19,70, + 170,3,0,0,115,4,0,0,0,0,3,6,1,122,19,70, 105,108,101,76,111,97,100,101,114,46,95,95,105,110,105,116, 95,95,99,2,0,0,0,0,0,0,0,0,0,0,0,2, 0,0,0,2,0,0,0,67,0,0,0,115,24,0,0,0, @@ -1422,7 +1422,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 9,95,95,99,108,97,115,115,95,95,114,131,0,0,0,169, 2,114,119,0,0,0,90,5,111,116,104,101,114,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,218,6,95,95, - 101,113,95,95,174,3,0,0,115,6,0,0,0,0,1,12, + 101,113,95,95,176,3,0,0,115,6,0,0,0,0,1,12, 1,10,255,122,17,70,105,108,101,76,111,97,100,101,114,46, 95,95,101,113,95,95,99,1,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,0,3,0,0,0,67,0,0,0,115, @@ -1430,7 +1430,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 106,2,131,1,65,0,83,0,114,110,0,0,0,169,3,218, 4,104,97,115,104,114,117,0,0,0,114,44,0,0,0,169, 1,114,119,0,0,0,114,3,0,0,0,114,3,0,0,0, - 114,6,0,0,0,218,8,95,95,104,97,115,104,95,95,178, + 114,6,0,0,0,218,8,95,95,104,97,115,104,95,95,180, 3,0,0,115,2,0,0,0,0,1,122,19,70,105,108,101, 76,111,97,100,101,114,46,95,95,104,97,115,104,95,95,99, 2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, @@ -1445,7 +1445,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 32,32,32,32,32,41,3,218,5,115,117,112,101,114,114,239, 0,0,0,114,220,0,0,0,114,219,0,0,0,169,1,114, 241,0,0,0,114,3,0,0,0,114,6,0,0,0,114,220, - 0,0,0,181,3,0,0,115,2,0,0,0,0,10,122,22, + 0,0,0,183,3,0,0,115,2,0,0,0,0,10,122,22, 70,105,108,101,76,111,97,100,101,114,46,108,111,97,100,95, 109,111,100,117,108,101,99,2,0,0,0,0,0,0,0,0, 0,0,0,2,0,0,0,1,0,0,0,67,0,0,0,115, @@ -1455,7 +1455,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 32,97,115,32,102,111,117,110,100,32,98,121,32,116,104,101, 32,102,105,110,100,101,114,46,114,48,0,0,0,114,219,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,179,0,0,0,193,3,0,0,115,2,0,0,0,0, + 0,114,179,0,0,0,195,3,0,0,115,2,0,0,0,0, 3,122,23,70,105,108,101,76,111,97,100,101,114,46,103,101, 116,95,102,105,108,101,110,97,109,101,99,2,0,0,0,0, 0,0,0,0,0,0,0,3,0,0,0,10,0,0,0,67, @@ -1475,7 +1475,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,90,4,114,101,97,100,114,65,0,0,0,41,3, 114,119,0,0,0,114,44,0,0,0,114,68,0,0,0,114, 3,0,0,0,114,3,0,0,0,114,6,0,0,0,114,227, - 0,0,0,198,3,0,0,115,10,0,0,0,0,2,14,1, + 0,0,0,200,3,0,0,115,10,0,0,0,0,2,14,1, 16,1,28,2,14,1,122,19,70,105,108,101,76,111,97,100, 101,114,46,103,101,116,95,100,97,116,97,99,2,0,0,0, 0,0,0,0,0,0,0,0,2,0,0,0,3,0,0,0, @@ -1484,7 +1484,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,1,114,182,0,0,0,169,2,114,119,0,0,0,114,216, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, 0,0,218,19,103,101,116,95,114,101,115,111,117,114,99,101, - 95,114,101,97,100,101,114,209,3,0,0,115,6,0,0,0, + 95,114,101,97,100,101,114,211,3,0,0,115,6,0,0,0, 0,2,10,1,4,1,122,30,70,105,108,101,76,111,97,100, 101,114,46,103,101,116,95,114,101,115,111,117,114,99,101,95, 114,101,97,100,101,114,99,2,0,0,0,0,0,0,0,0, @@ -1497,7 +1497,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,90,8,114,101,115,111,117,114,99,101,114,44,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, 0,218,13,111,112,101,110,95,114,101,115,111,117,114,99,101, - 215,3,0,0,115,4,0,0,0,0,1,20,1,122,24,70, + 217,3,0,0,115,4,0,0,0,0,1,20,1,122,24,70, 105,108,101,76,111,97,100,101,114,46,111,112,101,110,95,114, 101,115,111,117,114,99,101,99,2,0,0,0,0,0,0,0, 0,0,0,0,3,0,0,0,3,0,0,0,67,0,0,0, @@ -1509,7 +1509,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 69,114,114,111,114,114,38,0,0,0,114,47,0,0,0,114, 44,0,0,0,114,255,0,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,13,114,101,115,111,117,114, - 99,101,95,112,97,116,104,219,3,0,0,115,8,0,0,0, + 99,101,95,112,97,116,104,221,3,0,0,115,8,0,0,0, 0,1,10,1,4,1,20,1,122,24,70,105,108,101,76,111, 97,100,101,114,46,114,101,115,111,117,114,99,101,95,112,97, 116,104,99,2,0,0,0,0,0,0,0,0,0,0,0,3, @@ -1521,7 +1521,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,44,0,0,0,114,54,0,0,0,169,3,114,119, 0,0,0,114,117,0,0,0,114,44,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,114,2,1,0, - 0,225,3,0,0,115,8,0,0,0,0,1,8,1,4,1, + 0,227,3,0,0,115,8,0,0,0,0,1,8,1,4,1, 20,1,122,22,70,105,108,101,76,111,97,100,101,114,46,105, 115,95,114,101,115,111,117,114,99,101,99,1,0,0,0,0, 0,0,0,0,0,0,0,1,0,0,0,5,0,0,0,67, @@ -1531,7 +1531,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,218,7,108,105,115,116,100,105,114,114,47,0,0,0, 114,44,0,0,0,114,246,0,0,0,114,3,0,0,0,114, 3,0,0,0,114,6,0,0,0,218,8,99,111,110,116,101, - 110,116,115,231,3,0,0,115,2,0,0,0,0,1,122,19, + 110,116,115,233,3,0,0,115,2,0,0,0,0,1,122,19, 70,105,108,101,76,111,97,100,101,114,46,99,111,110,116,101, 110,116,115,41,17,114,125,0,0,0,114,124,0,0,0,114, 126,0,0,0,114,127,0,0,0,114,209,0,0,0,114,243, @@ -1540,7 +1540,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,0,1,0,0,114,4,1,0,0,114,2,1,0,0, 114,8,1,0,0,90,13,95,95,99,108,97,115,115,99,101, 108,108,95,95,114,3,0,0,0,114,3,0,0,0,114,249, - 0,0,0,114,6,0,0,0,114,239,0,0,0,163,3,0, + 0,0,0,114,6,0,0,0,114,239,0,0,0,165,3,0, 0,115,30,0,0,0,8,2,4,3,8,6,8,4,8,3, 2,1,14,11,2,1,10,4,8,11,2,1,10,5,8,4, 8,6,8,6,114,239,0,0,0,99,0,0,0,0,0,0, @@ -1563,7 +1563,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,49,0,0,0,218,8,115,116,95,109,116,105,109,101,90, 7,115,116,95,115,105,122,101,41,3,114,119,0,0,0,114, 44,0,0,0,114,238,0,0,0,114,3,0,0,0,114,3, - 0,0,0,114,6,0,0,0,114,224,0,0,0,239,3,0, + 0,0,0,114,6,0,0,0,114,224,0,0,0,241,3,0, 0,115,4,0,0,0,0,2,8,1,122,27,83,111,117,114, 99,101,70,105,108,101,76,111,97,100,101,114,46,112,97,116, 104,95,115,116,97,116,115,99,4,0,0,0,0,0,0,0, @@ -1574,7 +1574,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,225,0,0,0,41,5,114,119,0,0,0,114,108,0,0, 0,114,107,0,0,0,114,26,0,0,0,114,52,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 226,0,0,0,244,3,0,0,115,4,0,0,0,0,2,8, + 226,0,0,0,246,3,0,0,115,4,0,0,0,0,2,8, 1,122,32,83,111,117,114,99,101,70,105,108,101,76,111,97, 100,101,114,46,95,99,97,99,104,101,95,98,121,116,101,99, 111,100,101,114,60,0,0,0,114,11,1,0,0,99,3,0, @@ -1609,7 +1609,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 12,1,0,0,218,6,112,97,114,101,110,116,114,97,0,0, 0,114,37,0,0,0,114,33,0,0,0,114,228,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 225,0,0,0,249,3,0,0,115,48,0,0,0,0,2,12, + 225,0,0,0,251,3,0,0,115,48,0,0,0,0,2,12, 1,4,2,12,1,12,1,12,2,12,1,10,1,2,1,14, 1,14,2,8,1,16,3,6,1,2,0,2,255,4,2,28, 1,2,1,12,1,16,1,16,2,8,1,2,255,122,25,83, @@ -1618,7 +1618,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,124,0,0,0,114,126,0,0,0,114,127,0,0,0,114, 224,0,0,0,114,226,0,0,0,114,225,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,114,9,1,0,0,235,3,0,0,115,8,0,0,0, + 0,0,114,9,1,0,0,237,3,0,0,115,8,0,0,0, 8,2,4,2,8,5,8,5,114,9,1,0,0,99,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, 0,0,64,0,0,0,115,32,0,0,0,101,0,90,1,100, @@ -1640,7 +1640,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,235,0,0,0,41,5,114,119,0,0,0,114, 139,0,0,0,114,44,0,0,0,114,26,0,0,0,114,151, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,114,213,0,0,0,28,4,0,0,115,22,0,0,0, + 0,0,114,213,0,0,0,30,4,0,0,115,22,0,0,0, 0,1,10,1,10,4,2,1,2,254,6,4,12,1,2,1, 14,1,2,1,2,253,122,29,83,111,117,114,99,101,108,101, 115,115,70,105,108,101,76,111,97,100,101,114,46,103,101,116, @@ -1651,13 +1651,13 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 105,115,32,110,111,32,115,111,117,114,99,101,32,99,111,100, 101,46,78,114,3,0,0,0,114,219,0,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,114,229,0,0, - 0,44,4,0,0,115,2,0,0,0,0,2,122,31,83,111, + 0,46,4,0,0,115,2,0,0,0,0,2,122,31,83,111, 117,114,99,101,108,101,115,115,70,105,108,101,76,111,97,100, 101,114,46,103,101,116,95,115,111,117,114,99,101,78,41,6, 114,125,0,0,0,114,124,0,0,0,114,126,0,0,0,114, 127,0,0,0,114,213,0,0,0,114,229,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,114,15,1,0,0,24,4,0,0,115,6,0,0,0, + 0,0,114,15,1,0,0,26,4,0,0,115,6,0,0,0, 8,2,4,2,8,16,114,15,1,0,0,99,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0, 64,0,0,0,115,92,0,0,0,101,0,90,1,100,0,90, @@ -1677,7 +1677,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 67,0,0,0,115,16,0,0,0,124,1,124,0,95,0,124, 2,124,0,95,1,100,0,83,0,114,110,0,0,0,114,159, 0,0,0,114,5,1,0,0,114,3,0,0,0,114,3,0, - 0,0,114,6,0,0,0,114,209,0,0,0,61,4,0,0, + 0,0,114,6,0,0,0,114,209,0,0,0,63,4,0,0, 115,4,0,0,0,0,1,6,1,122,28,69,120,116,101,110, 115,105,111,110,70,105,108,101,76,111,97,100,101,114,46,95, 95,105,110,105,116,95,95,99,2,0,0,0,0,0,0,0, @@ -1686,7 +1686,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 22,124,0,106,1,124,1,106,1,107,2,83,0,114,110,0, 0,0,114,240,0,0,0,114,242,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,114,243,0,0,0, - 65,4,0,0,115,6,0,0,0,0,1,12,1,10,255,122, + 67,4,0,0,115,6,0,0,0,0,1,12,1,10,255,122, 26,69,120,116,101,110,115,105,111,110,70,105,108,101,76,111, 97,100,101,114,46,95,95,101,113,95,95,99,1,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0, @@ -1694,7 +1694,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 1,116,0,124,0,106,2,131,1,65,0,83,0,114,110,0, 0,0,114,244,0,0,0,114,246,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,114,247,0,0,0, - 69,4,0,0,115,2,0,0,0,0,1,122,28,69,120,116, + 71,4,0,0,115,2,0,0,0,0,1,122,28,69,120,116, 101,110,115,105,111,110,70,105,108,101,76,111,97,100,101,114, 46,95,95,104,97,115,104,95,95,99,2,0,0,0,0,0, 0,0,0,0,0,0,3,0,0,0,5,0,0,0,67,0, @@ -1711,7 +1711,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,117,0,0,0,114,44,0,0,0,41,3,114, 119,0,0,0,114,187,0,0,0,114,216,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,114,212,0, - 0,0,72,4,0,0,115,18,0,0,0,0,2,4,1,4, + 0,0,74,4,0,0,115,18,0,0,0,0,2,4,1,4, 0,2,255,4,2,6,1,4,0,4,255,4,2,122,33,69, 120,116,101,110,115,105,111,110,70,105,108,101,76,111,97,100, 101,114,46,99,114,101,97,116,101,95,109,111,100,117,108,101, @@ -1728,7 +1728,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 90,12,101,120,101,99,95,100,121,110,97,109,105,99,114,149, 0,0,0,114,117,0,0,0,114,44,0,0,0,114,253,0, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,217,0,0,0,80,4,0,0,115,10,0,0,0,0, + 0,114,217,0,0,0,82,4,0,0,115,10,0,0,0,0, 2,14,1,6,1,4,0,4,255,122,31,69,120,116,101,110, 115,105,111,110,70,105,108,101,76,111,97,100,101,114,46,101, 120,101,99,95,109,111,100,117,108,101,99,2,0,0,0,0, @@ -1746,7 +1746,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,209,0,0,0,78,114,3,0,0,0,169,2,114,32,0, 0,0,218,6,115,117,102,102,105,120,169,1,90,9,102,105, 108,101,95,110,97,109,101,114,3,0,0,0,114,6,0,0, - 0,218,9,60,103,101,110,101,120,112,114,62,89,4,0,0, + 0,218,9,60,103,101,110,101,120,112,114,62,91,4,0,0, 115,4,0,0,0,4,1,2,255,122,49,69,120,116,101,110, 115,105,111,110,70,105,108,101,76,111,97,100,101,114,46,105, 115,95,112,97,99,107,97,103,101,46,60,108,111,99,97,108, @@ -1754,7 +1754,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,114,44,0,0,0,218,3,97,110,121,218,18,69, 88,84,69,78,83,73,79,78,95,83,85,70,70,73,88,69, 83,114,219,0,0,0,114,3,0,0,0,114,18,1,0,0, - 114,6,0,0,0,114,182,0,0,0,86,4,0,0,115,8, + 114,6,0,0,0,114,182,0,0,0,88,4,0,0,115,8, 0,0,0,0,2,14,1,12,1,2,255,122,30,69,120,116, 101,110,115,105,111,110,70,105,108,101,76,111,97,100,101,114, 46,105,115,95,112,97,99,107,97,103,101,99,2,0,0,0, @@ -1765,7 +1765,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 117,108,101,32,99,97,110,110,111,116,32,99,114,101,97,116, 101,32,97,32,99,111,100,101,32,111,98,106,101,99,116,46, 78,114,3,0,0,0,114,219,0,0,0,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,213,0,0,0,92, + 114,3,0,0,0,114,6,0,0,0,114,213,0,0,0,94, 4,0,0,115,2,0,0,0,0,2,122,28,69,120,116,101, 110,115,105,111,110,70,105,108,101,76,111,97,100,101,114,46, 103,101,116,95,99,111,100,101,99,2,0,0,0,0,0,0, @@ -1776,14 +1776,14 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,118,101,32,110,111,32,115,111,117,114,99,101,32,99,111, 100,101,46,78,114,3,0,0,0,114,219,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,114,229,0, - 0,0,96,4,0,0,115,2,0,0,0,0,2,122,30,69, + 0,0,98,4,0,0,115,2,0,0,0,0,2,122,30,69, 120,116,101,110,115,105,111,110,70,105,108,101,76,111,97,100, 101,114,46,103,101,116,95,115,111,117,114,99,101,99,2,0, 0,0,0,0,0,0,0,0,0,0,2,0,0,0,1,0, 0,0,67,0,0,0,115,6,0,0,0,124,0,106,0,83, 0,114,250,0,0,0,114,48,0,0,0,114,219,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 179,0,0,0,100,4,0,0,115,2,0,0,0,0,3,122, + 179,0,0,0,102,4,0,0,115,2,0,0,0,0,3,122, 32,69,120,116,101,110,115,105,111,110,70,105,108,101,76,111, 97,100,101,114,46,103,101,116,95,102,105,108,101,110,97,109, 101,78,41,14,114,125,0,0,0,114,124,0,0,0,114,126, @@ -1792,7 +1792,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,182,0,0,0,114,213,0,0,0,114,229,0,0,0, 114,136,0,0,0,114,179,0,0,0,114,3,0,0,0,114, 3,0,0,0,114,3,0,0,0,114,6,0,0,0,114,252, - 0,0,0,53,4,0,0,115,22,0,0,0,8,2,4,6, + 0,0,0,55,4,0,0,115,22,0,0,0,8,2,4,6, 8,4,8,4,8,3,8,8,8,6,8,6,8,4,8,4, 2,1,114,252,0,0,0,99,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,2,0,0,0,64,0,0,0, @@ -1834,7 +1834,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 116,104,95,102,105,110,100,101,114,169,4,114,119,0,0,0, 114,117,0,0,0,114,44,0,0,0,90,11,112,97,116,104, 95,102,105,110,100,101,114,114,3,0,0,0,114,3,0,0, - 0,114,6,0,0,0,114,209,0,0,0,113,4,0,0,115, + 0,114,6,0,0,0,114,209,0,0,0,115,4,0,0,115, 8,0,0,0,0,1,6,1,6,1,14,1,122,23,95,78, 97,109,101,115,112,97,99,101,80,97,116,104,46,95,95,105, 110,105,116,95,95,99,1,0,0,0,0,0,0,0,0,0, @@ -1852,7 +1852,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 1,0,0,218,3,100,111,116,90,2,109,101,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,23,95,102,105, 110,100,95,112,97,114,101,110,116,95,112,97,116,104,95,110, - 97,109,101,115,119,4,0,0,115,8,0,0,0,0,2,18, + 97,109,101,115,121,4,0,0,115,8,0,0,0,0,2,18, 1,8,2,4,3,122,38,95,78,97,109,101,115,112,97,99, 101,80,97,116,104,46,95,102,105,110,100,95,112,97,114,101, 110,116,95,112,97,116,104,95,110,97,109,101,115,99,1,0, @@ -1865,7 +1865,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,114,101,110,116,95,109,111,100,117,108,101,95,110,97,109, 101,90,14,112,97,116,104,95,97,116,116,114,95,110,97,109, 101,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,25,1,0,0,129,4,0,0,115,4,0,0,0,0,1, + 114,25,1,0,0,131,4,0,0,115,4,0,0,0,0,1, 12,1,122,31,95,78,97,109,101,115,112,97,99,101,80,97, 116,104,46,95,103,101,116,95,112,97,114,101,110,116,95,112, 97,116,104,99,1,0,0,0,0,0,0,0,0,0,0,0, @@ -1881,7 +1881,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,3,114,119,0,0,0,90,11,112,97,114,101,110,116,95, 112,97,116,104,114,187,0,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,12,95,114,101,99,97,108, - 99,117,108,97,116,101,133,4,0,0,115,16,0,0,0,0, + 99,117,108,97,116,101,135,4,0,0,115,16,0,0,0,0, 2,12,1,10,1,14,3,18,1,6,1,8,1,6,1,122, 27,95,78,97,109,101,115,112,97,99,101,80,97,116,104,46, 95,114,101,99,97,108,99,117,108,97,116,101,99,1,0,0, @@ -1890,7 +1890,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 161,0,131,1,83,0,114,110,0,0,0,41,2,114,6,1, 0,0,114,32,1,0,0,114,246,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,8,95,95,105, - 116,101,114,95,95,146,4,0,0,115,2,0,0,0,0,1, + 116,101,114,95,95,148,4,0,0,115,2,0,0,0,0,1, 122,23,95,78,97,109,101,115,112,97,99,101,80,97,116,104, 46,95,95,105,116,101,114,95,95,99,2,0,0,0,0,0, 0,0,0,0,0,0,2,0,0,0,2,0,0,0,67,0, @@ -1898,7 +1898,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,83,0,114,110,0,0,0,169,1,114,32,1,0,0,41, 2,114,119,0,0,0,218,5,105,110,100,101,120,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,218,11,95,95, - 103,101,116,105,116,101,109,95,95,149,4,0,0,115,2,0, + 103,101,116,105,116,101,109,95,95,151,4,0,0,115,2,0, 0,0,0,1,122,26,95,78,97,109,101,115,112,97,99,101, 80,97,116,104,46,95,95,103,101,116,105,116,101,109,95,95, 99,3,0,0,0,0,0,0,0,0,0,0,0,3,0,0, @@ -1907,7 +1907,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,41,1,114,24,1,0,0,41,3,114,119,0,0,0,114, 35,1,0,0,114,44,0,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,11,95,95,115,101,116,105, - 116,101,109,95,95,152,4,0,0,115,2,0,0,0,0,1, + 116,101,109,95,95,154,4,0,0,115,2,0,0,0,0,1, 122,26,95,78,97,109,101,115,112,97,99,101,80,97,116,104, 46,95,95,115,101,116,105,116,101,109,95,95,99,1,0,0, 0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0, @@ -1915,7 +1915,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 161,0,131,1,83,0,114,110,0,0,0,41,2,114,22,0, 0,0,114,32,1,0,0,114,246,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,218,7,95,95,108, - 101,110,95,95,155,4,0,0,115,2,0,0,0,0,1,122, + 101,110,95,95,157,4,0,0,115,2,0,0,0,0,1,122, 22,95,78,97,109,101,115,112,97,99,101,80,97,116,104,46, 95,95,108,101,110,95,95,99,1,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,3,0,0,0,67,0,0,0, @@ -1924,7 +1924,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 80,97,116,104,40,123,33,114,125,41,41,2,114,62,0,0, 0,114,24,1,0,0,114,246,0,0,0,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,8,95,95,114,101, - 112,114,95,95,158,4,0,0,115,2,0,0,0,0,1,122, + 112,114,95,95,160,4,0,0,115,2,0,0,0,0,1,122, 23,95,78,97,109,101,115,112,97,99,101,80,97,116,104,46, 95,95,114,101,112,114,95,95,99,2,0,0,0,0,0,0, 0,0,0,0,0,2,0,0,0,3,0,0,0,67,0,0, @@ -1932,7 +1932,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 83,0,114,110,0,0,0,114,34,1,0,0,169,2,114,119, 0,0,0,218,4,105,116,101,109,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,12,95,95,99,111,110,116, - 97,105,110,115,95,95,161,4,0,0,115,2,0,0,0,0, + 97,105,110,115,95,95,163,4,0,0,115,2,0,0,0,0, 1,122,27,95,78,97,109,101,115,112,97,99,101,80,97,116, 104,46,95,95,99,111,110,116,97,105,110,115,95,95,99,2, 0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,3, @@ -1940,7 +1940,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 160,1,124,1,161,1,1,0,100,0,83,0,114,110,0,0, 0,41,2,114,24,1,0,0,114,186,0,0,0,114,40,1, 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, - 0,114,186,0,0,0,164,4,0,0,115,2,0,0,0,0, + 0,114,186,0,0,0,166,4,0,0,115,2,0,0,0,0, 1,122,21,95,78,97,109,101,115,112,97,99,101,80,97,116, 104,46,97,112,112,101,110,100,78,41,15,114,125,0,0,0, 114,124,0,0,0,114,126,0,0,0,114,127,0,0,0,114, @@ -1948,7 +1948,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 1,0,0,114,33,1,0,0,114,36,1,0,0,114,37,1, 0,0,114,38,1,0,0,114,39,1,0,0,114,42,1,0, 0,114,186,0,0,0,114,3,0,0,0,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,22,1,0,0,106, + 114,3,0,0,0,114,6,0,0,0,114,22,1,0,0,108, 4,0,0,115,24,0,0,0,8,1,4,6,8,6,8,10, 8,4,8,13,8,3,8,3,8,3,8,3,8,3,8,3, 114,22,1,0,0,99,0,0,0,0,0,0,0,0,0,0, @@ -1965,7 +1965,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,95,1,100,0,83,0,114,110,0,0,0,41,2,114,22, 1,0,0,114,24,1,0,0,114,28,1,0,0,114,3,0, 0,0,114,3,0,0,0,114,6,0,0,0,114,209,0,0, - 0,170,4,0,0,115,2,0,0,0,0,1,122,25,95,78, + 0,172,4,0,0,115,2,0,0,0,0,1,122,25,95,78, 97,109,101,115,112,97,99,101,76,111,97,100,101,114,46,95, 95,105,110,105,116,95,95,99,2,0,0,0,0,0,0,0, 0,0,0,0,2,0,0,0,3,0,0,0,67,0,0,0, @@ -1982,21 +1982,21 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 101,41,62,41,2,114,62,0,0,0,114,125,0,0,0,41, 2,114,193,0,0,0,114,216,0,0,0,114,3,0,0,0, 114,3,0,0,0,114,6,0,0,0,218,11,109,111,100,117, - 108,101,95,114,101,112,114,173,4,0,0,115,2,0,0,0, + 108,101,95,114,101,112,114,175,4,0,0,115,2,0,0,0, 0,7,122,28,95,78,97,109,101,115,112,97,99,101,76,111, 97,100,101,114,46,109,111,100,117,108,101,95,114,101,112,114, 99,2,0,0,0,0,0,0,0,0,0,0,0,2,0,0, 0,1,0,0,0,67,0,0,0,115,4,0,0,0,100,1, 83,0,41,2,78,84,114,3,0,0,0,114,219,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 182,0,0,0,182,4,0,0,115,2,0,0,0,0,1,122, + 182,0,0,0,184,4,0,0,115,2,0,0,0,0,1,122, 27,95,78,97,109,101,115,112,97,99,101,76,111,97,100,101, 114,46,105,115,95,112,97,99,107,97,103,101,99,2,0,0, 0,0,0,0,0,0,0,0,0,2,0,0,0,1,0,0, 0,67,0,0,0,115,4,0,0,0,100,1,83,0,41,2, 78,114,40,0,0,0,114,3,0,0,0,114,219,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 229,0,0,0,185,4,0,0,115,2,0,0,0,0,1,122, + 229,0,0,0,187,4,0,0,115,2,0,0,0,0,1,122, 27,95,78,97,109,101,115,112,97,99,101,76,111,97,100,101, 114,46,103,101,116,95,115,111,117,114,99,101,99,2,0,0, 0,0,0,0,0,0,0,0,0,2,0,0,0,6,0,0, @@ -2005,21 +2005,21 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,122,8,60,115,116,114,105,110,103,62,114,215,0,0, 0,84,41,1,114,231,0,0,0,41,1,114,232,0,0,0, 114,219,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,114,213,0,0,0,188,4,0,0,115,2,0, + 6,0,0,0,114,213,0,0,0,190,4,0,0,115,2,0, 0,0,0,1,122,25,95,78,97,109,101,115,112,97,99,101, 76,111,97,100,101,114,46,103,101,116,95,99,111,100,101,99, 2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, 1,0,0,0,67,0,0,0,115,4,0,0,0,100,1,83, 0,114,210,0,0,0,114,3,0,0,0,114,211,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 212,0,0,0,191,4,0,0,115,2,0,0,0,0,1,122, + 212,0,0,0,193,4,0,0,115,2,0,0,0,0,1,122, 30,95,78,97,109,101,115,112,97,99,101,76,111,97,100,101, 114,46,99,114,101,97,116,101,95,109,111,100,117,108,101,99, 2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, 1,0,0,0,67,0,0,0,115,4,0,0,0,100,0,83, 0,114,110,0,0,0,114,3,0,0,0,114,253,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 217,0,0,0,194,4,0,0,115,2,0,0,0,0,1,122, + 217,0,0,0,196,4,0,0,115,2,0,0,0,0,1,122, 28,95,78,97,109,101,115,112,97,99,101,76,111,97,100,101, 114,46,101,120,101,99,95,109,111,100,117,108,101,99,2,0, 0,0,0,0,0,0,0,0,0,0,2,0,0,0,4,0, @@ -2037,7 +2037,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 33,114,125,41,4,114,134,0,0,0,114,149,0,0,0,114, 24,1,0,0,114,218,0,0,0,114,219,0,0,0,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,114,220,0, - 0,0,197,4,0,0,115,8,0,0,0,0,7,6,1,4, + 0,0,199,4,0,0,115,8,0,0,0,0,7,6,1,4, 255,4,2,122,28,95,78,97,109,101,115,112,97,99,101,76, 111,97,100,101,114,46,108,111,97,100,95,109,111,100,117,108, 101,78,41,12,114,125,0,0,0,114,124,0,0,0,114,126, @@ -2045,7 +2045,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,182,0,0,0,114,229,0,0,0,114,213,0,0, 0,114,212,0,0,0,114,217,0,0,0,114,220,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,114,43,1,0,0,169,4,0,0,115,18,0, + 6,0,0,0,114,43,1,0,0,171,4,0,0,115,18,0, 0,0,8,1,8,3,2,1,10,8,8,3,8,3,8,3, 8,3,8,3,114,43,1,0,0,99,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,4,0,0,0,64,0, @@ -2085,7 +2085,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 95,99,97,99,104,101,218,5,105,116,101,109,115,114,128,0, 0,0,114,46,1,0,0,41,3,114,193,0,0,0,114,117, 0,0,0,218,6,102,105,110,100,101,114,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,46,1,0,0,215, + 114,3,0,0,0,114,6,0,0,0,114,46,1,0,0,217, 4,0,0,115,10,0,0,0,0,4,22,1,8,1,10,1, 10,1,122,28,80,97,116,104,70,105,110,100,101,114,46,105, 110,118,97,108,105,100,97,116,101,95,99,97,99,104,101,115, @@ -2106,7 +2106,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,118,0,0,0,41,3,114,193,0,0,0,114,44,0,0, 0,90,4,104,111,111,107,114,3,0,0,0,114,3,0,0, 0,114,6,0,0,0,218,11,95,112,97,116,104,95,104,111, - 111,107,115,225,4,0,0,115,16,0,0,0,0,3,16,1, + 111,107,115,227,4,0,0,115,16,0,0,0,0,3,16,1, 12,1,10,1,2,1,14,1,14,1,12,2,122,22,80,97, 116,104,70,105,110,100,101,114,46,95,112,97,116,104,95,104, 111,111,107,115,99,2,0,0,0,0,0,0,0,0,0,0, @@ -2137,7 +2137,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,3,114,193,0,0,0,114,44,0,0,0,114,50,1,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, 218,20,95,112,97,116,104,95,105,109,112,111,114,116,101,114, - 95,99,97,99,104,101,238,4,0,0,115,22,0,0,0,0, + 95,99,97,99,104,101,240,4,0,0,115,22,0,0,0,0, 8,8,1,2,1,12,1,14,3,8,1,2,1,14,1,14, 1,10,1,16,1,122,31,80,97,116,104,70,105,110,100,101, 114,46,95,112,97,116,104,95,105,109,112,111,114,116,101,114, @@ -2155,7 +2155,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 50,1,0,0,114,140,0,0,0,114,141,0,0,0,114,187, 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, 0,0,218,16,95,108,101,103,97,99,121,95,103,101,116,95, - 115,112,101,99,4,5,0,0,115,18,0,0,0,0,4,10, + 115,112,101,99,6,5,0,0,115,18,0,0,0,0,4,10, 1,16,2,10,1,4,1,8,1,12,1,12,1,6,1,122, 27,80,97,116,104,70,105,110,100,101,114,46,95,108,101,103, 97,99,121,95,103,101,116,95,115,112,101,99,78,99,4,0, @@ -2186,7 +2186,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 115,112,97,99,101,95,112,97,116,104,90,5,101,110,116,114, 121,114,50,1,0,0,114,187,0,0,0,114,141,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 9,95,103,101,116,95,115,112,101,99,19,5,0,0,115,40, + 9,95,103,101,116,95,115,112,101,99,21,5,0,0,115,40, 0,0,0,0,5,4,1,8,1,14,1,2,1,10,1,8, 1,10,1,14,2,12,1,8,1,2,1,10,1,8,1,6, 1,8,1,8,5,12,2,12,1,6,1,122,20,80,97,116, @@ -2213,7 +2213,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,181,0,0,0,114,22,1,0,0,41,6,114,193,0,0, 0,114,139,0,0,0,114,44,0,0,0,114,202,0,0,0, 114,187,0,0,0,114,57,1,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,203,0,0,0,51,5, + 3,0,0,0,114,6,0,0,0,114,203,0,0,0,53,5, 0,0,115,26,0,0,0,0,6,8,1,6,1,14,1,8, 1,4,1,10,1,6,1,4,3,6,1,16,1,4,2,6, 2,122,20,80,97,116,104,70,105,110,100,101,114,46,102,105, @@ -2234,7 +2234,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 110,115,116,101,97,100,46,10,10,32,32,32,32,32,32,32, 32,78,114,204,0,0,0,114,205,0,0,0,114,3,0,0, 0,114,3,0,0,0,114,6,0,0,0,114,206,0,0,0, - 75,5,0,0,115,8,0,0,0,0,8,12,1,8,1,4, + 77,5,0,0,115,8,0,0,0,0,8,12,1,8,1,4, 1,122,22,80,97,116,104,70,105,110,100,101,114,46,102,105, 110,100,95,109,111,100,117,108,101,122,45,40,63,58,123,112, 97,116,116,101,114,110,125,40,45,46,42,41,63,92,46,40, @@ -2276,7 +2276,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 112,97,116,116,101,114,110,90,5,102,111,117,110,100,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,218,18,102, 105,110,100,95,100,105,115,116,114,105,98,117,116,105,111,110, - 115,90,5,0,0,115,14,0,0,0,0,10,8,1,12,1, + 115,92,5,0,0,115,14,0,0,0,0,10,8,1,12,1, 8,1,6,1,22,1,12,1,122,29,80,97,116,104,70,105, 110,100,101,114,46,102,105,110,100,95,100,105,115,116,114,105, 98,117,116,105,111,110,115,99,3,0,0,0,0,0,0,0, @@ -2295,7 +2295,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 97,114,99,104,95,112,97,116,104,41,2,114,32,0,0,0, 114,44,0,0,0,169,2,114,193,0,0,0,114,63,1,0, 0,114,3,0,0,0,114,6,0,0,0,114,19,1,0,0, - 112,5,0,0,115,4,0,0,0,4,2,2,255,122,43,80, + 114,5,0,0,115,4,0,0,0,4,2,2,255,122,43,80, 97,116,104,70,105,110,100,101,114,46,95,115,101,97,114,99, 104,95,112,97,116,104,115,46,60,108,111,99,97,108,115,62, 46,60,103,101,110,101,120,112,114,62,41,5,218,9,105,116, @@ -2304,7 +2304,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,218,12,95,115,119,105,116,99,104,95,112,97,116,104, 41,4,114,193,0,0,0,114,63,1,0,0,90,5,112,97, 116,104,115,114,67,1,0,0,114,3,0,0,0,114,66,1, - 0,0,114,6,0,0,0,114,61,1,0,0,108,5,0,0, + 0,0,114,6,0,0,0,114,61,1,0,0,110,5,0,0, 115,8,0,0,0,0,3,8,1,18,2,10,254,122,24,80, 97,116,104,70,105,110,100,101,114,46,95,115,101,97,114,99, 104,95,112,97,116,104,115,99,1,0,0,0,0,0,0,0, @@ -2321,7 +2321,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 104,108,105,98,114,70,1,0,0,218,9,69,120,99,101,112, 116,105,111,110,41,4,114,44,0,0,0,114,69,1,0,0, 114,71,1,0,0,114,70,1,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,68,1,0,0,117,5, + 3,0,0,0,114,6,0,0,0,114,68,1,0,0,119,5, 0,0,115,12,0,0,0,0,2,12,1,8,1,12,1,10, 1,28,1,122,23,80,97,116,104,70,105,110,100,101,114,46, 95,115,119,105,116,99,104,95,112,97,116,104,99,4,0,0, @@ -2335,7 +2335,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 193,0,0,0,114,63,1,0,0,218,4,114,111,111,116,114, 41,1,0,0,114,60,1,0,0,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,10,95,112,114,101,100,105, - 99,97,116,101,126,5,0,0,115,4,0,0,0,0,2,8, + 99,97,116,101,128,5,0,0,115,4,0,0,0,0,2,8, 1,122,21,80,97,116,104,70,105,110,100,101,114,46,95,112, 114,101,100,105,99,97,116,101,99,3,0,0,0,0,0,0, 0,0,0,0,0,4,0,0,0,4,0,0,0,3,0,0, @@ -2352,7 +2352,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,1,114,74,1,0,0,41,2,114,32,0,0,0,114,41, 1,0,0,169,3,114,193,0,0,0,90,7,109,97,116,99, 104,101,114,114,73,1,0,0,114,3,0,0,0,114,6,0, - 0,0,114,19,1,0,0,137,5,0,0,115,6,0,0,0, + 0,0,114,19,1,0,0,139,5,0,0,115,6,0,0,0, 4,0,2,1,14,255,122,42,80,97,116,104,70,105,110,100, 101,114,46,95,115,101,97,114,99,104,95,112,97,116,104,46, 60,108,111,99,97,108,115,62,46,60,103,101,110,101,120,112, @@ -2362,7 +2362,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 41,4,114,193,0,0,0,114,73,1,0,0,114,63,1,0, 0,90,10,110,111,114,109,97,108,105,122,101,100,114,3,0, 0,0,114,76,1,0,0,114,6,0,0,0,114,65,1,0, - 0,131,5,0,0,115,10,0,0,0,0,2,8,1,4,1, + 0,133,5,0,0,115,10,0,0,0,0,2,8,1,4,1, 12,1,14,1,122,23,80,97,116,104,70,105,110,100,101,114, 46,95,115,101,97,114,99,104,95,112,97,116,104,41,1,78, 41,2,78,78,41,1,78,41,2,78,78,41,19,114,125,0, @@ -2373,7 +2373,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 1,0,0,114,61,1,0,0,218,12,115,116,97,116,105,99, 109,101,116,104,111,100,114,68,1,0,0,114,74,1,0,0, 114,65,1,0,0,114,3,0,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,45,1,0,0,211,4, + 3,0,0,0,114,6,0,0,0,114,45,1,0,0,213,4, 0,0,115,52,0,0,0,8,2,4,2,2,1,10,9,2, 1,10,12,2,1,10,21,2,1,10,14,2,1,12,31,2, 1,12,23,2,1,12,12,4,2,2,1,12,17,2,1,10, @@ -2419,7 +2419,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,124,0,93,14,125,1,124,1,136,0,102,2,86,0, 1,0,113,2,100,0,83,0,114,110,0,0,0,114,3,0, 0,0,114,16,1,0,0,169,1,114,140,0,0,0,114,3, - 0,0,0,114,6,0,0,0,114,19,1,0,0,156,5,0, + 0,0,0,114,6,0,0,0,114,19,1,0,0,158,5,0, 0,115,4,0,0,0,4,0,2,0,122,38,70,105,108,101, 70,105,110,100,101,114,46,95,95,105,110,105,116,95,95,46, 60,108,111,99,97,108,115,62,46,60,103,101,110,101,120,112, @@ -2432,7 +2432,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,0,218,14,108,111,97,100,101,114,95,100,101,116,97, 105,108,115,90,7,108,111,97,100,101,114,115,114,189,0,0, 0,114,3,0,0,0,114,80,1,0,0,114,6,0,0,0, - 114,209,0,0,0,150,5,0,0,115,16,0,0,0,0,4, + 114,209,0,0,0,152,5,0,0,115,16,0,0,0,0,4, 4,1,12,1,26,1,6,2,10,1,6,1,8,1,122,19, 70,105,108,101,70,105,110,100,101,114,46,95,95,105,110,105, 116,95,95,99,1,0,0,0,0,0,0,0,0,0,0,0, @@ -2442,7 +2442,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,101,99,116,111,114,121,32,109,116,105,109,101,46,114,105, 0,0,0,78,41,1,114,82,1,0,0,114,246,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 46,1,0,0,164,5,0,0,115,2,0,0,0,0,2,122, + 46,1,0,0,166,5,0,0,115,2,0,0,0,0,2,122, 28,70,105,108,101,70,105,110,100,101,114,46,105,110,118,97, 108,105,100,97,116,101,95,99,97,99,104,101,115,99,2,0, 0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0, @@ -2465,7 +2465,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 3,114,203,0,0,0,114,140,0,0,0,114,178,0,0,0, 41,3,114,119,0,0,0,114,139,0,0,0,114,187,0,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,137,0,0,0,170,5,0,0,115,8,0,0,0,0,7, + 114,137,0,0,0,172,5,0,0,115,8,0,0,0,0,7, 10,1,8,1,8,1,122,22,70,105,108,101,70,105,110,100, 101,114,46,102,105,110,100,95,108,111,97,100,101,114,99,6, 0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,6, @@ -2475,7 +2475,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,190,0,0,0,41,7,114,119,0,0,0,114,188,0,0, 0,114,139,0,0,0,114,44,0,0,0,90,4,115,109,115, 108,114,202,0,0,0,114,140,0,0,0,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,58,1,0,0,182, + 114,3,0,0,0,114,6,0,0,0,114,58,1,0,0,184, 5,0,0,115,8,0,0,0,0,1,10,1,8,1,2,255, 122,20,70,105,108,101,70,105,110,100,101,114,46,95,103,101, 116,95,115,112,101,99,78,99,3,0,0,0,0,0,0,0, @@ -2530,7 +2530,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,114,188,0,0,0,90,13,105,110,105,116,95,102,105,108, 101,110,97,109,101,90,9,102,117,108,108,95,112,97,116,104, 114,187,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,114,203,0,0,0,187,5,0,0,115,74,0, + 6,0,0,0,114,203,0,0,0,189,5,0,0,115,74,0, 0,0,0,5,4,1,14,1,2,1,24,1,14,1,10,1, 10,1,8,1,6,2,6,1,6,1,10,2,6,1,4,2, 8,1,12,1,14,1,8,1,10,1,8,1,26,4,8,2, @@ -2562,7 +2562,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,146,2,113,4,83,0,114,3,0,0,0,41,1,114,106, 0,0,0,41,2,114,32,0,0,0,90,2,102,110,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,218,9,60, - 115,101,116,99,111,109,112,62,8,6,0,0,115,4,0,0, + 115,101,116,99,111,109,112,62,10,6,0,0,115,4,0,0, 0,6,0,2,0,122,41,70,105,108,101,70,105,110,100,101, 114,46,95,102,105,108,108,95,99,97,99,104,101,46,60,108, 111,99,97,108,115,62,46,60,115,101,116,99,111,109,112,62, @@ -2579,7 +2579,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,41,1,0,0,114,117,0,0,0,114,29,1,0,0,114, 17,1,0,0,90,8,110,101,119,95,110,97,109,101,114,3, 0,0,0,114,3,0,0,0,114,6,0,0,0,114,87,1, - 0,0,235,5,0,0,115,34,0,0,0,0,2,6,1,2, + 0,0,237,5,0,0,115,34,0,0,0,0,2,6,1,2, 1,22,1,20,3,10,3,12,1,12,7,6,1,8,1,16, 1,4,1,18,2,4,1,12,1,6,1,12,1,122,22,70, 105,108,101,70,105,110,100,101,114,46,95,102,105,108,108,95, @@ -2617,14 +2617,14 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,48,0,0,0,169,2,114,193,0,0,0,114,86, 1,0,0,114,3,0,0,0,114,6,0,0,0,218,24,112, 97,116,104,95,104,111,111,107,95,102,111,114,95,70,105,108, - 101,70,105,110,100,101,114,20,6,0,0,115,6,0,0,0, + 101,70,105,110,100,101,114,22,6,0,0,115,6,0,0,0, 0,2,8,1,12,1,122,54,70,105,108,101,70,105,110,100, 101,114,46,112,97,116,104,95,104,111,111,107,46,60,108,111, 99,97,108,115,62,46,112,97,116,104,95,104,111,111,107,95, 102,111,114,95,70,105,108,101,70,105,110,100,101,114,114,3, 0,0,0,41,3,114,193,0,0,0,114,86,1,0,0,114, 93,1,0,0,114,3,0,0,0,114,92,1,0,0,114,6, - 0,0,0,218,9,112,97,116,104,95,104,111,111,107,10,6, + 0,0,0,218,9,112,97,116,104,95,104,111,111,107,12,6, 0,0,115,4,0,0,0,0,10,14,6,122,20,70,105,108, 101,70,105,110,100,101,114,46,112,97,116,104,95,104,111,111, 107,99,1,0,0,0,0,0,0,0,0,0,0,0,1,0, @@ -2633,7 +2633,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 70,105,108,101,70,105,110,100,101,114,40,123,33,114,125,41, 41,2,114,62,0,0,0,114,44,0,0,0,114,246,0,0, 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,39,1,0,0,28,6,0,0,115,2,0,0,0,0,1, + 114,39,1,0,0,30,6,0,0,115,2,0,0,0,0,1, 122,19,70,105,108,101,70,105,110,100,101,114,46,95,95,114, 101,112,114,95,95,41,1,78,41,15,114,125,0,0,0,114, 124,0,0,0,114,126,0,0,0,114,127,0,0,0,114,209, @@ -2641,7 +2641,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,114,137,0,0,0,114,58,1,0,0,114,203,0,0, 0,114,87,1,0,0,114,207,0,0,0,114,94,1,0,0, 114,39,1,0,0,114,3,0,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,79,1,0,0,141,5, + 3,0,0,0,114,6,0,0,0,114,79,1,0,0,143,5, 0,0,115,22,0,0,0,8,2,4,7,8,14,8,4,4, 2,8,12,8,5,10,48,8,31,2,1,10,17,114,79,1, 0,0,99,4,0,0,0,0,0,0,0,0,0,0,0,6, @@ -2664,7 +2664,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,90,8,112,97,116,104,110,97,109,101,90,9,99,112,97, 116,104,110,97,109,101,114,140,0,0,0,114,187,0,0,0, 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 14,95,102,105,120,95,117,112,95,109,111,100,117,108,101,34, + 14,95,102,105,120,95,117,112,95,109,111,100,117,108,101,36, 6,0,0,115,34,0,0,0,0,2,10,1,10,1,4,1, 4,1,8,1,8,1,12,2,10,1,4,1,14,1,2,1, 8,1,8,1,8,1,12,1,14,2,114,98,1,0,0,99, @@ -2684,7 +2684,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 114,15,1,0,0,114,89,0,0,0,41,3,90,10,101,120, 116,101,110,115,105,111,110,115,90,6,115,111,117,114,99,101, 90,8,98,121,116,101,99,111,100,101,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,184,0,0,0,57,6, + 3,0,0,0,114,6,0,0,0,114,184,0,0,0,59,6, 0,0,115,8,0,0,0,0,5,12,1,8,1,8,1,114, 184,0,0,0,99,1,0,0,0,0,0,0,0,0,0,0, 0,12,0,0,0,9,0,0,0,67,0,0,0,115,178,1, @@ -2737,7 +2737,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,1,0,113,2,100,1,83,0,41,2,114,39,0,0,0, 78,41,1,114,22,0,0,0,41,2,114,32,0,0,0,114, 95,0,0,0,114,3,0,0,0,114,3,0,0,0,114,6, - 0,0,0,114,19,1,0,0,93,6,0,0,115,4,0,0, + 0,0,0,114,19,1,0,0,95,6,0,0,115,4,0,0, 0,4,0,2,0,122,25,95,115,101,116,117,112,46,60,108, 111,99,97,108,115,62,46,60,103,101,110,101,120,112,114,62, 114,73,0,0,0,122,30,105,109,112,111,114,116,108,105,98, @@ -2749,7 +2749,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 93,14,125,1,100,0,124,1,155,0,157,2,146,2,113,4, 83,0,41,1,114,74,0,0,0,114,3,0,0,0,41,2, 114,32,0,0,0,218,1,115,114,3,0,0,0,114,3,0, - 0,0,114,6,0,0,0,114,88,1,0,0,109,6,0,0, + 0,0,114,6,0,0,0,114,88,1,0,0,111,6,0,0, 115,4,0,0,0,6,0,2,0,122,25,95,115,101,116,117, 112,46,60,108,111,99,97,108,115,62,46,60,115,101,116,99, 111,109,112,62,90,7,95,116,104,114,101,97,100,90,8,95, @@ -2773,7 +2773,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 111,100,117,108,101,90,14,119,101,97,107,114,101,102,95,109, 111,100,117,108,101,90,13,119,105,110,114,101,103,95,109,111, 100,117,108,101,114,3,0,0,0,114,3,0,0,0,114,6, - 0,0,0,218,6,95,115,101,116,117,112,68,6,0,0,115, + 0,0,0,218,6,95,115,101,116,117,112,70,6,0,0,115, 78,0,0,0,0,8,4,1,6,1,6,3,10,1,8,1, 10,1,12,2,10,1,14,3,22,1,12,2,22,1,8,1, 10,1,10,1,6,2,2,1,10,1,10,1,14,1,12,2, @@ -2794,7 +2794,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 2,114,105,1,0,0,90,17,115,117,112,112,111,114,116,101, 100,95,108,111,97,100,101,114,115,114,3,0,0,0,114,3, 0,0,0,114,6,0,0,0,218,8,95,105,110,115,116,97, - 108,108,133,6,0,0,115,8,0,0,0,0,2,8,1,6, + 108,108,135,6,0,0,115,8,0,0,0,0,2,8,1,6, 1,20,1,114,108,1,0,0,41,1,114,60,0,0,0,41, 1,78,41,3,78,78,78,41,2,114,73,0,0,0,114,73, 0,0,0,41,1,84,41,1,78,41,1,78,41,63,114,127, @@ -2827,7 +2827,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 6,0,0,0,218,8,60,109,111,100,117,108,101,62,1,0, 0,0,115,126,0,0,0,4,22,4,1,4,1,2,1,2, 255,4,4,8,17,8,5,8,5,8,6,8,6,8,12,8, - 10,8,9,8,5,8,7,8,9,10,22,10,127,0,10,16, + 10,8,9,8,5,8,7,8,9,10,22,10,127,0,12,16, 1,12,2,4,1,4,2,6,2,6,2,8,2,16,71,8, 40,8,19,8,12,8,12,8,28,8,17,8,33,8,28,8, 24,10,13,10,10,10,11,8,14,6,3,4,1,2,255,12, From webhook-mailer at python.org Mon Jul 15 05:15:21 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 15 Jul 2019 09:15:21 -0000 Subject: [Python-checkins] bpo-37500: Make sure dead code does not generate bytecode but also detect syntax errors (GH-14612) Message-ID: https://github.com/python/cpython/commit/18c5f9d44dde37c0fae5585a604c6027825252d2 commit: 18c5f9d44dde37c0fae5585a604c6027825252d2 branch: master author: Pablo Galindo committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-15T02:15:01-07:00 summary: bpo-37500: Make sure dead code does not generate bytecode but also detect syntax errors (GH-14612) https://bugs.python.org/issue37500 Add a new field to the compiler structure that allows to be configured so no bytecode is emitted. In this way is possible to detect errors by walking the nodes while preserving optimizations. https://bugs.python.org/issue37500 files: M Lib/test/test_compile.py M Lib/test/test_syntax.py M Lib/test/test_sys_settrace.py M Python/compile.c diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 56f73f631534..9d77f7af05d6 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -697,6 +697,40 @@ def test_stack_overflow(self): # complex statements. compile("if a: b\n" * 200000, "", "exec") + # Multiple users rely on the fact that CPython does not generate + # bytecode for dead code blocks. See bpo-37500 for more context. + @support.cpython_only + def test_dead_blocks_do_not_generate_bytecode(self): + def unused_block_if(): + if 0: + return 42 + + def unused_block_while(): + while 0: + return 42 + + def unused_block_if_else(): + if 1: + return None + else: + return 42 + + def unused_block_while_else(): + while 1: + return None + else: + return 42 + + funcs = [unused_block_if, unused_block_while, + unused_block_if_else, unused_block_while_else] + + for func in funcs: + opcodes = list(dis.get_instructions(func)) + self.assertEqual(2, len(opcodes)) + self.assertEqual('LOAD_CONST', opcodes[0].opname) + self.assertEqual(None, opcodes[0].argval) + self.assertEqual('RETURN_VALUE', opcodes[1].opname) + class TestExpressionStackSize(unittest.TestCase): # These tests check that the computed stack size for a code object diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 8451c072f642..3829746f1799 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -697,18 +697,47 @@ def test_break_outside_loop(self): self._check_error("break", "outside loop") def test_yield_outside_function(self): - self._check_error("if 0: yield", "outside function") - self._check_error("class C:\n if 0: yield", "outside function") + self._check_error("if 0: yield", "outside function") + self._check_error("if 0: yield\nelse: x=1", "outside function") + self._check_error("if 1: pass\nelse: yield", "outside function") + self._check_error("while 0: yield", "outside function") + self._check_error("while 0: yield\nelse: x=1", "outside function") + self._check_error("class C:\n if 0: yield", "outside function") + self._check_error("class C:\n if 1: pass\n else: yield", + "outside function") + self._check_error("class C:\n while 0: yield", "outside function") + self._check_error("class C:\n while 0: yield\n else: x = 1", + "outside function") def test_return_outside_function(self): - self._check_error("if 0: return", "outside function") - self._check_error("class C:\n if 0: return", "outside function") + self._check_error("if 0: return", "outside function") + self._check_error("if 0: return\nelse: x=1", "outside function") + self._check_error("if 1: pass\nelse: return", "outside function") + self._check_error("while 0: return", "outside function") + self._check_error("class C:\n if 0: return", "outside function") + self._check_error("class C:\n while 0: return", "outside function") + self._check_error("class C:\n while 0: return\n else: x=1", + "outside function") + self._check_error("class C:\n if 0: return\n else: x= 1", + "outside function") + self._check_error("class C:\n if 1: pass\n else: return", + "outside function") def test_break_outside_loop(self): - self._check_error("if 0: break", "outside loop") + self._check_error("if 0: break", "outside loop") + self._check_error("if 0: break\nelse: x=1", "outside loop") + self._check_error("if 1: pass\nelse: break", "outside loop") + self._check_error("class C:\n if 0: break", "outside loop") + self._check_error("class C:\n if 1: pass\n else: break", + "outside loop") def test_continue_outside_loop(self): - self._check_error("if 0: continue", "not properly in loop") + self._check_error("if 0: continue", "not properly in loop") + self._check_error("if 0: continue\nelse: x=1", "not properly in loop") + self._check_error("if 1: pass\nelse: continue", "not properly in loop") + self._check_error("class C:\n if 0: continue", "not properly in loop") + self._check_error("class C:\n if 1: pass\n else: continue", + "not properly in loop") def test_unexpected_indent(self): self._check_error("foo()\n bar()\n", "unexpected indent", diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 112ea877205a..fdd789475d04 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -53,22 +53,52 @@ def basic(): # following that clause? -# The entire "while 0:" statement is optimized away. No code -# exists for it, so the line numbers skip directly from "del x" -# to "x = 1". -def arigo_example(): +# Some constructs like "while 0:", "if 0:" or "if 1:...else:..." are optimized +# away. No code # exists for them, so the line numbers skip directly from +# "del x" to "x = 1". +def arigo_example0(): x = 1 del x while 0: pass x = 1 -arigo_example.events = [(0, 'call'), +arigo_example0.events = [(0, 'call'), (1, 'line'), (2, 'line'), (5, 'line'), (5, 'return')] +def arigo_example1(): + x = 1 + del x + if 0: + pass + x = 1 + +arigo_example1.events = [(0, 'call'), + (1, 'line'), + (2, 'line'), + (5, 'line'), + (5, 'return')] + +def arigo_example2(): + x = 1 + del x + if 1: + x = 1 + else: + pass + return None + +arigo_example2.events = [(0, 'call'), + (1, 'line'), + (2, 'line'), + (4, 'line'), + (7, 'line'), + (7, 'return')] + + # check that lines consisting of just one instruction get traced: def one_instr_line(): x = 1 @@ -349,8 +379,12 @@ def fn(*args): def test_01_basic(self): self.run_test(basic) - def test_02_arigo(self): - self.run_test(arigo_example) + def test_02_arigo0(self): + self.run_test(arigo_example0) + def test_02_arigo1(self): + self.run_test(arigo_example1) + def test_02_arigo2(self): + self.run_test(arigo_example2) def test_03_one_instr(self): self.run_test(one_instr_line) def test_04_no_pop_blocks(self): diff --git a/Python/compile.c b/Python/compile.c index 9cce8aeb4e1f..0336959d3da2 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -161,6 +161,11 @@ struct compiler { int c_optimize; /* optimization level */ int c_interactive; /* true if in interactive mode */ int c_nestlevel; + int c_do_not_emit_bytecode; /* The compiler won't emit any bytecode + if this value is different from zero. + This can be used to temporarily visit + nodes without emitting bytecode to + check only errors. */ PyObject *c_const_cache; /* Python dict holding all constants, including names tuple */ @@ -340,6 +345,7 @@ PyAST_CompileObject(mod_ty mod, PyObject *filename, PyCompilerFlags *flags, c.c_flags = flags; c.c_optimize = (optimize == -1) ? config->optimization_level : optimize; c.c_nestlevel = 0; + c.c_do_not_emit_bytecode = 0; if (!_PyAST_Optimize(mod, arena, c.c_optimize)) { goto finally; @@ -1152,6 +1158,9 @@ compiler_addop(struct compiler *c, int opcode) struct instr *i; int off; assert(!HAS_ARG(opcode)); + if (c->c_do_not_emit_bytecode) { + return 1; + } off = compiler_next_instr(c, c->u->u_curblock); if (off < 0) return 0; @@ -1305,6 +1314,10 @@ merge_consts_recursive(struct compiler *c, PyObject *o) static Py_ssize_t compiler_add_const(struct compiler *c, PyObject *o) { + if (c->c_do_not_emit_bytecode) { + return 0; + } + PyObject *key = merge_consts_recursive(c, o); if (key == NULL) { return -1; @@ -1318,6 +1331,10 @@ compiler_add_const(struct compiler *c, PyObject *o) static int compiler_addop_load_const(struct compiler *c, PyObject *o) { + if (c->c_do_not_emit_bytecode) { + return 1; + } + Py_ssize_t arg = compiler_add_const(c, o); if (arg < 0) return 0; @@ -1328,6 +1345,10 @@ static int compiler_addop_o(struct compiler *c, int opcode, PyObject *dict, PyObject *o) { + if (c->c_do_not_emit_bytecode) { + return 1; + } + Py_ssize_t arg = compiler_add_o(c, dict, o); if (arg < 0) return 0; @@ -1339,6 +1360,11 @@ compiler_addop_name(struct compiler *c, int opcode, PyObject *dict, PyObject *o) { Py_ssize_t arg; + + if (c->c_do_not_emit_bytecode) { + return 1; + } + PyObject *mangled = _Py_Mangle(c->u->u_private, o); if (!mangled) return 0; @@ -1359,6 +1385,10 @@ compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg) struct instr *i; int off; + if (c->c_do_not_emit_bytecode) { + return 1; + } + /* oparg value is unsigned, but a signed C int is usually used to store it in the C code (like Python/ceval.c). @@ -1385,6 +1415,10 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) struct instr *i; int off; + if (c->c_do_not_emit_bytecode) { + return 1; + } + assert(HAS_ARG(opcode)); assert(b != NULL); off = compiler_next_instr(c, c->u->u_curblock); @@ -1519,6 +1553,17 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) } \ } +/* These macros allows to check only for errors and not emmit bytecode + * while visiting nodes. +*/ + +#define BEGIN_DO_NOT_EMIT_BYTECODE { \ + c->c_do_not_emit_bytecode++; + +#define END_DO_NOT_EMIT_BYTECODE \ + c->c_do_not_emit_bytecode--; \ +} + /* Search if variable annotations are present statically in a block. */ static int @@ -2546,13 +2591,23 @@ compiler_if(struct compiler *c, stmt_ty s) return 0; constant = expr_constant(s->v.If.test); - /* constant = 0: "if 0" Leave the optimizations to - * the pephole optimizer to check for syntax errors - * in the block. + /* constant = 0: "if 0" * constant = 1: "if 1", "if 2", ... * constant = -1: rest */ - if (constant == 1) { + if (constant == 0) { + BEGIN_DO_NOT_EMIT_BYTECODE + VISIT_SEQ(c, stmt, s->v.If.body); + END_DO_NOT_EMIT_BYTECODE + if (s->v.If.orelse) { + VISIT_SEQ(c, stmt, s->v.If.orelse); + } + } else if (constant == 1) { VISIT_SEQ(c, stmt, s->v.If.body); + if (s->v.If.orelse) { + BEGIN_DO_NOT_EMIT_BYTECODE + VISIT_SEQ(c, stmt, s->v.If.orelse); + END_DO_NOT_EMIT_BYTECODE + } } else { if (asdl_seq_LEN(s->v.If.orelse)) { next = compiler_new_block(c); @@ -2662,8 +2717,12 @@ compiler_while(struct compiler *c, stmt_ty s) int constant = expr_constant(s->v.While.test); if (constant == 0) { - if (s->v.While.orelse) + BEGIN_DO_NOT_EMIT_BYTECODE + VISIT_SEQ(c, stmt, s->v.While.body); + END_DO_NOT_EMIT_BYTECODE + if (s->v.While.orelse) { VISIT_SEQ(c, stmt, s->v.While.orelse); + } return 1; } loop = compiler_new_block(c); From webhook-mailer at python.org Mon Jul 15 10:37:36 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 15 Jul 2019 14:37:36 -0000 Subject: [Python-checkins] bpo-37284: Add note to sys.implementation doc (GH-14328) Message-ID: https://github.com/python/cpython/commit/52693c10e82622d883433b779a45d0bd792f17ed commit: 52693c10e82622d883433b779a45d0bd792f17ed branch: master author: Giovanni Cappellotto committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-15T07:37:09-07:00 summary: bpo-37284: Add note to sys.implementation doc (GH-14328) Add a brief note to indicate that any new required attributes must go through the PEP process. https://bugs.python.org/issue37284 files: A Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index acd54421a370..6119bee10ab3 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -875,6 +875,10 @@ always available. .. versionadded:: 3.3 + .. note:: + + The addition of new required attributes must go through the normal PEP + process. See :pep:`421` for more information. .. data:: int_info diff --git a/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst b/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst new file mode 100644 index 000000000000..f875791c9a93 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst @@ -0,0 +1 @@ +Add a brief note to indicate that any new ``sys.implementation`` required attributes must go through the PEP process. \ No newline at end of file From webhook-mailer at python.org Mon Jul 15 10:44:32 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 15 Jul 2019 14:44:32 -0000 Subject: [Python-checkins] bpo-37284: Add note to sys.implementation doc (GH-14328) Message-ID: https://github.com/python/cpython/commit/134f79682d6729e3cf84b665d615f576075550e8 commit: 134f79682d6729e3cf84b665d615f576075550e8 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-15T07:44:26-07:00 summary: bpo-37284: Add note to sys.implementation doc (GH-14328) Add a brief note to indicate that any new required attributes must go through the PEP process. https://bugs.python.org/issue37284 (cherry picked from commit 52693c10e82622d883433b779a45d0bd792f17ed) Co-authored-by: Giovanni Cappellotto files: A Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 9a8c2ca0c5d0..e54f2526a8df 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -809,6 +809,10 @@ always available. .. versionadded:: 3.3 + .. note:: + + The addition of new required attributes must go through the normal PEP + process. See :pep:`421` for more information. .. data:: int_info diff --git a/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst b/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst new file mode 100644 index 000000000000..f875791c9a93 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst @@ -0,0 +1 @@ +Add a brief note to indicate that any new ``sys.implementation`` required attributes must go through the PEP process. \ No newline at end of file From webhook-mailer at python.org Mon Jul 15 10:45:17 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 15 Jul 2019 14:45:17 -0000 Subject: [Python-checkins] bpo-37284: Add note to sys.implementation doc (GH-14328) Message-ID: https://github.com/python/cpython/commit/1ff4c4277421c02df5ca0894fb95de70cd9cd5f4 commit: 1ff4c4277421c02df5ca0894fb95de70cd9cd5f4 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-15T07:45:12-07:00 summary: bpo-37284: Add note to sys.implementation doc (GH-14328) Add a brief note to indicate that any new required attributes must go through the PEP process. https://bugs.python.org/issue37284 (cherry picked from commit 52693c10e82622d883433b779a45d0bd792f17ed) Co-authored-by: Giovanni Cappellotto files: A Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index a33e796cf753..09a987ca32ac 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -883,6 +883,10 @@ always available. .. versionadded:: 3.3 + .. note:: + + The addition of new required attributes must go through the normal PEP + process. See :pep:`421` for more information. .. data:: int_info diff --git a/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst b/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst new file mode 100644 index 000000000000..f875791c9a93 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst @@ -0,0 +1 @@ +Add a brief note to indicate that any new ``sys.implementation`` required attributes must go through the PEP process. \ No newline at end of file From webhook-mailer at python.org Mon Jul 15 11:19:20 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 15 Jul 2019 15:19:20 -0000 Subject: [Python-checkins] bpo-37468: make install no longer install wininst-*.exe files (GH-14511) Message-ID: https://github.com/python/cpython/commit/e8692818afd731c1b7e925c626ac8200b1f1c31e commit: e8692818afd731c1b7e925c626ac8200b1f1c31e branch: master author: Victor Stinner committer: GitHub date: 2019-07-15T17:18:42+02:00 summary: bpo-37468: make install no longer install wininst-*.exe files (GH-14511) make install no longer installs "wininst-*.exe" files used by distutils bdist_wininst: bdist_wininst only works on Windows. files: A Misc/NEWS.d/next/Build/2019-07-01-14-39-40.bpo-37468.trbQ-_.rst M Makefile.pre.in diff --git a/Makefile.pre.in b/Makefile.pre.in index ef9a4f88598f..94a1208efa09 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1431,6 +1431,9 @@ libinstall: build_all $(srcdir)/Modules/xxmodule.c *CVS) ;; \ *.py[co]) ;; \ *.orig) ;; \ + # bpo-37468: Don't install distutils/command/wininst-*.exe files used \ + # by distutils bdist_wininst: bdist_wininst only works on Windows. \ + *wininst-*.exe) ;; \ *~) ;; \ *) \ if test -d $$i; then continue; fi; \ diff --git a/Misc/NEWS.d/next/Build/2019-07-01-14-39-40.bpo-37468.trbQ-_.rst b/Misc/NEWS.d/next/Build/2019-07-01-14-39-40.bpo-37468.trbQ-_.rst new file mode 100644 index 000000000000..b8c70d574076 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2019-07-01-14-39-40.bpo-37468.trbQ-_.rst @@ -0,0 +1,2 @@ +``make install`` no longer installs ``wininst-*.exe`` files used by +distutils bdist_wininst: bdist_wininst only works on Windows. From webhook-mailer at python.org Tue Jul 16 10:55:48 2019 From: webhook-mailer at python.org (Gregory P. Smith) Date: Tue, 16 Jul 2019 14:55:48 -0000 Subject: [Python-checkins] Replace backquote with command substitution in subprocess doc example (GH-13941) Message-ID: https://github.com/python/cpython/commit/6a61714cde7037cd9a1bcc11ecccb17fe3081295 commit: 6a61714cde7037cd9a1bcc11ecccb17fe3081295 branch: master author: David Jones committer: Gregory P. Smith date: 2019-07-16T07:55:19-07:00 summary: Replace backquote with command substitution in subprocess doc example (GH-13941) Replace backquotes with POSIXy command substitution in example. files: M Doc/library/subprocess.rst M Doc/tools/susp-ignored.csv diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index af338795dae3..7e1e3f942c1c 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -1166,12 +1166,12 @@ In the following examples, we assume that the relevant functions have already been imported from the :mod:`subprocess` module. -Replacing /bin/sh shell backquote -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Replacing :program:`/bin/sh` shell command substitution +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash - output=`mycmd myarg` + output=$(mycmd myarg) becomes:: @@ -1182,7 +1182,7 @@ Replacing shell pipeline .. code-block:: bash - output=`dmesg | grep hda` + output=$(dmesg | grep hda) becomes:: @@ -1199,7 +1199,7 @@ be used directly: .. code-block:: bash - output=`dmesg | grep hda` + output=$(dmesg | grep hda) becomes:: diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index fcf556ec0daf..80757752388f 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -218,8 +218,6 @@ library/stdtypes,,:len,s[len(s):len(s)] library/stdtypes,,::,>>> y = m[::2] library/stdtypes,,::,>>> z = y[::-2] library/string,,`,"!""#$%&'()*+,-./:;<=>?@[\]^_`{|}~" -library/subprocess,,`,"output=`dmesg | grep hda`" -library/subprocess,,`,"output=`mycmd myarg`" library/tarfile,,:bz2, library/tarfile,,:compression,filemode[:compression] library/tarfile,,:gz, From webhook-mailer at python.org Tue Jul 16 11:05:43 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 16 Jul 2019 15:05:43 -0000 Subject: [Python-checkins] Replace backquote with command substitution in subprocess doc example (GH-13941) Message-ID: https://github.com/python/cpython/commit/86742d4ce8d6c301104e238699b30f6bdaf9f9cb commit: 86742d4ce8d6c301104e238699b30f6bdaf9f9cb branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-16T08:05:39-07:00 summary: Replace backquote with command substitution in subprocess doc example (GH-13941) Replace backquotes with POSIXy command substitution in example. (cherry picked from commit 6a61714cde7037cd9a1bcc11ecccb17fe3081295) Co-authored-by: David Jones files: M Doc/library/subprocess.rst M Doc/tools/susp-ignored.csv diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index af338795dae3..7e1e3f942c1c 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -1166,12 +1166,12 @@ In the following examples, we assume that the relevant functions have already been imported from the :mod:`subprocess` module. -Replacing /bin/sh shell backquote -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Replacing :program:`/bin/sh` shell command substitution +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash - output=`mycmd myarg` + output=$(mycmd myarg) becomes:: @@ -1182,7 +1182,7 @@ Replacing shell pipeline .. code-block:: bash - output=`dmesg | grep hda` + output=$(dmesg | grep hda) becomes:: @@ -1199,7 +1199,7 @@ be used directly: .. code-block:: bash - output=`dmesg | grep hda` + output=$(dmesg | grep hda) becomes:: diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index 85263d47c8bb..9129ee85d7e2 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -216,8 +216,6 @@ library/stdtypes,,:len,s[len(s):len(s)] library/stdtypes,,::,>>> y = m[::2] library/stdtypes,,::,>>> z = y[::-2] library/string,,`,"!""#$%&'()*+,-./:;<=>?@[\]^_`{|}~" -library/subprocess,,`,"output=`dmesg | grep hda`" -library/subprocess,,`,"output=`mycmd myarg`" library/tarfile,,:bz2, library/tarfile,,:compression,filemode[:compression] library/tarfile,,:gz, From webhook-mailer at python.org Tue Jul 16 11:13:44 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Tue, 16 Jul 2019 15:13:44 -0000 Subject: [Python-checkins] bpo-37352: Minor word-smithing for design.rst (GH #14730) Message-ID: https://github.com/python/cpython/commit/a0f7119f1584db5b32deffb60a68d830673ebbdf commit: a0f7119f1584db5b32deffb60a68d830673ebbdf branch: master author: Ilya Kamenshchikov committer: Raymond Hettinger date: 2019-07-16T08:13:38-07:00 summary: bpo-37352: Minor word-smithing for design.rst (GH #14730) files: M Doc/faq/design.rst diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst index 387420c17bd1..47fc4d498e76 100644 --- a/Doc/faq/design.rst +++ b/Doc/faq/design.rst @@ -641,11 +641,11 @@ to the end of some internal list; an interface specification cannot test that your :meth:`append` implementation will actually do this correctly, but it's trivial to check this property in a test suite. -Writing test suites is very helpful, and you might want to design your code with -an eye to making it easily tested. One increasingly popular technique, -test-directed development, calls for writing parts of the test suite first, -before you write any of the actual code. Of course Python allows you to be -sloppy and not write test cases at all. +Writing test suites is very helpful, and you might want to design your code to +make it easily tested. One increasingly popular technique, test-driven +development, calls for writing parts of the test suite first, before you write +any of the actual code. Of course Python allows you to be sloppy and not write +test cases at all. Why is there no goto? From webhook-mailer at python.org Tue Jul 16 11:15:23 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Tue, 16 Jul 2019 15:15:23 -0000 Subject: [Python-checkins] bpo-37599: Remove a vague statement in documentation of Integer Objects (#14786) Message-ID: https://github.com/python/cpython/commit/1d8b04edfdc3030e645730492bfcc27b75718b96 commit: 1d8b04edfdc3030e645730492bfcc27b75718b96 branch: master author: sgal <32255369+sgalal at users.noreply.github.com> committer: Raymond Hettinger date: 2019-07-16T08:15:17-07:00 summary: bpo-37599: Remove a vague statement in documentation of Integer Objects (#14786) * Remove a vague statement in documentation * Remove another vague sentence A sentence starting with "So it should be possible..." shouldn't be in the docs either. Co-Authored-By: Kyle Stanley * Include the removal of the previous line Co-Authored-By: Kyle Stanley * Remove an extra space files: M Doc/c-api/long.rst diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index fdaefafe21ba..aeebf3060eb4 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -42,9 +42,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. The current implementation keeps an array of integer objects for all integers between ``-5`` and ``256``, when you create an int in that range you actually - just get back a reference to the existing object. So it should be possible to - change the value of ``1``. I suspect the behaviour of Python in this case is - undefined. :-) + just get back a reference to the existing object. .. c:function:: PyObject* PyLong_FromUnsignedLong(unsigned long v) From webhook-mailer at python.org Tue Jul 16 13:50:26 2019 From: webhook-mailer at python.org (Barry Warsaw) Date: Tue, 16 Jul 2019 17:50:26 -0000 Subject: [Python-checkins] Fix infinite loop in email folding logic (GH-12732) Message-ID: https://github.com/python/cpython/commit/f69d5c61981ea97d251db515c7ff280fcc17182d commit: f69d5c61981ea97d251db515c7ff280fcc17182d branch: master author: Paul Ganssle committer: Barry Warsaw date: 2019-07-16T10:50:01-07:00 summary: Fix infinite loop in email folding logic (GH-12732) As far as I can tell, this infinite loop would be triggered if: 1. The value being folded contains a single word (no spaces) longer than max_line_length 2. The max_line_length is shorter than the encoding's name + 9 characters. bpo-36564: https://bugs.python.org/issue36564 files: A Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst M Lib/email/_header_value_parser.py M Lib/email/parser.py M Lib/test/test_email/test_policy.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index aefc457f6781..37dc76470160 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -2846,15 +2846,22 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset): trailing_wsp = to_encode[-1] to_encode = to_encode[:-1] new_last_ew = len(lines[-1]) if last_ew is None else last_ew + + encode_as = 'utf-8' if charset == 'us-ascii' else charset + + # The RFC2047 chrome takes up 7 characters plus the length + # of the charset name. + chrome_len = len(encode_as) + 7 + + if (chrome_len + 1) >= maxlen: + raise errors.HeaderParseError( + "max_line_length is too small to fit an encoded word") + while to_encode: remaining_space = maxlen - len(lines[-1]) - # The RFC2047 chrome takes up 7 characters plus the length - # of the charset name. - encode_as = 'utf-8' if charset == 'us-ascii' else charset - text_space = remaining_space - len(encode_as) - 7 + text_space = remaining_space - chrome_len if text_space <= 0: lines.append(' ') - # XXX We'll get an infinite loop here if maxlen is <= 7 continue to_encode_word = to_encode[:text_space] diff --git a/Lib/email/parser.py b/Lib/email/parser.py index 555b17256061..7db4da1ff081 100644 --- a/Lib/email/parser.py +++ b/Lib/email/parser.py @@ -13,7 +13,6 @@ from email._policybase import compat32 - class Parser: def __init__(self, _class=None, *, policy=compat32): """Parser of RFC 2822 and MIME email messages. diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index 1e39aa062c0a..e87c27554940 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -2,6 +2,7 @@ import types import textwrap import unittest +import email.errors import email.policy import email.parser import email.generator @@ -257,6 +258,25 @@ def test_non_ascii_chars_do_not_cause_inf_loop(self): 'Subject: \n' + 12 * ' =?utf-8?q?=C4=85?=\n') + def test_short_maxlen_error(self): + # RFC 2047 chrome takes up 7 characters, plus the length of the charset + # name, so folding should fail if maxlen is lower than the minimum + # required length for a line. + + # Note: This is only triggered when there is a single word longer than + # max_line_length, hence the 1234567890 at the end of this whimsical + # subject. This is because when we encounter a word longer than + # max_line_length, it is broken down into encoded words to fit + # max_line_length. If the max_line_length isn't large enough to even + # contain the RFC 2047 chrome (`?=?q??=`), we fail. + subject = "Melt away the pounds with this one simple trick! 1234567890" + + for maxlen in [3, 7, 9]: + with self.subTest(maxlen=maxlen): + policy = email.policy.default.clone(max_line_length=maxlen) + with self.assertRaises(email.errors.HeaderParseError): + policy.fold("Subject", subject) + # XXX: Need subclassing tests. # For adding subclassed objects, make sure the usual rules apply (subclass # wins), but that the order still works (right overrides left). diff --git a/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst new file mode 100644 index 000000000000..ddd17aec1dd8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst @@ -0,0 +1,3 @@ +Fix infinite loop in email header folding logic that would be triggered when +an email policy's max_line_length is not long enough to include the required +markup and any values in the message. Patch by Paul Ganssle From webhook-mailer at python.org Tue Jul 16 14:08:42 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 16 Jul 2019 18:08:42 -0000 Subject: [Python-checkins] Fix infinite loop in email folding logic (GH-12732) Message-ID: https://github.com/python/cpython/commit/6a2aec0ff587032beb8aac8cbebb09e7a52f6694 commit: 6a2aec0ff587032beb8aac8cbebb09e7a52f6694 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-16T11:08:36-07:00 summary: Fix infinite loop in email folding logic (GH-12732) As far as I can tell, this infinite loop would be triggered if: 1. The value being folded contains a single word (no spaces) longer than max_line_length 2. The max_line_length is shorter than the encoding's name + 9 characters. bpo-36564: https://bugs.python.org/issue36564 (cherry picked from commit f69d5c61981ea97d251db515c7ff280fcc17182d) Co-authored-by: Paul Ganssle files: A Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst M Lib/email/_header_value_parser.py M Lib/email/parser.py M Lib/test/test_email/test_policy.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index d9f592b0da40..64753218a369 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -2825,15 +2825,22 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset): trailing_wsp = to_encode[-1] to_encode = to_encode[:-1] new_last_ew = len(lines[-1]) if last_ew is None else last_ew + + encode_as = 'utf-8' if charset == 'us-ascii' else charset + + # The RFC2047 chrome takes up 7 characters plus the length + # of the charset name. + chrome_len = len(encode_as) + 7 + + if (chrome_len + 1) >= maxlen: + raise errors.HeaderParseError( + "max_line_length is too small to fit an encoded word") + while to_encode: remaining_space = maxlen - len(lines[-1]) - # The RFC2047 chrome takes up 7 characters plus the length - # of the charset name. - encode_as = 'utf-8' if charset == 'us-ascii' else charset - text_space = remaining_space - len(encode_as) - 7 + text_space = remaining_space - chrome_len if text_space <= 0: lines.append(' ') - # XXX We'll get an infinite loop here if maxlen is <= 7 continue to_encode_word = to_encode[:text_space] diff --git a/Lib/email/parser.py b/Lib/email/parser.py index 555b17256061..7db4da1ff081 100644 --- a/Lib/email/parser.py +++ b/Lib/email/parser.py @@ -13,7 +13,6 @@ from email._policybase import compat32 - class Parser: def __init__(self, _class=None, *, policy=compat32): """Parser of RFC 2822 and MIME email messages. diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index 1e39aa062c0a..e87c27554940 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -2,6 +2,7 @@ import types import textwrap import unittest +import email.errors import email.policy import email.parser import email.generator @@ -257,6 +258,25 @@ def test_non_ascii_chars_do_not_cause_inf_loop(self): 'Subject: \n' + 12 * ' =?utf-8?q?=C4=85?=\n') + def test_short_maxlen_error(self): + # RFC 2047 chrome takes up 7 characters, plus the length of the charset + # name, so folding should fail if maxlen is lower than the minimum + # required length for a line. + + # Note: This is only triggered when there is a single word longer than + # max_line_length, hence the 1234567890 at the end of this whimsical + # subject. This is because when we encounter a word longer than + # max_line_length, it is broken down into encoded words to fit + # max_line_length. If the max_line_length isn't large enough to even + # contain the RFC 2047 chrome (`?=?q??=`), we fail. + subject = "Melt away the pounds with this one simple trick! 1234567890" + + for maxlen in [3, 7, 9]: + with self.subTest(maxlen=maxlen): + policy = email.policy.default.clone(max_line_length=maxlen) + with self.assertRaises(email.errors.HeaderParseError): + policy.fold("Subject", subject) + # XXX: Need subclassing tests. # For adding subclassed objects, make sure the usual rules apply (subclass # wins), but that the order still works (right overrides left). diff --git a/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst new file mode 100644 index 000000000000..ddd17aec1dd8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst @@ -0,0 +1,3 @@ +Fix infinite loop in email header folding logic that would be triggered when +an email policy's max_line_length is not long enough to include the required +markup and any values in the message. Patch by Paul Ganssle From webhook-mailer at python.org Tue Jul 16 14:15:47 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 16 Jul 2019 18:15:47 -0000 Subject: [Python-checkins] Fix infinite loop in email folding logic (GH-12732) Message-ID: https://github.com/python/cpython/commit/e7bec26937ce602ca21cc1f78a391adcf5eafdf1 commit: e7bec26937ce602ca21cc1f78a391adcf5eafdf1 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-16T11:15:40-07:00 summary: Fix infinite loop in email folding logic (GH-12732) As far as I can tell, this infinite loop would be triggered if: 1. The value being folded contains a single word (no spaces) longer than max_line_length 2. The max_line_length is shorter than the encoding's name + 9 characters. bpo-36564: https://bugs.python.org/issue36564 (cherry picked from commit f69d5c61981ea97d251db515c7ff280fcc17182d) Co-authored-by: Paul Ganssle files: A Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst M Lib/email/_header_value_parser.py M Lib/email/parser.py M Lib/test/test_email/test_policy.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index 3da3f721f53f..dbb2b6469605 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -2740,15 +2740,22 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset): trailing_wsp = to_encode[-1] to_encode = to_encode[:-1] new_last_ew = len(lines[-1]) if last_ew is None else last_ew + + encode_as = 'utf-8' if charset == 'us-ascii' else charset + + # The RFC2047 chrome takes up 7 characters plus the length + # of the charset name. + chrome_len = len(encode_as) + 7 + + if (chrome_len + 1) >= maxlen: + raise errors.HeaderParseError( + "max_line_length is too small to fit an encoded word") + while to_encode: remaining_space = maxlen - len(lines[-1]) - # The RFC2047 chrome takes up 7 characters plus the length - # of the charset name. - encode_as = 'utf-8' if charset == 'us-ascii' else charset - text_space = remaining_space - len(encode_as) - 7 + text_space = remaining_space - chrome_len if text_space <= 0: lines.append(' ') - # XXX We'll get an infinite loop here if maxlen is <= 7 continue to_encode_word = to_encode[:text_space] diff --git a/Lib/email/parser.py b/Lib/email/parser.py index 555b17256061..7db4da1ff081 100644 --- a/Lib/email/parser.py +++ b/Lib/email/parser.py @@ -13,7 +13,6 @@ from email._policybase import compat32 - class Parser: def __init__(self, _class=None, *, policy=compat32): """Parser of RFC 2822 and MIME email messages. diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index 0aea934df434..ebc3ce6f7682 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -3,6 +3,7 @@ import types import textwrap import unittest +import email.errors import email.policy import email.parser import email.generator @@ -258,6 +259,25 @@ def test_non_ascii_chars_do_not_cause_inf_loop(self): 'Subject: \n' + 12 * ' =?utf-8?q?=C4=85?=\n') + def test_short_maxlen_error(self): + # RFC 2047 chrome takes up 7 characters, plus the length of the charset + # name, so folding should fail if maxlen is lower than the minimum + # required length for a line. + + # Note: This is only triggered when there is a single word longer than + # max_line_length, hence the 1234567890 at the end of this whimsical + # subject. This is because when we encounter a word longer than + # max_line_length, it is broken down into encoded words to fit + # max_line_length. If the max_line_length isn't large enough to even + # contain the RFC 2047 chrome (`?=?q??=`), we fail. + subject = "Melt away the pounds with this one simple trick! 1234567890" + + for maxlen in [3, 7, 9]: + with self.subTest(maxlen=maxlen): + policy = email.policy.default.clone(max_line_length=maxlen) + with self.assertRaises(email.errors.HeaderParseError): + policy.fold("Subject", subject) + # XXX: Need subclassing tests. # For adding subclassed objects, make sure the usual rules apply (subclass # wins), but that the order still works (right overrides left). diff --git a/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst new file mode 100644 index 000000000000..ddd17aec1dd8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst @@ -0,0 +1,3 @@ +Fix infinite loop in email header folding logic that would be triggered when +an email policy's max_line_length is not long enough to include the required +markup and any values in the message. Patch by Paul Ganssle From webhook-mailer at python.org Tue Jul 16 16:58:31 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 16 Jul 2019 20:58:31 -0000 Subject: [Python-checkins] bpo-27452: IDLE: Cleanup config.py code (GH-14577) Message-ID: https://github.com/python/cpython/commit/f8d4cc7dbbf54b9c5435c3080582a4aa421a067d commit: f8d4cc7dbbf54b9c5435c3080582a4aa421a067d branch: master author: Cheryl Sabella committer: Terry Jan Reedy date: 2019-07-16T16:58:25-04:00 summary: bpo-27452: IDLE: Cleanup config.py code (GH-14577) files: A Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst M Lib/idlelib/config.py M Lib/idlelib/idle_test/test_config.py diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 2233dacd66ba..0c55c9a7d75d 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -123,17 +123,11 @@ def IsEmpty(self): self.RemoveEmptySections() return not self.sections() - def RemoveFile(self): - "Remove user config file self.file from disk if it exists." - if os.path.exists(self.file): - os.remove(self.file) - def Save(self): """Update user configuration file. If self not empty after removing empty sections, write the file to disk. Otherwise, remove the file from disk if it exists. - """ fname = self.file if fname: @@ -145,8 +139,8 @@ def Save(self): cfgFile = open(fname, 'w') with cfgFile: self.write(cfgFile) - else: - self.RemoveFile() + elif os.path.exists(self.file): + os.remove(self.file) class IdleConf: """Hold config parsers for all idle config files in singleton instance. @@ -171,24 +165,13 @@ def __init__(self, _utest=False): def CreateConfigHandlers(self): "Populate default and user config parser dictionaries." - #build idle install path - if __name__ != '__main__': # we were imported - idleDir = os.path.dirname(__file__) - else: # we were exec'ed (for testing only) - idleDir = os.path.abspath(sys.path[0]) - self.userdir = userDir = self.GetUserCfgDir() - - defCfgFiles = {} - usrCfgFiles = {} - # TODO eliminate these temporaries by combining loops - for cfgType in self.config_types: #build config file names - defCfgFiles[cfgType] = os.path.join( - idleDir, 'config-' + cfgType + '.def') - usrCfgFiles[cfgType] = os.path.join( - userDir, 'config-' + cfgType + '.cfg') - for cfgType in self.config_types: #create config parsers - self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType]) - self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType]) + idledir = os.path.dirname(__file__) + self.userdir = userdir = self.GetUserCfgDir() + for cfg_type in self.config_types: + self.defaultCfg[cfg_type] = IdleConfParser( + os.path.join(idledir, f'config-{cfg_type}.def')) + self.userCfg[cfg_type] = IdleUserConfParser( + os.path.join(userdir, f'config-{cfg_type}.cfg')) def GetUserCfgDir(self): """Return a filesystem directory for storing user config files. diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index 255210df7d96..492f2f648924 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -159,19 +159,6 @@ def test_is_empty(self): self.assertFalse(parser.IsEmpty()) self.assertCountEqual(parser.sections(), ['Foo']) - def test_remove_file(self): - with tempfile.TemporaryDirectory() as tdir: - path = os.path.join(tdir, 'test.cfg') - parser = self.new_parser(path) - parser.RemoveFile() # Should not raise exception. - - parser.AddSection('Foo') - parser.SetOption('Foo', 'bar', 'true') - parser.Save() - self.assertTrue(os.path.exists(path)) - parser.RemoveFile() - self.assertFalse(os.path.exists(path)) - def test_save(self): with tempfile.TemporaryDirectory() as tdir: path = os.path.join(tdir, 'test.cfg') diff --git a/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst b/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst new file mode 100644 index 000000000000..ddd37bb86505 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst @@ -0,0 +1 @@ +Cleanup ``config.py`` by inlining ``RemoveFile`` and simplifying the handling of ``file`` in ``CreateConfigHandlers``. \ No newline at end of file From webhook-mailer at python.org Tue Jul 16 17:19:17 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 16 Jul 2019 21:19:17 -0000 Subject: [Python-checkins] bpo-27452: IDLE: Cleanup config.py code (GH-14577) (GH-14802) Message-ID: https://github.com/python/cpython/commit/178f09f8b7d25348d46751a774666dd4ec32f3e6 commit: 178f09f8b7d25348d46751a774666dd4ec32f3e6 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-16T17:19:10-04:00 summary: bpo-27452: IDLE: Cleanup config.py code (GH-14577) (GH-14802) (cherry picked from commit f8d4cc7dbbf54b9c5435c3080582a4aa421a067d) Co-authored-by: Cheryl Sabella files: A Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst M Lib/idlelib/config.py M Lib/idlelib/idle_test/test_config.py diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 2233dacd66ba..0c55c9a7d75d 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -123,17 +123,11 @@ def IsEmpty(self): self.RemoveEmptySections() return not self.sections() - def RemoveFile(self): - "Remove user config file self.file from disk if it exists." - if os.path.exists(self.file): - os.remove(self.file) - def Save(self): """Update user configuration file. If self not empty after removing empty sections, write the file to disk. Otherwise, remove the file from disk if it exists. - """ fname = self.file if fname: @@ -145,8 +139,8 @@ def Save(self): cfgFile = open(fname, 'w') with cfgFile: self.write(cfgFile) - else: - self.RemoveFile() + elif os.path.exists(self.file): + os.remove(self.file) class IdleConf: """Hold config parsers for all idle config files in singleton instance. @@ -171,24 +165,13 @@ def __init__(self, _utest=False): def CreateConfigHandlers(self): "Populate default and user config parser dictionaries." - #build idle install path - if __name__ != '__main__': # we were imported - idleDir = os.path.dirname(__file__) - else: # we were exec'ed (for testing only) - idleDir = os.path.abspath(sys.path[0]) - self.userdir = userDir = self.GetUserCfgDir() - - defCfgFiles = {} - usrCfgFiles = {} - # TODO eliminate these temporaries by combining loops - for cfgType in self.config_types: #build config file names - defCfgFiles[cfgType] = os.path.join( - idleDir, 'config-' + cfgType + '.def') - usrCfgFiles[cfgType] = os.path.join( - userDir, 'config-' + cfgType + '.cfg') - for cfgType in self.config_types: #create config parsers - self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType]) - self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType]) + idledir = os.path.dirname(__file__) + self.userdir = userdir = self.GetUserCfgDir() + for cfg_type in self.config_types: + self.defaultCfg[cfg_type] = IdleConfParser( + os.path.join(idledir, f'config-{cfg_type}.def')) + self.userCfg[cfg_type] = IdleUserConfParser( + os.path.join(userdir, f'config-{cfg_type}.cfg')) def GetUserCfgDir(self): """Return a filesystem directory for storing user config files. diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index 255210df7d96..492f2f648924 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -159,19 +159,6 @@ def test_is_empty(self): self.assertFalse(parser.IsEmpty()) self.assertCountEqual(parser.sections(), ['Foo']) - def test_remove_file(self): - with tempfile.TemporaryDirectory() as tdir: - path = os.path.join(tdir, 'test.cfg') - parser = self.new_parser(path) - parser.RemoveFile() # Should not raise exception. - - parser.AddSection('Foo') - parser.SetOption('Foo', 'bar', 'true') - parser.Save() - self.assertTrue(os.path.exists(path)) - parser.RemoveFile() - self.assertFalse(os.path.exists(path)) - def test_save(self): with tempfile.TemporaryDirectory() as tdir: path = os.path.join(tdir, 'test.cfg') diff --git a/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst b/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst new file mode 100644 index 000000000000..ddd37bb86505 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst @@ -0,0 +1 @@ +Cleanup ``config.py`` by inlining ``RemoveFile`` and simplifying the handling of ``file`` in ``CreateConfigHandlers``. \ No newline at end of file From webhook-mailer at python.org Tue Jul 16 17:31:51 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 16 Jul 2019 21:31:51 -0000 Subject: [Python-checkins] bpo-27452: IDLE: Cleanup config.py code (GH-14577) (GH-14803) Message-ID: https://github.com/python/cpython/commit/efd23a199b1415a9850a914d525a1e5711fdd6d8 commit: efd23a199b1415a9850a914d525a1e5711fdd6d8 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-16T17:31:47-04:00 summary: bpo-27452: IDLE: Cleanup config.py code (GH-14577) (GH-14803) (cherry picked from commit f8d4cc7dbbf54b9c5435c3080582a4aa421a067d) Co-authored-by: Cheryl Sabella files: A Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst M Lib/idlelib/config.py M Lib/idlelib/idle_test/test_config.py diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 2233dacd66ba..0c55c9a7d75d 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -123,17 +123,11 @@ def IsEmpty(self): self.RemoveEmptySections() return not self.sections() - def RemoveFile(self): - "Remove user config file self.file from disk if it exists." - if os.path.exists(self.file): - os.remove(self.file) - def Save(self): """Update user configuration file. If self not empty after removing empty sections, write the file to disk. Otherwise, remove the file from disk if it exists. - """ fname = self.file if fname: @@ -145,8 +139,8 @@ def Save(self): cfgFile = open(fname, 'w') with cfgFile: self.write(cfgFile) - else: - self.RemoveFile() + elif os.path.exists(self.file): + os.remove(self.file) class IdleConf: """Hold config parsers for all idle config files in singleton instance. @@ -171,24 +165,13 @@ def __init__(self, _utest=False): def CreateConfigHandlers(self): "Populate default and user config parser dictionaries." - #build idle install path - if __name__ != '__main__': # we were imported - idleDir = os.path.dirname(__file__) - else: # we were exec'ed (for testing only) - idleDir = os.path.abspath(sys.path[0]) - self.userdir = userDir = self.GetUserCfgDir() - - defCfgFiles = {} - usrCfgFiles = {} - # TODO eliminate these temporaries by combining loops - for cfgType in self.config_types: #build config file names - defCfgFiles[cfgType] = os.path.join( - idleDir, 'config-' + cfgType + '.def') - usrCfgFiles[cfgType] = os.path.join( - userDir, 'config-' + cfgType + '.cfg') - for cfgType in self.config_types: #create config parsers - self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType]) - self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType]) + idledir = os.path.dirname(__file__) + self.userdir = userdir = self.GetUserCfgDir() + for cfg_type in self.config_types: + self.defaultCfg[cfg_type] = IdleConfParser( + os.path.join(idledir, f'config-{cfg_type}.def')) + self.userCfg[cfg_type] = IdleUserConfParser( + os.path.join(userdir, f'config-{cfg_type}.cfg')) def GetUserCfgDir(self): """Return a filesystem directory for storing user config files. diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index 255210df7d96..492f2f648924 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -159,19 +159,6 @@ def test_is_empty(self): self.assertFalse(parser.IsEmpty()) self.assertCountEqual(parser.sections(), ['Foo']) - def test_remove_file(self): - with tempfile.TemporaryDirectory() as tdir: - path = os.path.join(tdir, 'test.cfg') - parser = self.new_parser(path) - parser.RemoveFile() # Should not raise exception. - - parser.AddSection('Foo') - parser.SetOption('Foo', 'bar', 'true') - parser.Save() - self.assertTrue(os.path.exists(path)) - parser.RemoveFile() - self.assertFalse(os.path.exists(path)) - def test_save(self): with tempfile.TemporaryDirectory() as tdir: path = os.path.join(tdir, 'test.cfg') diff --git a/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst b/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst new file mode 100644 index 000000000000..ddd37bb86505 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst @@ -0,0 +1 @@ +Cleanup ``config.py`` by inlining ``RemoveFile`` and simplifying the handling of ``file`` in ``CreateConfigHandlers``. \ No newline at end of file From webhook-mailer at python.org Wed Jul 17 04:13:09 2019 From: webhook-mailer at python.org (Tal Einat) Date: Wed, 17 Jul 2019 08:13:09 -0000 Subject: [Python-checkins] Docs: Correct formatting of a multiline code block (GH-13806) Message-ID: https://github.com/python/cpython/commit/bd26a4466b507e196fc9a5e4a6cb7cd6d39f85aa commit: bd26a4466b507e196fc9a5e4a6cb7cd6d39f85aa branch: master author: Joseph Fox-Rabinovitz committer: Tal Einat date: 2019-07-17T11:13:01+03:00 summary: Docs: Correct formatting of a multiline code block (GH-13806) files: M Doc/c-api/buffer.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 72d965053cfd..7553efc2cfbc 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -355,8 +355,10 @@ If :c:member:`~Py_buffer.strides` is *NULL*, the array is interpreted as a standard n-dimensional C-array. Otherwise, the consumer must access an n-dimensional array as follows: - ``ptr = (char *)buf + indices[0] * strides[0] + ... + indices[n-1] * strides[n-1]`` - ``item = *((typeof(item) *)ptr);`` +.. code-block:: c + + ptr = (char *)buf + indices[0] * strides[0] + ... + indices[n-1] * strides[n-1]; + item = *((typeof(item) *)ptr); As noted above, :c:member:`~Py_buffer.buf` can point to any location within From webhook-mailer at python.org Wed Jul 17 04:16:00 2019 From: webhook-mailer at python.org (Tal Einat) Date: Wed, 17 Jul 2019 08:16:00 -0000 Subject: [Python-checkins] bpo-37530: simplify, optimize and clean up IDLE code context (GH-14675) Message-ID: https://github.com/python/cpython/commit/7036e1de3a87d36c7ef41b8a2b44ed6fc4d34be2 commit: 7036e1de3a87d36c7ef41b8a2b44ed6fc4d34be2 branch: master author: Tal Einat committer: GitHub date: 2019-07-17T11:15:53+03:00 summary: bpo-37530: simplify, optimize and clean up IDLE code context (GH-14675) * Only create CodeContext instances for "real" editors windows, but not e.g. shell or output windows. * Remove configuration update Tk event fired every second, by having the editor window ask its code context widget to update when necessary, i.e. upon font or highlighting updates. * When code context isn't being shown, avoid having a Tk event fired every 100ms to check whether the code context needs to be updated. * Use the editor window's getlineno() method where applicable. * Update font of the code context widget before the main text widget files: A Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst M Lib/idlelib/codecontext.py M Lib/idlelib/editor.py M Lib/idlelib/idle_test/test_codecontext.py M Lib/idlelib/outwin.py diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 2aed76de7fb6..9bd0fa1753fc 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -19,8 +19,6 @@ BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for", "if", "try", "while", "with", "async"} -UPDATEINTERVAL = 100 # millisec -CONFIGUPDATEINTERVAL = 1000 # millisec def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")): @@ -44,13 +42,13 @@ def get_line_info(codeline): class CodeContext: "Display block context above the edit window." + UPDATEINTERVAL = 100 # millisec def __init__(self, editwin): """Initialize settings for context block. editwin is the Editor window for the context block. self.text is the editor window text widget. - self.textfont is the editor window font. self.context displays the code context text above the editor text. Initially None, it is toggled via <>. @@ -65,29 +63,26 @@ def __init__(self, editwin): """ self.editwin = editwin self.text = editwin.text - self.textfont = self.text["font"] - self.contextcolors = CodeContext.colors self.context = None self.topvisible = 1 self.info = [(0, -1, "", False)] - # Start two update cycles, one for context lines, one for font changes. - self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event) - self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event) + self.t1 = None @classmethod def reload(cls): "Load class variables from config." cls.context_depth = idleConf.GetOption("extensions", "CodeContext", - "maxlines", type="int", default=15) - cls.colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context') + "maxlines", type="int", + default=15) def __del__(self): "Cancel scheduled events." - try: - self.text.after_cancel(self.t1) - self.text.after_cancel(self.t2) - except: - pass + if self.t1 is not None: + try: + self.text.after_cancel(self.t1) + except tkinter.TclError: + pass + self.t1 = None def toggle_code_context_event(self, event=None): """Toggle code context display. @@ -96,7 +91,7 @@ def toggle_code_context_event(self, event=None): window text (toggle on). If it does exist, destroy it (toggle off). Return 'break' to complete the processing of the binding. """ - if not self.context: + if self.context is None: # Calculate the border width and horizontal padding required to # align the context with the text in the main Text widget. # @@ -111,21 +106,23 @@ def toggle_code_context_event(self, event=None): padx += widget.tk.getint(widget.cget('padx')) border += widget.tk.getint(widget.cget('border')) self.context = tkinter.Text( - self.editwin.top, font=self.textfont, - bg=self.contextcolors['background'], - fg=self.contextcolors['foreground'], - height=1, - width=1, # Don't request more than we get. - padx=padx, border=border, relief=SUNKEN, state='disabled') + self.editwin.top, font=self.text['font'], + height=1, + width=1, # Don't request more than we get. + padx=padx, border=border, relief=SUNKEN, state='disabled') + self.update_highlight_colors() self.context.bind('', self.jumptoline) # Pack the context widget before and above the text_frame widget, # thus ensuring that it will appear directly above text_frame. self.context.pack(side=TOP, fill=X, expand=False, - before=self.editwin.text_frame) + before=self.editwin.text_frame) menu_status = 'Hide' + self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) else: self.context.destroy() self.context = None + self.text.after_cancel(self.t1) + self.t1 = None menu_status = 'Show' self.editwin.update_menu_label(menu='options', index='* Code Context', label=f'{menu_status} Code Context') @@ -169,7 +166,7 @@ def update_code_context(self): be retrieved and the context area will be updated with the code, up to the number of maxlines. """ - new_topvisible = int(self.text.index("@0,0").split('.')[0]) + new_topvisible = self.editwin.getlineno("@0,0") if self.topvisible == new_topvisible: # Haven't scrolled. return if self.topvisible < new_topvisible: # Scroll down. @@ -217,21 +214,19 @@ def jumptoline(self, event=None): def timer_event(self): "Event on editor text widget triggered every UPDATEINTERVAL ms." - if self.context: + if self.context is not None: self.update_code_context() - self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event) - - def config_timer_event(self): - "Event on editor text widget triggered every CONFIGUPDATEINTERVAL ms." - newtextfont = self.text["font"] - if (self.context and (newtextfont != self.textfont or - CodeContext.colors != self.contextcolors)): - self.textfont = newtextfont - self.contextcolors = CodeContext.colors - self.context["font"] = self.textfont - self.context['background'] = self.contextcolors['background'] - self.context['foreground'] = self.contextcolors['foreground'] - self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event) + self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) + + def update_font(self, font): + if self.context is not None: + self.context['font'] = font + + def update_highlight_colors(self): + if self.context is not None: + colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context') + self.context['background'] = colors['background'] + self.context['foreground'] = colors['foreground'] CodeContext.reload() diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 9b5364f0c774..b972e3db8461 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -62,6 +62,8 @@ class EditorWindow(object): filesystemencoding = sys.getfilesystemencoding() # for file names help_url = None + allow_codecontext = True + def __init__(self, flist=None, filename=None, key=None, root=None): # Delay import: runscript imports pyshell imports EditorWindow. from idlelib.runscript import ScriptBinding @@ -247,6 +249,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.good_load = False self.set_indentation_params(False) self.color = None # initialized below in self.ResetColorizer + self.codecontext = None if filename: if os.path.exists(filename) and not os.path.isdir(filename): if io.loadfile(filename): @@ -312,8 +315,10 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", ctip.refresh_calltip_event) text.bind("<>", ctip.force_open_calltip_event) text.bind("<>", self.ZoomHeight(self).zoom_height_event) - text.bind("<>", - self.CodeContext(self).toggle_code_context_event) + if self.allow_codecontext: + self.codecontext = self.CodeContext(self) + text.bind("<>", + self.codecontext.toggle_code_context_event) def _filename_to_unicode(self, filename): """Return filename as BMP unicode so displayable in Tk.""" @@ -773,6 +778,9 @@ def ResetColorizer(self): self._addcolorizer() EditorWindow.color_config(self.text) + if self.codecontext is not None: + self.codecontext.update_highlight_colors() + IDENTCHARS = string.ascii_letters + string.digits + "_" def colorize_syntax_error(self, text, pos): @@ -790,7 +798,12 @@ def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configdialog.py - self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow') + new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow') + # Update the code context widget first, since its height affects + # the height of the text widget. This avoids double re-rendering. + if self.codecontext is not None: + self.codecontext.update_font(new_font) + self.text['font'] = new_font def RemoveKeybindings(self): "Remove the keybindings before they are changed." diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index 6c6893580f42..05d3209db568 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -2,6 +2,7 @@ from idlelib import codecontext import unittest +import unittest.mock from test.support import requires from tkinter import Tk, Frame, Text, TclError @@ -42,6 +43,9 @@ def __init__(self, root, frame, text): self.text = text self.label = '' + def getlineno(self, index): + return int(float(self.text.index(index))) + def update_menu_label(self, **kwargs): self.label = kwargs['label'] @@ -75,6 +79,18 @@ def setUp(self): self.text.yview(0) self.cc = codecontext.CodeContext(self.editor) + self.highlight_cfg = {"background": '#abcdef', + "foreground": '#123456'} + orig_idleConf_GetHighlight = codecontext.idleConf.GetHighlight + def mock_idleconf_GetHighlight(theme, element): + if element == 'context': + return self.highlight_cfg + return orig_idleConf_GetHighlight(theme, element) + patcher = unittest.mock.patch.object( + codecontext.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) + patcher.start() + self.addCleanup(patcher.stop) + def tearDown(self): if self.cc.context: self.cc.context.destroy() @@ -89,30 +105,24 @@ def test_init(self): eq(cc.editwin, ed) eq(cc.text, ed.text) - eq(cc.textfont, ed.text['font']) + eq(cc.text['font'], ed.text['font']) self.assertIsNone(cc.context) eq(cc.info, [(0, -1, '', False)]) eq(cc.topvisible, 1) - eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') - eq(self.root.tk.call('after', 'info', self.cc.t2)[1], 'timer') + self.assertIsNone(self.cc.t1) def test_del(self): self.cc.__del__() - with self.assertRaises(TclError) as msg: - self.root.tk.call('after', 'info', self.cc.t1) - self.assertIn("doesn't exist", msg) - with self.assertRaises(TclError) as msg: - self.root.tk.call('after', 'info', self.cc.t2) - self.assertIn("doesn't exist", msg) - # For coverage on the except. Have to delete because the - # above Tcl error is caught by after_cancel. - del self.cc.t1, self.cc.t2 + + def test_del_with_timer(self): + timer = self.cc.t1 = self.text.after(10000, lambda: None) self.cc.__del__() + with self.assertRaises(TclError) as cm: + self.root.tk.call('after', 'info', timer) + self.assertIn("doesn't exist", str(cm.exception)) def test_reload(self): codecontext.CodeContext.reload() - self.assertEqual(self.cc.colors, {'background': 'lightgray', - 'foreground': '#000000'}) self.assertEqual(self.cc.context_depth, 15) def test_toggle_code_context_event(self): @@ -127,16 +137,18 @@ def test_toggle_code_context_event(self): # Toggle on. eq(toggle(), 'break') self.assertIsNotNone(cc.context) - eq(cc.context['font'], cc.textfont) - eq(cc.context['fg'], cc.colors['foreground']) - eq(cc.context['bg'], cc.colors['background']) + eq(cc.context['font'], self.text['font']) + eq(cc.context['fg'], self.highlight_cfg['foreground']) + eq(cc.context['bg'], self.highlight_cfg['background']) eq(cc.context.get('1.0', 'end-1c'), '') eq(cc.editwin.label, 'Hide Code Context') + eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') # Toggle off. eq(toggle(), 'break') self.assertIsNone(cc.context) eq(cc.editwin.label, 'Show Code Context') + self.assertIsNone(self.cc.t1) def test_get_context(self): eq = self.assertEqual @@ -227,7 +239,7 @@ def test_update_code_context(self): (4, 4, ' def __init__(self, a, b):', 'def')]) eq(cc.topvisible, 5) eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' - ' def __init__(self, a, b):') + ' def __init__(self, a, b):') # Scroll down to line 11. Last 'def' is removed. cc.text.yview(11) @@ -239,9 +251,9 @@ def test_update_code_context(self): (10, 8, ' elif a < b:', 'elif')]) eq(cc.topvisible, 12) eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' - ' def compare(self):\n' - ' if a > b:\n' - ' elif a < b:') + ' def compare(self):\n' + ' if a > b:\n' + ' elif a < b:') # No scroll. No update, even though context_depth changed. cc.update_code_context() @@ -253,9 +265,9 @@ def test_update_code_context(self): (10, 8, ' elif a < b:', 'elif')]) eq(cc.topvisible, 12) eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' - ' def compare(self):\n' - ' if a > b:\n' - ' elif a < b:') + ' def compare(self):\n' + ' if a > b:\n' + ' elif a < b:') # Scroll up. cc.text.yview(5) @@ -276,7 +288,7 @@ def test_jumptoline(self): cc.toggle_code_context_event() # Empty context. - cc.text.yview(f'{2}.0') + cc.text.yview('2.0') cc.update_code_context() eq(cc.topvisible, 2) cc.context.mark_set('insert', '1.5') @@ -284,7 +296,7 @@ def test_jumptoline(self): eq(cc.topvisible, 1) # 4 lines of context showing. - cc.text.yview(f'{12}.0') + cc.text.yview('12.0') cc.update_code_context() eq(cc.topvisible, 12) cc.context.mark_set('insert', '3.0') @@ -293,7 +305,7 @@ def test_jumptoline(self): # More context lines than limit. cc.context_depth = 2 - cc.text.yview(f'{12}.0') + cc.text.yview('12.0') cc.update_code_context() eq(cc.topvisible, 12) cc.context.mark_set('insert', '1.0') @@ -313,56 +325,72 @@ def test_timer_event(self, mock_update): self.cc.timer_event() mock_update.assert_called() - def test_config_timer_event(self): + def test_font(self): eq = self.assertEqual cc = self.cc save_font = cc.text['font'] - save_colors = codecontext.CodeContext.colors - test_font = 'FakeFont' + test_font = 'TkFixedFont' + + # Ensure code context is not active. + if cc.context is not None: + cc.toggle_code_context_event() + + # Nothing breaks or changes with inactive code context. + cc.update_font(test_font) + + # Activate code context, but no change to font. + cc.toggle_code_context_event() + eq(cc.context['font'], save_font) + # Call font update with the existing font. + cc.update_font(save_font) + eq(cc.context['font'], save_font) + cc.toggle_code_context_event() + + # Change text widget font and activate code context. + cc.text['font'] = test_font + cc.toggle_code_context_event(test_font) + eq(cc.context['font'], test_font) + + # Just call the font update. + cc.update_font(save_font) + eq(cc.context['font'], save_font) + cc.text['font'] = save_font + + def test_highlight_colors(self): + eq = self.assertEqual + cc = self.cc + save_colors = dict(self.highlight_cfg) test_colors = {'background': '#222222', 'foreground': '#ffff00'} # Ensure code context is not active. if cc.context: cc.toggle_code_context_event() - # Nothing updates on inactive code context. - cc.text['font'] = test_font - codecontext.CodeContext.colors = test_colors - cc.config_timer_event() - eq(cc.textfont, save_font) - eq(cc.contextcolors, save_colors) + # Nothing breaks with inactive code context. + cc.update_highlight_colors() - # Activate code context, but no change to font or color. + # Activate code context, but no change to colors. cc.toggle_code_context_event() - cc.text['font'] = save_font - codecontext.CodeContext.colors = save_colors - cc.config_timer_event() - eq(cc.textfont, save_font) - eq(cc.contextcolors, save_colors) - eq(cc.context['font'], save_font) eq(cc.context['background'], save_colors['background']) eq(cc.context['foreground'], save_colors['foreground']) - # Active code context, change font. - cc.text['font'] = test_font - cc.config_timer_event() - eq(cc.textfont, test_font) - eq(cc.contextcolors, save_colors) - eq(cc.context['font'], test_font) + # Call colors update, but no change to font. + cc.update_highlight_colors() eq(cc.context['background'], save_colors['background']) eq(cc.context['foreground'], save_colors['foreground']) + cc.toggle_code_context_event() - # Active code context, change color. - cc.text['font'] = save_font - codecontext.CodeContext.colors = test_colors - cc.config_timer_event() - eq(cc.textfont, save_font) - eq(cc.contextcolors, test_colors) - eq(cc.context['font'], save_font) + # Change colors and activate code context. + self.highlight_cfg = test_colors + cc.toggle_code_context_event() eq(cc.context['background'], test_colors['background']) eq(cc.context['foreground'], test_colors['foreground']) - codecontext.CodeContext.colors = save_colors - cc.config_timer_event() + + # Change colors and call highlight colors update. + self.highlight_cfg = save_colors + cc.update_highlight_colors() + eq(cc.context['background'], save_colors['background']) + eq(cc.context['foreground'], save_colors['foreground']) class HelperFunctionText(unittest.TestCase): diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index ecc53ef0195d..38c59bdf9b4e 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -74,6 +74,8 @@ class OutputWindow(EditorWindow): ("Go to file/line", "<>", None), ] + allow_codecontext = False + def __init__(self, *args): EditorWindow.__init__(self, *args) self.text.bind("<>", self.goto_file_line) diff --git a/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst b/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst new file mode 100644 index 000000000000..0b80860b8fc2 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst @@ -0,0 +1,3 @@ +Optimize code context to reduce unneeded background activity. +Font and highlight changes now occur along with text changes +instead of after a random delay. From webhook-mailer at python.org Wed Jul 17 04:32:57 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 17 Jul 2019 08:32:57 -0000 Subject: [Python-checkins] Docs: Correct formatting of a multiline code block (GH-13806) Message-ID: https://github.com/python/cpython/commit/35d36dacd410af96d794277c5ae6986d95d50e5a commit: 35d36dacd410af96d794277c5ae6986d95d50e5a branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-17T01:32:53-07:00 summary: Docs: Correct formatting of a multiline code block (GH-13806) (cherry picked from commit bd26a4466b507e196fc9a5e4a6cb7cd6d39f85aa) Co-authored-by: Joseph Fox-Rabinovitz files: M Doc/c-api/buffer.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 72d965053cfd..7553efc2cfbc 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -355,8 +355,10 @@ If :c:member:`~Py_buffer.strides` is *NULL*, the array is interpreted as a standard n-dimensional C-array. Otherwise, the consumer must access an n-dimensional array as follows: - ``ptr = (char *)buf + indices[0] * strides[0] + ... + indices[n-1] * strides[n-1]`` - ``item = *((typeof(item) *)ptr);`` +.. code-block:: c + + ptr = (char *)buf + indices[0] * strides[0] + ... + indices[n-1] * strides[n-1]; + item = *((typeof(item) *)ptr); As noted above, :c:member:`~Py_buffer.buf` can point to any location within From webhook-mailer at python.org Wed Jul 17 04:33:03 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 17 Jul 2019 08:33:03 -0000 Subject: [Python-checkins] Docs: Correct formatting of a multiline code block (GH-13806) Message-ID: https://github.com/python/cpython/commit/ba3c89f42e3935924a4a9a3278cee994f5bb7aee commit: ba3c89f42e3935924a4a9a3278cee994f5bb7aee branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-17T01:32:58-07:00 summary: Docs: Correct formatting of a multiline code block (GH-13806) (cherry picked from commit bd26a4466b507e196fc9a5e4a6cb7cd6d39f85aa) Co-authored-by: Joseph Fox-Rabinovitz files: M Doc/c-api/buffer.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index c7c1e3cc745a..1d4ac457e54c 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -355,8 +355,10 @@ If :c:member:`~Py_buffer.strides` is *NULL*, the array is interpreted as a standard n-dimensional C-array. Otherwise, the consumer must access an n-dimensional array as follows: - ``ptr = (char *)buf + indices[0] * strides[0] + ... + indices[n-1] * strides[n-1]`` - ``item = *((typeof(item) *)ptr);`` +.. code-block:: c + + ptr = (char *)buf + indices[0] * strides[0] + ... + indices[n-1] * strides[n-1]; + item = *((typeof(item) *)ptr); As noted above, :c:member:`~Py_buffer.buf` can point to any location within From webhook-mailer at python.org Wed Jul 17 04:44:30 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 17 Jul 2019 08:44:30 -0000 Subject: [Python-checkins] bpo-37530: simplify, optimize and clean up IDLE code context (GH-14675) Message-ID: https://github.com/python/cpython/commit/bb79ab84c258566bcba89a87eb549fbc8643f882 commit: bb79ab84c258566bcba89a87eb549fbc8643f882 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-17T01:44:18-07:00 summary: bpo-37530: simplify, optimize and clean up IDLE code context (GH-14675) * Only create CodeContext instances for "real" editors windows, but not e.g. shell or output windows. * Remove configuration update Tk event fired every second, by having the editor window ask its code context widget to update when necessary, i.e. upon font or highlighting updates. * When code context isn't being shown, avoid having a Tk event fired every 100ms to check whether the code context needs to be updated. * Use the editor window's getlineno() method where applicable. * Update font of the code context widget before the main text widget (cherry picked from commit 7036e1de3a87d36c7ef41b8a2b44ed6fc4d34be2) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst M Lib/idlelib/codecontext.py M Lib/idlelib/editor.py M Lib/idlelib/idle_test/test_codecontext.py M Lib/idlelib/outwin.py diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 2aed76de7fb6..9bd0fa1753fc 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -19,8 +19,6 @@ BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for", "if", "try", "while", "with", "async"} -UPDATEINTERVAL = 100 # millisec -CONFIGUPDATEINTERVAL = 1000 # millisec def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")): @@ -44,13 +42,13 @@ def get_line_info(codeline): class CodeContext: "Display block context above the edit window." + UPDATEINTERVAL = 100 # millisec def __init__(self, editwin): """Initialize settings for context block. editwin is the Editor window for the context block. self.text is the editor window text widget. - self.textfont is the editor window font. self.context displays the code context text above the editor text. Initially None, it is toggled via <>. @@ -65,29 +63,26 @@ def __init__(self, editwin): """ self.editwin = editwin self.text = editwin.text - self.textfont = self.text["font"] - self.contextcolors = CodeContext.colors self.context = None self.topvisible = 1 self.info = [(0, -1, "", False)] - # Start two update cycles, one for context lines, one for font changes. - self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event) - self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event) + self.t1 = None @classmethod def reload(cls): "Load class variables from config." cls.context_depth = idleConf.GetOption("extensions", "CodeContext", - "maxlines", type="int", default=15) - cls.colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context') + "maxlines", type="int", + default=15) def __del__(self): "Cancel scheduled events." - try: - self.text.after_cancel(self.t1) - self.text.after_cancel(self.t2) - except: - pass + if self.t1 is not None: + try: + self.text.after_cancel(self.t1) + except tkinter.TclError: + pass + self.t1 = None def toggle_code_context_event(self, event=None): """Toggle code context display. @@ -96,7 +91,7 @@ def toggle_code_context_event(self, event=None): window text (toggle on). If it does exist, destroy it (toggle off). Return 'break' to complete the processing of the binding. """ - if not self.context: + if self.context is None: # Calculate the border width and horizontal padding required to # align the context with the text in the main Text widget. # @@ -111,21 +106,23 @@ def toggle_code_context_event(self, event=None): padx += widget.tk.getint(widget.cget('padx')) border += widget.tk.getint(widget.cget('border')) self.context = tkinter.Text( - self.editwin.top, font=self.textfont, - bg=self.contextcolors['background'], - fg=self.contextcolors['foreground'], - height=1, - width=1, # Don't request more than we get. - padx=padx, border=border, relief=SUNKEN, state='disabled') + self.editwin.top, font=self.text['font'], + height=1, + width=1, # Don't request more than we get. + padx=padx, border=border, relief=SUNKEN, state='disabled') + self.update_highlight_colors() self.context.bind('', self.jumptoline) # Pack the context widget before and above the text_frame widget, # thus ensuring that it will appear directly above text_frame. self.context.pack(side=TOP, fill=X, expand=False, - before=self.editwin.text_frame) + before=self.editwin.text_frame) menu_status = 'Hide' + self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) else: self.context.destroy() self.context = None + self.text.after_cancel(self.t1) + self.t1 = None menu_status = 'Show' self.editwin.update_menu_label(menu='options', index='* Code Context', label=f'{menu_status} Code Context') @@ -169,7 +166,7 @@ def update_code_context(self): be retrieved and the context area will be updated with the code, up to the number of maxlines. """ - new_topvisible = int(self.text.index("@0,0").split('.')[0]) + new_topvisible = self.editwin.getlineno("@0,0") if self.topvisible == new_topvisible: # Haven't scrolled. return if self.topvisible < new_topvisible: # Scroll down. @@ -217,21 +214,19 @@ def jumptoline(self, event=None): def timer_event(self): "Event on editor text widget triggered every UPDATEINTERVAL ms." - if self.context: + if self.context is not None: self.update_code_context() - self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event) - - def config_timer_event(self): - "Event on editor text widget triggered every CONFIGUPDATEINTERVAL ms." - newtextfont = self.text["font"] - if (self.context and (newtextfont != self.textfont or - CodeContext.colors != self.contextcolors)): - self.textfont = newtextfont - self.contextcolors = CodeContext.colors - self.context["font"] = self.textfont - self.context['background'] = self.contextcolors['background'] - self.context['foreground'] = self.contextcolors['foreground'] - self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event) + self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) + + def update_font(self, font): + if self.context is not None: + self.context['font'] = font + + def update_highlight_colors(self): + if self.context is not None: + colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context') + self.context['background'] = colors['background'] + self.context['foreground'] = colors['foreground'] CodeContext.reload() diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 9b5364f0c774..b972e3db8461 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -62,6 +62,8 @@ class EditorWindow(object): filesystemencoding = sys.getfilesystemencoding() # for file names help_url = None + allow_codecontext = True + def __init__(self, flist=None, filename=None, key=None, root=None): # Delay import: runscript imports pyshell imports EditorWindow. from idlelib.runscript import ScriptBinding @@ -247,6 +249,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.good_load = False self.set_indentation_params(False) self.color = None # initialized below in self.ResetColorizer + self.codecontext = None if filename: if os.path.exists(filename) and not os.path.isdir(filename): if io.loadfile(filename): @@ -312,8 +315,10 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", ctip.refresh_calltip_event) text.bind("<>", ctip.force_open_calltip_event) text.bind("<>", self.ZoomHeight(self).zoom_height_event) - text.bind("<>", - self.CodeContext(self).toggle_code_context_event) + if self.allow_codecontext: + self.codecontext = self.CodeContext(self) + text.bind("<>", + self.codecontext.toggle_code_context_event) def _filename_to_unicode(self, filename): """Return filename as BMP unicode so displayable in Tk.""" @@ -773,6 +778,9 @@ def ResetColorizer(self): self._addcolorizer() EditorWindow.color_config(self.text) + if self.codecontext is not None: + self.codecontext.update_highlight_colors() + IDENTCHARS = string.ascii_letters + string.digits + "_" def colorize_syntax_error(self, text, pos): @@ -790,7 +798,12 @@ def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configdialog.py - self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow') + new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow') + # Update the code context widget first, since its height affects + # the height of the text widget. This avoids double re-rendering. + if self.codecontext is not None: + self.codecontext.update_font(new_font) + self.text['font'] = new_font def RemoveKeybindings(self): "Remove the keybindings before they are changed." diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index 6c6893580f42..05d3209db568 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -2,6 +2,7 @@ from idlelib import codecontext import unittest +import unittest.mock from test.support import requires from tkinter import Tk, Frame, Text, TclError @@ -42,6 +43,9 @@ def __init__(self, root, frame, text): self.text = text self.label = '' + def getlineno(self, index): + return int(float(self.text.index(index))) + def update_menu_label(self, **kwargs): self.label = kwargs['label'] @@ -75,6 +79,18 @@ def setUp(self): self.text.yview(0) self.cc = codecontext.CodeContext(self.editor) + self.highlight_cfg = {"background": '#abcdef', + "foreground": '#123456'} + orig_idleConf_GetHighlight = codecontext.idleConf.GetHighlight + def mock_idleconf_GetHighlight(theme, element): + if element == 'context': + return self.highlight_cfg + return orig_idleConf_GetHighlight(theme, element) + patcher = unittest.mock.patch.object( + codecontext.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) + patcher.start() + self.addCleanup(patcher.stop) + def tearDown(self): if self.cc.context: self.cc.context.destroy() @@ -89,30 +105,24 @@ def test_init(self): eq(cc.editwin, ed) eq(cc.text, ed.text) - eq(cc.textfont, ed.text['font']) + eq(cc.text['font'], ed.text['font']) self.assertIsNone(cc.context) eq(cc.info, [(0, -1, '', False)]) eq(cc.topvisible, 1) - eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') - eq(self.root.tk.call('after', 'info', self.cc.t2)[1], 'timer') + self.assertIsNone(self.cc.t1) def test_del(self): self.cc.__del__() - with self.assertRaises(TclError) as msg: - self.root.tk.call('after', 'info', self.cc.t1) - self.assertIn("doesn't exist", msg) - with self.assertRaises(TclError) as msg: - self.root.tk.call('after', 'info', self.cc.t2) - self.assertIn("doesn't exist", msg) - # For coverage on the except. Have to delete because the - # above Tcl error is caught by after_cancel. - del self.cc.t1, self.cc.t2 + + def test_del_with_timer(self): + timer = self.cc.t1 = self.text.after(10000, lambda: None) self.cc.__del__() + with self.assertRaises(TclError) as cm: + self.root.tk.call('after', 'info', timer) + self.assertIn("doesn't exist", str(cm.exception)) def test_reload(self): codecontext.CodeContext.reload() - self.assertEqual(self.cc.colors, {'background': 'lightgray', - 'foreground': '#000000'}) self.assertEqual(self.cc.context_depth, 15) def test_toggle_code_context_event(self): @@ -127,16 +137,18 @@ def test_toggle_code_context_event(self): # Toggle on. eq(toggle(), 'break') self.assertIsNotNone(cc.context) - eq(cc.context['font'], cc.textfont) - eq(cc.context['fg'], cc.colors['foreground']) - eq(cc.context['bg'], cc.colors['background']) + eq(cc.context['font'], self.text['font']) + eq(cc.context['fg'], self.highlight_cfg['foreground']) + eq(cc.context['bg'], self.highlight_cfg['background']) eq(cc.context.get('1.0', 'end-1c'), '') eq(cc.editwin.label, 'Hide Code Context') + eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') # Toggle off. eq(toggle(), 'break') self.assertIsNone(cc.context) eq(cc.editwin.label, 'Show Code Context') + self.assertIsNone(self.cc.t1) def test_get_context(self): eq = self.assertEqual @@ -227,7 +239,7 @@ def test_update_code_context(self): (4, 4, ' def __init__(self, a, b):', 'def')]) eq(cc.topvisible, 5) eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' - ' def __init__(self, a, b):') + ' def __init__(self, a, b):') # Scroll down to line 11. Last 'def' is removed. cc.text.yview(11) @@ -239,9 +251,9 @@ def test_update_code_context(self): (10, 8, ' elif a < b:', 'elif')]) eq(cc.topvisible, 12) eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' - ' def compare(self):\n' - ' if a > b:\n' - ' elif a < b:') + ' def compare(self):\n' + ' if a > b:\n' + ' elif a < b:') # No scroll. No update, even though context_depth changed. cc.update_code_context() @@ -253,9 +265,9 @@ def test_update_code_context(self): (10, 8, ' elif a < b:', 'elif')]) eq(cc.topvisible, 12) eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' - ' def compare(self):\n' - ' if a > b:\n' - ' elif a < b:') + ' def compare(self):\n' + ' if a > b:\n' + ' elif a < b:') # Scroll up. cc.text.yview(5) @@ -276,7 +288,7 @@ def test_jumptoline(self): cc.toggle_code_context_event() # Empty context. - cc.text.yview(f'{2}.0') + cc.text.yview('2.0') cc.update_code_context() eq(cc.topvisible, 2) cc.context.mark_set('insert', '1.5') @@ -284,7 +296,7 @@ def test_jumptoline(self): eq(cc.topvisible, 1) # 4 lines of context showing. - cc.text.yview(f'{12}.0') + cc.text.yview('12.0') cc.update_code_context() eq(cc.topvisible, 12) cc.context.mark_set('insert', '3.0') @@ -293,7 +305,7 @@ def test_jumptoline(self): # More context lines than limit. cc.context_depth = 2 - cc.text.yview(f'{12}.0') + cc.text.yview('12.0') cc.update_code_context() eq(cc.topvisible, 12) cc.context.mark_set('insert', '1.0') @@ -313,56 +325,72 @@ def test_timer_event(self, mock_update): self.cc.timer_event() mock_update.assert_called() - def test_config_timer_event(self): + def test_font(self): eq = self.assertEqual cc = self.cc save_font = cc.text['font'] - save_colors = codecontext.CodeContext.colors - test_font = 'FakeFont' + test_font = 'TkFixedFont' + + # Ensure code context is not active. + if cc.context is not None: + cc.toggle_code_context_event() + + # Nothing breaks or changes with inactive code context. + cc.update_font(test_font) + + # Activate code context, but no change to font. + cc.toggle_code_context_event() + eq(cc.context['font'], save_font) + # Call font update with the existing font. + cc.update_font(save_font) + eq(cc.context['font'], save_font) + cc.toggle_code_context_event() + + # Change text widget font and activate code context. + cc.text['font'] = test_font + cc.toggle_code_context_event(test_font) + eq(cc.context['font'], test_font) + + # Just call the font update. + cc.update_font(save_font) + eq(cc.context['font'], save_font) + cc.text['font'] = save_font + + def test_highlight_colors(self): + eq = self.assertEqual + cc = self.cc + save_colors = dict(self.highlight_cfg) test_colors = {'background': '#222222', 'foreground': '#ffff00'} # Ensure code context is not active. if cc.context: cc.toggle_code_context_event() - # Nothing updates on inactive code context. - cc.text['font'] = test_font - codecontext.CodeContext.colors = test_colors - cc.config_timer_event() - eq(cc.textfont, save_font) - eq(cc.contextcolors, save_colors) + # Nothing breaks with inactive code context. + cc.update_highlight_colors() - # Activate code context, but no change to font or color. + # Activate code context, but no change to colors. cc.toggle_code_context_event() - cc.text['font'] = save_font - codecontext.CodeContext.colors = save_colors - cc.config_timer_event() - eq(cc.textfont, save_font) - eq(cc.contextcolors, save_colors) - eq(cc.context['font'], save_font) eq(cc.context['background'], save_colors['background']) eq(cc.context['foreground'], save_colors['foreground']) - # Active code context, change font. - cc.text['font'] = test_font - cc.config_timer_event() - eq(cc.textfont, test_font) - eq(cc.contextcolors, save_colors) - eq(cc.context['font'], test_font) + # Call colors update, but no change to font. + cc.update_highlight_colors() eq(cc.context['background'], save_colors['background']) eq(cc.context['foreground'], save_colors['foreground']) + cc.toggle_code_context_event() - # Active code context, change color. - cc.text['font'] = save_font - codecontext.CodeContext.colors = test_colors - cc.config_timer_event() - eq(cc.textfont, save_font) - eq(cc.contextcolors, test_colors) - eq(cc.context['font'], save_font) + # Change colors and activate code context. + self.highlight_cfg = test_colors + cc.toggle_code_context_event() eq(cc.context['background'], test_colors['background']) eq(cc.context['foreground'], test_colors['foreground']) - codecontext.CodeContext.colors = save_colors - cc.config_timer_event() + + # Change colors and call highlight colors update. + self.highlight_cfg = save_colors + cc.update_highlight_colors() + eq(cc.context['background'], save_colors['background']) + eq(cc.context['foreground'], save_colors['foreground']) class HelperFunctionText(unittest.TestCase): diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index ecc53ef0195d..38c59bdf9b4e 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -74,6 +74,8 @@ class OutputWindow(EditorWindow): ("Go to file/line", "<>", None), ] + allow_codecontext = False + def __init__(self, *args): EditorWindow.__init__(self, *args) self.text.bind("<>", self.goto_file_line) diff --git a/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst b/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst new file mode 100644 index 000000000000..0b80860b8fc2 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst @@ -0,0 +1,3 @@ +Optimize code context to reduce unneeded background activity. +Font and highlight changes now occur along with text changes +instead of after a random delay. From webhook-mailer at python.org Wed Jul 17 04:44:59 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 17 Jul 2019 08:44:59 -0000 Subject: [Python-checkins] bpo-37530: simplify, optimize and clean up IDLE code context (GH-14675) Message-ID: https://github.com/python/cpython/commit/6cf7c45cb5691b75d9a774c904df02a4f8bfcd04 commit: 6cf7c45cb5691b75d9a774c904df02a4f8bfcd04 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-17T01:44:55-07:00 summary: bpo-37530: simplify, optimize and clean up IDLE code context (GH-14675) * Only create CodeContext instances for "real" editors windows, but not e.g. shell or output windows. * Remove configuration update Tk event fired every second, by having the editor window ask its code context widget to update when necessary, i.e. upon font or highlighting updates. * When code context isn't being shown, avoid having a Tk event fired every 100ms to check whether the code context needs to be updated. * Use the editor window's getlineno() method where applicable. * Update font of the code context widget before the main text widget (cherry picked from commit 7036e1de3a87d36c7ef41b8a2b44ed6fc4d34be2) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst M Lib/idlelib/codecontext.py M Lib/idlelib/editor.py M Lib/idlelib/idle_test/test_codecontext.py M Lib/idlelib/outwin.py diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 2aed76de7fb6..9bd0fa1753fc 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -19,8 +19,6 @@ BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for", "if", "try", "while", "with", "async"} -UPDATEINTERVAL = 100 # millisec -CONFIGUPDATEINTERVAL = 1000 # millisec def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")): @@ -44,13 +42,13 @@ def get_line_info(codeline): class CodeContext: "Display block context above the edit window." + UPDATEINTERVAL = 100 # millisec def __init__(self, editwin): """Initialize settings for context block. editwin is the Editor window for the context block. self.text is the editor window text widget. - self.textfont is the editor window font. self.context displays the code context text above the editor text. Initially None, it is toggled via <>. @@ -65,29 +63,26 @@ def __init__(self, editwin): """ self.editwin = editwin self.text = editwin.text - self.textfont = self.text["font"] - self.contextcolors = CodeContext.colors self.context = None self.topvisible = 1 self.info = [(0, -1, "", False)] - # Start two update cycles, one for context lines, one for font changes. - self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event) - self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event) + self.t1 = None @classmethod def reload(cls): "Load class variables from config." cls.context_depth = idleConf.GetOption("extensions", "CodeContext", - "maxlines", type="int", default=15) - cls.colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context') + "maxlines", type="int", + default=15) def __del__(self): "Cancel scheduled events." - try: - self.text.after_cancel(self.t1) - self.text.after_cancel(self.t2) - except: - pass + if self.t1 is not None: + try: + self.text.after_cancel(self.t1) + except tkinter.TclError: + pass + self.t1 = None def toggle_code_context_event(self, event=None): """Toggle code context display. @@ -96,7 +91,7 @@ def toggle_code_context_event(self, event=None): window text (toggle on). If it does exist, destroy it (toggle off). Return 'break' to complete the processing of the binding. """ - if not self.context: + if self.context is None: # Calculate the border width and horizontal padding required to # align the context with the text in the main Text widget. # @@ -111,21 +106,23 @@ def toggle_code_context_event(self, event=None): padx += widget.tk.getint(widget.cget('padx')) border += widget.tk.getint(widget.cget('border')) self.context = tkinter.Text( - self.editwin.top, font=self.textfont, - bg=self.contextcolors['background'], - fg=self.contextcolors['foreground'], - height=1, - width=1, # Don't request more than we get. - padx=padx, border=border, relief=SUNKEN, state='disabled') + self.editwin.top, font=self.text['font'], + height=1, + width=1, # Don't request more than we get. + padx=padx, border=border, relief=SUNKEN, state='disabled') + self.update_highlight_colors() self.context.bind('', self.jumptoline) # Pack the context widget before and above the text_frame widget, # thus ensuring that it will appear directly above text_frame. self.context.pack(side=TOP, fill=X, expand=False, - before=self.editwin.text_frame) + before=self.editwin.text_frame) menu_status = 'Hide' + self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) else: self.context.destroy() self.context = None + self.text.after_cancel(self.t1) + self.t1 = None menu_status = 'Show' self.editwin.update_menu_label(menu='options', index='* Code Context', label=f'{menu_status} Code Context') @@ -169,7 +166,7 @@ def update_code_context(self): be retrieved and the context area will be updated with the code, up to the number of maxlines. """ - new_topvisible = int(self.text.index("@0,0").split('.')[0]) + new_topvisible = self.editwin.getlineno("@0,0") if self.topvisible == new_topvisible: # Haven't scrolled. return if self.topvisible < new_topvisible: # Scroll down. @@ -217,21 +214,19 @@ def jumptoline(self, event=None): def timer_event(self): "Event on editor text widget triggered every UPDATEINTERVAL ms." - if self.context: + if self.context is not None: self.update_code_context() - self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event) - - def config_timer_event(self): - "Event on editor text widget triggered every CONFIGUPDATEINTERVAL ms." - newtextfont = self.text["font"] - if (self.context and (newtextfont != self.textfont or - CodeContext.colors != self.contextcolors)): - self.textfont = newtextfont - self.contextcolors = CodeContext.colors - self.context["font"] = self.textfont - self.context['background'] = self.contextcolors['background'] - self.context['foreground'] = self.contextcolors['foreground'] - self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event) + self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) + + def update_font(self, font): + if self.context is not None: + self.context['font'] = font + + def update_highlight_colors(self): + if self.context is not None: + colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context') + self.context['background'] = colors['background'] + self.context['foreground'] = colors['foreground'] CodeContext.reload() diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 9b5364f0c774..b972e3db8461 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -62,6 +62,8 @@ class EditorWindow(object): filesystemencoding = sys.getfilesystemencoding() # for file names help_url = None + allow_codecontext = True + def __init__(self, flist=None, filename=None, key=None, root=None): # Delay import: runscript imports pyshell imports EditorWindow. from idlelib.runscript import ScriptBinding @@ -247,6 +249,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.good_load = False self.set_indentation_params(False) self.color = None # initialized below in self.ResetColorizer + self.codecontext = None if filename: if os.path.exists(filename) and not os.path.isdir(filename): if io.loadfile(filename): @@ -312,8 +315,10 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", ctip.refresh_calltip_event) text.bind("<>", ctip.force_open_calltip_event) text.bind("<>", self.ZoomHeight(self).zoom_height_event) - text.bind("<>", - self.CodeContext(self).toggle_code_context_event) + if self.allow_codecontext: + self.codecontext = self.CodeContext(self) + text.bind("<>", + self.codecontext.toggle_code_context_event) def _filename_to_unicode(self, filename): """Return filename as BMP unicode so displayable in Tk.""" @@ -773,6 +778,9 @@ def ResetColorizer(self): self._addcolorizer() EditorWindow.color_config(self.text) + if self.codecontext is not None: + self.codecontext.update_highlight_colors() + IDENTCHARS = string.ascii_letters + string.digits + "_" def colorize_syntax_error(self, text, pos): @@ -790,7 +798,12 @@ def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configdialog.py - self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow') + new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow') + # Update the code context widget first, since its height affects + # the height of the text widget. This avoids double re-rendering. + if self.codecontext is not None: + self.codecontext.update_font(new_font) + self.text['font'] = new_font def RemoveKeybindings(self): "Remove the keybindings before they are changed." diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index 6c6893580f42..05d3209db568 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -2,6 +2,7 @@ from idlelib import codecontext import unittest +import unittest.mock from test.support import requires from tkinter import Tk, Frame, Text, TclError @@ -42,6 +43,9 @@ def __init__(self, root, frame, text): self.text = text self.label = '' + def getlineno(self, index): + return int(float(self.text.index(index))) + def update_menu_label(self, **kwargs): self.label = kwargs['label'] @@ -75,6 +79,18 @@ def setUp(self): self.text.yview(0) self.cc = codecontext.CodeContext(self.editor) + self.highlight_cfg = {"background": '#abcdef', + "foreground": '#123456'} + orig_idleConf_GetHighlight = codecontext.idleConf.GetHighlight + def mock_idleconf_GetHighlight(theme, element): + if element == 'context': + return self.highlight_cfg + return orig_idleConf_GetHighlight(theme, element) + patcher = unittest.mock.patch.object( + codecontext.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) + patcher.start() + self.addCleanup(patcher.stop) + def tearDown(self): if self.cc.context: self.cc.context.destroy() @@ -89,30 +105,24 @@ def test_init(self): eq(cc.editwin, ed) eq(cc.text, ed.text) - eq(cc.textfont, ed.text['font']) + eq(cc.text['font'], ed.text['font']) self.assertIsNone(cc.context) eq(cc.info, [(0, -1, '', False)]) eq(cc.topvisible, 1) - eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') - eq(self.root.tk.call('after', 'info', self.cc.t2)[1], 'timer') + self.assertIsNone(self.cc.t1) def test_del(self): self.cc.__del__() - with self.assertRaises(TclError) as msg: - self.root.tk.call('after', 'info', self.cc.t1) - self.assertIn("doesn't exist", msg) - with self.assertRaises(TclError) as msg: - self.root.tk.call('after', 'info', self.cc.t2) - self.assertIn("doesn't exist", msg) - # For coverage on the except. Have to delete because the - # above Tcl error is caught by after_cancel. - del self.cc.t1, self.cc.t2 + + def test_del_with_timer(self): + timer = self.cc.t1 = self.text.after(10000, lambda: None) self.cc.__del__() + with self.assertRaises(TclError) as cm: + self.root.tk.call('after', 'info', timer) + self.assertIn("doesn't exist", str(cm.exception)) def test_reload(self): codecontext.CodeContext.reload() - self.assertEqual(self.cc.colors, {'background': 'lightgray', - 'foreground': '#000000'}) self.assertEqual(self.cc.context_depth, 15) def test_toggle_code_context_event(self): @@ -127,16 +137,18 @@ def test_toggle_code_context_event(self): # Toggle on. eq(toggle(), 'break') self.assertIsNotNone(cc.context) - eq(cc.context['font'], cc.textfont) - eq(cc.context['fg'], cc.colors['foreground']) - eq(cc.context['bg'], cc.colors['background']) + eq(cc.context['font'], self.text['font']) + eq(cc.context['fg'], self.highlight_cfg['foreground']) + eq(cc.context['bg'], self.highlight_cfg['background']) eq(cc.context.get('1.0', 'end-1c'), '') eq(cc.editwin.label, 'Hide Code Context') + eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') # Toggle off. eq(toggle(), 'break') self.assertIsNone(cc.context) eq(cc.editwin.label, 'Show Code Context') + self.assertIsNone(self.cc.t1) def test_get_context(self): eq = self.assertEqual @@ -227,7 +239,7 @@ def test_update_code_context(self): (4, 4, ' def __init__(self, a, b):', 'def')]) eq(cc.topvisible, 5) eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' - ' def __init__(self, a, b):') + ' def __init__(self, a, b):') # Scroll down to line 11. Last 'def' is removed. cc.text.yview(11) @@ -239,9 +251,9 @@ def test_update_code_context(self): (10, 8, ' elif a < b:', 'elif')]) eq(cc.topvisible, 12) eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' - ' def compare(self):\n' - ' if a > b:\n' - ' elif a < b:') + ' def compare(self):\n' + ' if a > b:\n' + ' elif a < b:') # No scroll. No update, even though context_depth changed. cc.update_code_context() @@ -253,9 +265,9 @@ def test_update_code_context(self): (10, 8, ' elif a < b:', 'elif')]) eq(cc.topvisible, 12) eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' - ' def compare(self):\n' - ' if a > b:\n' - ' elif a < b:') + ' def compare(self):\n' + ' if a > b:\n' + ' elif a < b:') # Scroll up. cc.text.yview(5) @@ -276,7 +288,7 @@ def test_jumptoline(self): cc.toggle_code_context_event() # Empty context. - cc.text.yview(f'{2}.0') + cc.text.yview('2.0') cc.update_code_context() eq(cc.topvisible, 2) cc.context.mark_set('insert', '1.5') @@ -284,7 +296,7 @@ def test_jumptoline(self): eq(cc.topvisible, 1) # 4 lines of context showing. - cc.text.yview(f'{12}.0') + cc.text.yview('12.0') cc.update_code_context() eq(cc.topvisible, 12) cc.context.mark_set('insert', '3.0') @@ -293,7 +305,7 @@ def test_jumptoline(self): # More context lines than limit. cc.context_depth = 2 - cc.text.yview(f'{12}.0') + cc.text.yview('12.0') cc.update_code_context() eq(cc.topvisible, 12) cc.context.mark_set('insert', '1.0') @@ -313,56 +325,72 @@ def test_timer_event(self, mock_update): self.cc.timer_event() mock_update.assert_called() - def test_config_timer_event(self): + def test_font(self): eq = self.assertEqual cc = self.cc save_font = cc.text['font'] - save_colors = codecontext.CodeContext.colors - test_font = 'FakeFont' + test_font = 'TkFixedFont' + + # Ensure code context is not active. + if cc.context is not None: + cc.toggle_code_context_event() + + # Nothing breaks or changes with inactive code context. + cc.update_font(test_font) + + # Activate code context, but no change to font. + cc.toggle_code_context_event() + eq(cc.context['font'], save_font) + # Call font update with the existing font. + cc.update_font(save_font) + eq(cc.context['font'], save_font) + cc.toggle_code_context_event() + + # Change text widget font and activate code context. + cc.text['font'] = test_font + cc.toggle_code_context_event(test_font) + eq(cc.context['font'], test_font) + + # Just call the font update. + cc.update_font(save_font) + eq(cc.context['font'], save_font) + cc.text['font'] = save_font + + def test_highlight_colors(self): + eq = self.assertEqual + cc = self.cc + save_colors = dict(self.highlight_cfg) test_colors = {'background': '#222222', 'foreground': '#ffff00'} # Ensure code context is not active. if cc.context: cc.toggle_code_context_event() - # Nothing updates on inactive code context. - cc.text['font'] = test_font - codecontext.CodeContext.colors = test_colors - cc.config_timer_event() - eq(cc.textfont, save_font) - eq(cc.contextcolors, save_colors) + # Nothing breaks with inactive code context. + cc.update_highlight_colors() - # Activate code context, but no change to font or color. + # Activate code context, but no change to colors. cc.toggle_code_context_event() - cc.text['font'] = save_font - codecontext.CodeContext.colors = save_colors - cc.config_timer_event() - eq(cc.textfont, save_font) - eq(cc.contextcolors, save_colors) - eq(cc.context['font'], save_font) eq(cc.context['background'], save_colors['background']) eq(cc.context['foreground'], save_colors['foreground']) - # Active code context, change font. - cc.text['font'] = test_font - cc.config_timer_event() - eq(cc.textfont, test_font) - eq(cc.contextcolors, save_colors) - eq(cc.context['font'], test_font) + # Call colors update, but no change to font. + cc.update_highlight_colors() eq(cc.context['background'], save_colors['background']) eq(cc.context['foreground'], save_colors['foreground']) + cc.toggle_code_context_event() - # Active code context, change color. - cc.text['font'] = save_font - codecontext.CodeContext.colors = test_colors - cc.config_timer_event() - eq(cc.textfont, save_font) - eq(cc.contextcolors, test_colors) - eq(cc.context['font'], save_font) + # Change colors and activate code context. + self.highlight_cfg = test_colors + cc.toggle_code_context_event() eq(cc.context['background'], test_colors['background']) eq(cc.context['foreground'], test_colors['foreground']) - codecontext.CodeContext.colors = save_colors - cc.config_timer_event() + + # Change colors and call highlight colors update. + self.highlight_cfg = save_colors + cc.update_highlight_colors() + eq(cc.context['background'], save_colors['background']) + eq(cc.context['foreground'], save_colors['foreground']) class HelperFunctionText(unittest.TestCase): diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index ecc53ef0195d..38c59bdf9b4e 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -74,6 +74,8 @@ class OutputWindow(EditorWindow): ("Go to file/line", "<>", None), ] + allow_codecontext = False + def __init__(self, *args): EditorWindow.__init__(self, *args) self.text.bind("<>", self.goto_file_line) diff --git a/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst b/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst new file mode 100644 index 000000000000..0b80860b8fc2 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst @@ -0,0 +1,3 @@ +Optimize code context to reduce unneeded background activity. +Font and highlight changes now occur along with text changes +instead of after a random delay. From webhook-mailer at python.org Wed Jul 17 08:24:05 2019 From: webhook-mailer at python.org (Inada Naoki) Date: Wed, 17 Jul 2019 12:24:05 -0000 Subject: [Python-checkins] bpo-37543: optimize pymalloc (#14674) Message-ID: https://github.com/python/cpython/commit/fb26504d14a08fcd61bb92bb989b6d2b12188535 commit: fb26504d14a08fcd61bb92bb989b6d2b12188535 branch: master author: Inada Naoki committer: GitHub date: 2019-07-17T21:23:57+09:00 summary: bpo-37543: optimize pymalloc (#14674) PyObject_Malloc() and PyObject_Free() inlines pymalloc_alloc and pymalloc_free partially. But when PGO is not used, compiler don't know where is the hot part in pymalloc_alloc and pymalloc_free. files: A Misc/NEWS.d/next/Core and Builtins/2019-07-10-20-33-53.bpo-37543.EvI19D.rst M Objects/obmalloc.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-10-20-33-53.bpo-37543.EvI19D.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-10-20-33-53.bpo-37543.EvI19D.rst new file mode 100644 index 000000000000..3f74bbc5d071 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-10-20-33-53.bpo-37543.EvI19D.rst @@ -0,0 +1 @@ +Optimized pymalloc for non PGO build. diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 2c00efc255dc..8d5c700d5c1e 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -710,19 +710,21 @@ PyObject_Free(void *ptr) } -#ifdef WITH_PYMALLOC - -#ifdef WITH_VALGRIND -#include - /* If we're using GCC, use __builtin_expect() to reduce overhead of the valgrind checks */ #if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) # define UNLIKELY(value) __builtin_expect((value), 0) +# define LIKELY(value) __builtin_expect((value), 1) #else # define UNLIKELY(value) (value) +# define LIKELY(value) (value) #endif +#ifdef WITH_PYMALLOC + +#ifdef WITH_VALGRIND +#include + /* -1 indicates that we haven't checked that we're running on valgrind yet. */ static int running_on_valgrind = -1; #endif @@ -1424,96 +1426,48 @@ address_in_range(void *p, poolp pool) /*==========================================================================*/ -/* pymalloc allocator - - The basic blocks are ordered by decreasing execution frequency, - which minimizes the number of jumps in the most common cases, - improves branching prediction and instruction scheduling (small - block allocations typically result in a couple of instructions). - Unless the optimizer reorders everything, being too smart... - - Return 1 if pymalloc allocated memory and wrote the pointer into *ptr_p. - - Return 0 if pymalloc failed to allocate the memory block: on bigger - requests, on error in the code below (as a last chance to serve the request) - or when the max memory limit has been reached. */ -static int -pymalloc_alloc(void *ctx, void **ptr_p, size_t nbytes) +// Called when freelist is exhausted. Extend the freelist if there is +// space for a block. Otherwise, remove this pool from usedpools. +static void +pymalloc_pool_extend(poolp pool, uint size) { - block *bp; - poolp pool; - poolp next; - uint size; - -#ifdef WITH_VALGRIND - if (UNLIKELY(running_on_valgrind == -1)) { - running_on_valgrind = RUNNING_ON_VALGRIND; - } - if (UNLIKELY(running_on_valgrind)) { - return 0; - } -#endif - - if (nbytes == 0) { - return 0; - } - if (nbytes > SMALL_REQUEST_THRESHOLD) { - return 0; + if (UNLIKELY(pool->nextoffset <= pool->maxnextoffset)) { + /* There is room for another block. */ + pool->freeblock = (block*)pool + pool->nextoffset; + pool->nextoffset += INDEX2SIZE(size); + *(block **)(pool->freeblock) = NULL; + return; } - /* - * Most frequent paths first - */ - size = (uint)(nbytes - 1) >> ALIGNMENT_SHIFT; - pool = usedpools[size + size]; - if (pool != pool->nextpool) { - /* - * There is a used pool for this size class. - * Pick up the head block of its free list. - */ - ++pool->ref.count; - bp = pool->freeblock; - assert(bp != NULL); - if ((pool->freeblock = *(block **)bp) != NULL) { - goto success; - } - - /* - * Reached the end of the free list, try to extend it. - */ - if (pool->nextoffset <= pool->maxnextoffset) { - /* There is room for another block. */ - pool->freeblock = (block*)pool + - pool->nextoffset; - pool->nextoffset += INDEX2SIZE(size); - *(block **)(pool->freeblock) = NULL; - goto success; - } - - /* Pool is full, unlink from used pools. */ - next = pool->nextpool; - pool = pool->prevpool; - next->prevpool = pool; - pool->nextpool = next; - goto success; - } + /* Pool is full, unlink from used pools. */ + poolp next; + next = pool->nextpool; + pool = pool->prevpool; + next->prevpool = pool; + pool->nextpool = next; +} +/* called when pymalloc_alloc can not allocate a block from usedpool. + * This function takes new pool and allocate a block from it. + */ +static void* +allocate_from_new_pool(uint size) +{ /* There isn't a pool of the right size class immediately * available: use a free pool. */ - if (usable_arenas == NULL) { + if (UNLIKELY(usable_arenas == NULL)) { /* No arena has a free pool: allocate a new arena. */ #ifdef WITH_MEMORY_LIMITS if (narenas_currently_allocated >= MAX_ARENAS) { - goto failed; + return NULL; } #endif usable_arenas = new_arena(); if (usable_arenas == NULL) { - goto failed; + return NULL; } - usable_arenas->nextarena = - usable_arenas->prevarena = NULL; + usable_arenas->nextarena = usable_arenas->prevarena = NULL; assert(nfp2lasta[usable_arenas->nfreepools] == NULL); nfp2lasta[usable_arenas->nfreepools] = usable_arenas; } @@ -1536,12 +1490,12 @@ pymalloc_alloc(void *ctx, void **ptr_p, size_t nbytes) } /* Try to get a cached free pool. */ - pool = usable_arenas->freepools; - if (pool != NULL) { + poolp pool = usable_arenas->freepools; + if (LIKELY(pool != NULL)) { /* Unlink from cached pools. */ usable_arenas->freepools = pool->nextpool; - --usable_arenas->nfreepools; - if (usable_arenas->nfreepools == 0) { + usable_arenas->nfreepools--; + if (UNLIKELY(usable_arenas->nfreepools == 0)) { /* Wholly allocated: remove. */ assert(usable_arenas->freepools == NULL); assert(usable_arenas->nextarena == NULL || @@ -1564,73 +1518,123 @@ pymalloc_alloc(void *ctx, void **ptr_p, size_t nbytes) (block*)usable_arenas->address + ARENA_SIZE - POOL_SIZE); } + } + else { + /* Carve off a new pool. */ + assert(usable_arenas->nfreepools > 0); + assert(usable_arenas->freepools == NULL); + pool = (poolp)usable_arenas->pool_address; + assert((block*)pool <= (block*)usable_arenas->address + + ARENA_SIZE - POOL_SIZE); + pool->arenaindex = (uint)(usable_arenas - arenas); + assert(&arenas[pool->arenaindex] == usable_arenas); + pool->szidx = DUMMY_SIZE_IDX; + usable_arenas->pool_address += POOL_SIZE; + --usable_arenas->nfreepools; - init_pool: - /* Frontlink to used pools. */ - next = usedpools[size + size]; /* == prev */ - pool->nextpool = next; - pool->prevpool = next; - next->nextpool = pool; - next->prevpool = pool; - pool->ref.count = 1; - if (pool->szidx == size) { - /* Luckily, this pool last contained blocks - * of the same size class, so its header - * and free list are already initialized. - */ - bp = pool->freeblock; - assert(bp != NULL); - pool->freeblock = *(block **)bp; - goto success; + if (usable_arenas->nfreepools == 0) { + assert(usable_arenas->nextarena == NULL || + usable_arenas->nextarena->prevarena == + usable_arenas); + /* Unlink the arena: it is completely allocated. */ + usable_arenas = usable_arenas->nextarena; + if (usable_arenas != NULL) { + usable_arenas->prevarena = NULL; + assert(usable_arenas->address != 0); + } } - /* - * Initialize the pool header, set up the free list to - * contain just the second block, and return the first - * block. + } + + /* Frontlink to used pools. */ + block *bp; + poolp next = usedpools[size + size]; /* == prev */ + pool->nextpool = next; + pool->prevpool = next; + next->nextpool = pool; + next->prevpool = pool; + pool->ref.count = 1; + if (pool->szidx == size) { + /* Luckily, this pool last contained blocks + * of the same size class, so its header + * and free list are already initialized. */ - pool->szidx = size; - size = INDEX2SIZE(size); - bp = (block *)pool + POOL_OVERHEAD; - pool->nextoffset = POOL_OVERHEAD + (size << 1); - pool->maxnextoffset = POOL_SIZE - size; - pool->freeblock = bp + size; - *(block **)(pool->freeblock) = NULL; - goto success; + bp = pool->freeblock; + assert(bp != NULL); + pool->freeblock = *(block **)bp; + return bp; + } + /* + * Initialize the pool header, set up the free list to + * contain just the second block, and return the first + * block. + */ + pool->szidx = size; + size = INDEX2SIZE(size); + bp = (block *)pool + POOL_OVERHEAD; + pool->nextoffset = POOL_OVERHEAD + (size << 1); + pool->maxnextoffset = POOL_SIZE - size; + pool->freeblock = bp + size; + *(block **)(pool->freeblock) = NULL; + return bp; +} + +/* pymalloc allocator + + Return 1 if pymalloc allocated memory and wrote the pointer into *ptr_p. + + Return 0 if pymalloc failed to allocate the memory block: on bigger + requests, on error in the code below (as a last chance to serve the request) + or when the max memory limit has been reached. +*/ +static inline int +pymalloc_alloc(void *ctx, void **ptr_p, size_t nbytes) +{ +#ifdef WITH_VALGRIND + if (UNLIKELY(running_on_valgrind == -1)) { + running_on_valgrind = RUNNING_ON_VALGRIND; } + if (UNLIKELY(running_on_valgrind)) { + return 0; + } +#endif - /* Carve off a new pool. */ - assert(usable_arenas->nfreepools > 0); - assert(usable_arenas->freepools == NULL); - pool = (poolp)usable_arenas->pool_address; - assert((block*)pool <= (block*)usable_arenas->address + - ARENA_SIZE - POOL_SIZE); - pool->arenaindex = (uint)(usable_arenas - arenas); - assert(&arenas[pool->arenaindex] == usable_arenas); - pool->szidx = DUMMY_SIZE_IDX; - usable_arenas->pool_address += POOL_SIZE; - --usable_arenas->nfreepools; - - if (usable_arenas->nfreepools == 0) { - assert(usable_arenas->nextarena == NULL || - usable_arenas->nextarena->prevarena == - usable_arenas); - /* Unlink the arena: it is completely allocated. */ - usable_arenas = usable_arenas->nextarena; - if (usable_arenas != NULL) { - usable_arenas->prevarena = NULL; - assert(usable_arenas->address != 0); - } + if (UNLIKELY(nbytes == 0)) { + return 0; + } + if (UNLIKELY(nbytes > SMALL_REQUEST_THRESHOLD)) { + return 0; } - goto init_pool; + uint size = (uint)(nbytes - 1) >> ALIGNMENT_SHIFT; + poolp pool = usedpools[size + size]; + block *bp; + + if (LIKELY(pool != pool->nextpool)) { + /* + * There is a used pool for this size class. + * Pick up the head block of its free list. + */ + ++pool->ref.count; + bp = pool->freeblock; + + if (UNLIKELY((pool->freeblock = *(block **)bp) == NULL)) { + // Reached the end of the free list, try to extend it. + pymalloc_pool_extend(pool, size); + } + } + else { + /* There isn't a pool of the right size class immediately + * available: use a free pool. + */ + bp = allocate_from_new_pool(size); + if (UNLIKELY(bp == NULL)) { + return 0; + } + } -success: assert(bp != NULL); *ptr_p = (void *)bp; return 1; - -failed: - return 0; } @@ -1638,7 +1642,7 @@ static void * _PyObject_Malloc(void *ctx, size_t nbytes) { void* ptr; - if (pymalloc_alloc(ctx, &ptr, nbytes)) { + if (LIKELY(pymalloc_alloc(ctx, &ptr, nbytes))) { return ptr; } @@ -1658,7 +1662,7 @@ _PyObject_Calloc(void *ctx, size_t nelem, size_t elsize) assert(elsize == 0 || nelem <= (size_t)PY_SSIZE_T_MAX / elsize); size_t nbytes = nelem * elsize; - if (pymalloc_alloc(ctx, &ptr, nbytes)) { + if (LIKELY(pymalloc_alloc(ctx, &ptr, nbytes))) { memset(ptr, 0, nbytes); return ptr; } @@ -1671,88 +1675,37 @@ _PyObject_Calloc(void *ctx, size_t nelem, size_t elsize) } -/* Free a memory block allocated by pymalloc_alloc(). - Return 1 if it was freed. - Return 0 if the block was not allocated by pymalloc_alloc(). */ -static int -pymalloc_free(void *ctx, void *p) +static void +insert_to_usedpool(poolp pool) { - poolp pool; - block *lastfree; - poolp next, prev; - uint size; - - assert(p != NULL); - -#ifdef WITH_VALGRIND - if (UNLIKELY(running_on_valgrind > 0)) { - return 0; - } -#endif + assert(pool->ref.count > 0); /* else the pool is empty */ - pool = POOL_ADDR(p); - if (!address_in_range(p, pool)) { - return 0; - } - /* We allocated this address. */ - - /* Link p to the start of the pool's freeblock list. Since - * the pool had at least the p block outstanding, the pool - * wasn't empty (so it's already in a usedpools[] list, or - * was full and is in no list -- it's not in the freeblocks - * list in any case). - */ - assert(pool->ref.count > 0); /* else it was empty */ - *(block **)p = lastfree = pool->freeblock; - pool->freeblock = (block *)p; - if (!lastfree) { - /* Pool was full, so doesn't currently live in any list: - * link it to the front of the appropriate usedpools[] list. - * This mimics LRU pool usage for new allocations and - * targets optimal filling when several pools contain - * blocks of the same size class. - */ - --pool->ref.count; - assert(pool->ref.count > 0); /* else the pool is empty */ - size = pool->szidx; - next = usedpools[size + size]; - prev = next->prevpool; - - /* insert pool before next: prev <-> pool <-> next */ - pool->nextpool = next; - pool->prevpool = prev; - next->prevpool = pool; - prev->nextpool = pool; - goto success; - } + uint size = pool->szidx; + poolp next = usedpools[size + size]; + poolp prev = next->prevpool; - struct arena_object* ao; - uint nf; /* ao->nfreepools */ + /* insert pool before next: prev <-> pool <-> next */ + pool->nextpool = next; + pool->prevpool = prev; + next->prevpool = pool; + prev->nextpool = pool; +} - /* freeblock wasn't NULL, so the pool wasn't full, - * and the pool is in a usedpools[] list. - */ - if (--pool->ref.count != 0) { - /* pool isn't empty: leave it in usedpools */ - goto success; - } - /* Pool is now empty: unlink from usedpools, and - * link to the front of freepools. This ensures that - * previously freed pools will be allocated later - * (being not referenced, they are perhaps paged out). - */ - next = pool->nextpool; - prev = pool->prevpool; +static void +insert_to_freepool(poolp pool) +{ + poolp next = pool->nextpool; + poolp prev = pool->prevpool; next->prevpool = prev; prev->nextpool = next; /* Link the pool to freepools. This is a singly-linked * list, and pool->prevpool isn't used there. */ - ao = &arenas[pool->arenaindex]; + struct arena_object *ao = &arenas[pool->arenaindex]; pool->nextpool = ao->freepools; ao->freepools = pool; - nf = ao->nfreepools; + uint nf = ao->nfreepools; /* If this is the rightmost arena with this number of free pools, * nfp2lasta[nf] needs to change. Caution: if nf is 0, there * are no arenas in usable_arenas with that value. @@ -1826,7 +1779,7 @@ pymalloc_free(void *ctx, void *p) ao->address = 0; /* mark unassociated */ --narenas_currently_allocated; - goto success; + return; } if (nf == 1) { @@ -1845,7 +1798,7 @@ pymalloc_free(void *ctx, void *p) nfp2lasta[1] = ao; } - goto success; + return; } /* If this arena is now out of order, we need to keep @@ -1862,7 +1815,7 @@ pymalloc_free(void *ctx, void *p) /* If this was the rightmost of the old size, it remains in place. */ if (ao == lastnf) { /* Case 4. Nothing to do. */ - goto success; + return; } /* If ao were the only arena in the list, the last block would have * gotten us out. @@ -1898,10 +1851,65 @@ pymalloc_free(void *ctx, void *p) assert(ao->nextarena == NULL || ao->nextarena->prevarena == ao); assert((usable_arenas == ao && ao->prevarena == NULL) || ao->prevarena->nextarena == ao); +} + +/* Free a memory block allocated by pymalloc_alloc(). + Return 1 if it was freed. + Return 0 if the block was not allocated by pymalloc_alloc(). */ +static inline int +pymalloc_free(void *ctx, void *p) +{ + assert(p != NULL); + +#ifdef WITH_VALGRIND + if (UNLIKELY(running_on_valgrind > 0)) { + return 0; + } +#endif + + poolp pool = POOL_ADDR(p); + if (UNLIKELY(!address_in_range(p, pool))) { + return 0; + } + /* We allocated this address. */ + + /* Link p to the start of the pool's freeblock list. Since + * the pool had at least the p block outstanding, the pool + * wasn't empty (so it's already in a usedpools[] list, or + * was full and is in no list -- it's not in the freeblocks + * list in any case). + */ + assert(pool->ref.count > 0); /* else it was empty */ + block *lastfree = pool->freeblock; + *(block **)p = lastfree; + pool->freeblock = (block *)p; + pool->ref.count--; + + if (UNLIKELY(lastfree == NULL)) { + /* Pool was full, so doesn't currently live in any list: + * link it to the front of the appropriate usedpools[] list. + * This mimics LRU pool usage for new allocations and + * targets optimal filling when several pools contain + * blocks of the same size class. + */ + insert_to_usedpool(pool); + return 1; + } - goto success; + /* freeblock wasn't NULL, so the pool wasn't full, + * and the pool is in a usedpools[] list. + */ + if (LIKELY(pool->ref.count != 0)) { + /* pool isn't empty: leave it in usedpools */ + return 1; + } -success: + /* Pool is now empty: unlink from usedpools, and + * link to the front of freepools. This ensures that + * previously freed pools will be allocated later + * (being not referenced, they are perhaps paged out). + */ + insert_to_freepool(pool); return 1; } @@ -1914,7 +1922,7 @@ _PyObject_Free(void *ctx, void *p) return; } - if (!pymalloc_free(ctx, p)) { + if (UNLIKELY(!pymalloc_free(ctx, p))) { /* pymalloc didn't allocate this address */ PyMem_RawFree(p); raw_allocated_blocks--; From webhook-mailer at python.org Wed Jul 17 09:44:59 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Wed, 17 Jul 2019 13:44:59 -0000 Subject: [Python-checkins] bpo-36390: IDLE: Combine region formatting methods. (GH-12481) Message-ID: https://github.com/python/cpython/commit/82494aa6d947c4a320c09c58fe0f100cdcf7af0b commit: 82494aa6d947c4a320c09c58fe0f100cdcf7af0b branch: master author: Cheryl Sabella committer: Terry Jan Reedy date: 2019-07-17T09:44:44-04:00 summary: bpo-36390: IDLE: Combine region formatting methods. (GH-12481) Rename paragraph.py to format.py and add region formatting methods from editor.py. Add tests for the latter. files: A Lib/idlelib/format.py A Lib/idlelib/idle_test/test_format.py A Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst D Lib/idlelib/idle_test/test_paragraph.py D Lib/idlelib/paragraph.py M Lib/idlelib/configdialog.py M Lib/idlelib/editor.py M Lib/idlelib/mainmenu.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 807ff60413d1..6ddbc7fc8e4d 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -29,7 +29,7 @@ from idlelib.autocomplete import AutoComplete from idlelib.codecontext import CodeContext from idlelib.parenmatch import ParenMatch -from idlelib.paragraph import FormatParagraph +from idlelib.format import FormatParagraph from idlelib.squeezer import Squeezer changes = ConfigChanges() diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index b972e3db8461..f02498da3521 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,7 +53,7 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext - from idlelib.paragraph import FormatParagraph + from idlelib.format import FormatParagraph, FormatRegion from idlelib.parenmatch import ParenMatch from idlelib.rstrip import Rstrip from idlelib.squeezer import Squeezer @@ -172,13 +172,14 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.smart_backspace_event) text.bind("<>",self.newline_and_indent_event) text.bind("<>",self.smart_indent_event) - text.bind("<>",self.indent_region_event) - text.bind("<>",self.dedent_region_event) - text.bind("<>",self.comment_region_event) - text.bind("<>",self.uncomment_region_event) - text.bind("<>",self.tabify_region_event) - text.bind("<>",self.untabify_region_event) - text.bind("<>",self.toggle_tabs_event) + self.fregion = fregion = self.FormatRegion(self) + text.bind("<>", fregion.indent_region_event) + text.bind("<>", fregion.dedent_region_event) + text.bind("<>", fregion.comment_region_event) + text.bind("<>", fregion.uncomment_region_event) + text.bind("<>", fregion.tabify_region_event) + text.bind("<>", fregion.untabify_region_event) + text.bind("<>", self.toggle_tabs_event) text.bind("<>",self.change_indentwidth_event) text.bind("", self.move_at_edge_if_selection(0)) text.bind("", self.move_at_edge_if_selection(1)) @@ -1290,7 +1291,7 @@ def smart_indent_event(self, event): try: if first and last: if index2line(first) != index2line(last): - return self.indent_region_event(event) + return self.fregion.indent_region_event(event) text.delete(first, last) text.mark_set("insert", first) prefix = text.get("insert linestart", "insert") @@ -1423,72 +1424,6 @@ def inner(offset, _startindex=startindex, return _icis(_startindex + "+%dc" % offset) return inner - def indent_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines)): - line = lines[pos] - if line: - raw, effective = get_line_indent(line, self.tabwidth) - effective = effective + self.indentwidth - lines[pos] = self._make_blanks(effective) + line[raw:] - self.set_region(head, tail, chars, lines) - return "break" - - def dedent_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines)): - line = lines[pos] - if line: - raw, effective = get_line_indent(line, self.tabwidth) - effective = max(effective - self.indentwidth, 0) - lines[pos] = self._make_blanks(effective) + line[raw:] - self.set_region(head, tail, chars, lines) - return "break" - - def comment_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines) - 1): - line = lines[pos] - lines[pos] = '##' + line - self.set_region(head, tail, chars, lines) - return "break" - - def uncomment_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines)): - line = lines[pos] - if not line: - continue - if line[:2] == '##': - line = line[2:] - elif line[:1] == '#': - line = line[1:] - lines[pos] = line - self.set_region(head, tail, chars, lines) - return "break" - - def tabify_region_event(self, event): - head, tail, chars, lines = self.get_region() - tabwidth = self._asktabwidth() - if tabwidth is None: return - for pos in range(len(lines)): - line = lines[pos] - if line: - raw, effective = get_line_indent(line, tabwidth) - ntabs, nspaces = divmod(effective, tabwidth) - lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] - self.set_region(head, tail, chars, lines) - return "break" - - def untabify_region_event(self, event): - head, tail, chars, lines = self.get_region() - tabwidth = self._asktabwidth() - if tabwidth is None: return - for pos in range(len(lines)): - lines[pos] = lines[pos].expandtabs(tabwidth) - self.set_region(head, tail, chars, lines) - return "break" - def toggle_tabs_event(self, event): if self.askyesno( "Toggle tabs", @@ -1523,33 +1458,6 @@ def change_indentwidth_event(self, event): self.indentwidth = new return "break" - def get_region(self): - text = self.text - first, last = self.get_selection_indices() - if first and last: - head = text.index(first + " linestart") - tail = text.index(last + "-1c lineend +1c") - else: - head = text.index("insert linestart") - tail = text.index("insert lineend +1c") - chars = text.get(head, tail) - lines = chars.split("\n") - return head, tail, chars, lines - - def set_region(self, head, tail, chars, lines): - text = self.text - newchars = "\n".join(lines) - if newchars == chars: - text.bell() - return - text.tag_remove("sel", "1.0", "end") - text.mark_set("insert", head) - text.undo_block_start() - text.delete(head, tail) - text.insert(head, newchars) - text.undo_block_stop() - text.tag_add("sel", head, "insert") - # Make string that displays as n leading blanks. def _make_blanks(self, n): @@ -1571,15 +1479,6 @@ def reindent_to(self, column): text.insert("insert", self._make_blanks(column)) text.undo_block_stop() - def _asktabwidth(self): - return self.askinteger( - "Tab width", - "Columns per tab? (2-16)", - parent=self.text, - initialvalue=self.indentwidth, - minvalue=2, - maxvalue=16) - # Guess indentwidth from text content. # Return guessed indentwidth. This should not be believed unless # it's in a reasonable range (e.g., it will be 0 if no indented diff --git a/Lib/idlelib/format.py b/Lib/idlelib/format.py new file mode 100644 index 000000000000..e11ca3a9d26f --- /dev/null +++ b/Lib/idlelib/format.py @@ -0,0 +1,357 @@ +"""Format all or a selected region (line slice) of text. + +Region formatting options: paragraph, comment block, indent, deindent, +comment, uncomment, tabify, and untabify. + +File renamed from paragraph.py with functions added from editor.py. +""" +import re +from tkinter.simpledialog import askinteger +from idlelib.config import idleConf + + +class FormatParagraph: + """Format a paragraph, comment block, or selection to a max width. + + Does basic, standard text formatting, and also understands Python + comment blocks. Thus, for editing Python source code, this + extension is really only suitable for reformatting these comment + blocks or triple-quoted strings. + + Known problems with comment reformatting: + * If there is a selection marked, and the first line of the + selection is not complete, the block will probably not be detected + as comments, and will have the normal "text formatting" rules + applied. + * If a comment block has leading whitespace that mixes tabs and + spaces, they will not be considered part of the same block. + * Fancy comments, like this bulleted list, aren't handled :-) + """ + def __init__(self, editwin): + self.editwin = editwin + + @classmethod + def reload(cls): + cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph', + 'max-width', type='int', default=72) + + def close(self): + self.editwin = None + + def format_paragraph_event(self, event, limit=None): + """Formats paragraph to a max width specified in idleConf. + + If text is selected, format_paragraph_event will start breaking lines + at the max width, starting from the beginning selection. + + If no text is selected, format_paragraph_event uses the current + cursor location to determine the paragraph (lines of text surrounded + by blank lines) and formats it. + + The length limit parameter is for testing with a known value. + """ + limit = self.max_width if limit is None else limit + text = self.editwin.text + first, last = self.editwin.get_selection_indices() + if first and last: + data = text.get(first, last) + comment_header = get_comment_header(data) + else: + first, last, comment_header, data = \ + find_paragraph(text, text.index("insert")) + if comment_header: + newdata = reformat_comment(data, limit, comment_header) + else: + newdata = reformat_paragraph(data, limit) + text.tag_remove("sel", "1.0", "end") + + if newdata != data: + text.mark_set("insert", first) + text.undo_block_start() + text.delete(first, last) + text.insert(first, newdata) + text.undo_block_stop() + else: + text.mark_set("insert", last) + text.see("insert") + return "break" + + +FormatParagraph.reload() + +def find_paragraph(text, mark): + """Returns the start/stop indices enclosing the paragraph that mark is in. + + Also returns the comment format string, if any, and paragraph of text + between the start/stop indices. + """ + lineno, col = map(int, mark.split(".")) + line = text.get("%d.0" % lineno, "%d.end" % lineno) + + # Look for start of next paragraph if the index passed in is a blank line + while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line): + lineno = lineno + 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + first_lineno = lineno + comment_header = get_comment_header(line) + comment_header_len = len(comment_header) + + # Once start line found, search for end of paragraph (a blank line) + while get_comment_header(line)==comment_header and \ + not is_all_white(line[comment_header_len:]): + lineno = lineno + 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + last = "%d.0" % lineno + + # Search back to beginning of paragraph (first blank line before) + lineno = first_lineno - 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + while lineno > 0 and \ + get_comment_header(line)==comment_header and \ + not is_all_white(line[comment_header_len:]): + lineno = lineno - 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + first = "%d.0" % (lineno+1) + + return first, last, comment_header, text.get(first, last) + +# This should perhaps be replaced with textwrap.wrap +def reformat_paragraph(data, limit): + """Return data reformatted to specified width (limit).""" + lines = data.split("\n") + i = 0 + n = len(lines) + while i < n and is_all_white(lines[i]): + i = i+1 + if i >= n: + return data + indent1 = get_indent(lines[i]) + if i+1 < n and not is_all_white(lines[i+1]): + indent2 = get_indent(lines[i+1]) + else: + indent2 = indent1 + new = lines[:i] + partial = indent1 + while i < n and not is_all_white(lines[i]): + # XXX Should take double space after period (etc.) into account + words = re.split(r"(\s+)", lines[i]) + for j in range(0, len(words), 2): + word = words[j] + if not word: + continue # Can happen when line ends in whitespace + if len((partial + word).expandtabs()) > limit and \ + partial != indent1: + new.append(partial.rstrip()) + partial = indent2 + partial = partial + word + " " + if j+1 < len(words) and words[j+1] != " ": + partial = partial + " " + i = i+1 + new.append(partial.rstrip()) + # XXX Should reformat remaining paragraphs as well + new.extend(lines[i:]) + return "\n".join(new) + +def reformat_comment(data, limit, comment_header): + """Return data reformatted to specified width with comment header.""" + + # Remove header from the comment lines + lc = len(comment_header) + data = "\n".join(line[lc:] for line in data.split("\n")) + # Reformat to maxformatwidth chars or a 20 char width, + # whichever is greater. + format_width = max(limit - len(comment_header), 20) + newdata = reformat_paragraph(data, format_width) + # re-split and re-insert the comment header. + newdata = newdata.split("\n") + # If the block ends in a \n, we don't want the comment prefix + # inserted after it. (Im not sure it makes sense to reformat a + # comment block that is not made of complete lines, but whatever!) + # Can't think of a clean solution, so we hack away + block_suffix = "" + if not newdata[-1]: + block_suffix = "\n" + newdata = newdata[:-1] + return '\n'.join(comment_header+line for line in newdata) + block_suffix + +def is_all_white(line): + """Return True if line is empty or all whitespace.""" + + return re.match(r"^\s*$", line) is not None + +def get_indent(line): + """Return the initial space or tab indent of line.""" + return re.match(r"^([ \t]*)", line).group() + +def get_comment_header(line): + """Return string with leading whitespace and '#' from line or ''. + + A null return indicates that the line is not a comment line. A non- + null return, such as ' #', will be used to find the other lines of + a comment block with the same indent. + """ + m = re.match(r"^([ \t]*#*)", line) + if m is None: return "" + return m.group(1) + + +# Copy from editor.py; importing it would cause an import cycle. +_line_indent_re = re.compile(r'[ \t]*') + +def get_line_indent(line, tabwidth): + """Return a line's indentation as (# chars, effective # of spaces). + + The effective # of spaces is the length after properly "expanding" + the tabs into spaces, as done by str.expandtabs(tabwidth). + """ + m = _line_indent_re.match(line) + return m.end(), len(m.group().expandtabs(tabwidth)) + + +class FormatRegion: + "Format selected text." + + def __init__(self, editwin): + self.editwin = editwin + + def get_region(self): + """Return line information about the selected text region. + + If text is selected, the first and last indices will be + for the selection. If there is no text selected, the + indices will be the current cursor location. + + Return a tuple containing (first index, last index, + string representation of text, list of text lines). + """ + text = self.editwin.text + first, last = self.editwin.get_selection_indices() + if first and last: + head = text.index(first + " linestart") + tail = text.index(last + "-1c lineend +1c") + else: + head = text.index("insert linestart") + tail = text.index("insert lineend +1c") + chars = text.get(head, tail) + lines = chars.split("\n") + return head, tail, chars, lines + + def set_region(self, head, tail, chars, lines): + """Replace the text between the given indices. + + Args: + head: Starting index of text to replace. + tail: Ending index of text to replace. + chars: Expected to be string of current text + between head and tail. + lines: List of new lines to insert between head + and tail. + """ + text = self.editwin.text + newchars = "\n".join(lines) + if newchars == chars: + text.bell() + return + text.tag_remove("sel", "1.0", "end") + text.mark_set("insert", head) + text.undo_block_start() + text.delete(head, tail) + text.insert(head, newchars) + text.undo_block_stop() + text.tag_add("sel", head, "insert") + + def indent_region_event(self, event=None): + "Indent region by indentwidth spaces." + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = get_line_indent(line, self.editwin.tabwidth) + effective = effective + self.editwin.indentwidth + lines[pos] = self.editwin._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def dedent_region_event(self, event=None): + "Dedent region by indentwidth spaces." + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = get_line_indent(line, self.editwin.tabwidth) + effective = max(effective - self.editwin.indentwidth, 0) + lines[pos] = self.editwin._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def comment_region_event(self, event=None): + """Comment out each line in region. + + ## is appended to the beginning of each line to comment it out. + """ + head, tail, chars, lines = self.get_region() + for pos in range(len(lines) - 1): + line = lines[pos] + lines[pos] = '##' + line + self.set_region(head, tail, chars, lines) + return "break" + + def uncomment_region_event(self, event=None): + """Uncomment each line in region. + + Remove ## or # in the first positions of a line. If the comment + is not in the beginning position, this command will have no effect. + """ + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if not line: + continue + if line[:2] == '##': + line = line[2:] + elif line[:1] == '#': + line = line[1:] + lines[pos] = line + self.set_region(head, tail, chars, lines) + return "break" + + def tabify_region_event(self, event=None): + "Convert leading spaces to tabs for each line in selected region." + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + if tabwidth is None: + return + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = get_line_indent(line, tabwidth) + ntabs, nspaces = divmod(effective, tabwidth) + lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def untabify_region_event(self, event=None): + "Expand tabs to spaces for each line in region." + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + if tabwidth is None: + return + for pos in range(len(lines)): + lines[pos] = lines[pos].expandtabs(tabwidth) + self.set_region(head, tail, chars, lines) + return "break" + + def _asktabwidth(self): + "Return value for tab width." + return askinteger( + "Tab width", + "Columns per tab? (2-16)", + parent=self.editwin.text, + initialvalue=self.editwin.indentwidth, + minvalue=2, + maxvalue=16) + + +if __name__ == "__main__": + from unittest import main + main('idlelib.idle_test.test_format', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_paragraph.py b/Lib/idlelib/idle_test/test_format.py similarity index 64% rename from Lib/idlelib/idle_test/test_paragraph.py rename to Lib/idlelib/idle_test/test_format.py index 0cb966fb96ca..a2d27ed69dd1 100644 --- a/Lib/idlelib/idle_test/test_paragraph.py +++ b/Lib/idlelib/idle_test/test_format.py @@ -1,7 +1,8 @@ -"Test paragraph, coverage 76%." +"Test format, coverage 99%." -from idlelib import paragraph as pg +from idlelib import format as ft import unittest +from unittest import mock from test.support import requires from tkinter import Tk, Text from idlelib.editor import EditorWindow @@ -16,26 +17,26 @@ class Is_Get_Test(unittest.TestCase): leadingws_nocomment = ' This is not a comment' def test_is_all_white(self): - self.assertTrue(pg.is_all_white('')) - self.assertTrue(pg.is_all_white('\t\n\r\f\v')) - self.assertFalse(pg.is_all_white(self.test_comment)) + self.assertTrue(ft.is_all_white('')) + self.assertTrue(ft.is_all_white('\t\n\r\f\v')) + self.assertFalse(ft.is_all_white(self.test_comment)) def test_get_indent(self): Equal = self.assertEqual - Equal(pg.get_indent(self.test_comment), '') - Equal(pg.get_indent(self.trailingws_comment), '') - Equal(pg.get_indent(self.leadingws_comment), ' ') - Equal(pg.get_indent(self.leadingws_nocomment), ' ') + Equal(ft.get_indent(self.test_comment), '') + Equal(ft.get_indent(self.trailingws_comment), '') + Equal(ft.get_indent(self.leadingws_comment), ' ') + Equal(ft.get_indent(self.leadingws_nocomment), ' ') def test_get_comment_header(self): Equal = self.assertEqual # Test comment strings - Equal(pg.get_comment_header(self.test_comment), '#') - Equal(pg.get_comment_header(self.trailingws_comment), '#') - Equal(pg.get_comment_header(self.leadingws_comment), ' #') + Equal(ft.get_comment_header(self.test_comment), '#') + Equal(ft.get_comment_header(self.trailingws_comment), '#') + Equal(ft.get_comment_header(self.leadingws_comment), ' #') # Test non-comment strings - Equal(pg.get_comment_header(self.leadingws_nocomment), ' ') - Equal(pg.get_comment_header(self.test_nocomment), '') + Equal(ft.get_comment_header(self.leadingws_nocomment), ' ') + Equal(ft.get_comment_header(self.test_nocomment), '') class FindTest(unittest.TestCase): @@ -63,7 +64,7 @@ def runcase(self, inserttext, stopline, expected): linelength = int(text.index("%d.end" % line).split('.')[1]) for col in (0, linelength//2, linelength): tempindex = "%d.%d" % (line, col) - self.assertEqual(pg.find_paragraph(text, tempindex), expected) + self.assertEqual(ft.find_paragraph(text, tempindex), expected) text.delete('1.0', 'end') def test_find_comment(self): @@ -162,7 +163,7 @@ class ReformatFunctionTest(unittest.TestCase): def test_reformat_paragraph(self): Equal = self.assertEqual - reform = pg.reformat_paragraph + reform = ft.reformat_paragraph hw = "O hello world" Equal(reform(' ', 1), ' ') Equal(reform("Hello world", 20), "Hello world") @@ -193,7 +194,7 @@ def test_reformat_comment(self): test_string = ( " \"\"\"this is a test of a reformat for a triple quoted string" " will it reformat to less than 70 characters for me?\"\"\"") - result = pg.reformat_comment(test_string, 70, " ") + result = ft.reformat_comment(test_string, 70, " ") expected = ( " \"\"\"this is a test of a reformat for a triple quoted string will it\n" " reformat to less than 70 characters for me?\"\"\"") @@ -202,7 +203,7 @@ def test_reformat_comment(self): test_comment = ( "# this is a test of a reformat for a triple quoted string will " "it reformat to less than 70 characters for me?") - result = pg.reformat_comment(test_comment, 70, "#") + result = ft.reformat_comment(test_comment, 70, "#") expected = ( "# this is a test of a reformat for a triple quoted string will it\n" "# reformat to less than 70 characters for me?") @@ -211,7 +212,7 @@ def test_reformat_comment(self): class FormatClassTest(unittest.TestCase): def test_init_close(self): - instance = pg.FormatParagraph('editor') + instance = ft.FormatParagraph('editor') self.assertEqual(instance.editwin, 'editor') instance.close() self.assertEqual(instance.editwin, None) @@ -273,7 +274,7 @@ def setUpClass(cls): cls.root.withdraw() editor = Editor(root=cls.root) cls.text = editor.text.text # Test code does not need the wrapper. - cls.formatter = pg.FormatParagraph(editor).format_paragraph_event + cls.formatter = ft.FormatParagraph(editor).format_paragraph_event # Sets the insert mark just after the re-wrapped and inserted text. @classmethod @@ -375,5 +376,202 @@ def test_comment_block(self): ## text.delete('1.0', 'end') +class DummyEditwin: + def __init__(self, root, text): + self.root = root + self.text = text + self.indentwidth = 4 + self.tabwidth = 4 + self.usetabs = False + self.context_use_ps1 = True + + _make_blanks = EditorWindow._make_blanks + get_selection_indices = EditorWindow.get_selection_indices + + +class FormatRegionTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.root.withdraw() + cls.text = Text(cls.root) + cls.text.undo_block_start = mock.Mock() + cls.text.undo_block_stop = mock.Mock() + cls.editor = DummyEditwin(cls.root, cls.text) + cls.formatter = ft.FormatRegion(cls.editor) + + @classmethod + def tearDownClass(cls): + del cls.text, cls.formatter, cls.editor + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + + def setUp(self): + self.text.insert('1.0', self.code_sample) + + def tearDown(self): + self.text.delete('1.0', 'end') + + code_sample = """\ + +class C1(): + # Class comment. + def __init__(self, a, b): + self.a = a + self.b = b + + def compare(self): + if a > b: + return a + elif a < b: + return b + else: + return None +""" + + def test_get_region(self): + get = self.formatter.get_region + text = self.text + eq = self.assertEqual + + # Add selection. + text.tag_add('sel', '7.0', '10.0') + expected_lines = ['', + ' def compare(self):', + ' if a > b:', + ''] + eq(get(), ('7.0', '10.0', '\n'.join(expected_lines), expected_lines)) + + # Remove selection. + text.tag_remove('sel', '1.0', 'end') + eq(get(), ('15.0', '16.0', '\n', ['', ''])) + + def test_set_region(self): + set_ = self.formatter.set_region + text = self.text + eq = self.assertEqual + + save_bell = text.bell + text.bell = mock.Mock() + line6 = self.code_sample.splitlines()[5] + line10 = self.code_sample.splitlines()[9] + + text.tag_add('sel', '6.0', '11.0') + head, tail, chars, lines = self.formatter.get_region() + + # No changes. + set_(head, tail, chars, lines) + text.bell.assert_called_once() + eq(text.get('6.0', '11.0'), chars) + eq(text.get('sel.first', 'sel.last'), chars) + text.tag_remove('sel', '1.0', 'end') + + # Alter selected lines by changing lines and adding a newline. + newstring = 'added line 1\n\n\n\n' + newlines = newstring.split('\n') + set_('7.0', '10.0', chars, newlines) + # Selection changed. + eq(text.get('sel.first', 'sel.last'), newstring) + # Additional line added, so last index is changed. + eq(text.get('7.0', '11.0'), newstring) + # Before and after lines unchanged. + eq(text.get('6.0', '7.0-1c'), line6) + eq(text.get('11.0', '12.0-1c'), line10) + text.tag_remove('sel', '1.0', 'end') + + text.bell = save_bell + + def test_indent_region_event(self): + indent = self.formatter.indent_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + indent() + # Blank lines aren't affected by indent. + eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) + + def test_dedent_region_event(self): + dedent = self.formatter.dedent_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + dedent() + # Blank lines aren't affected by dedent. + eq(text.get('7.0', '10.0'), ('\ndef compare(self):\n if a > b:\n')) + + def test_comment_region_event(self): + comment = self.formatter.comment_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + comment() + eq(text.get('7.0', '10.0'), ('##\n## def compare(self):\n## if a > b:\n')) + + def test_uncomment_region_event(self): + comment = self.formatter.comment_region_event + uncomment = self.formatter.uncomment_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + comment() + uncomment() + eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) + + # Only remove comments at the beginning of a line. + text.tag_remove('sel', '1.0', 'end') + text.tag_add('sel', '3.0', '4.0') + uncomment() + eq(text.get('3.0', '3.end'), (' # Class comment.')) + + self.formatter.set_region('3.0', '4.0', '', ['# Class comment.', '']) + uncomment() + eq(text.get('3.0', '3.end'), (' Class comment.')) + + @mock.patch.object(ft.FormatRegion, "_asktabwidth") + def test_tabify_region_event(self, _asktabwidth): + tabify = self.formatter.tabify_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + # No tabwidth selected. + _asktabwidth.return_value = None + self.assertIsNone(tabify()) + + _asktabwidth.return_value = 3 + self.assertIsNotNone(tabify()) + eq(text.get('7.0', '10.0'), ('\n\t def compare(self):\n\t\t if a > b:\n')) + + @mock.patch.object(ft.FormatRegion, "_asktabwidth") + def test_untabify_region_event(self, _asktabwidth): + untabify = self.formatter.untabify_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + # No tabwidth selected. + _asktabwidth.return_value = None + self.assertIsNone(untabify()) + + _asktabwidth.return_value = 2 + self.formatter.tabify_region_event() + _asktabwidth.return_value = 3 + self.assertIsNotNone(untabify()) + eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) + + @mock.patch.object(ft, "askinteger") + def test_ask_tabwidth(self, askinteger): + ask = self.formatter._asktabwidth + askinteger.return_value = 10 + self.assertEqual(ask(), 10) + + if __name__ == '__main__': unittest.main(verbosity=2, exit=2) diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py index 1b8dc475650d..b0c85cf505c7 100644 --- a/Lib/idlelib/mainmenu.py +++ b/Lib/idlelib/mainmenu.py @@ -60,6 +60,7 @@ ]), ('format', [ + ('F_ormat Paragraph', '<>'), ('_Indent Region', '<>'), ('_Dedent Region', '<>'), ('Comment _Out Region', '<>'), @@ -68,7 +69,6 @@ ('Untabify Region', '<>'), ('Toggle Tabs', '<>'), ('New Indent Width', '<>'), - ('F_ormat Paragraph', '<>'), ('S_trip Trailing Whitespace', '<>'), ]), diff --git a/Lib/idlelib/paragraph.py b/Lib/idlelib/paragraph.py deleted file mode 100644 index 81422571fa32..000000000000 --- a/Lib/idlelib/paragraph.py +++ /dev/null @@ -1,194 +0,0 @@ -"""Format a paragraph, comment block, or selection to a max width. - -Does basic, standard text formatting, and also understands Python -comment blocks. Thus, for editing Python source code, this -extension is really only suitable for reformatting these comment -blocks or triple-quoted strings. - -Known problems with comment reformatting: -* If there is a selection marked, and the first line of the - selection is not complete, the block will probably not be detected - as comments, and will have the normal "text formatting" rules - applied. -* If a comment block has leading whitespace that mixes tabs and - spaces, they will not be considered part of the same block. -* Fancy comments, like this bulleted list, aren't handled :-) -""" -import re - -from idlelib.config import idleConf - - -class FormatParagraph: - - def __init__(self, editwin): - self.editwin = editwin - - @classmethod - def reload(cls): - cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph', - 'max-width', type='int', default=72) - - def close(self): - self.editwin = None - - def format_paragraph_event(self, event, limit=None): - """Formats paragraph to a max width specified in idleConf. - - If text is selected, format_paragraph_event will start breaking lines - at the max width, starting from the beginning selection. - - If no text is selected, format_paragraph_event uses the current - cursor location to determine the paragraph (lines of text surrounded - by blank lines) and formats it. - - The length limit parameter is for testing with a known value. - """ - limit = self.max_width if limit is None else limit - text = self.editwin.text - first, last = self.editwin.get_selection_indices() - if first and last: - data = text.get(first, last) - comment_header = get_comment_header(data) - else: - first, last, comment_header, data = \ - find_paragraph(text, text.index("insert")) - if comment_header: - newdata = reformat_comment(data, limit, comment_header) - else: - newdata = reformat_paragraph(data, limit) - text.tag_remove("sel", "1.0", "end") - - if newdata != data: - text.mark_set("insert", first) - text.undo_block_start() - text.delete(first, last) - text.insert(first, newdata) - text.undo_block_stop() - else: - text.mark_set("insert", last) - text.see("insert") - return "break" - - -FormatParagraph.reload() - -def find_paragraph(text, mark): - """Returns the start/stop indices enclosing the paragraph that mark is in. - - Also returns the comment format string, if any, and paragraph of text - between the start/stop indices. - """ - lineno, col = map(int, mark.split(".")) - line = text.get("%d.0" % lineno, "%d.end" % lineno) - - # Look for start of next paragraph if the index passed in is a blank line - while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line): - lineno = lineno + 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - first_lineno = lineno - comment_header = get_comment_header(line) - comment_header_len = len(comment_header) - - # Once start line found, search for end of paragraph (a blank line) - while get_comment_header(line)==comment_header and \ - not is_all_white(line[comment_header_len:]): - lineno = lineno + 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - last = "%d.0" % lineno - - # Search back to beginning of paragraph (first blank line before) - lineno = first_lineno - 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - while lineno > 0 and \ - get_comment_header(line)==comment_header and \ - not is_all_white(line[comment_header_len:]): - lineno = lineno - 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - first = "%d.0" % (lineno+1) - - return first, last, comment_header, text.get(first, last) - -# This should perhaps be replaced with textwrap.wrap -def reformat_paragraph(data, limit): - """Return data reformatted to specified width (limit).""" - lines = data.split("\n") - i = 0 - n = len(lines) - while i < n and is_all_white(lines[i]): - i = i+1 - if i >= n: - return data - indent1 = get_indent(lines[i]) - if i+1 < n and not is_all_white(lines[i+1]): - indent2 = get_indent(lines[i+1]) - else: - indent2 = indent1 - new = lines[:i] - partial = indent1 - while i < n and not is_all_white(lines[i]): - # XXX Should take double space after period (etc.) into account - words = re.split(r"(\s+)", lines[i]) - for j in range(0, len(words), 2): - word = words[j] - if not word: - continue # Can happen when line ends in whitespace - if len((partial + word).expandtabs()) > limit and \ - partial != indent1: - new.append(partial.rstrip()) - partial = indent2 - partial = partial + word + " " - if j+1 < len(words) and words[j+1] != " ": - partial = partial + " " - i = i+1 - new.append(partial.rstrip()) - # XXX Should reformat remaining paragraphs as well - new.extend(lines[i:]) - return "\n".join(new) - -def reformat_comment(data, limit, comment_header): - """Return data reformatted to specified width with comment header.""" - - # Remove header from the comment lines - lc = len(comment_header) - data = "\n".join(line[lc:] for line in data.split("\n")) - # Reformat to maxformatwidth chars or a 20 char width, - # whichever is greater. - format_width = max(limit - len(comment_header), 20) - newdata = reformat_paragraph(data, format_width) - # re-split and re-insert the comment header. - newdata = newdata.split("\n") - # If the block ends in a \n, we don't want the comment prefix - # inserted after it. (Im not sure it makes sense to reformat a - # comment block that is not made of complete lines, but whatever!) - # Can't think of a clean solution, so we hack away - block_suffix = "" - if not newdata[-1]: - block_suffix = "\n" - newdata = newdata[:-1] - return '\n'.join(comment_header+line for line in newdata) + block_suffix - -def is_all_white(line): - """Return True if line is empty or all whitespace.""" - - return re.match(r"^\s*$", line) is not None - -def get_indent(line): - """Return the initial space or tab indent of line.""" - return re.match(r"^([ \t]*)", line).group() - -def get_comment_header(line): - """Return string with leading whitespace and '#' from line or ''. - - A null return indicates that the line is not a comment line. A non- - null return, such as ' #', will be used to find the other lines of - a comment block with the same indent. - """ - m = re.match(r"^([ \t]*#*)", line) - if m is None: return "" - return m.group(1) - - -if __name__ == "__main__": - from unittest import main - main('idlelib.idle_test.test_paragraph', verbosity=2, exit=False) diff --git a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst new file mode 100644 index 000000000000..fabc75fd1c6e --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst @@ -0,0 +1,2 @@ +Rename paragraph.py to format.py and add region formatting methods +from editor.py. Add tests for the latter. From webhook-mailer at python.org Wed Jul 17 10:45:35 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Wed, 17 Jul 2019 14:45:35 -0000 Subject: [Python-checkins] bpo-36390: IDLE: Combine region formatting methods. (GH-12481) (GH-14811) Message-ID: https://github.com/python/cpython/commit/1fc43a3fafd22eb20832459654fd125f12aa3738 commit: 1fc43a3fafd22eb20832459654fd125f12aa3738 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-17T10:45:20-04:00 summary: bpo-36390: IDLE: Combine region formatting methods. (GH-12481) (GH-14811) Rename paragraph.py to format.py and add region formatting methods from editor.py. Add tests for the latter. (cherry picked from commit 82494aa6d947c4a320c09c58fe0f100cdcf7af0b) Co-authored-by: Cheryl Sabella files: A Lib/idlelib/format.py A Lib/idlelib/idle_test/test_format.py A Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst D Lib/idlelib/idle_test/test_paragraph.py D Lib/idlelib/paragraph.py M Lib/idlelib/configdialog.py M Lib/idlelib/editor.py M Lib/idlelib/mainmenu.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 807ff60413d1..6ddbc7fc8e4d 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -29,7 +29,7 @@ from idlelib.autocomplete import AutoComplete from idlelib.codecontext import CodeContext from idlelib.parenmatch import ParenMatch -from idlelib.paragraph import FormatParagraph +from idlelib.format import FormatParagraph from idlelib.squeezer import Squeezer changes = ConfigChanges() diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index b972e3db8461..f02498da3521 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,7 +53,7 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext - from idlelib.paragraph import FormatParagraph + from idlelib.format import FormatParagraph, FormatRegion from idlelib.parenmatch import ParenMatch from idlelib.rstrip import Rstrip from idlelib.squeezer import Squeezer @@ -172,13 +172,14 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.smart_backspace_event) text.bind("<>",self.newline_and_indent_event) text.bind("<>",self.smart_indent_event) - text.bind("<>",self.indent_region_event) - text.bind("<>",self.dedent_region_event) - text.bind("<>",self.comment_region_event) - text.bind("<>",self.uncomment_region_event) - text.bind("<>",self.tabify_region_event) - text.bind("<>",self.untabify_region_event) - text.bind("<>",self.toggle_tabs_event) + self.fregion = fregion = self.FormatRegion(self) + text.bind("<>", fregion.indent_region_event) + text.bind("<>", fregion.dedent_region_event) + text.bind("<>", fregion.comment_region_event) + text.bind("<>", fregion.uncomment_region_event) + text.bind("<>", fregion.tabify_region_event) + text.bind("<>", fregion.untabify_region_event) + text.bind("<>", self.toggle_tabs_event) text.bind("<>",self.change_indentwidth_event) text.bind("", self.move_at_edge_if_selection(0)) text.bind("", self.move_at_edge_if_selection(1)) @@ -1290,7 +1291,7 @@ def smart_indent_event(self, event): try: if first and last: if index2line(first) != index2line(last): - return self.indent_region_event(event) + return self.fregion.indent_region_event(event) text.delete(first, last) text.mark_set("insert", first) prefix = text.get("insert linestart", "insert") @@ -1423,72 +1424,6 @@ def inner(offset, _startindex=startindex, return _icis(_startindex + "+%dc" % offset) return inner - def indent_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines)): - line = lines[pos] - if line: - raw, effective = get_line_indent(line, self.tabwidth) - effective = effective + self.indentwidth - lines[pos] = self._make_blanks(effective) + line[raw:] - self.set_region(head, tail, chars, lines) - return "break" - - def dedent_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines)): - line = lines[pos] - if line: - raw, effective = get_line_indent(line, self.tabwidth) - effective = max(effective - self.indentwidth, 0) - lines[pos] = self._make_blanks(effective) + line[raw:] - self.set_region(head, tail, chars, lines) - return "break" - - def comment_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines) - 1): - line = lines[pos] - lines[pos] = '##' + line - self.set_region(head, tail, chars, lines) - return "break" - - def uncomment_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines)): - line = lines[pos] - if not line: - continue - if line[:2] == '##': - line = line[2:] - elif line[:1] == '#': - line = line[1:] - lines[pos] = line - self.set_region(head, tail, chars, lines) - return "break" - - def tabify_region_event(self, event): - head, tail, chars, lines = self.get_region() - tabwidth = self._asktabwidth() - if tabwidth is None: return - for pos in range(len(lines)): - line = lines[pos] - if line: - raw, effective = get_line_indent(line, tabwidth) - ntabs, nspaces = divmod(effective, tabwidth) - lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] - self.set_region(head, tail, chars, lines) - return "break" - - def untabify_region_event(self, event): - head, tail, chars, lines = self.get_region() - tabwidth = self._asktabwidth() - if tabwidth is None: return - for pos in range(len(lines)): - lines[pos] = lines[pos].expandtabs(tabwidth) - self.set_region(head, tail, chars, lines) - return "break" - def toggle_tabs_event(self, event): if self.askyesno( "Toggle tabs", @@ -1523,33 +1458,6 @@ def change_indentwidth_event(self, event): self.indentwidth = new return "break" - def get_region(self): - text = self.text - first, last = self.get_selection_indices() - if first and last: - head = text.index(first + " linestart") - tail = text.index(last + "-1c lineend +1c") - else: - head = text.index("insert linestart") - tail = text.index("insert lineend +1c") - chars = text.get(head, tail) - lines = chars.split("\n") - return head, tail, chars, lines - - def set_region(self, head, tail, chars, lines): - text = self.text - newchars = "\n".join(lines) - if newchars == chars: - text.bell() - return - text.tag_remove("sel", "1.0", "end") - text.mark_set("insert", head) - text.undo_block_start() - text.delete(head, tail) - text.insert(head, newchars) - text.undo_block_stop() - text.tag_add("sel", head, "insert") - # Make string that displays as n leading blanks. def _make_blanks(self, n): @@ -1571,15 +1479,6 @@ def reindent_to(self, column): text.insert("insert", self._make_blanks(column)) text.undo_block_stop() - def _asktabwidth(self): - return self.askinteger( - "Tab width", - "Columns per tab? (2-16)", - parent=self.text, - initialvalue=self.indentwidth, - minvalue=2, - maxvalue=16) - # Guess indentwidth from text content. # Return guessed indentwidth. This should not be believed unless # it's in a reasonable range (e.g., it will be 0 if no indented diff --git a/Lib/idlelib/format.py b/Lib/idlelib/format.py new file mode 100644 index 000000000000..e11ca3a9d26f --- /dev/null +++ b/Lib/idlelib/format.py @@ -0,0 +1,357 @@ +"""Format all or a selected region (line slice) of text. + +Region formatting options: paragraph, comment block, indent, deindent, +comment, uncomment, tabify, and untabify. + +File renamed from paragraph.py with functions added from editor.py. +""" +import re +from tkinter.simpledialog import askinteger +from idlelib.config import idleConf + + +class FormatParagraph: + """Format a paragraph, comment block, or selection to a max width. + + Does basic, standard text formatting, and also understands Python + comment blocks. Thus, for editing Python source code, this + extension is really only suitable for reformatting these comment + blocks or triple-quoted strings. + + Known problems with comment reformatting: + * If there is a selection marked, and the first line of the + selection is not complete, the block will probably not be detected + as comments, and will have the normal "text formatting" rules + applied. + * If a comment block has leading whitespace that mixes tabs and + spaces, they will not be considered part of the same block. + * Fancy comments, like this bulleted list, aren't handled :-) + """ + def __init__(self, editwin): + self.editwin = editwin + + @classmethod + def reload(cls): + cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph', + 'max-width', type='int', default=72) + + def close(self): + self.editwin = None + + def format_paragraph_event(self, event, limit=None): + """Formats paragraph to a max width specified in idleConf. + + If text is selected, format_paragraph_event will start breaking lines + at the max width, starting from the beginning selection. + + If no text is selected, format_paragraph_event uses the current + cursor location to determine the paragraph (lines of text surrounded + by blank lines) and formats it. + + The length limit parameter is for testing with a known value. + """ + limit = self.max_width if limit is None else limit + text = self.editwin.text + first, last = self.editwin.get_selection_indices() + if first and last: + data = text.get(first, last) + comment_header = get_comment_header(data) + else: + first, last, comment_header, data = \ + find_paragraph(text, text.index("insert")) + if comment_header: + newdata = reformat_comment(data, limit, comment_header) + else: + newdata = reformat_paragraph(data, limit) + text.tag_remove("sel", "1.0", "end") + + if newdata != data: + text.mark_set("insert", first) + text.undo_block_start() + text.delete(first, last) + text.insert(first, newdata) + text.undo_block_stop() + else: + text.mark_set("insert", last) + text.see("insert") + return "break" + + +FormatParagraph.reload() + +def find_paragraph(text, mark): + """Returns the start/stop indices enclosing the paragraph that mark is in. + + Also returns the comment format string, if any, and paragraph of text + between the start/stop indices. + """ + lineno, col = map(int, mark.split(".")) + line = text.get("%d.0" % lineno, "%d.end" % lineno) + + # Look for start of next paragraph if the index passed in is a blank line + while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line): + lineno = lineno + 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + first_lineno = lineno + comment_header = get_comment_header(line) + comment_header_len = len(comment_header) + + # Once start line found, search for end of paragraph (a blank line) + while get_comment_header(line)==comment_header and \ + not is_all_white(line[comment_header_len:]): + lineno = lineno + 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + last = "%d.0" % lineno + + # Search back to beginning of paragraph (first blank line before) + lineno = first_lineno - 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + while lineno > 0 and \ + get_comment_header(line)==comment_header and \ + not is_all_white(line[comment_header_len:]): + lineno = lineno - 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + first = "%d.0" % (lineno+1) + + return first, last, comment_header, text.get(first, last) + +# This should perhaps be replaced with textwrap.wrap +def reformat_paragraph(data, limit): + """Return data reformatted to specified width (limit).""" + lines = data.split("\n") + i = 0 + n = len(lines) + while i < n and is_all_white(lines[i]): + i = i+1 + if i >= n: + return data + indent1 = get_indent(lines[i]) + if i+1 < n and not is_all_white(lines[i+1]): + indent2 = get_indent(lines[i+1]) + else: + indent2 = indent1 + new = lines[:i] + partial = indent1 + while i < n and not is_all_white(lines[i]): + # XXX Should take double space after period (etc.) into account + words = re.split(r"(\s+)", lines[i]) + for j in range(0, len(words), 2): + word = words[j] + if not word: + continue # Can happen when line ends in whitespace + if len((partial + word).expandtabs()) > limit and \ + partial != indent1: + new.append(partial.rstrip()) + partial = indent2 + partial = partial + word + " " + if j+1 < len(words) and words[j+1] != " ": + partial = partial + " " + i = i+1 + new.append(partial.rstrip()) + # XXX Should reformat remaining paragraphs as well + new.extend(lines[i:]) + return "\n".join(new) + +def reformat_comment(data, limit, comment_header): + """Return data reformatted to specified width with comment header.""" + + # Remove header from the comment lines + lc = len(comment_header) + data = "\n".join(line[lc:] for line in data.split("\n")) + # Reformat to maxformatwidth chars or a 20 char width, + # whichever is greater. + format_width = max(limit - len(comment_header), 20) + newdata = reformat_paragraph(data, format_width) + # re-split and re-insert the comment header. + newdata = newdata.split("\n") + # If the block ends in a \n, we don't want the comment prefix + # inserted after it. (Im not sure it makes sense to reformat a + # comment block that is not made of complete lines, but whatever!) + # Can't think of a clean solution, so we hack away + block_suffix = "" + if not newdata[-1]: + block_suffix = "\n" + newdata = newdata[:-1] + return '\n'.join(comment_header+line for line in newdata) + block_suffix + +def is_all_white(line): + """Return True if line is empty or all whitespace.""" + + return re.match(r"^\s*$", line) is not None + +def get_indent(line): + """Return the initial space or tab indent of line.""" + return re.match(r"^([ \t]*)", line).group() + +def get_comment_header(line): + """Return string with leading whitespace and '#' from line or ''. + + A null return indicates that the line is not a comment line. A non- + null return, such as ' #', will be used to find the other lines of + a comment block with the same indent. + """ + m = re.match(r"^([ \t]*#*)", line) + if m is None: return "" + return m.group(1) + + +# Copy from editor.py; importing it would cause an import cycle. +_line_indent_re = re.compile(r'[ \t]*') + +def get_line_indent(line, tabwidth): + """Return a line's indentation as (# chars, effective # of spaces). + + The effective # of spaces is the length after properly "expanding" + the tabs into spaces, as done by str.expandtabs(tabwidth). + """ + m = _line_indent_re.match(line) + return m.end(), len(m.group().expandtabs(tabwidth)) + + +class FormatRegion: + "Format selected text." + + def __init__(self, editwin): + self.editwin = editwin + + def get_region(self): + """Return line information about the selected text region. + + If text is selected, the first and last indices will be + for the selection. If there is no text selected, the + indices will be the current cursor location. + + Return a tuple containing (first index, last index, + string representation of text, list of text lines). + """ + text = self.editwin.text + first, last = self.editwin.get_selection_indices() + if first and last: + head = text.index(first + " linestart") + tail = text.index(last + "-1c lineend +1c") + else: + head = text.index("insert linestart") + tail = text.index("insert lineend +1c") + chars = text.get(head, tail) + lines = chars.split("\n") + return head, tail, chars, lines + + def set_region(self, head, tail, chars, lines): + """Replace the text between the given indices. + + Args: + head: Starting index of text to replace. + tail: Ending index of text to replace. + chars: Expected to be string of current text + between head and tail. + lines: List of new lines to insert between head + and tail. + """ + text = self.editwin.text + newchars = "\n".join(lines) + if newchars == chars: + text.bell() + return + text.tag_remove("sel", "1.0", "end") + text.mark_set("insert", head) + text.undo_block_start() + text.delete(head, tail) + text.insert(head, newchars) + text.undo_block_stop() + text.tag_add("sel", head, "insert") + + def indent_region_event(self, event=None): + "Indent region by indentwidth spaces." + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = get_line_indent(line, self.editwin.tabwidth) + effective = effective + self.editwin.indentwidth + lines[pos] = self.editwin._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def dedent_region_event(self, event=None): + "Dedent region by indentwidth spaces." + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = get_line_indent(line, self.editwin.tabwidth) + effective = max(effective - self.editwin.indentwidth, 0) + lines[pos] = self.editwin._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def comment_region_event(self, event=None): + """Comment out each line in region. + + ## is appended to the beginning of each line to comment it out. + """ + head, tail, chars, lines = self.get_region() + for pos in range(len(lines) - 1): + line = lines[pos] + lines[pos] = '##' + line + self.set_region(head, tail, chars, lines) + return "break" + + def uncomment_region_event(self, event=None): + """Uncomment each line in region. + + Remove ## or # in the first positions of a line. If the comment + is not in the beginning position, this command will have no effect. + """ + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if not line: + continue + if line[:2] == '##': + line = line[2:] + elif line[:1] == '#': + line = line[1:] + lines[pos] = line + self.set_region(head, tail, chars, lines) + return "break" + + def tabify_region_event(self, event=None): + "Convert leading spaces to tabs for each line in selected region." + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + if tabwidth is None: + return + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = get_line_indent(line, tabwidth) + ntabs, nspaces = divmod(effective, tabwidth) + lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def untabify_region_event(self, event=None): + "Expand tabs to spaces for each line in region." + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + if tabwidth is None: + return + for pos in range(len(lines)): + lines[pos] = lines[pos].expandtabs(tabwidth) + self.set_region(head, tail, chars, lines) + return "break" + + def _asktabwidth(self): + "Return value for tab width." + return askinteger( + "Tab width", + "Columns per tab? (2-16)", + parent=self.editwin.text, + initialvalue=self.editwin.indentwidth, + minvalue=2, + maxvalue=16) + + +if __name__ == "__main__": + from unittest import main + main('idlelib.idle_test.test_format', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_paragraph.py b/Lib/idlelib/idle_test/test_format.py similarity index 64% rename from Lib/idlelib/idle_test/test_paragraph.py rename to Lib/idlelib/idle_test/test_format.py index 0cb966fb96ca..a2d27ed69dd1 100644 --- a/Lib/idlelib/idle_test/test_paragraph.py +++ b/Lib/idlelib/idle_test/test_format.py @@ -1,7 +1,8 @@ -"Test paragraph, coverage 76%." +"Test format, coverage 99%." -from idlelib import paragraph as pg +from idlelib import format as ft import unittest +from unittest import mock from test.support import requires from tkinter import Tk, Text from idlelib.editor import EditorWindow @@ -16,26 +17,26 @@ class Is_Get_Test(unittest.TestCase): leadingws_nocomment = ' This is not a comment' def test_is_all_white(self): - self.assertTrue(pg.is_all_white('')) - self.assertTrue(pg.is_all_white('\t\n\r\f\v')) - self.assertFalse(pg.is_all_white(self.test_comment)) + self.assertTrue(ft.is_all_white('')) + self.assertTrue(ft.is_all_white('\t\n\r\f\v')) + self.assertFalse(ft.is_all_white(self.test_comment)) def test_get_indent(self): Equal = self.assertEqual - Equal(pg.get_indent(self.test_comment), '') - Equal(pg.get_indent(self.trailingws_comment), '') - Equal(pg.get_indent(self.leadingws_comment), ' ') - Equal(pg.get_indent(self.leadingws_nocomment), ' ') + Equal(ft.get_indent(self.test_comment), '') + Equal(ft.get_indent(self.trailingws_comment), '') + Equal(ft.get_indent(self.leadingws_comment), ' ') + Equal(ft.get_indent(self.leadingws_nocomment), ' ') def test_get_comment_header(self): Equal = self.assertEqual # Test comment strings - Equal(pg.get_comment_header(self.test_comment), '#') - Equal(pg.get_comment_header(self.trailingws_comment), '#') - Equal(pg.get_comment_header(self.leadingws_comment), ' #') + Equal(ft.get_comment_header(self.test_comment), '#') + Equal(ft.get_comment_header(self.trailingws_comment), '#') + Equal(ft.get_comment_header(self.leadingws_comment), ' #') # Test non-comment strings - Equal(pg.get_comment_header(self.leadingws_nocomment), ' ') - Equal(pg.get_comment_header(self.test_nocomment), '') + Equal(ft.get_comment_header(self.leadingws_nocomment), ' ') + Equal(ft.get_comment_header(self.test_nocomment), '') class FindTest(unittest.TestCase): @@ -63,7 +64,7 @@ def runcase(self, inserttext, stopline, expected): linelength = int(text.index("%d.end" % line).split('.')[1]) for col in (0, linelength//2, linelength): tempindex = "%d.%d" % (line, col) - self.assertEqual(pg.find_paragraph(text, tempindex), expected) + self.assertEqual(ft.find_paragraph(text, tempindex), expected) text.delete('1.0', 'end') def test_find_comment(self): @@ -162,7 +163,7 @@ class ReformatFunctionTest(unittest.TestCase): def test_reformat_paragraph(self): Equal = self.assertEqual - reform = pg.reformat_paragraph + reform = ft.reformat_paragraph hw = "O hello world" Equal(reform(' ', 1), ' ') Equal(reform("Hello world", 20), "Hello world") @@ -193,7 +194,7 @@ def test_reformat_comment(self): test_string = ( " \"\"\"this is a test of a reformat for a triple quoted string" " will it reformat to less than 70 characters for me?\"\"\"") - result = pg.reformat_comment(test_string, 70, " ") + result = ft.reformat_comment(test_string, 70, " ") expected = ( " \"\"\"this is a test of a reformat for a triple quoted string will it\n" " reformat to less than 70 characters for me?\"\"\"") @@ -202,7 +203,7 @@ def test_reformat_comment(self): test_comment = ( "# this is a test of a reformat for a triple quoted string will " "it reformat to less than 70 characters for me?") - result = pg.reformat_comment(test_comment, 70, "#") + result = ft.reformat_comment(test_comment, 70, "#") expected = ( "# this is a test of a reformat for a triple quoted string will it\n" "# reformat to less than 70 characters for me?") @@ -211,7 +212,7 @@ def test_reformat_comment(self): class FormatClassTest(unittest.TestCase): def test_init_close(self): - instance = pg.FormatParagraph('editor') + instance = ft.FormatParagraph('editor') self.assertEqual(instance.editwin, 'editor') instance.close() self.assertEqual(instance.editwin, None) @@ -273,7 +274,7 @@ def setUpClass(cls): cls.root.withdraw() editor = Editor(root=cls.root) cls.text = editor.text.text # Test code does not need the wrapper. - cls.formatter = pg.FormatParagraph(editor).format_paragraph_event + cls.formatter = ft.FormatParagraph(editor).format_paragraph_event # Sets the insert mark just after the re-wrapped and inserted text. @classmethod @@ -375,5 +376,202 @@ def test_comment_block(self): ## text.delete('1.0', 'end') +class DummyEditwin: + def __init__(self, root, text): + self.root = root + self.text = text + self.indentwidth = 4 + self.tabwidth = 4 + self.usetabs = False + self.context_use_ps1 = True + + _make_blanks = EditorWindow._make_blanks + get_selection_indices = EditorWindow.get_selection_indices + + +class FormatRegionTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.root.withdraw() + cls.text = Text(cls.root) + cls.text.undo_block_start = mock.Mock() + cls.text.undo_block_stop = mock.Mock() + cls.editor = DummyEditwin(cls.root, cls.text) + cls.formatter = ft.FormatRegion(cls.editor) + + @classmethod + def tearDownClass(cls): + del cls.text, cls.formatter, cls.editor + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + + def setUp(self): + self.text.insert('1.0', self.code_sample) + + def tearDown(self): + self.text.delete('1.0', 'end') + + code_sample = """\ + +class C1(): + # Class comment. + def __init__(self, a, b): + self.a = a + self.b = b + + def compare(self): + if a > b: + return a + elif a < b: + return b + else: + return None +""" + + def test_get_region(self): + get = self.formatter.get_region + text = self.text + eq = self.assertEqual + + # Add selection. + text.tag_add('sel', '7.0', '10.0') + expected_lines = ['', + ' def compare(self):', + ' if a > b:', + ''] + eq(get(), ('7.0', '10.0', '\n'.join(expected_lines), expected_lines)) + + # Remove selection. + text.tag_remove('sel', '1.0', 'end') + eq(get(), ('15.0', '16.0', '\n', ['', ''])) + + def test_set_region(self): + set_ = self.formatter.set_region + text = self.text + eq = self.assertEqual + + save_bell = text.bell + text.bell = mock.Mock() + line6 = self.code_sample.splitlines()[5] + line10 = self.code_sample.splitlines()[9] + + text.tag_add('sel', '6.0', '11.0') + head, tail, chars, lines = self.formatter.get_region() + + # No changes. + set_(head, tail, chars, lines) + text.bell.assert_called_once() + eq(text.get('6.0', '11.0'), chars) + eq(text.get('sel.first', 'sel.last'), chars) + text.tag_remove('sel', '1.0', 'end') + + # Alter selected lines by changing lines and adding a newline. + newstring = 'added line 1\n\n\n\n' + newlines = newstring.split('\n') + set_('7.0', '10.0', chars, newlines) + # Selection changed. + eq(text.get('sel.first', 'sel.last'), newstring) + # Additional line added, so last index is changed. + eq(text.get('7.0', '11.0'), newstring) + # Before and after lines unchanged. + eq(text.get('6.0', '7.0-1c'), line6) + eq(text.get('11.0', '12.0-1c'), line10) + text.tag_remove('sel', '1.0', 'end') + + text.bell = save_bell + + def test_indent_region_event(self): + indent = self.formatter.indent_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + indent() + # Blank lines aren't affected by indent. + eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) + + def test_dedent_region_event(self): + dedent = self.formatter.dedent_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + dedent() + # Blank lines aren't affected by dedent. + eq(text.get('7.0', '10.0'), ('\ndef compare(self):\n if a > b:\n')) + + def test_comment_region_event(self): + comment = self.formatter.comment_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + comment() + eq(text.get('7.0', '10.0'), ('##\n## def compare(self):\n## if a > b:\n')) + + def test_uncomment_region_event(self): + comment = self.formatter.comment_region_event + uncomment = self.formatter.uncomment_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + comment() + uncomment() + eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) + + # Only remove comments at the beginning of a line. + text.tag_remove('sel', '1.0', 'end') + text.tag_add('sel', '3.0', '4.0') + uncomment() + eq(text.get('3.0', '3.end'), (' # Class comment.')) + + self.formatter.set_region('3.0', '4.0', '', ['# Class comment.', '']) + uncomment() + eq(text.get('3.0', '3.end'), (' Class comment.')) + + @mock.patch.object(ft.FormatRegion, "_asktabwidth") + def test_tabify_region_event(self, _asktabwidth): + tabify = self.formatter.tabify_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + # No tabwidth selected. + _asktabwidth.return_value = None + self.assertIsNone(tabify()) + + _asktabwidth.return_value = 3 + self.assertIsNotNone(tabify()) + eq(text.get('7.0', '10.0'), ('\n\t def compare(self):\n\t\t if a > b:\n')) + + @mock.patch.object(ft.FormatRegion, "_asktabwidth") + def test_untabify_region_event(self, _asktabwidth): + untabify = self.formatter.untabify_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + # No tabwidth selected. + _asktabwidth.return_value = None + self.assertIsNone(untabify()) + + _asktabwidth.return_value = 2 + self.formatter.tabify_region_event() + _asktabwidth.return_value = 3 + self.assertIsNotNone(untabify()) + eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) + + @mock.patch.object(ft, "askinteger") + def test_ask_tabwidth(self, askinteger): + ask = self.formatter._asktabwidth + askinteger.return_value = 10 + self.assertEqual(ask(), 10) + + if __name__ == '__main__': unittest.main(verbosity=2, exit=2) diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py index 1b8dc475650d..b0c85cf505c7 100644 --- a/Lib/idlelib/mainmenu.py +++ b/Lib/idlelib/mainmenu.py @@ -60,6 +60,7 @@ ]), ('format', [ + ('F_ormat Paragraph', '<>'), ('_Indent Region', '<>'), ('_Dedent Region', '<>'), ('Comment _Out Region', '<>'), @@ -68,7 +69,6 @@ ('Untabify Region', '<>'), ('Toggle Tabs', '<>'), ('New Indent Width', '<>'), - ('F_ormat Paragraph', '<>'), ('S_trip Trailing Whitespace', '<>'), ]), diff --git a/Lib/idlelib/paragraph.py b/Lib/idlelib/paragraph.py deleted file mode 100644 index 81422571fa32..000000000000 --- a/Lib/idlelib/paragraph.py +++ /dev/null @@ -1,194 +0,0 @@ -"""Format a paragraph, comment block, or selection to a max width. - -Does basic, standard text formatting, and also understands Python -comment blocks. Thus, for editing Python source code, this -extension is really only suitable for reformatting these comment -blocks or triple-quoted strings. - -Known problems with comment reformatting: -* If there is a selection marked, and the first line of the - selection is not complete, the block will probably not be detected - as comments, and will have the normal "text formatting" rules - applied. -* If a comment block has leading whitespace that mixes tabs and - spaces, they will not be considered part of the same block. -* Fancy comments, like this bulleted list, aren't handled :-) -""" -import re - -from idlelib.config import idleConf - - -class FormatParagraph: - - def __init__(self, editwin): - self.editwin = editwin - - @classmethod - def reload(cls): - cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph', - 'max-width', type='int', default=72) - - def close(self): - self.editwin = None - - def format_paragraph_event(self, event, limit=None): - """Formats paragraph to a max width specified in idleConf. - - If text is selected, format_paragraph_event will start breaking lines - at the max width, starting from the beginning selection. - - If no text is selected, format_paragraph_event uses the current - cursor location to determine the paragraph (lines of text surrounded - by blank lines) and formats it. - - The length limit parameter is for testing with a known value. - """ - limit = self.max_width if limit is None else limit - text = self.editwin.text - first, last = self.editwin.get_selection_indices() - if first and last: - data = text.get(first, last) - comment_header = get_comment_header(data) - else: - first, last, comment_header, data = \ - find_paragraph(text, text.index("insert")) - if comment_header: - newdata = reformat_comment(data, limit, comment_header) - else: - newdata = reformat_paragraph(data, limit) - text.tag_remove("sel", "1.0", "end") - - if newdata != data: - text.mark_set("insert", first) - text.undo_block_start() - text.delete(first, last) - text.insert(first, newdata) - text.undo_block_stop() - else: - text.mark_set("insert", last) - text.see("insert") - return "break" - - -FormatParagraph.reload() - -def find_paragraph(text, mark): - """Returns the start/stop indices enclosing the paragraph that mark is in. - - Also returns the comment format string, if any, and paragraph of text - between the start/stop indices. - """ - lineno, col = map(int, mark.split(".")) - line = text.get("%d.0" % lineno, "%d.end" % lineno) - - # Look for start of next paragraph if the index passed in is a blank line - while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line): - lineno = lineno + 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - first_lineno = lineno - comment_header = get_comment_header(line) - comment_header_len = len(comment_header) - - # Once start line found, search for end of paragraph (a blank line) - while get_comment_header(line)==comment_header and \ - not is_all_white(line[comment_header_len:]): - lineno = lineno + 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - last = "%d.0" % lineno - - # Search back to beginning of paragraph (first blank line before) - lineno = first_lineno - 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - while lineno > 0 and \ - get_comment_header(line)==comment_header and \ - not is_all_white(line[comment_header_len:]): - lineno = lineno - 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - first = "%d.0" % (lineno+1) - - return first, last, comment_header, text.get(first, last) - -# This should perhaps be replaced with textwrap.wrap -def reformat_paragraph(data, limit): - """Return data reformatted to specified width (limit).""" - lines = data.split("\n") - i = 0 - n = len(lines) - while i < n and is_all_white(lines[i]): - i = i+1 - if i >= n: - return data - indent1 = get_indent(lines[i]) - if i+1 < n and not is_all_white(lines[i+1]): - indent2 = get_indent(lines[i+1]) - else: - indent2 = indent1 - new = lines[:i] - partial = indent1 - while i < n and not is_all_white(lines[i]): - # XXX Should take double space after period (etc.) into account - words = re.split(r"(\s+)", lines[i]) - for j in range(0, len(words), 2): - word = words[j] - if not word: - continue # Can happen when line ends in whitespace - if len((partial + word).expandtabs()) > limit and \ - partial != indent1: - new.append(partial.rstrip()) - partial = indent2 - partial = partial + word + " " - if j+1 < len(words) and words[j+1] != " ": - partial = partial + " " - i = i+1 - new.append(partial.rstrip()) - # XXX Should reformat remaining paragraphs as well - new.extend(lines[i:]) - return "\n".join(new) - -def reformat_comment(data, limit, comment_header): - """Return data reformatted to specified width with comment header.""" - - # Remove header from the comment lines - lc = len(comment_header) - data = "\n".join(line[lc:] for line in data.split("\n")) - # Reformat to maxformatwidth chars or a 20 char width, - # whichever is greater. - format_width = max(limit - len(comment_header), 20) - newdata = reformat_paragraph(data, format_width) - # re-split and re-insert the comment header. - newdata = newdata.split("\n") - # If the block ends in a \n, we don't want the comment prefix - # inserted after it. (Im not sure it makes sense to reformat a - # comment block that is not made of complete lines, but whatever!) - # Can't think of a clean solution, so we hack away - block_suffix = "" - if not newdata[-1]: - block_suffix = "\n" - newdata = newdata[:-1] - return '\n'.join(comment_header+line for line in newdata) + block_suffix - -def is_all_white(line): - """Return True if line is empty or all whitespace.""" - - return re.match(r"^\s*$", line) is not None - -def get_indent(line): - """Return the initial space or tab indent of line.""" - return re.match(r"^([ \t]*)", line).group() - -def get_comment_header(line): - """Return string with leading whitespace and '#' from line or ''. - - A null return indicates that the line is not a comment line. A non- - null return, such as ' #', will be used to find the other lines of - a comment block with the same indent. - """ - m = re.match(r"^([ \t]*#*)", line) - if m is None: return "" - return m.group(1) - - -if __name__ == "__main__": - from unittest import main - main('idlelib.idle_test.test_paragraph', verbosity=2, exit=False) diff --git a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst new file mode 100644 index 000000000000..fabc75fd1c6e --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst @@ -0,0 +1,2 @@ +Rename paragraph.py to format.py and add region formatting methods +from editor.py. Add tests for the latter. From webhook-mailer at python.org Wed Jul 17 10:46:20 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Wed, 17 Jul 2019 14:46:20 -0000 Subject: [Python-checkins] bpo-36390: IDLE: Combine region formatting methods. (GH-12481) (GH-14812) Message-ID: https://github.com/python/cpython/commit/093e9b1268f21624a09777818903f1088c674689 commit: 093e9b1268f21624a09777818903f1088c674689 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-17T10:46:14-04:00 summary: bpo-36390: IDLE: Combine region formatting methods. (GH-12481) (GH-14812) Rename paragraph.py to format.py and add region formatting methods from editor.py. Add tests for the latter. (cherry picked from commit 82494aa6d947c4a320c09c58fe0f100cdcf7af0b) Co-authored-by: Cheryl Sabella files: A Lib/idlelib/format.py A Lib/idlelib/idle_test/test_format.py A Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst D Lib/idlelib/idle_test/test_paragraph.py D Lib/idlelib/paragraph.py M Lib/idlelib/configdialog.py M Lib/idlelib/editor.py M Lib/idlelib/mainmenu.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 807ff60413d1..6ddbc7fc8e4d 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -29,7 +29,7 @@ from idlelib.autocomplete import AutoComplete from idlelib.codecontext import CodeContext from idlelib.parenmatch import ParenMatch -from idlelib.paragraph import FormatParagraph +from idlelib.format import FormatParagraph from idlelib.squeezer import Squeezer changes = ConfigChanges() diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index b972e3db8461..f02498da3521 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,7 +53,7 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext - from idlelib.paragraph import FormatParagraph + from idlelib.format import FormatParagraph, FormatRegion from idlelib.parenmatch import ParenMatch from idlelib.rstrip import Rstrip from idlelib.squeezer import Squeezer @@ -172,13 +172,14 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.smart_backspace_event) text.bind("<>",self.newline_and_indent_event) text.bind("<>",self.smart_indent_event) - text.bind("<>",self.indent_region_event) - text.bind("<>",self.dedent_region_event) - text.bind("<>",self.comment_region_event) - text.bind("<>",self.uncomment_region_event) - text.bind("<>",self.tabify_region_event) - text.bind("<>",self.untabify_region_event) - text.bind("<>",self.toggle_tabs_event) + self.fregion = fregion = self.FormatRegion(self) + text.bind("<>", fregion.indent_region_event) + text.bind("<>", fregion.dedent_region_event) + text.bind("<>", fregion.comment_region_event) + text.bind("<>", fregion.uncomment_region_event) + text.bind("<>", fregion.tabify_region_event) + text.bind("<>", fregion.untabify_region_event) + text.bind("<>", self.toggle_tabs_event) text.bind("<>",self.change_indentwidth_event) text.bind("", self.move_at_edge_if_selection(0)) text.bind("", self.move_at_edge_if_selection(1)) @@ -1290,7 +1291,7 @@ def smart_indent_event(self, event): try: if first and last: if index2line(first) != index2line(last): - return self.indent_region_event(event) + return self.fregion.indent_region_event(event) text.delete(first, last) text.mark_set("insert", first) prefix = text.get("insert linestart", "insert") @@ -1423,72 +1424,6 @@ def inner(offset, _startindex=startindex, return _icis(_startindex + "+%dc" % offset) return inner - def indent_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines)): - line = lines[pos] - if line: - raw, effective = get_line_indent(line, self.tabwidth) - effective = effective + self.indentwidth - lines[pos] = self._make_blanks(effective) + line[raw:] - self.set_region(head, tail, chars, lines) - return "break" - - def dedent_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines)): - line = lines[pos] - if line: - raw, effective = get_line_indent(line, self.tabwidth) - effective = max(effective - self.indentwidth, 0) - lines[pos] = self._make_blanks(effective) + line[raw:] - self.set_region(head, tail, chars, lines) - return "break" - - def comment_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines) - 1): - line = lines[pos] - lines[pos] = '##' + line - self.set_region(head, tail, chars, lines) - return "break" - - def uncomment_region_event(self, event): - head, tail, chars, lines = self.get_region() - for pos in range(len(lines)): - line = lines[pos] - if not line: - continue - if line[:2] == '##': - line = line[2:] - elif line[:1] == '#': - line = line[1:] - lines[pos] = line - self.set_region(head, tail, chars, lines) - return "break" - - def tabify_region_event(self, event): - head, tail, chars, lines = self.get_region() - tabwidth = self._asktabwidth() - if tabwidth is None: return - for pos in range(len(lines)): - line = lines[pos] - if line: - raw, effective = get_line_indent(line, tabwidth) - ntabs, nspaces = divmod(effective, tabwidth) - lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] - self.set_region(head, tail, chars, lines) - return "break" - - def untabify_region_event(self, event): - head, tail, chars, lines = self.get_region() - tabwidth = self._asktabwidth() - if tabwidth is None: return - for pos in range(len(lines)): - lines[pos] = lines[pos].expandtabs(tabwidth) - self.set_region(head, tail, chars, lines) - return "break" - def toggle_tabs_event(self, event): if self.askyesno( "Toggle tabs", @@ -1523,33 +1458,6 @@ def change_indentwidth_event(self, event): self.indentwidth = new return "break" - def get_region(self): - text = self.text - first, last = self.get_selection_indices() - if first and last: - head = text.index(first + " linestart") - tail = text.index(last + "-1c lineend +1c") - else: - head = text.index("insert linestart") - tail = text.index("insert lineend +1c") - chars = text.get(head, tail) - lines = chars.split("\n") - return head, tail, chars, lines - - def set_region(self, head, tail, chars, lines): - text = self.text - newchars = "\n".join(lines) - if newchars == chars: - text.bell() - return - text.tag_remove("sel", "1.0", "end") - text.mark_set("insert", head) - text.undo_block_start() - text.delete(head, tail) - text.insert(head, newchars) - text.undo_block_stop() - text.tag_add("sel", head, "insert") - # Make string that displays as n leading blanks. def _make_blanks(self, n): @@ -1571,15 +1479,6 @@ def reindent_to(self, column): text.insert("insert", self._make_blanks(column)) text.undo_block_stop() - def _asktabwidth(self): - return self.askinteger( - "Tab width", - "Columns per tab? (2-16)", - parent=self.text, - initialvalue=self.indentwidth, - minvalue=2, - maxvalue=16) - # Guess indentwidth from text content. # Return guessed indentwidth. This should not be believed unless # it's in a reasonable range (e.g., it will be 0 if no indented diff --git a/Lib/idlelib/format.py b/Lib/idlelib/format.py new file mode 100644 index 000000000000..e11ca3a9d26f --- /dev/null +++ b/Lib/idlelib/format.py @@ -0,0 +1,357 @@ +"""Format all or a selected region (line slice) of text. + +Region formatting options: paragraph, comment block, indent, deindent, +comment, uncomment, tabify, and untabify. + +File renamed from paragraph.py with functions added from editor.py. +""" +import re +from tkinter.simpledialog import askinteger +from idlelib.config import idleConf + + +class FormatParagraph: + """Format a paragraph, comment block, or selection to a max width. + + Does basic, standard text formatting, and also understands Python + comment blocks. Thus, for editing Python source code, this + extension is really only suitable for reformatting these comment + blocks or triple-quoted strings. + + Known problems with comment reformatting: + * If there is a selection marked, and the first line of the + selection is not complete, the block will probably not be detected + as comments, and will have the normal "text formatting" rules + applied. + * If a comment block has leading whitespace that mixes tabs and + spaces, they will not be considered part of the same block. + * Fancy comments, like this bulleted list, aren't handled :-) + """ + def __init__(self, editwin): + self.editwin = editwin + + @classmethod + def reload(cls): + cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph', + 'max-width', type='int', default=72) + + def close(self): + self.editwin = None + + def format_paragraph_event(self, event, limit=None): + """Formats paragraph to a max width specified in idleConf. + + If text is selected, format_paragraph_event will start breaking lines + at the max width, starting from the beginning selection. + + If no text is selected, format_paragraph_event uses the current + cursor location to determine the paragraph (lines of text surrounded + by blank lines) and formats it. + + The length limit parameter is for testing with a known value. + """ + limit = self.max_width if limit is None else limit + text = self.editwin.text + first, last = self.editwin.get_selection_indices() + if first and last: + data = text.get(first, last) + comment_header = get_comment_header(data) + else: + first, last, comment_header, data = \ + find_paragraph(text, text.index("insert")) + if comment_header: + newdata = reformat_comment(data, limit, comment_header) + else: + newdata = reformat_paragraph(data, limit) + text.tag_remove("sel", "1.0", "end") + + if newdata != data: + text.mark_set("insert", first) + text.undo_block_start() + text.delete(first, last) + text.insert(first, newdata) + text.undo_block_stop() + else: + text.mark_set("insert", last) + text.see("insert") + return "break" + + +FormatParagraph.reload() + +def find_paragraph(text, mark): + """Returns the start/stop indices enclosing the paragraph that mark is in. + + Also returns the comment format string, if any, and paragraph of text + between the start/stop indices. + """ + lineno, col = map(int, mark.split(".")) + line = text.get("%d.0" % lineno, "%d.end" % lineno) + + # Look for start of next paragraph if the index passed in is a blank line + while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line): + lineno = lineno + 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + first_lineno = lineno + comment_header = get_comment_header(line) + comment_header_len = len(comment_header) + + # Once start line found, search for end of paragraph (a blank line) + while get_comment_header(line)==comment_header and \ + not is_all_white(line[comment_header_len:]): + lineno = lineno + 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + last = "%d.0" % lineno + + # Search back to beginning of paragraph (first blank line before) + lineno = first_lineno - 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + while lineno > 0 and \ + get_comment_header(line)==comment_header and \ + not is_all_white(line[comment_header_len:]): + lineno = lineno - 1 + line = text.get("%d.0" % lineno, "%d.end" % lineno) + first = "%d.0" % (lineno+1) + + return first, last, comment_header, text.get(first, last) + +# This should perhaps be replaced with textwrap.wrap +def reformat_paragraph(data, limit): + """Return data reformatted to specified width (limit).""" + lines = data.split("\n") + i = 0 + n = len(lines) + while i < n and is_all_white(lines[i]): + i = i+1 + if i >= n: + return data + indent1 = get_indent(lines[i]) + if i+1 < n and not is_all_white(lines[i+1]): + indent2 = get_indent(lines[i+1]) + else: + indent2 = indent1 + new = lines[:i] + partial = indent1 + while i < n and not is_all_white(lines[i]): + # XXX Should take double space after period (etc.) into account + words = re.split(r"(\s+)", lines[i]) + for j in range(0, len(words), 2): + word = words[j] + if not word: + continue # Can happen when line ends in whitespace + if len((partial + word).expandtabs()) > limit and \ + partial != indent1: + new.append(partial.rstrip()) + partial = indent2 + partial = partial + word + " " + if j+1 < len(words) and words[j+1] != " ": + partial = partial + " " + i = i+1 + new.append(partial.rstrip()) + # XXX Should reformat remaining paragraphs as well + new.extend(lines[i:]) + return "\n".join(new) + +def reformat_comment(data, limit, comment_header): + """Return data reformatted to specified width with comment header.""" + + # Remove header from the comment lines + lc = len(comment_header) + data = "\n".join(line[lc:] for line in data.split("\n")) + # Reformat to maxformatwidth chars or a 20 char width, + # whichever is greater. + format_width = max(limit - len(comment_header), 20) + newdata = reformat_paragraph(data, format_width) + # re-split and re-insert the comment header. + newdata = newdata.split("\n") + # If the block ends in a \n, we don't want the comment prefix + # inserted after it. (Im not sure it makes sense to reformat a + # comment block that is not made of complete lines, but whatever!) + # Can't think of a clean solution, so we hack away + block_suffix = "" + if not newdata[-1]: + block_suffix = "\n" + newdata = newdata[:-1] + return '\n'.join(comment_header+line for line in newdata) + block_suffix + +def is_all_white(line): + """Return True if line is empty or all whitespace.""" + + return re.match(r"^\s*$", line) is not None + +def get_indent(line): + """Return the initial space or tab indent of line.""" + return re.match(r"^([ \t]*)", line).group() + +def get_comment_header(line): + """Return string with leading whitespace and '#' from line or ''. + + A null return indicates that the line is not a comment line. A non- + null return, such as ' #', will be used to find the other lines of + a comment block with the same indent. + """ + m = re.match(r"^([ \t]*#*)", line) + if m is None: return "" + return m.group(1) + + +# Copy from editor.py; importing it would cause an import cycle. +_line_indent_re = re.compile(r'[ \t]*') + +def get_line_indent(line, tabwidth): + """Return a line's indentation as (# chars, effective # of spaces). + + The effective # of spaces is the length after properly "expanding" + the tabs into spaces, as done by str.expandtabs(tabwidth). + """ + m = _line_indent_re.match(line) + return m.end(), len(m.group().expandtabs(tabwidth)) + + +class FormatRegion: + "Format selected text." + + def __init__(self, editwin): + self.editwin = editwin + + def get_region(self): + """Return line information about the selected text region. + + If text is selected, the first and last indices will be + for the selection. If there is no text selected, the + indices will be the current cursor location. + + Return a tuple containing (first index, last index, + string representation of text, list of text lines). + """ + text = self.editwin.text + first, last = self.editwin.get_selection_indices() + if first and last: + head = text.index(first + " linestart") + tail = text.index(last + "-1c lineend +1c") + else: + head = text.index("insert linestart") + tail = text.index("insert lineend +1c") + chars = text.get(head, tail) + lines = chars.split("\n") + return head, tail, chars, lines + + def set_region(self, head, tail, chars, lines): + """Replace the text between the given indices. + + Args: + head: Starting index of text to replace. + tail: Ending index of text to replace. + chars: Expected to be string of current text + between head and tail. + lines: List of new lines to insert between head + and tail. + """ + text = self.editwin.text + newchars = "\n".join(lines) + if newchars == chars: + text.bell() + return + text.tag_remove("sel", "1.0", "end") + text.mark_set("insert", head) + text.undo_block_start() + text.delete(head, tail) + text.insert(head, newchars) + text.undo_block_stop() + text.tag_add("sel", head, "insert") + + def indent_region_event(self, event=None): + "Indent region by indentwidth spaces." + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = get_line_indent(line, self.editwin.tabwidth) + effective = effective + self.editwin.indentwidth + lines[pos] = self.editwin._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def dedent_region_event(self, event=None): + "Dedent region by indentwidth spaces." + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = get_line_indent(line, self.editwin.tabwidth) + effective = max(effective - self.editwin.indentwidth, 0) + lines[pos] = self.editwin._make_blanks(effective) + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def comment_region_event(self, event=None): + """Comment out each line in region. + + ## is appended to the beginning of each line to comment it out. + """ + head, tail, chars, lines = self.get_region() + for pos in range(len(lines) - 1): + line = lines[pos] + lines[pos] = '##' + line + self.set_region(head, tail, chars, lines) + return "break" + + def uncomment_region_event(self, event=None): + """Uncomment each line in region. + + Remove ## or # in the first positions of a line. If the comment + is not in the beginning position, this command will have no effect. + """ + head, tail, chars, lines = self.get_region() + for pos in range(len(lines)): + line = lines[pos] + if not line: + continue + if line[:2] == '##': + line = line[2:] + elif line[:1] == '#': + line = line[1:] + lines[pos] = line + self.set_region(head, tail, chars, lines) + return "break" + + def tabify_region_event(self, event=None): + "Convert leading spaces to tabs for each line in selected region." + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + if tabwidth is None: + return + for pos in range(len(lines)): + line = lines[pos] + if line: + raw, effective = get_line_indent(line, tabwidth) + ntabs, nspaces = divmod(effective, tabwidth) + lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] + self.set_region(head, tail, chars, lines) + return "break" + + def untabify_region_event(self, event=None): + "Expand tabs to spaces for each line in region." + head, tail, chars, lines = self.get_region() + tabwidth = self._asktabwidth() + if tabwidth is None: + return + for pos in range(len(lines)): + lines[pos] = lines[pos].expandtabs(tabwidth) + self.set_region(head, tail, chars, lines) + return "break" + + def _asktabwidth(self): + "Return value for tab width." + return askinteger( + "Tab width", + "Columns per tab? (2-16)", + parent=self.editwin.text, + initialvalue=self.editwin.indentwidth, + minvalue=2, + maxvalue=16) + + +if __name__ == "__main__": + from unittest import main + main('idlelib.idle_test.test_format', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_paragraph.py b/Lib/idlelib/idle_test/test_format.py similarity index 64% rename from Lib/idlelib/idle_test/test_paragraph.py rename to Lib/idlelib/idle_test/test_format.py index 0cb966fb96ca..a2d27ed69dd1 100644 --- a/Lib/idlelib/idle_test/test_paragraph.py +++ b/Lib/idlelib/idle_test/test_format.py @@ -1,7 +1,8 @@ -"Test paragraph, coverage 76%." +"Test format, coverage 99%." -from idlelib import paragraph as pg +from idlelib import format as ft import unittest +from unittest import mock from test.support import requires from tkinter import Tk, Text from idlelib.editor import EditorWindow @@ -16,26 +17,26 @@ class Is_Get_Test(unittest.TestCase): leadingws_nocomment = ' This is not a comment' def test_is_all_white(self): - self.assertTrue(pg.is_all_white('')) - self.assertTrue(pg.is_all_white('\t\n\r\f\v')) - self.assertFalse(pg.is_all_white(self.test_comment)) + self.assertTrue(ft.is_all_white('')) + self.assertTrue(ft.is_all_white('\t\n\r\f\v')) + self.assertFalse(ft.is_all_white(self.test_comment)) def test_get_indent(self): Equal = self.assertEqual - Equal(pg.get_indent(self.test_comment), '') - Equal(pg.get_indent(self.trailingws_comment), '') - Equal(pg.get_indent(self.leadingws_comment), ' ') - Equal(pg.get_indent(self.leadingws_nocomment), ' ') + Equal(ft.get_indent(self.test_comment), '') + Equal(ft.get_indent(self.trailingws_comment), '') + Equal(ft.get_indent(self.leadingws_comment), ' ') + Equal(ft.get_indent(self.leadingws_nocomment), ' ') def test_get_comment_header(self): Equal = self.assertEqual # Test comment strings - Equal(pg.get_comment_header(self.test_comment), '#') - Equal(pg.get_comment_header(self.trailingws_comment), '#') - Equal(pg.get_comment_header(self.leadingws_comment), ' #') + Equal(ft.get_comment_header(self.test_comment), '#') + Equal(ft.get_comment_header(self.trailingws_comment), '#') + Equal(ft.get_comment_header(self.leadingws_comment), ' #') # Test non-comment strings - Equal(pg.get_comment_header(self.leadingws_nocomment), ' ') - Equal(pg.get_comment_header(self.test_nocomment), '') + Equal(ft.get_comment_header(self.leadingws_nocomment), ' ') + Equal(ft.get_comment_header(self.test_nocomment), '') class FindTest(unittest.TestCase): @@ -63,7 +64,7 @@ def runcase(self, inserttext, stopline, expected): linelength = int(text.index("%d.end" % line).split('.')[1]) for col in (0, linelength//2, linelength): tempindex = "%d.%d" % (line, col) - self.assertEqual(pg.find_paragraph(text, tempindex), expected) + self.assertEqual(ft.find_paragraph(text, tempindex), expected) text.delete('1.0', 'end') def test_find_comment(self): @@ -162,7 +163,7 @@ class ReformatFunctionTest(unittest.TestCase): def test_reformat_paragraph(self): Equal = self.assertEqual - reform = pg.reformat_paragraph + reform = ft.reformat_paragraph hw = "O hello world" Equal(reform(' ', 1), ' ') Equal(reform("Hello world", 20), "Hello world") @@ -193,7 +194,7 @@ def test_reformat_comment(self): test_string = ( " \"\"\"this is a test of a reformat for a triple quoted string" " will it reformat to less than 70 characters for me?\"\"\"") - result = pg.reformat_comment(test_string, 70, " ") + result = ft.reformat_comment(test_string, 70, " ") expected = ( " \"\"\"this is a test of a reformat for a triple quoted string will it\n" " reformat to less than 70 characters for me?\"\"\"") @@ -202,7 +203,7 @@ def test_reformat_comment(self): test_comment = ( "# this is a test of a reformat for a triple quoted string will " "it reformat to less than 70 characters for me?") - result = pg.reformat_comment(test_comment, 70, "#") + result = ft.reformat_comment(test_comment, 70, "#") expected = ( "# this is a test of a reformat for a triple quoted string will it\n" "# reformat to less than 70 characters for me?") @@ -211,7 +212,7 @@ def test_reformat_comment(self): class FormatClassTest(unittest.TestCase): def test_init_close(self): - instance = pg.FormatParagraph('editor') + instance = ft.FormatParagraph('editor') self.assertEqual(instance.editwin, 'editor') instance.close() self.assertEqual(instance.editwin, None) @@ -273,7 +274,7 @@ def setUpClass(cls): cls.root.withdraw() editor = Editor(root=cls.root) cls.text = editor.text.text # Test code does not need the wrapper. - cls.formatter = pg.FormatParagraph(editor).format_paragraph_event + cls.formatter = ft.FormatParagraph(editor).format_paragraph_event # Sets the insert mark just after the re-wrapped and inserted text. @classmethod @@ -375,5 +376,202 @@ def test_comment_block(self): ## text.delete('1.0', 'end') +class DummyEditwin: + def __init__(self, root, text): + self.root = root + self.text = text + self.indentwidth = 4 + self.tabwidth = 4 + self.usetabs = False + self.context_use_ps1 = True + + _make_blanks = EditorWindow._make_blanks + get_selection_indices = EditorWindow.get_selection_indices + + +class FormatRegionTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.root.withdraw() + cls.text = Text(cls.root) + cls.text.undo_block_start = mock.Mock() + cls.text.undo_block_stop = mock.Mock() + cls.editor = DummyEditwin(cls.root, cls.text) + cls.formatter = ft.FormatRegion(cls.editor) + + @classmethod + def tearDownClass(cls): + del cls.text, cls.formatter, cls.editor + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + + def setUp(self): + self.text.insert('1.0', self.code_sample) + + def tearDown(self): + self.text.delete('1.0', 'end') + + code_sample = """\ + +class C1(): + # Class comment. + def __init__(self, a, b): + self.a = a + self.b = b + + def compare(self): + if a > b: + return a + elif a < b: + return b + else: + return None +""" + + def test_get_region(self): + get = self.formatter.get_region + text = self.text + eq = self.assertEqual + + # Add selection. + text.tag_add('sel', '7.0', '10.0') + expected_lines = ['', + ' def compare(self):', + ' if a > b:', + ''] + eq(get(), ('7.0', '10.0', '\n'.join(expected_lines), expected_lines)) + + # Remove selection. + text.tag_remove('sel', '1.0', 'end') + eq(get(), ('15.0', '16.0', '\n', ['', ''])) + + def test_set_region(self): + set_ = self.formatter.set_region + text = self.text + eq = self.assertEqual + + save_bell = text.bell + text.bell = mock.Mock() + line6 = self.code_sample.splitlines()[5] + line10 = self.code_sample.splitlines()[9] + + text.tag_add('sel', '6.0', '11.0') + head, tail, chars, lines = self.formatter.get_region() + + # No changes. + set_(head, tail, chars, lines) + text.bell.assert_called_once() + eq(text.get('6.0', '11.0'), chars) + eq(text.get('sel.first', 'sel.last'), chars) + text.tag_remove('sel', '1.0', 'end') + + # Alter selected lines by changing lines and adding a newline. + newstring = 'added line 1\n\n\n\n' + newlines = newstring.split('\n') + set_('7.0', '10.0', chars, newlines) + # Selection changed. + eq(text.get('sel.first', 'sel.last'), newstring) + # Additional line added, so last index is changed. + eq(text.get('7.0', '11.0'), newstring) + # Before and after lines unchanged. + eq(text.get('6.0', '7.0-1c'), line6) + eq(text.get('11.0', '12.0-1c'), line10) + text.tag_remove('sel', '1.0', 'end') + + text.bell = save_bell + + def test_indent_region_event(self): + indent = self.formatter.indent_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + indent() + # Blank lines aren't affected by indent. + eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) + + def test_dedent_region_event(self): + dedent = self.formatter.dedent_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + dedent() + # Blank lines aren't affected by dedent. + eq(text.get('7.0', '10.0'), ('\ndef compare(self):\n if a > b:\n')) + + def test_comment_region_event(self): + comment = self.formatter.comment_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + comment() + eq(text.get('7.0', '10.0'), ('##\n## def compare(self):\n## if a > b:\n')) + + def test_uncomment_region_event(self): + comment = self.formatter.comment_region_event + uncomment = self.formatter.uncomment_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + comment() + uncomment() + eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) + + # Only remove comments at the beginning of a line. + text.tag_remove('sel', '1.0', 'end') + text.tag_add('sel', '3.0', '4.0') + uncomment() + eq(text.get('3.0', '3.end'), (' # Class comment.')) + + self.formatter.set_region('3.0', '4.0', '', ['# Class comment.', '']) + uncomment() + eq(text.get('3.0', '3.end'), (' Class comment.')) + + @mock.patch.object(ft.FormatRegion, "_asktabwidth") + def test_tabify_region_event(self, _asktabwidth): + tabify = self.formatter.tabify_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + # No tabwidth selected. + _asktabwidth.return_value = None + self.assertIsNone(tabify()) + + _asktabwidth.return_value = 3 + self.assertIsNotNone(tabify()) + eq(text.get('7.0', '10.0'), ('\n\t def compare(self):\n\t\t if a > b:\n')) + + @mock.patch.object(ft.FormatRegion, "_asktabwidth") + def test_untabify_region_event(self, _asktabwidth): + untabify = self.formatter.untabify_region_event + text = self.text + eq = self.assertEqual + + text.tag_add('sel', '7.0', '10.0') + # No tabwidth selected. + _asktabwidth.return_value = None + self.assertIsNone(untabify()) + + _asktabwidth.return_value = 2 + self.formatter.tabify_region_event() + _asktabwidth.return_value = 3 + self.assertIsNotNone(untabify()) + eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) + + @mock.patch.object(ft, "askinteger") + def test_ask_tabwidth(self, askinteger): + ask = self.formatter._asktabwidth + askinteger.return_value = 10 + self.assertEqual(ask(), 10) + + if __name__ == '__main__': unittest.main(verbosity=2, exit=2) diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py index 1b8dc475650d..b0c85cf505c7 100644 --- a/Lib/idlelib/mainmenu.py +++ b/Lib/idlelib/mainmenu.py @@ -60,6 +60,7 @@ ]), ('format', [ + ('F_ormat Paragraph', '<>'), ('_Indent Region', '<>'), ('_Dedent Region', '<>'), ('Comment _Out Region', '<>'), @@ -68,7 +69,6 @@ ('Untabify Region', '<>'), ('Toggle Tabs', '<>'), ('New Indent Width', '<>'), - ('F_ormat Paragraph', '<>'), ('S_trip Trailing Whitespace', '<>'), ]), diff --git a/Lib/idlelib/paragraph.py b/Lib/idlelib/paragraph.py deleted file mode 100644 index 81422571fa32..000000000000 --- a/Lib/idlelib/paragraph.py +++ /dev/null @@ -1,194 +0,0 @@ -"""Format a paragraph, comment block, or selection to a max width. - -Does basic, standard text formatting, and also understands Python -comment blocks. Thus, for editing Python source code, this -extension is really only suitable for reformatting these comment -blocks or triple-quoted strings. - -Known problems with comment reformatting: -* If there is a selection marked, and the first line of the - selection is not complete, the block will probably not be detected - as comments, and will have the normal "text formatting" rules - applied. -* If a comment block has leading whitespace that mixes tabs and - spaces, they will not be considered part of the same block. -* Fancy comments, like this bulleted list, aren't handled :-) -""" -import re - -from idlelib.config import idleConf - - -class FormatParagraph: - - def __init__(self, editwin): - self.editwin = editwin - - @classmethod - def reload(cls): - cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph', - 'max-width', type='int', default=72) - - def close(self): - self.editwin = None - - def format_paragraph_event(self, event, limit=None): - """Formats paragraph to a max width specified in idleConf. - - If text is selected, format_paragraph_event will start breaking lines - at the max width, starting from the beginning selection. - - If no text is selected, format_paragraph_event uses the current - cursor location to determine the paragraph (lines of text surrounded - by blank lines) and formats it. - - The length limit parameter is for testing with a known value. - """ - limit = self.max_width if limit is None else limit - text = self.editwin.text - first, last = self.editwin.get_selection_indices() - if first and last: - data = text.get(first, last) - comment_header = get_comment_header(data) - else: - first, last, comment_header, data = \ - find_paragraph(text, text.index("insert")) - if comment_header: - newdata = reformat_comment(data, limit, comment_header) - else: - newdata = reformat_paragraph(data, limit) - text.tag_remove("sel", "1.0", "end") - - if newdata != data: - text.mark_set("insert", first) - text.undo_block_start() - text.delete(first, last) - text.insert(first, newdata) - text.undo_block_stop() - else: - text.mark_set("insert", last) - text.see("insert") - return "break" - - -FormatParagraph.reload() - -def find_paragraph(text, mark): - """Returns the start/stop indices enclosing the paragraph that mark is in. - - Also returns the comment format string, if any, and paragraph of text - between the start/stop indices. - """ - lineno, col = map(int, mark.split(".")) - line = text.get("%d.0" % lineno, "%d.end" % lineno) - - # Look for start of next paragraph if the index passed in is a blank line - while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line): - lineno = lineno + 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - first_lineno = lineno - comment_header = get_comment_header(line) - comment_header_len = len(comment_header) - - # Once start line found, search for end of paragraph (a blank line) - while get_comment_header(line)==comment_header and \ - not is_all_white(line[comment_header_len:]): - lineno = lineno + 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - last = "%d.0" % lineno - - # Search back to beginning of paragraph (first blank line before) - lineno = first_lineno - 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - while lineno > 0 and \ - get_comment_header(line)==comment_header and \ - not is_all_white(line[comment_header_len:]): - lineno = lineno - 1 - line = text.get("%d.0" % lineno, "%d.end" % lineno) - first = "%d.0" % (lineno+1) - - return first, last, comment_header, text.get(first, last) - -# This should perhaps be replaced with textwrap.wrap -def reformat_paragraph(data, limit): - """Return data reformatted to specified width (limit).""" - lines = data.split("\n") - i = 0 - n = len(lines) - while i < n and is_all_white(lines[i]): - i = i+1 - if i >= n: - return data - indent1 = get_indent(lines[i]) - if i+1 < n and not is_all_white(lines[i+1]): - indent2 = get_indent(lines[i+1]) - else: - indent2 = indent1 - new = lines[:i] - partial = indent1 - while i < n and not is_all_white(lines[i]): - # XXX Should take double space after period (etc.) into account - words = re.split(r"(\s+)", lines[i]) - for j in range(0, len(words), 2): - word = words[j] - if not word: - continue # Can happen when line ends in whitespace - if len((partial + word).expandtabs()) > limit and \ - partial != indent1: - new.append(partial.rstrip()) - partial = indent2 - partial = partial + word + " " - if j+1 < len(words) and words[j+1] != " ": - partial = partial + " " - i = i+1 - new.append(partial.rstrip()) - # XXX Should reformat remaining paragraphs as well - new.extend(lines[i:]) - return "\n".join(new) - -def reformat_comment(data, limit, comment_header): - """Return data reformatted to specified width with comment header.""" - - # Remove header from the comment lines - lc = len(comment_header) - data = "\n".join(line[lc:] for line in data.split("\n")) - # Reformat to maxformatwidth chars or a 20 char width, - # whichever is greater. - format_width = max(limit - len(comment_header), 20) - newdata = reformat_paragraph(data, format_width) - # re-split and re-insert the comment header. - newdata = newdata.split("\n") - # If the block ends in a \n, we don't want the comment prefix - # inserted after it. (Im not sure it makes sense to reformat a - # comment block that is not made of complete lines, but whatever!) - # Can't think of a clean solution, so we hack away - block_suffix = "" - if not newdata[-1]: - block_suffix = "\n" - newdata = newdata[:-1] - return '\n'.join(comment_header+line for line in newdata) + block_suffix - -def is_all_white(line): - """Return True if line is empty or all whitespace.""" - - return re.match(r"^\s*$", line) is not None - -def get_indent(line): - """Return the initial space or tab indent of line.""" - return re.match(r"^([ \t]*)", line).group() - -def get_comment_header(line): - """Return string with leading whitespace and '#' from line or ''. - - A null return indicates that the line is not a comment line. A non- - null return, such as ' #', will be used to find the other lines of - a comment block with the same indent. - """ - m = re.match(r"^([ \t]*#*)", line) - if m is None: return "" - return m.group(1) - - -if __name__ == "__main__": - from unittest import main - main('idlelib.idle_test.test_paragraph', verbosity=2, exit=False) diff --git a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst new file mode 100644 index 000000000000..fabc75fd1c6e --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst @@ -0,0 +1,2 @@ +Rename paragraph.py to format.py and add region formatting methods +from editor.py. Add tests for the latter. From webhook-mailer at python.org Wed Jul 17 12:44:42 2019 From: webhook-mailer at python.org (Barry Warsaw) Date: Wed, 17 Jul 2019 16:44:42 -0000 Subject: [Python-checkins] bpo-37461: Fix infinite loop in parsing of specially crafted email headers (GH-14794) Message-ID: https://github.com/python/cpython/commit/a4a994bd3e619cbaff97610a1cee8ffa87c672f5 commit: a4a994bd3e619cbaff97610a1cee8ffa87c672f5 branch: master author: Abhilash Raj committer: Barry Warsaw date: 2019-07-17T09:44:27-07:00 summary: bpo-37461: Fix infinite loop in parsing of specially crafted email headers (GH-14794) * bpo-37461: Fix infinite loop in parsing of specially crafted email headers. Some crafted email header would cause the get_parameter method to run in an infinite loop causing a DoS attack surface when parsing those headers. This patch fixes that by making sure the DQUOTE character is handled to prevent going into an infinite loop. files: A Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst M Lib/email/_header_value_parser.py M Lib/test/test_email/test__header_value_parser.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index 37dc76470160..66b042ee0ef0 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -2496,6 +2496,9 @@ def get_parameter(value): while value: if value[0] in WSP: token, value = get_fws(value) + elif value[0] == '"': + token = ValueTerminal('"', 'DQUOTE') + value = value[1:] else: token, value = get_qcontent(value) v.append(token) diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index c4e1a9f99495..a83915d6d059 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -2710,6 +2710,13 @@ def mime_parameters_as_value(self, # Defects are apparent missing *0*, and two 'out of sequence'. [errors.InvalidHeaderDefect]*3), + # bpo-37461: Check that we don't go into an infinite loop. + 'extra_dquote': ( + 'r*="\'a\'\\"', + ' r="\\""', + 'r*=\'a\'"', + [('r', '"')], + [errors.InvalidHeaderDefect]*2), } @parameterize diff --git a/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst new file mode 100644 index 000000000000..4bfd350c0b40 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst @@ -0,0 +1,2 @@ +Fix an inifite loop when parsing specially crafted email headers. Patch by +Abhilash Raj. From webhook-mailer at python.org Wed Jul 17 12:48:56 2019 From: webhook-mailer at python.org (Barry Warsaw) Date: Wed, 17 Jul 2019 16:48:56 -0000 Subject: [Python-checkins] Fix IndexError when parsing unexpectedly ending quoted-string. (GH-14813) Message-ID: https://github.com/python/cpython/commit/719a062bcb7b08a56e6576dcd75f4244e6053209 commit: 719a062bcb7b08a56e6576dcd75f4244e6053209 branch: master author: Abhilash Raj committer: Barry Warsaw date: 2019-07-17T09:48:52-07:00 summary: Fix IndexError when parsing unexpectedly ending quoted-string. (GH-14813) This exception was caused because the input ended unexpectedly with only one single quote instead of a pair with some value inside it. files: A Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst M Lib/email/_header_value_parser.py M Lib/test/test_email/test__header_value_parser.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index 66b042ee0ef0..daff57b9cfa2 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -1191,7 +1191,7 @@ def get_bare_quoted_string(value): "expected '\"' but found '{}'".format(value)) bare_quoted_string = BareQuotedString() value = value[1:] - if value[0] == '"': + if value and value[0] == '"': token, value = get_qcontent(value) bare_quoted_string.append(token) while value and value[0] != '"': diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index a83915d6d059..4fa56b16c478 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -522,6 +522,10 @@ def test_get_bare_quoted_string_only_quotes(self): self._test_get_x(parser.get_bare_quoted_string, '""', '""', '', [], '') + def test_get_bare_quoted_string_missing_endquotes(self): + self._test_get_x(parser.get_bare_quoted_string, + '"', '""', '', [errors.InvalidHeaderDefect], '') + def test_get_bare_quoted_string_following_wsp_preserved(self): self._test_get_x(parser.get_bare_quoted_string, '"foo"\t bar', '"foo"', 'foo', [], '\t bar') diff --git a/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst b/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst new file mode 100644 index 000000000000..af4287ba478b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst @@ -0,0 +1,2 @@ +Fix ``IndexError`` when parsing email headers with unexpectedly ending +bare-quoted string value. Patch by Abhilash Raj. From webhook-mailer at python.org Wed Jul 17 13:02:12 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 17 Jul 2019 17:02:12 -0000 Subject: [Python-checkins] bpo-37461: Fix infinite loop in parsing of specially crafted email headers (GH-14794) Message-ID: https://github.com/python/cpython/commit/391511ccaaf0050970dfbe95bf2df1bcf6c33440 commit: 391511ccaaf0050970dfbe95bf2df1bcf6c33440 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-17T10:02:05-07:00 summary: bpo-37461: Fix infinite loop in parsing of specially crafted email headers (GH-14794) * bpo-37461: Fix infinite loop in parsing of specially crafted email headers. Some crafted email header would cause the get_parameter method to run in an infinite loop causing a DoS attack surface when parsing those headers. This patch fixes that by making sure the DQUOTE character is handled to prevent going into an infinite loop. (cherry picked from commit a4a994bd3e619cbaff97610a1cee8ffa87c672f5) Co-authored-by: Abhilash Raj files: A Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst M Lib/email/_header_value_parser.py M Lib/test/test_email/test__header_value_parser.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index dbb2b6469605..a342a8e94742 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -2390,6 +2390,9 @@ def get_parameter(value): while value: if value[0] in WSP: token, value = get_fws(value) + elif value[0] == '"': + token = ValueTerminal('"', 'DQUOTE') + value = value[1:] else: token, value = get_qcontent(value) v.append(token) diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 9e8df3af043e..55c83efbcd83 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -2638,6 +2638,13 @@ def mime_parameters_as_value(self, # Defects are apparent missing *0*, and two 'out of sequence'. [errors.InvalidHeaderDefect]*3), + # bpo-37461: Check that we don't go into an infinite loop. + 'extra_dquote': ( + 'r*="\'a\'\\"', + ' r="\\""', + 'r*=\'a\'"', + [('r', '"')], + [errors.InvalidHeaderDefect]*2), } @parameterize diff --git a/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst new file mode 100644 index 000000000000..4bfd350c0b40 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst @@ -0,0 +1,2 @@ +Fix an inifite loop when parsing specially crafted email headers. Patch by +Abhilash Raj. From webhook-mailer at python.org Wed Jul 17 13:14:11 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 17 Jul 2019 17:14:11 -0000 Subject: [Python-checkins] bpo-37461: Fix infinite loop in parsing of specially crafted email headers (GH-14794) Message-ID: https://github.com/python/cpython/commit/6816ca30af7705db691343100e696ea3d8f447d5 commit: 6816ca30af7705db691343100e696ea3d8f447d5 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-17T10:13:54-07:00 summary: bpo-37461: Fix infinite loop in parsing of specially crafted email headers (GH-14794) * bpo-37461: Fix infinite loop in parsing of specially crafted email headers. Some crafted email header would cause the get_parameter method to run in an infinite loop causing a DoS attack surface when parsing those headers. This patch fixes that by making sure the DQUOTE character is handled to prevent going into an infinite loop. (cherry picked from commit a4a994bd3e619cbaff97610a1cee8ffa87c672f5) Co-authored-by: Abhilash Raj files: A Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst M Lib/email/_header_value_parser.py M Lib/test/test_email/test__header_value_parser.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index 64753218a369..2fc8abcbc5ad 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -2475,6 +2475,9 @@ def get_parameter(value): while value: if value[0] in WSP: token, value = get_fws(value) + elif value[0] == '"': + token = ValueTerminal('"', 'DQUOTE') + value = value[1:] else: token, value = get_qcontent(value) v.append(token) diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 14d1ff36f4a1..629174999961 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -2690,6 +2690,13 @@ def mime_parameters_as_value(self, # Defects are apparent missing *0*, and two 'out of sequence'. [errors.InvalidHeaderDefect]*3), + # bpo-37461: Check that we don't go into an infinite loop. + 'extra_dquote': ( + 'r*="\'a\'\\"', + ' r="\\""', + 'r*=\'a\'"', + [('r', '"')], + [errors.InvalidHeaderDefect]*2), } @parameterize diff --git a/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst new file mode 100644 index 000000000000..4bfd350c0b40 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst @@ -0,0 +1,2 @@ +Fix an inifite loop when parsing specially crafted email headers. Patch by +Abhilash Raj. From webhook-mailer at python.org Wed Jul 17 13:29:25 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 17 Jul 2019 17:29:25 -0000 Subject: [Python-checkins] Fix IndexError when parsing unexpectedly ending quoted-string. (GH-14813) Message-ID: https://github.com/python/cpython/commit/635743355d9dbffdc9aa7a2cc8de1403fbdb8d9b commit: 635743355d9dbffdc9aa7a2cc8de1403fbdb8d9b branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-17T10:29:18-07:00 summary: Fix IndexError when parsing unexpectedly ending quoted-string. (GH-14813) This exception was caused because the input ended unexpectedly with only one single quote instead of a pair with some value inside it. (cherry picked from commit 719a062bcb7b08a56e6576dcd75f4244e6053209) Co-authored-by: Abhilash Raj files: A Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst M Lib/email/_header_value_parser.py M Lib/test/test_email/test__header_value_parser.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index 2fc8abcbc5ad..7dfd9780a6e9 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -1170,7 +1170,7 @@ def get_bare_quoted_string(value): "expected '\"' but found '{}'".format(value)) bare_quoted_string = BareQuotedString() value = value[1:] - if value[0] == '"': + if value and value[0] == '"': token, value = get_qcontent(value) bare_quoted_string.append(token) while value and value[0] != '"': diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 629174999961..f4aad851c677 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -503,6 +503,10 @@ def test_get_bare_quoted_string_only_quotes(self): self._test_get_x(parser.get_bare_quoted_string, '""', '""', '', [], '') + def test_get_bare_quoted_string_missing_endquotes(self): + self._test_get_x(parser.get_bare_quoted_string, + '"', '""', '', [errors.InvalidHeaderDefect], '') + def test_get_bare_quoted_string_following_wsp_preserved(self): self._test_get_x(parser.get_bare_quoted_string, '"foo"\t bar', '"foo"', 'foo', [], '\t bar') diff --git a/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst b/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst new file mode 100644 index 000000000000..af4287ba478b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst @@ -0,0 +1,2 @@ +Fix ``IndexError`` when parsing email headers with unexpectedly ending +bare-quoted string value. Patch by Abhilash Raj. From webhook-mailer at python.org Wed Jul 17 13:32:38 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 17 Jul 2019 17:32:38 -0000 Subject: [Python-checkins] Fix IndexError when parsing unexpectedly ending quoted-string. (GH-14813) Message-ID: https://github.com/python/cpython/commit/80f74ce83b6757526ee6927fe89897f3460f25f9 commit: 80f74ce83b6757526ee6927fe89897f3460f25f9 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-17T10:32:31-07:00 summary: Fix IndexError when parsing unexpectedly ending quoted-string. (GH-14813) This exception was caused because the input ended unexpectedly with only one single quote instead of a pair with some value inside it. (cherry picked from commit 719a062bcb7b08a56e6576dcd75f4244e6053209) Co-authored-by: Abhilash Raj files: A Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst M Lib/email/_header_value_parser.py M Lib/test/test_email/test__header_value_parser.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index a342a8e94742..801ae728dd13 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -1189,7 +1189,7 @@ def get_bare_quoted_string(value): "expected '\"' but found '{}'".format(value)) bare_quoted_string = BareQuotedString() value = value[1:] - if value[0] == '"': + if value and value[0] == '"': token, value = get_qcontent(value) bare_quoted_string.append(token) while value and value[0] != '"': diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 55c83efbcd83..9e862feab10c 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -522,6 +522,10 @@ def test_get_bare_quoted_string_only_quotes(self): self._test_get_x(parser.get_bare_quoted_string, '""', '""', '', [], '') + def test_get_bare_quoted_string_missing_endquotes(self): + self._test_get_x(parser.get_bare_quoted_string, + '"', '""', '', [errors.InvalidHeaderDefect], '') + def test_get_bare_quoted_string_following_wsp_preserved(self): self._test_get_x(parser.get_bare_quoted_string, '"foo"\t bar', '"foo"', 'foo', [], '\t bar') diff --git a/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst b/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst new file mode 100644 index 000000000000..af4287ba478b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst @@ -0,0 +1,2 @@ +Fix ``IndexError`` when parsing email headers with unexpectedly ending +bare-quoted string value. Patch by Abhilash Raj. From webhook-mailer at python.org Wed Jul 17 17:54:32 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 17 Jul 2019 21:54:32 -0000 Subject: [Python-checkins] bpo-34155: Dont parse domains containing @ (GH-13079) Message-ID: https://github.com/python/cpython/commit/8cb65d1381b027f0b09ee36bfed7f35bb4dec9a9 commit: 8cb65d1381b027f0b09ee36bfed7f35bb4dec9a9 branch: master author: jpic committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-17T14:54:25-07:00 summary: bpo-34155: Dont parse domains containing @ (GH-13079) Before: >>> email.message_from_string('From: a at malicious.org@important.com', policy=email.policy.default)['from'].addresses (Address(display_name='', username='a', domain='malicious.org'),) >>> parseaddr('a at malicious.org@important.com') ('', 'a at malicious.org') After: >>> email.message_from_string('From: a at malicious.org@important.com', policy=email.policy.default)['from'].addresses (Address(display_name='', username='', domain=''),) >>> parseaddr('a at malicious.org@important.com') ('', 'a@') https://bugs.python.org/issue34155 files: A Misc/NEWS.d/next/Security/2019-05-04-13-33-37.bpo-34155.MJll68.rst M Lib/email/_header_value_parser.py M Lib/email/_parseaddr.py M Lib/test/test_email/test__header_value_parser.py M Lib/test/test_email/test_email.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index daff57b9cfa2..641e0979f3d7 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -1587,6 +1587,8 @@ def get_domain(value): token, value = get_dot_atom(value) except errors.HeaderParseError: token, value = get_atom(value) + if value and value[0] == '@': + raise errors.HeaderParseError('Invalid Domain') if leader is not None: token[:0] = [leader] domain.append(token) diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py index cdfa3729adc7..41ff6f8c000d 100644 --- a/Lib/email/_parseaddr.py +++ b/Lib/email/_parseaddr.py @@ -379,7 +379,12 @@ def getaddrspec(self): aslist.append('@') self.pos += 1 self.gotonext() - return EMPTYSTRING.join(aslist) + self.getdomain() + domain = self.getdomain() + if not domain: + # Invalid domain, return an empty address instead of returning a + # local part to denote failed parsing. + return EMPTYSTRING + return EMPTYSTRING.join(aslist) + domain def getdomain(self): """Get the complete domain name from an address.""" @@ -394,6 +399,10 @@ def getdomain(self): elif self.field[self.pos] == '.': self.pos += 1 sdlist.append('.') + elif self.field[self.pos] == '@': + # bpo-34155: Don't parse domains with two `@` like + # `a at malicious.org@important.com`. + return EMPTYSTRING elif self.field[self.pos] in self.atomends: break else: diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 4fa56b16c478..877cd3effe1d 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -1448,6 +1448,16 @@ def test_get_addr_spec_dot_atom(self): self.assertEqual(addr_spec.domain, 'example.com') self.assertEqual(addr_spec.addr_spec, 'star.a.star at example.com') + def test_get_addr_spec_multiple_domains(self): + with self.assertRaises(errors.HeaderParseError): + parser.get_addr_spec('star at a.star@example.com') + + with self.assertRaises(errors.HeaderParseError): + parser.get_addr_spec('star at a@example.com') + + with self.assertRaises(errors.HeaderParseError): + parser.get_addr_spec('star at 172.17.0.1@example.com') + # get_obs_route def test_get_obs_route_simple(self): diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index c29cc56203b1..aa775881c552 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -3041,6 +3041,20 @@ def test_parseaddr_empty(self): self.assertEqual(utils.parseaddr('<>'), ('', '')) self.assertEqual(utils.formataddr(utils.parseaddr('<>')), '') + def test_parseaddr_multiple_domains(self): + self.assertEqual( + utils.parseaddr('a at b@c'), + ('', '') + ) + self.assertEqual( + utils.parseaddr('a at b.c@c'), + ('', '') + ) + self.assertEqual( + utils.parseaddr('a at 172.17.0.1@c'), + ('', '') + ) + def test_noquote_dump(self): self.assertEqual( utils.formataddr(('A Silly Person', 'person at dom.ain')), diff --git a/Misc/NEWS.d/next/Security/2019-05-04-13-33-37.bpo-34155.MJll68.rst b/Misc/NEWS.d/next/Security/2019-05-04-13-33-37.bpo-34155.MJll68.rst new file mode 100644 index 000000000000..50292e29ed1d --- /dev/null +++ b/Misc/NEWS.d/next/Security/2019-05-04-13-33-37.bpo-34155.MJll68.rst @@ -0,0 +1 @@ +Fix parsing of invalid email addresses with more than one ``@`` (e.g. a at b@c.com.) to not return the part before 2nd ``@`` as valid email address. Patch by maxking & jpic. From webhook-mailer at python.org Wed Jul 17 20:48:40 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 18 Jul 2019 00:48:40 -0000 Subject: [Python-checkins] bpo-36390: Gather IDLE Format menu functions into format.py (#14827) Message-ID: https://github.com/python/cpython/commit/1b3892243433da7eae7f5f3a4f98f13d309c8926 commit: 1b3892243433da7eae7f5f3a4f98f13d309c8926 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-17T20:48:36-04:00 summary: bpo-36390: Gather IDLE Format menu functions into format.py (#14827) Add two indent spec methods from editor and Rstrip to existing file. Tests are not added for indent methods because they need change in lights of 3.x's prohibition on mixing tabs and spaces. files: D Lib/idlelib/idle_test/test_rstrip.py D Lib/idlelib/rstrip.py M Lib/idlelib/editor.py M Lib/idlelib/format.py M Lib/idlelib/idle_test/test_format.py M Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index f02498da3521..24b1ffc67975 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,9 +53,8 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext - from idlelib.format import FormatParagraph, FormatRegion + from idlelib.format import FormatParagraph, FormatRegion, Indents, Rstrip from idlelib.parenmatch import ParenMatch - from idlelib.rstrip import Rstrip from idlelib.squeezer import Squeezer from idlelib.zoomheight import ZoomHeight @@ -173,14 +172,15 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.newline_and_indent_event) text.bind("<>",self.smart_indent_event) self.fregion = fregion = self.FormatRegion(self) + # self.fregion used in smart_indent_event to access indent_region. text.bind("<>", fregion.indent_region_event) text.bind("<>", fregion.dedent_region_event) text.bind("<>", fregion.comment_region_event) text.bind("<>", fregion.uncomment_region_event) text.bind("<>", fregion.tabify_region_event) text.bind("<>", fregion.untabify_region_event) - text.bind("<>", self.toggle_tabs_event) - text.bind("<>",self.change_indentwidth_event) + text.bind("<>", self.Indents.toggle_tabs_event) + text.bind("<>", self.Indents.change_indentwidth_event) text.bind("", self.move_at_edge_if_selection(0)) text.bind("", self.move_at_edge_if_selection(1)) text.bind("<>", self.del_word_left) @@ -1424,20 +1424,6 @@ def inner(offset, _startindex=startindex, return _icis(_startindex + "+%dc" % offset) return inner - def toggle_tabs_event(self, event): - if self.askyesno( - "Toggle tabs", - "Turn tabs " + ("on", "off")[self.usetabs] + - "?\nIndent width " + - ("will be", "remains at")[self.usetabs] + " 8." + - "\n Note: a tab is always 8 columns", - parent=self.text): - self.usetabs = not self.usetabs - # Try to prevent inconsistent indentation. - # User must change indent width manually after using tabs. - self.indentwidth = 8 - return "break" - # XXX this isn't bound to anything -- see tabwidth comments ## def change_tabwidth_event(self, event): ## new = self._asktabwidth() @@ -1446,18 +1432,6 @@ def toggle_tabs_event(self, event): ## self.set_indentation_params(0, guess=0) ## return "break" - def change_indentwidth_event(self, event): - new = self.askinteger( - "Indent width", - "New indent width (2-16)\n(Always use 8 when using tabs)", - parent=self.text, - initialvalue=self.indentwidth, - minvalue=2, - maxvalue=16) - if new and new != self.indentwidth and not self.usetabs: - self.indentwidth = new - return "break" - # Make string that displays as n leading blanks. def _make_blanks(self, n): diff --git a/Lib/idlelib/format.py b/Lib/idlelib/format.py index e11ca3a9d26f..bced4c1770eb 100644 --- a/Lib/idlelib/format.py +++ b/Lib/idlelib/format.py @@ -6,6 +6,7 @@ File renamed from paragraph.py with functions added from editor.py. """ import re +from tkinter.messagebox import askyesno from tkinter.simpledialog import askinteger from idlelib.config import idleConf @@ -195,7 +196,7 @@ def get_comment_header(line): return m.group(1) -# Copy from editor.py; importing it would cause an import cycle. +# Copied from editor.py; importing it would cause an import cycle. _line_indent_re = re.compile(r'[ \t]*') def get_line_indent(line, tabwidth): @@ -209,7 +210,7 @@ def get_line_indent(line, tabwidth): class FormatRegion: - "Format selected text." + "Format selected text (region)." def __init__(self, editwin): self.editwin = editwin @@ -352,6 +353,65 @@ def _asktabwidth(self): maxvalue=16) +# With mixed indents not allowed, these are semi-useless and not unittested. +class Indents: # pragma: no cover + "Change future indents." + + def __init__(self, editwin): + self.editwin = editwin + + def toggle_tabs_event(self, event): + editwin = self.editwin + usetabs = editwin.usetabs + if askyesno( + "Toggle tabs", + "Turn tabs " + ("on", "off")[usetabs] + + "?\nIndent width " + + ("will be", "remains at")[usetabs] + " 8." + + "\n Note: a tab is always 8 columns", + parent=editwin.text): + editwin.usetabs = not usetabs + # Try to prevent inconsistent indentation. + # User must change indent width manually after using tabs. + editwin.indentwidth = 8 + return "break" + + def change_indentwidth_event(self, event): + editwin = self.editwin + new = askinteger( + "Indent width", + "New indent width (2-16)\n(Always use 8 when using tabs)", + parent=editwin.text, + initialvalue=editwin.indentwidth, + minvalue=2, + maxvalue=16) + if new and new != editwin.indentwidth and not editwin.usetabs: + editwin.indentwidth = new + return "break" + + +class Rstrip: # 'Strip Trailing Whitespace" on "Format" menu. + def __init__(self, editwin): + self.editwin = editwin + + def do_rstrip(self, event=None): + text = self.editwin.text + undo = self.editwin.undo + undo.undo_block_start() + + end_line = int(float(text.index('end'))) + for cur in range(1, end_line): + txt = text.get('%i.0' % cur, '%i.end' % cur) + raw = len(txt) + cut = len(txt.rstrip()) + # Since text.delete() marks file as changed, even if not, + # only call it when needed to actually delete something. + if cut < raw: + text.delete('%i.%i' % (cur, cut), '%i.end' % cur) + + undo.undo_block_stop() + + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_format', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_format.py b/Lib/idlelib/idle_test/test_format.py index a2d27ed69dd1..c7b123e9d513 100644 --- a/Lib/idlelib/idle_test/test_format.py +++ b/Lib/idlelib/idle_test/test_format.py @@ -6,6 +6,7 @@ from test.support import requires from tkinter import Tk, Text from idlelib.editor import EditorWindow +from idlelib.idle_test.mock_idle import Editor as MockEditor class Is_Get_Test(unittest.TestCase): @@ -573,5 +574,50 @@ def test_ask_tabwidth(self, askinteger): self.assertEqual(ask(), 10) +class rstripTest(unittest.TestCase): + + def test_rstrip_line(self): + editor = MockEditor() + text = editor.text + do_rstrip = ft.Rstrip(editor).do_rstrip + eq = self.assertEqual + + do_rstrip() + eq(text.get('1.0', 'insert'), '') + text.insert('1.0', ' ') + do_rstrip() + eq(text.get('1.0', 'insert'), '') + text.insert('1.0', ' \n') + do_rstrip() + eq(text.get('1.0', 'insert'), '\n') + + def test_rstrip_multiple(self): + editor = MockEditor() + # Comment above, uncomment 3 below to test with real Editor & Text. + #from idlelib.editor import EditorWindow as Editor + #from tkinter import Tk + #editor = Editor(root=Tk()) + text = editor.text + do_rstrip = ft.Rstrip(editor).do_rstrip + + original = ( + "Line with an ending tab \n" + "Line ending in 5 spaces \n" + "Linewithnospaces\n" + " indented line\n" + " indented line with trailing space \n" + " ") + stripped = ( + "Line with an ending tab\n" + "Line ending in 5 spaces\n" + "Linewithnospaces\n" + " indented line\n" + " indented line with trailing space\n") + + text.insert('1.0', original) + do_rstrip() + self.assertEqual(text.get('1.0', 'insert'), stripped) + + if __name__ == '__main__': unittest.main(verbosity=2, exit=2) diff --git a/Lib/idlelib/idle_test/test_rstrip.py b/Lib/idlelib/idle_test/test_rstrip.py deleted file mode 100644 index 2bc7c6f035e9..000000000000 --- a/Lib/idlelib/idle_test/test_rstrip.py +++ /dev/null @@ -1,53 +0,0 @@ -"Test rstrip, coverage 100%." - -from idlelib import rstrip -import unittest -from idlelib.idle_test.mock_idle import Editor - -class rstripTest(unittest.TestCase): - - def test_rstrip_line(self): - editor = Editor() - text = editor.text - do_rstrip = rstrip.Rstrip(editor).do_rstrip - - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '') - text.insert('1.0', ' ') - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '') - text.insert('1.0', ' \n') - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '\n') - - def test_rstrip_multiple(self): - editor = Editor() - # Comment above, uncomment 3 below to test with real Editor & Text. - #from idlelib.editor import EditorWindow as Editor - #from tkinter import Tk - #editor = Editor(root=Tk()) - text = editor.text - do_rstrip = rstrip.Rstrip(editor).do_rstrip - - original = ( - "Line with an ending tab \n" - "Line ending in 5 spaces \n" - "Linewithnospaces\n" - " indented line\n" - " indented line with trailing space \n" - " ") - stripped = ( - "Line with an ending tab\n" - "Line ending in 5 spaces\n" - "Linewithnospaces\n" - " indented line\n" - " indented line with trailing space\n") - - text.insert('1.0', original) - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), stripped) - - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/Lib/idlelib/rstrip.py b/Lib/idlelib/rstrip.py deleted file mode 100644 index f93b5e8fc200..000000000000 --- a/Lib/idlelib/rstrip.py +++ /dev/null @@ -1,29 +0,0 @@ -'Provides "Strip trailing whitespace" under the "Format" menu.' - -class Rstrip: - - def __init__(self, editwin): - self.editwin = editwin - - def do_rstrip(self, event=None): - - text = self.editwin.text - undo = self.editwin.undo - - undo.undo_block_start() - - end_line = int(float(text.index('end'))) - for cur in range(1, end_line): - txt = text.get('%i.0' % cur, '%i.end' % cur) - raw = len(txt) - cut = len(txt.rstrip()) - # Since text.delete() marks file as changed, even if not, - # only call it when needed to actually delete something. - if cut < raw: - text.delete('%i.%i' % (cur, cut), '%i.end' % cur) - - undo.undo_block_stop() - -if __name__ == "__main__": - from unittest import main - main('idlelib.idle_test.test_rstrip', verbosity=2,) diff --git a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst index fabc75fd1c6e..74bbda340249 100644 --- a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst +++ b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst @@ -1,2 +1,2 @@ -Rename paragraph.py to format.py and add region formatting methods -from editor.py. Add tests for the latter. +Gather Format menu functions into format.py. Combine +paragraph.py, rstrip.py, and format methods from editor.py. From webhook-mailer at python.org Wed Jul 17 21:21:12 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 18 Jul 2019 01:21:12 -0000 Subject: [Python-checkins] bpo-36390: Gather IDLE Format menu functions into format.py (GH-14827) (GH-14829) Message-ID: https://github.com/python/cpython/commit/028f1d2479a9a508e1f0bfcff42c20c348244549 commit: 028f1d2479a9a508e1f0bfcff42c20c348244549 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-17T21:21:08-04:00 summary: bpo-36390: Gather IDLE Format menu functions into format.py (GH-14827) (GH-14829) Add two indent spec methods from editor and Rstrip to existing file. Tests are not added for indent methods because they need change in lights of 3.x's prohibition on mixing tabs and spaces. (cherry picked from commit 1b3892243433da7eae7f5f3a4f98f13d309c8926) Co-authored-by: Terry Jan Reedy files: D Lib/idlelib/idle_test/test_rstrip.py D Lib/idlelib/rstrip.py M Lib/idlelib/editor.py M Lib/idlelib/format.py M Lib/idlelib/idle_test/test_format.py M Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index f02498da3521..24b1ffc67975 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,9 +53,8 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext - from idlelib.format import FormatParagraph, FormatRegion + from idlelib.format import FormatParagraph, FormatRegion, Indents, Rstrip from idlelib.parenmatch import ParenMatch - from idlelib.rstrip import Rstrip from idlelib.squeezer import Squeezer from idlelib.zoomheight import ZoomHeight @@ -173,14 +172,15 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.newline_and_indent_event) text.bind("<>",self.smart_indent_event) self.fregion = fregion = self.FormatRegion(self) + # self.fregion used in smart_indent_event to access indent_region. text.bind("<>", fregion.indent_region_event) text.bind("<>", fregion.dedent_region_event) text.bind("<>", fregion.comment_region_event) text.bind("<>", fregion.uncomment_region_event) text.bind("<>", fregion.tabify_region_event) text.bind("<>", fregion.untabify_region_event) - text.bind("<>", self.toggle_tabs_event) - text.bind("<>",self.change_indentwidth_event) + text.bind("<>", self.Indents.toggle_tabs_event) + text.bind("<>", self.Indents.change_indentwidth_event) text.bind("", self.move_at_edge_if_selection(0)) text.bind("", self.move_at_edge_if_selection(1)) text.bind("<>", self.del_word_left) @@ -1424,20 +1424,6 @@ def inner(offset, _startindex=startindex, return _icis(_startindex + "+%dc" % offset) return inner - def toggle_tabs_event(self, event): - if self.askyesno( - "Toggle tabs", - "Turn tabs " + ("on", "off")[self.usetabs] + - "?\nIndent width " + - ("will be", "remains at")[self.usetabs] + " 8." + - "\n Note: a tab is always 8 columns", - parent=self.text): - self.usetabs = not self.usetabs - # Try to prevent inconsistent indentation. - # User must change indent width manually after using tabs. - self.indentwidth = 8 - return "break" - # XXX this isn't bound to anything -- see tabwidth comments ## def change_tabwidth_event(self, event): ## new = self._asktabwidth() @@ -1446,18 +1432,6 @@ def toggle_tabs_event(self, event): ## self.set_indentation_params(0, guess=0) ## return "break" - def change_indentwidth_event(self, event): - new = self.askinteger( - "Indent width", - "New indent width (2-16)\n(Always use 8 when using tabs)", - parent=self.text, - initialvalue=self.indentwidth, - minvalue=2, - maxvalue=16) - if new and new != self.indentwidth and not self.usetabs: - self.indentwidth = new - return "break" - # Make string that displays as n leading blanks. def _make_blanks(self, n): diff --git a/Lib/idlelib/format.py b/Lib/idlelib/format.py index e11ca3a9d26f..bced4c1770eb 100644 --- a/Lib/idlelib/format.py +++ b/Lib/idlelib/format.py @@ -6,6 +6,7 @@ File renamed from paragraph.py with functions added from editor.py. """ import re +from tkinter.messagebox import askyesno from tkinter.simpledialog import askinteger from idlelib.config import idleConf @@ -195,7 +196,7 @@ def get_comment_header(line): return m.group(1) -# Copy from editor.py; importing it would cause an import cycle. +# Copied from editor.py; importing it would cause an import cycle. _line_indent_re = re.compile(r'[ \t]*') def get_line_indent(line, tabwidth): @@ -209,7 +210,7 @@ def get_line_indent(line, tabwidth): class FormatRegion: - "Format selected text." + "Format selected text (region)." def __init__(self, editwin): self.editwin = editwin @@ -352,6 +353,65 @@ def _asktabwidth(self): maxvalue=16) +# With mixed indents not allowed, these are semi-useless and not unittested. +class Indents: # pragma: no cover + "Change future indents." + + def __init__(self, editwin): + self.editwin = editwin + + def toggle_tabs_event(self, event): + editwin = self.editwin + usetabs = editwin.usetabs + if askyesno( + "Toggle tabs", + "Turn tabs " + ("on", "off")[usetabs] + + "?\nIndent width " + + ("will be", "remains at")[usetabs] + " 8." + + "\n Note: a tab is always 8 columns", + parent=editwin.text): + editwin.usetabs = not usetabs + # Try to prevent inconsistent indentation. + # User must change indent width manually after using tabs. + editwin.indentwidth = 8 + return "break" + + def change_indentwidth_event(self, event): + editwin = self.editwin + new = askinteger( + "Indent width", + "New indent width (2-16)\n(Always use 8 when using tabs)", + parent=editwin.text, + initialvalue=editwin.indentwidth, + minvalue=2, + maxvalue=16) + if new and new != editwin.indentwidth and not editwin.usetabs: + editwin.indentwidth = new + return "break" + + +class Rstrip: # 'Strip Trailing Whitespace" on "Format" menu. + def __init__(self, editwin): + self.editwin = editwin + + def do_rstrip(self, event=None): + text = self.editwin.text + undo = self.editwin.undo + undo.undo_block_start() + + end_line = int(float(text.index('end'))) + for cur in range(1, end_line): + txt = text.get('%i.0' % cur, '%i.end' % cur) + raw = len(txt) + cut = len(txt.rstrip()) + # Since text.delete() marks file as changed, even if not, + # only call it when needed to actually delete something. + if cut < raw: + text.delete('%i.%i' % (cur, cut), '%i.end' % cur) + + undo.undo_block_stop() + + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_format', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_format.py b/Lib/idlelib/idle_test/test_format.py index a2d27ed69dd1..c7b123e9d513 100644 --- a/Lib/idlelib/idle_test/test_format.py +++ b/Lib/idlelib/idle_test/test_format.py @@ -6,6 +6,7 @@ from test.support import requires from tkinter import Tk, Text from idlelib.editor import EditorWindow +from idlelib.idle_test.mock_idle import Editor as MockEditor class Is_Get_Test(unittest.TestCase): @@ -573,5 +574,50 @@ def test_ask_tabwidth(self, askinteger): self.assertEqual(ask(), 10) +class rstripTest(unittest.TestCase): + + def test_rstrip_line(self): + editor = MockEditor() + text = editor.text + do_rstrip = ft.Rstrip(editor).do_rstrip + eq = self.assertEqual + + do_rstrip() + eq(text.get('1.0', 'insert'), '') + text.insert('1.0', ' ') + do_rstrip() + eq(text.get('1.0', 'insert'), '') + text.insert('1.0', ' \n') + do_rstrip() + eq(text.get('1.0', 'insert'), '\n') + + def test_rstrip_multiple(self): + editor = MockEditor() + # Comment above, uncomment 3 below to test with real Editor & Text. + #from idlelib.editor import EditorWindow as Editor + #from tkinter import Tk + #editor = Editor(root=Tk()) + text = editor.text + do_rstrip = ft.Rstrip(editor).do_rstrip + + original = ( + "Line with an ending tab \n" + "Line ending in 5 spaces \n" + "Linewithnospaces\n" + " indented line\n" + " indented line with trailing space \n" + " ") + stripped = ( + "Line with an ending tab\n" + "Line ending in 5 spaces\n" + "Linewithnospaces\n" + " indented line\n" + " indented line with trailing space\n") + + text.insert('1.0', original) + do_rstrip() + self.assertEqual(text.get('1.0', 'insert'), stripped) + + if __name__ == '__main__': unittest.main(verbosity=2, exit=2) diff --git a/Lib/idlelib/idle_test/test_rstrip.py b/Lib/idlelib/idle_test/test_rstrip.py deleted file mode 100644 index 2bc7c6f035e9..000000000000 --- a/Lib/idlelib/idle_test/test_rstrip.py +++ /dev/null @@ -1,53 +0,0 @@ -"Test rstrip, coverage 100%." - -from idlelib import rstrip -import unittest -from idlelib.idle_test.mock_idle import Editor - -class rstripTest(unittest.TestCase): - - def test_rstrip_line(self): - editor = Editor() - text = editor.text - do_rstrip = rstrip.Rstrip(editor).do_rstrip - - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '') - text.insert('1.0', ' ') - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '') - text.insert('1.0', ' \n') - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '\n') - - def test_rstrip_multiple(self): - editor = Editor() - # Comment above, uncomment 3 below to test with real Editor & Text. - #from idlelib.editor import EditorWindow as Editor - #from tkinter import Tk - #editor = Editor(root=Tk()) - text = editor.text - do_rstrip = rstrip.Rstrip(editor).do_rstrip - - original = ( - "Line with an ending tab \n" - "Line ending in 5 spaces \n" - "Linewithnospaces\n" - " indented line\n" - " indented line with trailing space \n" - " ") - stripped = ( - "Line with an ending tab\n" - "Line ending in 5 spaces\n" - "Linewithnospaces\n" - " indented line\n" - " indented line with trailing space\n") - - text.insert('1.0', original) - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), stripped) - - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/Lib/idlelib/rstrip.py b/Lib/idlelib/rstrip.py deleted file mode 100644 index f93b5e8fc200..000000000000 --- a/Lib/idlelib/rstrip.py +++ /dev/null @@ -1,29 +0,0 @@ -'Provides "Strip trailing whitespace" under the "Format" menu.' - -class Rstrip: - - def __init__(self, editwin): - self.editwin = editwin - - def do_rstrip(self, event=None): - - text = self.editwin.text - undo = self.editwin.undo - - undo.undo_block_start() - - end_line = int(float(text.index('end'))) - for cur in range(1, end_line): - txt = text.get('%i.0' % cur, '%i.end' % cur) - raw = len(txt) - cut = len(txt.rstrip()) - # Since text.delete() marks file as changed, even if not, - # only call it when needed to actually delete something. - if cut < raw: - text.delete('%i.%i' % (cur, cut), '%i.end' % cur) - - undo.undo_block_stop() - -if __name__ == "__main__": - from unittest import main - main('idlelib.idle_test.test_rstrip', verbosity=2,) diff --git a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst index fabc75fd1c6e..74bbda340249 100644 --- a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst +++ b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst @@ -1,2 +1,2 @@ -Rename paragraph.py to format.py and add region formatting methods -from editor.py. Add tests for the latter. +Gather Format menu functions into format.py. Combine +paragraph.py, rstrip.py, and format methods from editor.py. From webhook-mailer at python.org Wed Jul 17 21:22:29 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 18 Jul 2019 01:22:29 -0000 Subject: [Python-checkins] bpo-36390: Gather IDLE Format menu functions into format.py (GH-14827) (GH-14830) Message-ID: https://github.com/python/cpython/commit/5eb19fddd2d6c70ded14a91cf083681d750d593b commit: 5eb19fddd2d6c70ded14a91cf083681d750d593b branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-17T21:22:25-04:00 summary: bpo-36390: Gather IDLE Format menu functions into format.py (GH-14827) (GH-14830) Add two indent spec methods from editor and Rstrip to existing file. Tests are not added for indent methods because they need change in lights of 3.x's prohibition on mixing tabs and spaces. (cherry picked from commit 1b3892243433da7eae7f5f3a4f98f13d309c8926) Co-authored-by: Terry Jan Reedy files: D Lib/idlelib/idle_test/test_rstrip.py D Lib/idlelib/rstrip.py M Lib/idlelib/editor.py M Lib/idlelib/format.py M Lib/idlelib/idle_test/test_format.py M Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index f02498da3521..24b1ffc67975 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,9 +53,8 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext - from idlelib.format import FormatParagraph, FormatRegion + from idlelib.format import FormatParagraph, FormatRegion, Indents, Rstrip from idlelib.parenmatch import ParenMatch - from idlelib.rstrip import Rstrip from idlelib.squeezer import Squeezer from idlelib.zoomheight import ZoomHeight @@ -173,14 +172,15 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>",self.newline_and_indent_event) text.bind("<>",self.smart_indent_event) self.fregion = fregion = self.FormatRegion(self) + # self.fregion used in smart_indent_event to access indent_region. text.bind("<>", fregion.indent_region_event) text.bind("<>", fregion.dedent_region_event) text.bind("<>", fregion.comment_region_event) text.bind("<>", fregion.uncomment_region_event) text.bind("<>", fregion.tabify_region_event) text.bind("<>", fregion.untabify_region_event) - text.bind("<>", self.toggle_tabs_event) - text.bind("<>",self.change_indentwidth_event) + text.bind("<>", self.Indents.toggle_tabs_event) + text.bind("<>", self.Indents.change_indentwidth_event) text.bind("", self.move_at_edge_if_selection(0)) text.bind("", self.move_at_edge_if_selection(1)) text.bind("<>", self.del_word_left) @@ -1424,20 +1424,6 @@ def inner(offset, _startindex=startindex, return _icis(_startindex + "+%dc" % offset) return inner - def toggle_tabs_event(self, event): - if self.askyesno( - "Toggle tabs", - "Turn tabs " + ("on", "off")[self.usetabs] + - "?\nIndent width " + - ("will be", "remains at")[self.usetabs] + " 8." + - "\n Note: a tab is always 8 columns", - parent=self.text): - self.usetabs = not self.usetabs - # Try to prevent inconsistent indentation. - # User must change indent width manually after using tabs. - self.indentwidth = 8 - return "break" - # XXX this isn't bound to anything -- see tabwidth comments ## def change_tabwidth_event(self, event): ## new = self._asktabwidth() @@ -1446,18 +1432,6 @@ def toggle_tabs_event(self, event): ## self.set_indentation_params(0, guess=0) ## return "break" - def change_indentwidth_event(self, event): - new = self.askinteger( - "Indent width", - "New indent width (2-16)\n(Always use 8 when using tabs)", - parent=self.text, - initialvalue=self.indentwidth, - minvalue=2, - maxvalue=16) - if new and new != self.indentwidth and not self.usetabs: - self.indentwidth = new - return "break" - # Make string that displays as n leading blanks. def _make_blanks(self, n): diff --git a/Lib/idlelib/format.py b/Lib/idlelib/format.py index e11ca3a9d26f..bced4c1770eb 100644 --- a/Lib/idlelib/format.py +++ b/Lib/idlelib/format.py @@ -6,6 +6,7 @@ File renamed from paragraph.py with functions added from editor.py. """ import re +from tkinter.messagebox import askyesno from tkinter.simpledialog import askinteger from idlelib.config import idleConf @@ -195,7 +196,7 @@ def get_comment_header(line): return m.group(1) -# Copy from editor.py; importing it would cause an import cycle. +# Copied from editor.py; importing it would cause an import cycle. _line_indent_re = re.compile(r'[ \t]*') def get_line_indent(line, tabwidth): @@ -209,7 +210,7 @@ def get_line_indent(line, tabwidth): class FormatRegion: - "Format selected text." + "Format selected text (region)." def __init__(self, editwin): self.editwin = editwin @@ -352,6 +353,65 @@ def _asktabwidth(self): maxvalue=16) +# With mixed indents not allowed, these are semi-useless and not unittested. +class Indents: # pragma: no cover + "Change future indents." + + def __init__(self, editwin): + self.editwin = editwin + + def toggle_tabs_event(self, event): + editwin = self.editwin + usetabs = editwin.usetabs + if askyesno( + "Toggle tabs", + "Turn tabs " + ("on", "off")[usetabs] + + "?\nIndent width " + + ("will be", "remains at")[usetabs] + " 8." + + "\n Note: a tab is always 8 columns", + parent=editwin.text): + editwin.usetabs = not usetabs + # Try to prevent inconsistent indentation. + # User must change indent width manually after using tabs. + editwin.indentwidth = 8 + return "break" + + def change_indentwidth_event(self, event): + editwin = self.editwin + new = askinteger( + "Indent width", + "New indent width (2-16)\n(Always use 8 when using tabs)", + parent=editwin.text, + initialvalue=editwin.indentwidth, + minvalue=2, + maxvalue=16) + if new and new != editwin.indentwidth and not editwin.usetabs: + editwin.indentwidth = new + return "break" + + +class Rstrip: # 'Strip Trailing Whitespace" on "Format" menu. + def __init__(self, editwin): + self.editwin = editwin + + def do_rstrip(self, event=None): + text = self.editwin.text + undo = self.editwin.undo + undo.undo_block_start() + + end_line = int(float(text.index('end'))) + for cur in range(1, end_line): + txt = text.get('%i.0' % cur, '%i.end' % cur) + raw = len(txt) + cut = len(txt.rstrip()) + # Since text.delete() marks file as changed, even if not, + # only call it when needed to actually delete something. + if cut < raw: + text.delete('%i.%i' % (cur, cut), '%i.end' % cur) + + undo.undo_block_stop() + + if __name__ == "__main__": from unittest import main main('idlelib.idle_test.test_format', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_format.py b/Lib/idlelib/idle_test/test_format.py index a2d27ed69dd1..c7b123e9d513 100644 --- a/Lib/idlelib/idle_test/test_format.py +++ b/Lib/idlelib/idle_test/test_format.py @@ -6,6 +6,7 @@ from test.support import requires from tkinter import Tk, Text from idlelib.editor import EditorWindow +from idlelib.idle_test.mock_idle import Editor as MockEditor class Is_Get_Test(unittest.TestCase): @@ -573,5 +574,50 @@ def test_ask_tabwidth(self, askinteger): self.assertEqual(ask(), 10) +class rstripTest(unittest.TestCase): + + def test_rstrip_line(self): + editor = MockEditor() + text = editor.text + do_rstrip = ft.Rstrip(editor).do_rstrip + eq = self.assertEqual + + do_rstrip() + eq(text.get('1.0', 'insert'), '') + text.insert('1.0', ' ') + do_rstrip() + eq(text.get('1.0', 'insert'), '') + text.insert('1.0', ' \n') + do_rstrip() + eq(text.get('1.0', 'insert'), '\n') + + def test_rstrip_multiple(self): + editor = MockEditor() + # Comment above, uncomment 3 below to test with real Editor & Text. + #from idlelib.editor import EditorWindow as Editor + #from tkinter import Tk + #editor = Editor(root=Tk()) + text = editor.text + do_rstrip = ft.Rstrip(editor).do_rstrip + + original = ( + "Line with an ending tab \n" + "Line ending in 5 spaces \n" + "Linewithnospaces\n" + " indented line\n" + " indented line with trailing space \n" + " ") + stripped = ( + "Line with an ending tab\n" + "Line ending in 5 spaces\n" + "Linewithnospaces\n" + " indented line\n" + " indented line with trailing space\n") + + text.insert('1.0', original) + do_rstrip() + self.assertEqual(text.get('1.0', 'insert'), stripped) + + if __name__ == '__main__': unittest.main(verbosity=2, exit=2) diff --git a/Lib/idlelib/idle_test/test_rstrip.py b/Lib/idlelib/idle_test/test_rstrip.py deleted file mode 100644 index 2bc7c6f035e9..000000000000 --- a/Lib/idlelib/idle_test/test_rstrip.py +++ /dev/null @@ -1,53 +0,0 @@ -"Test rstrip, coverage 100%." - -from idlelib import rstrip -import unittest -from idlelib.idle_test.mock_idle import Editor - -class rstripTest(unittest.TestCase): - - def test_rstrip_line(self): - editor = Editor() - text = editor.text - do_rstrip = rstrip.Rstrip(editor).do_rstrip - - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '') - text.insert('1.0', ' ') - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '') - text.insert('1.0', ' \n') - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), '\n') - - def test_rstrip_multiple(self): - editor = Editor() - # Comment above, uncomment 3 below to test with real Editor & Text. - #from idlelib.editor import EditorWindow as Editor - #from tkinter import Tk - #editor = Editor(root=Tk()) - text = editor.text - do_rstrip = rstrip.Rstrip(editor).do_rstrip - - original = ( - "Line with an ending tab \n" - "Line ending in 5 spaces \n" - "Linewithnospaces\n" - " indented line\n" - " indented line with trailing space \n" - " ") - stripped = ( - "Line with an ending tab\n" - "Line ending in 5 spaces\n" - "Linewithnospaces\n" - " indented line\n" - " indented line with trailing space\n") - - text.insert('1.0', original) - do_rstrip() - self.assertEqual(text.get('1.0', 'insert'), stripped) - - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/Lib/idlelib/rstrip.py b/Lib/idlelib/rstrip.py deleted file mode 100644 index f93b5e8fc200..000000000000 --- a/Lib/idlelib/rstrip.py +++ /dev/null @@ -1,29 +0,0 @@ -'Provides "Strip trailing whitespace" under the "Format" menu.' - -class Rstrip: - - def __init__(self, editwin): - self.editwin = editwin - - def do_rstrip(self, event=None): - - text = self.editwin.text - undo = self.editwin.undo - - undo.undo_block_start() - - end_line = int(float(text.index('end'))) - for cur in range(1, end_line): - txt = text.get('%i.0' % cur, '%i.end' % cur) - raw = len(txt) - cut = len(txt.rstrip()) - # Since text.delete() marks file as changed, even if not, - # only call it when needed to actually delete something. - if cut < raw: - text.delete('%i.%i' % (cur, cut), '%i.end' % cur) - - undo.undo_block_stop() - -if __name__ == "__main__": - from unittest import main - main('idlelib.idle_test.test_rstrip', verbosity=2,) diff --git a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst index fabc75fd1c6e..74bbda340249 100644 --- a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst +++ b/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst @@ -1,2 +1,2 @@ -Rename paragraph.py to format.py and add region formatting methods -from editor.py. Add tests for the latter. +Gather Format menu functions into format.py. Combine +paragraph.py, rstrip.py, and format methods from editor.py. From webhook-mailer at python.org Thu Jul 18 09:57:24 2019 From: webhook-mailer at python.org (Paul Ganssle) Date: Thu, 18 Jul 2019 13:57:24 -0000 Subject: [Python-checkins] bpo-37552: Skip failing tests in strptime/strftime with UCRT version 17763.615 (#14460) Message-ID: https://github.com/python/cpython/commit/9cd39b16e2655f748f7aa8d20bca4812da00ba70 commit: 9cd39b16e2655f748f7aa8d20bca4812da00ba70 branch: master author: Paul Monson committer: Paul Ganssle date: 2019-07-18T15:56:59+02:00 summary: bpo-37552: Skip failing tests in strptime/strftime with UCRT version 17763.615 (#14460) A bug in MSVC UCRT version 17763.615 (which has been fixed in newer versions) is causing test failures in some strptime/strftime tests when the default code page is c65001. This change selectively skips the tests affected by this. files: M Lib/test/support/__init__.py M Lib/test/test_strptime.py M Lib/test/test_time.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 423bb3ebd80d..4bf42e0eb423 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -14,6 +14,7 @@ import glob import importlib import importlib.util +import locale import logging.handlers import nntplib import os @@ -92,7 +93,7 @@ "bigmemtest", "bigaddrspacetest", "cpython_only", "get_attribute", "requires_IEEE_754", "skip_unless_xattr", "requires_zlib", "anticipate_failure", "load_package_tests", "detect_api_mismatch", - "check__all__", "skip_unless_bind_unix_socket", + "check__all__", "skip_unless_bind_unix_socket", "skip_if_buggy_ucrt_strfptime", "ignore_warnings", # sys "is_jython", "is_android", "check_impl_detail", "unix_shell", @@ -2501,6 +2502,27 @@ def skip_unless_symlink(test): msg = "Requires functional symlink implementation" return test if ok else unittest.skip(msg)(test) +_buggy_ucrt = None +def skip_if_buggy_ucrt_strfptime(test): + """ + Skip decorator for tests that use buggy strptime/strftime + + If the UCRT bugs are present time.localtime().tm_zone will be + an empty string, otherwise we assume the UCRT bugs are fixed + + See bpo-37552 [Windows] strptime/strftime return invalid + results with UCRT version 17763.615 + """ + global _buggy_ucrt + if _buggy_ucrt is None: + if(sys.platform == 'win32' and + locale.getdefaultlocale()[1] == 'cp65001' and + time.localtime().tm_zone == ''): + _buggy_ucrt = True + else: + _buggy_ucrt = False + return unittest.skip("buggy MSVC UCRT strptime/strftime")(test) if _buggy_ucrt else test + class PythonSymlink: """Creates a symlink for the current Python executable""" def __init__(self, link=None): diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 623da401eee4..55a0f426731a 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -7,6 +7,7 @@ import os import sys from test import support +from test.support import skip_if_buggy_ucrt_strfptime from datetime import date as datetime_date import _strptime @@ -135,6 +136,7 @@ def test_pattern_escaping(self): "%s does not have re characters escaped properly" % pattern_string) + @skip_if_buggy_ucrt_strfptime def test_compile(self): # Check that compiled regex is correct found = self.time_re.compile(r"%A").match(self.locale_time.f_weekday[6]) @@ -365,6 +367,7 @@ def test_bad_offset(self): _strptime._strptime("-01:3030", "%z") self.assertEqual("Inconsistent use of : in -01:3030", str(err.exception)) + @skip_if_buggy_ucrt_strfptime def test_timezone(self): # Test timezone directives. # When gmtime() is used with %Z, entire result of strftime() is empty. @@ -489,6 +492,7 @@ class CalculationTests(unittest.TestCase): def setUp(self): self.time_tuple = time.gmtime() + @skip_if_buggy_ucrt_strfptime def test_julian_calculation(self): # Make sure that when Julian is missing that it is calculated format_string = "%Y %m %d %H %M %S %w %Z" @@ -498,6 +502,7 @@ def test_julian_calculation(self): "Calculation of tm_yday failed; %s != %s" % (result.tm_yday, self.time_tuple.tm_yday)) + @skip_if_buggy_ucrt_strfptime def test_gregorian_calculation(self): # Test that Gregorian date can be calculated from Julian day format_string = "%Y %H %M %S %w %j %Z" @@ -512,6 +517,7 @@ def test_gregorian_calculation(self): self.time_tuple.tm_year, self.time_tuple.tm_mon, self.time_tuple.tm_mday)) + @skip_if_buggy_ucrt_strfptime def test_day_of_week_calculation(self): # Test that the day of the week is calculated as needed format_string = "%Y %m %d %H %S %j %Z" diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index f790d43b6f47..8d8d31e7825e 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -14,6 +14,7 @@ except ImportError: _testcapi = None +from test.support import skip_if_buggy_ucrt_strfptime # Max year is only limited by the size of C int. SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4 @@ -250,6 +251,7 @@ def test_default_values_for_zero(self): result = time.strftime("%Y %m %d %H %M %S %w %j", (2000,)+(0,)*8) self.assertEqual(expected, result) + @skip_if_buggy_ucrt_strfptime def test_strptime(self): # Should be able to go round-trip from strftime to strptime without # raising an exception. @@ -672,6 +674,7 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear, unittest.TestCase): class TestPytime(unittest.TestCase): + @skip_if_buggy_ucrt_strfptime @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") def test_localtime_timezone(self): From webhook-mailer at python.org Thu Jul 18 10:18:07 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 18 Jul 2019 14:18:07 -0000 Subject: [Python-checkins] bpo-37552: Skip failing tests in strptime/strftime with UCRT version 17763.615 (GH-14460) Message-ID: https://github.com/python/cpython/commit/652b667b13fc6176a565538b35ec11174cc9dacf commit: 652b667b13fc6176a565538b35ec11174cc9dacf branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-18T07:17:58-07:00 summary: bpo-37552: Skip failing tests in strptime/strftime with UCRT version 17763.615 (GH-14460) A bug in MSVC UCRT version 17763.615 (which has been fixed in newer versions) is causing test failures in some strptime/strftime tests when the default code page is c65001. This change selectively skips the tests affected by this. (cherry picked from commit 9cd39b16e2655f748f7aa8d20bca4812da00ba70) Co-authored-by: Paul Monson files: M Lib/test/support/__init__.py M Lib/test/test_strptime.py M Lib/test/test_time.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index a0fe086049a1..0d83fe54f4cb 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -14,6 +14,7 @@ import glob import importlib import importlib.util +import locale import logging.handlers import nntplib import os @@ -92,7 +93,7 @@ "bigmemtest", "bigaddrspacetest", "cpython_only", "get_attribute", "requires_IEEE_754", "skip_unless_xattr", "requires_zlib", "anticipate_failure", "load_package_tests", "detect_api_mismatch", - "check__all__", "skip_unless_bind_unix_socket", + "check__all__", "skip_unless_bind_unix_socket", "skip_if_buggy_ucrt_strfptime", "ignore_warnings", # sys "is_jython", "is_android", "check_impl_detail", "unix_shell", @@ -2499,6 +2500,27 @@ def skip_unless_symlink(test): msg = "Requires functional symlink implementation" return test if ok else unittest.skip(msg)(test) +_buggy_ucrt = None +def skip_if_buggy_ucrt_strfptime(test): + """ + Skip decorator for tests that use buggy strptime/strftime + + If the UCRT bugs are present time.localtime().tm_zone will be + an empty string, otherwise we assume the UCRT bugs are fixed + + See bpo-37552 [Windows] strptime/strftime return invalid + results with UCRT version 17763.615 + """ + global _buggy_ucrt + if _buggy_ucrt is None: + if(sys.platform == 'win32' and + locale.getdefaultlocale()[1] == 'cp65001' and + time.localtime().tm_zone == ''): + _buggy_ucrt = True + else: + _buggy_ucrt = False + return unittest.skip("buggy MSVC UCRT strptime/strftime")(test) if _buggy_ucrt else test + class PythonSymlink: """Creates a symlink for the current Python executable""" def __init__(self, link=None): diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 623da401eee4..55a0f426731a 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -7,6 +7,7 @@ import os import sys from test import support +from test.support import skip_if_buggy_ucrt_strfptime from datetime import date as datetime_date import _strptime @@ -135,6 +136,7 @@ def test_pattern_escaping(self): "%s does not have re characters escaped properly" % pattern_string) + @skip_if_buggy_ucrt_strfptime def test_compile(self): # Check that compiled regex is correct found = self.time_re.compile(r"%A").match(self.locale_time.f_weekday[6]) @@ -365,6 +367,7 @@ def test_bad_offset(self): _strptime._strptime("-01:3030", "%z") self.assertEqual("Inconsistent use of : in -01:3030", str(err.exception)) + @skip_if_buggy_ucrt_strfptime def test_timezone(self): # Test timezone directives. # When gmtime() is used with %Z, entire result of strftime() is empty. @@ -489,6 +492,7 @@ class CalculationTests(unittest.TestCase): def setUp(self): self.time_tuple = time.gmtime() + @skip_if_buggy_ucrt_strfptime def test_julian_calculation(self): # Make sure that when Julian is missing that it is calculated format_string = "%Y %m %d %H %M %S %w %Z" @@ -498,6 +502,7 @@ def test_julian_calculation(self): "Calculation of tm_yday failed; %s != %s" % (result.tm_yday, self.time_tuple.tm_yday)) + @skip_if_buggy_ucrt_strfptime def test_gregorian_calculation(self): # Test that Gregorian date can be calculated from Julian day format_string = "%Y %H %M %S %w %j %Z" @@ -512,6 +517,7 @@ def test_gregorian_calculation(self): self.time_tuple.tm_year, self.time_tuple.tm_mon, self.time_tuple.tm_mday)) + @skip_if_buggy_ucrt_strfptime def test_day_of_week_calculation(self): # Test that the day of the week is calculated as needed format_string = "%Y %m %d %H %S %j %Z" diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index f790d43b6f47..8d8d31e7825e 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -14,6 +14,7 @@ except ImportError: _testcapi = None +from test.support import skip_if_buggy_ucrt_strfptime # Max year is only limited by the size of C int. SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4 @@ -250,6 +251,7 @@ def test_default_values_for_zero(self): result = time.strftime("%Y %m %d %H %M %S %w %j", (2000,)+(0,)*8) self.assertEqual(expected, result) + @skip_if_buggy_ucrt_strfptime def test_strptime(self): # Should be able to go round-trip from strftime to strptime without # raising an exception. @@ -672,6 +674,7 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear, unittest.TestCase): class TestPytime(unittest.TestCase): + @skip_if_buggy_ucrt_strfptime @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") def test_localtime_timezone(self): From webhook-mailer at python.org Thu Jul 18 14:19:31 2019 From: webhook-mailer at python.org (Brett Cannon) Date: Thu, 18 Jul 2019 18:19:31 -0000 Subject: [Python-checkins] Adjust builtins.zip() docstring to better communicate its signature (GH-14833) Message-ID: https://github.com/python/cpython/commit/af2f5b1723b95e45e1f15b5bd52102b7de560f7c commit: af2f5b1723b95e45e1f15b5bd52102b7de560f7c branch: master author: Sergey Fedoseev committer: Brett Cannon <54418+brettcannon at users.noreply.github.com> date: 2019-07-18T11:19:25-07:00 summary: Adjust builtins.zip() docstring to better communicate its signature (GH-14833) files: M Python/bltinmodule.c diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 65110d8d12c6..7f187eacd169 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2655,7 +2655,7 @@ static PyMethodDef zip_methods[] = { }; PyDoc_STRVAR(zip_doc, -"zip(iter1 [,iter2 [...]]) --> zip object\n\ +"zip(*iterables) --> zip object\n\ \n\ Return a zip object whose .__next__() method returns a tuple where\n\ the i-th element comes from the i-th iterable argument. The .__next__()\n\ From webhook-mailer at python.org Thu Jul 18 14:37:17 2019 From: webhook-mailer at python.org (Ethan Furman) Date: Thu, 18 Jul 2019 18:37:17 -0000 Subject: [Python-checkins] bpo-34443: Use __qualname__ instead of __name__ in enum exception messages. (GH-14809) Message-ID: https://github.com/python/cpython/commit/323842c2792a81e87917790506ec3457832c84b3 commit: 323842c2792a81e87917790506ec3457832c84b3 branch: master author: Walter D?rwald committer: Ethan Furman date: 2019-07-18T11:37:13-07:00 summary: bpo-34443: Use __qualname__ instead of __name__ in enum exception messages. (GH-14809) * Use __qualname__ instead of __name__ in enum exception messages. files: A Misc/NEWS.d/next/Library/2019-07-17-11-10-08.bpo-34443.OFnGqz.rst M Lib/enum.py M Lib/test/test_enum.py diff --git a/Lib/enum.py b/Lib/enum.py index 69a7f49d8072..8f82a6da9958 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -577,7 +577,7 @@ def __new__(cls, value): if isinstance(result, cls): return result else: - ve_exc = ValueError("%r is not a valid %s" % (value, cls.__name__)) + ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__)) if result is None and exc is None: raise ve_exc elif exc is None: @@ -599,7 +599,7 @@ def _generate_next_value_(name, start, count, last_values): @classmethod def _missing_(cls, value): - raise ValueError("%r is not a valid %s" % (value, cls.__name__)) + raise ValueError("%r is not a valid %s" % (value, cls.__qualname__)) def __repr__(self): return "<%s.%s: %r>" % ( @@ -706,7 +706,7 @@ def _create_pseudo_member_(cls, value): # verify all bits are accounted for _, extra_flags = _decompose(cls, value) if extra_flags: - raise ValueError("%r is not a valid %s" % (value, cls.__name__)) + raise ValueError("%r is not a valid %s" % (value, cls.__qualname__)) # construct a singleton enum pseudo-member pseudo_member = object.__new__(cls) pseudo_member._name_ = None @@ -780,7 +780,7 @@ class IntFlag(int, Flag): @classmethod def _missing_(cls, value): if not isinstance(value, int): - raise ValueError("%r is not a valid %s" % (value, cls.__name__)) + raise ValueError("%r is not a valid %s" % (value, cls.__qualname__)) new_member = cls._create_pseudo_member_(value) return new_member diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index fcfa7d923e75..7bccd6660b52 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -2260,12 +2260,13 @@ class Bizarre(Flag): d = 4 f = 6 # Bizarre.c | Bizarre.d - self.assertRaisesRegex(ValueError, "5 is not a valid Bizarre", Bizarre, 5) - self.assertRaisesRegex(ValueError, "5 is not a valid Bizarre", Bizarre, 5) - self.assertRaisesRegex(ValueError, "2 is not a valid Bizarre", Bizarre, 2) - self.assertRaisesRegex(ValueError, "2 is not a valid Bizarre", Bizarre, 2) - self.assertRaisesRegex(ValueError, "1 is not a valid Bizarre", Bizarre, 1) - self.assertRaisesRegex(ValueError, "1 is not a valid Bizarre", Bizarre, 1) + name = "TestFlag.test_cascading_failure..Bizarre" + self.assertRaisesRegex(ValueError, "5 is not a valid " + name, Bizarre, 5) + self.assertRaisesRegex(ValueError, "5 is not a valid " + name, Bizarre, 5) + self.assertRaisesRegex(ValueError, "2 is not a valid " + name, Bizarre, 2) + self.assertRaisesRegex(ValueError, "2 is not a valid " + name, Bizarre, 2) + self.assertRaisesRegex(ValueError, "1 is not a valid " + name, Bizarre, 1) + self.assertRaisesRegex(ValueError, "1 is not a valid " + name, Bizarre, 1) def test_duplicate_auto(self): class Dupes(Enum): diff --git a/Misc/NEWS.d/next/Library/2019-07-17-11-10-08.bpo-34443.OFnGqz.rst b/Misc/NEWS.d/next/Library/2019-07-17-11-10-08.bpo-34443.OFnGqz.rst new file mode 100644 index 000000000000..8987a792b59c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-17-11-10-08.bpo-34443.OFnGqz.rst @@ -0,0 +1,2 @@ +Exceptions from :mod:`enum` now use the ``__qualname`` of the enum class in +the exception message instead of the ``__name__``. From webhook-mailer at python.org Thu Jul 18 16:03:24 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 18 Jul 2019 20:03:24 -0000 Subject: [Python-checkins] bpo-33610: IDLE's code-context always shows current context immediately (GH-14821) Message-ID: https://github.com/python/cpython/commit/e0a1f8fb5c60886dbddf1a3ccb5d47576bdd43e2 commit: e0a1f8fb5c60886dbddf1a3ccb5d47576bdd43e2 branch: master author: Tal Einat committer: Terry Jan Reedy date: 2019-07-18T16:03:18-04:00 summary: bpo-33610: IDLE's code-context always shows current context immediately (GH-14821) Eliminate delay of up to 100ms and accompanying visual artifact. Fix bug of never showing context when hide and show. files: A Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst M Lib/idlelib/codecontext.py M Lib/idlelib/idle_test/test_codecontext.py diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 9bd0fa1753fc..3103391cfe58 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -63,10 +63,13 @@ def __init__(self, editwin): """ self.editwin = editwin self.text = editwin.text + self._reset() + + def _reset(self): self.context = None + self.t1 = None self.topvisible = 1 self.info = [(0, -1, "", False)] - self.t1 = None @classmethod def reload(cls): @@ -112,17 +115,17 @@ def toggle_code_context_event(self, event=None): padx=padx, border=border, relief=SUNKEN, state='disabled') self.update_highlight_colors() self.context.bind('', self.jumptoline) + # Get the current context and initiate the recurring update event. + self.timer_event() # Pack the context widget before and above the text_frame widget, # thus ensuring that it will appear directly above text_frame. self.context.pack(side=TOP, fill=X, expand=False, before=self.editwin.text_frame) menu_status = 'Hide' - self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) else: self.context.destroy() - self.context = None self.text.after_cancel(self.t1) - self.t1 = None + self._reset() menu_status = 'Show' self.editwin.update_menu_label(menu='options', index='* Code Context', label=f'{menu_status} Code Context') diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index 05d3209db568..c6c8e8efcd4f 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -135,7 +135,7 @@ def test_toggle_code_context_event(self): toggle() # Toggle on. - eq(toggle(), 'break') + toggle() self.assertIsNotNone(cc.context) eq(cc.context['font'], self.text['font']) eq(cc.context['fg'], self.highlight_cfg['foreground']) @@ -145,11 +145,22 @@ def test_toggle_code_context_event(self): eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') # Toggle off. - eq(toggle(), 'break') + toggle() self.assertIsNone(cc.context) eq(cc.editwin.label, 'Show Code Context') self.assertIsNone(self.cc.t1) + # Scroll down and toggle back on. + line11_context = '\n'.join(x[2] for x in cc.get_context(11)[0]) + cc.text.yview(11) + toggle() + eq(cc.context.get('1.0', 'end-1c'), line11_context) + + # Toggle off and on again. + toggle() + toggle() + eq(cc.context.get('1.0', 'end-1c'), line11_context) + def test_get_context(self): eq = self.assertEqual gc = self.cc.get_context @@ -329,7 +340,7 @@ def test_font(self): eq = self.assertEqual cc = self.cc save_font = cc.text['font'] - test_font = 'TkFixedFont' + test_font = 'TkTextFont' # Ensure code context is not active. if cc.context is not None: diff --git a/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst b/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst new file mode 100644 index 000000000000..6775b04378b0 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst @@ -0,0 +1 @@ +Fix code context not showing the correct context when first toggled on. From webhook-mailer at python.org Thu Jul 18 16:46:40 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 18 Jul 2019 20:46:40 -0000 Subject: [Python-checkins] bpo-33610: IDLE's code-context always shows current context immediately (GH-14821) Message-ID: https://github.com/python/cpython/commit/db2957c8ebbe1c4609b3fc730c6e682cb8ccfeb0 commit: db2957c8ebbe1c4609b3fc730c6e682cb8ccfeb0 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-18T13:46:34-07:00 summary: bpo-33610: IDLE's code-context always shows current context immediately (GH-14821) Eliminate delay of up to 100ms and accompanying visual artifact. Fix bug of never showing context when hide and show. (cherry picked from commit e0a1f8fb5c60886dbddf1a3ccb5d47576bdd43e2) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst M Lib/idlelib/codecontext.py M Lib/idlelib/idle_test/test_codecontext.py diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 9bd0fa1753fc..3103391cfe58 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -63,10 +63,13 @@ def __init__(self, editwin): """ self.editwin = editwin self.text = editwin.text + self._reset() + + def _reset(self): self.context = None + self.t1 = None self.topvisible = 1 self.info = [(0, -1, "", False)] - self.t1 = None @classmethod def reload(cls): @@ -112,17 +115,17 @@ def toggle_code_context_event(self, event=None): padx=padx, border=border, relief=SUNKEN, state='disabled') self.update_highlight_colors() self.context.bind('', self.jumptoline) + # Get the current context and initiate the recurring update event. + self.timer_event() # Pack the context widget before and above the text_frame widget, # thus ensuring that it will appear directly above text_frame. self.context.pack(side=TOP, fill=X, expand=False, before=self.editwin.text_frame) menu_status = 'Hide' - self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) else: self.context.destroy() - self.context = None self.text.after_cancel(self.t1) - self.t1 = None + self._reset() menu_status = 'Show' self.editwin.update_menu_label(menu='options', index='* Code Context', label=f'{menu_status} Code Context') diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index 05d3209db568..c6c8e8efcd4f 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -135,7 +135,7 @@ def test_toggle_code_context_event(self): toggle() # Toggle on. - eq(toggle(), 'break') + toggle() self.assertIsNotNone(cc.context) eq(cc.context['font'], self.text['font']) eq(cc.context['fg'], self.highlight_cfg['foreground']) @@ -145,11 +145,22 @@ def test_toggle_code_context_event(self): eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') # Toggle off. - eq(toggle(), 'break') + toggle() self.assertIsNone(cc.context) eq(cc.editwin.label, 'Show Code Context') self.assertIsNone(self.cc.t1) + # Scroll down and toggle back on. + line11_context = '\n'.join(x[2] for x in cc.get_context(11)[0]) + cc.text.yview(11) + toggle() + eq(cc.context.get('1.0', 'end-1c'), line11_context) + + # Toggle off and on again. + toggle() + toggle() + eq(cc.context.get('1.0', 'end-1c'), line11_context) + def test_get_context(self): eq = self.assertEqual gc = self.cc.get_context @@ -329,7 +340,7 @@ def test_font(self): eq = self.assertEqual cc = self.cc save_font = cc.text['font'] - test_font = 'TkFixedFont' + test_font = 'TkTextFont' # Ensure code context is not active. if cc.context is not None: diff --git a/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst b/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst new file mode 100644 index 000000000000..6775b04378b0 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst @@ -0,0 +1 @@ +Fix code context not showing the correct context when first toggled on. From webhook-mailer at python.org Thu Jul 18 16:47:30 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 18 Jul 2019 20:47:30 -0000 Subject: [Python-checkins] bpo-33610: IDLE's code-context always shows current context immediately (GH-14821) (#14846) Message-ID: https://github.com/python/cpython/commit/86eb5daaf31860da479e034d69e9a6d4f47a8eb6 commit: 86eb5daaf31860da479e034d69e9a6d4f47a8eb6 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-18T16:47:26-04:00 summary: bpo-33610: IDLE's code-context always shows current context immediately (GH-14821) (#14846) Eliminate delay of up to 100ms and accompanying visual artifact. Fix bug of never showing context when hide and show. (cherry picked from commit e0a1f8fb5c60886dbddf1a3ccb5d47576bdd43e2) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst M Lib/idlelib/codecontext.py M Lib/idlelib/idle_test/test_codecontext.py diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 9bd0fa1753fc..3103391cfe58 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -63,10 +63,13 @@ def __init__(self, editwin): """ self.editwin = editwin self.text = editwin.text + self._reset() + + def _reset(self): self.context = None + self.t1 = None self.topvisible = 1 self.info = [(0, -1, "", False)] - self.t1 = None @classmethod def reload(cls): @@ -112,17 +115,17 @@ def toggle_code_context_event(self, event=None): padx=padx, border=border, relief=SUNKEN, state='disabled') self.update_highlight_colors() self.context.bind('', self.jumptoline) + # Get the current context and initiate the recurring update event. + self.timer_event() # Pack the context widget before and above the text_frame widget, # thus ensuring that it will appear directly above text_frame. self.context.pack(side=TOP, fill=X, expand=False, before=self.editwin.text_frame) menu_status = 'Hide' - self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) else: self.context.destroy() - self.context = None self.text.after_cancel(self.t1) - self.t1 = None + self._reset() menu_status = 'Show' self.editwin.update_menu_label(menu='options', index='* Code Context', label=f'{menu_status} Code Context') diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index 05d3209db568..c6c8e8efcd4f 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -135,7 +135,7 @@ def test_toggle_code_context_event(self): toggle() # Toggle on. - eq(toggle(), 'break') + toggle() self.assertIsNotNone(cc.context) eq(cc.context['font'], self.text['font']) eq(cc.context['fg'], self.highlight_cfg['foreground']) @@ -145,11 +145,22 @@ def test_toggle_code_context_event(self): eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') # Toggle off. - eq(toggle(), 'break') + toggle() self.assertIsNone(cc.context) eq(cc.editwin.label, 'Show Code Context') self.assertIsNone(self.cc.t1) + # Scroll down and toggle back on. + line11_context = '\n'.join(x[2] for x in cc.get_context(11)[0]) + cc.text.yview(11) + toggle() + eq(cc.context.get('1.0', 'end-1c'), line11_context) + + # Toggle off and on again. + toggle() + toggle() + eq(cc.context.get('1.0', 'end-1c'), line11_context) + def test_get_context(self): eq = self.assertEqual gc = self.cc.get_context @@ -329,7 +340,7 @@ def test_font(self): eq = self.assertEqual cc = self.cc save_font = cc.text['font'] - test_font = 'TkFixedFont' + test_font = 'TkTextFont' # Ensure code context is not active. if cc.context is not None: diff --git a/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst b/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst new file mode 100644 index 000000000000..6775b04378b0 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst @@ -0,0 +1 @@ +Fix code context not showing the correct context when first toggled on. From webhook-mailer at python.org Thu Jul 18 21:23:24 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 19 Jul 2019 01:23:24 -0000 Subject: [Python-checkins] bpo-37610: improve Using Python doc wrt Editors & IDE (GH-14850) Message-ID: https://github.com/python/cpython/commit/8f040b7a9f442e7c2605ef1cad9c6b5eb7dd7af7 commit: 8f040b7a9f442e7c2605ef1cad9c6b5eb7dd7af7 branch: master author: aldwinaldwin committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-18T18:23:17-07:00 summary: bpo-37610: improve Using Python doc wrt Editors & IDE (GH-14850) Move the Editors and IDE section out of the Unix section, to its own section. https://bugs.python.org/issue37610 files: A Doc/using/editors.rst M Doc/using/index.rst M Doc/using/unix.rst diff --git a/Doc/using/editors.rst b/Doc/using/editors.rst new file mode 100644 index 000000000000..f36f570125c1 --- /dev/null +++ b/Doc/using/editors.rst @@ -0,0 +1,14 @@ +.. highlight:: none + +.. _editors: + +****************** + Editors and IDEs +****************** + +There are a number of IDEs that support Python programming language. +Many editors and IDEs provide syntax highlighting, debugging tools, and :pep:`8` checks. + +Please go to `Python Editors `_ and +`Integrated Development Environments `_ +for a comprehensive list. diff --git a/Doc/using/index.rst b/Doc/using/index.rst index 4f0aa7d9577d..4a45121ac2ee 100644 --- a/Doc/using/index.rst +++ b/Doc/using/index.rst @@ -17,3 +17,4 @@ interpreter and things that make working with Python easier. unix.rst windows.rst mac.rst + editors.rst diff --git a/Doc/using/unix.rst b/Doc/using/unix.rst index 021f0d35a8ee..c0a5643fc2c2 100644 --- a/Doc/using/unix.rst +++ b/Doc/using/unix.rst @@ -134,14 +134,3 @@ some Unices may not have the :program:`env` command, so you may need to hardcode ``/usr/bin/python3`` as the interpreter path. To use shell commands in your Python scripts, look at the :mod:`subprocess` module. - - -Editors and IDEs -================ - -There are a number of IDEs that support Python programming language. -Many editors and IDEs provide syntax highlighting, debugging tools, and :pep:`8` checks. - -Please go to `Python Editors `_ and -`Integrated Development Environments `_ -for a comprehensive list. From webhook-mailer at python.org Thu Jul 18 21:28:53 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 19 Jul 2019 01:28:53 -0000 Subject: [Python-checkins] bpo-37610: improve Using Python doc wrt Editors & IDE (GH-14850) Message-ID: https://github.com/python/cpython/commit/87b6078fb90f11dc67ad8f31e98cbc40f74fcb81 commit: 87b6078fb90f11dc67ad8f31e98cbc40f74fcb81 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-18T18:28:49-07:00 summary: bpo-37610: improve Using Python doc wrt Editors & IDE (GH-14850) Move the Editors and IDE section out of the Unix section, to its own section. https://bugs.python.org/issue37610 (cherry picked from commit 8f040b7a9f442e7c2605ef1cad9c6b5eb7dd7af7) Co-authored-by: aldwinaldwin files: A Doc/using/editors.rst M Doc/using/index.rst M Doc/using/unix.rst diff --git a/Doc/using/editors.rst b/Doc/using/editors.rst new file mode 100644 index 000000000000..f36f570125c1 --- /dev/null +++ b/Doc/using/editors.rst @@ -0,0 +1,14 @@ +.. highlight:: none + +.. _editors: + +****************** + Editors and IDEs +****************** + +There are a number of IDEs that support Python programming language. +Many editors and IDEs provide syntax highlighting, debugging tools, and :pep:`8` checks. + +Please go to `Python Editors `_ and +`Integrated Development Environments `_ +for a comprehensive list. diff --git a/Doc/using/index.rst b/Doc/using/index.rst index 4f0aa7d9577d..4a45121ac2ee 100644 --- a/Doc/using/index.rst +++ b/Doc/using/index.rst @@ -17,3 +17,4 @@ interpreter and things that make working with Python easier. unix.rst windows.rst mac.rst + editors.rst diff --git a/Doc/using/unix.rst b/Doc/using/unix.rst index d18fd0670627..7fdab5d43329 100644 --- a/Doc/using/unix.rst +++ b/Doc/using/unix.rst @@ -134,14 +134,3 @@ some Unices may not have the :program:`env` command, so you may need to hardcode ``/usr/bin/python3`` as the interpreter path. To use shell commands in your Python scripts, look at the :mod:`subprocess` module. - - -Editors and IDEs -================ - -There are a number of IDEs that support Python programming language. -Many editors and IDEs provide syntax highlighting, debugging tools, and :pep:`8` checks. - -Please go to `Python Editors `_ and -`Integrated Development Environments `_ -for a comprehensive list. From webhook-mailer at python.org Thu Jul 18 21:31:45 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 19 Jul 2019 01:31:45 -0000 Subject: [Python-checkins] bpo-37610: improve Using Python doc wrt Editors & IDE (GH-14850) Message-ID: https://github.com/python/cpython/commit/840352455dfbb6acb3b69ea88a19c01b7358e801 commit: 840352455dfbb6acb3b69ea88a19c01b7358e801 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-18T18:31:41-07:00 summary: bpo-37610: improve Using Python doc wrt Editors & IDE (GH-14850) Move the Editors and IDE section out of the Unix section, to its own section. https://bugs.python.org/issue37610 (cherry picked from commit 8f040b7a9f442e7c2605ef1cad9c6b5eb7dd7af7) Co-authored-by: aldwinaldwin files: A Doc/using/editors.rst M Doc/using/index.rst M Doc/using/unix.rst diff --git a/Doc/using/editors.rst b/Doc/using/editors.rst new file mode 100644 index 000000000000..f36f570125c1 --- /dev/null +++ b/Doc/using/editors.rst @@ -0,0 +1,14 @@ +.. highlight:: none + +.. _editors: + +****************** + Editors and IDEs +****************** + +There are a number of IDEs that support Python programming language. +Many editors and IDEs provide syntax highlighting, debugging tools, and :pep:`8` checks. + +Please go to `Python Editors `_ and +`Integrated Development Environments `_ +for a comprehensive list. diff --git a/Doc/using/index.rst b/Doc/using/index.rst index 4f0aa7d9577d..4a45121ac2ee 100644 --- a/Doc/using/index.rst +++ b/Doc/using/index.rst @@ -17,3 +17,4 @@ interpreter and things that make working with Python easier. unix.rst windows.rst mac.rst + editors.rst diff --git a/Doc/using/unix.rst b/Doc/using/unix.rst index 021f0d35a8ee..c0a5643fc2c2 100644 --- a/Doc/using/unix.rst +++ b/Doc/using/unix.rst @@ -134,14 +134,3 @@ some Unices may not have the :program:`env` command, so you may need to hardcode ``/usr/bin/python3`` as the interpreter path. To use shell commands in your Python scripts, look at the :mod:`subprocess` module. - - -Editors and IDEs -================ - -There are a number of IDEs that support Python programming language. -Many editors and IDEs provide syntax highlighting, debugging tools, and :pep:`8` checks. - -Please go to `Python Editors `_ and -`Integrated Development Environments `_ -for a comprehensive list. From webhook-mailer at python.org Fri Jul 19 02:07:25 2019 From: webhook-mailer at python.org (Inada Naoki) Date: Fri, 19 Jul 2019 06:07:25 -0000 Subject: [Python-checkins] bpo-37547: Fix a compiler warning in winconsoleio.c (GH-14785) Message-ID: https://github.com/python/cpython/commit/d3952096537d9d2706e10af0c0596daeee6a58c9 commit: d3952096537d9d2706e10af0c0596daeee6a58c9 branch: master author: Zackery Spytz committer: Inada Naoki date: 2019-07-19T15:07:06+09:00 summary: bpo-37547: Fix a compiler warning in winconsoleio.c (GH-14785) The compiler warning was introduced in 59ad110d7a7784d53d0b502eebce0346597a6bef. files: M Modules/_io/winconsoleio.c diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index f27a6dc93a2b..94760acd47bf 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -205,7 +205,7 @@ _io__WindowsConsoleIO_close_impl(winconsoleio *self) int rc; _Py_IDENTIFIER(close); res = _PyObject_CallMethodIdOneArg((PyObject*)&PyRawIOBase_Type, - &PyId_close, self); + &PyId_close, (PyObject*)self); if (!self->closehandle) { self->handle = INVALID_HANDLE_VALUE; return res; From webhook-mailer at python.org Fri Jul 19 04:56:57 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Fri, 19 Jul 2019 08:56:57 -0000 Subject: [Python-checkins] bpo-37624: Document weight assumptions for random.choices() (GH-14855) Message-ID: https://github.com/python/cpython/commit/8dbe563aa6bd18b8c581951537dfb406d36e8063 commit: 8dbe563aa6bd18b8c581951537dfb406d36e8063 branch: master author: Raymond Hettinger committer: GitHub date: 2019-07-19T01:56:42-07:00 summary: bpo-37624: Document weight assumptions for random.choices() (GH-14855) files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index fcedba4dbc20..90b86248e6e1 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -160,7 +160,8 @@ Functions for sequences The *weights* or *cum_weights* can use any numeric type that interoperates with the :class:`float` values returned by :func:`random` (that includes - integers, floats, and fractions but excludes decimals). + integers, floats, and fractions but excludes decimals). Weights are + assumed to be non-negative. For a given seed, the :func:`choices` function with equal weighting typically produces a different sequence than repeated calls to From webhook-mailer at python.org Fri Jul 19 04:57:26 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Fri, 19 Jul 2019 08:57:26 -0000 Subject: [Python-checkins] bpo-36546: Clean-up comments (GH-14857) Message-ID: https://github.com/python/cpython/commit/eed5e9a9562d4dcd137e9f0fc7157bc3373c98cc commit: eed5e9a9562d4dcd137e9f0fc7157bc3373c98cc branch: master author: Raymond Hettinger committer: GitHub date: 2019-07-19T01:57:22-07:00 summary: bpo-36546: Clean-up comments (GH-14857) files: M Lib/statistics.py diff --git a/Lib/statistics.py b/Lib/statistics.py index 79b65a29183c..f09f7be354c2 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -596,12 +596,9 @@ def multimode(data): # intervals, and exactly 100p% of the intervals lie to the left of # Q7(p) and 100(1 - p)% of the intervals lie to the right of Q7(p)." -# If the need arises, we could add method="median" for a median -# unbiased, distribution-free alternative. Also if needed, the -# distribution-free approaches could be augmented by adding -# method='normal'. However, for now, the position is that fewer -# options make for easier choices and that external packages can be -# used for anything more advanced. +# If needed, other methods could be added. However, for now, the +# position is that fewer options make for easier choices and that +# external packages can be used for anything more advanced. def quantiles(dist, /, *, n=4, method='exclusive'): '''Divide *dist* into *n* continuous intervals with equal probability. @@ -620,9 +617,6 @@ def quantiles(dist, /, *, n=4, method='exclusive'): data. The minimum value is treated as the 0th percentile and the maximum value is treated as the 100th percentile. ''' - # Possible future API extensions: - # quantiles(data, already_sorted=True) - # quantiles(data, cut_points=[0.02, 0.25, 0.50, 0.75, 0.98]) if n < 1: raise StatisticsError('n must be at least 1') if hasattr(dist, 'inv_cdf'): From webhook-mailer at python.org Fri Jul 19 05:17:39 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Fri, 19 Jul 2019 09:17:39 -0000 Subject: [Python-checkins] bpo-37624: Document weight assumptions for random.choices() (GH-14855) (GH-14858) Message-ID: https://github.com/python/cpython/commit/a50a6225a06e5a83ce2a880a7eb4496043fdbb55 commit: a50a6225a06e5a83ce2a880a7eb4496043fdbb55 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Raymond Hettinger date: 2019-07-19T02:17:21-07:00 summary: bpo-37624: Document weight assumptions for random.choices() (GH-14855) (GH-14858) (cherry picked from commit 8dbe563aa6bd18b8c581951537dfb406d36e8063) Co-authored-by: Raymond Hettinger files: M Doc/library/random.rst diff --git a/Doc/library/random.rst b/Doc/library/random.rst index fcedba4dbc20..90b86248e6e1 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -160,7 +160,8 @@ Functions for sequences The *weights* or *cum_weights* can use any numeric type that interoperates with the :class:`float` values returned by :func:`random` (that includes - integers, floats, and fractions but excludes decimals). + integers, floats, and fractions but excludes decimals). Weights are + assumed to be non-negative. For a given seed, the :func:`choices` function with equal weighting typically produces a different sequence than repeated calls to From webhook-mailer at python.org Fri Jul 19 05:17:59 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Fri, 19 Jul 2019 09:17:59 -0000 Subject: [Python-checkins] bpo-36546: Clean-up comments (GH-14857) (#14859) Message-ID: https://github.com/python/cpython/commit/e5bfd1ce9da51b64d157392e0a831637f7335ff5 commit: e5bfd1ce9da51b64d157392e0a831637f7335ff5 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Raymond Hettinger date: 2019-07-19T02:17:53-07:00 summary: bpo-36546: Clean-up comments (GH-14857) (#14859) (cherry picked from commit eed5e9a9562d4dcd137e9f0fc7157bc3373c98cc) Co-authored-by: Raymond Hettinger files: M Lib/statistics.py diff --git a/Lib/statistics.py b/Lib/statistics.py index 79b65a29183c..f09f7be354c2 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -596,12 +596,9 @@ def multimode(data): # intervals, and exactly 100p% of the intervals lie to the left of # Q7(p) and 100(1 - p)% of the intervals lie to the right of Q7(p)." -# If the need arises, we could add method="median" for a median -# unbiased, distribution-free alternative. Also if needed, the -# distribution-free approaches could be augmented by adding -# method='normal'. However, for now, the position is that fewer -# options make for easier choices and that external packages can be -# used for anything more advanced. +# If needed, other methods could be added. However, for now, the +# position is that fewer options make for easier choices and that +# external packages can be used for anything more advanced. def quantiles(dist, /, *, n=4, method='exclusive'): '''Divide *dist* into *n* continuous intervals with equal probability. @@ -620,9 +617,6 @@ def quantiles(dist, /, *, n=4, method='exclusive'): data. The minimum value is treated as the 0th percentile and the maximum value is treated as the 100th percentile. ''' - # Possible future API extensions: - # quantiles(data, already_sorted=True) - # quantiles(data, cut_points=[0.02, 0.25, 0.50, 0.75, 0.98]) if n < 1: raise StatisticsError('n must be at least 1') if hasattr(dist, 'inv_cdf'): From webhook-mailer at python.org Fri Jul 19 14:27:17 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 19 Jul 2019 18:27:17 -0000 Subject: [Python-checkins] Adjust builtins.zip() docstring to better communicate its signature (GH-14833) Message-ID: https://github.com/python/cpython/commit/843fd85cf7e7e139fd1cbaf0dc89cd8771609ec9 commit: 843fd85cf7e7e139fd1cbaf0dc89cd8771609ec9 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-19T11:27:13-07:00 summary: Adjust builtins.zip() docstring to better communicate its signature (GH-14833) (cherry picked from commit af2f5b1723b95e45e1f15b5bd52102b7de560f7c) Co-authored-by: Sergey Fedoseev files: M Python/bltinmodule.c diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 8083ac961feb..12def95971a7 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2675,7 +2675,7 @@ static PyMethodDef zip_methods[] = { }; PyDoc_STRVAR(zip_doc, -"zip(iter1 [,iter2 [...]]) --> zip object\n\ +"zip(*iterables) --> zip object\n\ \n\ Return a zip object whose .__next__() method returns a tuple where\n\ the i-th element comes from the i-th iterable argument. The .__next__()\n\ From webhook-mailer at python.org Fri Jul 19 14:30:02 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 19 Jul 2019 18:30:02 -0000 Subject: [Python-checkins] Adjust builtins.zip() docstring to better communicate its signature (GH-14833) Message-ID: https://github.com/python/cpython/commit/3015191b29c9d69535ea63417c20d89008a73a76 commit: 3015191b29c9d69535ea63417c20d89008a73a76 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-19T11:29:58-07:00 summary: Adjust builtins.zip() docstring to better communicate its signature (GH-14833) (cherry picked from commit af2f5b1723b95e45e1f15b5bd52102b7de560f7c) Co-authored-by: Sergey Fedoseev files: M Python/bltinmodule.c diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 82ae7116841b..62502218ef4c 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2656,7 +2656,7 @@ static PyMethodDef zip_methods[] = { }; PyDoc_STRVAR(zip_doc, -"zip(iter1 [,iter2 [...]]) --> zip object\n\ +"zip(*iterables) --> zip object\n\ \n\ Return a zip object whose .__next__() method returns a tuple where\n\ the i-th element comes from the i-th iterable argument. The .__next__()\n\ From webhook-mailer at python.org Sat Jul 20 03:56:38 2019 From: webhook-mailer at python.org (Xiang Zhang) Date: Sat, 20 Jul 2019 07:56:38 -0000 Subject: [Python-checkins] bpo-37476: Adding tests for asutf8 and asutf8andsize (GH-14531) Message-ID: https://github.com/python/cpython/commit/5623ac87bbe5de481957eca5eeae06347612fbeb commit: 5623ac87bbe5de481957eca5eeae06347612fbeb branch: master author: Hai Shi committer: Xiang Zhang date: 2019-07-20T15:56:23+08:00 summary: bpo-37476: Adding tests for asutf8 and asutf8andsize (GH-14531) files: M Lib/test/test_unicode.py M Modules/_testcapimodule.c diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 177d80d27e1a..8be16c8da926 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -2819,6 +2819,34 @@ def test_asucs4(self): self.assertEqual(unicode_asucs4(s, len(s), 1), s+'\0') self.assertEqual(unicode_asucs4(s, len(s), 0), s+'\uffff') + # Test PyUnicode_AsUTF8() + @support.cpython_only + def test_asutf8(self): + from _testcapi import unicode_asutf8 + + bmp = '\u0100' + bmp2 = '\uffff' + nonbmp = chr(0x10ffff) + + self.assertEqual(unicode_asutf8(bmp), b'\xc4\x80') + self.assertEqual(unicode_asutf8(bmp2), b'\xef\xbf\xbf') + self.assertEqual(unicode_asutf8(nonbmp), b'\xf4\x8f\xbf\xbf') + self.assertRaises(UnicodeEncodeError, unicode_asutf8, 'a\ud800b\udfffc') + + # Test PyUnicode_AsUTF8AndSize() + @support.cpython_only + def test_asutf8andsize(self): + from _testcapi import unicode_asutf8andsize + + bmp = '\u0100' + bmp2 = '\uffff' + nonbmp = chr(0x10ffff) + + self.assertEqual(unicode_asutf8andsize(bmp), (b'\xc4\x80', 2)) + self.assertEqual(unicode_asutf8andsize(bmp2), (b'\xef\xbf\xbf', 3)) + self.assertEqual(unicode_asutf8andsize(nonbmp), (b'\xf4\x8f\xbf\xbf', 4)) + self.assertRaises(UnicodeEncodeError, unicode_asutf8andsize, 'a\ud800b\udfffc') + # Test PyUnicode_FindChar() @support.cpython_only def test_findchar(self): diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 8f34e935353b..8a6e741d2810 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1921,6 +1921,48 @@ unicode_asucs4(PyObject *self, PyObject *args) return result; } +static PyObject * +unicode_asutf8(PyObject *self, PyObject *args) +{ + PyObject *unicode; + const char *buffer; + + if (!PyArg_ParseTuple(args, "U", &unicode)) { + return NULL; + } + + buffer = PyUnicode_AsUTF8(unicode); + if (buffer == NULL) { + return NULL; + } + + return PyBytes_FromString(buffer); +} + +static PyObject * +unicode_asutf8andsize(PyObject *self, PyObject *args) +{ + PyObject *unicode, *result; + const char *buffer; + Py_ssize_t utf8_len; + + if(!PyArg_ParseTuple(args, "U", &unicode)) { + return NULL; + } + + buffer = PyUnicode_AsUTF8AndSize(unicode, &utf8_len); + if (buffer == NULL) { + return NULL; + } + + result = PyBytes_FromString(buffer); + if (result == NULL) { + return NULL; + } + + return Py_BuildValue("(Nn)", result, utf8_len); +} + static PyObject * unicode_findchar(PyObject *self, PyObject *args) { @@ -5174,6 +5216,8 @@ static PyMethodDef TestMethods[] = { {"unicode_aswidechar", unicode_aswidechar, METH_VARARGS}, {"unicode_aswidecharstring",unicode_aswidecharstring, METH_VARARGS}, {"unicode_asucs4", unicode_asucs4, METH_VARARGS}, + {"unicode_asutf8", unicode_asutf8, METH_VARARGS}, + {"unicode_asutf8andsize", unicode_asutf8andsize, METH_VARARGS}, {"unicode_findchar", unicode_findchar, METH_VARARGS}, {"unicode_copycharacters", unicode_copycharacters, METH_VARARGS}, {"unicode_encodedecimal", unicode_encodedecimal, METH_VARARGS}, From webhook-mailer at python.org Sun Jul 21 03:34:58 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Sun, 21 Jul 2019 07:34:58 -0000 Subject: [Python-checkins] bpo-36324: Make internal attributes for statistics.NormalDist() private. (GH-14871) Message-ID: https://github.com/python/cpython/commit/02c91f59b6f6e720a9e89635e00c55bcf7f932a8 commit: 02c91f59b6f6e720a9e89635e00c55bcf7f932a8 branch: master author: Raymond Hettinger committer: GitHub date: 2019-07-21T00:34:47-07:00 summary: bpo-36324: Make internal attributes for statistics.NormalDist() private. (GH-14871) * Make internals private * Finish making mu and sigma private * Add missing __hash__() method * Add blurb files: A Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst M Lib/statistics.py M Lib/test/test_statistics.py diff --git a/Lib/statistics.py b/Lib/statistics.py index f09f7be354c2..ff07dc4a6b55 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -812,15 +812,15 @@ class NormalDist: # https://en.wikipedia.org/wiki/Normal_distribution # https://en.wikipedia.org/wiki/Variance#Properties - __slots__ = {'mu': 'Arithmetic mean of a normal distribution', - 'sigma': 'Standard deviation of a normal distribution'} + __slots__ = {'_mu': 'Arithmetic mean of a normal distribution', + '_sigma': 'Standard deviation of a normal distribution'} def __init__(self, mu=0.0, sigma=1.0): 'NormalDist where mu is the mean and sigma is the standard deviation.' if sigma < 0.0: raise StatisticsError('sigma must be non-negative') - self.mu = mu - self.sigma = sigma + self._mu = mu + self._sigma = sigma @classmethod def from_samples(cls, data): @@ -833,21 +833,21 @@ def from_samples(cls, data): def samples(self, n, *, seed=None): 'Generate *n* samples for a given mean and standard deviation.' gauss = random.gauss if seed is None else random.Random(seed).gauss - mu, sigma = self.mu, self.sigma + mu, sigma = self._mu, self._sigma return [gauss(mu, sigma) for i in range(n)] def pdf(self, x): 'Probability density function. P(x <= X < x+dx) / dx' - variance = self.sigma ** 2.0 + variance = self._sigma ** 2.0 if not variance: raise StatisticsError('pdf() not defined when sigma is zero') - return exp((x - self.mu)**2.0 / (-2.0*variance)) / sqrt(tau * variance) + return exp((x - self._mu)**2.0 / (-2.0*variance)) / sqrt(tau * variance) def cdf(self, x): 'Cumulative distribution function. P(X <= x)' - if not self.sigma: + if not self._sigma: raise StatisticsError('cdf() not defined when sigma is zero') - return 0.5 * (1.0 + erf((x - self.mu) / (self.sigma * sqrt(2.0)))) + return 0.5 * (1.0 + erf((x - self._mu) / (self._sigma * sqrt(2.0)))) def inv_cdf(self, p): '''Inverse cumulative distribution function. x : P(X <= x) = p @@ -859,7 +859,7 @@ def inv_cdf(self, p): ''' if (p <= 0.0 or p >= 1.0): raise StatisticsError('p must be in the range 0.0 < p < 1.0') - if self.sigma <= 0.0: + if self._sigma <= 0.0: raise StatisticsError('cdf() not defined when sigma at or below zero') # There is no closed-form solution to the inverse CDF for the normal @@ -888,7 +888,7 @@ def inv_cdf(self, p): 4.23133_30701_60091_1252e+1) * r + 1.0) x = num / den - return self.mu + (x * self.sigma) + return self._mu + (x * self._sigma) r = p if q <= 0.0 else 1.0 - p r = sqrt(-log(r)) if r <= 5.0: @@ -930,7 +930,7 @@ def inv_cdf(self, p): x = num / den if q < 0.0: x = -x - return self.mu + (x * self.sigma) + return self._mu + (x * self._sigma) def overlap(self, other): '''Compute the overlapping coefficient (OVL) between two normal distributions. @@ -951,17 +951,17 @@ def overlap(self, other): if not isinstance(other, NormalDist): raise TypeError('Expected another NormalDist instance') X, Y = self, other - if (Y.sigma, Y.mu) < (X.sigma, X.mu): # sort to assure commutativity + if (Y._sigma, Y._mu) < (X._sigma, X._mu): # sort to assure commutativity X, Y = Y, X X_var, Y_var = X.variance, Y.variance if not X_var or not Y_var: raise StatisticsError('overlap() not defined when sigma is zero') dv = Y_var - X_var - dm = fabs(Y.mu - X.mu) + dm = fabs(Y._mu - X._mu) if not dv: - return 1.0 - erf(dm / (2.0 * X.sigma * sqrt(2.0))) - a = X.mu * Y_var - Y.mu * X_var - b = X.sigma * Y.sigma * sqrt(dm**2.0 + dv * log(Y_var / X_var)) + return 1.0 - erf(dm / (2.0 * X._sigma * sqrt(2.0))) + a = X._mu * Y_var - Y._mu * X_var + b = X._sigma * Y._sigma * sqrt(dm**2.0 + dv * log(Y_var / X_var)) x1 = (a + b) / dv x2 = (a - b) / dv return 1.0 - (fabs(Y.cdf(x1) - X.cdf(x1)) + fabs(Y.cdf(x2) - X.cdf(x2))) @@ -969,17 +969,17 @@ def overlap(self, other): @property def mean(self): 'Arithmetic mean of the normal distribution.' - return self.mu + return self._mu @property def stdev(self): 'Standard deviation of the normal distribution.' - return self.sigma + return self._sigma @property def variance(self): 'Square of the standard deviation.' - return self.sigma ** 2.0 + return self._sigma ** 2.0 def __add__(x1, x2): '''Add a constant or another NormalDist instance. @@ -992,8 +992,8 @@ def __add__(x1, x2): independent or if they are jointly normally distributed. ''' if isinstance(x2, NormalDist): - return NormalDist(x1.mu + x2.mu, hypot(x1.sigma, x2.sigma)) - return NormalDist(x1.mu + x2, x1.sigma) + return NormalDist(x1._mu + x2._mu, hypot(x1._sigma, x2._sigma)) + return NormalDist(x1._mu + x2, x1._sigma) def __sub__(x1, x2): '''Subtract a constant or another NormalDist instance. @@ -1006,8 +1006,8 @@ def __sub__(x1, x2): independent or if they are jointly normally distributed. ''' if isinstance(x2, NormalDist): - return NormalDist(x1.mu - x2.mu, hypot(x1.sigma, x2.sigma)) - return NormalDist(x1.mu - x2, x1.sigma) + return NormalDist(x1._mu - x2._mu, hypot(x1._sigma, x2._sigma)) + return NormalDist(x1._mu - x2, x1._sigma) def __mul__(x1, x2): '''Multiply both mu and sigma by a constant. @@ -1015,7 +1015,7 @@ def __mul__(x1, x2): Used for rescaling, perhaps to change measurement units. Sigma is scaled with the absolute value of the constant. ''' - return NormalDist(x1.mu * x2, x1.sigma * fabs(x2)) + return NormalDist(x1._mu * x2, x1._sigma * fabs(x2)) def __truediv__(x1, x2): '''Divide both mu and sigma by a constant. @@ -1023,15 +1023,15 @@ def __truediv__(x1, x2): Used for rescaling, perhaps to change measurement units. Sigma is scaled with the absolute value of the constant. ''' - return NormalDist(x1.mu / x2, x1.sigma / fabs(x2)) + return NormalDist(x1._mu / x2, x1._sigma / fabs(x2)) def __pos__(x1): 'Return a copy of the instance.' - return NormalDist(x1.mu, x1.sigma) + return NormalDist(x1._mu, x1._sigma) def __neg__(x1): 'Negates mu while keeping sigma the same.' - return NormalDist(-x1.mu, x1.sigma) + return NormalDist(-x1._mu, x1._sigma) __radd__ = __add__ @@ -1045,10 +1045,14 @@ def __eq__(x1, x2): 'Two NormalDist objects are equal if their mu and sigma are both equal.' if not isinstance(x2, NormalDist): return NotImplemented - return (x1.mu, x2.sigma) == (x2.mu, x2.sigma) + return (x1._mu, x2._sigma) == (x2._mu, x2._sigma) + + def __hash__(self): + 'NormalDist objects hash equal if their mu and sigma are both equal.' + return hash((self._mu, self._sigma)) def __repr__(self): - return f'{type(self).__name__}(mu={self.mu!r}, sigma={self.sigma!r})' + return f'{type(self).__name__}(mu={self._mu!r}, sigma={self._sigma!r})' if __name__ == '__main__': @@ -1065,8 +1069,8 @@ def __repr__(self): g2 = NormalDist(-5, 25) # Test scaling by a constant - assert (g1 * 5 / 5).mu == g1.mu - assert (g1 * 5 / 5).sigma == g1.sigma + assert (g1 * 5 / 5).mean == g1.mean + assert (g1 * 5 / 5).stdev == g1.stdev n = 100_000 G1 = g1.samples(n) @@ -1090,8 +1094,8 @@ def __repr__(self): print(NormalDist.from_samples(map(func, repeat(const), G1))) def assert_close(G1, G2): - assert isclose(G1.mu, G1.mu, rel_tol=0.01), (G1, G2) - assert isclose(G1.sigma, G2.sigma, rel_tol=0.01), (G1, G2) + assert isclose(G1.mean, G1.mean, rel_tol=0.01), (G1, G2) + assert isclose(G1.stdev, G2.stdev, rel_tol=0.01), (G1, G2) X = NormalDist(-105, 73) Y = NormalDist(31, 47) diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 946c7428c613..ed2f6579b0b9 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -2326,18 +2326,18 @@ def test_slots(self): nd = statistics.NormalDist(300, 23) with self.assertRaises(TypeError): vars(nd) - self.assertEqual(tuple(nd.__slots__), ('mu', 'sigma')) + self.assertEqual(tuple(nd.__slots__), ('_mu', '_sigma')) def test_instantiation_and_attributes(self): nd = statistics.NormalDist(500, 17) - self.assertEqual(nd.mu, 500) - self.assertEqual(nd.sigma, 17) + self.assertEqual(nd.mean, 500) + self.assertEqual(nd.stdev, 17) self.assertEqual(nd.variance, 17**2) # default arguments nd = statistics.NormalDist() - self.assertEqual(nd.mu, 0) - self.assertEqual(nd.sigma, 1) + self.assertEqual(nd.mean, 0) + self.assertEqual(nd.stdev, 1) self.assertEqual(nd.variance, 1**2) # error case: negative sigma @@ -2520,10 +2520,7 @@ def test_inv_cdf(self): with self.assertRaises(statistics.StatisticsError): iq.inv_cdf(1.1) # p over one with self.assertRaises(statistics.StatisticsError): - iq.sigma = 0.0 # sigma is zero - iq.inv_cdf(0.5) - with self.assertRaises(statistics.StatisticsError): - iq.sigma = -0.1 # sigma under zero + iq = NormalDist(100, 0) # sigma is zero iq.inv_cdf(0.5) # Special values @@ -2544,8 +2541,8 @@ def test_overlap(self): def overlap_numeric(X, Y, *, steps=8_192, z=5): 'Numerical integration cross-check for overlap() ' fsum = math.fsum - center = (X.mu + Y.mu) / 2.0 - width = z * max(X.sigma, Y.sigma) + center = (X.mean + Y.mean) / 2.0 + width = z * max(X.stdev, Y.stdev) start = center - width dx = 2.0 * width / steps x_arr = [start + i*dx for i in range(steps)] @@ -2626,12 +2623,12 @@ def test_unary_operations(self): X = NormalDist(100, 12) Y = +X self.assertIsNot(X, Y) - self.assertEqual(X.mu, Y.mu) - self.assertEqual(X.sigma, Y.sigma) + self.assertEqual(X.mean, Y.mean) + self.assertEqual(X.stdev, Y.stdev) Y = -X self.assertIsNot(X, Y) - self.assertEqual(X.mu, -Y.mu) - self.assertEqual(X.sigma, Y.sigma) + self.assertEqual(X.mean, -Y.mean) + self.assertEqual(X.stdev, Y.stdev) def test_equality(self): NormalDist = statistics.NormalDist @@ -2682,6 +2679,11 @@ def test_pickle_and_copy(self): nd3 = pickle.loads(pickle.dumps(nd)) self.assertEqual(nd, nd3) + def test_hashability(self): + ND = statistics.NormalDist + s = {ND(100, 15), ND(100.0, 15.0), ND(100, 10), ND(95, 15), ND(100, 15)} + self.assertEqual(len(s), 3) + def test_repr(self): nd = statistics.NormalDist(37.5, 5.625) self.assertEqual(repr(nd), 'NormalDist(mu=37.5, sigma=5.625)') diff --git a/Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst b/Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst new file mode 100644 index 000000000000..2e41211c685e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst @@ -0,0 +1 @@ +Make internal attributes for statistics.NormalDist() private. From webhook-mailer at python.org Sun Jul 21 03:55:24 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Sun, 21 Jul 2019 07:55:24 -0000 Subject: [Python-checkins] bpo-36324: Make internal attributes for statistics.NormalDist() private. (GH-14871) (GH-14875) Message-ID: https://github.com/python/cpython/commit/c613c3319ed9bdc8cd74c730ad946169c0776c8a commit: c613c3319ed9bdc8cd74c730ad946169c0776c8a branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Raymond Hettinger date: 2019-07-21T00:55:13-07:00 summary: bpo-36324: Make internal attributes for statistics.NormalDist() private. (GH-14871) (GH-14875) files: A Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst M Lib/statistics.py M Lib/test/test_statistics.py diff --git a/Lib/statistics.py b/Lib/statistics.py index f09f7be354c2..ff07dc4a6b55 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -812,15 +812,15 @@ class NormalDist: # https://en.wikipedia.org/wiki/Normal_distribution # https://en.wikipedia.org/wiki/Variance#Properties - __slots__ = {'mu': 'Arithmetic mean of a normal distribution', - 'sigma': 'Standard deviation of a normal distribution'} + __slots__ = {'_mu': 'Arithmetic mean of a normal distribution', + '_sigma': 'Standard deviation of a normal distribution'} def __init__(self, mu=0.0, sigma=1.0): 'NormalDist where mu is the mean and sigma is the standard deviation.' if sigma < 0.0: raise StatisticsError('sigma must be non-negative') - self.mu = mu - self.sigma = sigma + self._mu = mu + self._sigma = sigma @classmethod def from_samples(cls, data): @@ -833,21 +833,21 @@ def from_samples(cls, data): def samples(self, n, *, seed=None): 'Generate *n* samples for a given mean and standard deviation.' gauss = random.gauss if seed is None else random.Random(seed).gauss - mu, sigma = self.mu, self.sigma + mu, sigma = self._mu, self._sigma return [gauss(mu, sigma) for i in range(n)] def pdf(self, x): 'Probability density function. P(x <= X < x+dx) / dx' - variance = self.sigma ** 2.0 + variance = self._sigma ** 2.0 if not variance: raise StatisticsError('pdf() not defined when sigma is zero') - return exp((x - self.mu)**2.0 / (-2.0*variance)) / sqrt(tau * variance) + return exp((x - self._mu)**2.0 / (-2.0*variance)) / sqrt(tau * variance) def cdf(self, x): 'Cumulative distribution function. P(X <= x)' - if not self.sigma: + if not self._sigma: raise StatisticsError('cdf() not defined when sigma is zero') - return 0.5 * (1.0 + erf((x - self.mu) / (self.sigma * sqrt(2.0)))) + return 0.5 * (1.0 + erf((x - self._mu) / (self._sigma * sqrt(2.0)))) def inv_cdf(self, p): '''Inverse cumulative distribution function. x : P(X <= x) = p @@ -859,7 +859,7 @@ def inv_cdf(self, p): ''' if (p <= 0.0 or p >= 1.0): raise StatisticsError('p must be in the range 0.0 < p < 1.0') - if self.sigma <= 0.0: + if self._sigma <= 0.0: raise StatisticsError('cdf() not defined when sigma at or below zero') # There is no closed-form solution to the inverse CDF for the normal @@ -888,7 +888,7 @@ def inv_cdf(self, p): 4.23133_30701_60091_1252e+1) * r + 1.0) x = num / den - return self.mu + (x * self.sigma) + return self._mu + (x * self._sigma) r = p if q <= 0.0 else 1.0 - p r = sqrt(-log(r)) if r <= 5.0: @@ -930,7 +930,7 @@ def inv_cdf(self, p): x = num / den if q < 0.0: x = -x - return self.mu + (x * self.sigma) + return self._mu + (x * self._sigma) def overlap(self, other): '''Compute the overlapping coefficient (OVL) between two normal distributions. @@ -951,17 +951,17 @@ def overlap(self, other): if not isinstance(other, NormalDist): raise TypeError('Expected another NormalDist instance') X, Y = self, other - if (Y.sigma, Y.mu) < (X.sigma, X.mu): # sort to assure commutativity + if (Y._sigma, Y._mu) < (X._sigma, X._mu): # sort to assure commutativity X, Y = Y, X X_var, Y_var = X.variance, Y.variance if not X_var or not Y_var: raise StatisticsError('overlap() not defined when sigma is zero') dv = Y_var - X_var - dm = fabs(Y.mu - X.mu) + dm = fabs(Y._mu - X._mu) if not dv: - return 1.0 - erf(dm / (2.0 * X.sigma * sqrt(2.0))) - a = X.mu * Y_var - Y.mu * X_var - b = X.sigma * Y.sigma * sqrt(dm**2.0 + dv * log(Y_var / X_var)) + return 1.0 - erf(dm / (2.0 * X._sigma * sqrt(2.0))) + a = X._mu * Y_var - Y._mu * X_var + b = X._sigma * Y._sigma * sqrt(dm**2.0 + dv * log(Y_var / X_var)) x1 = (a + b) / dv x2 = (a - b) / dv return 1.0 - (fabs(Y.cdf(x1) - X.cdf(x1)) + fabs(Y.cdf(x2) - X.cdf(x2))) @@ -969,17 +969,17 @@ def overlap(self, other): @property def mean(self): 'Arithmetic mean of the normal distribution.' - return self.mu + return self._mu @property def stdev(self): 'Standard deviation of the normal distribution.' - return self.sigma + return self._sigma @property def variance(self): 'Square of the standard deviation.' - return self.sigma ** 2.0 + return self._sigma ** 2.0 def __add__(x1, x2): '''Add a constant or another NormalDist instance. @@ -992,8 +992,8 @@ def __add__(x1, x2): independent or if they are jointly normally distributed. ''' if isinstance(x2, NormalDist): - return NormalDist(x1.mu + x2.mu, hypot(x1.sigma, x2.sigma)) - return NormalDist(x1.mu + x2, x1.sigma) + return NormalDist(x1._mu + x2._mu, hypot(x1._sigma, x2._sigma)) + return NormalDist(x1._mu + x2, x1._sigma) def __sub__(x1, x2): '''Subtract a constant or another NormalDist instance. @@ -1006,8 +1006,8 @@ def __sub__(x1, x2): independent or if they are jointly normally distributed. ''' if isinstance(x2, NormalDist): - return NormalDist(x1.mu - x2.mu, hypot(x1.sigma, x2.sigma)) - return NormalDist(x1.mu - x2, x1.sigma) + return NormalDist(x1._mu - x2._mu, hypot(x1._sigma, x2._sigma)) + return NormalDist(x1._mu - x2, x1._sigma) def __mul__(x1, x2): '''Multiply both mu and sigma by a constant. @@ -1015,7 +1015,7 @@ def __mul__(x1, x2): Used for rescaling, perhaps to change measurement units. Sigma is scaled with the absolute value of the constant. ''' - return NormalDist(x1.mu * x2, x1.sigma * fabs(x2)) + return NormalDist(x1._mu * x2, x1._sigma * fabs(x2)) def __truediv__(x1, x2): '''Divide both mu and sigma by a constant. @@ -1023,15 +1023,15 @@ def __truediv__(x1, x2): Used for rescaling, perhaps to change measurement units. Sigma is scaled with the absolute value of the constant. ''' - return NormalDist(x1.mu / x2, x1.sigma / fabs(x2)) + return NormalDist(x1._mu / x2, x1._sigma / fabs(x2)) def __pos__(x1): 'Return a copy of the instance.' - return NormalDist(x1.mu, x1.sigma) + return NormalDist(x1._mu, x1._sigma) def __neg__(x1): 'Negates mu while keeping sigma the same.' - return NormalDist(-x1.mu, x1.sigma) + return NormalDist(-x1._mu, x1._sigma) __radd__ = __add__ @@ -1045,10 +1045,14 @@ def __eq__(x1, x2): 'Two NormalDist objects are equal if their mu and sigma are both equal.' if not isinstance(x2, NormalDist): return NotImplemented - return (x1.mu, x2.sigma) == (x2.mu, x2.sigma) + return (x1._mu, x2._sigma) == (x2._mu, x2._sigma) + + def __hash__(self): + 'NormalDist objects hash equal if their mu and sigma are both equal.' + return hash((self._mu, self._sigma)) def __repr__(self): - return f'{type(self).__name__}(mu={self.mu!r}, sigma={self.sigma!r})' + return f'{type(self).__name__}(mu={self._mu!r}, sigma={self._sigma!r})' if __name__ == '__main__': @@ -1065,8 +1069,8 @@ def __repr__(self): g2 = NormalDist(-5, 25) # Test scaling by a constant - assert (g1 * 5 / 5).mu == g1.mu - assert (g1 * 5 / 5).sigma == g1.sigma + assert (g1 * 5 / 5).mean == g1.mean + assert (g1 * 5 / 5).stdev == g1.stdev n = 100_000 G1 = g1.samples(n) @@ -1090,8 +1094,8 @@ def __repr__(self): print(NormalDist.from_samples(map(func, repeat(const), G1))) def assert_close(G1, G2): - assert isclose(G1.mu, G1.mu, rel_tol=0.01), (G1, G2) - assert isclose(G1.sigma, G2.sigma, rel_tol=0.01), (G1, G2) + assert isclose(G1.mean, G1.mean, rel_tol=0.01), (G1, G2) + assert isclose(G1.stdev, G2.stdev, rel_tol=0.01), (G1, G2) X = NormalDist(-105, 73) Y = NormalDist(31, 47) diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 946c7428c613..ed2f6579b0b9 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -2326,18 +2326,18 @@ def test_slots(self): nd = statistics.NormalDist(300, 23) with self.assertRaises(TypeError): vars(nd) - self.assertEqual(tuple(nd.__slots__), ('mu', 'sigma')) + self.assertEqual(tuple(nd.__slots__), ('_mu', '_sigma')) def test_instantiation_and_attributes(self): nd = statistics.NormalDist(500, 17) - self.assertEqual(nd.mu, 500) - self.assertEqual(nd.sigma, 17) + self.assertEqual(nd.mean, 500) + self.assertEqual(nd.stdev, 17) self.assertEqual(nd.variance, 17**2) # default arguments nd = statistics.NormalDist() - self.assertEqual(nd.mu, 0) - self.assertEqual(nd.sigma, 1) + self.assertEqual(nd.mean, 0) + self.assertEqual(nd.stdev, 1) self.assertEqual(nd.variance, 1**2) # error case: negative sigma @@ -2520,10 +2520,7 @@ def test_inv_cdf(self): with self.assertRaises(statistics.StatisticsError): iq.inv_cdf(1.1) # p over one with self.assertRaises(statistics.StatisticsError): - iq.sigma = 0.0 # sigma is zero - iq.inv_cdf(0.5) - with self.assertRaises(statistics.StatisticsError): - iq.sigma = -0.1 # sigma under zero + iq = NormalDist(100, 0) # sigma is zero iq.inv_cdf(0.5) # Special values @@ -2544,8 +2541,8 @@ def test_overlap(self): def overlap_numeric(X, Y, *, steps=8_192, z=5): 'Numerical integration cross-check for overlap() ' fsum = math.fsum - center = (X.mu + Y.mu) / 2.0 - width = z * max(X.sigma, Y.sigma) + center = (X.mean + Y.mean) / 2.0 + width = z * max(X.stdev, Y.stdev) start = center - width dx = 2.0 * width / steps x_arr = [start + i*dx for i in range(steps)] @@ -2626,12 +2623,12 @@ def test_unary_operations(self): X = NormalDist(100, 12) Y = +X self.assertIsNot(X, Y) - self.assertEqual(X.mu, Y.mu) - self.assertEqual(X.sigma, Y.sigma) + self.assertEqual(X.mean, Y.mean) + self.assertEqual(X.stdev, Y.stdev) Y = -X self.assertIsNot(X, Y) - self.assertEqual(X.mu, -Y.mu) - self.assertEqual(X.sigma, Y.sigma) + self.assertEqual(X.mean, -Y.mean) + self.assertEqual(X.stdev, Y.stdev) def test_equality(self): NormalDist = statistics.NormalDist @@ -2682,6 +2679,11 @@ def test_pickle_and_copy(self): nd3 = pickle.loads(pickle.dumps(nd)) self.assertEqual(nd, nd3) + def test_hashability(self): + ND = statistics.NormalDist + s = {ND(100, 15), ND(100.0, 15.0), ND(100, 10), ND(95, 15), ND(100, 15)} + self.assertEqual(len(s), 3) + def test_repr(self): nd = statistics.NormalDist(37.5, 5.625) self.assertEqual(repr(nd), 'NormalDist(mu=37.5, sigma=5.625)') diff --git a/Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst b/Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst new file mode 100644 index 000000000000..2e41211c685e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst @@ -0,0 +1 @@ +Make internal attributes for statistics.NormalDist() private. From webhook-mailer at python.org Sun Jul 21 10:01:47 2019 From: webhook-mailer at python.org (Ned Deily) Date: Sun, 21 Jul 2019 14:01:47 -0000 Subject: [Python-checkins] Fix infinite loop in email folding logic (GH-12732) (GH-14799) Message-ID: https://github.com/python/cpython/commit/79a47e2b9cff6c9facdbc022a752177ab89dc533 commit: 79a47e2b9cff6c9facdbc022a752177ab89dc533 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Ned Deily date: 2019-07-21T16:01:43+02:00 summary: Fix infinite loop in email folding logic (GH-12732) (GH-14799) As far as I can tell, this infinite loop would be triggered if: 1. The value being folded contains a single word (no spaces) longer than max_line_length 2. The max_line_length is shorter than the encoding's name + 9 characters. bpo-36564: https://bugs.python.org/issue36564 (cherry picked from commit f69d5c61981ea97d251db515c7ff280fcc17182d) Co-authored-by: Paul Ganssle files: A Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst M Lib/email/_header_value_parser.py M Lib/email/parser.py M Lib/test/test_email/test_policy.py diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index f42cde203cef..e3c343dffb42 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -2715,15 +2715,22 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset): trailing_wsp = to_encode[-1] to_encode = to_encode[:-1] new_last_ew = len(lines[-1]) if last_ew is None else last_ew + + encode_as = 'utf-8' if charset == 'us-ascii' else charset + + # The RFC2047 chrome takes up 7 characters plus the length + # of the charset name. + chrome_len = len(encode_as) + 7 + + if (chrome_len + 1) >= maxlen: + raise errors.HeaderParseError( + "max_line_length is too small to fit an encoded word") + while to_encode: remaining_space = maxlen - len(lines[-1]) - # The RFC2047 chrome takes up 7 characters plus the length - # of the charset name. - encode_as = 'utf-8' if charset == 'us-ascii' else charset - text_space = remaining_space - len(encode_as) - 7 + text_space = remaining_space - chrome_len if text_space <= 0: lines.append(' ') - # XXX We'll get an infinite loop here if maxlen is <= 7 continue to_encode_word = to_encode[:text_space] diff --git a/Lib/email/parser.py b/Lib/email/parser.py index 555b17256061..7db4da1ff081 100644 --- a/Lib/email/parser.py +++ b/Lib/email/parser.py @@ -13,7 +13,6 @@ from email._policybase import compat32 - class Parser: def __init__(self, _class=None, *, policy=compat32): """Parser of RFC 2822 and MIME email messages. diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index c2c437e6ac26..6999e4af10d9 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -2,6 +2,7 @@ import types import textwrap import unittest +import email.errors import email.policy import email.parser import email.generator @@ -245,6 +246,25 @@ def test_non_ascii_chars_do_not_cause_inf_loop(self): 'Subject: \n' + 12 * ' =?utf-8?q?=C4=85?=\n') + def test_short_maxlen_error(self): + # RFC 2047 chrome takes up 7 characters, plus the length of the charset + # name, so folding should fail if maxlen is lower than the minimum + # required length for a line. + + # Note: This is only triggered when there is a single word longer than + # max_line_length, hence the 1234567890 at the end of this whimsical + # subject. This is because when we encounter a word longer than + # max_line_length, it is broken down into encoded words to fit + # max_line_length. If the max_line_length isn't large enough to even + # contain the RFC 2047 chrome (`?=?q??=`), we fail. + subject = "Melt away the pounds with this one simple trick! 1234567890" + + for maxlen in [3, 7, 9]: + with self.subTest(maxlen=maxlen): + policy = email.policy.default.clone(max_line_length=maxlen) + with self.assertRaises(email.errors.HeaderParseError): + policy.fold("Subject", subject) + # XXX: Need subclassing tests. # For adding subclassed objects, make sure the usual rules apply (subclass # wins), but that the order still works (right overrides left). diff --git a/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst new file mode 100644 index 000000000000..ddd17aec1dd8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst @@ -0,0 +1,3 @@ +Fix infinite loop in email header folding logic that would be triggered when +an email policy's max_line_length is not long enough to include the required +markup and any values in the message. Patch by Paul Ganssle From webhook-mailer at python.org Sun Jul 21 11:37:41 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sun, 21 Jul 2019 15:37:41 -0000 Subject: [Python-checkins] bpo-37627: Initialize IDLE Custom Run dialog with previous entries (#14870) Message-ID: https://github.com/python/cpython/commit/35b87e6001bd991f625abe305951c77ddeb9a9c5 commit: 35b87e6001bd991f625abe305951c77ddeb9a9c5 branch: master author: Ngalim Siregar committer: Terry Jan Reedy date: 2019-07-21T11:37:28-04:00 summary: bpo-37627: Initialize IDLE Custom Run dialog with previous entries (#14870) Repeat the command line arguments most recently entered before so the user can edit them. files: A Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst M Lib/idlelib/idle_test/test_query.py M Lib/idlelib/query.py M Lib/idlelib/runscript.py diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py index 3b444de15d7c..f957585190dc 100644 --- a/Lib/idlelib/idle_test/test_query.py +++ b/Lib/idlelib/idle_test/test_query.py @@ -12,7 +12,7 @@ from idlelib import query import unittest from test.support import requires -from tkinter import Tk +from tkinter import Tk, END import sys from unittest import mock @@ -392,10 +392,12 @@ def setUpClass(cls): def test_click_args(self): root = Tk() root.withdraw() - dialog = query.CustomRun(root, 'Title', _utest=True) - dialog.entry.insert(0, 'okay') + dialog = query.CustomRun(root, 'Title', + cli_args=['a', 'b=1'], _utest=True) + self.assertEqual(dialog.entry.get(), 'a b=1') + dialog.entry.insert(END, ' c') dialog.button_ok.invoke() - self.assertEqual(dialog.result, (['okay'], True)) + self.assertEqual(dialog.result, (['a', 'b=1', 'c'], True)) root.destroy() diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index d74084feed76..097e6e61e356 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -325,9 +325,13 @@ class CustomRun(Query): """ # Used in runscript.run_custom_event - def __init__(self, parent, title, *, cli_args='', + def __init__(self, parent, title, *, cli_args=[], _htest=False, _utest=False): - # TODO Use cli_args to pre-populate entry. + """cli_args is a list of strings. + + The list is assigned to the default Entry StringVar. + The strings are displayed joined by ' ' for display. + """ message = 'Command Line Arguments for sys.argv:' super().__init__( parent, title, message, text0=cli_args, diff --git a/Lib/idlelib/runscript.py b/Lib/idlelib/runscript.py index b041e56fb840..f97cf528cce6 100644 --- a/Lib/idlelib/runscript.py +++ b/Lib/idlelib/runscript.py @@ -39,6 +39,8 @@ def __init__(self, editwin): # XXX This should be done differently self.flist = self.editwin.flist self.root = self.editwin.root + # cli_args is list of strings that extends sys.argv + self.cli_args = [] if macosx.isCocoaTk(): self.editwin.text_frame.bind('<>', self._run_module_event) @@ -137,10 +139,11 @@ def _run_module_event(self, event, *, customize=False): return 'break' if customize: title = f"Customize {self.editwin.short_title()} Run" - run_args = CustomRun(self.shell.text, title).result + run_args = CustomRun(self.shell.text, title, + cli_args=self.cli_args).result if not run_args: # User cancelled. return 'break' - cli_args, restart = run_args if customize else ([], True) + self.cli_args, restart = run_args if customize else ([], True) interp = self.shell.interp if pyshell.use_subprocess and restart: interp.restart_subprocess( @@ -148,8 +151,8 @@ def _run_module_event(self, event, *, customize=False): self.editwin._filename_to_unicode(filename)) dirname = os.path.dirname(filename) argv = [filename] - if cli_args: - argv += cli_args + if self.cli_args: + argv += self.cli_args interp.runcommand(f"""if 1: __file__ = {filename!r} import sys as _sys diff --git a/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst b/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst new file mode 100644 index 000000000000..d864d07f60ef --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst @@ -0,0 +1,3 @@ +Initialize the Customize Run dialog with the command line arguments +most recently entered before. The user can optionally edit before +submitting them. From webhook-mailer at python.org Sun Jul 21 12:09:24 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 21 Jul 2019 16:09:24 -0000 Subject: [Python-checkins] bpo-37627: Initialize IDLE Custom Run dialog with previous entries (GH-14870) Message-ID: https://github.com/python/cpython/commit/d9086f2324264e93155dd81f4484975215e38f00 commit: d9086f2324264e93155dd81f4484975215e38f00 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-21T09:09:17-07:00 summary: bpo-37627: Initialize IDLE Custom Run dialog with previous entries (GH-14870) Repeat the command line arguments most recently entered before so the user can edit them. (cherry picked from commit 35b87e6001bd991f625abe305951c77ddeb9a9c5) Co-authored-by: Ngalim Siregar files: A Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst M Lib/idlelib/idle_test/test_query.py M Lib/idlelib/query.py M Lib/idlelib/runscript.py diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py index 3b444de15d7c..f957585190dc 100644 --- a/Lib/idlelib/idle_test/test_query.py +++ b/Lib/idlelib/idle_test/test_query.py @@ -12,7 +12,7 @@ from idlelib import query import unittest from test.support import requires -from tkinter import Tk +from tkinter import Tk, END import sys from unittest import mock @@ -392,10 +392,12 @@ def setUpClass(cls): def test_click_args(self): root = Tk() root.withdraw() - dialog = query.CustomRun(root, 'Title', _utest=True) - dialog.entry.insert(0, 'okay') + dialog = query.CustomRun(root, 'Title', + cli_args=['a', 'b=1'], _utest=True) + self.assertEqual(dialog.entry.get(), 'a b=1') + dialog.entry.insert(END, ' c') dialog.button_ok.invoke() - self.assertEqual(dialog.result, (['okay'], True)) + self.assertEqual(dialog.result, (['a', 'b=1', 'c'], True)) root.destroy() diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index d74084feed76..097e6e61e356 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -325,9 +325,13 @@ class CustomRun(Query): """ # Used in runscript.run_custom_event - def __init__(self, parent, title, *, cli_args='', + def __init__(self, parent, title, *, cli_args=[], _htest=False, _utest=False): - # TODO Use cli_args to pre-populate entry. + """cli_args is a list of strings. + + The list is assigned to the default Entry StringVar. + The strings are displayed joined by ' ' for display. + """ message = 'Command Line Arguments for sys.argv:' super().__init__( parent, title, message, text0=cli_args, diff --git a/Lib/idlelib/runscript.py b/Lib/idlelib/runscript.py index b041e56fb840..f97cf528cce6 100644 --- a/Lib/idlelib/runscript.py +++ b/Lib/idlelib/runscript.py @@ -39,6 +39,8 @@ def __init__(self, editwin): # XXX This should be done differently self.flist = self.editwin.flist self.root = self.editwin.root + # cli_args is list of strings that extends sys.argv + self.cli_args = [] if macosx.isCocoaTk(): self.editwin.text_frame.bind('<>', self._run_module_event) @@ -137,10 +139,11 @@ def _run_module_event(self, event, *, customize=False): return 'break' if customize: title = f"Customize {self.editwin.short_title()} Run" - run_args = CustomRun(self.shell.text, title).result + run_args = CustomRun(self.shell.text, title, + cli_args=self.cli_args).result if not run_args: # User cancelled. return 'break' - cli_args, restart = run_args if customize else ([], True) + self.cli_args, restart = run_args if customize else ([], True) interp = self.shell.interp if pyshell.use_subprocess and restart: interp.restart_subprocess( @@ -148,8 +151,8 @@ def _run_module_event(self, event, *, customize=False): self.editwin._filename_to_unicode(filename)) dirname = os.path.dirname(filename) argv = [filename] - if cli_args: - argv += cli_args + if self.cli_args: + argv += self.cli_args interp.runcommand(f"""if 1: __file__ = {filename!r} import sys as _sys diff --git a/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst b/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst new file mode 100644 index 000000000000..d864d07f60ef --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst @@ -0,0 +1,3 @@ +Initialize the Customize Run dialog with the command line arguments +most recently entered before. The user can optionally edit before +submitting them. From webhook-mailer at python.org Sun Jul 21 14:02:00 2019 From: webhook-mailer at python.org (Ned Deily) Date: Sun, 21 Jul 2019 18:02:00 -0000 Subject: [Python-checkins] Bpo-37644: update suspicious.csv for distutils/examples (GH-14885) Message-ID: https://github.com/python/cpython/commit/22f0483d44b13140f6ea9927f0cf088c493e875a commit: 22f0483d44b13140f6ea9927f0cf088c493e875a branch: master author: Ned Deily committer: GitHub date: 2019-07-21T20:01:56+02:00 summary: Bpo-37644: update suspicious.csv for distutils/examples (GH-14885) files: M Doc/tools/susp-ignored.csv diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index 80757752388f..43d8d6e28f2f 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -4,7 +4,7 @@ c-api/sequence,,:i2,del o[i1:i2] c-api/sequence,,:i2,o[i1:i2] c-api/unicode,,:end,str[start:end] c-api/unicode,,:start,unicode[start:start+length] -distutils/examples,274,`,This is the description of the ``foobar`` package. +distutils/examples,267,`,This is the description of the ``foobar`` package. distutils/setupscript,,::, extending/embedding,,:numargs,"if(!PyArg_ParseTuple(args, "":numargs""))" extending/extending,,:myfunction,"PyArg_ParseTuple(args, ""D:myfunction"", &c);" From webhook-mailer at python.org Sun Jul 21 14:09:01 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 21 Jul 2019 18:09:01 -0000 Subject: [Python-checkins] Bpo-37644: update suspicious.csv for distutils/examples (GH-14885) Message-ID: https://github.com/python/cpython/commit/8f501647ca1fba32204c0c1a705e06b4e43c69b1 commit: 8f501647ca1fba32204c0c1a705e06b4e43c69b1 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-21T11:08:57-07:00 summary: Bpo-37644: update suspicious.csv for distutils/examples (GH-14885) (cherry picked from commit 22f0483d44b13140f6ea9927f0cf088c493e875a) Co-authored-by: Ned Deily files: M Doc/tools/susp-ignored.csv diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index 9129ee85d7e2..d6252ed2ffa2 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -4,7 +4,7 @@ c-api/sequence,,:i2,del o[i1:i2] c-api/sequence,,:i2,o[i1:i2] c-api/unicode,,:end,str[start:end] c-api/unicode,,:start,unicode[start:start+length] -distutils/examples,274,`,This is the description of the ``foobar`` package. +distutils/examples,267,`,This is the description of the ``foobar`` package. distutils/setupscript,,::, extending/embedding,,:numargs,"if(!PyArg_ParseTuple(args, "":numargs""))" extending/extending,,:myfunction,"PyArg_ParseTuple(args, ""D:myfunction"", &c);" From webhook-mailer at python.org Sun Jul 21 15:13:20 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Sun, 21 Jul 2019 19:13:20 -0000 Subject: [Python-checkins] Minor whitespace, indentation, and quoting changes to improve internal consistency and appease linters (GH-14888) Message-ID: https://github.com/python/cpython/commit/1c0e9bb94b8cfb5970532a9f3499563360ea5532 commit: 1c0e9bb94b8cfb5970532a9f3499563360ea5532 branch: master author: Raymond Hettinger committer: GitHub date: 2019-07-21T12:13:07-07:00 summary: Minor whitespace, indentation, and quoting changes to improve internal consistency and appease linters (GH-14888) files: M Lib/statistics.py diff --git a/Lib/statistics.py b/Lib/statistics.py index ff07dc4a6b55..8a6be7c75901 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -80,12 +80,25 @@ """ -__all__ = [ 'StatisticsError', 'NormalDist', 'quantiles', - 'pstdev', 'pvariance', 'stdev', 'variance', - 'median', 'median_low', 'median_high', 'median_grouped', - 'mean', 'mode', 'multimode', 'harmonic_mean', 'fmean', - 'geometric_mean', - ] +__all__ = [ + 'NormalDist', + 'StatisticsError', + 'fmean', + 'geometric_mean', + 'harmonic_mean', + 'mean', + 'median', + 'median_grouped', + 'median_high', + 'median_low', + 'mode', + 'multimode', + 'pstdev', + 'pvariance', + 'quantiles', + 'stdev', + 'variance', +] import math import numbers @@ -304,8 +317,9 @@ def mean(data): assert count == n return _convert(total/n, T) + def fmean(data): - """ Convert data to floats and compute the arithmetic mean. + """Convert data to floats and compute the arithmetic mean. This runs faster than the mean() function and it always returns a float. The result is highly accurate but not as perfect as mean(). @@ -313,7 +327,6 @@ def fmean(data): >>> fmean([3.5, 4.0, 5.25]) 4.25 - """ try: n = len(data) @@ -332,6 +345,7 @@ def count(iterable): except ZeroDivisionError: raise StatisticsError('fmean requires at least one data point') from None + def geometric_mean(data): """Convert data to floats and compute the geometric mean. @@ -350,6 +364,7 @@ def geometric_mean(data): raise StatisticsError('geometric mean requires a non-empty dataset ' ' containing positive numbers') from None + def harmonic_mean(data): """Return the harmonic mean of data. @@ -547,23 +562,23 @@ def mode(data): def multimode(data): - """ Return a list of the most frequently occurring values. - - Will return more than one result if there are multiple modes - or an empty list if *data* is empty. + """Return a list of the most frequently occurring values. - >>> multimode('aabbbbbbbbcc') - ['b'] - >>> multimode('aabbbbccddddeeffffgg') - ['b', 'd', 'f'] - >>> multimode('') - [] + Will return more than one result if there are multiple modes + or an empty list if *data* is empty. + >>> multimode('aabbbbbbbbcc') + ['b'] + >>> multimode('aabbbbccddddeeffffgg') + ['b', 'd', 'f'] + >>> multimode('') + [] """ counts = Counter(iter(data)).most_common() maxcount, mode_items = next(groupby(counts, key=itemgetter(1)), (0, [])) return list(map(itemgetter(0), mode_items)) + # Notes on methods for computing quantiles # ---------------------------------------- # @@ -601,7 +616,7 @@ def multimode(data): # external packages can be used for anything more advanced. def quantiles(dist, /, *, n=4, method='exclusive'): - '''Divide *dist* into *n* continuous intervals with equal probability. + """Divide *dist* into *n* continuous intervals with equal probability. Returns a list of (n - 1) cut points separating the intervals. @@ -616,7 +631,7 @@ def quantiles(dist, /, *, n=4, method='exclusive'): If *method* is set to *inclusive*, *dist* is treated as population data. The minimum value is treated as the 0th percentile and the maximum value is treated as the 100th percentile. - ''' + """ if n < 1: raise StatisticsError('n must be at least 1') if hasattr(dist, 'inv_cdf'): @@ -646,6 +661,7 @@ def quantiles(dist, /, *, n=4, method='exclusive'): return result raise ValueError(f'Unknown method: {method!r}') + # === Measures of spread === # See http://mathworld.wolfram.com/Variance.html @@ -805,18 +821,21 @@ def pstdev(data, mu=None): except AttributeError: return math.sqrt(var) + ## Normal Distribution ##################################################### class NormalDist: - 'Normal distribution of a random variable' + "Normal distribution of a random variable" # https://en.wikipedia.org/wiki/Normal_distribution # https://en.wikipedia.org/wiki/Variance#Properties - __slots__ = {'_mu': 'Arithmetic mean of a normal distribution', - '_sigma': 'Standard deviation of a normal distribution'} + __slots__ = { + '_mu': 'Arithmetic mean of a normal distribution', + '_sigma': 'Standard deviation of a normal distribution', + } def __init__(self, mu=0.0, sigma=1.0): - 'NormalDist where mu is the mean and sigma is the standard deviation.' + "NormalDist where mu is the mean and sigma is the standard deviation." if sigma < 0.0: raise StatisticsError('sigma must be non-negative') self._mu = mu @@ -824,40 +843,42 @@ def __init__(self, mu=0.0, sigma=1.0): @classmethod def from_samples(cls, data): - 'Make a normal distribution instance from sample data.' + "Make a normal distribution instance from sample data." if not isinstance(data, (list, tuple)): data = list(data) xbar = fmean(data) return cls(xbar, stdev(data, xbar)) def samples(self, n, *, seed=None): - 'Generate *n* samples for a given mean and standard deviation.' + "Generate *n* samples for a given mean and standard deviation." gauss = random.gauss if seed is None else random.Random(seed).gauss mu, sigma = self._mu, self._sigma return [gauss(mu, sigma) for i in range(n)] def pdf(self, x): - 'Probability density function. P(x <= X < x+dx) / dx' + "Probability density function. P(x <= X < x+dx) / dx" variance = self._sigma ** 2.0 if not variance: raise StatisticsError('pdf() not defined when sigma is zero') - return exp((x - self._mu)**2.0 / (-2.0*variance)) / sqrt(tau * variance) + return exp((x - self._mu)**2.0 / (-2.0*variance)) / sqrt(tau*variance) def cdf(self, x): - 'Cumulative distribution function. P(X <= x)' + "Cumulative distribution function. P(X <= x)" if not self._sigma: raise StatisticsError('cdf() not defined when sigma is zero') return 0.5 * (1.0 + erf((x - self._mu) / (self._sigma * sqrt(2.0)))) def inv_cdf(self, p): - '''Inverse cumulative distribution function. x : P(X <= x) = p + """Inverse cumulative distribution function. x : P(X <= x) = p - Finds the value of the random variable such that the probability of the - variable being less than or equal to that value equals the given probability. + Finds the value of the random variable such that the probability of + the variable being less than or equal to that value equals the given + probability. - This function is also called the percent point function or quantile function. - ''' - if (p <= 0.0 or p >= 1.0): + This function is also called the percent point function or quantile + function. + """ + if p <= 0.0 or p >= 1.0: raise StatisticsError('p must be in the range 0.0 < p < 1.0') if self._sigma <= 0.0: raise StatisticsError('cdf() not defined when sigma at or below zero') @@ -933,7 +954,7 @@ def inv_cdf(self, p): return self._mu + (x * self._sigma) def overlap(self, other): - '''Compute the overlapping coefficient (OVL) between two normal distributions. + """Compute the overlapping coefficient (OVL) between two normal distributions. Measures the agreement between two normal probability distributions. Returns a value between 0.0 and 1.0 giving the overlapping area in @@ -943,7 +964,7 @@ def overlap(self, other): >>> N2 = NormalDist(3.2, 2.0) >>> N1.overlap(N2) 0.8035050657330205 - ''' + """ # See: "The overlapping coefficient as a measure of agreement between # probability distributions and point estimation of the overlap of two # normal densities" -- Henry F. Inman and Edwin L. Bradley Jr @@ -968,21 +989,21 @@ def overlap(self, other): @property def mean(self): - 'Arithmetic mean of the normal distribution.' + "Arithmetic mean of the normal distribution." return self._mu @property def stdev(self): - 'Standard deviation of the normal distribution.' + "Standard deviation of the normal distribution." return self._sigma @property def variance(self): - 'Square of the standard deviation.' + "Square of the standard deviation." return self._sigma ** 2.0 def __add__(x1, x2): - '''Add a constant or another NormalDist instance. + """Add a constant or another NormalDist instance. If *other* is a constant, translate mu by the constant, leaving sigma unchanged. @@ -990,13 +1011,13 @@ def __add__(x1, x2): If *other* is a NormalDist, add both the means and the variances. Mathematically, this works only if the two distributions are independent or if they are jointly normally distributed. - ''' + """ if isinstance(x2, NormalDist): return NormalDist(x1._mu + x2._mu, hypot(x1._sigma, x2._sigma)) return NormalDist(x1._mu + x2, x1._sigma) def __sub__(x1, x2): - '''Subtract a constant or another NormalDist instance. + """Subtract a constant or another NormalDist instance. If *other* is a constant, translate by the constant mu, leaving sigma unchanged. @@ -1004,51 +1025,51 @@ def __sub__(x1, x2): If *other* is a NormalDist, subtract the means and add the variances. Mathematically, this works only if the two distributions are independent or if they are jointly normally distributed. - ''' + """ if isinstance(x2, NormalDist): return NormalDist(x1._mu - x2._mu, hypot(x1._sigma, x2._sigma)) return NormalDist(x1._mu - x2, x1._sigma) def __mul__(x1, x2): - '''Multiply both mu and sigma by a constant. + """Multiply both mu and sigma by a constant. Used for rescaling, perhaps to change measurement units. Sigma is scaled with the absolute value of the constant. - ''' + """ return NormalDist(x1._mu * x2, x1._sigma * fabs(x2)) def __truediv__(x1, x2): - '''Divide both mu and sigma by a constant. + """Divide both mu and sigma by a constant. Used for rescaling, perhaps to change measurement units. Sigma is scaled with the absolute value of the constant. - ''' + """ return NormalDist(x1._mu / x2, x1._sigma / fabs(x2)) def __pos__(x1): - 'Return a copy of the instance.' + "Return a copy of the instance." return NormalDist(x1._mu, x1._sigma) def __neg__(x1): - 'Negates mu while keeping sigma the same.' + "Negates mu while keeping sigma the same." return NormalDist(-x1._mu, x1._sigma) __radd__ = __add__ def __rsub__(x1, x2): - 'Subtract a NormalDist from a constant or another NormalDist.' + "Subtract a NormalDist from a constant or another NormalDist." return -(x1 - x2) __rmul__ = __mul__ def __eq__(x1, x2): - 'Two NormalDist objects are equal if their mu and sigma are both equal.' + "Two NormalDist objects are equal if their mu and sigma are both equal." if not isinstance(x2, NormalDist): return NotImplemented return (x1._mu, x2._sigma) == (x2._mu, x2._sigma) def __hash__(self): - 'NormalDist objects hash equal if their mu and sigma are both equal.' + "NormalDist objects hash equal if their mu and sigma are both equal." return hash((self._mu, self._sigma)) def __repr__(self): From webhook-mailer at python.org Sun Jul 21 15:24:49 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sun, 21 Jul 2019 19:24:49 -0000 Subject: [Python-checkins] Fix typo found by Min ho Kim (#14879) Message-ID: https://github.com/python/cpython/commit/8e3a7380ecb310b50e48f47d1f26190cc9c45eb6 commit: 8e3a7380ecb310b50e48f47d1f26190cc9c45eb6 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-21T15:24:45-04:00 summary: Fix typo found by Min ho Kim (#14879) files: M Lib/idlelib/help.py diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 652444a7f149..e19d3615016f 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -257,7 +257,7 @@ def copy_strip(): same, help.html can be backported. The internal Python version number is not displayed. If maintenance idle.rst diverges from the master version, then instead of backporting help.html from - master, repeat the proceedure above to generate a maintenance + master, repeat the procedure above to generate a maintenance version. """ src = join(abspath(dirname(dirname(dirname(__file__)))), From webhook-mailer at python.org Sun Jul 21 15:39:02 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Sun, 21 Jul 2019 19:39:02 -0000 Subject: [Python-checkins] Minor whitespace, indentation, and quoting changes to improve internal consistency and appease linters (GH-14888) (GH-14889) Message-ID: https://github.com/python/cpython/commit/e8b3a2e78a4ce72a9d2684c0e92a53f35363d108 commit: e8b3a2e78a4ce72a9d2684c0e92a53f35363d108 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Raymond Hettinger date: 2019-07-21T12:38:58-07:00 summary: Minor whitespace, indentation, and quoting changes to improve internal consistency and appease linters (GH-14888) (GH-14889) (cherry picked from commit 1c0e9bb94b8cfb5970532a9f3499563360ea5532) Co-authored-by: Raymond Hettinger files: M Lib/statistics.py diff --git a/Lib/statistics.py b/Lib/statistics.py index ff07dc4a6b55..8a6be7c75901 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -80,12 +80,25 @@ """ -__all__ = [ 'StatisticsError', 'NormalDist', 'quantiles', - 'pstdev', 'pvariance', 'stdev', 'variance', - 'median', 'median_low', 'median_high', 'median_grouped', - 'mean', 'mode', 'multimode', 'harmonic_mean', 'fmean', - 'geometric_mean', - ] +__all__ = [ + 'NormalDist', + 'StatisticsError', + 'fmean', + 'geometric_mean', + 'harmonic_mean', + 'mean', + 'median', + 'median_grouped', + 'median_high', + 'median_low', + 'mode', + 'multimode', + 'pstdev', + 'pvariance', + 'quantiles', + 'stdev', + 'variance', +] import math import numbers @@ -304,8 +317,9 @@ def mean(data): assert count == n return _convert(total/n, T) + def fmean(data): - """ Convert data to floats and compute the arithmetic mean. + """Convert data to floats and compute the arithmetic mean. This runs faster than the mean() function and it always returns a float. The result is highly accurate but not as perfect as mean(). @@ -313,7 +327,6 @@ def fmean(data): >>> fmean([3.5, 4.0, 5.25]) 4.25 - """ try: n = len(data) @@ -332,6 +345,7 @@ def count(iterable): except ZeroDivisionError: raise StatisticsError('fmean requires at least one data point') from None + def geometric_mean(data): """Convert data to floats and compute the geometric mean. @@ -350,6 +364,7 @@ def geometric_mean(data): raise StatisticsError('geometric mean requires a non-empty dataset ' ' containing positive numbers') from None + def harmonic_mean(data): """Return the harmonic mean of data. @@ -547,23 +562,23 @@ def mode(data): def multimode(data): - """ Return a list of the most frequently occurring values. - - Will return more than one result if there are multiple modes - or an empty list if *data* is empty. + """Return a list of the most frequently occurring values. - >>> multimode('aabbbbbbbbcc') - ['b'] - >>> multimode('aabbbbccddddeeffffgg') - ['b', 'd', 'f'] - >>> multimode('') - [] + Will return more than one result if there are multiple modes + or an empty list if *data* is empty. + >>> multimode('aabbbbbbbbcc') + ['b'] + >>> multimode('aabbbbccddddeeffffgg') + ['b', 'd', 'f'] + >>> multimode('') + [] """ counts = Counter(iter(data)).most_common() maxcount, mode_items = next(groupby(counts, key=itemgetter(1)), (0, [])) return list(map(itemgetter(0), mode_items)) + # Notes on methods for computing quantiles # ---------------------------------------- # @@ -601,7 +616,7 @@ def multimode(data): # external packages can be used for anything more advanced. def quantiles(dist, /, *, n=4, method='exclusive'): - '''Divide *dist* into *n* continuous intervals with equal probability. + """Divide *dist* into *n* continuous intervals with equal probability. Returns a list of (n - 1) cut points separating the intervals. @@ -616,7 +631,7 @@ def quantiles(dist, /, *, n=4, method='exclusive'): If *method* is set to *inclusive*, *dist* is treated as population data. The minimum value is treated as the 0th percentile and the maximum value is treated as the 100th percentile. - ''' + """ if n < 1: raise StatisticsError('n must be at least 1') if hasattr(dist, 'inv_cdf'): @@ -646,6 +661,7 @@ def quantiles(dist, /, *, n=4, method='exclusive'): return result raise ValueError(f'Unknown method: {method!r}') + # === Measures of spread === # See http://mathworld.wolfram.com/Variance.html @@ -805,18 +821,21 @@ def pstdev(data, mu=None): except AttributeError: return math.sqrt(var) + ## Normal Distribution ##################################################### class NormalDist: - 'Normal distribution of a random variable' + "Normal distribution of a random variable" # https://en.wikipedia.org/wiki/Normal_distribution # https://en.wikipedia.org/wiki/Variance#Properties - __slots__ = {'_mu': 'Arithmetic mean of a normal distribution', - '_sigma': 'Standard deviation of a normal distribution'} + __slots__ = { + '_mu': 'Arithmetic mean of a normal distribution', + '_sigma': 'Standard deviation of a normal distribution', + } def __init__(self, mu=0.0, sigma=1.0): - 'NormalDist where mu is the mean and sigma is the standard deviation.' + "NormalDist where mu is the mean and sigma is the standard deviation." if sigma < 0.0: raise StatisticsError('sigma must be non-negative') self._mu = mu @@ -824,40 +843,42 @@ def __init__(self, mu=0.0, sigma=1.0): @classmethod def from_samples(cls, data): - 'Make a normal distribution instance from sample data.' + "Make a normal distribution instance from sample data." if not isinstance(data, (list, tuple)): data = list(data) xbar = fmean(data) return cls(xbar, stdev(data, xbar)) def samples(self, n, *, seed=None): - 'Generate *n* samples for a given mean and standard deviation.' + "Generate *n* samples for a given mean and standard deviation." gauss = random.gauss if seed is None else random.Random(seed).gauss mu, sigma = self._mu, self._sigma return [gauss(mu, sigma) for i in range(n)] def pdf(self, x): - 'Probability density function. P(x <= X < x+dx) / dx' + "Probability density function. P(x <= X < x+dx) / dx" variance = self._sigma ** 2.0 if not variance: raise StatisticsError('pdf() not defined when sigma is zero') - return exp((x - self._mu)**2.0 / (-2.0*variance)) / sqrt(tau * variance) + return exp((x - self._mu)**2.0 / (-2.0*variance)) / sqrt(tau*variance) def cdf(self, x): - 'Cumulative distribution function. P(X <= x)' + "Cumulative distribution function. P(X <= x)" if not self._sigma: raise StatisticsError('cdf() not defined when sigma is zero') return 0.5 * (1.0 + erf((x - self._mu) / (self._sigma * sqrt(2.0)))) def inv_cdf(self, p): - '''Inverse cumulative distribution function. x : P(X <= x) = p + """Inverse cumulative distribution function. x : P(X <= x) = p - Finds the value of the random variable such that the probability of the - variable being less than or equal to that value equals the given probability. + Finds the value of the random variable such that the probability of + the variable being less than or equal to that value equals the given + probability. - This function is also called the percent point function or quantile function. - ''' - if (p <= 0.0 or p >= 1.0): + This function is also called the percent point function or quantile + function. + """ + if p <= 0.0 or p >= 1.0: raise StatisticsError('p must be in the range 0.0 < p < 1.0') if self._sigma <= 0.0: raise StatisticsError('cdf() not defined when sigma at or below zero') @@ -933,7 +954,7 @@ def inv_cdf(self, p): return self._mu + (x * self._sigma) def overlap(self, other): - '''Compute the overlapping coefficient (OVL) between two normal distributions. + """Compute the overlapping coefficient (OVL) between two normal distributions. Measures the agreement between two normal probability distributions. Returns a value between 0.0 and 1.0 giving the overlapping area in @@ -943,7 +964,7 @@ def overlap(self, other): >>> N2 = NormalDist(3.2, 2.0) >>> N1.overlap(N2) 0.8035050657330205 - ''' + """ # See: "The overlapping coefficient as a measure of agreement between # probability distributions and point estimation of the overlap of two # normal densities" -- Henry F. Inman and Edwin L. Bradley Jr @@ -968,21 +989,21 @@ def overlap(self, other): @property def mean(self): - 'Arithmetic mean of the normal distribution.' + "Arithmetic mean of the normal distribution." return self._mu @property def stdev(self): - 'Standard deviation of the normal distribution.' + "Standard deviation of the normal distribution." return self._sigma @property def variance(self): - 'Square of the standard deviation.' + "Square of the standard deviation." return self._sigma ** 2.0 def __add__(x1, x2): - '''Add a constant or another NormalDist instance. + """Add a constant or another NormalDist instance. If *other* is a constant, translate mu by the constant, leaving sigma unchanged. @@ -990,13 +1011,13 @@ def __add__(x1, x2): If *other* is a NormalDist, add both the means and the variances. Mathematically, this works only if the two distributions are independent or if they are jointly normally distributed. - ''' + """ if isinstance(x2, NormalDist): return NormalDist(x1._mu + x2._mu, hypot(x1._sigma, x2._sigma)) return NormalDist(x1._mu + x2, x1._sigma) def __sub__(x1, x2): - '''Subtract a constant or another NormalDist instance. + """Subtract a constant or another NormalDist instance. If *other* is a constant, translate by the constant mu, leaving sigma unchanged. @@ -1004,51 +1025,51 @@ def __sub__(x1, x2): If *other* is a NormalDist, subtract the means and add the variances. Mathematically, this works only if the two distributions are independent or if they are jointly normally distributed. - ''' + """ if isinstance(x2, NormalDist): return NormalDist(x1._mu - x2._mu, hypot(x1._sigma, x2._sigma)) return NormalDist(x1._mu - x2, x1._sigma) def __mul__(x1, x2): - '''Multiply both mu and sigma by a constant. + """Multiply both mu and sigma by a constant. Used for rescaling, perhaps to change measurement units. Sigma is scaled with the absolute value of the constant. - ''' + """ return NormalDist(x1._mu * x2, x1._sigma * fabs(x2)) def __truediv__(x1, x2): - '''Divide both mu and sigma by a constant. + """Divide both mu and sigma by a constant. Used for rescaling, perhaps to change measurement units. Sigma is scaled with the absolute value of the constant. - ''' + """ return NormalDist(x1._mu / x2, x1._sigma / fabs(x2)) def __pos__(x1): - 'Return a copy of the instance.' + "Return a copy of the instance." return NormalDist(x1._mu, x1._sigma) def __neg__(x1): - 'Negates mu while keeping sigma the same.' + "Negates mu while keeping sigma the same." return NormalDist(-x1._mu, x1._sigma) __radd__ = __add__ def __rsub__(x1, x2): - 'Subtract a NormalDist from a constant or another NormalDist.' + "Subtract a NormalDist from a constant or another NormalDist." return -(x1 - x2) __rmul__ = __mul__ def __eq__(x1, x2): - 'Two NormalDist objects are equal if their mu and sigma are both equal.' + "Two NormalDist objects are equal if their mu and sigma are both equal." if not isinstance(x2, NormalDist): return NotImplemented return (x1._mu, x2._sigma) == (x2._mu, x2._sigma) def __hash__(self): - 'NormalDist objects hash equal if their mu and sigma are both equal.' + "NormalDist objects hash equal if their mu and sigma are both equal." return hash((self._mu, self._sigma)) def __repr__(self): From webhook-mailer at python.org Sun Jul 21 15:44:32 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 21 Jul 2019 19:44:32 -0000 Subject: [Python-checkins] Fix typo found by Min ho Kim (GH-14879) Message-ID: https://github.com/python/cpython/commit/a8cac5762c22120ccbacb6568bba77af2d22b3f7 commit: a8cac5762c22120ccbacb6568bba77af2d22b3f7 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-21T12:44:28-07:00 summary: Fix typo found by Min ho Kim (GH-14879) (cherry picked from commit 8e3a7380ecb310b50e48f47d1f26190cc9c45eb6) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/help.py diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 652444a7f149..e19d3615016f 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -257,7 +257,7 @@ def copy_strip(): same, help.html can be backported. The internal Python version number is not displayed. If maintenance idle.rst diverges from the master version, then instead of backporting help.html from - master, repeat the proceedure above to generate a maintenance + master, repeat the procedure above to generate a maintenance version. """ src = join(abspath(dirname(dirname(dirname(__file__)))), From webhook-mailer at python.org Sun Jul 21 15:54:38 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 21 Jul 2019 19:54:38 -0000 Subject: [Python-checkins] Fix typo found by Min ho Kim (GH-14879) Message-ID: https://github.com/python/cpython/commit/7649f8c7f891626a47b05a434d7249647dfcfa69 commit: 7649f8c7f891626a47b05a434d7249647dfcfa69 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-21T12:54:34-07:00 summary: Fix typo found by Min ho Kim (GH-14879) (cherry picked from commit 8e3a7380ecb310b50e48f47d1f26190cc9c45eb6) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/help.py diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 652444a7f149..e19d3615016f 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -257,7 +257,7 @@ def copy_strip(): same, help.html can be backported. The internal Python version number is not displayed. If maintenance idle.rst diverges from the master version, then instead of backporting help.html from - master, repeat the proceedure above to generate a maintenance + master, repeat the procedure above to generate a maintenance version. """ src = join(abspath(dirname(dirname(dirname(__file__)))), From webhook-mailer at python.org Sun Jul 21 16:12:37 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sun, 21 Jul 2019 20:12:37 -0000 Subject: [Python-checkins] Fix typos in docs, comments and test assert messages (#14872) Message-ID: https://github.com/python/cpython/commit/96e12d5f4f3c5a20986566038ee763dff3c228a1 commit: 96e12d5f4f3c5a20986566038ee763dff3c228a1 branch: master author: Min ho Kim committer: Terry Jan Reedy date: 2019-07-21T16:12:33-04:00 summary: Fix typos in docs, comments and test assert messages (#14872) files: M Doc/library/ctypes.rst M Lib/asyncio/unix_events.py M Lib/collections/__init__.py M Lib/dataclasses.py M Lib/email/headerregistry.py M Lib/importlib/_bootstrap_external.py M Lib/test/support/__init__.py M Lib/test/test_dataclasses.py M Lib/test/test_eintr.py M Lib/test/test_fstring.py M Lib/test/test_importlib/test_main.py M Lib/test/test_math.py M Lib/test/test_random.py M Misc/NEWS.d/3.5.2rc1.rst M Misc/NEWS.d/3.6.0a1.rst M Misc/NEWS.d/3.6.3rc1.rst M Misc/NEWS.d/3.8.0a1.rst M Misc/NEWS.d/next/Build/2019-06-17-09-40-59.bpo-37189.j5ebdT.rst M Modules/_pickle.c M Modules/_xxtestfuzz/fuzzer.c M Python/initconfig.c diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 02a5500e8115..eb0bcf4ae2e3 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1175,7 +1175,7 @@ Keep in mind that retrieving sub-objects from Structure, Unions, and Arrays doesn't *copy* the sub-object, instead it retrieves a wrapper object accessing the root-object's underlying buffer. -Another example that may behave different from what one would expect is this:: +Another example that may behave differently from what one would expect is this:: >>> s = c_char_p() >>> s.value = b"abc def ghi" diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index d7a4af86f71b..cbbb1065aeda 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -1243,7 +1243,7 @@ class ThreadedChildWatcher(AbstractChildWatcher): It doesn't require subscription on POSIX signal but a thread creation is not free. - The watcher has O(1) complexity, its perfomance doesn't depend + The watcher has O(1) complexity, its performance doesn't depend on amount of spawn processes. """ diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 2264efe94a7d..75d797579d94 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -440,7 +440,7 @@ def __getnewargs__(self): '__slots__': (), '_fields': field_names, '_field_defaults': field_defaults, - # alternate spelling for backward compatiblity + # alternate spelling for backward compatibility '_fields_defaults': field_defaults, '__new__': __new__, '_make': _make, diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 5e57e200847f..f778a27912de 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1189,7 +1189,7 @@ class C(Base): raise TypeError(f'Invalid field: {item!r}') if not isinstance(name, str) or not name.isidentifier(): - raise TypeError(f'Field names must be valid identifers: {name!r}') + raise TypeError(f'Field names must be valid identifiers: {name!r}') if keyword.iskeyword(name): raise TypeError(f'Field names must not be keywords: {name!r}') if name in seen: diff --git a/Lib/email/headerregistry.py b/Lib/email/headerregistry.py index 452c6ad50846..8d1a2025271f 100644 --- a/Lib/email/headerregistry.py +++ b/Lib/email/headerregistry.py @@ -245,7 +245,7 @@ def fold(self, *, policy): the header name and the ': ' separator. """ - # At some point we need to put fws here iif it was in the source. + # At some point we need to put fws here if it was in the source. header = parser.Header([ parser.HeaderLabel([ parser.ValueTerminal(self.name, 'header-name'), diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 1bafc242c518..5aac048060c2 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -261,7 +261,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.7a2 3391 (update GET_AITER #31709) # Python 3.7a4 3392 (PEP 552: Deterministic pycs #31650) # Python 3.7b1 3393 (remove STORE_ANNOTATION opcode #32550) -# Python 3.7b5 3394 (restored docstring as the firts stmt in the body; +# Python 3.7b5 3394 (restored docstring as the first stmt in the body; # this might affected the first line number #32911) # Python 3.8a1 3400 (move frame block handling to compiler #17611) # Python 3.8a1 3401 (add END_ASYNC_FOR #33041) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 4bf42e0eb423..9efb3d91705f 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2978,7 +2978,7 @@ def fd_count(): if sys.platform.startswith(('linux', 'freebsd')): try: names = os.listdir("/proc/self/fd") - # Substract one because listdir() opens internally a file + # Subtract one because listdir() internally opens a file # descriptor to list the content of the /proc/self/fd/ directory. return len(names) - 1 except FileNotFoundError: diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 53e8443c2adf..ea42904b003d 100755 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -3043,11 +3043,11 @@ def test_keyword_field_names(self): def test_non_identifier_field_names(self): for field in ['()', 'x,y', '*', '2 at 3', '', 'little johnny tables']: with self.subTest(field=field): - with self.assertRaisesRegex(TypeError, 'must be valid identifers'): + with self.assertRaisesRegex(TypeError, 'must be valid identifiers'): make_dataclass('C', ['a', field]) - with self.assertRaisesRegex(TypeError, 'must be valid identifers'): + with self.assertRaisesRegex(TypeError, 'must be valid identifiers'): make_dataclass('C', [field]) - with self.assertRaisesRegex(TypeError, 'must be valid identifers'): + with self.assertRaisesRegex(TypeError, 'must be valid identifiers'): make_dataclass('C', [field, 'a']) def test_underscore_field_names(self): diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index f61efa3c648e..a5f8f6465e88 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -22,7 +22,7 @@ def test_all(self): print() print("--- run eintr_tester.py ---", flush=True) # In verbose mode, the child process inherit stdout and stdout, - # to see output in realtime and reduce the risk of loosing output. + # to see output in realtime and reduce the risk of losing output. args = [sys.executable, "-E", "-X", "faulthandler", *args] proc = subprocess.run(args) print(f"--- eintr_tester.py completed: " diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index c9e6e7de5afd..fb761441fcee 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -1161,7 +1161,7 @@ def __repr__(self): # These next lines contains tabs. Backslash escapes don't # work in f-strings. - # patchcheck doens't like these tabs. So the only way to test + # patchcheck doesn't like these tabs. So the only way to test # this will be to dynamically created and exec the f-strings. But # that's such a hassle I'll save it for another day. For now, convert # the tabs to spaces just to shut up patchcheck. diff --git a/Lib/test/test_importlib/test_main.py b/Lib/test/test_importlib/test_main.py index 844ed26c3ec7..5e2cb264e28a 100644 --- a/Lib/test/test_importlib/test_main.py +++ b/Lib/test/test_importlib/test_main.py @@ -32,7 +32,7 @@ def test_new_style_classes(self): class ImportTests(fixtures.DistInfoPkg, unittest.TestCase): def test_import_nonexistent_module(self): # Ensure that the MetadataPathFinder does not crash an import of a - # non-existant module. + # nonexistent module. with self.assertRaises(ImportError): importlib.import_module('does_not_exist') diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 393cdaff1818..567a5c694c15 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1875,7 +1875,7 @@ def test_fractions(self): def testPerm(self): perm = math.perm factorial = math.factorial - # Test if factorial defintion is satisfied + # Test if factorial definition is satisfied for n in range(100): for k in range(n + 1): self.assertEqual(perm(n, k), @@ -1939,7 +1939,7 @@ def testPerm(self): def testComb(self): comb = math.comb factorial = math.factorial - # Test if factorial defintion is satisfied + # Test if factorial definition is satisfied for n in range(100): for k in range(n + 1): self.assertEqual(comb(n, k), factorial(n) diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index ff1ddcaf1407..899ca108c65d 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -228,7 +228,7 @@ def test_choices(self): choices([], cum_weights=[], k=5) def test_choices_subnormal(self): - # Subnormal weights would occassionally trigger an IndexError + # Subnormal weights would occasionally trigger an IndexError # in choices() when the value returned by random() was large # enough to make `random() * total` round up to the total. # See https://bugs.python.org/msg275594 for more detail. diff --git a/Misc/NEWS.d/3.5.2rc1.rst b/Misc/NEWS.d/3.5.2rc1.rst index 3036625d7358..d891fa0880da 100644 --- a/Misc/NEWS.d/3.5.2rc1.rst +++ b/Misc/NEWS.d/3.5.2rc1.rst @@ -2005,7 +2005,7 @@ Adds validation of ucrtbase[d].dll version with warning for old versions. .. nonce: 102DA- .. section: Build -Avoid error about nonexistant fileblocks.o file by using a lower-level check +Avoid error about nonexistent fileblocks.o file by using a lower-level check for st_blocks in struct stat. .. diff --git a/Misc/NEWS.d/3.6.0a1.rst b/Misc/NEWS.d/3.6.0a1.rst index dc08e42fb7e1..3fa356c56d94 100644 --- a/Misc/NEWS.d/3.6.0a1.rst +++ b/Misc/NEWS.d/3.6.0a1.rst @@ -3694,7 +3694,7 @@ Adds validation of ucrtbase[d].dll version with warning for old versions. .. nonce: 102DA- .. section: Build -Avoid error about nonexistant fileblocks.o file by using a lower-level check +Avoid error about nonexistent fileblocks.o file by using a lower-level check for st_blocks in struct stat. .. diff --git a/Misc/NEWS.d/3.6.3rc1.rst b/Misc/NEWS.d/3.6.3rc1.rst index 759436028b66..4dc2eef5d3b6 100644 --- a/Misc/NEWS.d/3.6.3rc1.rst +++ b/Misc/NEWS.d/3.6.3rc1.rst @@ -877,7 +877,7 @@ The affected events are '<>', '<>', '<>', '<>', and '<>'. Any (global) customizations made before 3.6.3 will not affect their keyset-specific customization after 3.6.3. and vice versa. -Inital patch by Charles Wohlganger. +Initial patch by Charles Wohlganger. .. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index 84c2a6c12bcf..3d376693d380 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -1358,7 +1358,7 @@ Improved syntax error messages for unbalanced parentheses. .. section: Core and Builtins The list constructor will pre-size and not over-allocate when the input -lenght is known. +length is known. .. diff --git a/Misc/NEWS.d/next/Build/2019-06-17-09-40-59.bpo-37189.j5ebdT.rst b/Misc/NEWS.d/next/Build/2019-06-17-09-40-59.bpo-37189.j5ebdT.rst index f11f2746e5a0..8b3be5eca72d 100644 --- a/Misc/NEWS.d/next/Build/2019-06-17-09-40-59.bpo-37189.j5ebdT.rst +++ b/Misc/NEWS.d/next/Build/2019-06-17-09-40-59.bpo-37189.j5ebdT.rst @@ -1,3 +1,3 @@ Many ``PyRun_XXX()`` functions like :c:func:`PyRun_String` were no longer exported in ``libpython38.dll`` by mistake. Export them again to fix the ABI -compatibiliy. +compatibility. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 735c13d03d8f..054276d85adf 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -2119,7 +2119,7 @@ save_long(PicklerObject *self, PyObject *obj) /* How many bytes do we need? There are nbits >> 3 full * bytes of data, and nbits & 7 leftover bits. If there * are any leftover bits, then we clearly need another - * byte. Wnat's not so obvious is that we *probably* + * byte. What's not so obvious is that we *probably* * need another byte even if there aren't any leftovers: * the most-significant bit of the most-significant byte * acts like a sign bit, and it's usually got a sense diff --git a/Modules/_xxtestfuzz/fuzzer.c b/Modules/_xxtestfuzz/fuzzer.c index 2446e712c81a..dae1eaeabd00 100644 --- a/Modules/_xxtestfuzz/fuzzer.c +++ b/Modules/_xxtestfuzz/fuzzer.c @@ -210,7 +210,7 @@ static int fuzz_sre_compile(const char* data, size_t size) { /* Some random patterns used to test re.match. Be careful not to add catostraphically slow regexes here, we want to - excercise the matching code without causing timeouts.*/ + exercise the matching code without causing timeouts.*/ static const char* regex_patterns[] = { ".", "^", "abc", "abc|def", "^xxx$", "\\b", "()", "[a-zA-Z0-9]", "abc+", "[^A-Z]", "[x]", "(?=)", "a{z}", "a+b", "a*?", "a??", "a+?", diff --git a/Python/initconfig.c b/Python/initconfig.c index c44ae6bdfacc..924744302890 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -300,7 +300,7 @@ PyStatus PyWideStringList_Append(PyWideStringList *list, const wchar_t *item) { if (list->length == PY_SSIZE_T_MAX) { - /* lenght+1 would overflow */ + /* length+1 would overflow */ return _PyStatus_NO_MEMORY(); } From webhook-mailer at python.org Sun Jul 21 16:25:47 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 21 Jul 2019 20:25:47 -0000 Subject: [Python-checkins] bpo-37627: Initialize IDLE Custom Run dialog with previous entries (GH-14870) Message-ID: https://github.com/python/cpython/commit/9325f4091b66f3a9cc85681435367671d335e3e7 commit: 9325f4091b66f3a9cc85681435367671d335e3e7 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-21T13:25:43-07:00 summary: bpo-37627: Initialize IDLE Custom Run dialog with previous entries (GH-14870) Repeat the command line arguments most recently entered before so the user can edit them. (cherry picked from commit 35b87e6001bd991f625abe305951c77ddeb9a9c5) Co-authored-by: Ngalim Siregar files: A Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst M Lib/idlelib/idle_test/test_query.py M Lib/idlelib/query.py M Lib/idlelib/runscript.py diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py index 3b444de15d7c..f957585190dc 100644 --- a/Lib/idlelib/idle_test/test_query.py +++ b/Lib/idlelib/idle_test/test_query.py @@ -12,7 +12,7 @@ from idlelib import query import unittest from test.support import requires -from tkinter import Tk +from tkinter import Tk, END import sys from unittest import mock @@ -392,10 +392,12 @@ def setUpClass(cls): def test_click_args(self): root = Tk() root.withdraw() - dialog = query.CustomRun(root, 'Title', _utest=True) - dialog.entry.insert(0, 'okay') + dialog = query.CustomRun(root, 'Title', + cli_args=['a', 'b=1'], _utest=True) + self.assertEqual(dialog.entry.get(), 'a b=1') + dialog.entry.insert(END, ' c') dialog.button_ok.invoke() - self.assertEqual(dialog.result, (['okay'], True)) + self.assertEqual(dialog.result, (['a', 'b=1', 'c'], True)) root.destroy() diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index d74084feed76..097e6e61e356 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -325,9 +325,13 @@ class CustomRun(Query): """ # Used in runscript.run_custom_event - def __init__(self, parent, title, *, cli_args='', + def __init__(self, parent, title, *, cli_args=[], _htest=False, _utest=False): - # TODO Use cli_args to pre-populate entry. + """cli_args is a list of strings. + + The list is assigned to the default Entry StringVar. + The strings are displayed joined by ' ' for display. + """ message = 'Command Line Arguments for sys.argv:' super().__init__( parent, title, message, text0=cli_args, diff --git a/Lib/idlelib/runscript.py b/Lib/idlelib/runscript.py index b041e56fb840..f97cf528cce6 100644 --- a/Lib/idlelib/runscript.py +++ b/Lib/idlelib/runscript.py @@ -39,6 +39,8 @@ def __init__(self, editwin): # XXX This should be done differently self.flist = self.editwin.flist self.root = self.editwin.root + # cli_args is list of strings that extends sys.argv + self.cli_args = [] if macosx.isCocoaTk(): self.editwin.text_frame.bind('<>', self._run_module_event) @@ -137,10 +139,11 @@ def _run_module_event(self, event, *, customize=False): return 'break' if customize: title = f"Customize {self.editwin.short_title()} Run" - run_args = CustomRun(self.shell.text, title).result + run_args = CustomRun(self.shell.text, title, + cli_args=self.cli_args).result if not run_args: # User cancelled. return 'break' - cli_args, restart = run_args if customize else ([], True) + self.cli_args, restart = run_args if customize else ([], True) interp = self.shell.interp if pyshell.use_subprocess and restart: interp.restart_subprocess( @@ -148,8 +151,8 @@ def _run_module_event(self, event, *, customize=False): self.editwin._filename_to_unicode(filename)) dirname = os.path.dirname(filename) argv = [filename] - if cli_args: - argv += cli_args + if self.cli_args: + argv += self.cli_args interp.runcommand(f"""if 1: __file__ = {filename!r} import sys as _sys diff --git a/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst b/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst new file mode 100644 index 000000000000..d864d07f60ef --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst @@ -0,0 +1,3 @@ +Initialize the Customize Run dialog with the command line arguments +most recently entered before. The user can optionally edit before +submitting them. From webhook-mailer at python.org Sun Jul 21 16:26:28 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sun, 21 Jul 2019 20:26:28 -0000 Subject: [Python-checkins] bpo-37627: Add acknowledgment (#14883) Message-ID: https://github.com/python/cpython/commit/4214f1ec3b3c73badd639229eff81eb5e57b82ec commit: 4214f1ec3b3c73badd639229eff81eb5e57b82ec branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-21T16:26:24-04:00 summary: bpo-37627: Add acknowledgment (#14883) files: M Misc/ACKS diff --git a/Misc/ACKS b/Misc/ACKS index 3e6a0aca3573..b062855b9e28 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1537,6 +1537,7 @@ Ng Pheng Siong Yann Sionneau George Sipe J. Sipprell +Ngalim Siregar Kragen Sitaker Kaartic Sivaraam Ville Skytt? From webhook-mailer at python.org Sun Jul 21 16:38:05 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 21 Jul 2019 20:38:05 -0000 Subject: [Python-checkins] bpo-37627: Add acknowledgment (GH-14883) Message-ID: https://github.com/python/cpython/commit/849a37a2b640af14cfb004cdbced01983b0d9d2b commit: 849a37a2b640af14cfb004cdbced01983b0d9d2b branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-21T13:38:01-07:00 summary: bpo-37627: Add acknowledgment (GH-14883) (cherry picked from commit 4214f1ec3b3c73badd639229eff81eb5e57b82ec) Co-authored-by: Terry Jan Reedy files: M Misc/ACKS diff --git a/Misc/ACKS b/Misc/ACKS index 4e2810300e6b..1b54d7ef7f18 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1509,6 +1509,7 @@ Ng Pheng Siong Yann Sionneau George Sipe J. Sipprell +Ngalim Siregar Kragen Sitaker Kaartic Sivaraam Ville Skytt? From webhook-mailer at python.org Sun Jul 21 16:38:13 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 21 Jul 2019 20:38:13 -0000 Subject: [Python-checkins] bpo-37627: Add acknowledgment (GH-14883) Message-ID: https://github.com/python/cpython/commit/52ee929957871da3d2622ab16887a1a03e6e08bf commit: 52ee929957871da3d2622ab16887a1a03e6e08bf branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-21T13:38:09-07:00 summary: bpo-37627: Add acknowledgment (GH-14883) (cherry picked from commit 4214f1ec3b3c73badd639229eff81eb5e57b82ec) Co-authored-by: Terry Jan Reedy files: M Misc/ACKS diff --git a/Misc/ACKS b/Misc/ACKS index 5c7ec998a547..a4973a89d414 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1533,6 +1533,7 @@ Ng Pheng Siong Yann Sionneau George Sipe J. Sipprell +Ngalim Siregar Kragen Sitaker Kaartic Sivaraam Ville Skytt? From webhook-mailer at python.org Sun Jul 21 19:32:04 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Sun, 21 Jul 2019 23:32:04 -0000 Subject: [Python-checkins] Add examples to elucidate the formulas (GH-14898) Message-ID: https://github.com/python/cpython/commit/b530a4460b7a6ea96f1fa81a7c5bb529ead574ef commit: b530a4460b7a6ea96f1fa81a7c5bb529ead574ef branch: master author: Raymond Hettinger committer: GitHub date: 2019-07-21T16:32:00-07:00 summary: Add examples to elucidate the formulas (GH-14898) files: M Doc/library/statistics.rst diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst index bc841fda72f8..a906a591e62c 100644 --- a/Doc/library/statistics.rst +++ b/Doc/library/statistics.rst @@ -527,14 +527,18 @@ However, for reading convenience, most of the examples show sorted sequences. The default *method* is "exclusive" and is used for data sampled from a population that can have more extreme values than found in the samples. The portion of the population falling below the *i-th* of - *m* data points is computed as ``i / (m + 1)``. + *m* sorted data points is computed as ``i / (m + 1)``. Given nine + sample values, the method sorts them and assigns the following + percentiles: 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%. Setting the *method* to "inclusive" is used for describing population - data or for samples that include the extreme points. The minimum - value in *dist* is treated as the 0th percentile and the maximum - value is treated as the 100th percentile. The portion of the - population falling below the *i-th* of *m* data points is computed as - ``(i - 1) / (m - 1)``. + data or for samples that are known to include the most extreme values + from the population. The minimum value in *dist* is treated as the 0th + percentile and the maximum value is treated as the 100th percentile. + The portion of the population falling below the *i-th* of *m* sorted + data points is computed as ``(i - 1) / (m - 1)``. Given 11 sample + values, the method sorts them and assigns the following percentiles: + 0%, 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%, 100%. If *dist* is an instance of a class that defines an :meth:`~inv_cdf` method, setting *method* has no effect. From webhook-mailer at python.org Sun Jul 21 19:39:14 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Sun, 21 Jul 2019 23:39:14 -0000 Subject: [Python-checkins] Add examples to elucidate the formulas (GH-14898) (GH-14899) Message-ID: https://github.com/python/cpython/commit/0104841d12b42ebe5716efbe3ddcb8744dd7cea8 commit: 0104841d12b42ebe5716efbe3ddcb8744dd7cea8 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Raymond Hettinger date: 2019-07-21T16:39:08-07:00 summary: Add examples to elucidate the formulas (GH-14898) (GH-14899) (cherry picked from commit b530a4460b7a6ea96f1fa81a7c5bb529ead574ef) Co-authored-by: Raymond Hettinger files: M Doc/library/statistics.rst diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst index bc841fda72f8..a906a591e62c 100644 --- a/Doc/library/statistics.rst +++ b/Doc/library/statistics.rst @@ -527,14 +527,18 @@ However, for reading convenience, most of the examples show sorted sequences. The default *method* is "exclusive" and is used for data sampled from a population that can have more extreme values than found in the samples. The portion of the population falling below the *i-th* of - *m* data points is computed as ``i / (m + 1)``. + *m* sorted data points is computed as ``i / (m + 1)``. Given nine + sample values, the method sorts them and assigns the following + percentiles: 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%. Setting the *method* to "inclusive" is used for describing population - data or for samples that include the extreme points. The minimum - value in *dist* is treated as the 0th percentile and the maximum - value is treated as the 100th percentile. The portion of the - population falling below the *i-th* of *m* data points is computed as - ``(i - 1) / (m - 1)``. + data or for samples that are known to include the most extreme values + from the population. The minimum value in *dist* is treated as the 0th + percentile and the maximum value is treated as the 100th percentile. + The portion of the population falling below the *i-th* of *m* sorted + data points is computed as ``(i - 1) / (m - 1)``. Given 11 sample + values, the method sorts them and assigns the following percentiles: + 0%, 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%, 100%. If *dist* is an instance of a class that defines an :meth:`~inv_cdf` method, setting *method* has no effect. From webhook-mailer at python.org Sun Jul 21 22:49:11 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Mon, 22 Jul 2019 02:49:11 -0000 Subject: [Python-checkins] [3.8] Fix typos in docs, comments and test assert messages (GH-14872). (#14900) Message-ID: https://github.com/python/cpython/commit/24b5b360faf306ef67419dfe5d49ff22dbe827b1 commit: 24b5b360faf306ef67419dfe5d49ff22dbe827b1 branch: 3.8 author: Kyle Stanley committer: Terry Jan Reedy date: 2019-07-21T22:48:45-04:00 summary: [3.8] Fix typos in docs, comments and test assert messages (GH-14872). (#14900) (cherry picked from commit 96e12d5f4f3c5a20986566038ee763dff3c228a1) Co-authored-by: Min ho Kim files: M Doc/library/ctypes.rst M Lib/asyncio/unix_events.py M Lib/collections/__init__.py M Lib/dataclasses.py M Lib/email/headerregistry.py M Lib/importlib/_bootstrap_external.py M Lib/test/support/__init__.py M Lib/test/test_dataclasses.py M Lib/test/test_eintr.py M Lib/test/test_fstring.py M Lib/test/test_importlib/test_main.py M Lib/test/test_math.py M Lib/test/test_random.py M Misc/NEWS.d/3.5.2rc1.rst M Misc/NEWS.d/3.6.0a1.rst M Misc/NEWS.d/3.6.3rc1.rst M Misc/NEWS.d/3.8.0a1.rst M Modules/_pickle.c M Modules/_xxtestfuzz/fuzzer.c M Python/initconfig.c diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 02a5500e8115..eb0bcf4ae2e3 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1175,7 +1175,7 @@ Keep in mind that retrieving sub-objects from Structure, Unions, and Arrays doesn't *copy* the sub-object, instead it retrieves a wrapper object accessing the root-object's underlying buffer. -Another example that may behave different from what one would expect is this:: +Another example that may behave differently from what one would expect is this:: >>> s = c_char_p() >>> s.value = b"abc def ghi" diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index d7a4af86f71b..cbbb1065aeda 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -1243,7 +1243,7 @@ class ThreadedChildWatcher(AbstractChildWatcher): It doesn't require subscription on POSIX signal but a thread creation is not free. - The watcher has O(1) complexity, its perfomance doesn't depend + The watcher has O(1) complexity, its performance doesn't depend on amount of spawn processes. """ diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index e9999e27d5f5..3d1d93e113fd 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -440,7 +440,7 @@ def __getnewargs__(self): '__slots__': (), '_fields': field_names, '_field_defaults': field_defaults, - # alternate spelling for backward compatiblity + # alternate spelling for backward compatibility '_fields_defaults': field_defaults, '__new__': __new__, '_make': _make, diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 18713722a764..23712fa8709d 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1189,7 +1189,7 @@ class C(Base): raise TypeError(f'Invalid field: {item!r}') if not isinstance(name, str) or not name.isidentifier(): - raise TypeError(f'Field names must be valid identifers: {name!r}') + raise TypeError(f'Field names must be valid identifiers: {name!r}') if keyword.iskeyword(name): raise TypeError(f'Field names must not be keywords: {name!r}') if name in seen: diff --git a/Lib/email/headerregistry.py b/Lib/email/headerregistry.py index 452c6ad50846..8d1a2025271f 100644 --- a/Lib/email/headerregistry.py +++ b/Lib/email/headerregistry.py @@ -245,7 +245,7 @@ def fold(self, *, policy): the header name and the ': ' separator. """ - # At some point we need to put fws here iif it was in the source. + # At some point we need to put fws here if it was in the source. header = parser.Header([ parser.HeaderLabel([ parser.ValueTerminal(self.name, 'header-name'), diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 1bafc242c518..5aac048060c2 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -261,7 +261,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.7a2 3391 (update GET_AITER #31709) # Python 3.7a4 3392 (PEP 552: Deterministic pycs #31650) # Python 3.7b1 3393 (remove STORE_ANNOTATION opcode #32550) -# Python 3.7b5 3394 (restored docstring as the firts stmt in the body; +# Python 3.7b5 3394 (restored docstring as the first stmt in the body; # this might affected the first line number #32911) # Python 3.8a1 3400 (move frame block handling to compiler #17611) # Python 3.8a1 3401 (add END_ASYNC_FOR #33041) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 0d83fe54f4cb..1ada1f927d9c 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2976,7 +2976,7 @@ def fd_count(): if sys.platform.startswith(('linux', 'freebsd')): try: names = os.listdir("/proc/self/fd") - # Substract one because listdir() opens internally a file + # Subtract one because listdir() internally opens a file # descriptor to list the content of the /proc/self/fd/ directory. return len(names) - 1 except FileNotFoundError: diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index cb0e18c242d1..facf9d2e2c05 100755 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -3043,11 +3043,11 @@ def test_keyword_field_names(self): def test_non_identifier_field_names(self): for field in ['()', 'x,y', '*', '2 at 3', '', 'little johnny tables']: with self.subTest(field=field): - with self.assertRaisesRegex(TypeError, 'must be valid identifers'): + with self.assertRaisesRegex(TypeError, 'must be valid identifiers'): make_dataclass('C', ['a', field]) - with self.assertRaisesRegex(TypeError, 'must be valid identifers'): + with self.assertRaisesRegex(TypeError, 'must be valid identifiers'): make_dataclass('C', [field]) - with self.assertRaisesRegex(TypeError, 'must be valid identifers'): + with self.assertRaisesRegex(TypeError, 'must be valid identifiers'): make_dataclass('C', [field, 'a']) def test_underscore_field_names(self): diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index f61efa3c648e..a5f8f6465e88 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -22,7 +22,7 @@ def test_all(self): print() print("--- run eintr_tester.py ---", flush=True) # In verbose mode, the child process inherit stdout and stdout, - # to see output in realtime and reduce the risk of loosing output. + # to see output in realtime and reduce the risk of losing output. args = [sys.executable, "-E", "-X", "faulthandler", *args] proc = subprocess.run(args) print(f"--- eintr_tester.py completed: " diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index c9e6e7de5afd..fb761441fcee 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -1161,7 +1161,7 @@ def __repr__(self): # These next lines contains tabs. Backslash escapes don't # work in f-strings. - # patchcheck doens't like these tabs. So the only way to test + # patchcheck doesn't like these tabs. So the only way to test # this will be to dynamically created and exec the f-strings. But # that's such a hassle I'll save it for another day. For now, convert # the tabs to spaces just to shut up patchcheck. diff --git a/Lib/test/test_importlib/test_main.py b/Lib/test/test_importlib/test_main.py index 844ed26c3ec7..5e2cb264e28a 100644 --- a/Lib/test/test_importlib/test_main.py +++ b/Lib/test/test_importlib/test_main.py @@ -32,7 +32,7 @@ def test_new_style_classes(self): class ImportTests(fixtures.DistInfoPkg, unittest.TestCase): def test_import_nonexistent_module(self): # Ensure that the MetadataPathFinder does not crash an import of a - # non-existant module. + # nonexistent module. with self.assertRaises(ImportError): importlib.import_module('does_not_exist') diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 96af655061f6..90b8b7b4a9e3 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1868,7 +1868,7 @@ def test_fractions(self): def testPerm(self): perm = math.perm factorial = math.factorial - # Test if factorial defintion is satisfied + # Test if factorial definition is satisfied for n in range(100): for k in range(n + 1): self.assertEqual(perm(n, k), @@ -1932,7 +1932,7 @@ def testPerm(self): def testComb(self): comb = math.comb factorial = math.factorial - # Test if factorial defintion is satisfied + # Test if factorial definition is satisfied for n in range(100): for k in range(n + 1): self.assertEqual(comb(n, k), factorial(n) diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index ff1ddcaf1407..899ca108c65d 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -228,7 +228,7 @@ def test_choices(self): choices([], cum_weights=[], k=5) def test_choices_subnormal(self): - # Subnormal weights would occassionally trigger an IndexError + # Subnormal weights would occasionally trigger an IndexError # in choices() when the value returned by random() was large # enough to make `random() * total` round up to the total. # See https://bugs.python.org/msg275594 for more detail. diff --git a/Misc/NEWS.d/3.5.2rc1.rst b/Misc/NEWS.d/3.5.2rc1.rst index 3036625d7358..d891fa0880da 100644 --- a/Misc/NEWS.d/3.5.2rc1.rst +++ b/Misc/NEWS.d/3.5.2rc1.rst @@ -2005,7 +2005,7 @@ Adds validation of ucrtbase[d].dll version with warning for old versions. .. nonce: 102DA- .. section: Build -Avoid error about nonexistant fileblocks.o file by using a lower-level check +Avoid error about nonexistent fileblocks.o file by using a lower-level check for st_blocks in struct stat. .. diff --git a/Misc/NEWS.d/3.6.0a1.rst b/Misc/NEWS.d/3.6.0a1.rst index dc08e42fb7e1..3fa356c56d94 100644 --- a/Misc/NEWS.d/3.6.0a1.rst +++ b/Misc/NEWS.d/3.6.0a1.rst @@ -3694,7 +3694,7 @@ Adds validation of ucrtbase[d].dll version with warning for old versions. .. nonce: 102DA- .. section: Build -Avoid error about nonexistant fileblocks.o file by using a lower-level check +Avoid error about nonexistent fileblocks.o file by using a lower-level check for st_blocks in struct stat. .. diff --git a/Misc/NEWS.d/3.6.3rc1.rst b/Misc/NEWS.d/3.6.3rc1.rst index 759436028b66..4dc2eef5d3b6 100644 --- a/Misc/NEWS.d/3.6.3rc1.rst +++ b/Misc/NEWS.d/3.6.3rc1.rst @@ -877,7 +877,7 @@ The affected events are '<>', '<>', '<>', '<>', and '<>'. Any (global) customizations made before 3.6.3 will not affect their keyset-specific customization after 3.6.3. and vice versa. -Inital patch by Charles Wohlganger. +Initial patch by Charles Wohlganger. .. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index 84c2a6c12bcf..3d376693d380 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -1358,7 +1358,7 @@ Improved syntax error messages for unbalanced parentheses. .. section: Core and Builtins The list constructor will pre-size and not over-allocate when the input -lenght is known. +length is known. .. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 34e11bd5f820..0a597575b358 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -2119,7 +2119,7 @@ save_long(PicklerObject *self, PyObject *obj) /* How many bytes do we need? There are nbits >> 3 full * bytes of data, and nbits & 7 leftover bits. If there * are any leftover bits, then we clearly need another - * byte. Wnat's not so obvious is that we *probably* + * byte. What's not so obvious is that we *probably* * need another byte even if there aren't any leftovers: * the most-significant bit of the most-significant byte * acts like a sign bit, and it's usually got a sense diff --git a/Modules/_xxtestfuzz/fuzzer.c b/Modules/_xxtestfuzz/fuzzer.c index 16104e492ab1..1821eb2a0f01 100644 --- a/Modules/_xxtestfuzz/fuzzer.c +++ b/Modules/_xxtestfuzz/fuzzer.c @@ -210,7 +210,7 @@ static int fuzz_sre_compile(const char* data, size_t size) { /* Some random patterns used to test re.match. Be careful not to add catostraphically slow regexes here, we want to - excercise the matching code without causing timeouts.*/ + exercise the matching code without causing timeouts.*/ static const char* regex_patterns[] = { ".", "^", "abc", "abc|def", "^xxx$", "\\b", "()", "[a-zA-Z0-9]", "abc+", "[^A-Z]", "[x]", "(?=)", "a{z}", "a+b", "a*?", "a??", "a+?", diff --git a/Python/initconfig.c b/Python/initconfig.c index 1c7078a6b570..dd8dd7aeefc6 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -300,7 +300,7 @@ PyStatus PyWideStringList_Append(PyWideStringList *list, const wchar_t *item) { if (list->length == PY_SSIZE_T_MAX) { - /* lenght+1 would overflow */ + /* length+1 would overflow */ return _PyStatus_NO_MEMORY(); } From webhook-mailer at python.org Mon Jul 22 03:38:40 2019 From: webhook-mailer at python.org (Chris Withers) Date: Mon, 22 Jul 2019 07:38:40 -0000 Subject: [Python-checkins] bpo-21478: Record calls to parent when autospecced objects are used as child with attach_mock (GH 14688) Message-ID: https://github.com/python/cpython/commit/7397cda99795a4a8d96193d710105e77a07b7411 commit: 7397cda99795a4a8d96193d710105e77a07b7411 branch: master author: Xtreak committer: Chris Withers date: 2019-07-22T08:38:22+01:00 summary: bpo-21478: Record calls to parent when autospecced objects are used as child with attach_mock (GH 14688) * Clear name and parent of mock in autospecced objects used with attach_mock * Add NEWS entry * Fix reversed order of comparison * Test child and standalone function calls * Use a helper function extracting mock to avoid code duplication and refactor tests. files: A Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testmock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index c2802726d75d..b3dc640f8fe5 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -72,6 +72,15 @@ def _is_exception(obj): ) +def _extract_mock(obj): + # Autospecced functions will return a FunctionType with "mock" attribute + # which is the actual mock object that needs to be used. + if isinstance(obj, FunctionTypes) and hasattr(obj, 'mock'): + return obj.mock + else: + return obj + + def _get_signature_object(func, as_instance, eat_self): """ Given an arbitrary, possibly callable object, try to create a suitable @@ -346,13 +355,7 @@ def __repr__(self): def _check_and_set_parent(parent, value, name, new_name): - # function passed to create_autospec will have mock - # attribute attached to which parent must be set - if isinstance(value, FunctionTypes): - try: - value = value.mock - except AttributeError: - pass + value = _extract_mock(value) if not _is_instance_mock(value): return False @@ -467,10 +470,12 @@ def attach_mock(self, mock, attribute): Attach a mock as an attribute of this one, replacing its name and parent. Calls to the attached mock will be recorded in the `method_calls` and `mock_calls` attributes of this one.""" - mock._mock_parent = None - mock._mock_new_parent = None - mock._mock_name = '' - mock._mock_new_name = None + inner_mock = _extract_mock(mock) + + inner_mock._mock_parent = None + inner_mock._mock_new_parent = None + inner_mock._mock_name = '' + inner_mock._mock_new_name = None setattr(self, attribute, mock) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 0f30bccc9cf0..090da45fb660 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -37,6 +37,9 @@ def cmeth(cls, a, b, c, d=None): pass def smeth(a, b, c, d=None): pass +def something(a): pass + + class MockTest(unittest.TestCase): def test_all(self): @@ -1808,6 +1811,26 @@ def test_attach_mock_return_value(self): self.assertEqual(m.mock_calls, call().foo().call_list()) + def test_attach_mock_patch_autospec(self): + parent = Mock() + + with mock.patch(f'{__name__}.something', autospec=True) as mock_func: + self.assertEqual(mock_func.mock._extract_mock_name(), 'something') + parent.attach_mock(mock_func, 'child') + parent.child(1) + something(2) + mock_func(3) + + parent_calls = [call.child(1), call.child(2), call.child(3)] + child_calls = [call(1), call(2), call(3)] + self.assertEqual(parent.mock_calls, parent_calls) + self.assertEqual(parent.child.mock_calls, child_calls) + self.assertEqual(something.mock_calls, child_calls) + self.assertEqual(mock_func.mock_calls, child_calls) + self.assertIn('mock.child', repr(parent.child.mock)) + self.assertEqual(mock_func.mock._extract_mock_name(), 'mock.child') + + def test_attribute_deletion(self): for mock in (Mock(), MagicMock(), NonCallableMagicMock(), NonCallableMock()): @@ -1891,6 +1914,20 @@ def foo(a, b): pass self.assertRaises(TypeError, mock.child, 1) self.assertEqual(mock.mock_calls, [call.child(1, 2)]) + self.assertIn('mock.child', repr(mock.child.mock)) + + def test_parent_propagation_with_autospec_attach_mock(self): + + def foo(a, b): pass + + parent = Mock() + parent.attach_mock(create_autospec(foo, name='bar'), 'child') + parent.child(1, 2) + + self.assertRaises(TypeError, parent.child, 1) + self.assertEqual(parent.child.mock_calls, [call.child(1, 2)]) + self.assertIn('mock.child', repr(parent.child.mock)) + def test_isinstance_under_settrace(self): # bpo-36593 : __class__ is not set for a class that has __class__ diff --git a/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst b/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst new file mode 100644 index 000000000000..0ac9b8eadcab --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst @@ -0,0 +1,2 @@ +Record calls to parent when autospecced object is attached to a mock using +:func:`unittest.mock.attach_mock`. Patch by Karthikeyan Singaravelan. From webhook-mailer at python.org Mon Jul 22 03:59:14 2019 From: webhook-mailer at python.org (Chris Withers) Date: Mon, 22 Jul 2019 07:59:14 -0000 Subject: [Python-checkins] bpo-21478: Record calls to parent when autospecced objects are used as child with attach_mock (GH 14688) (GH-14902) Message-ID: https://github.com/python/cpython/commit/22fd679dc363bfcbda336775da16aff4d6fcb33f commit: 22fd679dc363bfcbda336775da16aff4d6fcb33f branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2019-07-22T08:59:00+01:00 summary: bpo-21478: Record calls to parent when autospecced objects are used as child with attach_mock (GH 14688) (GH-14902) * Clear name and parent of mock in autospecced objects used with attach_mock * Add NEWS entry * Fix reversed order of comparison * Test child and standalone function calls * Use a helper function extracting mock to avoid code duplication and refactor tests. (cherry picked from commit 7397cda99795a4a8d96193d710105e77a07b7411) Co-authored-by: Xtreak files: A Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testmock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index c2802726d75d..b3dc640f8fe5 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -72,6 +72,15 @@ def _is_exception(obj): ) +def _extract_mock(obj): + # Autospecced functions will return a FunctionType with "mock" attribute + # which is the actual mock object that needs to be used. + if isinstance(obj, FunctionTypes) and hasattr(obj, 'mock'): + return obj.mock + else: + return obj + + def _get_signature_object(func, as_instance, eat_self): """ Given an arbitrary, possibly callable object, try to create a suitable @@ -346,13 +355,7 @@ def __repr__(self): def _check_and_set_parent(parent, value, name, new_name): - # function passed to create_autospec will have mock - # attribute attached to which parent must be set - if isinstance(value, FunctionTypes): - try: - value = value.mock - except AttributeError: - pass + value = _extract_mock(value) if not _is_instance_mock(value): return False @@ -467,10 +470,12 @@ def attach_mock(self, mock, attribute): Attach a mock as an attribute of this one, replacing its name and parent. Calls to the attached mock will be recorded in the `method_calls` and `mock_calls` attributes of this one.""" - mock._mock_parent = None - mock._mock_new_parent = None - mock._mock_name = '' - mock._mock_new_name = None + inner_mock = _extract_mock(mock) + + inner_mock._mock_parent = None + inner_mock._mock_new_parent = None + inner_mock._mock_name = '' + inner_mock._mock_new_name = None setattr(self, attribute, mock) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 0f30bccc9cf0..090da45fb660 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -37,6 +37,9 @@ def cmeth(cls, a, b, c, d=None): pass def smeth(a, b, c, d=None): pass +def something(a): pass + + class MockTest(unittest.TestCase): def test_all(self): @@ -1808,6 +1811,26 @@ def test_attach_mock_return_value(self): self.assertEqual(m.mock_calls, call().foo().call_list()) + def test_attach_mock_patch_autospec(self): + parent = Mock() + + with mock.patch(f'{__name__}.something', autospec=True) as mock_func: + self.assertEqual(mock_func.mock._extract_mock_name(), 'something') + parent.attach_mock(mock_func, 'child') + parent.child(1) + something(2) + mock_func(3) + + parent_calls = [call.child(1), call.child(2), call.child(3)] + child_calls = [call(1), call(2), call(3)] + self.assertEqual(parent.mock_calls, parent_calls) + self.assertEqual(parent.child.mock_calls, child_calls) + self.assertEqual(something.mock_calls, child_calls) + self.assertEqual(mock_func.mock_calls, child_calls) + self.assertIn('mock.child', repr(parent.child.mock)) + self.assertEqual(mock_func.mock._extract_mock_name(), 'mock.child') + + def test_attribute_deletion(self): for mock in (Mock(), MagicMock(), NonCallableMagicMock(), NonCallableMock()): @@ -1891,6 +1914,20 @@ def foo(a, b): pass self.assertRaises(TypeError, mock.child, 1) self.assertEqual(mock.mock_calls, [call.child(1, 2)]) + self.assertIn('mock.child', repr(mock.child.mock)) + + def test_parent_propagation_with_autospec_attach_mock(self): + + def foo(a, b): pass + + parent = Mock() + parent.attach_mock(create_autospec(foo, name='bar'), 'child') + parent.child(1, 2) + + self.assertRaises(TypeError, parent.child, 1) + self.assertEqual(parent.child.mock_calls, [call.child(1, 2)]) + self.assertIn('mock.child', repr(parent.child.mock)) + def test_isinstance_under_settrace(self): # bpo-36593 : __class__ is not set for a class that has __class__ diff --git a/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst b/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst new file mode 100644 index 000000000000..0ac9b8eadcab --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst @@ -0,0 +1,2 @@ +Record calls to parent when autospecced object is attached to a mock using +:func:`unittest.mock.attach_mock`. Patch by Karthikeyan Singaravelan. From webhook-mailer at python.org Mon Jul 22 04:04:13 2019 From: webhook-mailer at python.org (Chris Withers) Date: Mon, 22 Jul 2019 08:04:13 -0000 Subject: [Python-checkins] bpo-21478: Record calls to parent when autospecced objects are used as child with attach_mock (GH 14688) (GH-14903) Message-ID: https://github.com/python/cpython/commit/e9b187a2bfbb0586fc5d554ce745b7fe04e0b9a8 commit: e9b187a2bfbb0586fc5d554ce745b7fe04e0b9a8 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2019-07-22T09:04:07+01:00 summary: bpo-21478: Record calls to parent when autospecced objects are used as child with attach_mock (GH 14688) (GH-14903) * Clear name and parent of mock in autospecced objects used with attach_mock * Add NEWS entry * Fix reversed order of comparison * Test child and standalone function calls * Use a helper function extracting mock to avoid code duplication and refactor tests. (cherry picked from commit 7397cda99795a4a8d96193d710105e77a07b7411) Co-authored-by: Xtreak files: A Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testmock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 569a5146c8c1..ee291fc89a10 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -62,6 +62,15 @@ def _is_exception(obj): ) +def _extract_mock(obj): + # Autospecced functions will return a FunctionType with "mock" attribute + # which is the actual mock object that needs to be used. + if isinstance(obj, FunctionTypes) and hasattr(obj, 'mock'): + return obj.mock + else: + return obj + + def _get_signature_object(func, as_instance, eat_self): """ Given an arbitrary, possibly callable object, try to create a suitable @@ -323,13 +332,7 @@ def __repr__(self): def _check_and_set_parent(parent, value, name, new_name): - # function passed to create_autospec will have mock - # attribute attached to which parent must be set - if isinstance(value, FunctionTypes): - try: - value = value.mock - except AttributeError: - pass + value = _extract_mock(value) if not _is_instance_mock(value): return False @@ -433,10 +436,12 @@ def attach_mock(self, mock, attribute): Attach a mock as an attribute of this one, replacing its name and parent. Calls to the attached mock will be recorded in the `method_calls` and `mock_calls` attributes of this one.""" - mock._mock_parent = None - mock._mock_new_parent = None - mock._mock_name = '' - mock._mock_new_name = None + inner_mock = _extract_mock(mock) + + inner_mock._mock_parent = None + inner_mock._mock_new_parent = None + inner_mock._mock_name = '' + inner_mock._mock_new_name = None setattr(self, attribute, mock) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index f92b921fe664..8329e5f50217 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -39,6 +39,9 @@ def smeth(a, b, c, d=None): pass +def something(a): pass + + class MockTest(unittest.TestCase): def test_all(self): @@ -1765,6 +1768,26 @@ def test_attach_mock_return_value(self): self.assertEqual(m.mock_calls, call().foo().call_list()) + def test_attach_mock_patch_autospec(self): + parent = Mock() + + with mock.patch(f'{__name__}.something', autospec=True) as mock_func: + self.assertEqual(mock_func.mock._extract_mock_name(), 'something') + parent.attach_mock(mock_func, 'child') + parent.child(1) + something(2) + mock_func(3) + + parent_calls = [call.child(1), call.child(2), call.child(3)] + child_calls = [call(1), call(2), call(3)] + self.assertEqual(parent.mock_calls, parent_calls) + self.assertEqual(parent.child.mock_calls, child_calls) + self.assertEqual(something.mock_calls, child_calls) + self.assertEqual(mock_func.mock_calls, child_calls) + self.assertIn('mock.child', repr(parent.child.mock)) + self.assertEqual(mock_func.mock._extract_mock_name(), 'mock.child') + + def test_attribute_deletion(self): for mock in (Mock(), MagicMock(), NonCallableMagicMock(), NonCallableMock()): @@ -1849,6 +1872,20 @@ def foo(a, b): self.assertRaises(TypeError, mock.child, 1) self.assertEqual(mock.mock_calls, [call.child(1, 2)]) + self.assertIn('mock.child', repr(mock.child.mock)) + + def test_parent_propagation_with_autospec_attach_mock(self): + + def foo(a, b): pass + + parent = Mock() + parent.attach_mock(create_autospec(foo, name='bar'), 'child') + parent.child(1, 2) + + self.assertRaises(TypeError, parent.child, 1) + self.assertEqual(parent.child.mock_calls, [call.child(1, 2)]) + self.assertIn('mock.child', repr(parent.child.mock)) + def test_isinstance_under_settrace(self): # bpo-36593 : __class__ is not set for a class that has __class__ diff --git a/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst b/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst new file mode 100644 index 000000000000..0ac9b8eadcab --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst @@ -0,0 +1,2 @@ +Record calls to parent when autospecced object is attached to a mock using +:func:`unittest.mock.attach_mock`. Patch by Karthikeyan Singaravelan. From webhook-mailer at python.org Mon Jul 22 07:14:57 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 22 Jul 2019 11:14:57 -0000 Subject: [Python-checkins] Update logging cookbook to show multiple worker processes using the concurrent.futures module. (#14905) Message-ID: https://github.com/python/cpython/commit/d309352c6fd93a51f2b3011ca8c2125d3a5d394b commit: d309352c6fd93a51f2b3011ca8c2125d3a5d394b branch: master author: Vinay Sajip committer: GitHub date: 2019-07-22T12:14:50+01:00 summary: Update logging cookbook to show multiple worker processes using the concurrent.futures module. (#14905) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 87ac79ef8072..e62308192d16 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -948,6 +948,41 @@ This variant shows how you can e.g. apply configuration for particular loggers machinery in the main process (even though the logging events are generated in the worker processes) to direct the messages to the appropriate destinations. +Using concurrent.futures.ProcessPoolExecutor +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you want to use :class:`concurrent.futures.ProcessPoolExecutor` to start +your worker processes, you need to create the queue slightly differently. +Instead of + +.. code-block:: python + + queue = multiprocessing.Queue(-1) + +you should use + +.. code-block:: python + + queue = multiprocessing.Manager().Queue(-1) # also works with the examples above + +and you can then replace the worker creation from this:: + + workers = [] + for i in range(10): + worker = multiprocessing.Process(target=worker_process, + args=(queue, worker_configurer)) + workers.append(worker) + worker.start() + for w in workers: + w.join() + +to this (remembering to first import :mod:`concurrent.futures`):: + + with concurrent.futures.ProcessPoolExecutor(max_workers=10) as executor: + for i in range(10): + executor.submit(worker_process, queue, worker_configurer) + + Using file rotation ------------------- From webhook-mailer at python.org Mon Jul 22 08:25:28 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 22 Jul 2019 12:25:28 -0000 Subject: [Python-checkins] Update logging cookbook to show multiple worker processes using the concurrent.futures module. (GH-14905) (GH-14907) Message-ID: https://github.com/python/cpython/commit/5d3d0f382f6663fcb8b426a62ef3a4cbd9a938d6 commit: 5d3d0f382f6663fcb8b426a62ef3a4cbd9a938d6 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-22T13:25:22+01:00 summary: Update logging cookbook to show multiple worker processes using the concurrent.futures module. (GH-14905) (GH-14907) (cherry picked from commit d309352c6fd93a51f2b3011ca8c2125d3a5d394b) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 105ae5ed25a0..a9222ab6ce38 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -949,6 +949,41 @@ This variant shows how you can e.g. apply configuration for particular loggers machinery in the main process (even though the logging events are generated in the worker processes) to direct the messages to the appropriate destinations. +Using concurrent.futures.ProcessPoolExecutor +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you want to use :class:`concurrent.futures.ProcessPoolExecutor` to start +your worker processes, you need to create the queue slightly differently. +Instead of + +.. code-block:: python + + queue = multiprocessing.Queue(-1) + +you should use + +.. code-block:: python + + queue = multiprocessing.Manager().Queue(-1) # also works with the examples above + +and you can then replace the worker creation from this:: + + workers = [] + for i in range(10): + worker = multiprocessing.Process(target=worker_process, + args=(queue, worker_configurer)) + workers.append(worker) + worker.start() + for w in workers: + w.join() + +to this (remembering to first import :mod:`concurrent.futures`):: + + with concurrent.futures.ProcessPoolExecutor(max_workers=10) as executor: + for i in range(10): + executor.submit(worker_process, queue, worker_configurer) + + Using file rotation ------------------- From webhook-mailer at python.org Mon Jul 22 08:25:50 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Mon, 22 Jul 2019 12:25:50 -0000 Subject: [Python-checkins] Update logging cookbook to show multiple worker processes using the concurrent.futures module. (GH-14905) (GH-14906) Message-ID: https://github.com/python/cpython/commit/5b398520a8ff397b5a7cc28851a582faaa5ee492 commit: 5b398520a8ff397b5a7cc28851a582faaa5ee492 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-22T13:25:46+01:00 summary: Update logging cookbook to show multiple worker processes using the concurrent.futures module. (GH-14905) (GH-14906) (cherry picked from commit d309352c6fd93a51f2b3011ca8c2125d3a5d394b) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 87ac79ef8072..e62308192d16 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -948,6 +948,41 @@ This variant shows how you can e.g. apply configuration for particular loggers machinery in the main process (even though the logging events are generated in the worker processes) to direct the messages to the appropriate destinations. +Using concurrent.futures.ProcessPoolExecutor +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you want to use :class:`concurrent.futures.ProcessPoolExecutor` to start +your worker processes, you need to create the queue slightly differently. +Instead of + +.. code-block:: python + + queue = multiprocessing.Queue(-1) + +you should use + +.. code-block:: python + + queue = multiprocessing.Manager().Queue(-1) # also works with the examples above + +and you can then replace the worker creation from this:: + + workers = [] + for i in range(10): + worker = multiprocessing.Process(target=worker_process, + args=(queue, worker_configurer)) + workers.append(worker) + worker.start() + for w in workers: + w.join() + +to this (remembering to first import :mod:`concurrent.futures`):: + + with concurrent.futures.ProcessPoolExecutor(max_workers=10) as executor: + for i in range(10): + executor.submit(worker_process, queue, worker_configurer) + + Using file rotation ------------------- From webhook-mailer at python.org Mon Jul 22 14:14:29 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Mon, 22 Jul 2019 18:14:29 -0000 Subject: [Python-checkins] [3.7] Fix typos in docs, comments and test assert messages (GH-14872). (#14901) Message-ID: https://github.com/python/cpython/commit/83cec020ba177fa27727330ba4ccf60eebc22a54 commit: 83cec020ba177fa27727330ba4ccf60eebc22a54 branch: 3.7 author: Kyle Stanley committer: Terry Jan Reedy date: 2019-07-22T14:14:07-04:00 summary: [3.7] Fix typos in docs, comments and test assert messages (GH-14872). (#14901) (cherry picked from commit 96e12d5f4f3c5a20986566038ee763dff3c228a1) Co-authored-by: Min ho Kim files: M Doc/library/ctypes.rst M Lib/collections/__init__.py M Lib/dataclasses.py M Lib/email/headerregistry.py M Lib/importlib/_bootstrap_external.py M Lib/test/support/__init__.py M Lib/test/test_dataclasses.py M Lib/test/test_eintr.py M Lib/test/test_random.py M Misc/NEWS.d/3.5.2rc1.rst M Misc/NEWS.d/3.6.0a1.rst M Misc/NEWS.d/3.6.3rc1.rst M Modules/_pickle.c diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 6cd4f9779f6d..2eafa654e2e6 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1175,7 +1175,7 @@ Keep in mind that retrieving sub-objects from Structure, Unions, and Arrays doesn't *copy* the sub-object, instead it retrieves a wrapper object accessing the root-object's underlying buffer. -Another example that may behave different from what one would expect is this:: +Another example that may behave differently from what one would expect is this:: >>> s = c_char_p() >>> s.value = b"abc def ghi" diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 64bbee8faba8..7ff3c80a06a7 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -444,7 +444,7 @@ def __getnewargs__(self): '__slots__': (), '_fields': field_names, '_field_defaults': field_defaults, - # alternate spelling for backward compatiblity + # alternate spelling for backward compatibility '_fields_defaults': field_defaults, '__new__': __new__, '_make': _make, diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 8c3d638e86bc..7725621e0fae 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1185,7 +1185,7 @@ class C(Base): raise TypeError(f'Invalid field: {item!r}') if not isinstance(name, str) or not name.isidentifier(): - raise TypeError(f'Field names must be valid identifers: {name!r}') + raise TypeError(f'Field names must be valid identifiers: {name!r}') if keyword.iskeyword(name): raise TypeError(f'Field names must not be keywords: {name!r}') if name in seen: diff --git a/Lib/email/headerregistry.py b/Lib/email/headerregistry.py index 00652049f2fa..0218cbfbd098 100644 --- a/Lib/email/headerregistry.py +++ b/Lib/email/headerregistry.py @@ -245,7 +245,7 @@ def fold(self, *, policy): the header name and the ': ' separator. """ - # At some point we need to put fws here iif it was in the source. + # At some point we need to put fws here if it was in the source. header = parser.Header([ parser.HeaderLabel([ parser.ValueTerminal(self.name, 'header-name'), diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 53b24ff1b0f2..66a16a683962 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -246,7 +246,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.7a2 3391 (update GET_AITER #31709) # Python 3.7a4 3392 (PEP 552: Deterministic pycs #31650) # Python 3.7b1 3393 (remove STORE_ANNOTATION opcode #32550) -# Python 3.7b5 3394 (restored docstring as the firts stmt in the body; +# Python 3.7b5 3394 (restored docstring as the first stmt in the body; # this might affected the first line number #32911) # # MAGIC must change whenever the bytecode emitted by the compiler may no diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 87bfa9f54627..6e40a143c274 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2806,7 +2806,7 @@ def fd_count(): if sys.platform.startswith(('linux', 'freebsd')): try: names = os.listdir("/proc/self/fd") - # Substract one because listdir() opens internally a file + # Subtract one because listdir() internally opens a file # descriptor to list the content of the /proc/self/fd/ directory. return len(names) - 1 except FileNotFoundError: diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index a2c7ceea9da2..f36098c69339 100755 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -3037,11 +3037,11 @@ def test_keyword_field_names(self): def test_non_identifier_field_names(self): for field in ['()', 'x,y', '*', '2 at 3', '', 'little johnny tables']: with self.subTest(field=field): - with self.assertRaisesRegex(TypeError, 'must be valid identifers'): + with self.assertRaisesRegex(TypeError, 'must be valid identifiers'): make_dataclass('C', ['a', field]) - with self.assertRaisesRegex(TypeError, 'must be valid identifers'): + with self.assertRaisesRegex(TypeError, 'must be valid identifiers'): make_dataclass('C', [field]) - with self.assertRaisesRegex(TypeError, 'must be valid identifers'): + with self.assertRaisesRegex(TypeError, 'must be valid identifiers'): make_dataclass('C', [field, 'a']) def test_underscore_field_names(self): diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index f61efa3c648e..a5f8f6465e88 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -22,7 +22,7 @@ def test_all(self): print() print("--- run eintr_tester.py ---", flush=True) # In verbose mode, the child process inherit stdout and stdout, - # to see output in realtime and reduce the risk of loosing output. + # to see output in realtime and reduce the risk of losing output. args = [sys.executable, "-E", "-X", "faulthandler", *args] proc = subprocess.run(args) print(f"--- eintr_tester.py completed: " diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index f0822cddec86..6b9e90594cb2 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -228,7 +228,7 @@ def test_choices(self): choices([], cum_weights=[], k=5) def test_choices_subnormal(self): - # Subnormal weights would occassionally trigger an IndexError + # Subnormal weights would occasionally trigger an IndexError # in choices() when the value returned by random() was large # enough to make `random() * total` round up to the total. # See https://bugs.python.org/msg275594 for more detail. diff --git a/Misc/NEWS.d/3.5.2rc1.rst b/Misc/NEWS.d/3.5.2rc1.rst index 231c3cac9c5f..40b9e803da29 100644 --- a/Misc/NEWS.d/3.5.2rc1.rst +++ b/Misc/NEWS.d/3.5.2rc1.rst @@ -2006,7 +2006,7 @@ Adds validation of ucrtbase[d].dll version with warning for old versions. .. nonce: 102DA- .. section: Build -Avoid error about nonexistant fileblocks.o file by using a lower-level check +Avoid error about nonexistent fileblocks.o file by using a lower-level check for st_blocks in struct stat. .. diff --git a/Misc/NEWS.d/3.6.0a1.rst b/Misc/NEWS.d/3.6.0a1.rst index 254d36166450..2df4c613a8e8 100644 --- a/Misc/NEWS.d/3.6.0a1.rst +++ b/Misc/NEWS.d/3.6.0a1.rst @@ -3695,7 +3695,7 @@ Adds validation of ucrtbase[d].dll version with warning for old versions. .. nonce: 102DA- .. section: Build -Avoid error about nonexistant fileblocks.o file by using a lower-level check +Avoid error about nonexistent fileblocks.o file by using a lower-level check for st_blocks in struct stat. .. diff --git a/Misc/NEWS.d/3.6.3rc1.rst b/Misc/NEWS.d/3.6.3rc1.rst index ca812c63eed9..29cff7eae77f 100644 --- a/Misc/NEWS.d/3.6.3rc1.rst +++ b/Misc/NEWS.d/3.6.3rc1.rst @@ -885,8 +885,7 @@ The affected events are '<>', '<>', '<>', '<>', and '<>'. Any (global) customizations made before 3.6.3 will not affect their keyset-specific customization after 3.6.3. and vice versa. - -Inital patch by Charles Wohlganger. +Initial patch by Charles Wohlganger. .. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 9a6207b519fa..dd45772595bc 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -1999,7 +1999,7 @@ save_long(PicklerObject *self, PyObject *obj) /* How many bytes do we need? There are nbits >> 3 full * bytes of data, and nbits & 7 leftover bits. If there * are any leftover bits, then we clearly need another - * byte. Wnat's not so obvious is that we *probably* + * byte. What's not so obvious is that we *probably* * need another byte even if there aren't any leftovers: * the most-significant bit of the most-significant byte * acts like a sign bit, and it's usually got a sense From webhook-mailer at python.org Mon Jul 22 15:54:32 2019 From: webhook-mailer at python.org (Neil Schemenauer) Date: Mon, 22 Jul 2019 19:54:32 -0000 Subject: [Python-checkins] bpo-36044: Reduce number of unit tests run for PGO build (GH-14702) Message-ID: https://github.com/python/cpython/commit/4e16a4a3112161a5c6981c0588142d4a4673a934 commit: 4e16a4a3112161a5c6981c0588142d4a4673a934 branch: master author: Neil Schemenauer committer: GitHub date: 2019-07-22T12:54:25-07:00 summary: bpo-36044: Reduce number of unit tests run for PGO build (GH-14702) Reduce the number of unit tests run for the PGO generation task. This speeds up the task by a factor of about 15x. Running the full unit test suite is slow. This change may result in a slightly less optimized build since not as many code branches will be executed. If you are willing to wait for the much slower build, the old behavior can be restored using './configure [..] PROFILE_TASK="-m test --pgo-extended"'. We make no guarantees as to which PGO task set produces a faster build. Users who care should run their own relevant benchmarks as results can depend on the environment, workload, and compiler tool chain. files: A Lib/test/libregrtest/pgo.py A Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst M Lib/test/libregrtest/cmdline.py M Lib/test/libregrtest/main.py M Makefile.pre.in M configure M configure.ac diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 9f1bf6800824..c8fedc7ad329 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -264,7 +264,9 @@ def _create_parser(): help='only write the name of test cases that will be run' ' , don\'t execute them') group.add_argument('-P', '--pgo', dest='pgo', action='store_true', - help='enable Profile Guided Optimization training') + help='enable Profile Guided Optimization (PGO) training') + group.add_argument('--pgo-extended', action='store_true', + help='enable extended PGO training (slower training)') group.add_argument('--fail-env-changed', action='store_true', help='if a test file alters the environment, mark ' 'the test as failed') @@ -344,6 +346,8 @@ def _parse_args(args, **kwargs): parser.error("-G/--failfast needs either -v or -W") if ns.pgo and (ns.verbose or ns.verbose2 or ns.verbose3): parser.error("--pgo/-v don't go together!") + if ns.pgo_extended: + ns.pgo = True # pgo_extended implies pgo if ns.nowindows: print("Warning: the --nowindows (-n) option is deprecated. " diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index e2274254fdb8..78b6790685c9 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -17,6 +17,7 @@ INTERRUPTED, CHILD_ERROR, TEST_DID_NOT_RUN, PROGRESS_MIN_TIME, format_test_result, is_failed) from test.libregrtest.setup import setup_tests +from test.libregrtest.pgo import setup_pgo_tests from test.libregrtest.utils import removepy, count, format_duration, printlist from test import support @@ -214,6 +215,9 @@ def find_tests(self, tests): removepy(self.tests) + # add default PGO tests if no tests are specified + setup_pgo_tests(self.ns) + stdtests = STDTESTS[:] nottests = NOTTESTS.copy() if self.ns.exclude: diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py new file mode 100644 index 000000000000..327f19374c3f --- /dev/null +++ b/Lib/test/libregrtest/pgo.py @@ -0,0 +1,52 @@ +# Set of tests run by default if --pgo is specified. The tests below were +# chosen based on the following criteria: either they exercise a commonly used +# C extension module or type, or they run some relatively typical Python code. +# Long running tests should be avoided because the PGO instrumented executable +# runs slowly. +PGO_TESTS = [ + 'test_array', + 'test_base64', + 'test_binascii', + 'test_binop', + 'test_bisect', + 'test_bytes', + 'test_cmath', + 'test_codecs', + 'test_collections', + 'test_complex', + 'test_dataclasses', + 'test_datetime', + 'test_decimal', + 'test_difflib', + 'test_embed', + 'test_float', + 'test_fstring', + 'test_functools', + 'test_generators', + 'test_hashlib', + 'test_heapq', + 'test_int', + 'test_itertools', + 'test_json', + 'test_long', + 'test_math', + 'test_memoryview', + 'test_operator', + 'test_ordered_dict', + 'test_pickle', + 'test_pprint', + 'test_re', + 'test_set', + 'test_statistics', + 'test_struct', + 'test_tabnanny', + 'test_time', + 'test_unicode', + 'test_xml_etree', + 'test_xml_etree_c', +] + +def setup_pgo_tests(ns): + if not ns.args and not ns.pgo_extended: + # run default set of tests for PGO training + ns.args = PGO_TESTS[:] diff --git a/Makefile.pre.in b/Makefile.pre.in index 94a1208efa09..dd267b483a29 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -255,9 +255,10 @@ TCLTK_INCLUDES= @TCLTK_INCLUDES@ TCLTK_LIBS= @TCLTK_LIBS@ # The task to run while instrumented when building the profile-opt target. -# We exclude unittests with -x that take a rediculious amount of time to -# run in the instrumented training build or do not provide much value. -PROFILE_TASK=-m test.regrtest --pgo +# To speed up profile generation, we don't run the full unit test suite +# by default. The default is "-m test --pgo". To run more tests, use +# PROFILE_TASK="-m test --pgo-extended" +PROFILE_TASK= @PROFILE_TASK@ # report files for gcov / lcov coverage report COVERAGE_INFO= $(abs_builddir)/coverage.info diff --git a/Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst b/Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst new file mode 100644 index 000000000000..177c4cb6d17c --- /dev/null +++ b/Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst @@ -0,0 +1,9 @@ +Reduce the number of unit tests run for the PGO generation task. This +speeds up the task by a factor of about 15x. Running the full unit test +suite is slow. This change may result in a slightly less optimized build +since not as many code branches will be executed. If you are willing to +wait for the much slower build, the old behavior can be restored using +'./configure [..] PROFILE_TASK="-m test --pgo-extended"'. We make no +guarantees as to which PGO task set produces a faster build. Users who +care should run their own relevant benchmarks as results can depend on +the environment, workload, and compiler tool chain. diff --git a/configure b/configure index 2c49d853a384..4cea98e36528 100755 --- a/configure +++ b/configure @@ -686,6 +686,7 @@ target_vendor target_cpu target LLVM_AR +PROFILE_TASK DEF_MAKE_RULE DEF_MAKE_ALL_RULE ABIFLAGS @@ -856,6 +857,7 @@ LDFLAGS LIBS CPPFLAGS CPP +PROFILE_TASK PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR' @@ -1559,6 +1561,8 @@ Some influential environment variables: CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor + PROFILE_TASK + Python args for PGO generation task PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path @@ -6426,6 +6430,16 @@ else DEF_MAKE_RULE="all" fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking PROFILE_TASK" >&5 +$as_echo_n "checking PROFILE_TASK... " >&6; } +if test -z "$PROFILE_TASK" +then + PROFILE_TASK='-m test --pgo' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROFILE_TASK" >&5 +$as_echo "$PROFILE_TASK" >&6; } + # Make llvm-relatec checks work on systems where llvm tools are not installed with their # normal names in the default $PATH (ie: Ubuntu). They exist under the # non-suffixed name in their versioned llvm directory. diff --git a/configure.ac b/configure.ac index cc31df866d52..b9759e12f89f 100644 --- a/configure.ac +++ b/configure.ac @@ -1293,6 +1293,14 @@ else DEF_MAKE_RULE="all" fi +AC_ARG_VAR(PROFILE_TASK, Python args for PGO generation task) +AC_MSG_CHECKING(PROFILE_TASK) +if test -z "$PROFILE_TASK" +then + PROFILE_TASK='-m test --pgo' +fi +AC_MSG_RESULT($PROFILE_TASK) + # Make llvm-relatec checks work on systems where llvm tools are not installed with their # normal names in the default $PATH (ie: Ubuntu). They exist under the # non-suffixed name in their versioned llvm directory. From webhook-mailer at python.org Mon Jul 22 16:17:27 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 22 Jul 2019 20:17:27 -0000 Subject: [Python-checkins] bpo-36044: Reduce number of unit tests run for PGO build (GH-14702) Message-ID: https://github.com/python/cpython/commit/2406672984e4c1b18629e615edad52928a72ffcc commit: 2406672984e4c1b18629e615edad52928a72ffcc branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-22T13:17:23-07:00 summary: bpo-36044: Reduce number of unit tests run for PGO build (GH-14702) Reduce the number of unit tests run for the PGO generation task. This speeds up the task by a factor of about 15x. Running the full unit test suite is slow. This change may result in a slightly less optimized build since not as many code branches will be executed. If you are willing to wait for the much slower build, the old behavior can be restored using './configure [..] PROFILE_TASK="-m test --pgo-extended"'. We make no guarantees as to which PGO task set produces a faster build. Users who care should run their own relevant benchmarks as results can depend on the environment, workload, and compiler tool chain. (cherry picked from commit 4e16a4a3112161a5c6981c0588142d4a4673a934) Co-authored-by: Neil Schemenauer files: A Lib/test/libregrtest/pgo.py A Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst M Lib/test/libregrtest/cmdline.py M Lib/test/libregrtest/main.py M Makefile.pre.in M configure M configure.ac diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 9f1bf6800824..c8fedc7ad329 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -264,7 +264,9 @@ def _create_parser(): help='only write the name of test cases that will be run' ' , don\'t execute them') group.add_argument('-P', '--pgo', dest='pgo', action='store_true', - help='enable Profile Guided Optimization training') + help='enable Profile Guided Optimization (PGO) training') + group.add_argument('--pgo-extended', action='store_true', + help='enable extended PGO training (slower training)') group.add_argument('--fail-env-changed', action='store_true', help='if a test file alters the environment, mark ' 'the test as failed') @@ -344,6 +346,8 @@ def _parse_args(args, **kwargs): parser.error("-G/--failfast needs either -v or -W") if ns.pgo and (ns.verbose or ns.verbose2 or ns.verbose3): parser.error("--pgo/-v don't go together!") + if ns.pgo_extended: + ns.pgo = True # pgo_extended implies pgo if ns.nowindows: print("Warning: the --nowindows (-n) option is deprecated. " diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index e2274254fdb8..78b6790685c9 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -17,6 +17,7 @@ INTERRUPTED, CHILD_ERROR, TEST_DID_NOT_RUN, PROGRESS_MIN_TIME, format_test_result, is_failed) from test.libregrtest.setup import setup_tests +from test.libregrtest.pgo import setup_pgo_tests from test.libregrtest.utils import removepy, count, format_duration, printlist from test import support @@ -214,6 +215,9 @@ def find_tests(self, tests): removepy(self.tests) + # add default PGO tests if no tests are specified + setup_pgo_tests(self.ns) + stdtests = STDTESTS[:] nottests = NOTTESTS.copy() if self.ns.exclude: diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py new file mode 100644 index 000000000000..327f19374c3f --- /dev/null +++ b/Lib/test/libregrtest/pgo.py @@ -0,0 +1,52 @@ +# Set of tests run by default if --pgo is specified. The tests below were +# chosen based on the following criteria: either they exercise a commonly used +# C extension module or type, or they run some relatively typical Python code. +# Long running tests should be avoided because the PGO instrumented executable +# runs slowly. +PGO_TESTS = [ + 'test_array', + 'test_base64', + 'test_binascii', + 'test_binop', + 'test_bisect', + 'test_bytes', + 'test_cmath', + 'test_codecs', + 'test_collections', + 'test_complex', + 'test_dataclasses', + 'test_datetime', + 'test_decimal', + 'test_difflib', + 'test_embed', + 'test_float', + 'test_fstring', + 'test_functools', + 'test_generators', + 'test_hashlib', + 'test_heapq', + 'test_int', + 'test_itertools', + 'test_json', + 'test_long', + 'test_math', + 'test_memoryview', + 'test_operator', + 'test_ordered_dict', + 'test_pickle', + 'test_pprint', + 'test_re', + 'test_set', + 'test_statistics', + 'test_struct', + 'test_tabnanny', + 'test_time', + 'test_unicode', + 'test_xml_etree', + 'test_xml_etree_c', +] + +def setup_pgo_tests(ns): + if not ns.args and not ns.pgo_extended: + # run default set of tests for PGO training + ns.args = PGO_TESTS[:] diff --git a/Makefile.pre.in b/Makefile.pre.in index 88abb563600d..6a9f4b52704d 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -255,9 +255,10 @@ TCLTK_INCLUDES= @TCLTK_INCLUDES@ TCLTK_LIBS= @TCLTK_LIBS@ # The task to run while instrumented when building the profile-opt target. -# We exclude unittests with -x that take a rediculious amount of time to -# run in the instrumented training build or do not provide much value. -PROFILE_TASK=-m test.regrtest --pgo +# To speed up profile generation, we don't run the full unit test suite +# by default. The default is "-m test --pgo". To run more tests, use +# PROFILE_TASK="-m test --pgo-extended" +PROFILE_TASK= @PROFILE_TASK@ # report files for gcov / lcov coverage report COVERAGE_INFO= $(abs_builddir)/coverage.info diff --git a/Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst b/Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst new file mode 100644 index 000000000000..177c4cb6d17c --- /dev/null +++ b/Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst @@ -0,0 +1,9 @@ +Reduce the number of unit tests run for the PGO generation task. This +speeds up the task by a factor of about 15x. Running the full unit test +suite is slow. This change may result in a slightly less optimized build +since not as many code branches will be executed. If you are willing to +wait for the much slower build, the old behavior can be restored using +'./configure [..] PROFILE_TASK="-m test --pgo-extended"'. We make no +guarantees as to which PGO task set produces a faster build. Users who +care should run their own relevant benchmarks as results can depend on +the environment, workload, and compiler tool chain. diff --git a/configure b/configure index 6e7f277bace9..cb5f130d38e0 100755 --- a/configure +++ b/configure @@ -686,6 +686,7 @@ target_vendor target_cpu target LLVM_AR +PROFILE_TASK DEF_MAKE_RULE DEF_MAKE_ALL_RULE ABIFLAGS @@ -856,6 +857,7 @@ LDFLAGS LIBS CPPFLAGS CPP +PROFILE_TASK PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR' @@ -1559,6 +1561,8 @@ Some influential environment variables: CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor + PROFILE_TASK + Python args for PGO generation task PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path @@ -6426,6 +6430,16 @@ else DEF_MAKE_RULE="all" fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking PROFILE_TASK" >&5 +$as_echo_n "checking PROFILE_TASK... " >&6; } +if test -z "$PROFILE_TASK" +then + PROFILE_TASK='-m test --pgo' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROFILE_TASK" >&5 +$as_echo "$PROFILE_TASK" >&6; } + # Make llvm-relatec checks work on systems where llvm tools are not installed with their # normal names in the default $PATH (ie: Ubuntu). They exist under the # non-suffixed name in their versioned llvm directory. diff --git a/configure.ac b/configure.ac index 324ce0bd99a9..b31ed242f1a8 100644 --- a/configure.ac +++ b/configure.ac @@ -1293,6 +1293,14 @@ else DEF_MAKE_RULE="all" fi +AC_ARG_VAR(PROFILE_TASK, Python args for PGO generation task) +AC_MSG_CHECKING(PROFILE_TASK) +if test -z "$PROFILE_TASK" +then + PROFILE_TASK='-m test --pgo' +fi +AC_MSG_RESULT($PROFILE_TASK) + # Make llvm-relatec checks work on systems where llvm tools are not installed with their # normal names in the default $PATH (ie: Ubuntu). They exist under the # non-suffixed name in their versioned llvm directory. From webhook-mailer at python.org Tue Jul 23 06:02:23 2019 From: webhook-mailer at python.org (Tal Einat) Date: Tue, 23 Jul 2019 10:02:23 -0000 Subject: [Python-checkins] bpo-33610: validate non-negative integer inputs in IDLE's config (GH-14822) Message-ID: https://github.com/python/cpython/commit/1ebee37dde5c2aabc8e2d2c7bbe2a69b293133bb commit: 1ebee37dde5c2aabc8e2d2c7bbe2a69b293133bb branch: master author: Tal Einat committer: GitHub date: 2019-07-23T13:02:11+03:00 summary: bpo-33610: validate non-negative integer inputs in IDLE's config (GH-14822) files: M Lib/idlelib/configdialog.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 6ddbc7fc8e4d..6b0ecc9d68e5 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -9,6 +9,8 @@ Refer to comments in EditorWindow autoindent code for details. """ +import re + from tkinter import (Toplevel, Listbox, Text, Scale, Canvas, StringVar, BooleanVar, IntVar, TRUE, FALSE, TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE, @@ -1764,9 +1766,18 @@ class GenPage(Frame): def __init__(self, master): super().__init__(master) + + self.init_validators() self.create_page_general() self.load_general_cfg() + def init_validators(self): + digits_or_empty_re = re.compile(r'[0-9]*') + def is_digits_or_empty(s): + "Return 's is blank or contains only digits'" + return digits_or_empty_re.fullmatch(s) is not None + self.digits_only = (self.register(is_digits_or_empty), '%P',) + def create_page_general(self): """Return frame of widgets for General tab. @@ -1883,16 +1894,23 @@ def create_page_general(self): frame_win_size, text='Initial Window Size (in characters)') win_width_title = Label(frame_win_size, text='Width') self.win_width_int = Entry( - frame_win_size, textvariable=self.win_width, width=3) + frame_win_size, textvariable=self.win_width, width=3, + validatecommand=self.digits_only, validate='key', + ) win_height_title = Label(frame_win_size, text='Height') self.win_height_int = Entry( - frame_win_size, textvariable=self.win_height, width=3) + frame_win_size, textvariable=self.win_height, width=3, + validatecommand=self.digits_only, validate='key', + ) frame_autocomplete = Frame(frame_window, borderwidth=0,) auto_wait_title = Label(frame_autocomplete, text='Completions Popup Wait (milliseconds)') self.auto_wait_int = Entry(frame_autocomplete, width=6, - textvariable=self.autocomplete_wait) + textvariable=self.autocomplete_wait, + validatecommand=self.digits_only, + validate='key', + ) frame_paren1 = Frame(frame_window, borderwidth=0) paren_style_title = Label(frame_paren1, text='Paren Match Style') @@ -1922,20 +1940,26 @@ def create_page_general(self): format_width_title = Label(frame_format, text='Format Paragraph Max Width') self.format_width_int = Entry( - frame_format, textvariable=self.format_width, width=4) + frame_format, textvariable=self.format_width, width=4, + validatecommand=self.digits_only, validate='key', + ) frame_context = Frame(frame_editor, borderwidth=0) context_title = Label(frame_context, text='Max Context Lines :') self.context_int = Entry( - frame_context, textvariable=self.context_lines, width=3) + frame_context, textvariable=self.context_lines, width=3, + validatecommand=self.digits_only, validate='key', + ) # Frame_shell. frame_auto_squeeze_min_lines = Frame(frame_shell, borderwidth=0) auto_squeeze_min_lines_title = Label(frame_auto_squeeze_min_lines, text='Auto-Squeeze Min. Lines:') self.auto_squeeze_min_lines_int = Entry( - frame_auto_squeeze_min_lines, width=4, - textvariable=self.auto_squeeze_min_lines) + frame_auto_squeeze_min_lines, width=4, + textvariable=self.auto_squeeze_min_lines, + validatecommand=self.digits_only, validate='key', + ) # frame_help. frame_helplist = Frame(frame_help) From webhook-mailer at python.org Tue Jul 23 06:18:48 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 23 Jul 2019 10:18:48 -0000 Subject: [Python-checkins] bpo-33610: validate non-negative integer inputs in IDLE's config (GH-14822) Message-ID: https://github.com/python/cpython/commit/5dab5e7d24c790d54b8d1eca0568e798bfda2c68 commit: 5dab5e7d24c790d54b8d1eca0568e798bfda2c68 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-23T03:18:41-07:00 summary: bpo-33610: validate non-negative integer inputs in IDLE's config (GH-14822) (cherry picked from commit 1ebee37dde5c2aabc8e2d2c7bbe2a69b293133bb) Co-authored-by: Tal Einat files: M Lib/idlelib/configdialog.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 6ddbc7fc8e4d..6b0ecc9d68e5 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -9,6 +9,8 @@ Refer to comments in EditorWindow autoindent code for details. """ +import re + from tkinter import (Toplevel, Listbox, Text, Scale, Canvas, StringVar, BooleanVar, IntVar, TRUE, FALSE, TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE, @@ -1764,9 +1766,18 @@ class GenPage(Frame): def __init__(self, master): super().__init__(master) + + self.init_validators() self.create_page_general() self.load_general_cfg() + def init_validators(self): + digits_or_empty_re = re.compile(r'[0-9]*') + def is_digits_or_empty(s): + "Return 's is blank or contains only digits'" + return digits_or_empty_re.fullmatch(s) is not None + self.digits_only = (self.register(is_digits_or_empty), '%P',) + def create_page_general(self): """Return frame of widgets for General tab. @@ -1883,16 +1894,23 @@ def create_page_general(self): frame_win_size, text='Initial Window Size (in characters)') win_width_title = Label(frame_win_size, text='Width') self.win_width_int = Entry( - frame_win_size, textvariable=self.win_width, width=3) + frame_win_size, textvariable=self.win_width, width=3, + validatecommand=self.digits_only, validate='key', + ) win_height_title = Label(frame_win_size, text='Height') self.win_height_int = Entry( - frame_win_size, textvariable=self.win_height, width=3) + frame_win_size, textvariable=self.win_height, width=3, + validatecommand=self.digits_only, validate='key', + ) frame_autocomplete = Frame(frame_window, borderwidth=0,) auto_wait_title = Label(frame_autocomplete, text='Completions Popup Wait (milliseconds)') self.auto_wait_int = Entry(frame_autocomplete, width=6, - textvariable=self.autocomplete_wait) + textvariable=self.autocomplete_wait, + validatecommand=self.digits_only, + validate='key', + ) frame_paren1 = Frame(frame_window, borderwidth=0) paren_style_title = Label(frame_paren1, text='Paren Match Style') @@ -1922,20 +1940,26 @@ def create_page_general(self): format_width_title = Label(frame_format, text='Format Paragraph Max Width') self.format_width_int = Entry( - frame_format, textvariable=self.format_width, width=4) + frame_format, textvariable=self.format_width, width=4, + validatecommand=self.digits_only, validate='key', + ) frame_context = Frame(frame_editor, borderwidth=0) context_title = Label(frame_context, text='Max Context Lines :') self.context_int = Entry( - frame_context, textvariable=self.context_lines, width=3) + frame_context, textvariable=self.context_lines, width=3, + validatecommand=self.digits_only, validate='key', + ) # Frame_shell. frame_auto_squeeze_min_lines = Frame(frame_shell, borderwidth=0) auto_squeeze_min_lines_title = Label(frame_auto_squeeze_min_lines, text='Auto-Squeeze Min. Lines:') self.auto_squeeze_min_lines_int = Entry( - frame_auto_squeeze_min_lines, width=4, - textvariable=self.auto_squeeze_min_lines) + frame_auto_squeeze_min_lines, width=4, + textvariable=self.auto_squeeze_min_lines, + validatecommand=self.digits_only, validate='key', + ) # frame_help. frame_helplist = Frame(frame_help) From webhook-mailer at python.org Tue Jul 23 06:21:56 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 23 Jul 2019 10:21:56 -0000 Subject: [Python-checkins] bpo-33610: validate non-negative integer inputs in IDLE's config (GH-14822) Message-ID: https://github.com/python/cpython/commit/28815e0e2c904644660d29995cdfbb65f67e4a1d commit: 28815e0e2c904644660d29995cdfbb65f67e4a1d branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-23T03:21:52-07:00 summary: bpo-33610: validate non-negative integer inputs in IDLE's config (GH-14822) (cherry picked from commit 1ebee37dde5c2aabc8e2d2c7bbe2a69b293133bb) Co-authored-by: Tal Einat files: M Lib/idlelib/configdialog.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 6ddbc7fc8e4d..6b0ecc9d68e5 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -9,6 +9,8 @@ Refer to comments in EditorWindow autoindent code for details. """ +import re + from tkinter import (Toplevel, Listbox, Text, Scale, Canvas, StringVar, BooleanVar, IntVar, TRUE, FALSE, TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE, @@ -1764,9 +1766,18 @@ class GenPage(Frame): def __init__(self, master): super().__init__(master) + + self.init_validators() self.create_page_general() self.load_general_cfg() + def init_validators(self): + digits_or_empty_re = re.compile(r'[0-9]*') + def is_digits_or_empty(s): + "Return 's is blank or contains only digits'" + return digits_or_empty_re.fullmatch(s) is not None + self.digits_only = (self.register(is_digits_or_empty), '%P',) + def create_page_general(self): """Return frame of widgets for General tab. @@ -1883,16 +1894,23 @@ def create_page_general(self): frame_win_size, text='Initial Window Size (in characters)') win_width_title = Label(frame_win_size, text='Width') self.win_width_int = Entry( - frame_win_size, textvariable=self.win_width, width=3) + frame_win_size, textvariable=self.win_width, width=3, + validatecommand=self.digits_only, validate='key', + ) win_height_title = Label(frame_win_size, text='Height') self.win_height_int = Entry( - frame_win_size, textvariable=self.win_height, width=3) + frame_win_size, textvariable=self.win_height, width=3, + validatecommand=self.digits_only, validate='key', + ) frame_autocomplete = Frame(frame_window, borderwidth=0,) auto_wait_title = Label(frame_autocomplete, text='Completions Popup Wait (milliseconds)') self.auto_wait_int = Entry(frame_autocomplete, width=6, - textvariable=self.autocomplete_wait) + textvariable=self.autocomplete_wait, + validatecommand=self.digits_only, + validate='key', + ) frame_paren1 = Frame(frame_window, borderwidth=0) paren_style_title = Label(frame_paren1, text='Paren Match Style') @@ -1922,20 +1940,26 @@ def create_page_general(self): format_width_title = Label(frame_format, text='Format Paragraph Max Width') self.format_width_int = Entry( - frame_format, textvariable=self.format_width, width=4) + frame_format, textvariable=self.format_width, width=4, + validatecommand=self.digits_only, validate='key', + ) frame_context = Frame(frame_editor, borderwidth=0) context_title = Label(frame_context, text='Max Context Lines :') self.context_int = Entry( - frame_context, textvariable=self.context_lines, width=3) + frame_context, textvariable=self.context_lines, width=3, + validatecommand=self.digits_only, validate='key', + ) # Frame_shell. frame_auto_squeeze_min_lines = Frame(frame_shell, borderwidth=0) auto_squeeze_min_lines_title = Label(frame_auto_squeeze_min_lines, text='Auto-Squeeze Min. Lines:') self.auto_squeeze_min_lines_int = Entry( - frame_auto_squeeze_min_lines, width=4, - textvariable=self.auto_squeeze_min_lines) + frame_auto_squeeze_min_lines, width=4, + textvariable=self.auto_squeeze_min_lines, + validatecommand=self.digits_only, validate='key', + ) # frame_help. frame_helplist = Frame(frame_help) From webhook-mailer at python.org Tue Jul 23 08:22:29 2019 From: webhook-mailer at python.org (Tal Einat) Date: Tue, 23 Jul 2019 12:22:29 -0000 Subject: [Python-checkins] bpo-17535: IDLE editor line numbers (GH-14030) Message-ID: https://github.com/python/cpython/commit/7123ea009b0b004062d91f69859bddf422c34ab4 commit: 7123ea009b0b004062d91f69859bddf422c34ab4 branch: master author: Tal Einat committer: GitHub date: 2019-07-23T15:22:11+03:00 summary: bpo-17535: IDLE editor line numbers (GH-14030) files: A Lib/idlelib/idle_test/test_sidebar.py A Lib/idlelib/sidebar.py A Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst M Doc/library/idle.rst M Doc/whatsnew/3.7.rst M Doc/whatsnew/3.8.rst M Lib/idlelib/codecontext.py M Lib/idlelib/config-highlight.def M Lib/idlelib/config-main.def M Lib/idlelib/config.py M Lib/idlelib/configdialog.py M Lib/idlelib/editor.py M Lib/idlelib/help.html M Lib/idlelib/idle_test/htest.py M Lib/idlelib/idle_test/test_codecontext.py M Lib/idlelib/mainmenu.py M Lib/idlelib/outwin.py M Lib/idlelib/pyshell.py diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index de58f266bf5e..5975e3bb5806 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -290,22 +290,31 @@ Options menu (Shell and Editor) Configure IDLE Open a configuration dialog and change preferences for the following: fonts, indentation, keybindings, text color themes, startup windows and - size, additional help sources, and extensions. On macOS, open the + size, additional help sources, and extensions. On macOS, open the configuration dialog by selecting Preferences in the application - menu. For more, see + menu. For more details, see :ref:`Setting preferences ` under Help and preferences. + Most configuration options apply to all windows or all future windows. + The option items below only apply to the active window. + Show/Hide Code Context (Editor Window only) Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window. See - :ref:`Code Context ` in the Editing and Navigation section below. + :ref:`Code Context ` in the Editing and Navigation section + below. + +Show/Hide Line Numbers (Editor Window only) + Open a column to the left of the edit window which shows the number + of each line of text. The default is off, which may be changed in the + preferences (see :ref:`Setting preferences `). Zoom/Restore Height Toggles the window between normal size and maximum height. The initial size defaults to 40 lines by 80 chars unless changed on the General tab of the Configure IDLE dialog. The maximum height for a screen is determined by momentarily maximizing a window the first time one is zoomed on the screen. - Changing screen settings may invalidate the saved height. This toogle has + Changing screen settings may invalidate the saved height. This toggle has no effect when a window is maximized. Window menu (Shell and Editor) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index fc867ac15a5f..46834ec42d9c 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1017,6 +1017,13 @@ by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.) The changes above have been backported to 3.6 maintenance releases. +New in 3.7.5: + +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. +(Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) + importlib --------- diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 95f25cd1cc52..3f84e092827b 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -515,6 +515,11 @@ for certain types of invalid or corrupt gzip files. idlelib and IDLE ---------------- +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. +(Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) + Output over N lines (50 by default) is squeezed down to a button. N can be changed in the PyShell section of the General page of the Settings dialog. Fewer, but possibly extra long, lines can be squeezed by diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 3103391cfe58..4ce98136fe41 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -13,7 +13,7 @@ from sys import maxsize as INFINITY import tkinter -from tkinter.constants import TOP, X, SUNKEN +from tkinter.constants import NSEW, SUNKEN from idlelib.config import idleConf @@ -67,6 +67,7 @@ def __init__(self, editwin): def _reset(self): self.context = None + self.cell00 = None self.t1 = None self.topvisible = 1 self.info = [(0, -1, "", False)] @@ -105,25 +106,37 @@ def toggle_code_context_event(self, event=None): padx = 0 border = 0 for widget in widgets: - padx += widget.tk.getint(widget.pack_info()['padx']) + info = (widget.grid_info() + if widget is self.editwin.text + else widget.pack_info()) + padx += widget.tk.getint(info['padx']) padx += widget.tk.getint(widget.cget('padx')) border += widget.tk.getint(widget.cget('border')) self.context = tkinter.Text( - self.editwin.top, font=self.text['font'], + self.editwin.text_frame, height=1, width=1, # Don't request more than we get. + highlightthickness=0, padx=padx, border=border, relief=SUNKEN, state='disabled') + self.update_font() self.update_highlight_colors() self.context.bind('', self.jumptoline) # Get the current context and initiate the recurring update event. self.timer_event() - # Pack the context widget before and above the text_frame widget, - # thus ensuring that it will appear directly above text_frame. - self.context.pack(side=TOP, fill=X, expand=False, - before=self.editwin.text_frame) + # Grid the context widget above the text widget. + self.context.grid(row=0, column=1, sticky=NSEW) + + line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(), + 'linenumber') + self.cell00 = tkinter.Frame(self.editwin.text_frame, + bg=line_number_colors['background']) + self.cell00.grid(row=0, column=0, sticky=NSEW) menu_status = 'Hide' else: self.context.destroy() + self.context = None + self.cell00.destroy() + self.cell00 = None self.text.after_cancel(self.t1) self._reset() menu_status = 'Show' @@ -221,8 +234,9 @@ def timer_event(self): self.update_code_context() self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) - def update_font(self, font): + def update_font(self): if self.context is not None: + font = idleConf.GetFont(self.text, 'main', 'EditorWindow') self.context['font'] = font def update_highlight_colors(self): @@ -231,6 +245,11 @@ def update_highlight_colors(self): self.context['background'] = colors['background'] self.context['foreground'] = colors['foreground'] + if self.cell00 is not None: + line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(), + 'linenumber') + self.cell00.config(bg=line_number_colors['background']) + CodeContext.reload() diff --git a/Lib/idlelib/config-highlight.def b/Lib/idlelib/config-highlight.def index aaa2b57a7f6d..a7b0433831c5 100644 --- a/Lib/idlelib/config-highlight.def +++ b/Lib/idlelib/config-highlight.def @@ -22,6 +22,10 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 +context-foreground= #000000 +context-background= lightgray +linenumber-foreground= gray +linenumber-background= #ffffff #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -31,8 +35,6 @@ stderr-foreground= red stderr-background= #ffffff console-foreground= #770000 console-background= #ffffff -context-foreground= #000000 -context-background= lightgray [IDLE New] normal-foreground= #000000 @@ -55,6 +57,10 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 +context-foreground= #000000 +context-background= lightgray +linenumber-foreground= gray +linenumber-background= #ffffff #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -64,8 +70,6 @@ stderr-foreground= red stderr-background= #ffffff console-foreground= #770000 console-background= #ffffff -context-foreground= #000000 -context-background= lightgray [IDLE Dark] comment-foreground = #dd0000 @@ -97,3 +101,5 @@ comment-background = #002240 break-foreground = #FFFFFF context-foreground= #ffffff context-background= #454545 +linenumber-foreground= gray +linenumber-background= #002240 diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 06e3c5adb0e3..b2be6250d021 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -36,7 +36,7 @@ # Additional help sources are listed in the [HelpFiles] section below # and should be viewable by a web browser (or the Windows Help viewer in # the case of .chm files). These sources will be listed on the Help -# menu. The pattern, and two examples, are +# menu. The pattern, and two examples, are: # # # 1 = IDLE;C:/Programs/Python36/Lib/idlelib/help.html @@ -46,7 +46,7 @@ # platform specific because of path separators, drive specs etc. # # The default files should not be edited except to add new sections to -# config-extensions.def for added extensions . The user files should be +# config-extensions.def for added extensions. The user files should be # modified through the Settings dialog. [General] @@ -65,6 +65,7 @@ font= TkFixedFont font-size= 10 font-bold= 0 encoding= none +line-numbers-default= 0 [PyShell] auto-squeeze-min-lines= 50 diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 0c55c9a7d75d..683b000a488b 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -319,6 +319,10 @@ def GetThemeDict(self, type, themeName): 'hit-background':'#000000', 'error-foreground':'#ffffff', 'error-background':'#000000', + 'context-foreground':'#000000', + 'context-background':'#ffffff', + 'linenumber-foreground':'#000000', + 'linenumber-background':'#ffffff', #cursor (only foreground can be set) 'cursor-foreground':'#000000', #shell window @@ -328,11 +332,11 @@ def GetThemeDict(self, type, themeName): 'stderr-background':'#ffffff', 'console-foreground':'#000000', 'console-background':'#ffffff', - 'context-foreground':'#000000', - 'context-background':'#ffffff', } for element in theme: - if not cfgParser.has_option(themeName, element): + if not (cfgParser.has_option(themeName, element) or + # Skip warning for new elements. + element.startswith(('context-', 'linenumber-'))): # Print warning that will return a default color warning = ('\n Warning: config.IdleConf.GetThemeDict' ' -\n problem retrieving theme element %r' diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 6b0ecc9d68e5..217f8fd0a5fb 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -819,6 +819,7 @@ def create_page_highlight(self): 'Shell Error Text': ('error', '12'), 'Shell Stdout Text': ('stdout', '13'), 'Shell Stderr Text': ('stderr', '14'), + 'Line Number': ('linenumber', '16'), } self.builtin_name = tracers.add( StringVar(self), self.var_changed_builtin_name) @@ -866,6 +867,11 @@ def create_page_highlight(self): ('stderr', 'stderr'), ('\n\n', 'normal')) for texttag in text_and_tags: text.insert(END, texttag[0], texttag[1]) + n_lines = len(text.get('1.0', END).splitlines()) + for lineno in range(1, n_lines + 1): + text.insert(f'{lineno}.0', + f'{lineno:{len(str(n_lines))}d} ', + 'linenumber') for element in self.theme_elements: def tem(event, elem=element): # event.widget.winfo_top_level().highlight_target.set(elem) @@ -1827,6 +1833,9 @@ def create_page_general(self): frame_format: Frame format_width_title: Label (*)format_width_int: Entry - format_width + frame_line_numbers_default: Frame + line_numbers_default_title: Label + (*)line_numbers_default_bool: Checkbutton - line_numbers_default frame_context: Frame context_title: Label (*)context_int: Entry - context_lines @@ -1866,6 +1875,9 @@ def create_page_general(self): IntVar(self), ('main', 'General', 'autosave')) self.format_width = tracers.add( StringVar(self), ('extensions', 'FormatParagraph', 'max-width')) + self.line_numbers_default = tracers.add( + BooleanVar(self), + ('main', 'EditorWindow', 'line-numbers-default')) self.context_lines = tracers.add( StringVar(self), ('extensions', 'CodeContext', 'maxlines')) @@ -1944,6 +1956,14 @@ def create_page_general(self): validatecommand=self.digits_only, validate='key', ) + frame_line_numbers_default = Frame(frame_editor, borderwidth=0) + line_numbers_default_title = Label( + frame_line_numbers_default, text='Show line numbers in new windows') + self.line_numbers_default_bool = Checkbutton( + frame_line_numbers_default, + variable=self.line_numbers_default, + width=1) + frame_context = Frame(frame_editor, borderwidth=0) context_title = Label(frame_context, text='Max Context Lines :') self.context_int = Entry( @@ -2021,6 +2041,10 @@ def create_page_general(self): frame_format.pack(side=TOP, padx=5, pady=0, fill=X) format_width_title.pack(side=LEFT, anchor=W, padx=5, pady=5) self.format_width_int.pack(side=TOP, padx=10, pady=5) + # frame_line_numbers_default. + frame_line_numbers_default.pack(side=TOP, padx=5, pady=0, fill=X) + line_numbers_default_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.line_numbers_default_bool.pack(side=LEFT, padx=5, pady=5) # frame_context. frame_context.pack(side=TOP, padx=5, pady=0, fill=X) context_title.pack(side=LEFT, anchor=W, padx=5, pady=5) @@ -2063,6 +2087,8 @@ def load_general_cfg(self): 'main', 'General', 'autosave', default=0, type='bool')) self.format_width.set(idleConf.GetOption( 'extensions', 'FormatParagraph', 'max-width', type='int')) + self.line_numbers_default.set(idleConf.GetOption( + 'main', 'EditorWindow', 'line-numbers-default', type='bool')) self.context_lines.set(idleConf.GetOption( 'extensions', 'CodeContext', 'maxlines', type='int')) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 24b1ffc67975..497ee12f1814 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,6 +53,7 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext + from idlelib.sidebar import LineNumbers from idlelib.format import FormatParagraph, FormatRegion, Indents, Rstrip from idlelib.parenmatch import ParenMatch from idlelib.squeezer import Squeezer @@ -61,7 +62,8 @@ class EditorWindow(object): filesystemencoding = sys.getfilesystemencoding() # for file names help_url = None - allow_codecontext = True + allow_code_context = True + allow_line_numbers = True def __init__(self, flist=None, filename=None, key=None, root=None): # Delay import: runscript imports pyshell imports EditorWindow. @@ -198,12 +200,14 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", self.open_turtle_demo) self.set_status_bar() + text_frame.pack(side=LEFT, fill=BOTH, expand=1) + text_frame.rowconfigure(1, weight=1) + text_frame.columnconfigure(1, weight=1) vbar['command'] = self.handle_yview - vbar.pack(side=RIGHT, fill=Y) + vbar.grid(row=1, column=2, sticky=NSEW) text['yscrollcommand'] = vbar.set text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow') - text_frame.pack(side=LEFT, fill=BOTH, expand=1) - text.pack(side=TOP, fill=BOTH, expand=1) + text.grid(row=1, column=1, sticky=NSEW) text.focus_set() # usetabs true -> literal tab characters are used by indent and @@ -250,7 +254,8 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.good_load = False self.set_indentation_params(False) self.color = None # initialized below in self.ResetColorizer - self.codecontext = None + self.code_context = None # optionally initialized later below + self.line_numbers = None # optionally initialized later below if filename: if os.path.exists(filename) and not os.path.isdir(filename): if io.loadfile(filename): @@ -316,10 +321,20 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", ctip.refresh_calltip_event) text.bind("<>", ctip.force_open_calltip_event) text.bind("<>", self.ZoomHeight(self).zoom_height_event) - if self.allow_codecontext: - self.codecontext = self.CodeContext(self) + if self.allow_code_context: + self.code_context = self.CodeContext(self) text.bind("<>", - self.codecontext.toggle_code_context_event) + self.code_context.toggle_code_context_event) + else: + self.update_menu_state('options', '*Code Context', 'disabled') + if self.allow_line_numbers: + self.line_numbers = self.LineNumbers(self) + if idleConf.GetOption('main', 'EditorWindow', + 'line-numbers-default', type='bool'): + self.toggle_line_numbers_event() + text.bind("<>", self.toggle_line_numbers_event) + else: + self.update_menu_state('options', '*Line Numbers', 'disabled') def _filename_to_unicode(self, filename): """Return filename as BMP unicode so displayable in Tk.""" @@ -779,8 +794,11 @@ def ResetColorizer(self): self._addcolorizer() EditorWindow.color_config(self.text) - if self.codecontext is not None: - self.codecontext.update_highlight_colors() + if self.code_context is not None: + self.code_context.update_highlight_colors() + + if self.line_numbers is not None: + self.line_numbers.update_colors() IDENTCHARS = string.ascii_letters + string.digits + "_" @@ -799,11 +817,16 @@ def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configdialog.py - new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow') # Update the code context widget first, since its height affects # the height of the text widget. This avoids double re-rendering. - if self.codecontext is not None: - self.codecontext.update_font(new_font) + if self.code_context is not None: + self.code_context.update_font() + # Next, update the line numbers widget, since its width affects + # the width of the text widget. + if self.line_numbers is not None: + self.line_numbers.update_font() + # Finally, update the main text widget. + new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow') self.text['font'] = new_font def RemoveKeybindings(self): @@ -1467,6 +1490,19 @@ def guess_indent(self): indentsmall = indentlarge = 0 return indentlarge - indentsmall + def toggle_line_numbers_event(self, event=None): + if self.line_numbers is None: + return + + if self.line_numbers.is_shown: + self.line_numbers.hide_sidebar() + menu_label = "Show" + else: + self.line_numbers.show_sidebar() + menu_label = "Hide" + self.update_menu_label(menu='options', index='*Line Numbers', + label=f'{menu_label} Line Numbers') + # "line.col" -> line, as an int def index2line(index): return int(float(index)) diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index cee6887df68f..49068df7a8b9 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -271,10 +271,15 @@

    Options menu (Shell and Editor)Setting preferences under Help and preferences. +Setting preferences under Help and preferences. +Most configuration options apply to all windows or all future windows. +The option items below only apply to the active window.
    Show/Hide Code Context (Editor Window only)
    Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window. See Code Context in the Editing and Navigation section below.
    +
    Line Numbers (Editor Window only)
    Open a column to the left of the edit window which shows the linenumber +of each line of text. The default is off unless configured on +(see Setting preferences).
    Zoom/Restore Height
    Toggles the window between normal size and maximum height. The initial size defaults to 40 lines by 80 chars unless changed on the General tab of the Configure IDLE dialog. The maximum height for a screen is determined by @@ -607,7 +612,7 @@

    Running user codesys.getrecursionlimit and -sys.setrecursionlimit to reduce their visibility.

    +sys.setrecursionlimit to reduce the effect of the additional stack frames.

    If sys is reset by user code, such as with importlib.reload(sys), IDLE?s changes are lost and input from the keyboard and output to the screen will not work correctly.

    @@ -895,7 +900,7 @@

    Navigation



    - Last updated on Jul 04, 2019. + Last updated on Jul 23, 2019.
    Found a bug?
    diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 20e5e9014ee7..f2f37e161632 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -67,6 +67,7 @@ def _wrapper(parent): # htest # import idlelib.pyshell # Set Windows DPI awareness before Tk(). from importlib import import_module +import textwrap import tkinter as tk from tkinter.ttk import Scrollbar tk.NoDefaultRoot() @@ -205,6 +206,19 @@ def _wrapper(parent): # htest # "Check that changes were saved by opening the file elsewhere." } +_linenumbers_drag_scrolling_spec = { + 'file': 'sidebar', + 'kwds': {}, + 'msg': textwrap.dedent("""\ + Click on the line numbers and drag down below the edge of the + window, moving the mouse a bit and then leaving it there for a while. + The text and line numbers should gradually scroll down, with the + selection updated continuously. + Do the same as above, dragging to above the window. The text and line + numbers should gradually scroll up, with the selection updated + continuously."""), + } + _multi_call_spec = { 'file': 'multicall', 'kwds': {}, diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index c6c8e8efcd4f..3ec49e97af6f 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -4,7 +4,7 @@ import unittest import unittest.mock from test.support import requires -from tkinter import Tk, Frame, Text, TclError +from tkinter import NSEW, Tk, Frame, Text, TclError from unittest import mock import re @@ -62,7 +62,7 @@ def setUpClass(cls): text.insert('1.0', code_sample) # Need to pack for creation of code context text widget. frame.pack(side='left', fill='both', expand=1) - text.pack(side='top', fill='both', expand=1) + text.grid(row=1, column=1, sticky=NSEW) cls.editor = DummyEditwin(root, frame, text) codecontext.idleConf.userCfg = testcfg @@ -77,6 +77,7 @@ def tearDownClass(cls): def setUp(self): self.text.yview(0) + self.text['font'] = 'TkFixedFont' self.cc = codecontext.CodeContext(self.editor) self.highlight_cfg = {"background": '#abcdef', @@ -86,10 +87,18 @@ def mock_idleconf_GetHighlight(theme, element): if element == 'context': return self.highlight_cfg return orig_idleConf_GetHighlight(theme, element) - patcher = unittest.mock.patch.object( + GetHighlight_patcher = unittest.mock.patch.object( codecontext.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) - patcher.start() - self.addCleanup(patcher.stop) + GetHighlight_patcher.start() + self.addCleanup(GetHighlight_patcher.stop) + + self.font_override = 'TkFixedFont' + def mock_idleconf_GetFont(root, configType, section): + return self.font_override + GetFont_patcher = unittest.mock.patch.object( + codecontext.idleConf, 'GetFont', mock_idleconf_GetFont) + GetFont_patcher.start() + self.addCleanup(GetFont_patcher.stop) def tearDown(self): if self.cc.context: @@ -339,69 +348,59 @@ def test_timer_event(self, mock_update): def test_font(self): eq = self.assertEqual cc = self.cc - save_font = cc.text['font'] + + orig_font = cc.text['font'] test_font = 'TkTextFont' + self.assertNotEqual(orig_font, test_font) # Ensure code context is not active. if cc.context is not None: cc.toggle_code_context_event() + self.font_override = test_font # Nothing breaks or changes with inactive code context. - cc.update_font(test_font) + cc.update_font() - # Activate code context, but no change to font. - cc.toggle_code_context_event() - eq(cc.context['font'], save_font) - # Call font update with the existing font. - cc.update_font(save_font) - eq(cc.context['font'], save_font) + # Activate code context, previous font change is immediately effective. cc.toggle_code_context_event() - - # Change text widget font and activate code context. - cc.text['font'] = test_font - cc.toggle_code_context_event(test_font) eq(cc.context['font'], test_font) - # Just call the font update. - cc.update_font(save_font) - eq(cc.context['font'], save_font) - cc.text['font'] = save_font + # Call the font update, change is picked up. + self.font_override = orig_font + cc.update_font() + eq(cc.context['font'], orig_font) def test_highlight_colors(self): eq = self.assertEqual cc = self.cc - save_colors = dict(self.highlight_cfg) + + orig_colors = dict(self.highlight_cfg) test_colors = {'background': '#222222', 'foreground': '#ffff00'} + def assert_colors_are_equal(colors): + eq(cc.context['background'], colors['background']) + eq(cc.context['foreground'], colors['foreground']) + # Ensure code context is not active. if cc.context: cc.toggle_code_context_event() + self.highlight_cfg = test_colors # Nothing breaks with inactive code context. cc.update_highlight_colors() - # Activate code context, but no change to colors. + # Activate code context, previous colors change is immediately effective. cc.toggle_code_context_event() - eq(cc.context['background'], save_colors['background']) - eq(cc.context['foreground'], save_colors['foreground']) + assert_colors_are_equal(test_colors) - # Call colors update, but no change to font. + # Call colors update with no change to the configured colors. cc.update_highlight_colors() - eq(cc.context['background'], save_colors['background']) - eq(cc.context['foreground'], save_colors['foreground']) - cc.toggle_code_context_event() - - # Change colors and activate code context. - self.highlight_cfg = test_colors - cc.toggle_code_context_event() - eq(cc.context['background'], test_colors['background']) - eq(cc.context['foreground'], test_colors['foreground']) + assert_colors_are_equal(test_colors) - # Change colors and call highlight colors update. - self.highlight_cfg = save_colors + # Call the colors update with code context active, change is picked up. + self.highlight_cfg = orig_colors cc.update_highlight_colors() - eq(cc.context['background'], save_colors['background']) - eq(cc.context['foreground'], save_colors['foreground']) + assert_colors_are_equal(orig_colors) class HelperFunctionText(unittest.TestCase): diff --git a/Lib/idlelib/idle_test/test_sidebar.py b/Lib/idlelib/idle_test/test_sidebar.py new file mode 100644 index 000000000000..8c98a0c0cbde --- /dev/null +++ b/Lib/idlelib/idle_test/test_sidebar.py @@ -0,0 +1,351 @@ +"""Test sidebar, coverage 93%""" +from itertools import chain +import unittest +import unittest.mock +from test.support import requires +import tkinter as tk + +from idlelib.delegator import Delegator +from idlelib.percolator import Percolator +import idlelib.sidebar + + +class Dummy_editwin: + def __init__(self, text): + self.text = text + self.text_frame = self.text.master + self.per = Percolator(text) + self.undo = Delegator() + self.per.insertfilter(self.undo) + + def setvar(self, name, value): + pass + + def getlineno(self, index): + return int(float(self.text.index(index))) + + +class LineNumbersTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = tk.Tk() + + cls.text_frame = tk.Frame(cls.root) + cls.text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + cls.text_frame.rowconfigure(1, weight=1) + cls.text_frame.columnconfigure(1, weight=1) + + cls.text = tk.Text(cls.text_frame, width=80, height=24, wrap=tk.NONE) + cls.text.grid(row=1, column=1, sticky=tk.NSEW) + + cls.editwin = Dummy_editwin(cls.text) + cls.editwin.vbar = tk.Scrollbar(cls.text_frame) + + @classmethod + def tearDownClass(cls): + cls.editwin.per.close() + cls.root.update() + cls.root.destroy() + del cls.text, cls.text_frame, cls.editwin, cls.root + + def setUp(self): + self.linenumber = idlelib.sidebar.LineNumbers(self.editwin) + + self.highlight_cfg = {"background": '#abcdef', + "foreground": '#123456'} + orig_idleConf_GetHighlight = idlelib.sidebar.idleConf.GetHighlight + def mock_idleconf_GetHighlight(theme, element): + if element == 'linenumber': + return self.highlight_cfg + return orig_idleConf_GetHighlight(theme, element) + GetHighlight_patcher = unittest.mock.patch.object( + idlelib.sidebar.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) + GetHighlight_patcher.start() + self.addCleanup(GetHighlight_patcher.stop) + + self.font_override = 'TkFixedFont' + def mock_idleconf_GetFont(root, configType, section): + return self.font_override + GetFont_patcher = unittest.mock.patch.object( + idlelib.sidebar.idleConf, 'GetFont', mock_idleconf_GetFont) + GetFont_patcher.start() + self.addCleanup(GetFont_patcher.stop) + + def tearDown(self): + self.text.delete('1.0', 'end') + + def get_selection(self): + return tuple(map(str, self.text.tag_ranges('sel'))) + + def get_line_screen_position(self, line): + bbox = self.linenumber.sidebar_text.bbox(f'{line}.end -1c') + x = bbox[0] + 2 + y = bbox[1] + 2 + return x, y + + def assert_state_disabled(self): + state = self.linenumber.sidebar_text.config()['state'] + self.assertEqual(state[-1], tk.DISABLED) + + def get_sidebar_text_contents(self): + return self.linenumber.sidebar_text.get('1.0', tk.END) + + def assert_sidebar_n_lines(self, n_lines): + expected = '\n'.join(chain(map(str, range(1, n_lines + 1)), [''])) + self.assertEqual(self.get_sidebar_text_contents(), expected) + + def assert_text_equals(self, expected): + return self.assertEqual(self.text.get('1.0', 'end'), expected) + + def test_init_empty(self): + self.assert_sidebar_n_lines(1) + + def test_init_not_empty(self): + self.text.insert('insert', 'foo bar\n'*3) + self.assert_text_equals('foo bar\n'*3 + '\n') + self.assert_sidebar_n_lines(4) + + def test_toggle_linenumbering(self): + self.assertEqual(self.linenumber.is_shown, False) + self.linenumber.show_sidebar() + self.assertEqual(self.linenumber.is_shown, True) + self.linenumber.hide_sidebar() + self.assertEqual(self.linenumber.is_shown, False) + self.linenumber.hide_sidebar() + self.assertEqual(self.linenumber.is_shown, False) + self.linenumber.show_sidebar() + self.assertEqual(self.linenumber.is_shown, True) + self.linenumber.show_sidebar() + self.assertEqual(self.linenumber.is_shown, True) + + def test_insert(self): + self.text.insert('insert', 'foobar') + self.assert_text_equals('foobar\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + self.text.insert('insert', '\nfoo') + self.assert_text_equals('foobar\nfoo\n') + self.assert_sidebar_n_lines(2) + self.assert_state_disabled() + + self.text.insert('insert', 'hello\n'*2) + self.assert_text_equals('foobar\nfoohello\nhello\n\n') + self.assert_sidebar_n_lines(4) + self.assert_state_disabled() + + self.text.insert('insert', '\nworld') + self.assert_text_equals('foobar\nfoohello\nhello\n\nworld\n') + self.assert_sidebar_n_lines(5) + self.assert_state_disabled() + + def test_delete(self): + self.text.insert('insert', 'foobar') + self.assert_text_equals('foobar\n') + self.text.delete('1.1', '1.3') + self.assert_text_equals('fbar\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + self.text.insert('insert', 'foo\n'*2) + self.assert_text_equals('fbarfoo\nfoo\n\n') + self.assert_sidebar_n_lines(3) + self.assert_state_disabled() + + # Note: deleting up to "2.end" doesn't delete the final newline. + self.text.delete('2.0', '2.end') + self.assert_text_equals('fbarfoo\n\n\n') + self.assert_sidebar_n_lines(3) + self.assert_state_disabled() + + self.text.delete('1.3', 'end') + self.assert_text_equals('fba\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + # Note: Text widgets always keep a single '\n' character at the end. + self.text.delete('1.0', 'end') + self.assert_text_equals('\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + def test_sidebar_text_width(self): + """ + Test that linenumber text widget is always at the minimum + width + """ + def get_width(): + return self.linenumber.sidebar_text.config()['width'][-1] + + self.assert_sidebar_n_lines(1) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo') + self.assert_sidebar_n_lines(1) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo\n'*8) + self.assert_sidebar_n_lines(9) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(10) + self.assertEqual(get_width(), 2) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(11) + self.assertEqual(get_width(), 2) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(10) + self.assertEqual(get_width(), 2) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(9) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo\n'*90) + self.assert_sidebar_n_lines(99) + self.assertEqual(get_width(), 2) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(100) + self.assertEqual(get_width(), 3) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(101) + self.assertEqual(get_width(), 3) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(100) + self.assertEqual(get_width(), 3) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(99) + self.assertEqual(get_width(), 2) + + self.text.delete('50.0 -1c', 'end -1c') + self.assert_sidebar_n_lines(49) + self.assertEqual(get_width(), 2) + + self.text.delete('5.0 -1c', 'end -1c') + self.assert_sidebar_n_lines(4) + self.assertEqual(get_width(), 1) + + # Note: Text widgets always keep a single '\n' character at the end. + self.text.delete('1.0', 'end -1c') + self.assert_sidebar_n_lines(1) + self.assertEqual(get_width(), 1) + + def test_click_selection(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') + self.root.update() + + # Click on the second line. + x, y = self.get_line_screen_position(2) + self.linenumber.sidebar_text.event_generate('', x=x, y=y) + self.linenumber.sidebar_text.update() + self.root.update() + + self.assertEqual(self.get_selection(), ('2.0', '3.0')) + + def test_drag_selection(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') + self.root.update() + + # Drag from the first line to the third line. + start_x, start_y = self.get_line_screen_position(1) + end_x, end_y = self.get_line_screen_position(3) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.root.update() + + self.assertEqual(self.get_selection(), ('1.0', '4.0')) + + def test_scroll(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'line\n' * 100) + self.root.update() + + # Scroll down 10 lines. + self.text.yview_scroll(10, 'unit') + self.root.update() + self.assertEqual(self.text.index('@0,0'), '11.0') + self.assertEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0') + + # Generate a mouse-wheel event and make sure it scrolled up or down. + # The meaning of the "delta" is OS-dependant, so this just checks for + # any change. + self.linenumber.sidebar_text.event_generate('', + x=0, y=0, + delta=10) + self.root.update() + self.assertNotEqual(self.text.index('@0,0'), '11.0') + self.assertNotEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0') + + def test_font(self): + ln = self.linenumber + + orig_font = ln.sidebar_text['font'] + test_font = 'TkTextFont' + self.assertNotEqual(orig_font, test_font) + + # Ensure line numbers aren't shown. + ln.hide_sidebar() + + self.font_override = test_font + # Nothing breaks when line numbers aren't shown. + ln.update_font() + + # Activate line numbers, previous font change is immediately effective. + ln.show_sidebar() + self.assertEqual(ln.sidebar_text['font'], test_font) + + # Call the font update with line numbers shown, change is picked up. + self.font_override = orig_font + ln.update_font() + self.assertEqual(ln.sidebar_text['font'], orig_font) + + def test_highlight_colors(self): + ln = self.linenumber + + orig_colors = dict(self.highlight_cfg) + test_colors = {'background': '#222222', 'foreground': '#ffff00'} + + def assert_colors_are_equal(colors): + self.assertEqual(ln.sidebar_text['background'], colors['background']) + self.assertEqual(ln.sidebar_text['foreground'], colors['foreground']) + + # Ensure line numbers aren't shown. + ln.hide_sidebar() + + self.highlight_cfg = test_colors + # Nothing breaks with inactive code context. + ln.update_colors() + + # Show line numbers, previous colors change is immediately effective. + ln.show_sidebar() + assert_colors_are_equal(test_colors) + + # Call colors update with no change to the configured colors. + ln.update_colors() + assert_colors_are_equal(test_colors) + + # Call the colors update with line numbers shown, change is picked up. + self.highlight_cfg = orig_colors + ln.update_colors() + assert_colors_are_equal(orig_colors) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py index b0c85cf505c7..fc51fb1b5d34 100644 --- a/Lib/idlelib/mainmenu.py +++ b/Lib/idlelib/mainmenu.py @@ -100,7 +100,8 @@ ('Configure _IDLE', '<>'), None, ('Show _Code Context', '<>'), - ('Zoom Height', '<>'), + ('Show _Line Numbers', '<>'), + ('_Zoom Height', '<>'), ]), ('window', [ diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index 38c59bdf9b4e..90272b6feb4a 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -74,13 +74,11 @@ class OutputWindow(EditorWindow): ("Go to file/line", "<>", None), ] - allow_codecontext = False + allow_code_context = False def __init__(self, *args): EditorWindow.__init__(self, *args) self.text.bind("<>", self.goto_file_line) - self.text.unbind("<>") - self.update_menu_state('options', '*Code Context', 'disabled') # Customize EditorWindow def ispythonsource(self, filename): diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 7ad5a76c3bd5..87401f33f55f 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -861,6 +861,8 @@ class PyShell(OutputWindow): ("Squeeze", "<>"), ] + allow_line_numbers = False + # New classes from idlelib.history import History diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py new file mode 100644 index 000000000000..8f9bf80b5260 --- /dev/null +++ b/Lib/idlelib/sidebar.py @@ -0,0 +1,324 @@ +"""Line numbering implementation for IDLE as an extension. +Includes BaseSideBar which can be extended for other sidebar based extensions +""" +import functools +import itertools + +import tkinter as tk +from idlelib.config import idleConf +from idlelib.delegator import Delegator + + +def get_end_linenumber(text): + """Utility to get the last line's number in a Tk text widget.""" + return int(float(text.index('end-1c'))) + + +def get_widget_padding(widget): + """Get the total padding of a Tk widget, including its border.""" + # TODO: use also in codecontext.py + manager = widget.winfo_manager() + if manager == 'pack': + info = widget.pack_info() + elif manager == 'grid': + info = widget.grid_info() + else: + raise ValueError(f"Unsupported geometry manager: {manager}") + + # All values are passed through getint(), since some + # values may be pixel objects, which can't simply be added to ints. + padx = sum(map(widget.tk.getint, [ + info['padx'], + widget.cget('padx'), + widget.cget('border'), + ])) + pady = sum(map(widget.tk.getint, [ + info['pady'], + widget.cget('pady'), + widget.cget('border'), + ])) + return padx, pady + + +class BaseSideBar: + """ + The base class for extensions which require a sidebar. + """ + def __init__(self, editwin): + self.editwin = editwin + self.parent = editwin.text_frame + self.text = editwin.text + + _padx, pady = get_widget_padding(self.text) + self.sidebar_text = tk.Text(self.parent, width=1, wrap=tk.NONE, + padx=0, pady=pady, + borderwidth=0, highlightthickness=0) + self.sidebar_text.config(state=tk.DISABLED) + self.text['yscrollcommand'] = self.redirect_yscroll_event + self.update_font() + self.update_colors() + + self.is_shown = False + + def update_font(self): + """Update the sidebar text font, usually after config changes.""" + font = idleConf.GetFont(self.text, 'main', 'EditorWindow') + self._update_font(font) + + def _update_font(self, font): + self.sidebar_text['font'] = font + + def update_colors(self): + """Update the sidebar text colors, usually after config changes.""" + colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'normal') + self._update_colors(foreground=colors['foreground'], + background=colors['background']) + + def _update_colors(self, foreground, background): + self.sidebar_text.config( + fg=foreground, bg=background, + selectforeground=foreground, selectbackground=background, + inactiveselectbackground=background, + ) + + def show_sidebar(self): + if not self.is_shown: + self.sidebar_text.grid(row=1, column=0, sticky=tk.NSEW) + self.is_shown = True + + def hide_sidebar(self): + if self.is_shown: + self.sidebar_text.grid_forget() + self.is_shown = False + + def redirect_yscroll_event(self, *args, **kwargs): + """Redirect vertical scrolling to the main editor text widget. + + The scroll bar is also updated. + """ + self.editwin.vbar.set(*args) + self.sidebar_text.yview_moveto(args[0]) + return 'break' + + def redirect_focusin_event(self, event): + """Redirect focus-in events to the main editor text widget.""" + self.text.focus_set() + return 'break' + + def redirect_mousebutton_event(self, event, event_name): + """Redirect mouse button events to the main editor text widget.""" + self.text.focus_set() + self.text.event_generate(event_name, x=0, y=event.y) + return 'break' + + def redirect_mousewheel_event(self, event): + """Redirect mouse wheel events to the editwin text widget.""" + self.text.event_generate('', + x=0, y=event.y, delta=event.delta) + return 'break' + + +class EndLineDelegator(Delegator): + """Generate callbacks with the current end line number after + insert or delete operations""" + def __init__(self, changed_callback): + """ + changed_callback - Callable, will be called after insert + or delete operations with the current + end line number. + """ + Delegator.__init__(self) + self.changed_callback = changed_callback + + def insert(self, index, chars, tags=None): + self.delegate.insert(index, chars, tags) + self.changed_callback(get_end_linenumber(self.delegate)) + + def delete(self, index1, index2=None): + self.delegate.delete(index1, index2) + self.changed_callback(get_end_linenumber(self.delegate)) + + +class LineNumbers(BaseSideBar): + """Line numbers support for editor windows.""" + def __init__(self, editwin): + BaseSideBar.__init__(self, editwin) + self.prev_end = 1 + self._sidebar_width_type = type(self.sidebar_text['width']) + self.sidebar_text.config(state=tk.NORMAL) + self.sidebar_text.insert('insert', '1', 'linenumber') + self.sidebar_text.config(state=tk.DISABLED) + self.sidebar_text.config(takefocus=False, exportselection=False) + self.sidebar_text.tag_config('linenumber', justify=tk.RIGHT) + + self.bind_events() + + end = get_end_linenumber(self.text) + self.update_sidebar_text(end) + + end_line_delegator = EndLineDelegator(self.update_sidebar_text) + # Insert the delegator after the undo delegator, so that line numbers + # are properly updated after undo and redo actions. + end_line_delegator.setdelegate(self.editwin.undo.delegate) + self.editwin.undo.setdelegate(end_line_delegator) + # Reset the delegator caches of the delegators "above" the + # end line delegator we just inserted. + delegator = self.editwin.per.top + while delegator is not end_line_delegator: + delegator.resetcache() + delegator = delegator.delegate + + self.is_shown = False + + def bind_events(self): + # Ensure focus is always redirected to the main editor text widget. + self.sidebar_text.bind('', self.redirect_focusin_event) + + # Redirect mouse scrolling to the main editor text widget. + # + # Note that without this, scrolling with the mouse only scrolls + # the line numbers. + self.sidebar_text.bind('', self.redirect_mousewheel_event) + + # Redirect mouse button events to the main editor text widget, + # except for the left mouse button (1). + # + # Note: X-11 sends Button-4 and Button-5 events for the scroll wheel. + def bind_mouse_event(event_name, target_event_name): + handler = functools.partial(self.redirect_mousebutton_event, + event_name=target_event_name) + self.sidebar_text.bind(event_name, handler) + + for button in [2, 3, 4, 5]: + for event_name in (f'', + f'', + f'', + ): + bind_mouse_event(event_name, target_event_name=event_name) + + # Convert double- and triple-click events to normal click events, + # since event_generate() doesn't allow generating such events. + for event_name in (f'', + f'', + ): + bind_mouse_event(event_name, + target_event_name=f'') + + start_line = None + def b1_mousedown_handler(event): + # select the entire line + lineno = self.editwin.getlineno(f"@0,{event.y}") + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", f"{lineno}.0", f"{lineno+1}.0") + self.text.mark_set("insert", f"{lineno+1}.0") + + # remember this line in case this is the beginning of dragging + nonlocal start_line + start_line = lineno + self.sidebar_text.bind('', b1_mousedown_handler) + + # These are set by b1_motion_handler() and read by selection_handler(); + # see below. last_y is passed this way since the mouse Y-coordinate + # is not available on selection event objects. last_yview is passed + # this way to recognize scrolling while the mouse isn't moving. + last_y = last_yview = None + + def drag_update_selection_and_insert_mark(y_coord): + """Helper function for drag and selection event handlers.""" + lineno = self.editwin.getlineno(f"@0,{y_coord}") + a, b = sorted([start_line, lineno]) + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", f"{a}.0", f"{b+1}.0") + self.text.mark_set("insert", + f"{lineno if lineno == a else lineno + 1}.0") + + # Special handling of dragging with mouse button 1. In "normal" text + # widgets this selects text, but the line numbers text widget has + # selection disabled. Still, dragging triggers some selection-related + # functionality under the hood. Specifically, dragging to above or + # below the text widget triggers scrolling, in a way that bypasses the + # other scrolling synchronization mechanisms.i + def b1_drag_handler(event, *args): + nonlocal last_y + nonlocal last_yview + last_y = event.y + last_yview = self.sidebar_text.yview() + if not 0 <= last_y <= self.sidebar_text.winfo_height(): + self.text.yview_moveto(last_yview[0]) + drag_update_selection_and_insert_mark(event.y) + self.sidebar_text.bind('', b1_drag_handler) + + # With mouse-drag scrolling fixed by the above, there is still an edge- + # case we need to handle: When drag-scrolling, scrolling can continue + # while the mouse isn't moving, leading to the above fix not scrolling + # properly. + def selection_handler(event): + yview = self.sidebar_text.yview() + if yview != last_yview: + self.text.yview_moveto(yview[0]) + drag_update_selection_and_insert_mark(last_y) + self.sidebar_text.bind('<>', selection_handler) + + def update_colors(self): + """Update the sidebar text colors, usually after config changes.""" + colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'linenumber') + self._update_colors(foreground=colors['foreground'], + background=colors['background']) + + def update_sidebar_text(self, end): + """ + Perform the following action: + Each line sidebar_text contains the linenumber for that line + Synchronize with editwin.text so that both sidebar_text and + editwin.text contain the same number of lines""" + if end == self.prev_end: + return + + width_difference = len(str(end)) - len(str(self.prev_end)) + if width_difference: + cur_width = int(float(self.sidebar_text['width'])) + new_width = cur_width + width_difference + self.sidebar_text['width'] = self._sidebar_width_type(new_width) + + self.sidebar_text.config(state=tk.NORMAL) + if end > self.prev_end: + new_text = '\n'.join(itertools.chain( + [''], + map(str, range(self.prev_end + 1, end + 1)), + )) + self.sidebar_text.insert(f'end -1c', new_text, 'linenumber') + else: + self.sidebar_text.delete(f'{end+1}.0 -1c', 'end -1c') + self.sidebar_text.config(state=tk.DISABLED) + + self.prev_end = end + + +def _linenumbers_drag_scrolling(parent): # htest # + from idlelib.idle_test.test_sidebar import Dummy_editwin + + toplevel = tk.Toplevel(parent) + text_frame = tk.Frame(toplevel) + text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + text_frame.rowconfigure(1, weight=1) + text_frame.columnconfigure(1, weight=1) + + font = idleConf.GetFont(toplevel, 'main', 'EditorWindow') + text = tk.Text(text_frame, width=80, height=24, wrap=tk.NONE, font=font) + text.grid(row=1, column=1, sticky=tk.NSEW) + + editwin = Dummy_editwin(text) + editwin.vbar = tk.Scrollbar(text_frame) + + linenumbers = LineNumbers(editwin) + linenumbers.show_sidebar() + + text.insert('1.0', '\n'.join('a'*i for i in range(1, 101))) + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_sidebar', verbosity=2, exit=False) + + from idlelib.idle_test.htest import run + run(_linenumbers_drag_scrolling) diff --git a/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst b/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst new file mode 100644 index 000000000000..201a413b42a3 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst @@ -0,0 +1,4 @@ +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. + From webhook-mailer at python.org Tue Jul 23 09:04:32 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 23 Jul 2019 13:04:32 -0000 Subject: [Python-checkins] bpo-17535: IDLE editor line numbers (GH-14030) Message-ID: https://github.com/python/cpython/commit/1da6a313ddaa269dddce303cb03ca95fe57d0c85 commit: 1da6a313ddaa269dddce303cb03ca95fe57d0c85 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-23T06:04:13-07:00 summary: bpo-17535: IDLE editor line numbers (GH-14030) (cherry picked from commit 7123ea009b0b004062d91f69859bddf422c34ab4) Co-authored-by: Tal Einat files: A Lib/idlelib/idle_test/test_sidebar.py A Lib/idlelib/sidebar.py A Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst M Doc/library/idle.rst M Doc/whatsnew/3.7.rst M Doc/whatsnew/3.8.rst M Lib/idlelib/codecontext.py M Lib/idlelib/config-highlight.def M Lib/idlelib/config-main.def M Lib/idlelib/config.py M Lib/idlelib/configdialog.py M Lib/idlelib/editor.py M Lib/idlelib/help.html M Lib/idlelib/idle_test/htest.py M Lib/idlelib/idle_test/test_codecontext.py M Lib/idlelib/mainmenu.py M Lib/idlelib/outwin.py M Lib/idlelib/pyshell.py diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index de58f266bf5e..5975e3bb5806 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -290,22 +290,31 @@ Options menu (Shell and Editor) Configure IDLE Open a configuration dialog and change preferences for the following: fonts, indentation, keybindings, text color themes, startup windows and - size, additional help sources, and extensions. On macOS, open the + size, additional help sources, and extensions. On macOS, open the configuration dialog by selecting Preferences in the application - menu. For more, see + menu. For more details, see :ref:`Setting preferences ` under Help and preferences. + Most configuration options apply to all windows or all future windows. + The option items below only apply to the active window. + Show/Hide Code Context (Editor Window only) Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window. See - :ref:`Code Context ` in the Editing and Navigation section below. + :ref:`Code Context ` in the Editing and Navigation section + below. + +Show/Hide Line Numbers (Editor Window only) + Open a column to the left of the edit window which shows the number + of each line of text. The default is off, which may be changed in the + preferences (see :ref:`Setting preferences `). Zoom/Restore Height Toggles the window between normal size and maximum height. The initial size defaults to 40 lines by 80 chars unless changed on the General tab of the Configure IDLE dialog. The maximum height for a screen is determined by momentarily maximizing a window the first time one is zoomed on the screen. - Changing screen settings may invalidate the saved height. This toogle has + Changing screen settings may invalidate the saved height. This toggle has no effect when a window is maximized. Window menu (Shell and Editor) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index fc867ac15a5f..46834ec42d9c 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1017,6 +1017,13 @@ by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.) The changes above have been backported to 3.6 maintenance releases. +New in 3.7.5: + +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. +(Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) + importlib --------- diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 38413a38bb8d..f8cc3304b207 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -512,6 +512,11 @@ for certain types of invalid or corrupt gzip files. idlelib and IDLE ---------------- +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. +(Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) + Output over N lines (50 by default) is squeezed down to a button. N can be changed in the PyShell section of the General page of the Settings dialog. Fewer, but possibly extra long, lines can be squeezed by diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 3103391cfe58..4ce98136fe41 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -13,7 +13,7 @@ from sys import maxsize as INFINITY import tkinter -from tkinter.constants import TOP, X, SUNKEN +from tkinter.constants import NSEW, SUNKEN from idlelib.config import idleConf @@ -67,6 +67,7 @@ def __init__(self, editwin): def _reset(self): self.context = None + self.cell00 = None self.t1 = None self.topvisible = 1 self.info = [(0, -1, "", False)] @@ -105,25 +106,37 @@ def toggle_code_context_event(self, event=None): padx = 0 border = 0 for widget in widgets: - padx += widget.tk.getint(widget.pack_info()['padx']) + info = (widget.grid_info() + if widget is self.editwin.text + else widget.pack_info()) + padx += widget.tk.getint(info['padx']) padx += widget.tk.getint(widget.cget('padx')) border += widget.tk.getint(widget.cget('border')) self.context = tkinter.Text( - self.editwin.top, font=self.text['font'], + self.editwin.text_frame, height=1, width=1, # Don't request more than we get. + highlightthickness=0, padx=padx, border=border, relief=SUNKEN, state='disabled') + self.update_font() self.update_highlight_colors() self.context.bind('', self.jumptoline) # Get the current context and initiate the recurring update event. self.timer_event() - # Pack the context widget before and above the text_frame widget, - # thus ensuring that it will appear directly above text_frame. - self.context.pack(side=TOP, fill=X, expand=False, - before=self.editwin.text_frame) + # Grid the context widget above the text widget. + self.context.grid(row=0, column=1, sticky=NSEW) + + line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(), + 'linenumber') + self.cell00 = tkinter.Frame(self.editwin.text_frame, + bg=line_number_colors['background']) + self.cell00.grid(row=0, column=0, sticky=NSEW) menu_status = 'Hide' else: self.context.destroy() + self.context = None + self.cell00.destroy() + self.cell00 = None self.text.after_cancel(self.t1) self._reset() menu_status = 'Show' @@ -221,8 +234,9 @@ def timer_event(self): self.update_code_context() self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) - def update_font(self, font): + def update_font(self): if self.context is not None: + font = idleConf.GetFont(self.text, 'main', 'EditorWindow') self.context['font'] = font def update_highlight_colors(self): @@ -231,6 +245,11 @@ def update_highlight_colors(self): self.context['background'] = colors['background'] self.context['foreground'] = colors['foreground'] + if self.cell00 is not None: + line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(), + 'linenumber') + self.cell00.config(bg=line_number_colors['background']) + CodeContext.reload() diff --git a/Lib/idlelib/config-highlight.def b/Lib/idlelib/config-highlight.def index aaa2b57a7f6d..a7b0433831c5 100644 --- a/Lib/idlelib/config-highlight.def +++ b/Lib/idlelib/config-highlight.def @@ -22,6 +22,10 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 +context-foreground= #000000 +context-background= lightgray +linenumber-foreground= gray +linenumber-background= #ffffff #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -31,8 +35,6 @@ stderr-foreground= red stderr-background= #ffffff console-foreground= #770000 console-background= #ffffff -context-foreground= #000000 -context-background= lightgray [IDLE New] normal-foreground= #000000 @@ -55,6 +57,10 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 +context-foreground= #000000 +context-background= lightgray +linenumber-foreground= gray +linenumber-background= #ffffff #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -64,8 +70,6 @@ stderr-foreground= red stderr-background= #ffffff console-foreground= #770000 console-background= #ffffff -context-foreground= #000000 -context-background= lightgray [IDLE Dark] comment-foreground = #dd0000 @@ -97,3 +101,5 @@ comment-background = #002240 break-foreground = #FFFFFF context-foreground= #ffffff context-background= #454545 +linenumber-foreground= gray +linenumber-background= #002240 diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 06e3c5adb0e3..b2be6250d021 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -36,7 +36,7 @@ # Additional help sources are listed in the [HelpFiles] section below # and should be viewable by a web browser (or the Windows Help viewer in # the case of .chm files). These sources will be listed on the Help -# menu. The pattern, and two examples, are +# menu. The pattern, and two examples, are: # # # 1 = IDLE;C:/Programs/Python36/Lib/idlelib/help.html @@ -46,7 +46,7 @@ # platform specific because of path separators, drive specs etc. # # The default files should not be edited except to add new sections to -# config-extensions.def for added extensions . The user files should be +# config-extensions.def for added extensions. The user files should be # modified through the Settings dialog. [General] @@ -65,6 +65,7 @@ font= TkFixedFont font-size= 10 font-bold= 0 encoding= none +line-numbers-default= 0 [PyShell] auto-squeeze-min-lines= 50 diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 0c55c9a7d75d..683b000a488b 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -319,6 +319,10 @@ def GetThemeDict(self, type, themeName): 'hit-background':'#000000', 'error-foreground':'#ffffff', 'error-background':'#000000', + 'context-foreground':'#000000', + 'context-background':'#ffffff', + 'linenumber-foreground':'#000000', + 'linenumber-background':'#ffffff', #cursor (only foreground can be set) 'cursor-foreground':'#000000', #shell window @@ -328,11 +332,11 @@ def GetThemeDict(self, type, themeName): 'stderr-background':'#ffffff', 'console-foreground':'#000000', 'console-background':'#ffffff', - 'context-foreground':'#000000', - 'context-background':'#ffffff', } for element in theme: - if not cfgParser.has_option(themeName, element): + if not (cfgParser.has_option(themeName, element) or + # Skip warning for new elements. + element.startswith(('context-', 'linenumber-'))): # Print warning that will return a default color warning = ('\n Warning: config.IdleConf.GetThemeDict' ' -\n problem retrieving theme element %r' diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 6b0ecc9d68e5..217f8fd0a5fb 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -819,6 +819,7 @@ def create_page_highlight(self): 'Shell Error Text': ('error', '12'), 'Shell Stdout Text': ('stdout', '13'), 'Shell Stderr Text': ('stderr', '14'), + 'Line Number': ('linenumber', '16'), } self.builtin_name = tracers.add( StringVar(self), self.var_changed_builtin_name) @@ -866,6 +867,11 @@ def create_page_highlight(self): ('stderr', 'stderr'), ('\n\n', 'normal')) for texttag in text_and_tags: text.insert(END, texttag[0], texttag[1]) + n_lines = len(text.get('1.0', END).splitlines()) + for lineno in range(1, n_lines + 1): + text.insert(f'{lineno}.0', + f'{lineno:{len(str(n_lines))}d} ', + 'linenumber') for element in self.theme_elements: def tem(event, elem=element): # event.widget.winfo_top_level().highlight_target.set(elem) @@ -1827,6 +1833,9 @@ def create_page_general(self): frame_format: Frame format_width_title: Label (*)format_width_int: Entry - format_width + frame_line_numbers_default: Frame + line_numbers_default_title: Label + (*)line_numbers_default_bool: Checkbutton - line_numbers_default frame_context: Frame context_title: Label (*)context_int: Entry - context_lines @@ -1866,6 +1875,9 @@ def create_page_general(self): IntVar(self), ('main', 'General', 'autosave')) self.format_width = tracers.add( StringVar(self), ('extensions', 'FormatParagraph', 'max-width')) + self.line_numbers_default = tracers.add( + BooleanVar(self), + ('main', 'EditorWindow', 'line-numbers-default')) self.context_lines = tracers.add( StringVar(self), ('extensions', 'CodeContext', 'maxlines')) @@ -1944,6 +1956,14 @@ def create_page_general(self): validatecommand=self.digits_only, validate='key', ) + frame_line_numbers_default = Frame(frame_editor, borderwidth=0) + line_numbers_default_title = Label( + frame_line_numbers_default, text='Show line numbers in new windows') + self.line_numbers_default_bool = Checkbutton( + frame_line_numbers_default, + variable=self.line_numbers_default, + width=1) + frame_context = Frame(frame_editor, borderwidth=0) context_title = Label(frame_context, text='Max Context Lines :') self.context_int = Entry( @@ -2021,6 +2041,10 @@ def create_page_general(self): frame_format.pack(side=TOP, padx=5, pady=0, fill=X) format_width_title.pack(side=LEFT, anchor=W, padx=5, pady=5) self.format_width_int.pack(side=TOP, padx=10, pady=5) + # frame_line_numbers_default. + frame_line_numbers_default.pack(side=TOP, padx=5, pady=0, fill=X) + line_numbers_default_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.line_numbers_default_bool.pack(side=LEFT, padx=5, pady=5) # frame_context. frame_context.pack(side=TOP, padx=5, pady=0, fill=X) context_title.pack(side=LEFT, anchor=W, padx=5, pady=5) @@ -2063,6 +2087,8 @@ def load_general_cfg(self): 'main', 'General', 'autosave', default=0, type='bool')) self.format_width.set(idleConf.GetOption( 'extensions', 'FormatParagraph', 'max-width', type='int')) + self.line_numbers_default.set(idleConf.GetOption( + 'main', 'EditorWindow', 'line-numbers-default', type='bool')) self.context_lines.set(idleConf.GetOption( 'extensions', 'CodeContext', 'maxlines', type='int')) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 24b1ffc67975..497ee12f1814 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,6 +53,7 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext + from idlelib.sidebar import LineNumbers from idlelib.format import FormatParagraph, FormatRegion, Indents, Rstrip from idlelib.parenmatch import ParenMatch from idlelib.squeezer import Squeezer @@ -61,7 +62,8 @@ class EditorWindow(object): filesystemencoding = sys.getfilesystemencoding() # for file names help_url = None - allow_codecontext = True + allow_code_context = True + allow_line_numbers = True def __init__(self, flist=None, filename=None, key=None, root=None): # Delay import: runscript imports pyshell imports EditorWindow. @@ -198,12 +200,14 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", self.open_turtle_demo) self.set_status_bar() + text_frame.pack(side=LEFT, fill=BOTH, expand=1) + text_frame.rowconfigure(1, weight=1) + text_frame.columnconfigure(1, weight=1) vbar['command'] = self.handle_yview - vbar.pack(side=RIGHT, fill=Y) + vbar.grid(row=1, column=2, sticky=NSEW) text['yscrollcommand'] = vbar.set text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow') - text_frame.pack(side=LEFT, fill=BOTH, expand=1) - text.pack(side=TOP, fill=BOTH, expand=1) + text.grid(row=1, column=1, sticky=NSEW) text.focus_set() # usetabs true -> literal tab characters are used by indent and @@ -250,7 +254,8 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.good_load = False self.set_indentation_params(False) self.color = None # initialized below in self.ResetColorizer - self.codecontext = None + self.code_context = None # optionally initialized later below + self.line_numbers = None # optionally initialized later below if filename: if os.path.exists(filename) and not os.path.isdir(filename): if io.loadfile(filename): @@ -316,10 +321,20 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", ctip.refresh_calltip_event) text.bind("<>", ctip.force_open_calltip_event) text.bind("<>", self.ZoomHeight(self).zoom_height_event) - if self.allow_codecontext: - self.codecontext = self.CodeContext(self) + if self.allow_code_context: + self.code_context = self.CodeContext(self) text.bind("<>", - self.codecontext.toggle_code_context_event) + self.code_context.toggle_code_context_event) + else: + self.update_menu_state('options', '*Code Context', 'disabled') + if self.allow_line_numbers: + self.line_numbers = self.LineNumbers(self) + if idleConf.GetOption('main', 'EditorWindow', + 'line-numbers-default', type='bool'): + self.toggle_line_numbers_event() + text.bind("<>", self.toggle_line_numbers_event) + else: + self.update_menu_state('options', '*Line Numbers', 'disabled') def _filename_to_unicode(self, filename): """Return filename as BMP unicode so displayable in Tk.""" @@ -779,8 +794,11 @@ def ResetColorizer(self): self._addcolorizer() EditorWindow.color_config(self.text) - if self.codecontext is not None: - self.codecontext.update_highlight_colors() + if self.code_context is not None: + self.code_context.update_highlight_colors() + + if self.line_numbers is not None: + self.line_numbers.update_colors() IDENTCHARS = string.ascii_letters + string.digits + "_" @@ -799,11 +817,16 @@ def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configdialog.py - new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow') # Update the code context widget first, since its height affects # the height of the text widget. This avoids double re-rendering. - if self.codecontext is not None: - self.codecontext.update_font(new_font) + if self.code_context is not None: + self.code_context.update_font() + # Next, update the line numbers widget, since its width affects + # the width of the text widget. + if self.line_numbers is not None: + self.line_numbers.update_font() + # Finally, update the main text widget. + new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow') self.text['font'] = new_font def RemoveKeybindings(self): @@ -1467,6 +1490,19 @@ def guess_indent(self): indentsmall = indentlarge = 0 return indentlarge - indentsmall + def toggle_line_numbers_event(self, event=None): + if self.line_numbers is None: + return + + if self.line_numbers.is_shown: + self.line_numbers.hide_sidebar() + menu_label = "Show" + else: + self.line_numbers.show_sidebar() + menu_label = "Hide" + self.update_menu_label(menu='options', index='*Line Numbers', + label=f'{menu_label} Line Numbers') + # "line.col" -> line, as an int def index2line(index): return int(float(index)) diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index cee6887df68f..49068df7a8b9 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -271,10 +271,15 @@

    Options menu (Shell and Editor)Setting preferences under Help and preferences.

    +Setting preferences under Help and preferences. +Most configuration options apply to all windows or all future windows. +The option items below only apply to the active window.
    Show/Hide Code Context (Editor Window only)
    Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window. See Code Context in the Editing and Navigation section below.
    +
    Line Numbers (Editor Window only)
    Open a column to the left of the edit window which shows the linenumber +of each line of text. The default is off unless configured on +(see Setting preferences).
    Zoom/Restore Height
    Toggles the window between normal size and maximum height. The initial size defaults to 40 lines by 80 chars unless changed on the General tab of the Configure IDLE dialog. The maximum height for a screen is determined by @@ -607,7 +612,7 @@

    Running user codesys.getrecursionlimit and -sys.setrecursionlimit to reduce their visibility.

    +sys.setrecursionlimit to reduce the effect of the additional stack frames.

    If sys is reset by user code, such as with importlib.reload(sys), IDLE?s changes are lost and input from the keyboard and output to the screen will not work correctly.

    @@ -895,7 +900,7 @@

    Navigation



    - Last updated on Jul 04, 2019. + Last updated on Jul 23, 2019.
    Found a bug?
    diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 20e5e9014ee7..f2f37e161632 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -67,6 +67,7 @@ def _wrapper(parent): # htest # import idlelib.pyshell # Set Windows DPI awareness before Tk(). from importlib import import_module +import textwrap import tkinter as tk from tkinter.ttk import Scrollbar tk.NoDefaultRoot() @@ -205,6 +206,19 @@ def _wrapper(parent): # htest # "Check that changes were saved by opening the file elsewhere." } +_linenumbers_drag_scrolling_spec = { + 'file': 'sidebar', + 'kwds': {}, + 'msg': textwrap.dedent("""\ + Click on the line numbers and drag down below the edge of the + window, moving the mouse a bit and then leaving it there for a while. + The text and line numbers should gradually scroll down, with the + selection updated continuously. + Do the same as above, dragging to above the window. The text and line + numbers should gradually scroll up, with the selection updated + continuously."""), + } + _multi_call_spec = { 'file': 'multicall', 'kwds': {}, diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index c6c8e8efcd4f..3ec49e97af6f 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -4,7 +4,7 @@ import unittest import unittest.mock from test.support import requires -from tkinter import Tk, Frame, Text, TclError +from tkinter import NSEW, Tk, Frame, Text, TclError from unittest import mock import re @@ -62,7 +62,7 @@ def setUpClass(cls): text.insert('1.0', code_sample) # Need to pack for creation of code context text widget. frame.pack(side='left', fill='both', expand=1) - text.pack(side='top', fill='both', expand=1) + text.grid(row=1, column=1, sticky=NSEW) cls.editor = DummyEditwin(root, frame, text) codecontext.idleConf.userCfg = testcfg @@ -77,6 +77,7 @@ def tearDownClass(cls): def setUp(self): self.text.yview(0) + self.text['font'] = 'TkFixedFont' self.cc = codecontext.CodeContext(self.editor) self.highlight_cfg = {"background": '#abcdef', @@ -86,10 +87,18 @@ def mock_idleconf_GetHighlight(theme, element): if element == 'context': return self.highlight_cfg return orig_idleConf_GetHighlight(theme, element) - patcher = unittest.mock.patch.object( + GetHighlight_patcher = unittest.mock.patch.object( codecontext.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) - patcher.start() - self.addCleanup(patcher.stop) + GetHighlight_patcher.start() + self.addCleanup(GetHighlight_patcher.stop) + + self.font_override = 'TkFixedFont' + def mock_idleconf_GetFont(root, configType, section): + return self.font_override + GetFont_patcher = unittest.mock.patch.object( + codecontext.idleConf, 'GetFont', mock_idleconf_GetFont) + GetFont_patcher.start() + self.addCleanup(GetFont_patcher.stop) def tearDown(self): if self.cc.context: @@ -339,69 +348,59 @@ def test_timer_event(self, mock_update): def test_font(self): eq = self.assertEqual cc = self.cc - save_font = cc.text['font'] + + orig_font = cc.text['font'] test_font = 'TkTextFont' + self.assertNotEqual(orig_font, test_font) # Ensure code context is not active. if cc.context is not None: cc.toggle_code_context_event() + self.font_override = test_font # Nothing breaks or changes with inactive code context. - cc.update_font(test_font) + cc.update_font() - # Activate code context, but no change to font. - cc.toggle_code_context_event() - eq(cc.context['font'], save_font) - # Call font update with the existing font. - cc.update_font(save_font) - eq(cc.context['font'], save_font) + # Activate code context, previous font change is immediately effective. cc.toggle_code_context_event() - - # Change text widget font and activate code context. - cc.text['font'] = test_font - cc.toggle_code_context_event(test_font) eq(cc.context['font'], test_font) - # Just call the font update. - cc.update_font(save_font) - eq(cc.context['font'], save_font) - cc.text['font'] = save_font + # Call the font update, change is picked up. + self.font_override = orig_font + cc.update_font() + eq(cc.context['font'], orig_font) def test_highlight_colors(self): eq = self.assertEqual cc = self.cc - save_colors = dict(self.highlight_cfg) + + orig_colors = dict(self.highlight_cfg) test_colors = {'background': '#222222', 'foreground': '#ffff00'} + def assert_colors_are_equal(colors): + eq(cc.context['background'], colors['background']) + eq(cc.context['foreground'], colors['foreground']) + # Ensure code context is not active. if cc.context: cc.toggle_code_context_event() + self.highlight_cfg = test_colors # Nothing breaks with inactive code context. cc.update_highlight_colors() - # Activate code context, but no change to colors. + # Activate code context, previous colors change is immediately effective. cc.toggle_code_context_event() - eq(cc.context['background'], save_colors['background']) - eq(cc.context['foreground'], save_colors['foreground']) + assert_colors_are_equal(test_colors) - # Call colors update, but no change to font. + # Call colors update with no change to the configured colors. cc.update_highlight_colors() - eq(cc.context['background'], save_colors['background']) - eq(cc.context['foreground'], save_colors['foreground']) - cc.toggle_code_context_event() - - # Change colors and activate code context. - self.highlight_cfg = test_colors - cc.toggle_code_context_event() - eq(cc.context['background'], test_colors['background']) - eq(cc.context['foreground'], test_colors['foreground']) + assert_colors_are_equal(test_colors) - # Change colors and call highlight colors update. - self.highlight_cfg = save_colors + # Call the colors update with code context active, change is picked up. + self.highlight_cfg = orig_colors cc.update_highlight_colors() - eq(cc.context['background'], save_colors['background']) - eq(cc.context['foreground'], save_colors['foreground']) + assert_colors_are_equal(orig_colors) class HelperFunctionText(unittest.TestCase): diff --git a/Lib/idlelib/idle_test/test_sidebar.py b/Lib/idlelib/idle_test/test_sidebar.py new file mode 100644 index 000000000000..8c98a0c0cbde --- /dev/null +++ b/Lib/idlelib/idle_test/test_sidebar.py @@ -0,0 +1,351 @@ +"""Test sidebar, coverage 93%""" +from itertools import chain +import unittest +import unittest.mock +from test.support import requires +import tkinter as tk + +from idlelib.delegator import Delegator +from idlelib.percolator import Percolator +import idlelib.sidebar + + +class Dummy_editwin: + def __init__(self, text): + self.text = text + self.text_frame = self.text.master + self.per = Percolator(text) + self.undo = Delegator() + self.per.insertfilter(self.undo) + + def setvar(self, name, value): + pass + + def getlineno(self, index): + return int(float(self.text.index(index))) + + +class LineNumbersTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = tk.Tk() + + cls.text_frame = tk.Frame(cls.root) + cls.text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + cls.text_frame.rowconfigure(1, weight=1) + cls.text_frame.columnconfigure(1, weight=1) + + cls.text = tk.Text(cls.text_frame, width=80, height=24, wrap=tk.NONE) + cls.text.grid(row=1, column=1, sticky=tk.NSEW) + + cls.editwin = Dummy_editwin(cls.text) + cls.editwin.vbar = tk.Scrollbar(cls.text_frame) + + @classmethod + def tearDownClass(cls): + cls.editwin.per.close() + cls.root.update() + cls.root.destroy() + del cls.text, cls.text_frame, cls.editwin, cls.root + + def setUp(self): + self.linenumber = idlelib.sidebar.LineNumbers(self.editwin) + + self.highlight_cfg = {"background": '#abcdef', + "foreground": '#123456'} + orig_idleConf_GetHighlight = idlelib.sidebar.idleConf.GetHighlight + def mock_idleconf_GetHighlight(theme, element): + if element == 'linenumber': + return self.highlight_cfg + return orig_idleConf_GetHighlight(theme, element) + GetHighlight_patcher = unittest.mock.patch.object( + idlelib.sidebar.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) + GetHighlight_patcher.start() + self.addCleanup(GetHighlight_patcher.stop) + + self.font_override = 'TkFixedFont' + def mock_idleconf_GetFont(root, configType, section): + return self.font_override + GetFont_patcher = unittest.mock.patch.object( + idlelib.sidebar.idleConf, 'GetFont', mock_idleconf_GetFont) + GetFont_patcher.start() + self.addCleanup(GetFont_patcher.stop) + + def tearDown(self): + self.text.delete('1.0', 'end') + + def get_selection(self): + return tuple(map(str, self.text.tag_ranges('sel'))) + + def get_line_screen_position(self, line): + bbox = self.linenumber.sidebar_text.bbox(f'{line}.end -1c') + x = bbox[0] + 2 + y = bbox[1] + 2 + return x, y + + def assert_state_disabled(self): + state = self.linenumber.sidebar_text.config()['state'] + self.assertEqual(state[-1], tk.DISABLED) + + def get_sidebar_text_contents(self): + return self.linenumber.sidebar_text.get('1.0', tk.END) + + def assert_sidebar_n_lines(self, n_lines): + expected = '\n'.join(chain(map(str, range(1, n_lines + 1)), [''])) + self.assertEqual(self.get_sidebar_text_contents(), expected) + + def assert_text_equals(self, expected): + return self.assertEqual(self.text.get('1.0', 'end'), expected) + + def test_init_empty(self): + self.assert_sidebar_n_lines(1) + + def test_init_not_empty(self): + self.text.insert('insert', 'foo bar\n'*3) + self.assert_text_equals('foo bar\n'*3 + '\n') + self.assert_sidebar_n_lines(4) + + def test_toggle_linenumbering(self): + self.assertEqual(self.linenumber.is_shown, False) + self.linenumber.show_sidebar() + self.assertEqual(self.linenumber.is_shown, True) + self.linenumber.hide_sidebar() + self.assertEqual(self.linenumber.is_shown, False) + self.linenumber.hide_sidebar() + self.assertEqual(self.linenumber.is_shown, False) + self.linenumber.show_sidebar() + self.assertEqual(self.linenumber.is_shown, True) + self.linenumber.show_sidebar() + self.assertEqual(self.linenumber.is_shown, True) + + def test_insert(self): + self.text.insert('insert', 'foobar') + self.assert_text_equals('foobar\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + self.text.insert('insert', '\nfoo') + self.assert_text_equals('foobar\nfoo\n') + self.assert_sidebar_n_lines(2) + self.assert_state_disabled() + + self.text.insert('insert', 'hello\n'*2) + self.assert_text_equals('foobar\nfoohello\nhello\n\n') + self.assert_sidebar_n_lines(4) + self.assert_state_disabled() + + self.text.insert('insert', '\nworld') + self.assert_text_equals('foobar\nfoohello\nhello\n\nworld\n') + self.assert_sidebar_n_lines(5) + self.assert_state_disabled() + + def test_delete(self): + self.text.insert('insert', 'foobar') + self.assert_text_equals('foobar\n') + self.text.delete('1.1', '1.3') + self.assert_text_equals('fbar\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + self.text.insert('insert', 'foo\n'*2) + self.assert_text_equals('fbarfoo\nfoo\n\n') + self.assert_sidebar_n_lines(3) + self.assert_state_disabled() + + # Note: deleting up to "2.end" doesn't delete the final newline. + self.text.delete('2.0', '2.end') + self.assert_text_equals('fbarfoo\n\n\n') + self.assert_sidebar_n_lines(3) + self.assert_state_disabled() + + self.text.delete('1.3', 'end') + self.assert_text_equals('fba\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + # Note: Text widgets always keep a single '\n' character at the end. + self.text.delete('1.0', 'end') + self.assert_text_equals('\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + def test_sidebar_text_width(self): + """ + Test that linenumber text widget is always at the minimum + width + """ + def get_width(): + return self.linenumber.sidebar_text.config()['width'][-1] + + self.assert_sidebar_n_lines(1) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo') + self.assert_sidebar_n_lines(1) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo\n'*8) + self.assert_sidebar_n_lines(9) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(10) + self.assertEqual(get_width(), 2) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(11) + self.assertEqual(get_width(), 2) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(10) + self.assertEqual(get_width(), 2) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(9) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo\n'*90) + self.assert_sidebar_n_lines(99) + self.assertEqual(get_width(), 2) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(100) + self.assertEqual(get_width(), 3) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(101) + self.assertEqual(get_width(), 3) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(100) + self.assertEqual(get_width(), 3) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(99) + self.assertEqual(get_width(), 2) + + self.text.delete('50.0 -1c', 'end -1c') + self.assert_sidebar_n_lines(49) + self.assertEqual(get_width(), 2) + + self.text.delete('5.0 -1c', 'end -1c') + self.assert_sidebar_n_lines(4) + self.assertEqual(get_width(), 1) + + # Note: Text widgets always keep a single '\n' character at the end. + self.text.delete('1.0', 'end -1c') + self.assert_sidebar_n_lines(1) + self.assertEqual(get_width(), 1) + + def test_click_selection(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') + self.root.update() + + # Click on the second line. + x, y = self.get_line_screen_position(2) + self.linenumber.sidebar_text.event_generate('', x=x, y=y) + self.linenumber.sidebar_text.update() + self.root.update() + + self.assertEqual(self.get_selection(), ('2.0', '3.0')) + + def test_drag_selection(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') + self.root.update() + + # Drag from the first line to the third line. + start_x, start_y = self.get_line_screen_position(1) + end_x, end_y = self.get_line_screen_position(3) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.root.update() + + self.assertEqual(self.get_selection(), ('1.0', '4.0')) + + def test_scroll(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'line\n' * 100) + self.root.update() + + # Scroll down 10 lines. + self.text.yview_scroll(10, 'unit') + self.root.update() + self.assertEqual(self.text.index('@0,0'), '11.0') + self.assertEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0') + + # Generate a mouse-wheel event and make sure it scrolled up or down. + # The meaning of the "delta" is OS-dependant, so this just checks for + # any change. + self.linenumber.sidebar_text.event_generate('', + x=0, y=0, + delta=10) + self.root.update() + self.assertNotEqual(self.text.index('@0,0'), '11.0') + self.assertNotEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0') + + def test_font(self): + ln = self.linenumber + + orig_font = ln.sidebar_text['font'] + test_font = 'TkTextFont' + self.assertNotEqual(orig_font, test_font) + + # Ensure line numbers aren't shown. + ln.hide_sidebar() + + self.font_override = test_font + # Nothing breaks when line numbers aren't shown. + ln.update_font() + + # Activate line numbers, previous font change is immediately effective. + ln.show_sidebar() + self.assertEqual(ln.sidebar_text['font'], test_font) + + # Call the font update with line numbers shown, change is picked up. + self.font_override = orig_font + ln.update_font() + self.assertEqual(ln.sidebar_text['font'], orig_font) + + def test_highlight_colors(self): + ln = self.linenumber + + orig_colors = dict(self.highlight_cfg) + test_colors = {'background': '#222222', 'foreground': '#ffff00'} + + def assert_colors_are_equal(colors): + self.assertEqual(ln.sidebar_text['background'], colors['background']) + self.assertEqual(ln.sidebar_text['foreground'], colors['foreground']) + + # Ensure line numbers aren't shown. + ln.hide_sidebar() + + self.highlight_cfg = test_colors + # Nothing breaks with inactive code context. + ln.update_colors() + + # Show line numbers, previous colors change is immediately effective. + ln.show_sidebar() + assert_colors_are_equal(test_colors) + + # Call colors update with no change to the configured colors. + ln.update_colors() + assert_colors_are_equal(test_colors) + + # Call the colors update with line numbers shown, change is picked up. + self.highlight_cfg = orig_colors + ln.update_colors() + assert_colors_are_equal(orig_colors) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py index b0c85cf505c7..fc51fb1b5d34 100644 --- a/Lib/idlelib/mainmenu.py +++ b/Lib/idlelib/mainmenu.py @@ -100,7 +100,8 @@ ('Configure _IDLE', '<>'), None, ('Show _Code Context', '<>'), - ('Zoom Height', '<>'), + ('Show _Line Numbers', '<>'), + ('_Zoom Height', '<>'), ]), ('window', [ diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index 38c59bdf9b4e..90272b6feb4a 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -74,13 +74,11 @@ class OutputWindow(EditorWindow): ("Go to file/line", "<>", None), ] - allow_codecontext = False + allow_code_context = False def __init__(self, *args): EditorWindow.__init__(self, *args) self.text.bind("<>", self.goto_file_line) - self.text.unbind("<>") - self.update_menu_state('options', '*Code Context', 'disabled') # Customize EditorWindow def ispythonsource(self, filename): diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 7ad5a76c3bd5..87401f33f55f 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -861,6 +861,8 @@ class PyShell(OutputWindow): ("Squeeze", "<>"), ] + allow_line_numbers = False + # New classes from idlelib.history import History diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py new file mode 100644 index 000000000000..8f9bf80b5260 --- /dev/null +++ b/Lib/idlelib/sidebar.py @@ -0,0 +1,324 @@ +"""Line numbering implementation for IDLE as an extension. +Includes BaseSideBar which can be extended for other sidebar based extensions +""" +import functools +import itertools + +import tkinter as tk +from idlelib.config import idleConf +from idlelib.delegator import Delegator + + +def get_end_linenumber(text): + """Utility to get the last line's number in a Tk text widget.""" + return int(float(text.index('end-1c'))) + + +def get_widget_padding(widget): + """Get the total padding of a Tk widget, including its border.""" + # TODO: use also in codecontext.py + manager = widget.winfo_manager() + if manager == 'pack': + info = widget.pack_info() + elif manager == 'grid': + info = widget.grid_info() + else: + raise ValueError(f"Unsupported geometry manager: {manager}") + + # All values are passed through getint(), since some + # values may be pixel objects, which can't simply be added to ints. + padx = sum(map(widget.tk.getint, [ + info['padx'], + widget.cget('padx'), + widget.cget('border'), + ])) + pady = sum(map(widget.tk.getint, [ + info['pady'], + widget.cget('pady'), + widget.cget('border'), + ])) + return padx, pady + + +class BaseSideBar: + """ + The base class for extensions which require a sidebar. + """ + def __init__(self, editwin): + self.editwin = editwin + self.parent = editwin.text_frame + self.text = editwin.text + + _padx, pady = get_widget_padding(self.text) + self.sidebar_text = tk.Text(self.parent, width=1, wrap=tk.NONE, + padx=0, pady=pady, + borderwidth=0, highlightthickness=0) + self.sidebar_text.config(state=tk.DISABLED) + self.text['yscrollcommand'] = self.redirect_yscroll_event + self.update_font() + self.update_colors() + + self.is_shown = False + + def update_font(self): + """Update the sidebar text font, usually after config changes.""" + font = idleConf.GetFont(self.text, 'main', 'EditorWindow') + self._update_font(font) + + def _update_font(self, font): + self.sidebar_text['font'] = font + + def update_colors(self): + """Update the sidebar text colors, usually after config changes.""" + colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'normal') + self._update_colors(foreground=colors['foreground'], + background=colors['background']) + + def _update_colors(self, foreground, background): + self.sidebar_text.config( + fg=foreground, bg=background, + selectforeground=foreground, selectbackground=background, + inactiveselectbackground=background, + ) + + def show_sidebar(self): + if not self.is_shown: + self.sidebar_text.grid(row=1, column=0, sticky=tk.NSEW) + self.is_shown = True + + def hide_sidebar(self): + if self.is_shown: + self.sidebar_text.grid_forget() + self.is_shown = False + + def redirect_yscroll_event(self, *args, **kwargs): + """Redirect vertical scrolling to the main editor text widget. + + The scroll bar is also updated. + """ + self.editwin.vbar.set(*args) + self.sidebar_text.yview_moveto(args[0]) + return 'break' + + def redirect_focusin_event(self, event): + """Redirect focus-in events to the main editor text widget.""" + self.text.focus_set() + return 'break' + + def redirect_mousebutton_event(self, event, event_name): + """Redirect mouse button events to the main editor text widget.""" + self.text.focus_set() + self.text.event_generate(event_name, x=0, y=event.y) + return 'break' + + def redirect_mousewheel_event(self, event): + """Redirect mouse wheel events to the editwin text widget.""" + self.text.event_generate('', + x=0, y=event.y, delta=event.delta) + return 'break' + + +class EndLineDelegator(Delegator): + """Generate callbacks with the current end line number after + insert or delete operations""" + def __init__(self, changed_callback): + """ + changed_callback - Callable, will be called after insert + or delete operations with the current + end line number. + """ + Delegator.__init__(self) + self.changed_callback = changed_callback + + def insert(self, index, chars, tags=None): + self.delegate.insert(index, chars, tags) + self.changed_callback(get_end_linenumber(self.delegate)) + + def delete(self, index1, index2=None): + self.delegate.delete(index1, index2) + self.changed_callback(get_end_linenumber(self.delegate)) + + +class LineNumbers(BaseSideBar): + """Line numbers support for editor windows.""" + def __init__(self, editwin): + BaseSideBar.__init__(self, editwin) + self.prev_end = 1 + self._sidebar_width_type = type(self.sidebar_text['width']) + self.sidebar_text.config(state=tk.NORMAL) + self.sidebar_text.insert('insert', '1', 'linenumber') + self.sidebar_text.config(state=tk.DISABLED) + self.sidebar_text.config(takefocus=False, exportselection=False) + self.sidebar_text.tag_config('linenumber', justify=tk.RIGHT) + + self.bind_events() + + end = get_end_linenumber(self.text) + self.update_sidebar_text(end) + + end_line_delegator = EndLineDelegator(self.update_sidebar_text) + # Insert the delegator after the undo delegator, so that line numbers + # are properly updated after undo and redo actions. + end_line_delegator.setdelegate(self.editwin.undo.delegate) + self.editwin.undo.setdelegate(end_line_delegator) + # Reset the delegator caches of the delegators "above" the + # end line delegator we just inserted. + delegator = self.editwin.per.top + while delegator is not end_line_delegator: + delegator.resetcache() + delegator = delegator.delegate + + self.is_shown = False + + def bind_events(self): + # Ensure focus is always redirected to the main editor text widget. + self.sidebar_text.bind('', self.redirect_focusin_event) + + # Redirect mouse scrolling to the main editor text widget. + # + # Note that without this, scrolling with the mouse only scrolls + # the line numbers. + self.sidebar_text.bind('', self.redirect_mousewheel_event) + + # Redirect mouse button events to the main editor text widget, + # except for the left mouse button (1). + # + # Note: X-11 sends Button-4 and Button-5 events for the scroll wheel. + def bind_mouse_event(event_name, target_event_name): + handler = functools.partial(self.redirect_mousebutton_event, + event_name=target_event_name) + self.sidebar_text.bind(event_name, handler) + + for button in [2, 3, 4, 5]: + for event_name in (f'', + f'', + f'', + ): + bind_mouse_event(event_name, target_event_name=event_name) + + # Convert double- and triple-click events to normal click events, + # since event_generate() doesn't allow generating such events. + for event_name in (f'', + f'', + ): + bind_mouse_event(event_name, + target_event_name=f'') + + start_line = None + def b1_mousedown_handler(event): + # select the entire line + lineno = self.editwin.getlineno(f"@0,{event.y}") + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", f"{lineno}.0", f"{lineno+1}.0") + self.text.mark_set("insert", f"{lineno+1}.0") + + # remember this line in case this is the beginning of dragging + nonlocal start_line + start_line = lineno + self.sidebar_text.bind('', b1_mousedown_handler) + + # These are set by b1_motion_handler() and read by selection_handler(); + # see below. last_y is passed this way since the mouse Y-coordinate + # is not available on selection event objects. last_yview is passed + # this way to recognize scrolling while the mouse isn't moving. + last_y = last_yview = None + + def drag_update_selection_and_insert_mark(y_coord): + """Helper function for drag and selection event handlers.""" + lineno = self.editwin.getlineno(f"@0,{y_coord}") + a, b = sorted([start_line, lineno]) + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", f"{a}.0", f"{b+1}.0") + self.text.mark_set("insert", + f"{lineno if lineno == a else lineno + 1}.0") + + # Special handling of dragging with mouse button 1. In "normal" text + # widgets this selects text, but the line numbers text widget has + # selection disabled. Still, dragging triggers some selection-related + # functionality under the hood. Specifically, dragging to above or + # below the text widget triggers scrolling, in a way that bypasses the + # other scrolling synchronization mechanisms.i + def b1_drag_handler(event, *args): + nonlocal last_y + nonlocal last_yview + last_y = event.y + last_yview = self.sidebar_text.yview() + if not 0 <= last_y <= self.sidebar_text.winfo_height(): + self.text.yview_moveto(last_yview[0]) + drag_update_selection_and_insert_mark(event.y) + self.sidebar_text.bind('', b1_drag_handler) + + # With mouse-drag scrolling fixed by the above, there is still an edge- + # case we need to handle: When drag-scrolling, scrolling can continue + # while the mouse isn't moving, leading to the above fix not scrolling + # properly. + def selection_handler(event): + yview = self.sidebar_text.yview() + if yview != last_yview: + self.text.yview_moveto(yview[0]) + drag_update_selection_and_insert_mark(last_y) + self.sidebar_text.bind('<>', selection_handler) + + def update_colors(self): + """Update the sidebar text colors, usually after config changes.""" + colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'linenumber') + self._update_colors(foreground=colors['foreground'], + background=colors['background']) + + def update_sidebar_text(self, end): + """ + Perform the following action: + Each line sidebar_text contains the linenumber for that line + Synchronize with editwin.text so that both sidebar_text and + editwin.text contain the same number of lines""" + if end == self.prev_end: + return + + width_difference = len(str(end)) - len(str(self.prev_end)) + if width_difference: + cur_width = int(float(self.sidebar_text['width'])) + new_width = cur_width + width_difference + self.sidebar_text['width'] = self._sidebar_width_type(new_width) + + self.sidebar_text.config(state=tk.NORMAL) + if end > self.prev_end: + new_text = '\n'.join(itertools.chain( + [''], + map(str, range(self.prev_end + 1, end + 1)), + )) + self.sidebar_text.insert(f'end -1c', new_text, 'linenumber') + else: + self.sidebar_text.delete(f'{end+1}.0 -1c', 'end -1c') + self.sidebar_text.config(state=tk.DISABLED) + + self.prev_end = end + + +def _linenumbers_drag_scrolling(parent): # htest # + from idlelib.idle_test.test_sidebar import Dummy_editwin + + toplevel = tk.Toplevel(parent) + text_frame = tk.Frame(toplevel) + text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + text_frame.rowconfigure(1, weight=1) + text_frame.columnconfigure(1, weight=1) + + font = idleConf.GetFont(toplevel, 'main', 'EditorWindow') + text = tk.Text(text_frame, width=80, height=24, wrap=tk.NONE, font=font) + text.grid(row=1, column=1, sticky=tk.NSEW) + + editwin = Dummy_editwin(text) + editwin.vbar = tk.Scrollbar(text_frame) + + linenumbers = LineNumbers(editwin) + linenumbers.show_sidebar() + + text.insert('1.0', '\n'.join('a'*i for i in range(1, 101))) + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_sidebar', verbosity=2, exit=False) + + from idlelib.idle_test.htest import run + run(_linenumbers_drag_scrolling) diff --git a/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst b/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst new file mode 100644 index 000000000000..201a413b42a3 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst @@ -0,0 +1,4 @@ +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. + From webhook-mailer at python.org Tue Jul 23 09:27:22 2019 From: webhook-mailer at python.org (Tal Einat) Date: Tue, 23 Jul 2019 13:27:22 -0000 Subject: [Python-checkins] [3.7] bpo-17535: IDLE editor line numbers (GH-14030) Message-ID: https://github.com/python/cpython/commit/e9ec1663d8fa0b3829add14e784107482af8dacb commit: e9ec1663d8fa0b3829add14e784107482af8dacb branch: 3.7 author: Tal Einat committer: GitHub date: 2019-07-23T16:27:04+03:00 summary: [3.7] bpo-17535: IDLE editor line numbers (GH-14030) (cherry picked from commit 7123ea009b0b004062d91f69859bddf422c34ab4) files: A Lib/idlelib/idle_test/test_sidebar.py A Lib/idlelib/sidebar.py A Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst M Doc/library/idle.rst M Doc/whatsnew/3.7.rst M Lib/idlelib/codecontext.py M Lib/idlelib/config-highlight.def M Lib/idlelib/config-main.def M Lib/idlelib/config.py M Lib/idlelib/configdialog.py M Lib/idlelib/editor.py M Lib/idlelib/help.html M Lib/idlelib/idle_test/htest.py M Lib/idlelib/idle_test/test_codecontext.py M Lib/idlelib/mainmenu.py M Lib/idlelib/outwin.py M Lib/idlelib/pyshell.py diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index de58f266bf5e..5975e3bb5806 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -290,22 +290,31 @@ Options menu (Shell and Editor) Configure IDLE Open a configuration dialog and change preferences for the following: fonts, indentation, keybindings, text color themes, startup windows and - size, additional help sources, and extensions. On macOS, open the + size, additional help sources, and extensions. On macOS, open the configuration dialog by selecting Preferences in the application - menu. For more, see + menu. For more details, see :ref:`Setting preferences ` under Help and preferences. + Most configuration options apply to all windows or all future windows. + The option items below only apply to the active window. + Show/Hide Code Context (Editor Window only) Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window. See - :ref:`Code Context ` in the Editing and Navigation section below. + :ref:`Code Context ` in the Editing and Navigation section + below. + +Show/Hide Line Numbers (Editor Window only) + Open a column to the left of the edit window which shows the number + of each line of text. The default is off, which may be changed in the + preferences (see :ref:`Setting preferences `). Zoom/Restore Height Toggles the window between normal size and maximum height. The initial size defaults to 40 lines by 80 chars unless changed on the General tab of the Configure IDLE dialog. The maximum height for a screen is determined by momentarily maximizing a window the first time one is zoomed on the screen. - Changing screen settings may invalidate the saved height. This toogle has + Changing screen settings may invalidate the saved height. This toggle has no effect when a window is maximized. Window menu (Shell and Editor) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index e78af225d385..934a5693ebf2 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1017,6 +1017,13 @@ by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.) The changes above have been backported to 3.6 maintenance releases. +New in 3.7.5: + +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. +(Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) + importlib --------- diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 3103391cfe58..4ce98136fe41 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -13,7 +13,7 @@ from sys import maxsize as INFINITY import tkinter -from tkinter.constants import TOP, X, SUNKEN +from tkinter.constants import NSEW, SUNKEN from idlelib.config import idleConf @@ -67,6 +67,7 @@ def __init__(self, editwin): def _reset(self): self.context = None + self.cell00 = None self.t1 = None self.topvisible = 1 self.info = [(0, -1, "", False)] @@ -105,25 +106,37 @@ def toggle_code_context_event(self, event=None): padx = 0 border = 0 for widget in widgets: - padx += widget.tk.getint(widget.pack_info()['padx']) + info = (widget.grid_info() + if widget is self.editwin.text + else widget.pack_info()) + padx += widget.tk.getint(info['padx']) padx += widget.tk.getint(widget.cget('padx')) border += widget.tk.getint(widget.cget('border')) self.context = tkinter.Text( - self.editwin.top, font=self.text['font'], + self.editwin.text_frame, height=1, width=1, # Don't request more than we get. + highlightthickness=0, padx=padx, border=border, relief=SUNKEN, state='disabled') + self.update_font() self.update_highlight_colors() self.context.bind('', self.jumptoline) # Get the current context and initiate the recurring update event. self.timer_event() - # Pack the context widget before and above the text_frame widget, - # thus ensuring that it will appear directly above text_frame. - self.context.pack(side=TOP, fill=X, expand=False, - before=self.editwin.text_frame) + # Grid the context widget above the text widget. + self.context.grid(row=0, column=1, sticky=NSEW) + + line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(), + 'linenumber') + self.cell00 = tkinter.Frame(self.editwin.text_frame, + bg=line_number_colors['background']) + self.cell00.grid(row=0, column=0, sticky=NSEW) menu_status = 'Hide' else: self.context.destroy() + self.context = None + self.cell00.destroy() + self.cell00 = None self.text.after_cancel(self.t1) self._reset() menu_status = 'Show' @@ -221,8 +234,9 @@ def timer_event(self): self.update_code_context() self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event) - def update_font(self, font): + def update_font(self): if self.context is not None: + font = idleConf.GetFont(self.text, 'main', 'EditorWindow') self.context['font'] = font def update_highlight_colors(self): @@ -231,6 +245,11 @@ def update_highlight_colors(self): self.context['background'] = colors['background'] self.context['foreground'] = colors['foreground'] + if self.cell00 is not None: + line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(), + 'linenumber') + self.cell00.config(bg=line_number_colors['background']) + CodeContext.reload() diff --git a/Lib/idlelib/config-highlight.def b/Lib/idlelib/config-highlight.def index aaa2b57a7f6d..a7b0433831c5 100644 --- a/Lib/idlelib/config-highlight.def +++ b/Lib/idlelib/config-highlight.def @@ -22,6 +22,10 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 +context-foreground= #000000 +context-background= lightgray +linenumber-foreground= gray +linenumber-background= #ffffff #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -31,8 +35,6 @@ stderr-foreground= red stderr-background= #ffffff console-foreground= #770000 console-background= #ffffff -context-foreground= #000000 -context-background= lightgray [IDLE New] normal-foreground= #000000 @@ -55,6 +57,10 @@ hit-foreground= #ffffff hit-background= #000000 error-foreground= #000000 error-background= #ff7777 +context-foreground= #000000 +context-background= lightgray +linenumber-foreground= gray +linenumber-background= #ffffff #cursor (only foreground can be set, restart IDLE) cursor-foreground= black #shell window @@ -64,8 +70,6 @@ stderr-foreground= red stderr-background= #ffffff console-foreground= #770000 console-background= #ffffff -context-foreground= #000000 -context-background= lightgray [IDLE Dark] comment-foreground = #dd0000 @@ -97,3 +101,5 @@ comment-background = #002240 break-foreground = #FFFFFF context-foreground= #ffffff context-background= #454545 +linenumber-foreground= gray +linenumber-background= #002240 diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 06e3c5adb0e3..b2be6250d021 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -36,7 +36,7 @@ # Additional help sources are listed in the [HelpFiles] section below # and should be viewable by a web browser (or the Windows Help viewer in # the case of .chm files). These sources will be listed on the Help -# menu. The pattern, and two examples, are +# menu. The pattern, and two examples, are: # # # 1 = IDLE;C:/Programs/Python36/Lib/idlelib/help.html @@ -46,7 +46,7 @@ # platform specific because of path separators, drive specs etc. # # The default files should not be edited except to add new sections to -# config-extensions.def for added extensions . The user files should be +# config-extensions.def for added extensions. The user files should be # modified through the Settings dialog. [General] @@ -65,6 +65,7 @@ font= TkFixedFont font-size= 10 font-bold= 0 encoding= none +line-numbers-default= 0 [PyShell] auto-squeeze-min-lines= 50 diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 0c55c9a7d75d..683b000a488b 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -319,6 +319,10 @@ def GetThemeDict(self, type, themeName): 'hit-background':'#000000', 'error-foreground':'#ffffff', 'error-background':'#000000', + 'context-foreground':'#000000', + 'context-background':'#ffffff', + 'linenumber-foreground':'#000000', + 'linenumber-background':'#ffffff', #cursor (only foreground can be set) 'cursor-foreground':'#000000', #shell window @@ -328,11 +332,11 @@ def GetThemeDict(self, type, themeName): 'stderr-background':'#ffffff', 'console-foreground':'#000000', 'console-background':'#ffffff', - 'context-foreground':'#000000', - 'context-background':'#ffffff', } for element in theme: - if not cfgParser.has_option(themeName, element): + if not (cfgParser.has_option(themeName, element) or + # Skip warning for new elements. + element.startswith(('context-', 'linenumber-'))): # Print warning that will return a default color warning = ('\n Warning: config.IdleConf.GetThemeDict' ' -\n problem retrieving theme element %r' diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 6b0ecc9d68e5..217f8fd0a5fb 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -819,6 +819,7 @@ def create_page_highlight(self): 'Shell Error Text': ('error', '12'), 'Shell Stdout Text': ('stdout', '13'), 'Shell Stderr Text': ('stderr', '14'), + 'Line Number': ('linenumber', '16'), } self.builtin_name = tracers.add( StringVar(self), self.var_changed_builtin_name) @@ -866,6 +867,11 @@ def create_page_highlight(self): ('stderr', 'stderr'), ('\n\n', 'normal')) for texttag in text_and_tags: text.insert(END, texttag[0], texttag[1]) + n_lines = len(text.get('1.0', END).splitlines()) + for lineno in range(1, n_lines + 1): + text.insert(f'{lineno}.0', + f'{lineno:{len(str(n_lines))}d} ', + 'linenumber') for element in self.theme_elements: def tem(event, elem=element): # event.widget.winfo_top_level().highlight_target.set(elem) @@ -1827,6 +1833,9 @@ def create_page_general(self): frame_format: Frame format_width_title: Label (*)format_width_int: Entry - format_width + frame_line_numbers_default: Frame + line_numbers_default_title: Label + (*)line_numbers_default_bool: Checkbutton - line_numbers_default frame_context: Frame context_title: Label (*)context_int: Entry - context_lines @@ -1866,6 +1875,9 @@ def create_page_general(self): IntVar(self), ('main', 'General', 'autosave')) self.format_width = tracers.add( StringVar(self), ('extensions', 'FormatParagraph', 'max-width')) + self.line_numbers_default = tracers.add( + BooleanVar(self), + ('main', 'EditorWindow', 'line-numbers-default')) self.context_lines = tracers.add( StringVar(self), ('extensions', 'CodeContext', 'maxlines')) @@ -1944,6 +1956,14 @@ def create_page_general(self): validatecommand=self.digits_only, validate='key', ) + frame_line_numbers_default = Frame(frame_editor, borderwidth=0) + line_numbers_default_title = Label( + frame_line_numbers_default, text='Show line numbers in new windows') + self.line_numbers_default_bool = Checkbutton( + frame_line_numbers_default, + variable=self.line_numbers_default, + width=1) + frame_context = Frame(frame_editor, borderwidth=0) context_title = Label(frame_context, text='Max Context Lines :') self.context_int = Entry( @@ -2021,6 +2041,10 @@ def create_page_general(self): frame_format.pack(side=TOP, padx=5, pady=0, fill=X) format_width_title.pack(side=LEFT, anchor=W, padx=5, pady=5) self.format_width_int.pack(side=TOP, padx=10, pady=5) + # frame_line_numbers_default. + frame_line_numbers_default.pack(side=TOP, padx=5, pady=0, fill=X) + line_numbers_default_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.line_numbers_default_bool.pack(side=LEFT, padx=5, pady=5) # frame_context. frame_context.pack(side=TOP, padx=5, pady=0, fill=X) context_title.pack(side=LEFT, anchor=W, padx=5, pady=5) @@ -2063,6 +2087,8 @@ def load_general_cfg(self): 'main', 'General', 'autosave', default=0, type='bool')) self.format_width.set(idleConf.GetOption( 'extensions', 'FormatParagraph', 'max-width', type='int')) + self.line_numbers_default.set(idleConf.GetOption( + 'main', 'EditorWindow', 'line-numbers-default', type='bool')) self.context_lines.set(idleConf.GetOption( 'extensions', 'CodeContext', 'maxlines', type='int')) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 24b1ffc67975..497ee12f1814 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -53,6 +53,7 @@ class EditorWindow(object): from idlelib.autoexpand import AutoExpand from idlelib.calltip import Calltip from idlelib.codecontext import CodeContext + from idlelib.sidebar import LineNumbers from idlelib.format import FormatParagraph, FormatRegion, Indents, Rstrip from idlelib.parenmatch import ParenMatch from idlelib.squeezer import Squeezer @@ -61,7 +62,8 @@ class EditorWindow(object): filesystemencoding = sys.getfilesystemencoding() # for file names help_url = None - allow_codecontext = True + allow_code_context = True + allow_line_numbers = True def __init__(self, flist=None, filename=None, key=None, root=None): # Delay import: runscript imports pyshell imports EditorWindow. @@ -198,12 +200,14 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", self.open_turtle_demo) self.set_status_bar() + text_frame.pack(side=LEFT, fill=BOTH, expand=1) + text_frame.rowconfigure(1, weight=1) + text_frame.columnconfigure(1, weight=1) vbar['command'] = self.handle_yview - vbar.pack(side=RIGHT, fill=Y) + vbar.grid(row=1, column=2, sticky=NSEW) text['yscrollcommand'] = vbar.set text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow') - text_frame.pack(side=LEFT, fill=BOTH, expand=1) - text.pack(side=TOP, fill=BOTH, expand=1) + text.grid(row=1, column=1, sticky=NSEW) text.focus_set() # usetabs true -> literal tab characters are used by indent and @@ -250,7 +254,8 @@ def __init__(self, flist=None, filename=None, key=None, root=None): self.good_load = False self.set_indentation_params(False) self.color = None # initialized below in self.ResetColorizer - self.codecontext = None + self.code_context = None # optionally initialized later below + self.line_numbers = None # optionally initialized later below if filename: if os.path.exists(filename) and not os.path.isdir(filename): if io.loadfile(filename): @@ -316,10 +321,20 @@ def __init__(self, flist=None, filename=None, key=None, root=None): text.bind("<>", ctip.refresh_calltip_event) text.bind("<>", ctip.force_open_calltip_event) text.bind("<>", self.ZoomHeight(self).zoom_height_event) - if self.allow_codecontext: - self.codecontext = self.CodeContext(self) + if self.allow_code_context: + self.code_context = self.CodeContext(self) text.bind("<>", - self.codecontext.toggle_code_context_event) + self.code_context.toggle_code_context_event) + else: + self.update_menu_state('options', '*Code Context', 'disabled') + if self.allow_line_numbers: + self.line_numbers = self.LineNumbers(self) + if idleConf.GetOption('main', 'EditorWindow', + 'line-numbers-default', type='bool'): + self.toggle_line_numbers_event() + text.bind("<>", self.toggle_line_numbers_event) + else: + self.update_menu_state('options', '*Line Numbers', 'disabled') def _filename_to_unicode(self, filename): """Return filename as BMP unicode so displayable in Tk.""" @@ -779,8 +794,11 @@ def ResetColorizer(self): self._addcolorizer() EditorWindow.color_config(self.text) - if self.codecontext is not None: - self.codecontext.update_highlight_colors() + if self.code_context is not None: + self.code_context.update_highlight_colors() + + if self.line_numbers is not None: + self.line_numbers.update_colors() IDENTCHARS = string.ascii_letters + string.digits + "_" @@ -799,11 +817,16 @@ def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configdialog.py - new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow') # Update the code context widget first, since its height affects # the height of the text widget. This avoids double re-rendering. - if self.codecontext is not None: - self.codecontext.update_font(new_font) + if self.code_context is not None: + self.code_context.update_font() + # Next, update the line numbers widget, since its width affects + # the width of the text widget. + if self.line_numbers is not None: + self.line_numbers.update_font() + # Finally, update the main text widget. + new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow') self.text['font'] = new_font def RemoveKeybindings(self): @@ -1467,6 +1490,19 @@ def guess_indent(self): indentsmall = indentlarge = 0 return indentlarge - indentsmall + def toggle_line_numbers_event(self, event=None): + if self.line_numbers is None: + return + + if self.line_numbers.is_shown: + self.line_numbers.hide_sidebar() + menu_label = "Show" + else: + self.line_numbers.show_sidebar() + menu_label = "Hide" + self.update_menu_label(menu='options', index='*Line Numbers', + label=f'{menu_label} Line Numbers') + # "line.col" -> line, as an int def index2line(index): return int(float(index)) diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index cee6887df68f..49068df7a8b9 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -271,10 +271,15 @@

    Options menu (Shell and Editor)Setting preferences under Help and preferences.

    +Setting preferences under Help and preferences. +Most configuration options apply to all windows or all future windows. +The option items below only apply to the active window.
    Show/Hide Code Context (Editor Window only)
    Open a pane at the top of the edit window which shows the block context of the code which has scrolled above the top of the window. See Code Context in the Editing and Navigation section below.
    +
    Line Numbers (Editor Window only)
    Open a column to the left of the edit window which shows the linenumber +of each line of text. The default is off unless configured on +(see Setting preferences).
    Zoom/Restore Height
    Toggles the window between normal size and maximum height. The initial size defaults to 40 lines by 80 chars unless changed on the General tab of the Configure IDLE dialog. The maximum height for a screen is determined by @@ -607,7 +612,7 @@

    Running user codesys.getrecursionlimit and -sys.setrecursionlimit to reduce their visibility.

    +sys.setrecursionlimit to reduce the effect of the additional stack frames.

    If sys is reset by user code, such as with importlib.reload(sys), IDLE?s changes are lost and input from the keyboard and output to the screen will not work correctly.

    @@ -895,7 +900,7 @@

    Navigation



    - Last updated on Jul 04, 2019. + Last updated on Jul 23, 2019.
    Found a bug?
    diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 20e5e9014ee7..f2f37e161632 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -67,6 +67,7 @@ def _wrapper(parent): # htest # import idlelib.pyshell # Set Windows DPI awareness before Tk(). from importlib import import_module +import textwrap import tkinter as tk from tkinter.ttk import Scrollbar tk.NoDefaultRoot() @@ -205,6 +206,19 @@ def _wrapper(parent): # htest # "Check that changes were saved by opening the file elsewhere." } +_linenumbers_drag_scrolling_spec = { + 'file': 'sidebar', + 'kwds': {}, + 'msg': textwrap.dedent("""\ + Click on the line numbers and drag down below the edge of the + window, moving the mouse a bit and then leaving it there for a while. + The text and line numbers should gradually scroll down, with the + selection updated continuously. + Do the same as above, dragging to above the window. The text and line + numbers should gradually scroll up, with the selection updated + continuously."""), + } + _multi_call_spec = { 'file': 'multicall', 'kwds': {}, diff --git a/Lib/idlelib/idle_test/test_codecontext.py b/Lib/idlelib/idle_test/test_codecontext.py index c6c8e8efcd4f..3ec49e97af6f 100644 --- a/Lib/idlelib/idle_test/test_codecontext.py +++ b/Lib/idlelib/idle_test/test_codecontext.py @@ -4,7 +4,7 @@ import unittest import unittest.mock from test.support import requires -from tkinter import Tk, Frame, Text, TclError +from tkinter import NSEW, Tk, Frame, Text, TclError from unittest import mock import re @@ -62,7 +62,7 @@ def setUpClass(cls): text.insert('1.0', code_sample) # Need to pack for creation of code context text widget. frame.pack(side='left', fill='both', expand=1) - text.pack(side='top', fill='both', expand=1) + text.grid(row=1, column=1, sticky=NSEW) cls.editor = DummyEditwin(root, frame, text) codecontext.idleConf.userCfg = testcfg @@ -77,6 +77,7 @@ def tearDownClass(cls): def setUp(self): self.text.yview(0) + self.text['font'] = 'TkFixedFont' self.cc = codecontext.CodeContext(self.editor) self.highlight_cfg = {"background": '#abcdef', @@ -86,10 +87,18 @@ def mock_idleconf_GetHighlight(theme, element): if element == 'context': return self.highlight_cfg return orig_idleConf_GetHighlight(theme, element) - patcher = unittest.mock.patch.object( + GetHighlight_patcher = unittest.mock.patch.object( codecontext.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) - patcher.start() - self.addCleanup(patcher.stop) + GetHighlight_patcher.start() + self.addCleanup(GetHighlight_patcher.stop) + + self.font_override = 'TkFixedFont' + def mock_idleconf_GetFont(root, configType, section): + return self.font_override + GetFont_patcher = unittest.mock.patch.object( + codecontext.idleConf, 'GetFont', mock_idleconf_GetFont) + GetFont_patcher.start() + self.addCleanup(GetFont_patcher.stop) def tearDown(self): if self.cc.context: @@ -339,69 +348,59 @@ def test_timer_event(self, mock_update): def test_font(self): eq = self.assertEqual cc = self.cc - save_font = cc.text['font'] + + orig_font = cc.text['font'] test_font = 'TkTextFont' + self.assertNotEqual(orig_font, test_font) # Ensure code context is not active. if cc.context is not None: cc.toggle_code_context_event() + self.font_override = test_font # Nothing breaks or changes with inactive code context. - cc.update_font(test_font) + cc.update_font() - # Activate code context, but no change to font. - cc.toggle_code_context_event() - eq(cc.context['font'], save_font) - # Call font update with the existing font. - cc.update_font(save_font) - eq(cc.context['font'], save_font) + # Activate code context, previous font change is immediately effective. cc.toggle_code_context_event() - - # Change text widget font and activate code context. - cc.text['font'] = test_font - cc.toggle_code_context_event(test_font) eq(cc.context['font'], test_font) - # Just call the font update. - cc.update_font(save_font) - eq(cc.context['font'], save_font) - cc.text['font'] = save_font + # Call the font update, change is picked up. + self.font_override = orig_font + cc.update_font() + eq(cc.context['font'], orig_font) def test_highlight_colors(self): eq = self.assertEqual cc = self.cc - save_colors = dict(self.highlight_cfg) + + orig_colors = dict(self.highlight_cfg) test_colors = {'background': '#222222', 'foreground': '#ffff00'} + def assert_colors_are_equal(colors): + eq(cc.context['background'], colors['background']) + eq(cc.context['foreground'], colors['foreground']) + # Ensure code context is not active. if cc.context: cc.toggle_code_context_event() + self.highlight_cfg = test_colors # Nothing breaks with inactive code context. cc.update_highlight_colors() - # Activate code context, but no change to colors. + # Activate code context, previous colors change is immediately effective. cc.toggle_code_context_event() - eq(cc.context['background'], save_colors['background']) - eq(cc.context['foreground'], save_colors['foreground']) + assert_colors_are_equal(test_colors) - # Call colors update, but no change to font. + # Call colors update with no change to the configured colors. cc.update_highlight_colors() - eq(cc.context['background'], save_colors['background']) - eq(cc.context['foreground'], save_colors['foreground']) - cc.toggle_code_context_event() - - # Change colors and activate code context. - self.highlight_cfg = test_colors - cc.toggle_code_context_event() - eq(cc.context['background'], test_colors['background']) - eq(cc.context['foreground'], test_colors['foreground']) + assert_colors_are_equal(test_colors) - # Change colors and call highlight colors update. - self.highlight_cfg = save_colors + # Call the colors update with code context active, change is picked up. + self.highlight_cfg = orig_colors cc.update_highlight_colors() - eq(cc.context['background'], save_colors['background']) - eq(cc.context['foreground'], save_colors['foreground']) + assert_colors_are_equal(orig_colors) class HelperFunctionText(unittest.TestCase): diff --git a/Lib/idlelib/idle_test/test_sidebar.py b/Lib/idlelib/idle_test/test_sidebar.py new file mode 100644 index 000000000000..8c98a0c0cbde --- /dev/null +++ b/Lib/idlelib/idle_test/test_sidebar.py @@ -0,0 +1,351 @@ +"""Test sidebar, coverage 93%""" +from itertools import chain +import unittest +import unittest.mock +from test.support import requires +import tkinter as tk + +from idlelib.delegator import Delegator +from idlelib.percolator import Percolator +import idlelib.sidebar + + +class Dummy_editwin: + def __init__(self, text): + self.text = text + self.text_frame = self.text.master + self.per = Percolator(text) + self.undo = Delegator() + self.per.insertfilter(self.undo) + + def setvar(self, name, value): + pass + + def getlineno(self, index): + return int(float(self.text.index(index))) + + +class LineNumbersTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = tk.Tk() + + cls.text_frame = tk.Frame(cls.root) + cls.text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + cls.text_frame.rowconfigure(1, weight=1) + cls.text_frame.columnconfigure(1, weight=1) + + cls.text = tk.Text(cls.text_frame, width=80, height=24, wrap=tk.NONE) + cls.text.grid(row=1, column=1, sticky=tk.NSEW) + + cls.editwin = Dummy_editwin(cls.text) + cls.editwin.vbar = tk.Scrollbar(cls.text_frame) + + @classmethod + def tearDownClass(cls): + cls.editwin.per.close() + cls.root.update() + cls.root.destroy() + del cls.text, cls.text_frame, cls.editwin, cls.root + + def setUp(self): + self.linenumber = idlelib.sidebar.LineNumbers(self.editwin) + + self.highlight_cfg = {"background": '#abcdef', + "foreground": '#123456'} + orig_idleConf_GetHighlight = idlelib.sidebar.idleConf.GetHighlight + def mock_idleconf_GetHighlight(theme, element): + if element == 'linenumber': + return self.highlight_cfg + return orig_idleConf_GetHighlight(theme, element) + GetHighlight_patcher = unittest.mock.patch.object( + idlelib.sidebar.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) + GetHighlight_patcher.start() + self.addCleanup(GetHighlight_patcher.stop) + + self.font_override = 'TkFixedFont' + def mock_idleconf_GetFont(root, configType, section): + return self.font_override + GetFont_patcher = unittest.mock.patch.object( + idlelib.sidebar.idleConf, 'GetFont', mock_idleconf_GetFont) + GetFont_patcher.start() + self.addCleanup(GetFont_patcher.stop) + + def tearDown(self): + self.text.delete('1.0', 'end') + + def get_selection(self): + return tuple(map(str, self.text.tag_ranges('sel'))) + + def get_line_screen_position(self, line): + bbox = self.linenumber.sidebar_text.bbox(f'{line}.end -1c') + x = bbox[0] + 2 + y = bbox[1] + 2 + return x, y + + def assert_state_disabled(self): + state = self.linenumber.sidebar_text.config()['state'] + self.assertEqual(state[-1], tk.DISABLED) + + def get_sidebar_text_contents(self): + return self.linenumber.sidebar_text.get('1.0', tk.END) + + def assert_sidebar_n_lines(self, n_lines): + expected = '\n'.join(chain(map(str, range(1, n_lines + 1)), [''])) + self.assertEqual(self.get_sidebar_text_contents(), expected) + + def assert_text_equals(self, expected): + return self.assertEqual(self.text.get('1.0', 'end'), expected) + + def test_init_empty(self): + self.assert_sidebar_n_lines(1) + + def test_init_not_empty(self): + self.text.insert('insert', 'foo bar\n'*3) + self.assert_text_equals('foo bar\n'*3 + '\n') + self.assert_sidebar_n_lines(4) + + def test_toggle_linenumbering(self): + self.assertEqual(self.linenumber.is_shown, False) + self.linenumber.show_sidebar() + self.assertEqual(self.linenumber.is_shown, True) + self.linenumber.hide_sidebar() + self.assertEqual(self.linenumber.is_shown, False) + self.linenumber.hide_sidebar() + self.assertEqual(self.linenumber.is_shown, False) + self.linenumber.show_sidebar() + self.assertEqual(self.linenumber.is_shown, True) + self.linenumber.show_sidebar() + self.assertEqual(self.linenumber.is_shown, True) + + def test_insert(self): + self.text.insert('insert', 'foobar') + self.assert_text_equals('foobar\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + self.text.insert('insert', '\nfoo') + self.assert_text_equals('foobar\nfoo\n') + self.assert_sidebar_n_lines(2) + self.assert_state_disabled() + + self.text.insert('insert', 'hello\n'*2) + self.assert_text_equals('foobar\nfoohello\nhello\n\n') + self.assert_sidebar_n_lines(4) + self.assert_state_disabled() + + self.text.insert('insert', '\nworld') + self.assert_text_equals('foobar\nfoohello\nhello\n\nworld\n') + self.assert_sidebar_n_lines(5) + self.assert_state_disabled() + + def test_delete(self): + self.text.insert('insert', 'foobar') + self.assert_text_equals('foobar\n') + self.text.delete('1.1', '1.3') + self.assert_text_equals('fbar\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + self.text.insert('insert', 'foo\n'*2) + self.assert_text_equals('fbarfoo\nfoo\n\n') + self.assert_sidebar_n_lines(3) + self.assert_state_disabled() + + # Note: deleting up to "2.end" doesn't delete the final newline. + self.text.delete('2.0', '2.end') + self.assert_text_equals('fbarfoo\n\n\n') + self.assert_sidebar_n_lines(3) + self.assert_state_disabled() + + self.text.delete('1.3', 'end') + self.assert_text_equals('fba\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + # Note: Text widgets always keep a single '\n' character at the end. + self.text.delete('1.0', 'end') + self.assert_text_equals('\n') + self.assert_sidebar_n_lines(1) + self.assert_state_disabled() + + def test_sidebar_text_width(self): + """ + Test that linenumber text widget is always at the minimum + width + """ + def get_width(): + return self.linenumber.sidebar_text.config()['width'][-1] + + self.assert_sidebar_n_lines(1) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo') + self.assert_sidebar_n_lines(1) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo\n'*8) + self.assert_sidebar_n_lines(9) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(10) + self.assertEqual(get_width(), 2) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(11) + self.assertEqual(get_width(), 2) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(10) + self.assertEqual(get_width(), 2) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(9) + self.assertEqual(get_width(), 1) + + self.text.insert('insert', 'foo\n'*90) + self.assert_sidebar_n_lines(99) + self.assertEqual(get_width(), 2) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(100) + self.assertEqual(get_width(), 3) + + self.text.insert('insert', 'foo\n') + self.assert_sidebar_n_lines(101) + self.assertEqual(get_width(), 3) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(100) + self.assertEqual(get_width(), 3) + + self.text.delete('insert -1l linestart', 'insert linestart') + self.assert_sidebar_n_lines(99) + self.assertEqual(get_width(), 2) + + self.text.delete('50.0 -1c', 'end -1c') + self.assert_sidebar_n_lines(49) + self.assertEqual(get_width(), 2) + + self.text.delete('5.0 -1c', 'end -1c') + self.assert_sidebar_n_lines(4) + self.assertEqual(get_width(), 1) + + # Note: Text widgets always keep a single '\n' character at the end. + self.text.delete('1.0', 'end -1c') + self.assert_sidebar_n_lines(1) + self.assertEqual(get_width(), 1) + + def test_click_selection(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') + self.root.update() + + # Click on the second line. + x, y = self.get_line_screen_position(2) + self.linenumber.sidebar_text.event_generate('', x=x, y=y) + self.linenumber.sidebar_text.update() + self.root.update() + + self.assertEqual(self.get_selection(), ('2.0', '3.0')) + + def test_drag_selection(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') + self.root.update() + + # Drag from the first line to the third line. + start_x, start_y = self.get_line_screen_position(1) + end_x, end_y = self.get_line_screen_position(3) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.root.update() + + self.assertEqual(self.get_selection(), ('1.0', '4.0')) + + def test_scroll(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'line\n' * 100) + self.root.update() + + # Scroll down 10 lines. + self.text.yview_scroll(10, 'unit') + self.root.update() + self.assertEqual(self.text.index('@0,0'), '11.0') + self.assertEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0') + + # Generate a mouse-wheel event and make sure it scrolled up or down. + # The meaning of the "delta" is OS-dependant, so this just checks for + # any change. + self.linenumber.sidebar_text.event_generate('', + x=0, y=0, + delta=10) + self.root.update() + self.assertNotEqual(self.text.index('@0,0'), '11.0') + self.assertNotEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0') + + def test_font(self): + ln = self.linenumber + + orig_font = ln.sidebar_text['font'] + test_font = 'TkTextFont' + self.assertNotEqual(orig_font, test_font) + + # Ensure line numbers aren't shown. + ln.hide_sidebar() + + self.font_override = test_font + # Nothing breaks when line numbers aren't shown. + ln.update_font() + + # Activate line numbers, previous font change is immediately effective. + ln.show_sidebar() + self.assertEqual(ln.sidebar_text['font'], test_font) + + # Call the font update with line numbers shown, change is picked up. + self.font_override = orig_font + ln.update_font() + self.assertEqual(ln.sidebar_text['font'], orig_font) + + def test_highlight_colors(self): + ln = self.linenumber + + orig_colors = dict(self.highlight_cfg) + test_colors = {'background': '#222222', 'foreground': '#ffff00'} + + def assert_colors_are_equal(colors): + self.assertEqual(ln.sidebar_text['background'], colors['background']) + self.assertEqual(ln.sidebar_text['foreground'], colors['foreground']) + + # Ensure line numbers aren't shown. + ln.hide_sidebar() + + self.highlight_cfg = test_colors + # Nothing breaks with inactive code context. + ln.update_colors() + + # Show line numbers, previous colors change is immediately effective. + ln.show_sidebar() + assert_colors_are_equal(test_colors) + + # Call colors update with no change to the configured colors. + ln.update_colors() + assert_colors_are_equal(test_colors) + + # Call the colors update with line numbers shown, change is picked up. + self.highlight_cfg = orig_colors + ln.update_colors() + assert_colors_are_equal(orig_colors) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py index b0c85cf505c7..fc51fb1b5d34 100644 --- a/Lib/idlelib/mainmenu.py +++ b/Lib/idlelib/mainmenu.py @@ -100,7 +100,8 @@ ('Configure _IDLE', '<>'), None, ('Show _Code Context', '<>'), - ('Zoom Height', '<>'), + ('Show _Line Numbers', '<>'), + ('_Zoom Height', '<>'), ]), ('window', [ diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index 38c59bdf9b4e..90272b6feb4a 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -74,13 +74,11 @@ class OutputWindow(EditorWindow): ("Go to file/line", "<>", None), ] - allow_codecontext = False + allow_code_context = False def __init__(self, *args): EditorWindow.__init__(self, *args) self.text.bind("<>", self.goto_file_line) - self.text.unbind("<>") - self.update_menu_state('options', '*Code Context', 'disabled') # Customize EditorWindow def ispythonsource(self, filename): diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 7ad5a76c3bd5..87401f33f55f 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -861,6 +861,8 @@ class PyShell(OutputWindow): ("Squeeze", "<>"), ] + allow_line_numbers = False + # New classes from idlelib.history import History diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py new file mode 100644 index 000000000000..8f9bf80b5260 --- /dev/null +++ b/Lib/idlelib/sidebar.py @@ -0,0 +1,324 @@ +"""Line numbering implementation for IDLE as an extension. +Includes BaseSideBar which can be extended for other sidebar based extensions +""" +import functools +import itertools + +import tkinter as tk +from idlelib.config import idleConf +from idlelib.delegator import Delegator + + +def get_end_linenumber(text): + """Utility to get the last line's number in a Tk text widget.""" + return int(float(text.index('end-1c'))) + + +def get_widget_padding(widget): + """Get the total padding of a Tk widget, including its border.""" + # TODO: use also in codecontext.py + manager = widget.winfo_manager() + if manager == 'pack': + info = widget.pack_info() + elif manager == 'grid': + info = widget.grid_info() + else: + raise ValueError(f"Unsupported geometry manager: {manager}") + + # All values are passed through getint(), since some + # values may be pixel objects, which can't simply be added to ints. + padx = sum(map(widget.tk.getint, [ + info['padx'], + widget.cget('padx'), + widget.cget('border'), + ])) + pady = sum(map(widget.tk.getint, [ + info['pady'], + widget.cget('pady'), + widget.cget('border'), + ])) + return padx, pady + + +class BaseSideBar: + """ + The base class for extensions which require a sidebar. + """ + def __init__(self, editwin): + self.editwin = editwin + self.parent = editwin.text_frame + self.text = editwin.text + + _padx, pady = get_widget_padding(self.text) + self.sidebar_text = tk.Text(self.parent, width=1, wrap=tk.NONE, + padx=0, pady=pady, + borderwidth=0, highlightthickness=0) + self.sidebar_text.config(state=tk.DISABLED) + self.text['yscrollcommand'] = self.redirect_yscroll_event + self.update_font() + self.update_colors() + + self.is_shown = False + + def update_font(self): + """Update the sidebar text font, usually after config changes.""" + font = idleConf.GetFont(self.text, 'main', 'EditorWindow') + self._update_font(font) + + def _update_font(self, font): + self.sidebar_text['font'] = font + + def update_colors(self): + """Update the sidebar text colors, usually after config changes.""" + colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'normal') + self._update_colors(foreground=colors['foreground'], + background=colors['background']) + + def _update_colors(self, foreground, background): + self.sidebar_text.config( + fg=foreground, bg=background, + selectforeground=foreground, selectbackground=background, + inactiveselectbackground=background, + ) + + def show_sidebar(self): + if not self.is_shown: + self.sidebar_text.grid(row=1, column=0, sticky=tk.NSEW) + self.is_shown = True + + def hide_sidebar(self): + if self.is_shown: + self.sidebar_text.grid_forget() + self.is_shown = False + + def redirect_yscroll_event(self, *args, **kwargs): + """Redirect vertical scrolling to the main editor text widget. + + The scroll bar is also updated. + """ + self.editwin.vbar.set(*args) + self.sidebar_text.yview_moveto(args[0]) + return 'break' + + def redirect_focusin_event(self, event): + """Redirect focus-in events to the main editor text widget.""" + self.text.focus_set() + return 'break' + + def redirect_mousebutton_event(self, event, event_name): + """Redirect mouse button events to the main editor text widget.""" + self.text.focus_set() + self.text.event_generate(event_name, x=0, y=event.y) + return 'break' + + def redirect_mousewheel_event(self, event): + """Redirect mouse wheel events to the editwin text widget.""" + self.text.event_generate('', + x=0, y=event.y, delta=event.delta) + return 'break' + + +class EndLineDelegator(Delegator): + """Generate callbacks with the current end line number after + insert or delete operations""" + def __init__(self, changed_callback): + """ + changed_callback - Callable, will be called after insert + or delete operations with the current + end line number. + """ + Delegator.__init__(self) + self.changed_callback = changed_callback + + def insert(self, index, chars, tags=None): + self.delegate.insert(index, chars, tags) + self.changed_callback(get_end_linenumber(self.delegate)) + + def delete(self, index1, index2=None): + self.delegate.delete(index1, index2) + self.changed_callback(get_end_linenumber(self.delegate)) + + +class LineNumbers(BaseSideBar): + """Line numbers support for editor windows.""" + def __init__(self, editwin): + BaseSideBar.__init__(self, editwin) + self.prev_end = 1 + self._sidebar_width_type = type(self.sidebar_text['width']) + self.sidebar_text.config(state=tk.NORMAL) + self.sidebar_text.insert('insert', '1', 'linenumber') + self.sidebar_text.config(state=tk.DISABLED) + self.sidebar_text.config(takefocus=False, exportselection=False) + self.sidebar_text.tag_config('linenumber', justify=tk.RIGHT) + + self.bind_events() + + end = get_end_linenumber(self.text) + self.update_sidebar_text(end) + + end_line_delegator = EndLineDelegator(self.update_sidebar_text) + # Insert the delegator after the undo delegator, so that line numbers + # are properly updated after undo and redo actions. + end_line_delegator.setdelegate(self.editwin.undo.delegate) + self.editwin.undo.setdelegate(end_line_delegator) + # Reset the delegator caches of the delegators "above" the + # end line delegator we just inserted. + delegator = self.editwin.per.top + while delegator is not end_line_delegator: + delegator.resetcache() + delegator = delegator.delegate + + self.is_shown = False + + def bind_events(self): + # Ensure focus is always redirected to the main editor text widget. + self.sidebar_text.bind('', self.redirect_focusin_event) + + # Redirect mouse scrolling to the main editor text widget. + # + # Note that without this, scrolling with the mouse only scrolls + # the line numbers. + self.sidebar_text.bind('', self.redirect_mousewheel_event) + + # Redirect mouse button events to the main editor text widget, + # except for the left mouse button (1). + # + # Note: X-11 sends Button-4 and Button-5 events for the scroll wheel. + def bind_mouse_event(event_name, target_event_name): + handler = functools.partial(self.redirect_mousebutton_event, + event_name=target_event_name) + self.sidebar_text.bind(event_name, handler) + + for button in [2, 3, 4, 5]: + for event_name in (f'', + f'', + f'', + ): + bind_mouse_event(event_name, target_event_name=event_name) + + # Convert double- and triple-click events to normal click events, + # since event_generate() doesn't allow generating such events. + for event_name in (f'', + f'', + ): + bind_mouse_event(event_name, + target_event_name=f'') + + start_line = None + def b1_mousedown_handler(event): + # select the entire line + lineno = self.editwin.getlineno(f"@0,{event.y}") + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", f"{lineno}.0", f"{lineno+1}.0") + self.text.mark_set("insert", f"{lineno+1}.0") + + # remember this line in case this is the beginning of dragging + nonlocal start_line + start_line = lineno + self.sidebar_text.bind('', b1_mousedown_handler) + + # These are set by b1_motion_handler() and read by selection_handler(); + # see below. last_y is passed this way since the mouse Y-coordinate + # is not available on selection event objects. last_yview is passed + # this way to recognize scrolling while the mouse isn't moving. + last_y = last_yview = None + + def drag_update_selection_and_insert_mark(y_coord): + """Helper function for drag and selection event handlers.""" + lineno = self.editwin.getlineno(f"@0,{y_coord}") + a, b = sorted([start_line, lineno]) + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", f"{a}.0", f"{b+1}.0") + self.text.mark_set("insert", + f"{lineno if lineno == a else lineno + 1}.0") + + # Special handling of dragging with mouse button 1. In "normal" text + # widgets this selects text, but the line numbers text widget has + # selection disabled. Still, dragging triggers some selection-related + # functionality under the hood. Specifically, dragging to above or + # below the text widget triggers scrolling, in a way that bypasses the + # other scrolling synchronization mechanisms.i + def b1_drag_handler(event, *args): + nonlocal last_y + nonlocal last_yview + last_y = event.y + last_yview = self.sidebar_text.yview() + if not 0 <= last_y <= self.sidebar_text.winfo_height(): + self.text.yview_moveto(last_yview[0]) + drag_update_selection_and_insert_mark(event.y) + self.sidebar_text.bind('', b1_drag_handler) + + # With mouse-drag scrolling fixed by the above, there is still an edge- + # case we need to handle: When drag-scrolling, scrolling can continue + # while the mouse isn't moving, leading to the above fix not scrolling + # properly. + def selection_handler(event): + yview = self.sidebar_text.yview() + if yview != last_yview: + self.text.yview_moveto(yview[0]) + drag_update_selection_and_insert_mark(last_y) + self.sidebar_text.bind('<>', selection_handler) + + def update_colors(self): + """Update the sidebar text colors, usually after config changes.""" + colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'linenumber') + self._update_colors(foreground=colors['foreground'], + background=colors['background']) + + def update_sidebar_text(self, end): + """ + Perform the following action: + Each line sidebar_text contains the linenumber for that line + Synchronize with editwin.text so that both sidebar_text and + editwin.text contain the same number of lines""" + if end == self.prev_end: + return + + width_difference = len(str(end)) - len(str(self.prev_end)) + if width_difference: + cur_width = int(float(self.sidebar_text['width'])) + new_width = cur_width + width_difference + self.sidebar_text['width'] = self._sidebar_width_type(new_width) + + self.sidebar_text.config(state=tk.NORMAL) + if end > self.prev_end: + new_text = '\n'.join(itertools.chain( + [''], + map(str, range(self.prev_end + 1, end + 1)), + )) + self.sidebar_text.insert(f'end -1c', new_text, 'linenumber') + else: + self.sidebar_text.delete(f'{end+1}.0 -1c', 'end -1c') + self.sidebar_text.config(state=tk.DISABLED) + + self.prev_end = end + + +def _linenumbers_drag_scrolling(parent): # htest # + from idlelib.idle_test.test_sidebar import Dummy_editwin + + toplevel = tk.Toplevel(parent) + text_frame = tk.Frame(toplevel) + text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + text_frame.rowconfigure(1, weight=1) + text_frame.columnconfigure(1, weight=1) + + font = idleConf.GetFont(toplevel, 'main', 'EditorWindow') + text = tk.Text(text_frame, width=80, height=24, wrap=tk.NONE, font=font) + text.grid(row=1, column=1, sticky=tk.NSEW) + + editwin = Dummy_editwin(text) + editwin.vbar = tk.Scrollbar(text_frame) + + linenumbers = LineNumbers(editwin) + linenumbers.show_sidebar() + + text.insert('1.0', '\n'.join('a'*i for i in range(1, 101))) + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_sidebar', verbosity=2, exit=False) + + from idlelib.idle_test.htest import run + run(_linenumbers_drag_scrolling) diff --git a/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst b/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst new file mode 100644 index 000000000000..201a413b42a3 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst @@ -0,0 +1,4 @@ +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. + From webhook-mailer at python.org Tue Jul 23 12:12:14 2019 From: webhook-mailer at python.org (Ned Deily) Date: Tue, 23 Jul 2019 16:12:14 -0000 Subject: [Python-checkins] bpo-37653: Fix libinstall target in Makefile (GH-14911) Message-ID: https://github.com/python/cpython/commit/387c3c5a04bc62cfa7c9dabe206ba2c582a71c05 commit: 387c3c5a04bc62cfa7c9dabe206ba2c582a71c05 branch: master author: Erlend Egeberg Aasland committer: Ned Deily date: 2019-07-23T18:11:50+02:00 summary: bpo-37653: Fix libinstall target in Makefile (GH-14911) files: M Makefile.pre.in diff --git a/Makefile.pre.in b/Makefile.pre.in index dd267b483a29..363a4eb9d2cb 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1432,8 +1432,6 @@ libinstall: build_all $(srcdir)/Modules/xxmodule.c *CVS) ;; \ *.py[co]) ;; \ *.orig) ;; \ - # bpo-37468: Don't install distutils/command/wininst-*.exe files used \ - # by distutils bdist_wininst: bdist_wininst only works on Windows. \ *wininst-*.exe) ;; \ *~) ;; \ *) \ From webhook-mailer at python.org Tue Jul 23 16:15:05 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 23 Jul 2019 20:15:05 -0000 Subject: [Python-checkins] bpo-29446: IDLE -- add explicit imports (GH-14919) Message-ID: https://github.com/python/cpython/commit/c6fd6c83b70df76421d05a7628367e64a2f83589 commit: c6fd6c83b70df76421d05a7628367e64a2f83589 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-23T16:14:59-04:00 summary: bpo-29446: IDLE -- add explicit imports (GH-14919) Stop depending on tkinter import *. files: M Lib/idlelib/editor.py diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 497ee12f1814..35027da76bce 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -2,7 +2,9 @@ import importlib.util import os import platform +import re import string +import sys import tokenize import traceback import webbrowser From webhook-mailer at python.org Tue Jul 23 16:23:47 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 23 Jul 2019 20:23:47 -0000 Subject: [Python-checkins] bpo-29446: IDLE -- add explicit imports (GH-14919) (GH-14920) Message-ID: https://github.com/python/cpython/commit/fc63d5a361f7d20282019cad84336a242c124267 commit: fc63d5a361f7d20282019cad84336a242c124267 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-23T16:23:43-04:00 summary: bpo-29446: IDLE -- add explicit imports (GH-14919) (GH-14920) Stop depending on tkinter import *. (cherry picked from commit c6fd6c83b70df76421d05a7628367e64a2f83589) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/editor.py diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 497ee12f1814..35027da76bce 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -2,7 +2,9 @@ import importlib.util import os import platform +import re import string +import sys import tokenize import traceback import webbrowser From webhook-mailer at python.org Tue Jul 23 16:37:03 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 23 Jul 2019 20:37:03 -0000 Subject: [Python-checkins] bpo-29446: IDLE -- add explicit imports (GH-14919) (#14921) Message-ID: https://github.com/python/cpython/commit/aee260f1c4d32bf67edec209d949294d4dc403c1 commit: aee260f1c4d32bf67edec209d949294d4dc403c1 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Terry Jan Reedy date: 2019-07-23T16:36:57-04:00 summary: bpo-29446: IDLE -- add explicit imports (GH-14919) (#14921) Stop depending on tkinter import *. (cherry picked from commit c6fd6c83b70df76421d05a7628367e64a2f83589) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/editor.py diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 497ee12f1814..35027da76bce 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -2,7 +2,9 @@ import importlib.util import os import platform +import re import string +import sys import tokenize import traceback import webbrowser From webhook-mailer at python.org Tue Jul 23 17:34:39 2019 From: webhook-mailer at python.org (Brett Cannon) Date: Tue, 23 Jul 2019 21:34:39 -0000 Subject: [Python-checkins] Touch up venv docs (GH-14922) Message-ID: https://github.com/python/cpython/commit/2f224a077a83ac9de8a12bb7dcc516642b8176d8 commit: 2f224a077a83ac9de8a12bb7dcc516642b8176d8 branch: master author: Brett Cannon <54418+brettcannon at users.noreply.github.com> committer: GitHub date: 2019-07-23T14:34:32-07:00 summary: Touch up venv docs (GH-14922) files: M Doc/library/venv.rst diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index d3d5ae2b007d..62732d224386 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -47,7 +47,7 @@ Creating virtual environments A virtual environment is a directory tree which contains Python executable files and other files which indicate that it is a virtual environment. - Common installation tools such as ``Setuptools`` and ``pip`` work as + Common installation tools such as setuptools_ and pip_ work as expected with virtual environments. In other words, when a virtual environment is active, they install Python packages into the virtual environment without needing to be told to do so explicitly. @@ -64,24 +64,25 @@ Creating virtual environments Python installation). When a virtual environment is active, any options that change the - installation path will be ignored from all distutils configuration files to - prevent projects being inadvertently installed outside of the virtual - environment. + installation path will be ignored from all :mod:`distutils` configuration + files to prevent projects being inadvertently installed outside of the + virtual environment. When working in a command shell, users can make a virtual environment active by running an ``activate`` script in the virtual environment's executables - directory (the precise filename is shell-dependent), which prepends the - virtual environment's directory for executables to the ``PATH`` environment - variable for the running shell. There should be no need in other - circumstances to activate a virtual environment?scripts installed into - virtual environments have a "shebang" line which points to the virtual - environment's Python interpreter. This means that the script will run with - that interpreter regardless of the value of ``PATH``. On Windows, "shebang" - line processing is supported if you have the Python Launcher for Windows - installed (this was added to Python in 3.3 - see :pep:`397` for more - details). Thus, double-clicking an installed script in a Windows Explorer - window should run the script with the correct interpreter without there - needing to be any reference to its virtual environment in ``PATH``. + directory (the precise filename and command to use the file is + shell-dependent), which prepends the virtual environment's directory for + executables to the ``PATH`` environment variable for the running shell. There + should be no need in other circumstances to activate a virtual + environment; scripts installed into virtual environments have a "shebang" + line which points to the virtual environment's Python interpreter. This means + that the script will run with that interpreter regardless of the value of + ``PATH``. On Windows, "shebang" line processing is supported if you have the + Python Launcher for Windows installed (this was added to Python in 3.3 - see + :pep:`397` for more details). Thus, double-clicking an installed script in a + Windows Explorer window should run the script with the correct interpreter + without there needing to be any reference to its virtual environment in + ``PATH``. .. _venv-api: @@ -135,20 +136,20 @@ creation according to their needs, the :class:`EnvBuilder` class. Added the ``upgrade_deps`` parameter Creators of third-party virtual environment tools will be free to use the - provided ``EnvBuilder`` class as a base class. + provided :class:`EnvBuilder` class as a base class. The returned env-builder is an object which has a method, ``create``: .. method:: create(env_dir) - This method takes as required argument the path (absolute or relative to - the current directory) of the target directory which is to contain the + Create a virtual environment by specifying the target directory + (absolute or relative to the current directory) which is to contain the virtual environment. The ``create`` method will either create the environment in the specified directory, or raise an appropriate exception. - The ``create`` method of the ``EnvBuilder`` class illustrates the hooks - available for subclass customization:: + The ``create`` method of the :class:`EnvBuilder` class illustrates the + hooks available for subclass customization:: def create(self, env_dir): """ @@ -476,3 +477,7 @@ subclass which installs setuptools and pip into a created virtual environment:: This script is also available for download `online `_. + + +.. _setuptools: https://pypi.org/project/setuptools/ +.. _pip: https://pypi.org/project/pip/ From webhook-mailer at python.org Wed Jul 24 00:33:56 2019 From: webhook-mailer at python.org (Gregory P. Smith) Date: Wed, 24 Jul 2019 04:33:56 -0000 Subject: [Python-checkins] Only setup PGO tests when --pgo is enabled. (GH-14927) Message-ID: https://github.com/python/cpython/commit/f0807ab24cbd13163bff7e0de3a97fe83584b80d commit: f0807ab24cbd13163bff7e0de3a97fe83584b80d branch: master author: Gregory P. Smith committer: GitHub date: 2019-07-23T21:33:48-07:00 summary: Only setup PGO tests when --pgo is enabled. (GH-14927) files: M Lib/test/libregrtest/main.py diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 78b6790685c9..2b6607df15dd 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -215,8 +215,9 @@ def find_tests(self, tests): removepy(self.tests) - # add default PGO tests if no tests are specified - setup_pgo_tests(self.ns) + if self.ns.pgo: + # add default PGO tests if no tests are specified + setup_pgo_tests(self.ns) stdtests = STDTESTS[:] nottests = NOTTESTS.copy() From webhook-mailer at python.org Wed Jul 24 00:53:38 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 24 Jul 2019 04:53:38 -0000 Subject: [Python-checkins] Only setup PGO tests when --pgo is enabled. (GH-14927) Message-ID: https://github.com/python/cpython/commit/9f77cfc37e369c2e618fc25e4b09e0504910920a commit: 9f77cfc37e369c2e618fc25e4b09e0504910920a branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-23T21:53:34-07:00 summary: Only setup PGO tests when --pgo is enabled. (GH-14927) (cherry picked from commit f0807ab24cbd13163bff7e0de3a97fe83584b80d) Co-authored-by: Gregory P. Smith files: M Lib/test/libregrtest/main.py diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 78b6790685c9..2b6607df15dd 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -215,8 +215,9 @@ def find_tests(self, tests): removepy(self.tests) - # add default PGO tests if no tests are specified - setup_pgo_tests(self.ns) + if self.ns.pgo: + # add default PGO tests if no tests are specified + setup_pgo_tests(self.ns) stdtests = STDTESTS[:] nottests = NOTTESTS.copy() From webhook-mailer at python.org Wed Jul 24 01:28:54 2019 From: webhook-mailer at python.org (Gregory P. Smith) Date: Wed, 24 Jul 2019 05:28:54 -0000 Subject: [Python-checkins] bpo-37667: Add regression test for regrtest. (GH-14929) Message-ID: https://github.com/python/cpython/commit/e95ac20103437d8099fb91cd4468ab030852f3b7 commit: e95ac20103437d8099fb91cd4468ab030852f3b7 branch: master author: Gregory P. Smith committer: GitHub date: 2019-07-23T22:28:48-07:00 summary: bpo-37667: Add regression test for regrtest. (GH-14929) Verify that it appears to find roughly the right number of tests in the stdlib's testsuite. files: M Lib/test/test_regrtest.py diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 2e5df00dc51b..fdab8c8ef5d1 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -6,6 +6,7 @@ import contextlib import faulthandler +import glob import io import os.path import platform @@ -529,6 +530,31 @@ def run_python(self, args, **kw): return proc.stdout +class CheckActualTests(BaseTestCase): + """ + Check that regrtest appears to find the expected set of tests. + """ + + def test_finds_expected_number_of_tests(self): + args = ['-Wd', '-E', '-bb', '-m', 'test.regrtest', '--list-tests'] + output = self.run_python(args) + rough_number_of_tests_found = len(output.splitlines()) + actual_testsuite_glob = os.path.join(os.path.dirname(__file__), + 'test*.py') + rough_counted_test_py_files = len(glob.glob(actual_testsuite_glob)) + # We're not trying to duplicate test finding logic in here, + # just give a rough estimate of how many there should be and + # be near that. This is a regression test to prevent mishaps + # such as https://bugs.python.org/issue37667 in the future. + # If you need to change the values in here during some + # mythical future test suite reorganization, don't go + # overboard with logic and keep that goal in mind. + self.assertGreater(rough_number_of_tests_found, + rough_counted_test_py_files*9//10, + msg='Unexpectedly low number of tests found in:\n' + f'{", ".join(output.splitlines())}') + + class ProgramsTestCase(BaseTestCase): """ Test various ways to run the Python test suite. Use options close From webhook-mailer at python.org Wed Jul 24 02:30:50 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 24 Jul 2019 06:30:50 -0000 Subject: [Python-checkins] bpo-37667: Add regression test for regrtest. (GH-14929) Message-ID: https://github.com/python/cpython/commit/9d54de16589b9d0f073093fb90a8c7040224335a commit: 9d54de16589b9d0f073093fb90a8c7040224335a branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-23T23:30:27-07:00 summary: bpo-37667: Add regression test for regrtest. (GH-14929) Verify that it appears to find roughly the right number of tests in the stdlib's testsuite. (cherry picked from commit e95ac20103437d8099fb91cd4468ab030852f3b7) Co-authored-by: Gregory P. Smith files: M Lib/test/test_regrtest.py diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 2b082b0c56ad..b569100c6777 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -6,6 +6,7 @@ import contextlib import faulthandler +import glob import io import os.path import platform @@ -529,6 +530,31 @@ def run_python(self, args, **kw): return proc.stdout +class CheckActualTests(BaseTestCase): + """ + Check that regrtest appears to find the expected set of tests. + """ + + def test_finds_expected_number_of_tests(self): + args = ['-Wd', '-E', '-bb', '-m', 'test.regrtest', '--list-tests'] + output = self.run_python(args) + rough_number_of_tests_found = len(output.splitlines()) + actual_testsuite_glob = os.path.join(os.path.dirname(__file__), + 'test*.py') + rough_counted_test_py_files = len(glob.glob(actual_testsuite_glob)) + # We're not trying to duplicate test finding logic in here, + # just give a rough estimate of how many there should be and + # be near that. This is a regression test to prevent mishaps + # such as https://bugs.python.org/issue37667 in the future. + # If you need to change the values in here during some + # mythical future test suite reorganization, don't go + # overboard with logic and keep that goal in mind. + self.assertGreater(rough_number_of_tests_found, + rough_counted_test_py_files*9//10, + msg='Unexpectedly low number of tests found in:\n' + f'{", ".join(output.splitlines())}') + + class ProgramsTestCase(BaseTestCase): """ Test various ways to run the Python test suite. Use options close From webhook-mailer at python.org Wed Jul 24 08:03:15 2019 From: webhook-mailer at python.org (Inada Naoki) Date: Wed, 24 Jul 2019 12:03:15 -0000 Subject: [Python-checkins] bpo-29548: deprecate PyEval_Call* functions (GH-14804) Message-ID: https://github.com/python/cpython/commit/151b91dfd21a100ecb1eba9e293c0a8695bf3bf5 commit: 151b91dfd21a100ecb1eba9e293c0a8695bf3bf5 branch: master author: Jeroen Demeyer committer: Inada Naoki date: 2019-07-24T21:02:49+09:00 summary: bpo-29548: deprecate PyEval_Call* functions (GH-14804) files: A Misc/NEWS.d/next/C API/2019-07-17-09-50-50.bpo-29548.5wIptQ.rst M Include/ceval.h diff --git a/Include/ceval.h b/Include/ceval.h index e78194d51237..61db777cc4d5 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -8,25 +8,25 @@ extern "C" { /* Interface to random parts in ceval.c */ /* PyEval_CallObjectWithKeywords(), PyEval_CallObject(), PyEval_CallFunction - * and PyEval_CallMethod are kept for backward compatibility: PyObject_Call(), - * PyObject_CallFunction() and PyObject_CallMethod() are recommended to call - * a callable object. + * and PyEval_CallMethod are deprecated. Since they are officially part of the + * stable ABI (PEP 384), they must be kept for backward compatibility. + * PyObject_Call(), PyObject_CallFunction() and PyObject_CallMethod() are + * recommended to call a callable object. */ -PyAPI_FUNC(PyObject *) PyEval_CallObjectWithKeywords( +Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallObjectWithKeywords( PyObject *callable, PyObject *args, PyObject *kwargs); -/* Inline this */ +/* Deprecated since PyEval_CallObjectWithKeywords is deprecated */ #define PyEval_CallObject(callable, arg) \ PyEval_CallObjectWithKeywords(callable, arg, (PyObject *)NULL) -PyAPI_FUNC(PyObject *) PyEval_CallFunction(PyObject *callable, - const char *format, ...); -PyAPI_FUNC(PyObject *) PyEval_CallMethod(PyObject *obj, - const char *name, - const char *format, ...); +Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallFunction( + PyObject *callable, const char *format, ...); +Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallMethod( + PyObject *obj, const char *name, const char *format, ...); #ifndef Py_LIMITED_API PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *); diff --git a/Misc/NEWS.d/next/C API/2019-07-17-09-50-50.bpo-29548.5wIptQ.rst b/Misc/NEWS.d/next/C API/2019-07-17-09-50-50.bpo-29548.5wIptQ.rst new file mode 100644 index 000000000000..e7409191c0ef --- /dev/null +++ b/Misc/NEWS.d/next/C API/2019-07-17-09-50-50.bpo-29548.5wIptQ.rst @@ -0,0 +1,3 @@ +The functions ``PyEval_CallObject``, ``PyEval_CallFunction``, +``PyEval_CallMethod`` and ``PyEval_CallObjectWithKeywords`` are deprecated. +Use :c:func:`PyObject_Call` and its variants instead. From webhook-mailer at python.org Wed Jul 24 13:14:52 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 24 Jul 2019 17:14:52 -0000 Subject: [Python-checkins] [3.7] Touch up venv docs (GH-14922) (GH-14924) Message-ID: https://github.com/python/cpython/commit/913210d13bd78132f32159af38e28490b421537d commit: 913210d13bd78132f32159af38e28490b421537d branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-24T10:14:35-07:00 summary: [3.7] Touch up venv docs (GH-14922) (GH-14924) (cherry picked from commit 2f224a077a83ac9de8a12bb7dcc516642b8176d8) Co-authored-by: Brett Cannon <54418+brettcannon at users.noreply.github.com> Automerge-Triggered-By: @brettcannon files: M Doc/library/venv.rst diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index d61df484f9e7..18804b5eed64 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -52,7 +52,7 @@ Creating virtual environments A virtual environment is a directory tree which contains Python executable files and other files which indicate that it is a virtual environment. - Common installation tools such as ``Setuptools`` and ``pip`` work as + Common installation tools such as setuptools_ and pip_ work as expected with virtual environments. In other words, when a virtual environment is active, they install Python packages into the virtual environment without needing to be told to do so explicitly. @@ -69,24 +69,25 @@ Creating virtual environments Python installation). When a virtual environment is active, any options that change the - installation path will be ignored from all distutils configuration files to - prevent projects being inadvertently installed outside of the virtual - environment. + installation path will be ignored from all :mod:`distutils` configuration + files to prevent projects being inadvertently installed outside of the + virtual environment. When working in a command shell, users can make a virtual environment active by running an ``activate`` script in the virtual environment's executables - directory (the precise filename is shell-dependent), which prepends the - virtual environment's directory for executables to the ``PATH`` environment - variable for the running shell. There should be no need in other - circumstances to activate a virtual environment?scripts installed into - virtual environments have a "shebang" line which points to the virtual - environment's Python interpreter. This means that the script will run with - that interpreter regardless of the value of ``PATH``. On Windows, "shebang" - line processing is supported if you have the Python Launcher for Windows - installed (this was added to Python in 3.3 - see :pep:`397` for more - details). Thus, double-clicking an installed script in a Windows Explorer - window should run the script with the correct interpreter without there - needing to be any reference to its virtual environment in ``PATH``. + directory (the precise filename and command to use the file is + shell-dependent), which prepends the virtual environment's directory for + executables to the ``PATH`` environment variable for the running shell. There + should be no need in other circumstances to activate a virtual + environment; scripts installed into virtual environments have a "shebang" + line which points to the virtual environment's Python interpreter. This means + that the script will run with that interpreter regardless of the value of + ``PATH``. On Windows, "shebang" line processing is supported if you have the + Python Launcher for Windows installed (this was added to Python in 3.3 - see + :pep:`397` for more details). Thus, double-clicking an installed script in a + Windows Explorer window should run the script with the correct interpreter + without there needing to be any reference to its virtual environment in + ``PATH``. .. _venv-api: @@ -135,20 +136,20 @@ creation according to their needs, the :class:`EnvBuilder` class. Added the ``prompt`` parameter Creators of third-party virtual environment tools will be free to use the - provided ``EnvBuilder`` class as a base class. + provided :class:`EnvBuilder` class as a base class. The returned env-builder is an object which has a method, ``create``: .. method:: create(env_dir) - This method takes as required argument the path (absolute or relative to - the current directory) of the target directory which is to contain the + Create a virtual environment by specifying the target directory + (absolute or relative to the current directory) which is to contain the virtual environment. The ``create`` method will either create the environment in the specified directory, or raise an appropriate exception. - The ``create`` method of the ``EnvBuilder`` class illustrates the hooks - available for subclass customization:: + The ``create`` method of the :class:`EnvBuilder` class illustrates the + hooks available for subclass customization:: def create(self, env_dir): """ @@ -476,3 +477,7 @@ subclass which installs setuptools and pip into a created virtual environment:: This script is also available for download `online `_. + + +.. _setuptools: https://pypi.org/project/setuptools/ +.. _pip: https://pypi.org/project/pip/ From webhook-mailer at python.org Wed Jul 24 13:14:58 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 24 Jul 2019 17:14:58 -0000 Subject: [Python-checkins] [3.8] Touch up venv docs (GH-14922) (GH-14923) Message-ID: https://github.com/python/cpython/commit/eb62274ed629483c67001e5bade3039197d04b55 commit: eb62274ed629483c67001e5bade3039197d04b55 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-24T10:14:53-07:00 summary: [3.8] Touch up venv docs (GH-14922) (GH-14923) (cherry picked from commit 2f224a077a83ac9de8a12bb7dcc516642b8176d8) Co-authored-by: Brett Cannon <54418+brettcannon at users.noreply.github.com> Automerge-Triggered-By: @brettcannon files: M Doc/library/venv.rst diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index 4f083a3181e7..1e825c3c21e8 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -47,7 +47,7 @@ Creating virtual environments A virtual environment is a directory tree which contains Python executable files and other files which indicate that it is a virtual environment. - Common installation tools such as ``Setuptools`` and ``pip`` work as + Common installation tools such as setuptools_ and pip_ work as expected with virtual environments. In other words, when a virtual environment is active, they install Python packages into the virtual environment without needing to be told to do so explicitly. @@ -64,24 +64,25 @@ Creating virtual environments Python installation). When a virtual environment is active, any options that change the - installation path will be ignored from all distutils configuration files to - prevent projects being inadvertently installed outside of the virtual - environment. + installation path will be ignored from all :mod:`distutils` configuration + files to prevent projects being inadvertently installed outside of the + virtual environment. When working in a command shell, users can make a virtual environment active by running an ``activate`` script in the virtual environment's executables - directory (the precise filename is shell-dependent), which prepends the - virtual environment's directory for executables to the ``PATH`` environment - variable for the running shell. There should be no need in other - circumstances to activate a virtual environment?scripts installed into - virtual environments have a "shebang" line which points to the virtual - environment's Python interpreter. This means that the script will run with - that interpreter regardless of the value of ``PATH``. On Windows, "shebang" - line processing is supported if you have the Python Launcher for Windows - installed (this was added to Python in 3.3 - see :pep:`397` for more - details). Thus, double-clicking an installed script in a Windows Explorer - window should run the script with the correct interpreter without there - needing to be any reference to its virtual environment in ``PATH``. + directory (the precise filename and command to use the file is + shell-dependent), which prepends the virtual environment's directory for + executables to the ``PATH`` environment variable for the running shell. There + should be no need in other circumstances to activate a virtual + environment; scripts installed into virtual environments have a "shebang" + line which points to the virtual environment's Python interpreter. This means + that the script will run with that interpreter regardless of the value of + ``PATH``. On Windows, "shebang" line processing is supported if you have the + Python Launcher for Windows installed (this was added to Python in 3.3 - see + :pep:`397` for more details). Thus, double-clicking an installed script in a + Windows Explorer window should run the script with the correct interpreter + without there needing to be any reference to its virtual environment in + ``PATH``. .. _venv-api: @@ -130,20 +131,20 @@ creation according to their needs, the :class:`EnvBuilder` class. Added the ``prompt`` parameter Creators of third-party virtual environment tools will be free to use the - provided ``EnvBuilder`` class as a base class. + provided :class:`EnvBuilder` class as a base class. The returned env-builder is an object which has a method, ``create``: .. method:: create(env_dir) - This method takes as required argument the path (absolute or relative to - the current directory) of the target directory which is to contain the + Create a virtual environment by specifying the target directory + (absolute or relative to the current directory) which is to contain the virtual environment. The ``create`` method will either create the environment in the specified directory, or raise an appropriate exception. - The ``create`` method of the ``EnvBuilder`` class illustrates the hooks - available for subclass customization:: + The ``create`` method of the :class:`EnvBuilder` class illustrates the + hooks available for subclass customization:: def create(self, env_dir): """ @@ -471,3 +472,7 @@ subclass which installs setuptools and pip into a created virtual environment:: This script is also available for download `online `_. + + +.. _setuptools: https://pypi.org/project/setuptools/ +.. _pip: https://pypi.org/project/pip/ From webhook-mailer at python.org Wed Jul 24 14:08:08 2019 From: webhook-mailer at python.org (Stefan Behnel) Date: Wed, 24 Jul 2019 18:08:08 -0000 Subject: [Python-checkins] bpo-37399: Correctly attach tail text to the last element/comment/pi (GH-14856) Message-ID: https://github.com/python/cpython/commit/c6cb4cdd21c0c3a09b0617dbfaa7053d3bfa6def commit: c6cb4cdd21c0c3a09b0617dbfaa7053d3bfa6def branch: master author: Stefan Behnel committer: GitHub date: 2019-07-24T20:08:02+02:00 summary: bpo-37399: Correctly attach tail text to the last element/comment/pi (GH-14856) * bpo-37399: Correctly attach tail text to the last element/comment/pi, even when comments or pis are discarded. Also fixes the insertion of PIs when "insert_pis=True" is configured for a TreeBuilder. files: M Lib/test/test_xml_etree.py M Modules/_elementtree.c diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 61737493a904..b2492cda848f 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2954,6 +2954,66 @@ def test_treebuilder_pi(self): self.assertEqual(b.pi('target'), (len('target'), None)) self.assertEqual(b.pi('pitarget', ' text '), (len('pitarget'), ' text ')) + def test_late_tail(self): + # Issue #37399: The tail of an ignored comment could overwrite the text before it. + class TreeBuilderSubclass(ET.TreeBuilder): + pass + + xml = "texttail" + a = ET.fromstring(xml) + self.assertEqual(a.text, "texttail") + + parser = ET.XMLParser(target=TreeBuilderSubclass()) + parser.feed(xml) + a = parser.close() + self.assertEqual(a.text, "texttail") + + xml = "texttail" + a = ET.fromstring(xml) + self.assertEqual(a.text, "texttail") + + xml = "texttail" + parser = ET.XMLParser(target=TreeBuilderSubclass()) + parser.feed(xml) + a = parser.close() + self.assertEqual(a.text, "texttail") + + def test_late_tail_mix_pi_comments(self): + # Issue #37399: The tail of an ignored comment could overwrite the text before it. + # Test appending tails to comments/pis. + class TreeBuilderSubclass(ET.TreeBuilder): + pass + + xml = "text \ntail" + parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=True)) + parser.feed(xml) + a = parser.close() + self.assertEqual(a[0].text, ' comment ') + self.assertEqual(a[0].tail, '\ntail') + self.assertEqual(a.text, "text ") + + parser = ET.XMLParser(target=TreeBuilderSubclass(insert_comments=True)) + parser.feed(xml) + a = parser.close() + self.assertEqual(a[0].text, ' comment ') + self.assertEqual(a[0].tail, '\ntail') + self.assertEqual(a.text, "text ") + + xml = "text\ntail" + parser = ET.XMLParser(target=ET.TreeBuilder(insert_pis=True)) + parser.feed(xml) + a = parser.close() + self.assertEqual(a[0].text, 'pi data') + self.assertEqual(a[0].tail, 'tail') + self.assertEqual(a.text, "text\n") + + parser = ET.XMLParser(target=TreeBuilderSubclass(insert_pis=True)) + parser.feed(xml) + a = parser.close() + self.assertEqual(a[0].text, 'pi data') + self.assertEqual(a[0].tail, 'tail') + self.assertEqual(a.text, "text\n") + def test_treebuilder_elementfactory_none(self): parser = ET.XMLParser(target=ET.TreeBuilder(element_factory=None)) parser.feed(self.sample1) diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index b9b50165bfda..830ce8635eae 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2399,6 +2399,7 @@ typedef struct { PyObject *this; /* current node */ PyObject *last; /* most recently created node */ + PyObject *last_for_tail; /* most recently created node that takes a tail */ PyObject *data; /* data collector (string or list), or NULL */ @@ -2530,6 +2531,7 @@ treebuilder_gc_traverse(TreeBuilderObject *self, visitproc visit, void *arg) Py_VISIT(self->root); Py_VISIT(self->this); Py_VISIT(self->last); + Py_VISIT(self->last_for_tail); Py_VISIT(self->data); Py_VISIT(self->stack); Py_VISIT(self->pi_factory); @@ -2551,6 +2553,7 @@ treebuilder_gc_clear(TreeBuilderObject *self) Py_CLEAR(self->stack); Py_CLEAR(self->data); Py_CLEAR(self->last); + Py_CLEAR(self->last_for_tail); Py_CLEAR(self->this); Py_CLEAR(self->pi_factory); Py_CLEAR(self->comment_factory); @@ -2622,21 +2625,50 @@ _elementtree__set_factories_impl(PyObject *module, PyObject *comment_factory, } static int -treebuilder_set_element_text_or_tail(PyObject *element, PyObject **data, - PyObject **dest, _Py_Identifier *name) +treebuilder_extend_element_text_or_tail(PyObject *element, PyObject **data, + PyObject **dest, _Py_Identifier *name) { + /* Fast paths for the "almost always" cases. */ if (Element_CheckExact(element)) { - PyObject *tmp = JOIN_OBJ(*dest); - *dest = JOIN_SET(*data, PyList_CheckExact(*data)); - *data = NULL; - Py_DECREF(tmp); - return 0; + PyObject *dest_obj = JOIN_OBJ(*dest); + if (dest_obj == Py_None) { + *dest = JOIN_SET(*data, PyList_CheckExact(*data)); + *data = NULL; + Py_DECREF(dest_obj); + return 0; + } + else if (JOIN_GET(*dest)) { + if (PyList_SetSlice(dest_obj, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, *data) < 0) { + return -1; + } + Py_CLEAR(*data); + return 0; + } } - else { - PyObject *joined = list_join(*data); + + /* Fallback for the non-Element / non-trivial cases. */ + { int r; - if (joined == NULL) + PyObject* joined; + PyObject* previous = _PyObject_GetAttrId(element, name); + if (!previous) + return -1; + joined = list_join(*data); + if (!joined) { + Py_DECREF(previous); return -1; + } + if (previous != Py_None) { + PyObject *tmp = PyNumber_Add(previous, joined); + Py_DECREF(joined); + Py_DECREF(previous); + if (!tmp) + return -1; + joined = tmp; + } else { + Py_DECREF(previous); + } + r = _PyObject_SetAttrId(element, name, joined); Py_DECREF(joined); if (r < 0) @@ -2649,21 +2681,21 @@ treebuilder_set_element_text_or_tail(PyObject *element, PyObject **data, LOCAL(int) treebuilder_flush_data(TreeBuilderObject* self) { - PyObject *element = self->last; - if (!self->data) { return 0; } - if (self->this == element) { + if (!self->last_for_tail) { + PyObject *element = self->last; _Py_IDENTIFIER(text); - return treebuilder_set_element_text_or_tail( + return treebuilder_extend_element_text_or_tail( element, &self->data, &((ElementObject *) element)->text, &PyId_text); } else { + PyObject *element = self->last_for_tail; _Py_IDENTIFIER(tail); - return treebuilder_set_element_text_or_tail( + return treebuilder_extend_element_text_or_tail( element, &self->data, &((ElementObject *) element)->tail, &PyId_tail); } @@ -2739,6 +2771,7 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag, } this = self->this; + Py_CLEAR(self->last_for_tail); if (this != Py_None) { if (treebuilder_add_subelement(this, node) < 0) @@ -2836,6 +2869,8 @@ treebuilder_handle_end(TreeBuilderObject* self, PyObject* tag) item = self->last; self->last = self->this; + Py_INCREF(self->last); + Py_XSETREF(self->last_for_tail, self->last); self->index--; self->this = PyList_GET_ITEM(self->stack, self->index); Py_INCREF(self->this); @@ -2851,7 +2886,7 @@ treebuilder_handle_end(TreeBuilderObject* self, PyObject* tag) LOCAL(PyObject*) treebuilder_handle_comment(TreeBuilderObject* self, PyObject* text) { - PyObject* comment = NULL; + PyObject* comment; PyObject* this; if (treebuilder_flush_data(self) < 0) { @@ -2867,6 +2902,8 @@ treebuilder_handle_comment(TreeBuilderObject* self, PyObject* text) if (self->insert_comments && this != Py_None) { if (treebuilder_add_subelement(this, comment) < 0) goto error; + Py_INCREF(comment); + Py_XSETREF(self->last_for_tail, comment); } } else { Py_INCREF(text); @@ -2888,7 +2925,7 @@ treebuilder_handle_comment(TreeBuilderObject* self, PyObject* text) LOCAL(PyObject*) treebuilder_handle_pi(TreeBuilderObject* self, PyObject* target, PyObject* text) { - PyObject* pi = NULL; + PyObject* pi; PyObject* this; PyObject* stack[2] = {target, text}; @@ -2906,6 +2943,8 @@ treebuilder_handle_pi(TreeBuilderObject* self, PyObject* target, PyObject* text) if (self->insert_pis && this != Py_None) { if (treebuilder_add_subelement(this, pi) < 0) goto error; + Py_INCREF(pi); + Py_XSETREF(self->last_for_tail, pi); } } else { pi = PyTuple_Pack(2, target, text); @@ -3495,8 +3534,8 @@ expat_end_ns_handler(XMLParserObject* self, const XML_Char* prefix_in) static void expat_comment_handler(XMLParserObject* self, const XML_Char* comment_in) { - PyObject* comment = NULL; - PyObject* res = NULL; + PyObject* comment; + PyObject* res; if (PyErr_Occurred()) return; @@ -3510,16 +3549,17 @@ expat_comment_handler(XMLParserObject* self, const XML_Char* comment_in) return; /* parser will look for errors */ res = treebuilder_handle_comment(target, comment); + Py_XDECREF(res); + Py_DECREF(comment); } else if (self->handle_comment) { comment = PyUnicode_DecodeUTF8(comment_in, strlen(comment_in), "strict"); if (!comment) return; res = _PyObject_CallOneArg(self->handle_comment, comment); + Py_XDECREF(res); + Py_DECREF(comment); } - - Py_XDECREF(res); - Py_DECREF(comment); } static void @@ -3587,7 +3627,7 @@ static void expat_pi_handler(XMLParserObject* self, const XML_Char* target_in, const XML_Char* data_in) { - PyObject* pi_target = NULL; + PyObject* pi_target; PyObject* data; PyObject* res; PyObject* stack[2]; @@ -3599,7 +3639,7 @@ expat_pi_handler(XMLParserObject* self, const XML_Char* target_in, /* shortcut */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; - if (target->events_append && target->pi_event_obj) { + if ((target->events_append && target->pi_event_obj) || target->insert_pis) { pi_target = PyUnicode_DecodeUTF8(target_in, strlen(target_in), "strict"); if (!pi_target) goto error; From webhook-mailer at python.org Wed Jul 24 14:22:57 2019 From: webhook-mailer at python.org (Stefan Behnel) Date: Wed, 24 Jul 2019 18:22:57 -0000 Subject: [Python-checkins] bpo-34160: explain how to deal with attribute order in ElementTree (GH-14867) Message-ID: https://github.com/python/cpython/commit/a3697db0102b9b6747fe36009e42f9b08f0c1ea8 commit: a3697db0102b9b6747fe36009e42f9b08f0c1ea8 branch: master author: Stefan Behnel committer: GitHub date: 2019-07-24T20:22:50+02:00 summary: bpo-34160: explain how to deal with attribute order in ElementTree (GH-14867) * Fix the formatting in the documentation of the tostring() functions. * bpo-34160: Document that the tostring() and tostringlist() functions also preserve the attribute order now. * bpo-34160: Add an explanation of how users should deal with the attribute order. files: M Doc/library/xml.etree.elementtree.rst diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index c4667315793e..9f46755c2685 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -659,7 +659,7 @@ Functions .. function:: tostring(element, encoding="us-ascii", method="xml", *, \ - xml_declaration=None, default_namespace=None, + xml_declaration=None, default_namespace=None, \ short_empty_elements=True) Generates a string representation of an XML element, including all @@ -677,9 +677,13 @@ Functions .. versionadded:: 3.8 The *xml_declaration* and *default_namespace* parameters. + .. versionchanged:: 3.8 + The :func:`tostring` function now preserves the attribute order + specified by the user. + .. function:: tostringlist(element, encoding="us-ascii", method="xml", *, \ - xml_declaration=None, default_namespace=None, + xml_declaration=None, default_namespace=None, \ short_empty_elements=True) Generates a string representation of an XML element, including all @@ -700,6 +704,10 @@ Functions .. versionadded:: 3.8 The *xml_declaration* and *default_namespace* parameters. + .. versionchanged:: 3.8 + The :func:`tostringlist` function now preserves the attribute order + specified by the user. + .. function:: XML(text, parser=None) @@ -930,6 +938,36 @@ Element Objects if element is None: print("element not found") + Prior to Python 3.8, the serialisation order of the XML attributes of + elements was artificially made predictable by sorting the attributes by + their name. Based on the now guaranteed ordering of dicts, this arbitrary + reordering was removed in Python 3.8 to preserve the order in which + attributes were originally parsed or created by user code. + + In general, user code should try not to depend on a specific ordering of + attributes, given that the `XML Information Set + `_ explicitly excludes the attribute + order from conveying information. Code should be prepared to deal with + any ordering on input. In cases where deterministic XML output is required, + e.g. for cryptographic signing or test data sets, canonical serialisation + is available with the :func:`canonicalize` function. + + In cases where canonical output is not applicable but a specific attribute + order is still desirable on output, code should aim for creating the + attributes directly in the desired order, to avoid perceptual mismatches + for readers of the code. In cases where this is difficult to achieve, a + recipe like the following can be applied prior to serialisation to enforce + an order independently from the Element creation:: + + def reorder_attributes(root): + for el in root.iter(): + attrib = el.attrib + if len(attrib) > 1: + # adjust attribute order, e.g. by sorting + attribs = sorted(attrib.items()) + attrib.clear() + attrib.update(attribs) + .. _elementtree-elementtree-objects: From webhook-mailer at python.org Wed Jul 24 14:33:06 2019 From: webhook-mailer at python.org (Stefan Behnel) Date: Wed, 24 Jul 2019 18:33:06 -0000 Subject: [Python-checkins] [3.8] bpo-34160: explain how to deal with attribute order in ElementTree (GH-14867) (GH-14935) Message-ID: https://github.com/python/cpython/commit/63673916464bace8e2147357395fdf3497967ecb commit: 63673916464bace8e2147357395fdf3497967ecb branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Stefan Behnel date: 2019-07-24T20:32:56+02:00 summary: [3.8] bpo-34160: explain how to deal with attribute order in ElementTree (GH-14867) (GH-14935) * Fix the formatting in the documentation of the tostring() functions. * bpo-34160: Document that the tostring() and tostringlist() functions also preserve the attribute order now. * bpo-34160: Add an explanation of how users should deal with the attribute order. (cherry picked from commit a3697db0102b9b6747fe36009e42f9b08f0c1ea8) Co-authored-by: Stefan Behnel files: M Doc/library/xml.etree.elementtree.rst diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index c4667315793e..9f46755c2685 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -659,7 +659,7 @@ Functions .. function:: tostring(element, encoding="us-ascii", method="xml", *, \ - xml_declaration=None, default_namespace=None, + xml_declaration=None, default_namespace=None, \ short_empty_elements=True) Generates a string representation of an XML element, including all @@ -677,9 +677,13 @@ Functions .. versionadded:: 3.8 The *xml_declaration* and *default_namespace* parameters. + .. versionchanged:: 3.8 + The :func:`tostring` function now preserves the attribute order + specified by the user. + .. function:: tostringlist(element, encoding="us-ascii", method="xml", *, \ - xml_declaration=None, default_namespace=None, + xml_declaration=None, default_namespace=None, \ short_empty_elements=True) Generates a string representation of an XML element, including all @@ -700,6 +704,10 @@ Functions .. versionadded:: 3.8 The *xml_declaration* and *default_namespace* parameters. + .. versionchanged:: 3.8 + The :func:`tostringlist` function now preserves the attribute order + specified by the user. + .. function:: XML(text, parser=None) @@ -930,6 +938,36 @@ Element Objects if element is None: print("element not found") + Prior to Python 3.8, the serialisation order of the XML attributes of + elements was artificially made predictable by sorting the attributes by + their name. Based on the now guaranteed ordering of dicts, this arbitrary + reordering was removed in Python 3.8 to preserve the order in which + attributes were originally parsed or created by user code. + + In general, user code should try not to depend on a specific ordering of + attributes, given that the `XML Information Set + `_ explicitly excludes the attribute + order from conveying information. Code should be prepared to deal with + any ordering on input. In cases where deterministic XML output is required, + e.g. for cryptographic signing or test data sets, canonical serialisation + is available with the :func:`canonicalize` function. + + In cases where canonical output is not applicable but a specific attribute + order is still desirable on output, code should aim for creating the + attributes directly in the desired order, to avoid perceptual mismatches + for readers of the code. In cases where this is difficult to achieve, a + recipe like the following can be applied prior to serialisation to enforce + an order independently from the Element creation:: + + def reorder_attributes(root): + for el in root.iter(): + attrib = el.attrib + if len(attrib) > 1: + # adjust attribute order, e.g. by sorting + attribs = sorted(attrib.items()) + attrib.clear() + attrib.update(attribs) + .. _elementtree-elementtree-objects: From webhook-mailer at python.org Wed Jul 24 14:46:13 2019 From: webhook-mailer at python.org (Stefan Behnel) Date: Wed, 24 Jul 2019 18:46:13 -0000 Subject: [Python-checkins] [3.8] bpo-37399: Correctly attach tail text to the last element/comment/pi (GH-14856) (GH-14936) Message-ID: https://github.com/python/cpython/commit/bb697899aa65d90488af1950ac7cceeb3877d409 commit: bb697899aa65d90488af1950ac7cceeb3877d409 branch: 3.8 author: Stefan Behnel committer: GitHub date: 2019-07-24T20:46:01+02:00 summary: [3.8] bpo-37399: Correctly attach tail text to the last element/comment/pi (GH-14856) (GH-14936) * bpo-37399: Correctly attach tail text to the last element/comment/pi, even when comments or pis are discarded. Also fixes the insertion of PIs when "insert_pis=True" is configured for a TreeBuilder. files: M Lib/test/test_xml_etree.py M Modules/_elementtree.c diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 61737493a904..b2492cda848f 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2954,6 +2954,66 @@ def test_treebuilder_pi(self): self.assertEqual(b.pi('target'), (len('target'), None)) self.assertEqual(b.pi('pitarget', ' text '), (len('pitarget'), ' text ')) + def test_late_tail(self): + # Issue #37399: The tail of an ignored comment could overwrite the text before it. + class TreeBuilderSubclass(ET.TreeBuilder): + pass + + xml = "texttail" + a = ET.fromstring(xml) + self.assertEqual(a.text, "texttail") + + parser = ET.XMLParser(target=TreeBuilderSubclass()) + parser.feed(xml) + a = parser.close() + self.assertEqual(a.text, "texttail") + + xml = "texttail" + a = ET.fromstring(xml) + self.assertEqual(a.text, "texttail") + + xml = "texttail" + parser = ET.XMLParser(target=TreeBuilderSubclass()) + parser.feed(xml) + a = parser.close() + self.assertEqual(a.text, "texttail") + + def test_late_tail_mix_pi_comments(self): + # Issue #37399: The tail of an ignored comment could overwrite the text before it. + # Test appending tails to comments/pis. + class TreeBuilderSubclass(ET.TreeBuilder): + pass + + xml = "text \ntail" + parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=True)) + parser.feed(xml) + a = parser.close() + self.assertEqual(a[0].text, ' comment ') + self.assertEqual(a[0].tail, '\ntail') + self.assertEqual(a.text, "text ") + + parser = ET.XMLParser(target=TreeBuilderSubclass(insert_comments=True)) + parser.feed(xml) + a = parser.close() + self.assertEqual(a[0].text, ' comment ') + self.assertEqual(a[0].tail, '\ntail') + self.assertEqual(a.text, "text ") + + xml = "text\ntail" + parser = ET.XMLParser(target=ET.TreeBuilder(insert_pis=True)) + parser.feed(xml) + a = parser.close() + self.assertEqual(a[0].text, 'pi data') + self.assertEqual(a[0].tail, 'tail') + self.assertEqual(a.text, "text\n") + + parser = ET.XMLParser(target=TreeBuilderSubclass(insert_pis=True)) + parser.feed(xml) + a = parser.close() + self.assertEqual(a[0].text, 'pi data') + self.assertEqual(a[0].tail, 'tail') + self.assertEqual(a.text, "text\n") + def test_treebuilder_elementfactory_none(self): parser = ET.XMLParser(target=ET.TreeBuilder(element_factory=None)) parser.feed(self.sample1) diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 8119c8b1e2b1..75c94bcf3a09 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2399,6 +2399,7 @@ typedef struct { PyObject *this; /* current node */ PyObject *last; /* most recently created node */ + PyObject *last_for_tail; /* most recently created node that takes a tail */ PyObject *data; /* data collector (string or list), or NULL */ @@ -2530,6 +2531,7 @@ treebuilder_gc_traverse(TreeBuilderObject *self, visitproc visit, void *arg) Py_VISIT(self->root); Py_VISIT(self->this); Py_VISIT(self->last); + Py_VISIT(self->last_for_tail); Py_VISIT(self->data); Py_VISIT(self->stack); Py_VISIT(self->pi_factory); @@ -2551,6 +2553,7 @@ treebuilder_gc_clear(TreeBuilderObject *self) Py_CLEAR(self->stack); Py_CLEAR(self->data); Py_CLEAR(self->last); + Py_CLEAR(self->last_for_tail); Py_CLEAR(self->this); Py_CLEAR(self->pi_factory); Py_CLEAR(self->comment_factory); @@ -2622,21 +2625,50 @@ _elementtree__set_factories_impl(PyObject *module, PyObject *comment_factory, } static int -treebuilder_set_element_text_or_tail(PyObject *element, PyObject **data, - PyObject **dest, _Py_Identifier *name) +treebuilder_extend_element_text_or_tail(PyObject *element, PyObject **data, + PyObject **dest, _Py_Identifier *name) { + /* Fast paths for the "almost always" cases. */ if (Element_CheckExact(element)) { - PyObject *tmp = JOIN_OBJ(*dest); - *dest = JOIN_SET(*data, PyList_CheckExact(*data)); - *data = NULL; - Py_DECREF(tmp); - return 0; + PyObject *dest_obj = JOIN_OBJ(*dest); + if (dest_obj == Py_None) { + *dest = JOIN_SET(*data, PyList_CheckExact(*data)); + *data = NULL; + Py_DECREF(dest_obj); + return 0; + } + else if (JOIN_GET(*dest)) { + if (PyList_SetSlice(dest_obj, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, *data) < 0) { + return -1; + } + Py_CLEAR(*data); + return 0; + } } - else { - PyObject *joined = list_join(*data); + + /* Fallback for the non-Element / non-trivial cases. */ + { int r; - if (joined == NULL) + PyObject* joined; + PyObject* previous = _PyObject_GetAttrId(element, name); + if (!previous) + return -1; + joined = list_join(*data); + if (!joined) { + Py_DECREF(previous); return -1; + } + if (previous != Py_None) { + PyObject *tmp = PyNumber_Add(previous, joined); + Py_DECREF(joined); + Py_DECREF(previous); + if (!tmp) + return -1; + joined = tmp; + } else { + Py_DECREF(previous); + } + r = _PyObject_SetAttrId(element, name, joined); Py_DECREF(joined); if (r < 0) @@ -2649,21 +2681,21 @@ treebuilder_set_element_text_or_tail(PyObject *element, PyObject **data, LOCAL(int) treebuilder_flush_data(TreeBuilderObject* self) { - PyObject *element = self->last; - if (!self->data) { return 0; } - if (self->this == element) { + if (!self->last_for_tail) { + PyObject *element = self->last; _Py_IDENTIFIER(text); - return treebuilder_set_element_text_or_tail( + return treebuilder_extend_element_text_or_tail( element, &self->data, &((ElementObject *) element)->text, &PyId_text); } else { + PyObject *element = self->last_for_tail; _Py_IDENTIFIER(tail); - return treebuilder_set_element_text_or_tail( + return treebuilder_extend_element_text_or_tail( element, &self->data, &((ElementObject *) element)->tail, &PyId_tail); } @@ -2739,6 +2771,7 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag, } this = self->this; + Py_CLEAR(self->last_for_tail); if (this != Py_None) { if (treebuilder_add_subelement(this, node) < 0) @@ -2836,6 +2869,8 @@ treebuilder_handle_end(TreeBuilderObject* self, PyObject* tag) item = self->last; self->last = self->this; + Py_INCREF(self->last); + Py_XSETREF(self->last_for_tail, self->last); self->index--; self->this = PyList_GET_ITEM(self->stack, self->index); Py_INCREF(self->this); @@ -2851,7 +2886,7 @@ treebuilder_handle_end(TreeBuilderObject* self, PyObject* tag) LOCAL(PyObject*) treebuilder_handle_comment(TreeBuilderObject* self, PyObject* text) { - PyObject* comment = NULL; + PyObject* comment; PyObject* this; if (treebuilder_flush_data(self) < 0) { @@ -2867,6 +2902,8 @@ treebuilder_handle_comment(TreeBuilderObject* self, PyObject* text) if (self->insert_comments && this != Py_None) { if (treebuilder_add_subelement(this, comment) < 0) goto error; + Py_INCREF(comment); + Py_XSETREF(self->last_for_tail, comment); } } else { Py_INCREF(text); @@ -2888,7 +2925,7 @@ treebuilder_handle_comment(TreeBuilderObject* self, PyObject* text) LOCAL(PyObject*) treebuilder_handle_pi(TreeBuilderObject* self, PyObject* target, PyObject* text) { - PyObject* pi = NULL; + PyObject* pi; PyObject* this; PyObject* stack[2] = {target, text}; @@ -2906,6 +2943,8 @@ treebuilder_handle_pi(TreeBuilderObject* self, PyObject* target, PyObject* text) if (self->insert_pis && this != Py_None) { if (treebuilder_add_subelement(this, pi) < 0) goto error; + Py_INCREF(pi); + Py_XSETREF(self->last_for_tail, pi); } } else { pi = PyTuple_Pack(2, target, text); @@ -3495,8 +3534,8 @@ expat_end_ns_handler(XMLParserObject* self, const XML_Char* prefix_in) static void expat_comment_handler(XMLParserObject* self, const XML_Char* comment_in) { - PyObject* comment = NULL; - PyObject* res = NULL; + PyObject* comment; + PyObject* res; if (PyErr_Occurred()) return; @@ -3510,16 +3549,17 @@ expat_comment_handler(XMLParserObject* self, const XML_Char* comment_in) return; /* parser will look for errors */ res = treebuilder_handle_comment(target, comment); + Py_XDECREF(res); + Py_DECREF(comment); } else if (self->handle_comment) { comment = PyUnicode_DecodeUTF8(comment_in, strlen(comment_in), "strict"); if (!comment) return; res = _PyObject_FastCall(self->handle_comment, &comment, 1); + Py_XDECREF(res); + Py_DECREF(comment); } - - Py_XDECREF(res); - Py_DECREF(comment); } static void @@ -3587,7 +3627,7 @@ static void expat_pi_handler(XMLParserObject* self, const XML_Char* target_in, const XML_Char* data_in) { - PyObject* pi_target = NULL; + PyObject* pi_target; PyObject* data; PyObject* res; PyObject* stack[2]; @@ -3599,7 +3639,7 @@ expat_pi_handler(XMLParserObject* self, const XML_Char* target_in, /* shortcut */ TreeBuilderObject *target = (TreeBuilderObject*) self->target; - if (target->events_append && target->pi_event_obj) { + if ((target->events_append && target->pi_event_obj) || target->insert_pis) { pi_target = PyUnicode_DecodeUTF8(target_in, strlen(target_in), "strict"); if (!pi_target) goto error; From webhook-mailer at python.org Wed Jul 24 15:08:07 2019 From: webhook-mailer at python.org (Steve Dower) Date: Wed, 24 Jul 2019 19:08:07 -0000 Subject: [Python-checkins] bpo-37664: Update bundled pip to 19.2.1 and setuptools to 41.0.1 (GH-14934) Message-ID: https://github.com/python/cpython/commit/5380def8269b24a8a3bc46396373a1dc91b1dd1a commit: 5380def8269b24a8a3bc46396373a1dc91b1dd1a branch: master author: Pradyun Gedam committer: Steve Dower date: 2019-07-24T12:08:02-07:00 summary: bpo-37664: Update bundled pip to 19.2.1 and setuptools to 41.0.1 (GH-14934) files: A Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl A Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl A Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst D Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl D Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl M Lib/ensurepip/__init__.py diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 6f2569da8167..63de20e28873 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -8,9 +8,9 @@ __all__ = ["version", "bootstrap"] -_SETUPTOOLS_VERSION = "40.8.0" +_SETUPTOOLS_VERSION = "41.0.1" -_PIP_VERSION = "19.0.3" +_PIP_VERSION = "19.2.1" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION), diff --git a/Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl similarity index 51% rename from Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl index 24f247caa0ab..1690eb3f4f2a 100644 Binary files a/Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl and b/Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl differ diff --git a/Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl similarity index 86% rename from Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl rename to Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl index fdc66e9330e0..92836e984026 100644 Binary files a/Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl and b/Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl differ diff --git a/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst b/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst new file mode 100644 index 000000000000..3343d9100946 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst @@ -0,0 +1 @@ +Update wheels bundled with ensurepip (pip 19.2.1 and setuptools 41.0.1) From webhook-mailer at python.org Wed Jul 24 15:28:16 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 24 Jul 2019 19:28:16 -0000 Subject: [Python-checkins] bpo-37664: Update bundled pip to 19.2.1 and setuptools to 41.0.1 (GH-14934) Message-ID: https://github.com/python/cpython/commit/0cdb21d6eb3428abe50a55f9291ca0e9728654d9 commit: 0cdb21d6eb3428abe50a55f9291ca0e9728654d9 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-24T12:28:12-07:00 summary: bpo-37664: Update bundled pip to 19.2.1 and setuptools to 41.0.1 (GH-14934) (cherry picked from commit 5380def8269b24a8a3bc46396373a1dc91b1dd1a) Co-authored-by: Pradyun Gedam files: A Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl A Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl A Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst D Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl D Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl M Lib/ensurepip/__init__.py diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 6f2569da8167..63de20e28873 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -8,9 +8,9 @@ __all__ = ["version", "bootstrap"] -_SETUPTOOLS_VERSION = "40.8.0" +_SETUPTOOLS_VERSION = "41.0.1" -_PIP_VERSION = "19.0.3" +_PIP_VERSION = "19.2.1" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION), diff --git a/Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl similarity index 51% rename from Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl index 24f247caa0ab..1690eb3f4f2a 100644 Binary files a/Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl and b/Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl differ diff --git a/Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl similarity index 86% rename from Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl rename to Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl index fdc66e9330e0..92836e984026 100644 Binary files a/Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl and b/Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl differ diff --git a/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst b/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst new file mode 100644 index 000000000000..3343d9100946 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst @@ -0,0 +1 @@ +Update wheels bundled with ensurepip (pip 19.2.1 and setuptools 41.0.1) From webhook-mailer at python.org Wed Jul 24 16:31:46 2019 From: webhook-mailer at python.org (Pablo Galindo) Date: Wed, 24 Jul 2019 20:31:46 -0000 Subject: [Python-checkins] Remove duplicate call to strip method in Parser/pgen/token.py (GH-14938) Message-ID: https://github.com/python/cpython/commit/e018dc52d1268bc0c5e4037c132e463f1a4002af commit: e018dc52d1268bc0c5e4037c132e463f1a4002af branch: master author: Hansraj Das committer: Pablo Galindo date: 2019-07-24T21:31:19+01:00 summary: Remove duplicate call to strip method in Parser/pgen/token.py (GH-14938) files: M Parser/pgen/token.py diff --git a/Parser/pgen/token.py b/Parser/pgen/token.py index 008e241e175e..e7e8f3f1b661 100644 --- a/Parser/pgen/token.py +++ b/Parser/pgen/token.py @@ -6,9 +6,7 @@ def generate_tokens(tokens): for line in tokens: line = line.strip() - if not line: - continue - if line.strip().startswith('#'): + if not line or line.startswith('#'): continue name = line.split()[0] @@ -22,9 +20,7 @@ def generate_opmap(tokens): for line in tokens: line = line.strip() - if not line: - continue - if line.strip().startswith('#'): + if not line or line.startswith('#'): continue pieces = line.split() From webhook-mailer at python.org Wed Jul 24 18:13:36 2019 From: webhook-mailer at python.org (Steve Dower) Date: Wed, 24 Jul 2019 22:13:36 -0000 Subject: [Python-checkins] bpo-37672: Switch Windows Store package to use pip.ini for user mode (GH-14939) Message-ID: https://github.com/python/cpython/commit/123536fdab7b8def15c859aa70232bc55ec73096 commit: 123536fdab7b8def15c859aa70232bc55ec73096 branch: master author: Steve Dower committer: GitHub date: 2019-07-24T15:13:22-07:00 summary: bpo-37672: Switch Windows Store package to use pip.ini for user mode (GH-14939) files: A Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst M PC/layout/main.py M PC/layout/support/pip.py M PC/python_uwp.cpp diff --git a/Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst b/Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst new file mode 100644 index 000000000000..78b51c191825 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst @@ -0,0 +1,2 @@ +Switch Windows Store package's pip to use bundled :file:`pip.ini` instead of +:envvar:`PIP_USER` variable. diff --git a/PC/layout/main.py b/PC/layout/main.py index c39aab208d35..111f8aad2561 100644 --- a/PC/layout/main.py +++ b/PC/layout/main.py @@ -228,7 +228,7 @@ def _c(d): if ns.include_pip: for dest, src in get_pip_layout(ns): - if isinstance(src, tuple) or not ( + if not isinstance(src, tuple) and ( src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB ): continue diff --git a/PC/layout/support/pip.py b/PC/layout/support/pip.py index eada456655ec..4ad3b1dd5bc0 100644 --- a/PC/layout/support/pip.py +++ b/PC/layout/support/pip.py @@ -33,7 +33,11 @@ def get_pip_layout(ns): pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}" for dest, src in rglob(pip_dir, "**/*"): yield pkg_root.format(dest), src - yield "pip.ini", ("pip.ini", b"[global]\nuser=yes") + content = "\n".join( + "[{}]\nuser=yes".format(n) + for n in ["install", "uninstall", "freeze", "list"] + ) + yield "pip.ini", ("pip.ini", content.encode()) def extract_pip_files(ns): diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp index 2352f45e8a3d..06c1dd353655 100644 --- a/PC/python_uwp.cpp +++ b/PC/python_uwp.cpp @@ -122,6 +122,12 @@ set_process_name(PyConfig *config) break; } } + size_t i = executable.find_last_of(L"/\\"); + if (i == std::wstring::npos) { + executable = PROGNAME; + } else { + executable.replace(i + 1, std::wstring::npos, PROGNAME); + } } if (!home.empty()) { @@ -163,10 +169,29 @@ wmain(int argc, wchar_t **argv) PyPreConfig preconfig; PyConfig config; + const wchar_t *moduleName = NULL; + const wchar_t *p = wcsrchr(argv[0], L'\\'); + if (!p) { + p = argv[0]; + } + if (p) { + if (*p == L'\\') { + p++; + } + + if (wcsnicmp(p, L"pip", 3) == 0) { + moduleName = L"pip"; + } else if (wcsnicmp(p, L"idle", 4) == 0) { + moduleName = L"idlelib"; + } + } + PyPreConfig_InitPythonConfig(&preconfig); - status = Py_PreInitializeFromArgs(&preconfig, argc, argv); - if (PyStatus_Exception(status)) { - goto fail_without_config; + if (!moduleName) { + status = Py_PreInitializeFromArgs(&preconfig, argc, argv); + if (PyStatus_Exception(status)) { + goto fail_without_config; + } } status = PyConfig_InitPythonConfig(&config); @@ -178,48 +203,32 @@ wmain(int argc, wchar_t **argv) if (PyStatus_Exception(status)) { goto fail; } + if (moduleName) { + config.parse_argv = 0; + } status = set_process_name(&config); if (PyStatus_Exception(status)) { goto fail; } - const wchar_t *p = _wgetenv(L"PYTHONUSERBASE"); + p = _wgetenv(L"PYTHONUSERBASE"); if (!p || !*p) { _wputenv_s(L"PYTHONUSERBASE", get_user_base().c_str()); } - p = wcsrchr(argv[0], L'\\'); - if (!p) { - p = argv[0]; - } - if (p) { - if (*p == L'\\') { - p++; + if (moduleName) { + status = PyConfig_SetString(&config, &config.run_module, moduleName); + if (PyStatus_Exception(status)) { + goto fail; } - - const wchar_t *moduleName = NULL; - if (wcsnicmp(p, L"pip", 3) == 0) { - moduleName = L"pip"; - /* No longer required when pip 19.1 is added */ - _wputenv_s(L"PIP_USER", L"true"); - } else if (wcsnicmp(p, L"idle", 4) == 0) { - moduleName = L"idlelib"; + status = PyConfig_SetString(&config, &config.run_filename, NULL); + if (PyStatus_Exception(status)) { + goto fail; } - - if (moduleName) { - status = PyConfig_SetString(&config, &config.run_module, moduleName); - if (PyStatus_Exception(status)) { - goto fail; - } - status = PyConfig_SetString(&config, &config.run_filename, NULL); - if (PyStatus_Exception(status)) { - goto fail; - } - status = PyConfig_SetString(&config, &config.run_command, NULL); - if (PyStatus_Exception(status)) { - goto fail; - } + status = PyConfig_SetString(&config, &config.run_command, NULL); + if (PyStatus_Exception(status)) { + goto fail; } } From webhook-mailer at python.org Wed Jul 24 18:31:52 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 24 Jul 2019 22:31:52 -0000 Subject: [Python-checkins] bpo-37672: Switch Windows Store package to use pip.ini for user mode (GH-14939) Message-ID: https://github.com/python/cpython/commit/4b7ce105ff80467bf4d79c413d7adc256b824153 commit: 4b7ce105ff80467bf4d79c413d7adc256b824153 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-24T15:31:48-07:00 summary: bpo-37672: Switch Windows Store package to use pip.ini for user mode (GH-14939) (cherry picked from commit 123536fdab7b8def15c859aa70232bc55ec73096) Co-authored-by: Steve Dower files: A Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst M PC/layout/main.py M PC/layout/support/pip.py M PC/python_uwp.cpp diff --git a/Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst b/Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst new file mode 100644 index 000000000000..78b51c191825 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst @@ -0,0 +1,2 @@ +Switch Windows Store package's pip to use bundled :file:`pip.ini` instead of +:envvar:`PIP_USER` variable. diff --git a/PC/layout/main.py b/PC/layout/main.py index c39aab208d35..111f8aad2561 100644 --- a/PC/layout/main.py +++ b/PC/layout/main.py @@ -228,7 +228,7 @@ def _c(d): if ns.include_pip: for dest, src in get_pip_layout(ns): - if isinstance(src, tuple) or not ( + if not isinstance(src, tuple) and ( src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB ): continue diff --git a/PC/layout/support/pip.py b/PC/layout/support/pip.py index eada456655ec..4ad3b1dd5bc0 100644 --- a/PC/layout/support/pip.py +++ b/PC/layout/support/pip.py @@ -33,7 +33,11 @@ def get_pip_layout(ns): pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}" for dest, src in rglob(pip_dir, "**/*"): yield pkg_root.format(dest), src - yield "pip.ini", ("pip.ini", b"[global]\nuser=yes") + content = "\n".join( + "[{}]\nuser=yes".format(n) + for n in ["install", "uninstall", "freeze", "list"] + ) + yield "pip.ini", ("pip.ini", content.encode()) def extract_pip_files(ns): diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp index 2352f45e8a3d..06c1dd353655 100644 --- a/PC/python_uwp.cpp +++ b/PC/python_uwp.cpp @@ -122,6 +122,12 @@ set_process_name(PyConfig *config) break; } } + size_t i = executable.find_last_of(L"/\\"); + if (i == std::wstring::npos) { + executable = PROGNAME; + } else { + executable.replace(i + 1, std::wstring::npos, PROGNAME); + } } if (!home.empty()) { @@ -163,10 +169,29 @@ wmain(int argc, wchar_t **argv) PyPreConfig preconfig; PyConfig config; + const wchar_t *moduleName = NULL; + const wchar_t *p = wcsrchr(argv[0], L'\\'); + if (!p) { + p = argv[0]; + } + if (p) { + if (*p == L'\\') { + p++; + } + + if (wcsnicmp(p, L"pip", 3) == 0) { + moduleName = L"pip"; + } else if (wcsnicmp(p, L"idle", 4) == 0) { + moduleName = L"idlelib"; + } + } + PyPreConfig_InitPythonConfig(&preconfig); - status = Py_PreInitializeFromArgs(&preconfig, argc, argv); - if (PyStatus_Exception(status)) { - goto fail_without_config; + if (!moduleName) { + status = Py_PreInitializeFromArgs(&preconfig, argc, argv); + if (PyStatus_Exception(status)) { + goto fail_without_config; + } } status = PyConfig_InitPythonConfig(&config); @@ -178,48 +203,32 @@ wmain(int argc, wchar_t **argv) if (PyStatus_Exception(status)) { goto fail; } + if (moduleName) { + config.parse_argv = 0; + } status = set_process_name(&config); if (PyStatus_Exception(status)) { goto fail; } - const wchar_t *p = _wgetenv(L"PYTHONUSERBASE"); + p = _wgetenv(L"PYTHONUSERBASE"); if (!p || !*p) { _wputenv_s(L"PYTHONUSERBASE", get_user_base().c_str()); } - p = wcsrchr(argv[0], L'\\'); - if (!p) { - p = argv[0]; - } - if (p) { - if (*p == L'\\') { - p++; + if (moduleName) { + status = PyConfig_SetString(&config, &config.run_module, moduleName); + if (PyStatus_Exception(status)) { + goto fail; } - - const wchar_t *moduleName = NULL; - if (wcsnicmp(p, L"pip", 3) == 0) { - moduleName = L"pip"; - /* No longer required when pip 19.1 is added */ - _wputenv_s(L"PIP_USER", L"true"); - } else if (wcsnicmp(p, L"idle", 4) == 0) { - moduleName = L"idlelib"; + status = PyConfig_SetString(&config, &config.run_filename, NULL); + if (PyStatus_Exception(status)) { + goto fail; } - - if (moduleName) { - status = PyConfig_SetString(&config, &config.run_module, moduleName); - if (PyStatus_Exception(status)) { - goto fail; - } - status = PyConfig_SetString(&config, &config.run_filename, NULL); - if (PyStatus_Exception(status)) { - goto fail; - } - status = PyConfig_SetString(&config, &config.run_command, NULL); - if (PyStatus_Exception(status)) { - goto fail; - } + status = PyConfig_SetString(&config, &config.run_command, NULL); + if (PyStatus_Exception(status)) { + goto fail; } } From webhook-mailer at python.org Wed Jul 24 19:38:54 2019 From: webhook-mailer at python.org (Benjamin Peterson) Date: Wed, 24 Jul 2019 23:38:54 -0000 Subject: [Python-checkins] closes bpo-37675: Use pkgutil.iter_modules to find fixers in a package rather than listdir. (14942) Message-ID: https://github.com/python/cpython/commit/93e8aa62cfd0a61efed4a61a2ffc2283ae986ef2 commit: 93e8aa62cfd0a61efed4a61a2ffc2283ae986ef2 branch: master author: Benjamin Peterson committer: GitHub date: 2019-07-24T16:38:50-07:00 summary: closes bpo-37675: Use pkgutil.iter_modules to find fixers in a package rather than listdir. (14942) files: A Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst M Lib/lib2to3/refactor.py diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py index 7841b99a5cd4..55fd60fa27c3 100644 --- a/Lib/lib2to3/refactor.py +++ b/Lib/lib2to3/refactor.py @@ -14,6 +14,7 @@ # Python imports import io import os +import pkgutil import sys import logging import operator @@ -30,13 +31,12 @@ def get_all_fix_names(fixer_pkg, remove_prefix=True): """Return a sorted list of all available fix names in the given package.""" pkg = __import__(fixer_pkg, [], [], ["*"]) - fixer_dir = os.path.dirname(pkg.__file__) fix_names = [] - for name in sorted(os.listdir(fixer_dir)): - if name.startswith("fix_") and name.endswith(".py"): + for finder, name, ispkg in pkgutil.iter_modules(pkg.__path__): + if name.startswith("fix_"): if remove_prefix: name = name[4:] - fix_names.append(name[:-3]) + fix_names.append(name) return fix_names diff --git a/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst b/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst new file mode 100644 index 000000000000..e28fa207f918 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst @@ -0,0 +1 @@ +2to3 now works when run from a zipped standard library. From webhook-mailer at python.org Wed Jul 24 19:57:43 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 24 Jul 2019 23:57:43 -0000 Subject: [Python-checkins] closes bpo-37675: Use pkgutil.iter_modules to find fixers in a package rather than listdir. (14942) Message-ID: https://github.com/python/cpython/commit/9194a20a5b0b933a2a43ce0e81becbf8f0356314 commit: 9194a20a5b0b933a2a43ce0e81becbf8f0356314 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-24T16:57:39-07:00 summary: closes bpo-37675: Use pkgutil.iter_modules to find fixers in a package rather than listdir. (14942) (cherry picked from commit 93e8aa62cfd0a61efed4a61a2ffc2283ae986ef2) Co-authored-by: Benjamin Peterson files: A Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst M Lib/lib2to3/refactor.py diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py index 7841b99a5cd4..55fd60fa27c3 100644 --- a/Lib/lib2to3/refactor.py +++ b/Lib/lib2to3/refactor.py @@ -14,6 +14,7 @@ # Python imports import io import os +import pkgutil import sys import logging import operator @@ -30,13 +31,12 @@ def get_all_fix_names(fixer_pkg, remove_prefix=True): """Return a sorted list of all available fix names in the given package.""" pkg = __import__(fixer_pkg, [], [], ["*"]) - fixer_dir = os.path.dirname(pkg.__file__) fix_names = [] - for name in sorted(os.listdir(fixer_dir)): - if name.startswith("fix_") and name.endswith(".py"): + for finder, name, ispkg in pkgutil.iter_modules(pkg.__path__): + if name.startswith("fix_"): if remove_prefix: name = name[4:] - fix_names.append(name[:-3]) + fix_names.append(name) return fix_names diff --git a/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst b/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst new file mode 100644 index 000000000000..e28fa207f918 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst @@ -0,0 +1 @@ +2to3 now works when run from a zipped standard library. From webhook-mailer at python.org Wed Jul 24 19:59:35 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 24 Jul 2019 23:59:35 -0000 Subject: [Python-checkins] closes bpo-37675: Use pkgutil.iter_modules to find fixers in a package rather than listdir. (14942) Message-ID: https://github.com/python/cpython/commit/53639dd55a0d5b3b7b4ef6ae839a98008f22e2d3 commit: 53639dd55a0d5b3b7b4ef6ae839a98008f22e2d3 branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-24T16:59:31-07:00 summary: closes bpo-37675: Use pkgutil.iter_modules to find fixers in a package rather than listdir. (14942) (cherry picked from commit 93e8aa62cfd0a61efed4a61a2ffc2283ae986ef2) Co-authored-by: Benjamin Peterson files: A Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst M Lib/lib2to3/refactor.py diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py index 98386c5f310d..8a40deb8ac21 100644 --- a/Lib/lib2to3/refactor.py +++ b/Lib/lib2to3/refactor.py @@ -15,6 +15,7 @@ # Python imports import os +import pkgutil import sys import logging import operator @@ -33,13 +34,12 @@ def get_all_fix_names(fixer_pkg, remove_prefix=True): """Return a sorted list of all available fix names in the given package.""" pkg = __import__(fixer_pkg, [], [], ["*"]) - fixer_dir = os.path.dirname(pkg.__file__) fix_names = [] - for name in sorted(os.listdir(fixer_dir)): - if name.startswith("fix_") and name.endswith(".py"): + for finder, name, ispkg in pkgutil.iter_modules(pkg.__path__): + if name.startswith("fix_"): if remove_prefix: name = name[4:] - fix_names.append(name[:-3]) + fix_names.append(name) return fix_names diff --git a/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst b/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst new file mode 100644 index 000000000000..e28fa207f918 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst @@ -0,0 +1 @@ +2to3 now works when run from a zipped standard library. From webhook-mailer at python.org Wed Jul 24 20:00:43 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 25 Jul 2019 00:00:43 -0000 Subject: [Python-checkins] closes bpo-37675: Use pkgutil.iter_modules to find fixers in a package rather than listdir. (14942) Message-ID: https://github.com/python/cpython/commit/69802f6163c9f18ca0e0b9c4c43a49365fc63e2d commit: 69802f6163c9f18ca0e0b9c4c43a49365fc63e2d branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-24T17:00:39-07:00 summary: closes bpo-37675: Use pkgutil.iter_modules to find fixers in a package rather than listdir. (14942) (cherry picked from commit 93e8aa62cfd0a61efed4a61a2ffc2283ae986ef2) Co-authored-by: Benjamin Peterson files: A Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst M Lib/lib2to3/refactor.py diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py index 7841b99a5cd4..55fd60fa27c3 100644 --- a/Lib/lib2to3/refactor.py +++ b/Lib/lib2to3/refactor.py @@ -14,6 +14,7 @@ # Python imports import io import os +import pkgutil import sys import logging import operator @@ -30,13 +31,12 @@ def get_all_fix_names(fixer_pkg, remove_prefix=True): """Return a sorted list of all available fix names in the given package.""" pkg = __import__(fixer_pkg, [], [], ["*"]) - fixer_dir = os.path.dirname(pkg.__file__) fix_names = [] - for name in sorted(os.listdir(fixer_dir)): - if name.startswith("fix_") and name.endswith(".py"): + for finder, name, ispkg in pkgutil.iter_modules(pkg.__path__): + if name.startswith("fix_"): if remove_prefix: name = name[4:] - fix_names.append(name[:-3]) + fix_names.append(name) return fix_names diff --git a/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst b/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst new file mode 100644 index 000000000000..e28fa207f918 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst @@ -0,0 +1 @@ +2to3 now works when run from a zipped standard library. From webhook-mailer at python.org Thu Jul 25 12:00:42 2019 From: webhook-mailer at python.org (Antoine Pitrou) Date: Thu, 25 Jul 2019 16:00:42 -0000 Subject: [Python-checkins] bpo-37502: handle default parameter for buffers argument of pickle.loads correctly (GH-14593) Message-ID: https://github.com/python/cpython/commit/898318b53d921298d1f1fcfa0f415844afbeb318 commit: 898318b53d921298d1f1fcfa0f415844afbeb318 branch: master author: Markus Mohrhard committer: Antoine Pitrou date: 2019-07-25T18:00:34+02:00 summary: bpo-37502: handle default parameter for buffers argument of pickle.loads correctly (GH-14593) files: A Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst M Lib/test/pickletester.py M Modules/_pickle.c diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index f8f3bc92e7fe..2bc99e6becfd 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2765,6 +2765,11 @@ def test_buffers_error(self): with self.assertRaises(pickle.UnpicklingError): self.loads(data, buffers=[]) + def test_inband_accept_default_buffers_argument(self): + for proto in range(5, pickle.HIGHEST_PROTOCOL + 1): + data_pickled = self.dumps(1, proto, buffer_callback=None) + data = self.loads(data_pickled, buffers=None) + @unittest.skipIf(np is None, "Test needs Numpy") def test_buffers_numpy(self): def check_no_copy(x, y): diff --git a/Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst b/Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst new file mode 100644 index 000000000000..4eb6d0974bde --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst @@ -0,0 +1 @@ +pickle.loads() no longer raises TypeError when the buffers argument is set to None diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 054276d85adf..0c53f2e18685 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -1653,7 +1653,7 @@ _Unpickler_SetInputEncoding(UnpicklerObject *self, static int _Unpickler_SetBuffers(UnpicklerObject *self, PyObject *buffers) { - if (buffers == NULL) { + if (buffers == NULL || buffers == Py_None) { self->buffers = NULL; } else { From webhook-mailer at python.org Thu Jul 25 12:18:24 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 25 Jul 2019 16:18:24 -0000 Subject: [Python-checkins] bpo-37502: handle default parameter for buffers argument of pickle.loads correctly (GH-14593) Message-ID: https://github.com/python/cpython/commit/25cb4fd4fb0f44d2b6bf38379634f3d22b77aa17 commit: 25cb4fd4fb0f44d2b6bf38379634f3d22b77aa17 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-25T09:18:20-07:00 summary: bpo-37502: handle default parameter for buffers argument of pickle.loads correctly (GH-14593) (cherry picked from commit 898318b53d921298d1f1fcfa0f415844afbeb318) Co-authored-by: Markus Mohrhard files: A Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst M Lib/test/pickletester.py M Modules/_pickle.c diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index f8f3bc92e7fe..2bc99e6becfd 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2765,6 +2765,11 @@ def test_buffers_error(self): with self.assertRaises(pickle.UnpicklingError): self.loads(data, buffers=[]) + def test_inband_accept_default_buffers_argument(self): + for proto in range(5, pickle.HIGHEST_PROTOCOL + 1): + data_pickled = self.dumps(1, proto, buffer_callback=None) + data = self.loads(data_pickled, buffers=None) + @unittest.skipIf(np is None, "Test needs Numpy") def test_buffers_numpy(self): def check_no_copy(x, y): diff --git a/Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst b/Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst new file mode 100644 index 000000000000..4eb6d0974bde --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst @@ -0,0 +1 @@ +pickle.loads() no longer raises TypeError when the buffers argument is set to None diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 0a597575b358..bbe26034881a 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -1653,7 +1653,7 @@ _Unpickler_SetInputEncoding(UnpicklerObject *self, static int _Unpickler_SetBuffers(UnpicklerObject *self, PyObject *buffers) { - if (buffers == NULL) { + if (buffers == NULL || buffers == Py_None) { self->buffers = NULL; } else { From webhook-mailer at python.org Thu Jul 25 13:20:51 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 25 Jul 2019 17:20:51 -0000 Subject: [Python-checkins] Swap 'if' branches so content matches to condition in importlib example (GH-14947) Message-ID: https://github.com/python/cpython/commit/544fa15ea1b7b73068319bdb217b684e2fd7bacc commit: 544fa15ea1b7b73068319bdb217b684e2fd7bacc branch: master author: Tzu-ping Chung committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-25T10:20:33-07:00 summary: Swap 'if' branches so content matches to condition in importlib example (GH-14947) Prior to this change the guard on an 'elif' used an assignment expression whose value was used in a later 'else' block, causing some confusion for people. (Discussion on Twitter: https://twitter.com/brettsky/status/1153861041068994566.) Automerge-Triggered-By: @brettcannon files: M Doc/library/importlib.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 9fab0779aa74..65a685022e92 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1640,14 +1640,14 @@ import, then you should use :func:`importlib.util.find_spec`. if name in sys.modules: print(f"{name!r} already in sys.modules") - elif (spec := importlib.util.find_spec(name)) is None: - print(f"can't find the {name!r} module") - else: + elif (spec := importlib.util.find_spec(name)) is not None: # If you chose to perform the actual import ... module = importlib.util.module_from_spec(spec) sys.modules[name] = module spec.loader.exec_module(module) print(f"{name!r} has been imported") + else: + print(f"can't find the {name!r} module") Importing a source file directly From webhook-mailer at python.org Thu Jul 25 13:28:05 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 25 Jul 2019 17:28:05 -0000 Subject: [Python-checkins] Swap 'if' branches so content matches to condition in importlib example (GH-14947) Message-ID: https://github.com/python/cpython/commit/c594c274e92dc66afad02fe7a3c08a9b6c5a396d commit: c594c274e92dc66afad02fe7a3c08a9b6c5a396d branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-25T10:27:59-07:00 summary: Swap 'if' branches so content matches to condition in importlib example (GH-14947) Prior to this change the guard on an 'elif' used an assignment expression whose value was used in a later 'else' block, causing some confusion for people. (Discussion on Twitter: https://twitter.com/brettsky/status/1153861041068994566.) Automerge-Triggered-By: @brettcannon (cherry picked from commit 544fa15ea1b7b73068319bdb217b684e2fd7bacc) Co-authored-by: Tzu-ping Chung files: M Doc/library/importlib.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 9fab0779aa74..65a685022e92 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1640,14 +1640,14 @@ import, then you should use :func:`importlib.util.find_spec`. if name in sys.modules: print(f"{name!r} already in sys.modules") - elif (spec := importlib.util.find_spec(name)) is None: - print(f"can't find the {name!r} module") - else: + elif (spec := importlib.util.find_spec(name)) is not None: # If you chose to perform the actual import ... module = importlib.util.module_from_spec(spec) sys.modules[name] = module spec.loader.exec_module(module) print(f"{name!r} has been imported") + else: + print(f"can't find the {name!r} module") Importing a source file directly From webhook-mailer at python.org Thu Jul 25 17:37:04 2019 From: webhook-mailer at python.org (Steve Dower) Date: Thu, 25 Jul 2019 21:37:04 -0000 Subject: [Python-checkins] bpo-37641 preserve relative file location in embeddable zip (GH-14884) Message-ID: https://github.com/python/cpython/commit/c4cda4369f4b8f33082890d16dfc364a90658ef6 commit: c4cda4369f4b8f33082890d16dfc364a90658ef6 branch: master author: Bill Collins committer: Steve Dower date: 2019-07-25T14:36:58-07:00 summary: bpo-37641 preserve relative file location in embeddable zip (GH-14884) Previously, pyc files in the embeddable distribution reported their location as /.py. This causes a little confusion when interpreting stack traces as the file is in a (almost certainly) incorrect location, and lacks the full relative path to Lib (e.g. email/mime/image.py will only show image.py). This change preserves the Lib relative location of the source file as a path so that stack traces are (hopefully) less misleading and more informative. Co-Authored-By: Kyle Stanley files: M PC/layout/main.py diff --git a/PC/layout/main.py b/PC/layout/main.py index 111f8aad2561..fe934bfb1ab3 100644 --- a/PC/layout/main.py +++ b/PC/layout/main.py @@ -287,19 +287,18 @@ def _compile_one_py(src, dest, name, optimize, checked=True): log_warning("Failed to compile {}", src) return None - -def _py_temp_compile(src, ns, dest_dir=None, checked=True): +# name argument added to address bpo-37641 +def _py_temp_compile(src, name, ns, dest_dir=None, checked=True): if not ns.precompile or src not in PY_FILES or src.parent in DATA_DIRS: return None - - dest = (dest_dir or ns.temp) / (src.stem + ".py") + dest = (dest_dir or ns.temp) / (src.stem + ".pyc") return _compile_one_py( - src, dest.with_suffix(".pyc"), dest, optimize=2, checked=checked + src, dest, name, optimize=2, checked=checked ) def _write_to_zip(zf, dest, src, ns, checked=True): - pyc = _py_temp_compile(src, ns, checked=checked) + pyc = _py_temp_compile(src, dest, ns, checked=checked) if pyc: try: zf.write(str(pyc), dest.with_suffix(".pyc")) From webhook-mailer at python.org Thu Jul 25 17:55:48 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 25 Jul 2019 21:55:48 -0000 Subject: [Python-checkins] bpo-37641 preserve relative file location in embeddable zip (GH-14884) Message-ID: https://github.com/python/cpython/commit/0f211979c29109dcffc3039a24a9d3ecdfd900c9 commit: 0f211979c29109dcffc3039a24a9d3ecdfd900c9 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-25T14:55:43-07:00 summary: bpo-37641 preserve relative file location in embeddable zip (GH-14884) Previously, pyc files in the embeddable distribution reported their location as /.py. This causes a little confusion when interpreting stack traces as the file is in a (almost certainly) incorrect location, and lacks the full relative path to Lib (e.g. email/mime/image.py will only show image.py). This change preserves the Lib relative location of the source file as a path so that stack traces are (hopefully) less misleading and more informative. Co-Authored-By: Kyle Stanley (cherry picked from commit c4cda4369f4b8f33082890d16dfc364a90658ef6) Co-authored-by: Bill Collins files: M PC/layout/main.py diff --git a/PC/layout/main.py b/PC/layout/main.py index 111f8aad2561..fe934bfb1ab3 100644 --- a/PC/layout/main.py +++ b/PC/layout/main.py @@ -287,19 +287,18 @@ def _compile_one_py(src, dest, name, optimize, checked=True): log_warning("Failed to compile {}", src) return None - -def _py_temp_compile(src, ns, dest_dir=None, checked=True): +# name argument added to address bpo-37641 +def _py_temp_compile(src, name, ns, dest_dir=None, checked=True): if not ns.precompile or src not in PY_FILES or src.parent in DATA_DIRS: return None - - dest = (dest_dir or ns.temp) / (src.stem + ".py") + dest = (dest_dir or ns.temp) / (src.stem + ".pyc") return _compile_one_py( - src, dest.with_suffix(".pyc"), dest, optimize=2, checked=checked + src, dest, name, optimize=2, checked=checked ) def _write_to_zip(zf, dest, src, ns, checked=True): - pyc = _py_temp_compile(src, ns, checked=checked) + pyc = _py_temp_compile(src, dest, ns, checked=checked) if pyc: try: zf.write(str(pyc), dest.with_suffix(".pyc")) From webhook-mailer at python.org Thu Jul 25 21:30:42 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Fri, 26 Jul 2019 01:30:42 -0000 Subject: [Python-checkins] bpo-29446: tkinter 'import *' only imports what it should (GH-14864) Message-ID: https://github.com/python/cpython/commit/76b645124b3aaa34bc664eece43707c01ef1b382 commit: 76b645124b3aaa34bc664eece43707c01ef1b382 branch: master author: Flavian Hautbois committer: Terry Jan Reedy date: 2019-07-25T21:30:33-04:00 summary: bpo-29446: tkinter 'import *' only imports what it should (GH-14864) Add __all__ to tkinter.__init__ and submodules. Replace 'import *' with explicit imports in some submodules. files: A Misc/NEWS.d/next/Library/2019-07-19-16-06-48.bpo-29446.iXGuoi.rst M Lib/tkinter/__init__.py M Lib/tkinter/colorchooser.py M Lib/tkinter/commondialog.py M Lib/tkinter/dialog.py M Lib/tkinter/dnd.py M Lib/tkinter/filedialog.py M Lib/tkinter/font.py M Lib/tkinter/messagebox.py M Lib/tkinter/scrolledtext.py M Lib/tkinter/test/test_tkinter/test_misc.py M Misc/ACKS diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 57d5b2572822..9626a2780db6 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -32,13 +32,13 @@ import enum import sys +import types import _tkinter # If this fails your Python may not be configured for Tk TclError = _tkinter.TclError from tkinter.constants import * import re - wantobjects = 1 TkVersion = float(_tkinter.TK_VERSION) @@ -4569,5 +4569,9 @@ def _test(): root.mainloop() +__all__ = [name for name, obj in globals().items() + if not name.startswith('_') and not isinstance(obj, types.ModuleType) + and name not in {'wantobjects'}] + if __name__ == '__main__': _test() diff --git a/Lib/tkinter/colorchooser.py b/Lib/tkinter/colorchooser.py index 9dc967133640..3cfc06f6f1fa 100644 --- a/Lib/tkinter/colorchooser.py +++ b/Lib/tkinter/colorchooser.py @@ -21,6 +21,8 @@ from tkinter.commondialog import Dialog +__all__ = ["Chooser", "askcolor"] + # # color chooser class diff --git a/Lib/tkinter/commondialog.py b/Lib/tkinter/commondialog.py index c4ec010ee6b3..e56b5baf7d1e 100644 --- a/Lib/tkinter/commondialog.py +++ b/Lib/tkinter/commondialog.py @@ -8,15 +8,17 @@ # written by Fredrik Lundh, May 1997 # -from tkinter import * +__all__ = ["Dialog"] + +from tkinter import Frame class Dialog: - command = None + command = None def __init__(self, master=None, **options): - self.master = master + self.master = master self.options = options if not master and options.get('parent'): self.master = options['parent'] diff --git a/Lib/tkinter/dialog.py b/Lib/tkinter/dialog.py index cb463f717c0e..8ae214011727 100644 --- a/Lib/tkinter/dialog.py +++ b/Lib/tkinter/dialog.py @@ -1,7 +1,8 @@ # dialog.py -- Tkinter interface to the tk_dialog script. -from tkinter import * -from tkinter import _cnfmerge +from tkinter import _cnfmerge, Widget, TclError, Button, Pack + +__all__ = ["Dialog"] DIALOG_ICON = 'questhead' diff --git a/Lib/tkinter/dnd.py b/Lib/tkinter/dnd.py index 4de2331c8762..3120ff342f8c 100644 --- a/Lib/tkinter/dnd.py +++ b/Lib/tkinter/dnd.py @@ -99,9 +99,10 @@ """ - import tkinter +__all__ = ["dnd_start", "DndHandler"] + # The factory function diff --git a/Lib/tkinter/filedialog.py b/Lib/tkinter/filedialog.py index d9d3436145c9..dbb97dd5777e 100644 --- a/Lib/tkinter/filedialog.py +++ b/Lib/tkinter/filedialog.py @@ -11,14 +11,20 @@ directory dialogue available in Tk 8.3 and newer. These interfaces were written by Fredrik Lundh, May 1997. """ +__all__ = ["FileDialog", "LoadFileDialog", "SaveFileDialog", + "Open", "SaveAs", "Directory", + "askopenfilename", "asksaveasfilename", "askopenfilenames", + "askopenfile", "askopenfiles", "asksaveasfile", "askdirectory"] -from tkinter import * +import fnmatch +import os +from tkinter import ( + Frame, LEFT, YES, BOTTOM, Entry, TOP, Button, Tk, X, + Toplevel, RIGHT, Y, END, Listbox, BOTH, Scrollbar, +) from tkinter.dialog import Dialog from tkinter import commondialog -import os -import fnmatch - dialogstates = {} diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py index 136425726ab8..eeff454b5306 100644 --- a/Lib/tkinter/font.py +++ b/Lib/tkinter/font.py @@ -3,11 +3,12 @@ # written by Fredrik Lundh, February 1998 # -__version__ = "0.9" - import itertools import tkinter +__version__ = "0.9" +__all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", + "nametofont", "Font", "families", "names"] # weight/slant NORMAL = "normal" diff --git a/Lib/tkinter/messagebox.py b/Lib/tkinter/messagebox.py index 4a711fa623b3..5f0343b660c6 100644 --- a/Lib/tkinter/messagebox.py +++ b/Lib/tkinter/messagebox.py @@ -24,6 +24,10 @@ from tkinter.commondialog import Dialog +__all__ = ["showinfo", "showwarning", "showerror", + "askquestion", "askokcancel", "askyesno", + "askyesnocancel", "askretrycancel"] + # # constants diff --git a/Lib/tkinter/scrolledtext.py b/Lib/tkinter/scrolledtext.py index 749a06a6f00f..4f9a8815b618 100644 --- a/Lib/tkinter/scrolledtext.py +++ b/Lib/tkinter/scrolledtext.py @@ -11,11 +11,11 @@ Place methods are redirected to the Frame widget however. """ -__all__ = ['ScrolledText'] - from tkinter import Frame, Text, Scrollbar, Pack, Grid, Place from tkinter.constants import RIGHT, LEFT, Y, BOTH +__all__ = ['ScrolledText'] + class ScrolledText(Text): def __init__(self, master=None, **kw): diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index 1d1a3c29f6bc..9d5a93ef6fb3 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -7,6 +7,20 @@ class MiscTest(AbstractTkTest, unittest.TestCase): + def test_all(self): + self.assertIn("Widget", tkinter.__all__) + # Check that variables from tkinter.constants are also in tkinter.__all__ + self.assertIn("CASCADE", tkinter.__all__) + self.assertIsNotNone(tkinter.CASCADE) + # Check that sys, re, and constants are not in tkinter.__all__ + self.assertNotIn("re", tkinter.__all__) + self.assertNotIn("sys", tkinter.__all__) + self.assertNotIn("constants", tkinter.__all__) + # Check that an underscored functions is not in tkinter.__all__ + self.assertNotIn("_tkerror", tkinter.__all__) + # Check that wantobjects is not in tkinter.__all__ + self.assertNotIn("wantobjects", tkinter.__all__) + def test_repr(self): t = tkinter.Toplevel(self.root, name='top') f = tkinter.Frame(t, name='child') diff --git a/Misc/ACKS b/Misc/ACKS index b062855b9e28..e02e8e1fa515 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -649,6 +649,7 @@ Zac Hatfield-Dodds Shane Hathaway Michael Haubenwallner Janko Hauser +Flavian Hautbois Rycharde Hawkes Ben Hayden Jochen Hayek diff --git a/Misc/NEWS.d/next/Library/2019-07-19-16-06-48.bpo-29446.iXGuoi.rst b/Misc/NEWS.d/next/Library/2019-07-19-16-06-48.bpo-29446.iXGuoi.rst new file mode 100644 index 000000000000..9afda7efe451 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-19-16-06-48.bpo-29446.iXGuoi.rst @@ -0,0 +1 @@ +Make `from tkinter import *` import only the expected objects. \ No newline at end of file From webhook-mailer at python.org Fri Jul 26 02:05:58 2019 From: webhook-mailer at python.org (Inada Naoki) Date: Fri, 26 Jul 2019 06:05:58 -0000 Subject: [Python-checkins] bpo-37340: remove free_list for bound method objects (GH-14232) Message-ID: https://github.com/python/cpython/commit/3e54b575313c64f541e98216ed079fafed01ff5d commit: 3e54b575313c64f541e98216ed079fafed01ff5d branch: master author: Inada Naoki committer: GitHub date: 2019-07-26T15:05:50+09:00 summary: bpo-37340: remove free_list for bound method objects (GH-14232) files: A Misc/NEWS.d/next/Core and Builtins/2019-07-25-11-06-57.bpo-37340.5ktLEg.rst M Include/methodobject.h M Objects/classobject.c M Objects/methodobject.c M Objects/object.c diff --git a/Include/methodobject.h b/Include/methodobject.h index ab66b03f7a97..9f5f7c482c87 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -99,11 +99,6 @@ typedef struct { PyAPI_FUNC(int) PyCFunction_ClearFreeList(void); -#ifndef Py_LIMITED_API -PyAPI_FUNC(void) _PyCFunction_DebugMallocStats(FILE *out); -PyAPI_FUNC(void) _PyMethod_DebugMallocStats(FILE *out); -#endif - #ifdef __cplusplus } #endif diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-25-11-06-57.bpo-37340.5ktLEg.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-25-11-06-57.bpo-37340.5ktLEg.rst new file mode 100644 index 000000000000..e61146b6f797 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-25-11-06-57.bpo-37340.5ktLEg.rst @@ -0,0 +1,3 @@ +Removed object cache (``free_list``) for bound method objects. Temporary +bound method objects are less used than before thanks to the ``LOAD_METHOD`` +opcode and the ``_PyObject_VectorcallMethod`` C API. diff --git a/Objects/classobject.c b/Objects/classobject.c index 46525a737bc9..40cbeaa9f2aa 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -8,15 +8,6 @@ #define TP_DESCR_GET(t) ((t)->tp_descr_get) -/* Free list for method objects to safe malloc/free overhead - * The im_self element is used to chain the elements. - */ -static PyMethodObject *free_list; -static int numfree = 0; -#ifndef PyMethod_MAXFREELIST -#define PyMethod_MAXFREELIST 256 -#endif - _Py_IDENTIFIER(__name__); _Py_IDENTIFIER(__qualname__); @@ -103,21 +94,13 @@ method_vectorcall(PyObject *method, PyObject *const *args, PyObject * PyMethod_New(PyObject *func, PyObject *self) { - PyMethodObject *im; if (self == NULL) { PyErr_BadInternalCall(); return NULL; } - im = free_list; - if (im != NULL) { - free_list = (PyMethodObject *)(im->im_self); - (void)PyObject_INIT(im, &PyMethod_Type); - numfree--; - } - else { - im = PyObject_GC_New(PyMethodObject, &PyMethod_Type); - if (im == NULL) - return NULL; + PyMethodObject *im = PyObject_GC_New(PyMethodObject, &PyMethod_Type); + if (im == NULL) { + return NULL; } im->im_weakreflist = NULL; Py_INCREF(func); @@ -252,14 +235,7 @@ method_dealloc(PyMethodObject *im) PyObject_ClearWeakRefs((PyObject *)im); Py_DECREF(im->im_func); Py_XDECREF(im->im_self); - if (numfree < PyMethod_MAXFREELIST) { - im->im_self = (PyObject *)free_list; - free_list = im; - numfree++; - } - else { - PyObject_GC_Del(im); - } + PyObject_GC_Del(im); } static PyObject * @@ -395,16 +371,7 @@ PyTypeObject PyMethod_Type = { int PyMethod_ClearFreeList(void) { - int freelist_size = numfree; - - while (free_list) { - PyMethodObject *im = free_list; - free_list = (PyMethodObject *)(im->im_self); - PyObject_GC_Del(im); - numfree--; - } - assert(numfree == 0); - return freelist_size; + return 0; } void @@ -413,15 +380,6 @@ PyMethod_Fini(void) (void)PyMethod_ClearFreeList(); } -/* Print summary info about the state of the optimized allocator */ -void -_PyMethod_DebugMallocStats(FILE *out) -{ - _PyDebugAllocatorStats(out, - "free PyMethodObject", - numfree, sizeof(PyMethodObject)); -} - /* ------------------------------------------------------------------------ * instance method */ diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 3494f11d80fe..b9977467ac08 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -7,15 +7,6 @@ #include "pycore_pystate.h" #include "structmember.h" -/* Free list for method objects to safe malloc/free overhead - * The m_self element is used to chain the objects. - */ -static PyCFunctionObject *free_list = NULL; -static int numfree = 0; -#ifndef PyCFunction_MAXFREELIST -#define PyCFunction_MAXFREELIST 256 -#endif - /* undefine macro trampoline to PyCFunction_NewEx */ #undef PyCFunction_New @@ -66,17 +57,10 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) return NULL; } - PyCFunctionObject *op; - op = free_list; - if (op != NULL) { - free_list = (PyCFunctionObject *)(op->m_self); - (void)PyObject_INIT(op, &PyCFunction_Type); - numfree--; - } - else { - op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type); - if (op == NULL) - return NULL; + PyCFunctionObject *op = + PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type); + if (op == NULL) { + return NULL; } op->m_weakreflist = NULL; op->m_ml = ml; @@ -130,14 +114,7 @@ meth_dealloc(PyCFunctionObject *m) } Py_XDECREF(m->m_self); Py_XDECREF(m->m_module); - if (numfree < PyCFunction_MAXFREELIST) { - m->m_self = (PyObject *)free_list; - free_list = m; - numfree++; - } - else { - PyObject_GC_Del(m); - } + PyObject_GC_Del(m); } static PyObject * @@ -338,16 +315,7 @@ PyTypeObject PyCFunction_Type = { int PyCFunction_ClearFreeList(void) { - int freelist_size = numfree; - - while (free_list) { - PyCFunctionObject *v = free_list; - free_list = (PyCFunctionObject *)(v->m_self); - PyObject_GC_Del(v); - numfree--; - } - assert(numfree == 0); - return freelist_size; + return 0; } void @@ -356,15 +324,6 @@ PyCFunction_Fini(void) (void)PyCFunction_ClearFreeList(); } -/* Print summary info about the state of the optimized allocator */ -void -_PyCFunction_DebugMallocStats(FILE *out) -{ - _PyDebugAllocatorStats(out, - "free PyCFunctionObject", - numfree, sizeof(PyCFunctionObject)); -} - /* Vectorcall functions for each of the PyCFunction calling conventions, * except for METH_VARARGS (possibly combined with METH_KEYWORDS) which diff --git a/Objects/object.c b/Objects/object.c index 585a9748c846..ee2050656a04 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1959,12 +1959,10 @@ Py_ssize_t (*_Py_abstract_hack)(PyObject *) = PyObject_Size; void _PyObject_DebugTypeStats(FILE *out) { - _PyCFunction_DebugMallocStats(out); _PyDict_DebugMallocStats(out); _PyFloat_DebugMallocStats(out); _PyFrame_DebugMallocStats(out); _PyList_DebugMallocStats(out); - _PyMethod_DebugMallocStats(out); _PyTuple_DebugMallocStats(out); } From webhook-mailer at python.org Fri Jul 26 12:06:29 2019 From: webhook-mailer at python.org (Steve Dower) Date: Fri, 26 Jul 2019 16:06:29 -0000 Subject: [Python-checkins] bpo-37664: Update regex for ignoring cache warning on some buildbots (GH-14960) Message-ID: https://github.com/python/cpython/commit/b1eb20e68e30c8ab128f9d63d622f0a8b49dcf34 commit: b1eb20e68e30c8ab128f9d63d622f0a8b49dcf34 branch: master author: Steve Dower committer: GitHub date: 2019-07-26T09:06:04-07:00 summary: bpo-37664: Update regex for ignoring cache warning on some buildbots (GH-14960) files: M Lib/test/test_venv.py diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index ea016b59ae2b..228aa8d68ed7 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -473,8 +473,9 @@ def do_test_with_pip(self, system_site_packages): # Please check the permissions and owner of that directory. If # executing pip with sudo, you may want sudo's -H flag." # where $HOME is replaced by the HOME environment variable. - err = re.sub("^The directory .* or its parent directory is not owned " - "by the current user .*$", "", err, flags=re.MULTILINE) + err = re.sub("^(WARNING: )?The directory .* or its parent directory " + "is not owned by the current user .*$", "", + err, flags=re.MULTILINE) self.assertEqual(err.rstrip(), "") # Being fairly specific regarding the expected behaviour for the # initial bundling phase in Python 3.4. If the output changes in From webhook-mailer at python.org Fri Jul 26 12:24:47 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 26 Jul 2019 16:24:47 -0000 Subject: [Python-checkins] bpo-37664: Update regex for ignoring cache warning on some buildbots (GH-14960) Message-ID: https://github.com/python/cpython/commit/c5033901263d64a533ee47a84c6d286a223540c9 commit: c5033901263d64a533ee47a84c6d286a223540c9 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-26T09:24:43-07:00 summary: bpo-37664: Update regex for ignoring cache warning on some buildbots (GH-14960) (cherry picked from commit b1eb20e68e30c8ab128f9d63d622f0a8b49dcf34) Co-authored-by: Steve Dower files: M Lib/test/test_venv.py diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 6aafa1b14c28..e7adbeebe960 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -451,8 +451,9 @@ def do_test_with_pip(self, system_site_packages): # Please check the permissions and owner of that directory. If # executing pip with sudo, you may want sudo's -H flag." # where $HOME is replaced by the HOME environment variable. - err = re.sub("^The directory .* or its parent directory is not owned " - "by the current user .*$", "", err, flags=re.MULTILINE) + err = re.sub("^(WARNING: )?The directory .* or its parent directory " + "is not owned by the current user .*$", "", + err, flags=re.MULTILINE) self.assertEqual(err.rstrip(), "") # Being fairly specific regarding the expected behaviour for the # initial bundling phase in Python 3.4. If the output changes in From webhook-mailer at python.org Fri Jul 26 13:13:39 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 26 Jul 2019 17:13:39 -0000 Subject: [Python-checkins] bpo-37664: Update regex for ignoring cache warning on some buildbots (GH-14960) Message-ID: https://github.com/python/cpython/commit/0225d58cc8b786caec12f45e9245f90497610a2d commit: 0225d58cc8b786caec12f45e9245f90497610a2d branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-26T10:13:11-07:00 summary: bpo-37664: Update regex for ignoring cache warning on some buildbots (GH-14960) (cherry picked from commit b1eb20e68e30c8ab128f9d63d622f0a8b49dcf34) Co-authored-by: Steve Dower files: M Lib/test/test_venv.py diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 67f9f46e65e0..921512e03721 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -438,8 +438,9 @@ def do_test_with_pip(self, system_site_packages): # Please check the permissions and owner of that directory. If # executing pip with sudo, you may want sudo's -H flag." # where $HOME is replaced by the HOME environment variable. - err = re.sub("^The directory .* or its parent directory is not owned " - "by the current user .*$", "", err, flags=re.MULTILINE) + err = re.sub("^(WARNING: )?The directory .* or its parent directory " + "is not owned by the current user .*$", "", + err, flags=re.MULTILINE) self.assertEqual(err.rstrip(), "") # Being fairly specific regarding the expected behaviour for the # initial bundling phase in Python 3.4. If the output changes in From webhook-mailer at python.org Fri Jul 26 13:19:41 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 26 Jul 2019 17:19:41 -0000 Subject: [Python-checkins] bpo-37664: Update bundled pip to 19.2.1 and setuptools to 41.0.1 (GH-14934) Message-ID: https://github.com/python/cpython/commit/ea0f7aa47c5d2e58dc99314508172f0523e144c6 commit: ea0f7aa47c5d2e58dc99314508172f0523e144c6 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-26T10:19:15-07:00 summary: bpo-37664: Update bundled pip to 19.2.1 and setuptools to 41.0.1 (GH-14934) (cherry picked from commit 5380def8269b24a8a3bc46396373a1dc91b1dd1a) Co-authored-by: Pradyun Gedam files: A Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl A Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl A Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst D Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl D Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl M Lib/ensurepip/__init__.py diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 526dfd004a41..8ccf587257f3 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -8,9 +8,9 @@ __all__ = ["version", "bootstrap"] -_SETUPTOOLS_VERSION = "40.8.0" +_SETUPTOOLS_VERSION = "41.0.1" -_PIP_VERSION = "19.0.3" +_PIP_VERSION = "19.2.1" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION), diff --git a/Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl similarity index 51% rename from Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl rename to Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl index 24f247caa0ab..1690eb3f4f2a 100644 Binary files a/Lib/ensurepip/_bundled/pip-19.0.3-py2.py3-none-any.whl and b/Lib/ensurepip/_bundled/pip-19.2.1-py2.py3-none-any.whl differ diff --git a/Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl similarity index 86% rename from Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl rename to Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl index fdc66e9330e0..92836e984026 100644 Binary files a/Lib/ensurepip/_bundled/setuptools-40.8.0-py2.py3-none-any.whl and b/Lib/ensurepip/_bundled/setuptools-41.0.1-py2.py3-none-any.whl differ diff --git a/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst b/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst new file mode 100644 index 000000000000..3343d9100946 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst @@ -0,0 +1 @@ +Update wheels bundled with ensurepip (pip 19.2.1 and setuptools 41.0.1) From webhook-mailer at python.org Fri Jul 26 16:04:24 2019 From: webhook-mailer at python.org (Steve Dower) Date: Fri, 26 Jul 2019 20:04:24 -0000 Subject: [Python-checkins] bpo-35524: Update Windows installer image in docs (GH-14966) Message-ID: https://github.com/python/cpython/commit/9d9893a1c85e07f7369c848acb0aed3b8fe6c3af commit: 9d9893a1c85e07f7369c848acb0aed3b8fe6c3af branch: master author: Steve Dower committer: GitHub date: 2019-07-26T13:03:58-07:00 summary: bpo-35524: Update Windows installer image in docs (GH-14966) files: M Doc/using/win_installer.png diff --git a/Doc/using/win_installer.png b/Doc/using/win_installer.png index 0d2250c186c4..9c18ff19cf5e 100644 Binary files a/Doc/using/win_installer.png and b/Doc/using/win_installer.png differ From webhook-mailer at python.org Fri Jul 26 16:40:17 2019 From: webhook-mailer at python.org (Steve Dower) Date: Fri, 26 Jul 2019 20:40:17 -0000 Subject: [Python-checkins] bpo-35524: Update Windows installer image in docs (GH-14966) Message-ID: https://github.com/python/cpython/commit/886e66d111b6b865e582d8cc7974d5c0b398da99 commit: 886e66d111b6b865e582d8cc7974d5c0b398da99 branch: 3.8 author: Steve Dower committer: GitHub date: 2019-07-26T13:39:51-07:00 summary: bpo-35524: Update Windows installer image in docs (GH-14966) files: M Doc/using/win_installer.png diff --git a/Doc/using/win_installer.png b/Doc/using/win_installer.png index 0d2250c186c4..9c18ff19cf5e 100644 Binary files a/Doc/using/win_installer.png and b/Doc/using/win_installer.png differ From webhook-mailer at python.org Fri Jul 26 17:57:15 2019 From: webhook-mailer at python.org (Steve Dower) Date: Fri, 26 Jul 2019 21:57:15 -0000 Subject: [Python-checkins] bpo-32910: Remove implementation detail in venv documentation. (GH-14968) Message-ID: https://github.com/python/cpython/commit/91e49575095ca16d1b67dd8822deeb7885e421da commit: 91e49575095ca16d1b67dd8822deeb7885e421da branch: master author: Derek Keeler committer: Steve Dower date: 2019-07-26T14:57:11-07:00 summary: bpo-32910: Remove implementation detail in venv documentation. (GH-14968) files: A Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst M Doc/using/venv-create.inc diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc index 46bf0b49657d..cf5af437d6ae 100644 --- a/Doc/using/venv-create.inc +++ b/Doc/using/venv-create.inc @@ -123,10 +123,8 @@ installed in a virtual environment should be runnable without activating it, and run with the virtual environment's Python automatically. You can deactivate a virtual environment by typing "deactivate" in your shell. -The exact mechanism is platform-specific: for example, the Bash activation -script defines a "deactivate" function, whereas on Windows there are separate -scripts called ``deactivate.bat`` and ``Deactivate.ps1`` which are installed -when the virtual environment is created. +The exact mechanism is platform-specific and is an internal implementation +detail (typically a script or shell function will be used). .. versionadded:: 3.4 ``fish`` and ``csh`` activation scripts. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst b/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst new file mode 100644 index 000000000000..60386a196e7c --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst @@ -0,0 +1 @@ +Remove implementation-specific behaviour of how venv's Deactivate works. From webhook-mailer at python.org Fri Jul 26 18:03:27 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 26 Jul 2019 22:03:27 -0000 Subject: [Python-checkins] bpo-32910: Remove implementation detail in venv documentation. (GH-14968) Message-ID: https://github.com/python/cpython/commit/4b6421c61e9335253ea7004f2878317b88096c30 commit: 4b6421c61e9335253ea7004f2878317b88096c30 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-26T15:03:23-07:00 summary: bpo-32910: Remove implementation detail in venv documentation. (GH-14968) (cherry picked from commit 91e49575095ca16d1b67dd8822deeb7885e421da) Co-authored-by: Derek Keeler files: A Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst M Doc/using/venv-create.inc diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc index 1ada83c07a67..4a795704ed9a 100644 --- a/Doc/using/venv-create.inc +++ b/Doc/using/venv-create.inc @@ -115,10 +115,8 @@ installed in a virtual environment should be runnable without activating it, and run with the virtual environment's Python automatically. You can deactivate a virtual environment by typing "deactivate" in your shell. -The exact mechanism is platform-specific: for example, the Bash activation -script defines a "deactivate" function, whereas on Windows there are separate -scripts called ``deactivate.bat`` and ``Deactivate.ps1`` which are installed -when the virtual environment is created. +The exact mechanism is platform-specific and is an internal implementation +detail (typically a script or shell function will be used). .. versionadded:: 3.4 ``fish`` and ``csh`` activation scripts. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst b/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst new file mode 100644 index 000000000000..60386a196e7c --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst @@ -0,0 +1 @@ +Remove implementation-specific behaviour of how venv's Deactivate works. From webhook-mailer at python.org Fri Jul 26 18:04:32 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 26 Jul 2019 22:04:32 -0000 Subject: [Python-checkins] bpo-32910: Remove implementation detail in venv documentation. (GH-14968) Message-ID: https://github.com/python/cpython/commit/06e8fc95d138775e942c18c8e47e72cdcc32f95c commit: 06e8fc95d138775e942c18c8e47e72cdcc32f95c branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-26T15:04:28-07:00 summary: bpo-32910: Remove implementation detail in venv documentation. (GH-14968) (cherry picked from commit 91e49575095ca16d1b67dd8822deeb7885e421da) Co-authored-by: Derek Keeler files: A Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst M Doc/using/venv-create.inc diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc index af30cbb55980..1b24721bbcbc 100644 --- a/Doc/using/venv-create.inc +++ b/Doc/using/venv-create.inc @@ -118,10 +118,8 @@ installed in a virtual environment should be runnable without activating it, and run with the virtual environment's Python automatically. You can deactivate a virtual environment by typing "deactivate" in your shell. -The exact mechanism is platform-specific: for example, the Bash activation -script defines a "deactivate" function, whereas on Windows there are separate -scripts called ``deactivate.bat`` and ``Deactivate.ps1`` which are installed -when the virtual environment is created. +The exact mechanism is platform-specific and is an internal implementation +detail (typically a script or shell function will be used). .. versionadded:: 3.4 ``fish`` and ``csh`` activation scripts. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst b/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst new file mode 100644 index 000000000000..60386a196e7c --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst @@ -0,0 +1 @@ +Remove implementation-specific behaviour of how venv's Deactivate works. From webhook-mailer at python.org Fri Jul 26 23:24:41 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sat, 27 Jul 2019 03:24:41 -0000 Subject: [Python-checkins] bpo-17535: Increase line number horizontal padding by 2 pixels (GH-14959) Message-ID: https://github.com/python/cpython/commit/46ebd4a6a22431ce9676546d2bbe5a6dcd1cc1c1 commit: 46ebd4a6a22431ce9676546d2bbe5a6dcd1cc1c1 branch: master author: Tal Einat committer: Terry Jan Reedy date: 2019-07-26T23:24:36-04:00 summary: bpo-17535: Increase line number horizontal padding by 2 pixels (GH-14959) files: M Lib/idlelib/sidebar.py diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py index 8f9bf80b5260..76964a4cdfbe 100644 --- a/Lib/idlelib/sidebar.py +++ b/Lib/idlelib/sidebar.py @@ -51,7 +51,7 @@ def __init__(self, editwin): _padx, pady = get_widget_padding(self.text) self.sidebar_text = tk.Text(self.parent, width=1, wrap=tk.NONE, - padx=0, pady=pady, + padx=2, pady=pady, borderwidth=0, highlightthickness=0) self.sidebar_text.config(state=tk.DISABLED) self.text['yscrollcommand'] = self.redirect_yscroll_event From webhook-mailer at python.org Fri Jul 26 23:42:04 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 27 Jul 2019 03:42:04 -0000 Subject: [Python-checkins] bpo-17535: Increase line number horizontal padding by 2 pixels (GH-14959) Message-ID: https://github.com/python/cpython/commit/9e7697b3c55e0bd8663cf0641d4718e853af2d9c commit: 9e7697b3c55e0bd8663cf0641d4718e853af2d9c branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-26T20:42:00-07:00 summary: bpo-17535: Increase line number horizontal padding by 2 pixels (GH-14959) (cherry picked from commit 46ebd4a6a22431ce9676546d2bbe5a6dcd1cc1c1) Co-authored-by: Tal Einat files: M Lib/idlelib/sidebar.py diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py index 8f9bf80b5260..76964a4cdfbe 100644 --- a/Lib/idlelib/sidebar.py +++ b/Lib/idlelib/sidebar.py @@ -51,7 +51,7 @@ def __init__(self, editwin): _padx, pady = get_widget_padding(self.text) self.sidebar_text = tk.Text(self.parent, width=1, wrap=tk.NONE, - padx=0, pady=pady, + padx=2, pady=pady, borderwidth=0, highlightthickness=0) self.sidebar_text.config(state=tk.DISABLED) self.text['yscrollcommand'] = self.redirect_yscroll_event From webhook-mailer at python.org Fri Jul 26 23:46:57 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 27 Jul 2019 03:46:57 -0000 Subject: [Python-checkins] bpo-17535: Increase line number horizontal padding by 2 pixels (GH-14959) Message-ID: https://github.com/python/cpython/commit/f6ab188323444fe0dd916ed3860cc5c8806caa16 commit: f6ab188323444fe0dd916ed3860cc5c8806caa16 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-26T20:46:52-07:00 summary: bpo-17535: Increase line number horizontal padding by 2 pixels (GH-14959) (cherry picked from commit 46ebd4a6a22431ce9676546d2bbe5a6dcd1cc1c1) Co-authored-by: Tal Einat files: M Lib/idlelib/sidebar.py diff --git a/Lib/idlelib/sidebar.py b/Lib/idlelib/sidebar.py index 8f9bf80b5260..76964a4cdfbe 100644 --- a/Lib/idlelib/sidebar.py +++ b/Lib/idlelib/sidebar.py @@ -51,7 +51,7 @@ def __init__(self, editwin): _padx, pady = get_widget_padding(self.text) self.sidebar_text = tk.Text(self.parent, width=1, wrap=tk.NONE, - padx=0, pady=pady, + padx=2, pady=pady, borderwidth=0, highlightthickness=0) self.sidebar_text.config(state=tk.DISABLED) self.text['yscrollcommand'] = self.redirect_yscroll_event From webhook-mailer at python.org Sat Jul 27 08:46:59 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Sat, 27 Jul 2019 12:46:59 -0000 Subject: [Python-checkins] Add Qt GUI example to the logging cookbook. (GH-14978) Message-ID: https://github.com/python/cpython/commit/1ed915e8ae3cc59ad8f7a8eaccb94f27f19b10a6 commit: 1ed915e8ae3cc59ad8f7a8eaccb94f27f19b10a6 branch: master author: Vinay Sajip committer: GitHub date: 2019-07-27T13:46:53+01:00 summary: Add Qt GUI example to the logging cookbook. (GH-14978) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index e62308192d16..ef9ad447f801 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2744,3 +2744,216 @@ And if we want less: In this case, the commands don't print anything to the console, since nothing at ``WARNING`` level or above is logged by them. + +.. _qt-gui: + +A Qt GUI for logging +-------------------- + +A question that comes up from time to time is about how to log to a GUI +application. The `Qt `_ framework is a popular +cross-platform UI framework with Python bindings using `PySide2 +`_ or `PyQt5 +`_ libraries. + +The following example shows how to log to a Qt GUI. This introduces a simple +``QtHandler`` class which takes a callable, which should be a slot in the main +thread that does GUI updates. A worker thread is also created to show how you +can log to the GUI from both the UI itself (via a button for manual logging) +as well as a worker thread doing work in the background (here, just random +short delays). + +The worker thread is implemented using Qt's ``QThread`` class rather than the +:mod:`threading` module, as there are circumstances where one has to use +``QThread``, which offers better integration with other ``Qt`` components. + +The code should work with recent releases of either ``PySide2`` or ``PyQt5``. +You should be able to adapt the approach to earlier versions of Qt. Please +refer to the comments in the code for more detailed information. + +.. code-block:: python3 + + import datetime + import logging + import random + import sys + import time + + # Deal with minor differences between PySide2 and PyQt5 + try: + from PySide2 import QtCore, QtGui, QtWidgets + Signal = QtCore.Signal + Slot = QtCore.Slot + except ImportError: + from PyQt5 import QtCore, QtGui, QtWidgets + Signal = QtCore.pyqtSignal + Slot = QtCore.pyqtSlot + + logger = logging.getLogger(__name__) + + # + # Signals need to be contained in a QObject or subclass in order to be correctly + # initialized. + # + class Signaller(QtCore.QObject): + signal = Signal(str) + + # + # Output to a Qt GUI is only supposed to happen on the main thread. So, this + # handler is designed to take a slot function which is set up to run in the main + # thread. In this example, the function takes a single argument which is a + # formatted log message. You can attach a formatter instance which formats a + # LogRecord however you like, or change the slot function to take some other + # value derived from the LogRecord. + # + # You specify the slot function to do whatever GUI updates you want. The handler + # doesn't know or care about specific UI elements. + # + class QtHandler(logging.Handler): + def __init__(self, slotfunc, *args, **kwargs): + super(QtHandler, self).__init__(*args, **kwargs) + self.signaller = Signaller() + self.signaller.signal.connect(slotfunc) + + def emit(self, record): + s = self.format(record) + self.signaller.signal.emit(s) + + # + # This example uses QThreads, which means that the threads at the Python level + # are named something like "Dummy-1". The function below gets the Qt name of the + # current thread. + # + def ctname(): + return QtCore.QThread.currentThread().objectName() + + # + # This worker class represents work that is done in a thread separate to the + # main thread. The way the thread is kicked off to do work is via a button press + # that connects to a slot in the worker. + # + # Because the default threadName value in the LogRecord isn't much use, we add + # a qThreadName which contains the QThread name as computed above, and pass that + # value in an "extra" dictionary which is used to update the LogRecord with the + # QThread name. + # + # This example worker just outputs messages sequentially, interspersed with + # random delays of the order of a few seconds. + # + class Worker(QtCore.QObject): + @Slot() + def start(self): + extra = {'qThreadName': ctname() } + logger.debug('Started work', extra=extra) + i = 1 + # Let the thread run until interrupted. This allows reasonably clean + # thread termination. + while not QtCore.QThread.currentThread().isInterruptionRequested(): + delay = 0.5 + random.random() * 2 + time.sleep(delay) + logger.debug('Message after delay of %3.1f: %d', delay, i, extra=extra) + i += 1 + + # + # Implement a simple UI for this cookbook example. This contains: + # + # * A read-only text edit window which holds formatted log messages + # * A button to start work and log stuff in a separate thread + # * A button to log something from the main thread + # * A button to clear the log window + # + class Window(QtWidgets.QWidget): + + def __init__(self, app): + super(Window, self).__init__() + self.app = app + self.textedit = te = QtWidgets.QTextEdit(self) + # Set whatever the default monospace font is for the platform + f = QtGui.QFont('nosuchfont') + f.setStyleHint(f.Monospace) + te.setFont(f) + te.setReadOnly(True) + PB = QtWidgets.QPushButton + self.work_button = PB('Start background work', self) + self.log_button = PB('Log a message at a random level', self) + self.clear_button = PB('Clear log window', self) + self.handler = h = QtHandler(self.update_status) + # Remember to use qThreadName rather than threadName in the format string. + fs = '%(asctime)s %(qThreadName)-12s %(levelname)-8s %(message)s' + formatter = logging.Formatter(f) + h.setFormatter(formatter) + logger.addHandler(h) + # Set up to terminate the QThread when we exit + app.aboutToQuit.connect(self.force_quit) + + # Lay out all the widgets + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(te) + layout.addWidget(self.work_button) + layout.addWidget(self.log_button) + layout.addWidget(self.clear_button) + self.setFixedSize(900, 400) + + # Connect the non-worker slots and signals + self.log_button.clicked.connect(self.manual_update) + self.clear_button.clicked.connect(self.clear_display) + + # Start a new worker thread and connect the slots for the worker + self.start_thread() + self.work_button.clicked.connect(self.worker.start) + # Once started, the button should be disabled + self.work_button.clicked.connect(lambda : self.work_button.setEnabled(False)) + + def start_thread(self): + self.worker = Worker() + self.worker_thread = QtCore.QThread() + self.worker.setObjectName('Worker') + self.worker_thread.setObjectName('WorkerThread') # for qThreadName + self.worker.moveToThread(self.worker_thread) + # This will start an event loop in the worker thread + self.worker_thread.start() + + def kill_thread(self): + # Just tell the worker to stop, then tell it to quit and wait for that + # to happen + self.worker_thread.requestInterruption() + if self.worker_thread.isRunning(): + self.worker_thread.quit() + self.worker_thread.wait() + else: + print('worker has already exited.') + + def force_quit(self): + # For use when the window is closed + if self.worker_thread.isRunning(): + self.kill_thread() + + # The functions below update the UI and run in the main thread because + # that's where the slots are set up + + @Slot(str) + def update_status(self, status): + self.textedit.append(status) + + @Slot() + def manual_update(self): + levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, + logging.CRITICAL) + level = random.choice(levels) + extra = {'qThreadName': ctname() } + logger.log(level, 'Manually logged!', extra=extra) + + @Slot() + def clear_display(self): + self.textedit.clear() + + def main(): + QtCore.QThread.currentThread().setObjectName('MainThread') + logging.getLogger().setLevel(logging.DEBUG) + app = QtWidgets.QApplication(sys.argv) + example = Window(app) + example.show() + sys.exit(app.exec_()) + + if __name__=='__main__': + main() From webhook-mailer at python.org Sat Jul 27 09:04:17 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Sat, 27 Jul 2019 13:04:17 -0000 Subject: [Python-checkins] Add Qt GUI example to the logging cookbook. (GH-14978) (GH-14979) Message-ID: https://github.com/python/cpython/commit/d38fa5869502dcd00343978bdf6b726035415ddb commit: d38fa5869502dcd00343978bdf6b726035415ddb branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-27T14:04:13+01:00 summary: Add Qt GUI example to the logging cookbook. (GH-14978) (GH-14979) (cherry picked from commit 1ed915e8ae3cc59ad8f7a8eaccb94f27f19b10a6) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index e62308192d16..ef9ad447f801 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2744,3 +2744,216 @@ And if we want less: In this case, the commands don't print anything to the console, since nothing at ``WARNING`` level or above is logged by them. + +.. _qt-gui: + +A Qt GUI for logging +-------------------- + +A question that comes up from time to time is about how to log to a GUI +application. The `Qt `_ framework is a popular +cross-platform UI framework with Python bindings using `PySide2 +`_ or `PyQt5 +`_ libraries. + +The following example shows how to log to a Qt GUI. This introduces a simple +``QtHandler`` class which takes a callable, which should be a slot in the main +thread that does GUI updates. A worker thread is also created to show how you +can log to the GUI from both the UI itself (via a button for manual logging) +as well as a worker thread doing work in the background (here, just random +short delays). + +The worker thread is implemented using Qt's ``QThread`` class rather than the +:mod:`threading` module, as there are circumstances where one has to use +``QThread``, which offers better integration with other ``Qt`` components. + +The code should work with recent releases of either ``PySide2`` or ``PyQt5``. +You should be able to adapt the approach to earlier versions of Qt. Please +refer to the comments in the code for more detailed information. + +.. code-block:: python3 + + import datetime + import logging + import random + import sys + import time + + # Deal with minor differences between PySide2 and PyQt5 + try: + from PySide2 import QtCore, QtGui, QtWidgets + Signal = QtCore.Signal + Slot = QtCore.Slot + except ImportError: + from PyQt5 import QtCore, QtGui, QtWidgets + Signal = QtCore.pyqtSignal + Slot = QtCore.pyqtSlot + + logger = logging.getLogger(__name__) + + # + # Signals need to be contained in a QObject or subclass in order to be correctly + # initialized. + # + class Signaller(QtCore.QObject): + signal = Signal(str) + + # + # Output to a Qt GUI is only supposed to happen on the main thread. So, this + # handler is designed to take a slot function which is set up to run in the main + # thread. In this example, the function takes a single argument which is a + # formatted log message. You can attach a formatter instance which formats a + # LogRecord however you like, or change the slot function to take some other + # value derived from the LogRecord. + # + # You specify the slot function to do whatever GUI updates you want. The handler + # doesn't know or care about specific UI elements. + # + class QtHandler(logging.Handler): + def __init__(self, slotfunc, *args, **kwargs): + super(QtHandler, self).__init__(*args, **kwargs) + self.signaller = Signaller() + self.signaller.signal.connect(slotfunc) + + def emit(self, record): + s = self.format(record) + self.signaller.signal.emit(s) + + # + # This example uses QThreads, which means that the threads at the Python level + # are named something like "Dummy-1". The function below gets the Qt name of the + # current thread. + # + def ctname(): + return QtCore.QThread.currentThread().objectName() + + # + # This worker class represents work that is done in a thread separate to the + # main thread. The way the thread is kicked off to do work is via a button press + # that connects to a slot in the worker. + # + # Because the default threadName value in the LogRecord isn't much use, we add + # a qThreadName which contains the QThread name as computed above, and pass that + # value in an "extra" dictionary which is used to update the LogRecord with the + # QThread name. + # + # This example worker just outputs messages sequentially, interspersed with + # random delays of the order of a few seconds. + # + class Worker(QtCore.QObject): + @Slot() + def start(self): + extra = {'qThreadName': ctname() } + logger.debug('Started work', extra=extra) + i = 1 + # Let the thread run until interrupted. This allows reasonably clean + # thread termination. + while not QtCore.QThread.currentThread().isInterruptionRequested(): + delay = 0.5 + random.random() * 2 + time.sleep(delay) + logger.debug('Message after delay of %3.1f: %d', delay, i, extra=extra) + i += 1 + + # + # Implement a simple UI for this cookbook example. This contains: + # + # * A read-only text edit window which holds formatted log messages + # * A button to start work and log stuff in a separate thread + # * A button to log something from the main thread + # * A button to clear the log window + # + class Window(QtWidgets.QWidget): + + def __init__(self, app): + super(Window, self).__init__() + self.app = app + self.textedit = te = QtWidgets.QTextEdit(self) + # Set whatever the default monospace font is for the platform + f = QtGui.QFont('nosuchfont') + f.setStyleHint(f.Monospace) + te.setFont(f) + te.setReadOnly(True) + PB = QtWidgets.QPushButton + self.work_button = PB('Start background work', self) + self.log_button = PB('Log a message at a random level', self) + self.clear_button = PB('Clear log window', self) + self.handler = h = QtHandler(self.update_status) + # Remember to use qThreadName rather than threadName in the format string. + fs = '%(asctime)s %(qThreadName)-12s %(levelname)-8s %(message)s' + formatter = logging.Formatter(f) + h.setFormatter(formatter) + logger.addHandler(h) + # Set up to terminate the QThread when we exit + app.aboutToQuit.connect(self.force_quit) + + # Lay out all the widgets + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(te) + layout.addWidget(self.work_button) + layout.addWidget(self.log_button) + layout.addWidget(self.clear_button) + self.setFixedSize(900, 400) + + # Connect the non-worker slots and signals + self.log_button.clicked.connect(self.manual_update) + self.clear_button.clicked.connect(self.clear_display) + + # Start a new worker thread and connect the slots for the worker + self.start_thread() + self.work_button.clicked.connect(self.worker.start) + # Once started, the button should be disabled + self.work_button.clicked.connect(lambda : self.work_button.setEnabled(False)) + + def start_thread(self): + self.worker = Worker() + self.worker_thread = QtCore.QThread() + self.worker.setObjectName('Worker') + self.worker_thread.setObjectName('WorkerThread') # for qThreadName + self.worker.moveToThread(self.worker_thread) + # This will start an event loop in the worker thread + self.worker_thread.start() + + def kill_thread(self): + # Just tell the worker to stop, then tell it to quit and wait for that + # to happen + self.worker_thread.requestInterruption() + if self.worker_thread.isRunning(): + self.worker_thread.quit() + self.worker_thread.wait() + else: + print('worker has already exited.') + + def force_quit(self): + # For use when the window is closed + if self.worker_thread.isRunning(): + self.kill_thread() + + # The functions below update the UI and run in the main thread because + # that's where the slots are set up + + @Slot(str) + def update_status(self, status): + self.textedit.append(status) + + @Slot() + def manual_update(self): + levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, + logging.CRITICAL) + level = random.choice(levels) + extra = {'qThreadName': ctname() } + logger.log(level, 'Manually logged!', extra=extra) + + @Slot() + def clear_display(self): + self.textedit.clear() + + def main(): + QtCore.QThread.currentThread().setObjectName('MainThread') + logging.getLogger().setLevel(logging.DEBUG) + app = QtWidgets.QApplication(sys.argv) + example = Window(app) + example.show() + sys.exit(app.exec_()) + + if __name__=='__main__': + main() From webhook-mailer at python.org Sat Jul 27 12:58:13 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sat, 27 Jul 2019 16:58:13 -0000 Subject: [Python-checkins] bpo-37628: Fix IDLE config sample sizes (#14958) Message-ID: https://github.com/python/cpython/commit/3221a63c69268a9362802371a616f49d522a5c4f commit: 3221a63c69268a9362802371a616f49d522a5c4f branch: master author: Tal Einat committer: Terry Jan Reedy date: 2019-07-27T12:57:48-04:00 summary: bpo-37628: Fix IDLE config sample sizes (#14958) The boxes for the font and highlight samples are now constrained by the overall config dialog size. They gain scrollbars when the when a large font size makes the samples too large for the box. files: A Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst M Lib/idlelib/configdialog.py M Lib/idlelib/idle_test/htest.py M Lib/idlelib/idle_test/test_textview.py M Lib/idlelib/textview.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 217f8fd0a5fb..4df6ecee69f1 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -33,6 +33,7 @@ from idlelib.parenmatch import ParenMatch from idlelib.format import FormatParagraph from idlelib.squeezer import Squeezer +from idlelib.textview import ScrollableTextFrame changes = ConfigChanges() # Reload changed options in the following classes. @@ -556,7 +557,9 @@ def create_page_font_tab(self): frame_font_param, variable=self.font_bold, onvalue=1, offvalue=0, text='Bold') # frame_sample. - self.font_sample = Text(frame_sample, width=20, height=20) + font_sample_frame = ScrollableTextFrame(frame_sample) + self.font_sample = font_sample_frame.text + self.font_sample.config(wrap=NONE, width=1, height=1) self.font_sample.insert(END, font_sample_text) # frame_indent. indent_title = Label( @@ -568,8 +571,9 @@ def create_page_font_tab(self): # Grid and pack widgets: self.columnconfigure(1, weight=1) + self.rowconfigure(2, weight=1) frame_font.grid(row=0, column=0, padx=5, pady=5) - frame_sample.grid(row=0, column=1, rowspan=2, padx=5, pady=5, + frame_sample.grid(row=0, column=1, rowspan=3, padx=5, pady=5, sticky='nsew') frame_indent.grid(row=1, column=0, padx=5, pady=5, sticky='ew') # frame_font. @@ -582,7 +586,7 @@ def create_page_font_tab(self): self.sizelist.pack(side=LEFT, anchor=W) self.bold_toggle.pack(side=LEFT, anchor=W, padx=20) # frame_sample. - self.font_sample.pack(expand=TRUE, fill=BOTH) + font_sample_frame.pack(expand=TRUE, fill=BOTH) # frame_indent. indent_title.pack(side=TOP, anchor=W, padx=5) self.indent_scale.pack(side=TOP, padx=5, fill=X) @@ -840,9 +844,11 @@ def create_page_highlight(self): frame_theme = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Highlighting Theme ') # frame_custom. - text = self.highlight_sample = Text( - frame_custom, relief=SOLID, borderwidth=1, - font=('courier', 12, ''), cursor='hand2', width=21, height=13, + sample_frame = ScrollableTextFrame( + frame_custom, relief=SOLID, borderwidth=1) + text = self.highlight_sample = sample_frame.text + text.configure( + font=('courier', 12, ''), cursor='hand2', width=1, height=1, takefocus=FALSE, highlightthickness=0, wrap=NONE) text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') @@ -868,7 +874,7 @@ def create_page_highlight(self): for texttag in text_and_tags: text.insert(END, texttag[0], texttag[1]) n_lines = len(text.get('1.0', END).splitlines()) - for lineno in range(1, n_lines + 1): + for lineno in range(1, n_lines): text.insert(f'{lineno}.0', f'{lineno:{len(str(n_lines))}d} ', 'linenumber') @@ -920,9 +926,9 @@ def tem(event, elem=element): frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_theme.pack(side=TOP, padx=5, pady=5, fill=X) # frame_custom. - self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) + self.frame_color_set.pack(side=TOP, padx=5, pady=5, fill=X) frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0) - self.highlight_sample.pack( + sample_frame.pack( side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) self.button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) self.targetlist.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=3) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index f2f37e161632..6990af519b1f 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -349,7 +349,7 @@ def _wrapper(parent): # htest # ViewWindow_spec = { 'file': 'textview', 'kwds': {'title': 'Test textview', - 'text': 'The quick brown fox jumps over the lazy dog.\n'*35, + 'contents': 'The quick brown fox jumps over the lazy dog.\n'*35, '_htest': True}, 'msg': "Test for read-only property of text.\n" "Select text, scroll window, close" diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py index 6f0c1930518a..7189378ab3dd 100644 --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -6,12 +6,12 @@ information about calls. """ from idlelib import textview as tv -import unittest from test.support import requires requires('gui') import os -from tkinter import Tk +import unittest +from tkinter import Tk, TclError, CHAR, NONE, WORD from tkinter.ttk import Button from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Mbox_func @@ -69,13 +69,65 @@ def test_ok(self): view.destroy() -class TextFrameTest(unittest.TestCase): +class AutoHideScrollbarTest(unittest.TestCase): + # Method set is tested in ScrollableTextFrameTest + def test_forbidden_geometry(self): + scroll = tv.AutoHideScrollbar(root) + self.assertRaises(TclError, scroll.pack) + self.assertRaises(TclError, scroll.place) + + +class ScrollableTextFrameTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.root = root = Tk() + root.withdraw() + + @classmethod + def tearDownClass(cls): + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + + def make_frame(self, wrap=NONE, **kwargs): + frame = tv.ScrollableTextFrame(self.root, wrap=wrap, **kwargs) + def cleanup_frame(): + frame.update_idletasks() + frame.destroy() + self.addCleanup(cleanup_frame) + return frame + + def test_line1(self): + frame = self.make_frame() + frame.text.insert('1.0', 'test text') + self.assertEqual(frame.text.get('1.0', '1.end'), 'test text') + + def test_horiz_scrollbar(self): + # The horizontal scrollbar should be shown/hidden according to + # the 'wrap' setting: It should only be shown when 'wrap' is + # set to NONE. + + # wrap = NONE -> with horizontal scrolling + frame = self.make_frame(wrap=NONE) + self.assertEqual(frame.text.cget('wrap'), NONE) + self.assertIsNotNone(frame.xscroll) + + # wrap != NONE -> no horizontal scrolling + for wrap in [CHAR, WORD]: + with self.subTest(wrap=wrap): + frame = self.make_frame(wrap=wrap) + self.assertEqual(frame.text.cget('wrap'), wrap) + self.assertIsNone(frame.xscroll) + + +class ViewFrameTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.root = root = Tk() root.withdraw() - cls.frame = tv.TextFrame(root, 'test text') + cls.frame = tv.ViewFrame(root, 'test text') @classmethod def tearDownClass(cls): diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index 4867a80db1ab..808a2aefab4f 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -2,14 +2,15 @@ """ from tkinter import Toplevel, Text, TclError,\ - HORIZONTAL, VERTICAL, N, S, E, W + HORIZONTAL, VERTICAL, NS, EW, NSEW, NONE, WORD, SUNKEN from tkinter.ttk import Frame, Scrollbar, Button from tkinter.messagebox import showerror +from functools import update_wrapper from idlelib.colorizer import color_config -class AutoHiddenScrollbar(Scrollbar): +class AutoHideScrollbar(Scrollbar): """A scrollbar that is automatically hidden when not needed. Only the grid geometry manager is supported. @@ -28,52 +29,70 @@ def place(self, **kwargs): raise TclError(f'{self.__class__.__name__} does not support "place"') -class TextFrame(Frame): - "Display text with scrollbar." +class ScrollableTextFrame(Frame): + """Display text with scrollbar(s).""" - def __init__(self, parent, rawtext, wrap='word'): + def __init__(self, master, wrap=NONE, **kwargs): """Create a frame for Textview. - parent - parent widget for this frame - rawtext - text to display + master - master widget for this frame + wrap - type of text wrapping to use ('word', 'char' or 'none') + + All parameters except for 'wrap' are passed to Frame.__init__(). + + The Text widget is accessible via the 'text' attribute. + + Note: Changing the wrapping mode of the text widget after + instantiation is not supported. """ - super().__init__(parent) - self['relief'] = 'sunken' - self['height'] = 700 + super().__init__(master, **kwargs) - self.text = text = Text(self, wrap=wrap, highlightthickness=0) - color_config(text) - text.grid(row=0, column=0, sticky=N+S+E+W) + text = self.text = Text(self, wrap=wrap) + text.grid(row=0, column=0, sticky=NSEW) self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) - text.insert(0.0, rawtext) - text['state'] = 'disabled' - text.focus_set() # vertical scrollbar - self.yscroll = yscroll = AutoHiddenScrollbar(self, orient=VERTICAL, - takefocus=False, - command=text.yview) - text['yscrollcommand'] = yscroll.set - yscroll.grid(row=0, column=1, sticky=N+S) - - if wrap == 'none': - # horizontal scrollbar - self.xscroll = xscroll = AutoHiddenScrollbar(self, orient=HORIZONTAL, - takefocus=False, - command=text.xview) - text['xscrollcommand'] = xscroll.set - xscroll.grid(row=1, column=0, sticky=E+W) + self.yscroll = AutoHideScrollbar(self, orient=VERTICAL, + takefocus=False, + command=text.yview) + self.yscroll.grid(row=0, column=1, sticky=NS) + text['yscrollcommand'] = self.yscroll.set + + # horizontal scrollbar - only when wrap is set to NONE + if wrap == NONE: + self.xscroll = AutoHideScrollbar(self, orient=HORIZONTAL, + takefocus=False, + command=text.xview) + self.xscroll.grid(row=1, column=0, sticky=EW) + text['xscrollcommand'] = self.xscroll.set + else: + self.xscroll = None class ViewFrame(Frame): "Display TextFrame and Close button." - def __init__(self, parent, text, wrap='word'): + def __init__(self, parent, contents, wrap='word'): + """Create a frame for viewing text with a "Close" button. + + parent - parent widget for this frame + contents - text to display + wrap - type of text wrapping to use ('word', 'char' or 'none') + + The Text widget is accessible via the 'text' attribute. + """ super().__init__(parent) self.parent = parent self.bind('', self.ok) self.bind('', self.ok) - self.textframe = TextFrame(self, text, wrap=wrap) + self.textframe = ScrollableTextFrame(self, relief=SUNKEN, height=700) + + text = self.text = self.textframe.text + text.insert('1.0', contents) + text.configure(wrap=wrap, highlightthickness=0, state='disabled') + color_config(text) + text.focus_set() + self.button_ok = button_ok = Button( self, text='Close', command=self.ok, takefocus=False) self.textframe.pack(side='top', expand=True, fill='both') @@ -87,7 +106,7 @@ def ok(self, event=None): class ViewWindow(Toplevel): "A simple text viewer dialog for IDLE." - def __init__(self, parent, title, text, modal=True, wrap='word', + def __init__(self, parent, title, contents, modal=True, wrap=WORD, *, _htest=False, _utest=False): """Show the given text in a scrollable window with a 'close' button. @@ -96,7 +115,7 @@ def __init__(self, parent, title, text, modal=True, wrap='word', parent - parent of this dialog title - string which is title of popup dialog - text - text to display in dialog + contents - text to display in dialog wrap - type of text wrapping to use ('word', 'char' or 'none') _htest - bool; change box location when running htest. _utest - bool; don't wait_window when running unittest. @@ -109,7 +128,7 @@ def __init__(self, parent, title, text, modal=True, wrap='word', self.geometry(f'=750x500+{x}+{y}') self.title(title) - self.viewframe = ViewFrame(self, text, wrap=wrap) + self.viewframe = ViewFrame(self, contents, wrap=wrap) self.protocol("WM_DELETE_WINDOW", self.ok) self.button_ok = button_ok = Button(self, text='Close', command=self.ok, takefocus=False) @@ -129,18 +148,18 @@ def ok(self, event=None): self.destroy() -def view_text(parent, title, text, modal=True, wrap='word', _utest=False): +def view_text(parent, title, contents, modal=True, wrap='word', _utest=False): """Create text viewer for given text. parent - parent of this dialog title - string which is the title of popup dialog - text - text to display in this dialog + contents - text to display in this dialog wrap - type of text wrapping to use ('word', 'char' or 'none') modal - controls if users can interact with other windows while this dialog is displayed _utest - bool; controls wait_window on unittest """ - return ViewWindow(parent, title, text, modal, wrap=wrap, _utest=_utest) + return ViewWindow(parent, title, contents, modal, wrap=wrap, _utest=_utest) def view_file(parent, title, filename, encoding, modal=True, wrap='word', diff --git a/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst b/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst new file mode 100644 index 000000000000..60910c47e65b --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst @@ -0,0 +1 @@ +Settings dialog no longer expands with font size. From webhook-mailer at python.org Sat Jul 27 13:14:58 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 27 Jul 2019 17:14:58 -0000 Subject: [Python-checkins] bpo-37628: Fix IDLE config sample sizes (GH-14958) Message-ID: https://github.com/python/cpython/commit/0242eb3fa7818eb078c0915a4b844c5a2070b810 commit: 0242eb3fa7818eb078c0915a4b844c5a2070b810 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-27T10:14:54-07:00 summary: bpo-37628: Fix IDLE config sample sizes (GH-14958) The boxes for the font and highlight samples are now constrained by the overall config dialog size. They gain scrollbars when the when a large font size makes the samples too large for the box. (cherry picked from commit 3221a63c69268a9362802371a616f49d522a5c4f) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst M Lib/idlelib/configdialog.py M Lib/idlelib/idle_test/htest.py M Lib/idlelib/idle_test/test_textview.py M Lib/idlelib/textview.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 217f8fd0a5fb..4df6ecee69f1 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -33,6 +33,7 @@ from idlelib.parenmatch import ParenMatch from idlelib.format import FormatParagraph from idlelib.squeezer import Squeezer +from idlelib.textview import ScrollableTextFrame changes = ConfigChanges() # Reload changed options in the following classes. @@ -556,7 +557,9 @@ def create_page_font_tab(self): frame_font_param, variable=self.font_bold, onvalue=1, offvalue=0, text='Bold') # frame_sample. - self.font_sample = Text(frame_sample, width=20, height=20) + font_sample_frame = ScrollableTextFrame(frame_sample) + self.font_sample = font_sample_frame.text + self.font_sample.config(wrap=NONE, width=1, height=1) self.font_sample.insert(END, font_sample_text) # frame_indent. indent_title = Label( @@ -568,8 +571,9 @@ def create_page_font_tab(self): # Grid and pack widgets: self.columnconfigure(1, weight=1) + self.rowconfigure(2, weight=1) frame_font.grid(row=0, column=0, padx=5, pady=5) - frame_sample.grid(row=0, column=1, rowspan=2, padx=5, pady=5, + frame_sample.grid(row=0, column=1, rowspan=3, padx=5, pady=5, sticky='nsew') frame_indent.grid(row=1, column=0, padx=5, pady=5, sticky='ew') # frame_font. @@ -582,7 +586,7 @@ def create_page_font_tab(self): self.sizelist.pack(side=LEFT, anchor=W) self.bold_toggle.pack(side=LEFT, anchor=W, padx=20) # frame_sample. - self.font_sample.pack(expand=TRUE, fill=BOTH) + font_sample_frame.pack(expand=TRUE, fill=BOTH) # frame_indent. indent_title.pack(side=TOP, anchor=W, padx=5) self.indent_scale.pack(side=TOP, padx=5, fill=X) @@ -840,9 +844,11 @@ def create_page_highlight(self): frame_theme = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Highlighting Theme ') # frame_custom. - text = self.highlight_sample = Text( - frame_custom, relief=SOLID, borderwidth=1, - font=('courier', 12, ''), cursor='hand2', width=21, height=13, + sample_frame = ScrollableTextFrame( + frame_custom, relief=SOLID, borderwidth=1) + text = self.highlight_sample = sample_frame.text + text.configure( + font=('courier', 12, ''), cursor='hand2', width=1, height=1, takefocus=FALSE, highlightthickness=0, wrap=NONE) text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') @@ -868,7 +874,7 @@ def create_page_highlight(self): for texttag in text_and_tags: text.insert(END, texttag[0], texttag[1]) n_lines = len(text.get('1.0', END).splitlines()) - for lineno in range(1, n_lines + 1): + for lineno in range(1, n_lines): text.insert(f'{lineno}.0', f'{lineno:{len(str(n_lines))}d} ', 'linenumber') @@ -920,9 +926,9 @@ def tem(event, elem=element): frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_theme.pack(side=TOP, padx=5, pady=5, fill=X) # frame_custom. - self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) + self.frame_color_set.pack(side=TOP, padx=5, pady=5, fill=X) frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0) - self.highlight_sample.pack( + sample_frame.pack( side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) self.button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) self.targetlist.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=3) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index f2f37e161632..6990af519b1f 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -349,7 +349,7 @@ def _wrapper(parent): # htest # ViewWindow_spec = { 'file': 'textview', 'kwds': {'title': 'Test textview', - 'text': 'The quick brown fox jumps over the lazy dog.\n'*35, + 'contents': 'The quick brown fox jumps over the lazy dog.\n'*35, '_htest': True}, 'msg': "Test for read-only property of text.\n" "Select text, scroll window, close" diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py index 6f0c1930518a..7189378ab3dd 100644 --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -6,12 +6,12 @@ information about calls. """ from idlelib import textview as tv -import unittest from test.support import requires requires('gui') import os -from tkinter import Tk +import unittest +from tkinter import Tk, TclError, CHAR, NONE, WORD from tkinter.ttk import Button from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Mbox_func @@ -69,13 +69,65 @@ def test_ok(self): view.destroy() -class TextFrameTest(unittest.TestCase): +class AutoHideScrollbarTest(unittest.TestCase): + # Method set is tested in ScrollableTextFrameTest + def test_forbidden_geometry(self): + scroll = tv.AutoHideScrollbar(root) + self.assertRaises(TclError, scroll.pack) + self.assertRaises(TclError, scroll.place) + + +class ScrollableTextFrameTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.root = root = Tk() + root.withdraw() + + @classmethod + def tearDownClass(cls): + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + + def make_frame(self, wrap=NONE, **kwargs): + frame = tv.ScrollableTextFrame(self.root, wrap=wrap, **kwargs) + def cleanup_frame(): + frame.update_idletasks() + frame.destroy() + self.addCleanup(cleanup_frame) + return frame + + def test_line1(self): + frame = self.make_frame() + frame.text.insert('1.0', 'test text') + self.assertEqual(frame.text.get('1.0', '1.end'), 'test text') + + def test_horiz_scrollbar(self): + # The horizontal scrollbar should be shown/hidden according to + # the 'wrap' setting: It should only be shown when 'wrap' is + # set to NONE. + + # wrap = NONE -> with horizontal scrolling + frame = self.make_frame(wrap=NONE) + self.assertEqual(frame.text.cget('wrap'), NONE) + self.assertIsNotNone(frame.xscroll) + + # wrap != NONE -> no horizontal scrolling + for wrap in [CHAR, WORD]: + with self.subTest(wrap=wrap): + frame = self.make_frame(wrap=wrap) + self.assertEqual(frame.text.cget('wrap'), wrap) + self.assertIsNone(frame.xscroll) + + +class ViewFrameTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.root = root = Tk() root.withdraw() - cls.frame = tv.TextFrame(root, 'test text') + cls.frame = tv.ViewFrame(root, 'test text') @classmethod def tearDownClass(cls): diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index 4867a80db1ab..808a2aefab4f 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -2,14 +2,15 @@ """ from tkinter import Toplevel, Text, TclError,\ - HORIZONTAL, VERTICAL, N, S, E, W + HORIZONTAL, VERTICAL, NS, EW, NSEW, NONE, WORD, SUNKEN from tkinter.ttk import Frame, Scrollbar, Button from tkinter.messagebox import showerror +from functools import update_wrapper from idlelib.colorizer import color_config -class AutoHiddenScrollbar(Scrollbar): +class AutoHideScrollbar(Scrollbar): """A scrollbar that is automatically hidden when not needed. Only the grid geometry manager is supported. @@ -28,52 +29,70 @@ def place(self, **kwargs): raise TclError(f'{self.__class__.__name__} does not support "place"') -class TextFrame(Frame): - "Display text with scrollbar." +class ScrollableTextFrame(Frame): + """Display text with scrollbar(s).""" - def __init__(self, parent, rawtext, wrap='word'): + def __init__(self, master, wrap=NONE, **kwargs): """Create a frame for Textview. - parent - parent widget for this frame - rawtext - text to display + master - master widget for this frame + wrap - type of text wrapping to use ('word', 'char' or 'none') + + All parameters except for 'wrap' are passed to Frame.__init__(). + + The Text widget is accessible via the 'text' attribute. + + Note: Changing the wrapping mode of the text widget after + instantiation is not supported. """ - super().__init__(parent) - self['relief'] = 'sunken' - self['height'] = 700 + super().__init__(master, **kwargs) - self.text = text = Text(self, wrap=wrap, highlightthickness=0) - color_config(text) - text.grid(row=0, column=0, sticky=N+S+E+W) + text = self.text = Text(self, wrap=wrap) + text.grid(row=0, column=0, sticky=NSEW) self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) - text.insert(0.0, rawtext) - text['state'] = 'disabled' - text.focus_set() # vertical scrollbar - self.yscroll = yscroll = AutoHiddenScrollbar(self, orient=VERTICAL, - takefocus=False, - command=text.yview) - text['yscrollcommand'] = yscroll.set - yscroll.grid(row=0, column=1, sticky=N+S) - - if wrap == 'none': - # horizontal scrollbar - self.xscroll = xscroll = AutoHiddenScrollbar(self, orient=HORIZONTAL, - takefocus=False, - command=text.xview) - text['xscrollcommand'] = xscroll.set - xscroll.grid(row=1, column=0, sticky=E+W) + self.yscroll = AutoHideScrollbar(self, orient=VERTICAL, + takefocus=False, + command=text.yview) + self.yscroll.grid(row=0, column=1, sticky=NS) + text['yscrollcommand'] = self.yscroll.set + + # horizontal scrollbar - only when wrap is set to NONE + if wrap == NONE: + self.xscroll = AutoHideScrollbar(self, orient=HORIZONTAL, + takefocus=False, + command=text.xview) + self.xscroll.grid(row=1, column=0, sticky=EW) + text['xscrollcommand'] = self.xscroll.set + else: + self.xscroll = None class ViewFrame(Frame): "Display TextFrame and Close button." - def __init__(self, parent, text, wrap='word'): + def __init__(self, parent, contents, wrap='word'): + """Create a frame for viewing text with a "Close" button. + + parent - parent widget for this frame + contents - text to display + wrap - type of text wrapping to use ('word', 'char' or 'none') + + The Text widget is accessible via the 'text' attribute. + """ super().__init__(parent) self.parent = parent self.bind('', self.ok) self.bind('', self.ok) - self.textframe = TextFrame(self, text, wrap=wrap) + self.textframe = ScrollableTextFrame(self, relief=SUNKEN, height=700) + + text = self.text = self.textframe.text + text.insert('1.0', contents) + text.configure(wrap=wrap, highlightthickness=0, state='disabled') + color_config(text) + text.focus_set() + self.button_ok = button_ok = Button( self, text='Close', command=self.ok, takefocus=False) self.textframe.pack(side='top', expand=True, fill='both') @@ -87,7 +106,7 @@ def ok(self, event=None): class ViewWindow(Toplevel): "A simple text viewer dialog for IDLE." - def __init__(self, parent, title, text, modal=True, wrap='word', + def __init__(self, parent, title, contents, modal=True, wrap=WORD, *, _htest=False, _utest=False): """Show the given text in a scrollable window with a 'close' button. @@ -96,7 +115,7 @@ def __init__(self, parent, title, text, modal=True, wrap='word', parent - parent of this dialog title - string which is title of popup dialog - text - text to display in dialog + contents - text to display in dialog wrap - type of text wrapping to use ('word', 'char' or 'none') _htest - bool; change box location when running htest. _utest - bool; don't wait_window when running unittest. @@ -109,7 +128,7 @@ def __init__(self, parent, title, text, modal=True, wrap='word', self.geometry(f'=750x500+{x}+{y}') self.title(title) - self.viewframe = ViewFrame(self, text, wrap=wrap) + self.viewframe = ViewFrame(self, contents, wrap=wrap) self.protocol("WM_DELETE_WINDOW", self.ok) self.button_ok = button_ok = Button(self, text='Close', command=self.ok, takefocus=False) @@ -129,18 +148,18 @@ def ok(self, event=None): self.destroy() -def view_text(parent, title, text, modal=True, wrap='word', _utest=False): +def view_text(parent, title, contents, modal=True, wrap='word', _utest=False): """Create text viewer for given text. parent - parent of this dialog title - string which is the title of popup dialog - text - text to display in this dialog + contents - text to display in this dialog wrap - type of text wrapping to use ('word', 'char' or 'none') modal - controls if users can interact with other windows while this dialog is displayed _utest - bool; controls wait_window on unittest """ - return ViewWindow(parent, title, text, modal, wrap=wrap, _utest=_utest) + return ViewWindow(parent, title, contents, modal, wrap=wrap, _utest=_utest) def view_file(parent, title, filename, encoding, modal=True, wrap='word', diff --git a/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst b/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst new file mode 100644 index 000000000000..60910c47e65b --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst @@ -0,0 +1 @@ +Settings dialog no longer expands with font size. From webhook-mailer at python.org Sat Jul 27 13:19:16 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 27 Jul 2019 17:19:16 -0000 Subject: [Python-checkins] bpo-37628: Fix IDLE config sample sizes (GH-14958) Message-ID: https://github.com/python/cpython/commit/171019354aa2c717af2e7b2c90aec7b9724f7282 commit: 171019354aa2c717af2e7b2c90aec7b9724f7282 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-27T10:19:12-07:00 summary: bpo-37628: Fix IDLE config sample sizes (GH-14958) The boxes for the font and highlight samples are now constrained by the overall config dialog size. They gain scrollbars when the when a large font size makes the samples too large for the box. (cherry picked from commit 3221a63c69268a9362802371a616f49d522a5c4f) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst M Lib/idlelib/configdialog.py M Lib/idlelib/idle_test/htest.py M Lib/idlelib/idle_test/test_textview.py M Lib/idlelib/textview.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 217f8fd0a5fb..4df6ecee69f1 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -33,6 +33,7 @@ from idlelib.parenmatch import ParenMatch from idlelib.format import FormatParagraph from idlelib.squeezer import Squeezer +from idlelib.textview import ScrollableTextFrame changes = ConfigChanges() # Reload changed options in the following classes. @@ -556,7 +557,9 @@ def create_page_font_tab(self): frame_font_param, variable=self.font_bold, onvalue=1, offvalue=0, text='Bold') # frame_sample. - self.font_sample = Text(frame_sample, width=20, height=20) + font_sample_frame = ScrollableTextFrame(frame_sample) + self.font_sample = font_sample_frame.text + self.font_sample.config(wrap=NONE, width=1, height=1) self.font_sample.insert(END, font_sample_text) # frame_indent. indent_title = Label( @@ -568,8 +571,9 @@ def create_page_font_tab(self): # Grid and pack widgets: self.columnconfigure(1, weight=1) + self.rowconfigure(2, weight=1) frame_font.grid(row=0, column=0, padx=5, pady=5) - frame_sample.grid(row=0, column=1, rowspan=2, padx=5, pady=5, + frame_sample.grid(row=0, column=1, rowspan=3, padx=5, pady=5, sticky='nsew') frame_indent.grid(row=1, column=0, padx=5, pady=5, sticky='ew') # frame_font. @@ -582,7 +586,7 @@ def create_page_font_tab(self): self.sizelist.pack(side=LEFT, anchor=W) self.bold_toggle.pack(side=LEFT, anchor=W, padx=20) # frame_sample. - self.font_sample.pack(expand=TRUE, fill=BOTH) + font_sample_frame.pack(expand=TRUE, fill=BOTH) # frame_indent. indent_title.pack(side=TOP, anchor=W, padx=5) self.indent_scale.pack(side=TOP, padx=5, fill=X) @@ -840,9 +844,11 @@ def create_page_highlight(self): frame_theme = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Highlighting Theme ') # frame_custom. - text = self.highlight_sample = Text( - frame_custom, relief=SOLID, borderwidth=1, - font=('courier', 12, ''), cursor='hand2', width=21, height=13, + sample_frame = ScrollableTextFrame( + frame_custom, relief=SOLID, borderwidth=1) + text = self.highlight_sample = sample_frame.text + text.configure( + font=('courier', 12, ''), cursor='hand2', width=1, height=1, takefocus=FALSE, highlightthickness=0, wrap=NONE) text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') @@ -868,7 +874,7 @@ def create_page_highlight(self): for texttag in text_and_tags: text.insert(END, texttag[0], texttag[1]) n_lines = len(text.get('1.0', END).splitlines()) - for lineno in range(1, n_lines + 1): + for lineno in range(1, n_lines): text.insert(f'{lineno}.0', f'{lineno:{len(str(n_lines))}d} ', 'linenumber') @@ -920,9 +926,9 @@ def tem(event, elem=element): frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_theme.pack(side=TOP, padx=5, pady=5, fill=X) # frame_custom. - self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) + self.frame_color_set.pack(side=TOP, padx=5, pady=5, fill=X) frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0) - self.highlight_sample.pack( + sample_frame.pack( side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) self.button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) self.targetlist.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=3) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index f2f37e161632..6990af519b1f 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -349,7 +349,7 @@ def _wrapper(parent): # htest # ViewWindow_spec = { 'file': 'textview', 'kwds': {'title': 'Test textview', - 'text': 'The quick brown fox jumps over the lazy dog.\n'*35, + 'contents': 'The quick brown fox jumps over the lazy dog.\n'*35, '_htest': True}, 'msg': "Test for read-only property of text.\n" "Select text, scroll window, close" diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py index 6f0c1930518a..7189378ab3dd 100644 --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -6,12 +6,12 @@ information about calls. """ from idlelib import textview as tv -import unittest from test.support import requires requires('gui') import os -from tkinter import Tk +import unittest +from tkinter import Tk, TclError, CHAR, NONE, WORD from tkinter.ttk import Button from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Mbox_func @@ -69,13 +69,65 @@ def test_ok(self): view.destroy() -class TextFrameTest(unittest.TestCase): +class AutoHideScrollbarTest(unittest.TestCase): + # Method set is tested in ScrollableTextFrameTest + def test_forbidden_geometry(self): + scroll = tv.AutoHideScrollbar(root) + self.assertRaises(TclError, scroll.pack) + self.assertRaises(TclError, scroll.place) + + +class ScrollableTextFrameTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.root = root = Tk() + root.withdraw() + + @classmethod + def tearDownClass(cls): + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + + def make_frame(self, wrap=NONE, **kwargs): + frame = tv.ScrollableTextFrame(self.root, wrap=wrap, **kwargs) + def cleanup_frame(): + frame.update_idletasks() + frame.destroy() + self.addCleanup(cleanup_frame) + return frame + + def test_line1(self): + frame = self.make_frame() + frame.text.insert('1.0', 'test text') + self.assertEqual(frame.text.get('1.0', '1.end'), 'test text') + + def test_horiz_scrollbar(self): + # The horizontal scrollbar should be shown/hidden according to + # the 'wrap' setting: It should only be shown when 'wrap' is + # set to NONE. + + # wrap = NONE -> with horizontal scrolling + frame = self.make_frame(wrap=NONE) + self.assertEqual(frame.text.cget('wrap'), NONE) + self.assertIsNotNone(frame.xscroll) + + # wrap != NONE -> no horizontal scrolling + for wrap in [CHAR, WORD]: + with self.subTest(wrap=wrap): + frame = self.make_frame(wrap=wrap) + self.assertEqual(frame.text.cget('wrap'), wrap) + self.assertIsNone(frame.xscroll) + + +class ViewFrameTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.root = root = Tk() root.withdraw() - cls.frame = tv.TextFrame(root, 'test text') + cls.frame = tv.ViewFrame(root, 'test text') @classmethod def tearDownClass(cls): diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index 4867a80db1ab..808a2aefab4f 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -2,14 +2,15 @@ """ from tkinter import Toplevel, Text, TclError,\ - HORIZONTAL, VERTICAL, N, S, E, W + HORIZONTAL, VERTICAL, NS, EW, NSEW, NONE, WORD, SUNKEN from tkinter.ttk import Frame, Scrollbar, Button from tkinter.messagebox import showerror +from functools import update_wrapper from idlelib.colorizer import color_config -class AutoHiddenScrollbar(Scrollbar): +class AutoHideScrollbar(Scrollbar): """A scrollbar that is automatically hidden when not needed. Only the grid geometry manager is supported. @@ -28,52 +29,70 @@ def place(self, **kwargs): raise TclError(f'{self.__class__.__name__} does not support "place"') -class TextFrame(Frame): - "Display text with scrollbar." +class ScrollableTextFrame(Frame): + """Display text with scrollbar(s).""" - def __init__(self, parent, rawtext, wrap='word'): + def __init__(self, master, wrap=NONE, **kwargs): """Create a frame for Textview. - parent - parent widget for this frame - rawtext - text to display + master - master widget for this frame + wrap - type of text wrapping to use ('word', 'char' or 'none') + + All parameters except for 'wrap' are passed to Frame.__init__(). + + The Text widget is accessible via the 'text' attribute. + + Note: Changing the wrapping mode of the text widget after + instantiation is not supported. """ - super().__init__(parent) - self['relief'] = 'sunken' - self['height'] = 700 + super().__init__(master, **kwargs) - self.text = text = Text(self, wrap=wrap, highlightthickness=0) - color_config(text) - text.grid(row=0, column=0, sticky=N+S+E+W) + text = self.text = Text(self, wrap=wrap) + text.grid(row=0, column=0, sticky=NSEW) self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) - text.insert(0.0, rawtext) - text['state'] = 'disabled' - text.focus_set() # vertical scrollbar - self.yscroll = yscroll = AutoHiddenScrollbar(self, orient=VERTICAL, - takefocus=False, - command=text.yview) - text['yscrollcommand'] = yscroll.set - yscroll.grid(row=0, column=1, sticky=N+S) - - if wrap == 'none': - # horizontal scrollbar - self.xscroll = xscroll = AutoHiddenScrollbar(self, orient=HORIZONTAL, - takefocus=False, - command=text.xview) - text['xscrollcommand'] = xscroll.set - xscroll.grid(row=1, column=0, sticky=E+W) + self.yscroll = AutoHideScrollbar(self, orient=VERTICAL, + takefocus=False, + command=text.yview) + self.yscroll.grid(row=0, column=1, sticky=NS) + text['yscrollcommand'] = self.yscroll.set + + # horizontal scrollbar - only when wrap is set to NONE + if wrap == NONE: + self.xscroll = AutoHideScrollbar(self, orient=HORIZONTAL, + takefocus=False, + command=text.xview) + self.xscroll.grid(row=1, column=0, sticky=EW) + text['xscrollcommand'] = self.xscroll.set + else: + self.xscroll = None class ViewFrame(Frame): "Display TextFrame and Close button." - def __init__(self, parent, text, wrap='word'): + def __init__(self, parent, contents, wrap='word'): + """Create a frame for viewing text with a "Close" button. + + parent - parent widget for this frame + contents - text to display + wrap - type of text wrapping to use ('word', 'char' or 'none') + + The Text widget is accessible via the 'text' attribute. + """ super().__init__(parent) self.parent = parent self.bind('', self.ok) self.bind('', self.ok) - self.textframe = TextFrame(self, text, wrap=wrap) + self.textframe = ScrollableTextFrame(self, relief=SUNKEN, height=700) + + text = self.text = self.textframe.text + text.insert('1.0', contents) + text.configure(wrap=wrap, highlightthickness=0, state='disabled') + color_config(text) + text.focus_set() + self.button_ok = button_ok = Button( self, text='Close', command=self.ok, takefocus=False) self.textframe.pack(side='top', expand=True, fill='both') @@ -87,7 +106,7 @@ def ok(self, event=None): class ViewWindow(Toplevel): "A simple text viewer dialog for IDLE." - def __init__(self, parent, title, text, modal=True, wrap='word', + def __init__(self, parent, title, contents, modal=True, wrap=WORD, *, _htest=False, _utest=False): """Show the given text in a scrollable window with a 'close' button. @@ -96,7 +115,7 @@ def __init__(self, parent, title, text, modal=True, wrap='word', parent - parent of this dialog title - string which is title of popup dialog - text - text to display in dialog + contents - text to display in dialog wrap - type of text wrapping to use ('word', 'char' or 'none') _htest - bool; change box location when running htest. _utest - bool; don't wait_window when running unittest. @@ -109,7 +128,7 @@ def __init__(self, parent, title, text, modal=True, wrap='word', self.geometry(f'=750x500+{x}+{y}') self.title(title) - self.viewframe = ViewFrame(self, text, wrap=wrap) + self.viewframe = ViewFrame(self, contents, wrap=wrap) self.protocol("WM_DELETE_WINDOW", self.ok) self.button_ok = button_ok = Button(self, text='Close', command=self.ok, takefocus=False) @@ -129,18 +148,18 @@ def ok(self, event=None): self.destroy() -def view_text(parent, title, text, modal=True, wrap='word', _utest=False): +def view_text(parent, title, contents, modal=True, wrap='word', _utest=False): """Create text viewer for given text. parent - parent of this dialog title - string which is the title of popup dialog - text - text to display in this dialog + contents - text to display in this dialog wrap - type of text wrapping to use ('word', 'char' or 'none') modal - controls if users can interact with other windows while this dialog is displayed _utest - bool; controls wait_window on unittest """ - return ViewWindow(parent, title, text, modal, wrap=wrap, _utest=_utest) + return ViewWindow(parent, title, contents, modal, wrap=wrap, _utest=_utest) def view_file(parent, title, filename, encoding, modal=True, wrap='word', diff --git a/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst b/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst new file mode 100644 index 000000000000..60910c47e65b --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst @@ -0,0 +1 @@ +Settings dialog no longer expands with font size. From webhook-mailer at python.org Sat Jul 27 17:04:46 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Sat, 27 Jul 2019 21:04:46 -0000 Subject: [Python-checkins] bpo-37691: Let math.dist() accept sequences and iterables for coordinates (GH-14975) Message-ID: https://github.com/python/cpython/commit/6b5f1b496f0b20144592b640b9c975df43a29eb0 commit: 6b5f1b496f0b20144592b640b9c975df43a29eb0 branch: master author: Raymond Hettinger committer: GitHub date: 2019-07-27T14:04:29-07:00 summary: bpo-37691: Let math.dist() accept sequences and iterables for coordinates (GH-14975) files: A Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst M Doc/library/math.rst M Lib/test/test_math.py M Modules/clinic/mathmodule.c.h M Modules/mathmodule.c diff --git a/Doc/library/math.rst b/Doc/library/math.rst index be953cfe9599..43eaba935a16 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -400,7 +400,8 @@ Trigonometric functions .. function:: dist(p, q) Return the Euclidean distance between two points *p* and *q*, each - given as a tuple of coordinates. The two tuples must be the same size. + given as a sequence (or iterable) of coordinates. The two points + must have the same dimension. Roughly equivalent to:: diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 567a5c694c15..c237bc1942e6 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -833,6 +833,10 @@ def testDist(self): sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) ) + # Test non-tuple inputs + self.assertEqual(dist([1.0, 2.0, 3.0], [4.0, 2.0, -1.0]), 5.0) + self.assertEqual(dist(iter([1.0, 2.0, 3.0]), iter([4.0, 2.0, -1.0])), 5.0) + # Test allowable types (those with __float__) self.assertEqual(dist((14.0, 1.0), (2.0, -4.0)), 13.0) self.assertEqual(dist((14, 1), (2, -4)), 13) @@ -873,8 +877,6 @@ class T(tuple): dist((1, 2, 3), (4, 5, 6), (7, 8, 9)) with self.assertRaises(TypeError): # Scalars not allowed dist(1, 2) - with self.assertRaises(TypeError): # Lists not allowed - dist([1, 2, 3], [4, 5, 6]) with self.assertRaises(TypeError): # Reject values without __float__ dist((1.1, 'string', 2.2), (1, 2, 3)) with self.assertRaises(ValueError): # Check dimension agree diff --git a/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst b/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst new file mode 100644 index 000000000000..048478c008e8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst @@ -0,0 +1,2 @@ +Let math.dist() accept coordinates as sequences (or iterables) rather than +just tuples. diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 966b99b6a369..84561b955787 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -297,8 +297,8 @@ PyDoc_STRVAR(math_dist__doc__, "\n" "Return the Euclidean distance between two points p and q.\n" "\n" -"The points should be specified as tuples of coordinates.\n" -"Both tuples must be the same size.\n" +"The points should be specified as sequences (or iterables) of\n" +"coordinates. Both inputs must have the same dimension.\n" "\n" "Roughly equivalent to:\n" " sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))"); @@ -319,15 +319,7 @@ math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("dist", nargs, 2, 2)) { goto exit; } - if (!PyTuple_Check(args[0])) { - _PyArg_BadArgument("dist", 1, "tuple", args[0]); - goto exit; - } p = args[0]; - if (!PyTuple_Check(args[1])) { - _PyArg_BadArgument("dist", 2, "tuple", args[1]); - goto exit; - } q = args[1]; return_value = math_dist_impl(module, p, q); @@ -720,4 +712,4 @@ math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=0eb1e76a769cdd30 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f93cfe13ab2fdb4e input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 4c1dbbe15ecc..e1b46ec384a3 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2427,14 +2427,14 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan) /*[clinic input] math.dist - p: object(subclass_of='&PyTuple_Type') - q: object(subclass_of='&PyTuple_Type') + p: object + q: object / Return the Euclidean distance between two points p and q. -The points should be specified as tuples of coordinates. -Both tuples must be the same size. +The points should be specified as sequences (or iterables) of +coordinates. Both inputs must have the same dimension. Roughly equivalent to: sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) @@ -2442,16 +2442,34 @@ Roughly equivalent to: static PyObject * math_dist_impl(PyObject *module, PyObject *p, PyObject *q) -/*[clinic end generated code: output=56bd9538d06bbcfe input=937122eaa5f19272]*/ +/*[clinic end generated code: output=56bd9538d06bbcfe input=74e85e1b6092e68e]*/ { PyObject *item; double max = 0.0; double x, px, qx, result; Py_ssize_t i, m, n; - int found_nan = 0; + int found_nan = 0, p_allocated = 0, q_allocated = 0; double diffs_on_stack[NUM_STACK_ELEMS]; double *diffs = diffs_on_stack; + if (!PyTuple_Check(p)) { + p = PySequence_Tuple(p); + if (p == NULL) { + return NULL; + } + p_allocated = 1; + } + if (!PyTuple_Check(q)) { + q = PySequence_Tuple(q); + if (q == NULL) { + if (p_allocated) { + Py_DECREF(p); + } + return NULL; + } + q_allocated = 1; + } + m = PyTuple_GET_SIZE(p); n = PyTuple_GET_SIZE(q); if (m != n) { @@ -2482,12 +2500,24 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) if (diffs != diffs_on_stack) { PyObject_Free(diffs); } + if (p_allocated) { + Py_DECREF(p); + } + if (q_allocated) { + Py_DECREF(q); + } return PyFloat_FromDouble(result); error_exit: if (diffs != diffs_on_stack) { PyObject_Free(diffs); } + if (p_allocated) { + Py_DECREF(p); + } + if (q_allocated) { + Py_DECREF(q); + } return NULL; } From webhook-mailer at python.org Sat Jul 27 17:27:05 2019 From: webhook-mailer at python.org (Raymond Hettinger) Date: Sat, 27 Jul 2019 21:27:05 -0000 Subject: [Python-checkins] bpo-37691: Let math.dist() accept sequences and iterables for coordinates (GH-14975) (GH-14984) Message-ID: https://github.com/python/cpython/commit/76821bab9cb77fa7f847e66f8b2309ca30546c7f commit: 76821bab9cb77fa7f847e66f8b2309ca30546c7f branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Raymond Hettinger date: 2019-07-27T14:26:58-07:00 summary: bpo-37691: Let math.dist() accept sequences and iterables for coordinates (GH-14975) (GH-14984) (cherry picked from commit 6b5f1b496f0b20144592b640b9c975df43a29eb0) Co-authored-by: Raymond Hettinger files: A Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst M Doc/library/math.rst M Lib/test/test_math.py M Modules/clinic/mathmodule.c.h M Modules/mathmodule.c diff --git a/Doc/library/math.rst b/Doc/library/math.rst index ff937d27c6ce..776768dd8ee7 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -394,7 +394,8 @@ Trigonometric functions .. function:: dist(p, q) Return the Euclidean distance between two points *p* and *q*, each - given as a tuple of coordinates. The two tuples must be the same size. + given as a sequence (or iterable) of coordinates. The two points + must have the same dimension. Roughly equivalent to:: diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 90b8b7b4a9e3..6edc3e1a547a 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -826,6 +826,10 @@ def testDist(self): sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) ) + # Test non-tuple inputs + self.assertEqual(dist([1.0, 2.0, 3.0], [4.0, 2.0, -1.0]), 5.0) + self.assertEqual(dist(iter([1.0, 2.0, 3.0]), iter([4.0, 2.0, -1.0])), 5.0) + # Test allowable types (those with __float__) self.assertEqual(dist((14.0, 1.0), (2.0, -4.0)), 13.0) self.assertEqual(dist((14, 1), (2, -4)), 13) @@ -866,8 +870,6 @@ class T(tuple): dist((1, 2, 3), (4, 5, 6), (7, 8, 9)) with self.assertRaises(TypeError): # Scalars not allowed dist(1, 2) - with self.assertRaises(TypeError): # Lists not allowed - dist([1, 2, 3], [4, 5, 6]) with self.assertRaises(TypeError): # Reject values without __float__ dist((1.1, 'string', 2.2), (1, 2, 3)) with self.assertRaises(ValueError): # Check dimension agree diff --git a/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst b/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst new file mode 100644 index 000000000000..048478c008e8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst @@ -0,0 +1,2 @@ +Let math.dist() accept coordinates as sequences (or iterables) rather than +just tuples. diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 966b99b6a369..84561b955787 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -297,8 +297,8 @@ PyDoc_STRVAR(math_dist__doc__, "\n" "Return the Euclidean distance between two points p and q.\n" "\n" -"The points should be specified as tuples of coordinates.\n" -"Both tuples must be the same size.\n" +"The points should be specified as sequences (or iterables) of\n" +"coordinates. Both inputs must have the same dimension.\n" "\n" "Roughly equivalent to:\n" " sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))"); @@ -319,15 +319,7 @@ math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("dist", nargs, 2, 2)) { goto exit; } - if (!PyTuple_Check(args[0])) { - _PyArg_BadArgument("dist", 1, "tuple", args[0]); - goto exit; - } p = args[0]; - if (!PyTuple_Check(args[1])) { - _PyArg_BadArgument("dist", 2, "tuple", args[1]); - goto exit; - } q = args[1]; return_value = math_dist_impl(module, p, q); @@ -720,4 +712,4 @@ math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=0eb1e76a769cdd30 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f93cfe13ab2fdb4e input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index dd596c8de6e3..4e9733781111 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2418,14 +2418,14 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan) /*[clinic input] math.dist - p: object(subclass_of='&PyTuple_Type') - q: object(subclass_of='&PyTuple_Type') + p: object + q: object / Return the Euclidean distance between two points p and q. -The points should be specified as tuples of coordinates. -Both tuples must be the same size. +The points should be specified as sequences (or iterables) of +coordinates. Both inputs must have the same dimension. Roughly equivalent to: sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) @@ -2433,16 +2433,34 @@ Roughly equivalent to: static PyObject * math_dist_impl(PyObject *module, PyObject *p, PyObject *q) -/*[clinic end generated code: output=56bd9538d06bbcfe input=937122eaa5f19272]*/ +/*[clinic end generated code: output=56bd9538d06bbcfe input=74e85e1b6092e68e]*/ { PyObject *item; double max = 0.0; double x, px, qx, result; Py_ssize_t i, m, n; - int found_nan = 0; + int found_nan = 0, p_allocated = 0, q_allocated = 0; double diffs_on_stack[NUM_STACK_ELEMS]; double *diffs = diffs_on_stack; + if (!PyTuple_Check(p)) { + p = PySequence_Tuple(p); + if (p == NULL) { + return NULL; + } + p_allocated = 1; + } + if (!PyTuple_Check(q)) { + q = PySequence_Tuple(q); + if (q == NULL) { + if (p_allocated) { + Py_DECREF(p); + } + return NULL; + } + q_allocated = 1; + } + m = PyTuple_GET_SIZE(p); n = PyTuple_GET_SIZE(q); if (m != n) { @@ -2473,12 +2491,24 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) if (diffs != diffs_on_stack) { PyObject_Free(diffs); } + if (p_allocated) { + Py_DECREF(p); + } + if (q_allocated) { + Py_DECREF(q); + } return PyFloat_FromDouble(result); error_exit: if (diffs != diffs_on_stack) { PyObject_Free(diffs); } + if (p_allocated) { + Py_DECREF(p); + } + if (q_allocated) { + Py_DECREF(q); + } return NULL; } From webhook-mailer at python.org Sun Jul 28 07:41:04 2019 From: webhook-mailer at python.org (Nick Coghlan) Date: Sun, 28 Jul 2019 11:41:04 -0000 Subject: [Python-checkins] Remove trailing .0 from version changed note (GH-14987) Message-ID: https://github.com/python/cpython/commit/17a058ed6ffa7f56c0920d15d214ad080e7eef86 commit: 17a058ed6ffa7f56c0920d15d214ad080e7eef86 branch: master author: Nick Coghlan committer: GitHub date: 2019-07-28T21:40:47+10:00 summary: Remove trailing .0 from version changed note (GH-14987) files: M Doc/c-api/memory.rst diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index f97ae453487f..d3c8b30d360e 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -472,7 +472,7 @@ Customize Memory Allocators if the GIL is held when functions of :c:data:`PYMEM_DOMAIN_OBJ` and :c:data:`PYMEM_DOMAIN_MEM` domains are called. - .. versionchanged:: 3.8.0 + .. versionchanged:: 3.8 Byte patterns ``0xCB`` (``CLEANBYTE``), ``0xDB`` (``DEADBYTE``) and ``0xFB`` (``FORBIDDENBYTE``) have been replaced with ``0xCD``, ``0xDD`` and ``0xFD`` to use the same values than Windows CRT debug ``malloc()`` From webhook-mailer at python.org Sun Jul 28 07:48:30 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 28 Jul 2019 11:48:30 -0000 Subject: [Python-checkins] Remove trailing .0 from version changed note (GH-14987) Message-ID: https://github.com/python/cpython/commit/69372eea08509b4209977f5643db02d6b8d10ca3 commit: 69372eea08509b4209977f5643db02d6b8d10ca3 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-28T04:48:26-07:00 summary: Remove trailing .0 from version changed note (GH-14987) (cherry picked from commit 17a058ed6ffa7f56c0920d15d214ad080e7eef86) Co-authored-by: Nick Coghlan files: M Doc/c-api/memory.rst diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index f97ae453487f..d3c8b30d360e 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -472,7 +472,7 @@ Customize Memory Allocators if the GIL is held when functions of :c:data:`PYMEM_DOMAIN_OBJ` and :c:data:`PYMEM_DOMAIN_MEM` domains are called. - .. versionchanged:: 3.8.0 + .. versionchanged:: 3.8 Byte patterns ``0xCB`` (``CLEANBYTE``), ``0xDB`` (``DEADBYTE``) and ``0xFB`` (``FORBIDDENBYTE``) have been replaced with ``0xCD``, ``0xDD`` and ``0xFD`` to use the same values than Windows CRT debug ``malloc()`` From webhook-mailer at python.org Sun Jul 28 12:04:48 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Sun, 28 Jul 2019 16:04:48 -0000 Subject: [Python-checkins] bpo-37692: Improve highlight config sample (#14983) Message-ID: https://github.com/python/cpython/commit/b222955355c8077a3ceca79195731663d7c3dd5f commit: b222955355c8077a3ceca79195731663d7c3dd5f branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-28T12:04:31-04:00 summary: bpo-37692: Improve highlight config sample (#14983) Use an example shell interaction in the sample and better labels for shell elements. files: A Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst M Lib/idlelib/configdialog.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 4df6ecee69f1..bba17d34938d 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -808,7 +808,7 @@ def create_page_highlight(self): (*)theme_message: Label """ self.theme_elements = { - 'Normal Text': ('normal', '00'), + 'Normal Code or Text': ('normal', '00'), 'Code Context': ('context', '01'), 'Python Keywords': ('keyword', '02'), 'Python Definitions': ('definition', '03'), @@ -819,10 +819,10 @@ def create_page_highlight(self): 'Found Text': ('hit', '08'), 'Cursor': ('cursor', '09'), 'Editor Breakpoint': ('break', '10'), - 'Shell Normal Text': ('console', '11'), - 'Shell Error Text': ('error', '12'), - 'Shell Stdout Text': ('stdout', '13'), - 'Shell Stderr Text': ('stderr', '14'), + 'Shell Prompt': ('console', '11'), + 'Error Text': ('error', '12'), + 'Shell User Output': ('stdout', '13'), + 'Shell User Exception': ('stderr', '14'), 'Line Number': ('linenumber', '16'), } self.builtin_name = tracers.add( @@ -852,27 +852,26 @@ def create_page_highlight(self): takefocus=FALSE, highlightthickness=0, wrap=NONE) text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') - text_and_tags=( - ('\n', 'normal'), - ('#you can click here', 'comment'), ('\n', 'normal'), - ('#to choose items', 'comment'), ('\n', 'normal'), - ('code context section', 'context'), ('\n\n', 'normal'), + string_tags=( + ('# Click selects item.', 'comment'), ('\n', 'normal'), + ('code context section', 'context'), ('\n', 'normal'), + ('| cursor', 'cursor'), ('\n', 'normal'), ('def', 'keyword'), (' ', 'normal'), ('func', 'definition'), ('(param):\n ', 'normal'), - ('"""string"""', 'string'), ('\n var0 = ', 'normal'), + ('"Return None."', 'string'), ('\n var0 = ', 'normal'), ("'string'", 'string'), ('\n var1 = ', 'normal'), ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), ("'found'", 'hit'), ('\n var3 = ', 'normal'), ('list', 'builtin'), ('(', 'normal'), ('None', 'keyword'), (')\n', 'normal'), (' breakpoint("line")', 'break'), ('\n\n', 'normal'), - (' error ', 'error'), (' ', 'normal'), - ('cursor |', 'cursor'), ('\n ', 'normal'), - ('shell', 'console'), (' ', 'normal'), - ('stdout', 'stdout'), (' ', 'normal'), - ('stderr', 'stderr'), ('\n\n', 'normal')) - for texttag in text_and_tags: - text.insert(END, texttag[0], texttag[1]) + ('>>>', 'console'), (' 3.14**2\n', 'normal'), + ('9.8596', 'stdout'), ('\n', 'normal'), + ('>>>', 'console'), (' pri ', 'normal'), + ('n', 'error'), ('t(\n', 'normal'), + ('SyntaxError', 'stderr'), ('\n', 'normal')) + for string, tag in string_tags: + text.insert(END, string, tag) n_lines = len(text.get('1.0', END).splitlines()) for lineno in range(1, n_lines): text.insert(f'{lineno}.0', diff --git a/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst b/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst new file mode 100644 index 000000000000..b3beadc1e026 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst @@ -0,0 +1,2 @@ +Improve highlight config sample with example shell interaction and better +labels for shell elements. From webhook-mailer at python.org Sun Jul 28 12:22:24 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 28 Jul 2019 16:22:24 -0000 Subject: [Python-checkins] bpo-37692: Improve highlight config sample (GH-14983) Message-ID: https://github.com/python/cpython/commit/d30626443d3ca0ea690c6124bdc95a53c12ecd9d commit: d30626443d3ca0ea690c6124bdc95a53c12ecd9d branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-28T09:22:18-07:00 summary: bpo-37692: Improve highlight config sample (GH-14983) Use an example shell interaction in the sample and better labels for shell elements. (cherry picked from commit b222955355c8077a3ceca79195731663d7c3dd5f) Co-authored-by: Terry Jan Reedy files: A Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst M Lib/idlelib/configdialog.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 4df6ecee69f1..bba17d34938d 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -808,7 +808,7 @@ def create_page_highlight(self): (*)theme_message: Label """ self.theme_elements = { - 'Normal Text': ('normal', '00'), + 'Normal Code or Text': ('normal', '00'), 'Code Context': ('context', '01'), 'Python Keywords': ('keyword', '02'), 'Python Definitions': ('definition', '03'), @@ -819,10 +819,10 @@ def create_page_highlight(self): 'Found Text': ('hit', '08'), 'Cursor': ('cursor', '09'), 'Editor Breakpoint': ('break', '10'), - 'Shell Normal Text': ('console', '11'), - 'Shell Error Text': ('error', '12'), - 'Shell Stdout Text': ('stdout', '13'), - 'Shell Stderr Text': ('stderr', '14'), + 'Shell Prompt': ('console', '11'), + 'Error Text': ('error', '12'), + 'Shell User Output': ('stdout', '13'), + 'Shell User Exception': ('stderr', '14'), 'Line Number': ('linenumber', '16'), } self.builtin_name = tracers.add( @@ -852,27 +852,26 @@ def create_page_highlight(self): takefocus=FALSE, highlightthickness=0, wrap=NONE) text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') - text_and_tags=( - ('\n', 'normal'), - ('#you can click here', 'comment'), ('\n', 'normal'), - ('#to choose items', 'comment'), ('\n', 'normal'), - ('code context section', 'context'), ('\n\n', 'normal'), + string_tags=( + ('# Click selects item.', 'comment'), ('\n', 'normal'), + ('code context section', 'context'), ('\n', 'normal'), + ('| cursor', 'cursor'), ('\n', 'normal'), ('def', 'keyword'), (' ', 'normal'), ('func', 'definition'), ('(param):\n ', 'normal'), - ('"""string"""', 'string'), ('\n var0 = ', 'normal'), + ('"Return None."', 'string'), ('\n var0 = ', 'normal'), ("'string'", 'string'), ('\n var1 = ', 'normal'), ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), ("'found'", 'hit'), ('\n var3 = ', 'normal'), ('list', 'builtin'), ('(', 'normal'), ('None', 'keyword'), (')\n', 'normal'), (' breakpoint("line")', 'break'), ('\n\n', 'normal'), - (' error ', 'error'), (' ', 'normal'), - ('cursor |', 'cursor'), ('\n ', 'normal'), - ('shell', 'console'), (' ', 'normal'), - ('stdout', 'stdout'), (' ', 'normal'), - ('stderr', 'stderr'), ('\n\n', 'normal')) - for texttag in text_and_tags: - text.insert(END, texttag[0], texttag[1]) + ('>>>', 'console'), (' 3.14**2\n', 'normal'), + ('9.8596', 'stdout'), ('\n', 'normal'), + ('>>>', 'console'), (' pri ', 'normal'), + ('n', 'error'), ('t(\n', 'normal'), + ('SyntaxError', 'stderr'), ('\n', 'normal')) + for string, tag in string_tags: + text.insert(END, string, tag) n_lines = len(text.get('1.0', END).splitlines()) for lineno in range(1, n_lines): text.insert(f'{lineno}.0', diff --git a/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst b/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst new file mode 100644 index 000000000000..b3beadc1e026 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst @@ -0,0 +1,2 @@ +Improve highlight config sample with example shell interaction and better +labels for shell elements. From webhook-mailer at python.org Sun Jul 28 12:39:09 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 28 Jul 2019 16:39:09 -0000 Subject: [Python-checkins] bpo-37692: Improve highlight config sample (GH-14983) Message-ID: https://github.com/python/cpython/commit/c94386d01372e69a67935bf284bfab31dac2adab commit: c94386d01372e69a67935bf284bfab31dac2adab branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-28T09:39:03-07:00 summary: bpo-37692: Improve highlight config sample (GH-14983) Use an example shell interaction in the sample and better labels for shell elements. (cherry picked from commit b222955355c8077a3ceca79195731663d7c3dd5f) Co-authored-by: Terry Jan Reedy files: A Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst M Lib/idlelib/configdialog.py diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 4df6ecee69f1..bba17d34938d 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -808,7 +808,7 @@ def create_page_highlight(self): (*)theme_message: Label """ self.theme_elements = { - 'Normal Text': ('normal', '00'), + 'Normal Code or Text': ('normal', '00'), 'Code Context': ('context', '01'), 'Python Keywords': ('keyword', '02'), 'Python Definitions': ('definition', '03'), @@ -819,10 +819,10 @@ def create_page_highlight(self): 'Found Text': ('hit', '08'), 'Cursor': ('cursor', '09'), 'Editor Breakpoint': ('break', '10'), - 'Shell Normal Text': ('console', '11'), - 'Shell Error Text': ('error', '12'), - 'Shell Stdout Text': ('stdout', '13'), - 'Shell Stderr Text': ('stderr', '14'), + 'Shell Prompt': ('console', '11'), + 'Error Text': ('error', '12'), + 'Shell User Output': ('stdout', '13'), + 'Shell User Exception': ('stderr', '14'), 'Line Number': ('linenumber', '16'), } self.builtin_name = tracers.add( @@ -852,27 +852,26 @@ def create_page_highlight(self): takefocus=FALSE, highlightthickness=0, wrap=NONE) text.bind('', lambda e: 'break') text.bind('', lambda e: 'break') - text_and_tags=( - ('\n', 'normal'), - ('#you can click here', 'comment'), ('\n', 'normal'), - ('#to choose items', 'comment'), ('\n', 'normal'), - ('code context section', 'context'), ('\n\n', 'normal'), + string_tags=( + ('# Click selects item.', 'comment'), ('\n', 'normal'), + ('code context section', 'context'), ('\n', 'normal'), + ('| cursor', 'cursor'), ('\n', 'normal'), ('def', 'keyword'), (' ', 'normal'), ('func', 'definition'), ('(param):\n ', 'normal'), - ('"""string"""', 'string'), ('\n var0 = ', 'normal'), + ('"Return None."', 'string'), ('\n var0 = ', 'normal'), ("'string'", 'string'), ('\n var1 = ', 'normal'), ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), ("'found'", 'hit'), ('\n var3 = ', 'normal'), ('list', 'builtin'), ('(', 'normal'), ('None', 'keyword'), (')\n', 'normal'), (' breakpoint("line")', 'break'), ('\n\n', 'normal'), - (' error ', 'error'), (' ', 'normal'), - ('cursor |', 'cursor'), ('\n ', 'normal'), - ('shell', 'console'), (' ', 'normal'), - ('stdout', 'stdout'), (' ', 'normal'), - ('stderr', 'stderr'), ('\n\n', 'normal')) - for texttag in text_and_tags: - text.insert(END, texttag[0], texttag[1]) + ('>>>', 'console'), (' 3.14**2\n', 'normal'), + ('9.8596', 'stdout'), ('\n', 'normal'), + ('>>>', 'console'), (' pri ', 'normal'), + ('n', 'error'), ('t(\n', 'normal'), + ('SyntaxError', 'stderr'), ('\n', 'normal')) + for string, tag in string_tags: + text.insert(END, string, tag) n_lines = len(text.get('1.0', END).splitlines()) for lineno in range(1, n_lines): text.insert(f'{lineno}.0', diff --git a/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst b/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst new file mode 100644 index 000000000000..b3beadc1e026 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst @@ -0,0 +1,2 @@ +Improve highlight config sample with example shell interaction and better +labels for shell elements. From webhook-mailer at python.org Sun Jul 28 14:59:28 2019 From: webhook-mailer at python.org (Jason R. Coombs) Date: Sun, 28 Jul 2019 18:59:28 -0000 Subject: [Python-checkins] bpo-37697: Sync with importlib_metadata 0.19 (#14993) Message-ID: https://github.com/python/cpython/commit/049460da9c7b5f51732e2966195c44713af9dc4c commit: 049460da9c7b5f51732e2966195c44713af9dc4c branch: master author: Jason R. Coombs committer: GitHub date: 2019-07-28T14:59:24-04:00 summary: bpo-37697: Sync with importlib_metadata 0.19 (#14993) * bpo-37697: Sync with importlib_metadata 0.19 * Run make regen-importlib * ?? Added by blurb_it. files: A Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst M Lib/importlib/_bootstrap_external.py M Lib/importlib/metadata/__init__.py M Lib/test/test_importlib/fixtures.py M Lib/test/test_importlib/test_main.py M Lib/test/test_importlib/test_zip.py M Python/importlib_external.h diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 5aac048060c2..badd7a74cb90 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1367,8 +1367,6 @@ def find_module(cls, fullname, path=None): return None return spec.loader - search_template = r'(?:{pattern}(-.*)?\.(dist|egg)-info|EGG-INFO)' - @classmethod def find_distributions(cls, name=None, path=None): """ @@ -1400,24 +1398,35 @@ def _search_paths(cls, pattern, paths): def _switch_path(path): from contextlib import suppress import zipfile - from pathlib import Path - with suppress(Exception): - return zipfile.Path(path) - return Path(path) + import pathlib + PYPY_OPEN_BUG = False + if not PYPY_OPEN_BUG or os.path.isfile(path): # pragma: no branch + with suppress(Exception): + return zipfile.Path(path) + return pathlib.Path(path) + + @classmethod + def _matches_info(cls, normalized, item): + import re + template = r'{pattern}(-.*)?\.(dist|egg)-info' + manifest = template.format(pattern=normalized) + return re.match(manifest, item.name, flags=re.IGNORECASE) @classmethod - def _predicate(cls, pattern, root, item): + def _matches_legacy(cls, normalized, item): import re - return re.match(pattern, str(item.name), flags=re.IGNORECASE) + template = r'{pattern}-.*\.egg[\\/]EGG-INFO' + manifest = template.format(pattern=normalized) + return re.search(manifest, str(item), flags=re.IGNORECASE) @classmethod def _search_path(cls, root, pattern): if not root.is_dir(): return () normalized = pattern.replace('-', '_') - matcher = cls.search_template.format(pattern=normalized) return (item for item in root.iterdir() - if cls._predicate(matcher, root, item)) + if cls._matches_info(normalized, item) + or cls._matches_legacy(normalized, item)) class FileFinder: diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py index 944dbb5fdf7e..e80f4fa6409b 100644 --- a/Lib/importlib/metadata/__init__.py +++ b/Lib/importlib/metadata/__init__.py @@ -88,7 +88,7 @@ def _from_config(cls, config): @classmethod def _from_text(cls, text): - config = ConfigParser() + config = ConfigParser(delimiters='=') # case sensitive: https://stackoverflow.com/q/1611799/812183 config.optionxform = str try: diff --git a/Lib/test/test_importlib/fixtures.py b/Lib/test/test_importlib/fixtures.py index 3b926ba26df7..0b4ce18d5a6c 100644 --- a/Lib/test/test_importlib/fixtures.py +++ b/Lib/test/test_importlib/fixtures.py @@ -83,6 +83,7 @@ class DistInfoPkg(OnSysPath, SiteDir): "entry_points.txt": """ [entries] main = mod:main + ns:sub = mod:main """ }, "mod.py": """ diff --git a/Lib/test/test_importlib/test_main.py b/Lib/test/test_importlib/test_main.py index 5e2cb264e28a..bc42b83ee291 100644 --- a/Lib/test/test_importlib/test_main.py +++ b/Lib/test/test_importlib/test_main.py @@ -32,7 +32,7 @@ def test_new_style_classes(self): class ImportTests(fixtures.DistInfoPkg, unittest.TestCase): def test_import_nonexistent_module(self): # Ensure that the MetadataPathFinder does not crash an import of a - # nonexistent module. + # non-existant module. with self.assertRaises(ImportError): importlib.import_module('does_not_exist') @@ -41,6 +41,11 @@ def test_resolve(self): ep = entries['main'] self.assertEqual(ep.load().__name__, "main") + def test_entrypoint_with_colon_in_name(self): + entries = dict(entry_points()['entries']) + ep = entries['ns:sub'] + self.assertEqual(ep.value, 'mod:main') + def test_resolve_without_attr(self): ep = EntryPoint( name='ep', @@ -159,8 +164,16 @@ def test_package_discovery(self): class DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): - def test(self): + def test_egg_info(self): # make an `EGG-INFO` directory that's unrelated self.site_dir.joinpath('EGG-INFO').mkdir() # used to crash with `IsADirectoryError` - self.assertIsNone(version('unknown-package')) + with self.assertRaises(PackageNotFoundError): + version('unknown-package') + + def test_egg(self): + egg = self.site_dir.joinpath('foo-3.6.egg') + egg.mkdir() + with self.add_sys_path(egg): + with self.assertRaises(PackageNotFoundError): + version('foo') diff --git a/Lib/test/test_importlib/test_zip.py b/Lib/test/test_importlib/test_zip.py index bcf7cf3618d7..9568c226af03 100644 --- a/Lib/test/test_importlib/test_zip.py +++ b/Lib/test/test_importlib/test_zip.py @@ -2,7 +2,9 @@ import unittest from contextlib import ExitStack -from importlib.metadata import distribution, entry_points, files, version +from importlib.metadata import ( + distribution, entry_points, files, PackageNotFoundError, version, +) from importlib.resources import path @@ -22,6 +24,10 @@ def setUp(self): def test_zip_version(self): self.assertEqual(version('example'), '21.12') + def test_zip_version_does_not_match(self): + with self.assertRaises(PackageNotFoundError): + version('definitely-not-installed') + def test_zip_entry_points(self): scripts = dict(entry_points()['console_scripts']) entry_point = scripts['example'] diff --git a/Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst b/Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst new file mode 100644 index 000000000000..6c78d7cd17c4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst @@ -0,0 +1 @@ +Syncronize ``importlib.metadata`` with `importlib_metadata 0.19 `_, improving handling of EGG-INFO files and fixing a crash when entry point names contained colons. \ No newline at end of file diff --git a/Python/importlib_external.h b/Python/importlib_external.h index 4a3cb24e37b9..987ba5f33866 100644 --- a/Python/importlib_external.h +++ b/Python/importlib_external.h @@ -2049,789 +2049,806 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,8,1,8,3,2,1,10,8,8,3,8,3,8,3, 8,3,8,3,114,43,1,0,0,99,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,4,0,0,0,64,0, - 0,0,115,172,0,0,0,101,0,90,1,100,0,90,2,100, + 0,0,115,180,0,0,0,101,0,90,1,100,0,90,2,100, 1,90,3,101,4,100,2,100,3,132,0,131,1,90,5,101, 4,100,4,100,5,132,0,131,1,90,6,101,4,100,6,100, 7,132,0,131,1,90,7,101,4,100,8,100,9,132,0,131, - 1,90,8,101,4,100,28,100,11,100,12,132,1,131,1,90, - 9,101,4,100,29,100,13,100,14,132,1,131,1,90,10,101, - 4,100,30,100,15,100,16,132,1,131,1,90,11,100,17,90, - 12,101,4,100,31,100,18,100,19,132,1,131,1,90,13,101, - 4,100,20,100,21,132,0,131,1,90,14,101,15,100,22,100, - 23,132,0,131,1,90,16,101,4,100,24,100,25,132,0,131, - 1,90,17,101,4,100,26,100,27,132,0,131,1,90,18,100, - 10,83,0,41,32,218,10,80,97,116,104,70,105,110,100,101, - 114,122,62,77,101,116,97,32,112,97,116,104,32,102,105,110, - 100,101,114,32,102,111,114,32,115,121,115,46,112,97,116,104, - 32,97,110,100,32,112,97,99,107,97,103,101,32,95,95,112, - 97,116,104,95,95,32,97,116,116,114,105,98,117,116,101,115, - 46,99,1,0,0,0,0,0,0,0,0,0,0,0,3,0, - 0,0,4,0,0,0,67,0,0,0,115,64,0,0,0,116, - 0,116,1,106,2,160,3,161,0,131,1,68,0,93,44,92, - 2,125,1,125,2,124,2,100,1,107,8,114,40,116,1,106, - 2,124,1,61,0,113,14,116,4,124,2,100,2,131,2,114, - 14,124,2,160,5,161,0,1,0,113,14,100,1,83,0,41, - 3,122,125,67,97,108,108,32,116,104,101,32,105,110,118,97, - 108,105,100,97,116,101,95,99,97,99,104,101,115,40,41,32, - 109,101,116,104,111,100,32,111,110,32,97,108,108,32,112,97, - 116,104,32,101,110,116,114,121,32,102,105,110,100,101,114,115, - 10,32,32,32,32,32,32,32,32,115,116,111,114,101,100,32, - 105,110,32,115,121,115,46,112,97,116,104,95,105,109,112,111, - 114,116,101,114,95,99,97,99,104,101,115,32,40,119,104,101, - 114,101,32,105,109,112,108,101,109,101,110,116,101,100,41,46, - 78,218,17,105,110,118,97,108,105,100,97,116,101,95,99,97, - 99,104,101,115,41,6,218,4,108,105,115,116,114,8,0,0, - 0,218,19,112,97,116,104,95,105,109,112,111,114,116,101,114, - 95,99,97,99,104,101,218,5,105,116,101,109,115,114,128,0, - 0,0,114,46,1,0,0,41,3,114,193,0,0,0,114,117, - 0,0,0,218,6,102,105,110,100,101,114,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,46,1,0,0,217, - 4,0,0,115,10,0,0,0,0,4,22,1,8,1,10,1, - 10,1,122,28,80,97,116,104,70,105,110,100,101,114,46,105, - 110,118,97,108,105,100,97,116,101,95,99,97,99,104,101,115, - 99,2,0,0,0,0,0,0,0,0,0,0,0,3,0,0, - 0,9,0,0,0,67,0,0,0,115,84,0,0,0,116,0, - 106,1,100,1,107,9,114,28,116,0,106,1,115,28,116,2, - 160,3,100,2,116,4,161,2,1,0,116,0,106,1,68,0, - 93,44,125,2,122,14,124,2,124,1,131,1,87,0,2,0, - 1,0,83,0,4,0,116,5,107,10,114,76,1,0,1,0, - 1,0,89,0,113,34,89,0,113,34,88,0,113,34,100,1, - 83,0,41,3,122,46,83,101,97,114,99,104,32,115,121,115, - 46,112,97,116,104,95,104,111,111,107,115,32,102,111,114,32, - 97,32,102,105,110,100,101,114,32,102,111,114,32,39,112,97, - 116,104,39,46,78,122,23,115,121,115,46,112,97,116,104,95, - 104,111,111,107,115,32,105,115,32,101,109,112,116,121,41,6, - 114,8,0,0,0,218,10,112,97,116,104,95,104,111,111,107, - 115,114,75,0,0,0,114,76,0,0,0,114,138,0,0,0, - 114,118,0,0,0,41,3,114,193,0,0,0,114,44,0,0, - 0,90,4,104,111,111,107,114,3,0,0,0,114,3,0,0, - 0,114,6,0,0,0,218,11,95,112,97,116,104,95,104,111, - 111,107,115,227,4,0,0,115,16,0,0,0,0,3,16,1, - 12,1,10,1,2,1,14,1,14,1,12,2,122,22,80,97, - 116,104,70,105,110,100,101,114,46,95,112,97,116,104,95,104, - 111,111,107,115,99,2,0,0,0,0,0,0,0,0,0,0, - 0,3,0,0,0,8,0,0,0,67,0,0,0,115,104,0, - 0,0,124,1,100,1,107,2,114,44,122,12,116,0,160,1, - 161,0,125,1,87,0,110,22,4,0,116,2,107,10,114,42, - 1,0,1,0,1,0,89,0,100,2,83,0,88,0,122,14, - 116,3,106,4,124,1,25,0,125,2,87,0,110,40,4,0, - 116,5,107,10,114,98,1,0,1,0,1,0,124,0,160,6, - 124,1,161,1,125,2,124,2,116,3,106,4,124,1,60,0, - 89,0,110,2,88,0,124,2,83,0,41,3,122,210,71,101, - 116,32,116,104,101,32,102,105,110,100,101,114,32,102,111,114, - 32,116,104,101,32,112,97,116,104,32,101,110,116,114,121,32, - 102,114,111,109,32,115,121,115,46,112,97,116,104,95,105,109, - 112,111,114,116,101,114,95,99,97,99,104,101,46,10,10,32, - 32,32,32,32,32,32,32,73,102,32,116,104,101,32,112,97, - 116,104,32,101,110,116,114,121,32,105,115,32,110,111,116,32, - 105,110,32,116,104,101,32,99,97,99,104,101,44,32,102,105, - 110,100,32,116,104,101,32,97,112,112,114,111,112,114,105,97, - 116,101,32,102,105,110,100,101,114,10,32,32,32,32,32,32, - 32,32,97,110,100,32,99,97,99,104,101,32,105,116,46,32, - 73,102,32,110,111,32,102,105,110,100,101,114,32,105,115,32, - 97,118,97,105,108,97,98,108,101,44,32,115,116,111,114,101, - 32,78,111,110,101,46,10,10,32,32,32,32,32,32,32,32, - 114,40,0,0,0,78,41,7,114,2,0,0,0,114,55,0, - 0,0,114,3,1,0,0,114,8,0,0,0,114,48,1,0, - 0,218,8,75,101,121,69,114,114,111,114,114,52,1,0,0, - 41,3,114,193,0,0,0,114,44,0,0,0,114,50,1,0, - 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 218,20,95,112,97,116,104,95,105,109,112,111,114,116,101,114, - 95,99,97,99,104,101,240,4,0,0,115,22,0,0,0,0, - 8,8,1,2,1,12,1,14,3,8,1,2,1,14,1,14, - 1,10,1,16,1,122,31,80,97,116,104,70,105,110,100,101, - 114,46,95,112,97,116,104,95,105,109,112,111,114,116,101,114, - 95,99,97,99,104,101,99,3,0,0,0,0,0,0,0,0, - 0,0,0,6,0,0,0,4,0,0,0,67,0,0,0,115, - 82,0,0,0,116,0,124,2,100,1,131,2,114,26,124,2, - 160,1,124,1,161,1,92,2,125,3,125,4,110,14,124,2, - 160,2,124,1,161,1,125,3,103,0,125,4,124,3,100,0, - 107,9,114,60,116,3,160,4,124,1,124,3,161,2,83,0, - 116,3,160,5,124,1,100,0,161,2,125,5,124,4,124,5, - 95,6,124,5,83,0,41,2,78,114,137,0,0,0,41,7, - 114,128,0,0,0,114,137,0,0,0,114,206,0,0,0,114, - 134,0,0,0,114,201,0,0,0,114,183,0,0,0,114,178, - 0,0,0,41,6,114,193,0,0,0,114,139,0,0,0,114, - 50,1,0,0,114,140,0,0,0,114,141,0,0,0,114,187, - 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,218,16,95,108,101,103,97,99,121,95,103,101,116,95, - 115,112,101,99,6,5,0,0,115,18,0,0,0,0,4,10, - 1,16,2,10,1,4,1,8,1,12,1,12,1,6,1,122, - 27,80,97,116,104,70,105,110,100,101,114,46,95,108,101,103, - 97,99,121,95,103,101,116,95,115,112,101,99,78,99,4,0, - 0,0,0,0,0,0,0,0,0,0,9,0,0,0,5,0, - 0,0,67,0,0,0,115,166,0,0,0,103,0,125,4,124, - 2,68,0,93,134,125,5,116,0,124,5,116,1,116,2,102, - 2,131,2,115,28,113,8,124,0,160,3,124,5,161,1,125, - 6,124,6,100,1,107,9,114,8,116,4,124,6,100,2,131, - 2,114,70,124,6,160,5,124,1,124,3,161,2,125,7,110, - 12,124,0,160,6,124,1,124,6,161,2,125,7,124,7,100, - 1,107,8,114,92,113,8,124,7,106,7,100,1,107,9,114, - 110,124,7,2,0,1,0,83,0,124,7,106,8,125,8,124, - 8,100,1,107,8,114,132,116,9,100,3,131,1,130,1,124, - 4,160,10,124,8,161,1,1,0,113,8,116,11,160,12,124, - 1,100,1,161,2,125,7,124,4,124,7,95,8,124,7,83, - 0,41,4,122,63,70,105,110,100,32,116,104,101,32,108,111, - 97,100,101,114,32,111,114,32,110,97,109,101,115,112,97,99, - 101,95,112,97,116,104,32,102,111,114,32,116,104,105,115,32, - 109,111,100,117,108,101,47,112,97,99,107,97,103,101,32,110, - 97,109,101,46,78,114,203,0,0,0,122,19,115,112,101,99, - 32,109,105,115,115,105,110,103,32,108,111,97,100,101,114,41, - 13,114,161,0,0,0,114,85,0,0,0,218,5,98,121,116, - 101,115,114,54,1,0,0,114,128,0,0,0,114,203,0,0, - 0,114,55,1,0,0,114,140,0,0,0,114,178,0,0,0, - 114,118,0,0,0,114,167,0,0,0,114,134,0,0,0,114, - 183,0,0,0,41,9,114,193,0,0,0,114,139,0,0,0, - 114,44,0,0,0,114,202,0,0,0,218,14,110,97,109,101, - 115,112,97,99,101,95,112,97,116,104,90,5,101,110,116,114, - 121,114,50,1,0,0,114,187,0,0,0,114,141,0,0,0, - 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 9,95,103,101,116,95,115,112,101,99,21,5,0,0,115,40, - 0,0,0,0,5,4,1,8,1,14,1,2,1,10,1,8, - 1,10,1,14,2,12,1,8,1,2,1,10,1,8,1,6, - 1,8,1,8,5,12,2,12,1,6,1,122,20,80,97,116, - 104,70,105,110,100,101,114,46,95,103,101,116,95,115,112,101, - 99,99,4,0,0,0,0,0,0,0,0,0,0,0,6,0, - 0,0,5,0,0,0,67,0,0,0,115,100,0,0,0,124, - 2,100,1,107,8,114,14,116,0,106,1,125,2,124,0,160, - 2,124,1,124,2,124,3,161,3,125,4,124,4,100,1,107, - 8,114,40,100,1,83,0,124,4,106,3,100,1,107,8,114, - 92,124,4,106,4,125,5,124,5,114,86,100,1,124,4,95, - 5,116,6,124,1,124,5,124,0,106,2,131,3,124,4,95, - 4,124,4,83,0,100,1,83,0,110,4,124,4,83,0,100, - 1,83,0,41,2,122,141,84,114,121,32,116,111,32,102,105, - 110,100,32,97,32,115,112,101,99,32,102,111,114,32,39,102, - 117,108,108,110,97,109,101,39,32,111,110,32,115,121,115,46, - 112,97,116,104,32,111,114,32,39,112,97,116,104,39,46,10, - 10,32,32,32,32,32,32,32,32,84,104,101,32,115,101,97, - 114,99,104,32,105,115,32,98,97,115,101,100,32,111,110,32, - 115,121,115,46,112,97,116,104,95,104,111,111,107,115,32,97, - 110,100,32,115,121,115,46,112,97,116,104,95,105,109,112,111, - 114,116,101,114,95,99,97,99,104,101,46,10,32,32,32,32, - 32,32,32,32,78,41,7,114,8,0,0,0,114,44,0,0, - 0,114,58,1,0,0,114,140,0,0,0,114,178,0,0,0, - 114,181,0,0,0,114,22,1,0,0,41,6,114,193,0,0, - 0,114,139,0,0,0,114,44,0,0,0,114,202,0,0,0, - 114,187,0,0,0,114,57,1,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,203,0,0,0,53,5, - 0,0,115,26,0,0,0,0,6,8,1,6,1,14,1,8, - 1,4,1,10,1,6,1,4,3,6,1,16,1,4,2,6, - 2,122,20,80,97,116,104,70,105,110,100,101,114,46,102,105, - 110,100,95,115,112,101,99,99,3,0,0,0,0,0,0,0, - 0,0,0,0,4,0,0,0,4,0,0,0,67,0,0,0, - 115,30,0,0,0,124,0,160,0,124,1,124,2,161,2,125, - 3,124,3,100,1,107,8,114,24,100,1,83,0,124,3,106, - 1,83,0,41,2,122,170,102,105,110,100,32,116,104,101,32, - 109,111,100,117,108,101,32,111,110,32,115,121,115,46,112,97, - 116,104,32,111,114,32,39,112,97,116,104,39,32,98,97,115, - 101,100,32,111,110,32,115,121,115,46,112,97,116,104,95,104, - 111,111,107,115,32,97,110,100,10,32,32,32,32,32,32,32, - 32,115,121,115,46,112,97,116,104,95,105,109,112,111,114,116, - 101,114,95,99,97,99,104,101,46,10,10,32,32,32,32,32, - 32,32,32,84,104,105,115,32,109,101,116,104,111,100,32,105, - 115,32,100,101,112,114,101,99,97,116,101,100,46,32,32,85, - 115,101,32,102,105,110,100,95,115,112,101,99,40,41,32,105, - 110,115,116,101,97,100,46,10,10,32,32,32,32,32,32,32, - 32,78,114,204,0,0,0,114,205,0,0,0,114,3,0,0, - 0,114,3,0,0,0,114,6,0,0,0,114,206,0,0,0, - 77,5,0,0,115,8,0,0,0,0,8,12,1,8,1,4, - 1,122,22,80,97,116,104,70,105,110,100,101,114,46,102,105, - 110,100,95,109,111,100,117,108,101,122,45,40,63,58,123,112, - 97,116,116,101,114,110,125,40,45,46,42,41,63,92,46,40, - 100,105,115,116,124,101,103,103,41,45,105,110,102,111,124,69, - 71,71,45,73,78,70,79,41,99,3,0,0,0,0,0,0, - 0,0,0,0,0,7,0,0,0,4,0,0,0,67,0,0, - 0,115,78,0,0,0,100,1,100,2,108,0,125,3,100,1, - 100,3,108,1,109,2,125,4,1,0,124,2,100,2,107,8, - 114,34,116,3,106,4,125,2,124,1,100,2,107,8,114,46, - 100,4,110,8,124,3,160,5,124,1,161,1,125,5,124,0, - 160,6,124,5,124,2,161,2,125,6,116,7,124,4,124,6, - 131,2,83,0,41,5,97,37,1,0,0,10,32,32,32,32, - 32,32,32,32,70,105,110,100,32,100,105,115,116,114,105,98, - 117,116,105,111,110,115,46,10,10,32,32,32,32,32,32,32, - 32,82,101,116,117,114,110,32,97,110,32,105,116,101,114,97, - 98,108,101,32,111,102,32,97,108,108,32,68,105,115,116,114, - 105,98,117,116,105,111,110,32,105,110,115,116,97,110,99,101, - 115,32,99,97,112,97,98,108,101,32,111,102,10,32,32,32, - 32,32,32,32,32,108,111,97,100,105,110,103,32,116,104,101, - 32,109,101,116,97,100,97,116,97,32,102,111,114,32,112,97, - 99,107,97,103,101,115,32,109,97,116,99,104,105,110,103,32, - 116,104,101,32,96,96,110,97,109,101,96,96,10,32,32,32, - 32,32,32,32,32,40,111,114,32,97,108,108,32,110,97,109, - 101,115,32,105,102,32,110,111,116,32,115,117,112,112,108,105, - 101,100,41,32,97,108,111,110,103,32,116,104,101,32,112,97, - 116,104,115,32,105,110,32,116,104,101,32,108,105,115,116,10, - 32,32,32,32,32,32,32,32,111,102,32,100,105,114,101,99, - 116,111,114,105,101,115,32,96,96,112,97,116,104,96,96,32, - 40,100,101,102,97,117,108,116,115,32,116,111,32,115,121,115, - 46,112,97,116,104,41,46,10,32,32,32,32,32,32,32,32, - 114,73,0,0,0,78,41,1,218,16,80,97,116,104,68,105, - 115,116,114,105,98,117,116,105,111,110,122,2,46,42,41,8, - 218,2,114,101,90,18,105,109,112,111,114,116,108,105,98,46, - 109,101,116,97,100,97,116,97,114,59,1,0,0,114,8,0, - 0,0,114,44,0,0,0,90,6,101,115,99,97,112,101,218, - 13,95,115,101,97,114,99,104,95,112,97,116,104,115,218,3, - 109,97,112,41,7,114,193,0,0,0,114,117,0,0,0,114, - 44,0,0,0,114,60,1,0,0,114,59,1,0,0,218,7, - 112,97,116,116,101,114,110,90,5,102,111,117,110,100,114,3, - 0,0,0,114,3,0,0,0,114,6,0,0,0,218,18,102, - 105,110,100,95,100,105,115,116,114,105,98,117,116,105,111,110, - 115,92,5,0,0,115,14,0,0,0,0,10,8,1,12,1, - 8,1,6,1,22,1,12,1,122,29,80,97,116,104,70,105, - 110,100,101,114,46,102,105,110,100,95,100,105,115,116,114,105, - 98,117,116,105,111,110,115,99,3,0,0,0,0,0,0,0, - 0,0,0,0,4,0,0,0,6,0,0,0,3,0,0,0, - 115,44,0,0,0,100,1,100,2,108,0,125,3,124,3,106, - 1,160,2,135,0,135,1,102,2,100,3,100,4,132,8,116, - 3,136,0,106,4,124,2,131,2,68,0,131,1,161,1,83, - 0,41,5,122,49,70,105,110,100,32,109,101,116,97,100,97, - 116,97,32,100,105,114,101,99,116,111,114,105,101,115,32,105, - 110,32,112,97,116,104,115,32,104,101,117,114,105,115,116,105, - 99,97,108,108,121,46,114,73,0,0,0,78,99,1,0,0, - 0,0,0,0,0,0,0,0,0,2,0,0,0,5,0,0, - 0,51,0,0,0,115,26,0,0,0,124,0,93,18,125,1, - 136,0,160,0,124,1,136,1,161,2,86,0,1,0,113,2, - 100,0,83,0,114,110,0,0,0,41,1,218,12,95,115,101, - 97,114,99,104,95,112,97,116,104,41,2,114,32,0,0,0, - 114,44,0,0,0,169,2,114,193,0,0,0,114,63,1,0, - 0,114,3,0,0,0,114,6,0,0,0,114,19,1,0,0, - 114,5,0,0,115,4,0,0,0,4,2,2,255,122,43,80, - 97,116,104,70,105,110,100,101,114,46,95,115,101,97,114,99, - 104,95,112,97,116,104,115,46,60,108,111,99,97,108,115,62, - 46,60,103,101,110,101,120,112,114,62,41,5,218,9,105,116, - 101,114,116,111,111,108,115,90,5,99,104,97,105,110,90,13, - 102,114,111,109,95,105,116,101,114,97,98,108,101,114,62,1, - 0,0,218,12,95,115,119,105,116,99,104,95,112,97,116,104, - 41,4,114,193,0,0,0,114,63,1,0,0,90,5,112,97, - 116,104,115,114,67,1,0,0,114,3,0,0,0,114,66,1, - 0,0,114,6,0,0,0,114,61,1,0,0,110,5,0,0, - 115,8,0,0,0,0,3,8,1,18,2,10,254,122,24,80, + 1,90,8,101,4,100,29,100,11,100,12,132,1,131,1,90, + 9,101,4,100,30,100,13,100,14,132,1,131,1,90,10,101, + 4,100,31,100,15,100,16,132,1,131,1,90,11,101,4,100, + 32,100,17,100,18,132,1,131,1,90,12,101,4,100,19,100, + 20,132,0,131,1,90,13,101,14,100,21,100,22,132,0,131, + 1,90,15,101,4,100,23,100,24,132,0,131,1,90,16,101, + 4,100,25,100,26,132,0,131,1,90,17,101,4,100,27,100, + 28,132,0,131,1,90,18,100,10,83,0,41,33,218,10,80, + 97,116,104,70,105,110,100,101,114,122,62,77,101,116,97,32, + 112,97,116,104,32,102,105,110,100,101,114,32,102,111,114,32, + 115,121,115,46,112,97,116,104,32,97,110,100,32,112,97,99, + 107,97,103,101,32,95,95,112,97,116,104,95,95,32,97,116, + 116,114,105,98,117,116,101,115,46,99,1,0,0,0,0,0, + 0,0,0,0,0,0,3,0,0,0,4,0,0,0,67,0, + 0,0,115,64,0,0,0,116,0,116,1,106,2,160,3,161, + 0,131,1,68,0,93,44,92,2,125,1,125,2,124,2,100, + 1,107,8,114,40,116,1,106,2,124,1,61,0,113,14,116, + 4,124,2,100,2,131,2,114,14,124,2,160,5,161,0,1, + 0,113,14,100,1,83,0,41,3,122,125,67,97,108,108,32, + 116,104,101,32,105,110,118,97,108,105,100,97,116,101,95,99, + 97,99,104,101,115,40,41,32,109,101,116,104,111,100,32,111, + 110,32,97,108,108,32,112,97,116,104,32,101,110,116,114,121, + 32,102,105,110,100,101,114,115,10,32,32,32,32,32,32,32, + 32,115,116,111,114,101,100,32,105,110,32,115,121,115,46,112, + 97,116,104,95,105,109,112,111,114,116,101,114,95,99,97,99, + 104,101,115,32,40,119,104,101,114,101,32,105,109,112,108,101, + 109,101,110,116,101,100,41,46,78,218,17,105,110,118,97,108, + 105,100,97,116,101,95,99,97,99,104,101,115,41,6,218,4, + 108,105,115,116,114,8,0,0,0,218,19,112,97,116,104,95, + 105,109,112,111,114,116,101,114,95,99,97,99,104,101,218,5, + 105,116,101,109,115,114,128,0,0,0,114,46,1,0,0,41, + 3,114,193,0,0,0,114,117,0,0,0,218,6,102,105,110, + 100,101,114,114,3,0,0,0,114,3,0,0,0,114,6,0, + 0,0,114,46,1,0,0,217,4,0,0,115,10,0,0,0, + 0,4,22,1,8,1,10,1,10,1,122,28,80,97,116,104, + 70,105,110,100,101,114,46,105,110,118,97,108,105,100,97,116, + 101,95,99,97,99,104,101,115,99,2,0,0,0,0,0,0, + 0,0,0,0,0,3,0,0,0,9,0,0,0,67,0,0, + 0,115,84,0,0,0,116,0,106,1,100,1,107,9,114,28, + 116,0,106,1,115,28,116,2,160,3,100,2,116,4,161,2, + 1,0,116,0,106,1,68,0,93,44,125,2,122,14,124,2, + 124,1,131,1,87,0,2,0,1,0,83,0,4,0,116,5, + 107,10,114,76,1,0,1,0,1,0,89,0,113,34,89,0, + 113,34,88,0,113,34,100,1,83,0,41,3,122,46,83,101, + 97,114,99,104,32,115,121,115,46,112,97,116,104,95,104,111, + 111,107,115,32,102,111,114,32,97,32,102,105,110,100,101,114, + 32,102,111,114,32,39,112,97,116,104,39,46,78,122,23,115, + 121,115,46,112,97,116,104,95,104,111,111,107,115,32,105,115, + 32,101,109,112,116,121,41,6,114,8,0,0,0,218,10,112, + 97,116,104,95,104,111,111,107,115,114,75,0,0,0,114,76, + 0,0,0,114,138,0,0,0,114,118,0,0,0,41,3,114, + 193,0,0,0,114,44,0,0,0,90,4,104,111,111,107,114, + 3,0,0,0,114,3,0,0,0,114,6,0,0,0,218,11, + 95,112,97,116,104,95,104,111,111,107,115,227,4,0,0,115, + 16,0,0,0,0,3,16,1,12,1,10,1,2,1,14,1, + 14,1,12,2,122,22,80,97,116,104,70,105,110,100,101,114, + 46,95,112,97,116,104,95,104,111,111,107,115,99,2,0,0, + 0,0,0,0,0,0,0,0,0,3,0,0,0,8,0,0, + 0,67,0,0,0,115,104,0,0,0,124,1,100,1,107,2, + 114,44,122,12,116,0,160,1,161,0,125,1,87,0,110,22, + 4,0,116,2,107,10,114,42,1,0,1,0,1,0,89,0, + 100,2,83,0,88,0,122,14,116,3,106,4,124,1,25,0, + 125,2,87,0,110,40,4,0,116,5,107,10,114,98,1,0, + 1,0,1,0,124,0,160,6,124,1,161,1,125,2,124,2, + 116,3,106,4,124,1,60,0,89,0,110,2,88,0,124,2, + 83,0,41,3,122,210,71,101,116,32,116,104,101,32,102,105, + 110,100,101,114,32,102,111,114,32,116,104,101,32,112,97,116, + 104,32,101,110,116,114,121,32,102,114,111,109,32,115,121,115, + 46,112,97,116,104,95,105,109,112,111,114,116,101,114,95,99, + 97,99,104,101,46,10,10,32,32,32,32,32,32,32,32,73, + 102,32,116,104,101,32,112,97,116,104,32,101,110,116,114,121, + 32,105,115,32,110,111,116,32,105,110,32,116,104,101,32,99, + 97,99,104,101,44,32,102,105,110,100,32,116,104,101,32,97, + 112,112,114,111,112,114,105,97,116,101,32,102,105,110,100,101, + 114,10,32,32,32,32,32,32,32,32,97,110,100,32,99,97, + 99,104,101,32,105,116,46,32,73,102,32,110,111,32,102,105, + 110,100,101,114,32,105,115,32,97,118,97,105,108,97,98,108, + 101,44,32,115,116,111,114,101,32,78,111,110,101,46,10,10, + 32,32,32,32,32,32,32,32,114,40,0,0,0,78,41,7, + 114,2,0,0,0,114,55,0,0,0,114,3,1,0,0,114, + 8,0,0,0,114,48,1,0,0,218,8,75,101,121,69,114, + 114,111,114,114,52,1,0,0,41,3,114,193,0,0,0,114, + 44,0,0,0,114,50,1,0,0,114,3,0,0,0,114,3, + 0,0,0,114,6,0,0,0,218,20,95,112,97,116,104,95, + 105,109,112,111,114,116,101,114,95,99,97,99,104,101,240,4, + 0,0,115,22,0,0,0,0,8,8,1,2,1,12,1,14, + 3,8,1,2,1,14,1,14,1,10,1,16,1,122,31,80, + 97,116,104,70,105,110,100,101,114,46,95,112,97,116,104,95, + 105,109,112,111,114,116,101,114,95,99,97,99,104,101,99,3, + 0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,4, + 0,0,0,67,0,0,0,115,82,0,0,0,116,0,124,2, + 100,1,131,2,114,26,124,2,160,1,124,1,161,1,92,2, + 125,3,125,4,110,14,124,2,160,2,124,1,161,1,125,3, + 103,0,125,4,124,3,100,0,107,9,114,60,116,3,160,4, + 124,1,124,3,161,2,83,0,116,3,160,5,124,1,100,0, + 161,2,125,5,124,4,124,5,95,6,124,5,83,0,41,2, + 78,114,137,0,0,0,41,7,114,128,0,0,0,114,137,0, + 0,0,114,206,0,0,0,114,134,0,0,0,114,201,0,0, + 0,114,183,0,0,0,114,178,0,0,0,41,6,114,193,0, + 0,0,114,139,0,0,0,114,50,1,0,0,114,140,0,0, + 0,114,141,0,0,0,114,187,0,0,0,114,3,0,0,0, + 114,3,0,0,0,114,6,0,0,0,218,16,95,108,101,103, + 97,99,121,95,103,101,116,95,115,112,101,99,6,5,0,0, + 115,18,0,0,0,0,4,10,1,16,2,10,1,4,1,8, + 1,12,1,12,1,6,1,122,27,80,97,116,104,70,105,110, + 100,101,114,46,95,108,101,103,97,99,121,95,103,101,116,95, + 115,112,101,99,78,99,4,0,0,0,0,0,0,0,0,0, + 0,0,9,0,0,0,5,0,0,0,67,0,0,0,115,166, + 0,0,0,103,0,125,4,124,2,68,0,93,134,125,5,116, + 0,124,5,116,1,116,2,102,2,131,2,115,28,113,8,124, + 0,160,3,124,5,161,1,125,6,124,6,100,1,107,9,114, + 8,116,4,124,6,100,2,131,2,114,70,124,6,160,5,124, + 1,124,3,161,2,125,7,110,12,124,0,160,6,124,1,124, + 6,161,2,125,7,124,7,100,1,107,8,114,92,113,8,124, + 7,106,7,100,1,107,9,114,110,124,7,2,0,1,0,83, + 0,124,7,106,8,125,8,124,8,100,1,107,8,114,132,116, + 9,100,3,131,1,130,1,124,4,160,10,124,8,161,1,1, + 0,113,8,116,11,160,12,124,1,100,1,161,2,125,7,124, + 4,124,7,95,8,124,7,83,0,41,4,122,63,70,105,110, + 100,32,116,104,101,32,108,111,97,100,101,114,32,111,114,32, + 110,97,109,101,115,112,97,99,101,95,112,97,116,104,32,102, + 111,114,32,116,104,105,115,32,109,111,100,117,108,101,47,112, + 97,99,107,97,103,101,32,110,97,109,101,46,78,114,203,0, + 0,0,122,19,115,112,101,99,32,109,105,115,115,105,110,103, + 32,108,111,97,100,101,114,41,13,114,161,0,0,0,114,85, + 0,0,0,218,5,98,121,116,101,115,114,54,1,0,0,114, + 128,0,0,0,114,203,0,0,0,114,55,1,0,0,114,140, + 0,0,0,114,178,0,0,0,114,118,0,0,0,114,167,0, + 0,0,114,134,0,0,0,114,183,0,0,0,41,9,114,193, + 0,0,0,114,139,0,0,0,114,44,0,0,0,114,202,0, + 0,0,218,14,110,97,109,101,115,112,97,99,101,95,112,97, + 116,104,90,5,101,110,116,114,121,114,50,1,0,0,114,187, + 0,0,0,114,141,0,0,0,114,3,0,0,0,114,3,0, + 0,0,114,6,0,0,0,218,9,95,103,101,116,95,115,112, + 101,99,21,5,0,0,115,40,0,0,0,0,5,4,1,8, + 1,14,1,2,1,10,1,8,1,10,1,14,2,12,1,8, + 1,2,1,10,1,8,1,6,1,8,1,8,5,12,2,12, + 1,6,1,122,20,80,97,116,104,70,105,110,100,101,114,46, + 95,103,101,116,95,115,112,101,99,99,4,0,0,0,0,0, + 0,0,0,0,0,0,6,0,0,0,5,0,0,0,67,0, + 0,0,115,100,0,0,0,124,2,100,1,107,8,114,14,116, + 0,106,1,125,2,124,0,160,2,124,1,124,2,124,3,161, + 3,125,4,124,4,100,1,107,8,114,40,100,1,83,0,124, + 4,106,3,100,1,107,8,114,92,124,4,106,4,125,5,124, + 5,114,86,100,1,124,4,95,5,116,6,124,1,124,5,124, + 0,106,2,131,3,124,4,95,4,124,4,83,0,100,1,83, + 0,110,4,124,4,83,0,100,1,83,0,41,2,122,141,84, + 114,121,32,116,111,32,102,105,110,100,32,97,32,115,112,101, + 99,32,102,111,114,32,39,102,117,108,108,110,97,109,101,39, + 32,111,110,32,115,121,115,46,112,97,116,104,32,111,114,32, + 39,112,97,116,104,39,46,10,10,32,32,32,32,32,32,32, + 32,84,104,101,32,115,101,97,114,99,104,32,105,115,32,98, + 97,115,101,100,32,111,110,32,115,121,115,46,112,97,116,104, + 95,104,111,111,107,115,32,97,110,100,32,115,121,115,46,112, + 97,116,104,95,105,109,112,111,114,116,101,114,95,99,97,99, + 104,101,46,10,32,32,32,32,32,32,32,32,78,41,7,114, + 8,0,0,0,114,44,0,0,0,114,58,1,0,0,114,140, + 0,0,0,114,178,0,0,0,114,181,0,0,0,114,22,1, + 0,0,41,6,114,193,0,0,0,114,139,0,0,0,114,44, + 0,0,0,114,202,0,0,0,114,187,0,0,0,114,57,1, + 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, + 0,114,203,0,0,0,53,5,0,0,115,26,0,0,0,0, + 6,8,1,6,1,14,1,8,1,4,1,10,1,6,1,4, + 3,6,1,16,1,4,2,6,2,122,20,80,97,116,104,70, + 105,110,100,101,114,46,102,105,110,100,95,115,112,101,99,99, + 3,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0, + 4,0,0,0,67,0,0,0,115,30,0,0,0,124,0,160, + 0,124,1,124,2,161,2,125,3,124,3,100,1,107,8,114, + 24,100,1,83,0,124,3,106,1,83,0,41,2,122,170,102, + 105,110,100,32,116,104,101,32,109,111,100,117,108,101,32,111, + 110,32,115,121,115,46,112,97,116,104,32,111,114,32,39,112, + 97,116,104,39,32,98,97,115,101,100,32,111,110,32,115,121, + 115,46,112,97,116,104,95,104,111,111,107,115,32,97,110,100, + 10,32,32,32,32,32,32,32,32,115,121,115,46,112,97,116, + 104,95,105,109,112,111,114,116,101,114,95,99,97,99,104,101, + 46,10,10,32,32,32,32,32,32,32,32,84,104,105,115,32, + 109,101,116,104,111,100,32,105,115,32,100,101,112,114,101,99, + 97,116,101,100,46,32,32,85,115,101,32,102,105,110,100,95, + 115,112,101,99,40,41,32,105,110,115,116,101,97,100,46,10, + 10,32,32,32,32,32,32,32,32,78,114,204,0,0,0,114, + 205,0,0,0,114,3,0,0,0,114,3,0,0,0,114,6, + 0,0,0,114,206,0,0,0,77,5,0,0,115,8,0,0, + 0,0,8,12,1,8,1,4,1,122,22,80,97,116,104,70, + 105,110,100,101,114,46,102,105,110,100,95,109,111,100,117,108, + 101,99,3,0,0,0,0,0,0,0,0,0,0,0,7,0, + 0,0,4,0,0,0,67,0,0,0,115,78,0,0,0,100, + 1,100,2,108,0,125,3,100,1,100,3,108,1,109,2,125, + 4,1,0,124,2,100,2,107,8,114,34,116,3,106,4,125, + 2,124,1,100,2,107,8,114,46,100,4,110,8,124,3,160, + 5,124,1,161,1,125,5,124,0,160,6,124,5,124,2,161, + 2,125,6,116,7,124,4,124,6,131,2,83,0,41,5,97, + 37,1,0,0,10,32,32,32,32,32,32,32,32,70,105,110, + 100,32,100,105,115,116,114,105,98,117,116,105,111,110,115,46, + 10,10,32,32,32,32,32,32,32,32,82,101,116,117,114,110, + 32,97,110,32,105,116,101,114,97,98,108,101,32,111,102,32, + 97,108,108,32,68,105,115,116,114,105,98,117,116,105,111,110, + 32,105,110,115,116,97,110,99,101,115,32,99,97,112,97,98, + 108,101,32,111,102,10,32,32,32,32,32,32,32,32,108,111, + 97,100,105,110,103,32,116,104,101,32,109,101,116,97,100,97, + 116,97,32,102,111,114,32,112,97,99,107,97,103,101,115,32, + 109,97,116,99,104,105,110,103,32,116,104,101,32,96,96,110, + 97,109,101,96,96,10,32,32,32,32,32,32,32,32,40,111, + 114,32,97,108,108,32,110,97,109,101,115,32,105,102,32,110, + 111,116,32,115,117,112,112,108,105,101,100,41,32,97,108,111, + 110,103,32,116,104,101,32,112,97,116,104,115,32,105,110,32, + 116,104,101,32,108,105,115,116,10,32,32,32,32,32,32,32, + 32,111,102,32,100,105,114,101,99,116,111,114,105,101,115,32, + 96,96,112,97,116,104,96,96,32,40,100,101,102,97,117,108, + 116,115,32,116,111,32,115,121,115,46,112,97,116,104,41,46, + 10,32,32,32,32,32,32,32,32,114,73,0,0,0,78,41, + 1,218,16,80,97,116,104,68,105,115,116,114,105,98,117,116, + 105,111,110,122,2,46,42,41,8,218,2,114,101,90,18,105, + 109,112,111,114,116,108,105,98,46,109,101,116,97,100,97,116, + 97,114,59,1,0,0,114,8,0,0,0,114,44,0,0,0, + 90,6,101,115,99,97,112,101,218,13,95,115,101,97,114,99, + 104,95,112,97,116,104,115,218,3,109,97,112,41,7,114,193, + 0,0,0,114,117,0,0,0,114,44,0,0,0,114,60,1, + 0,0,114,59,1,0,0,218,7,112,97,116,116,101,114,110, + 90,5,102,111,117,110,100,114,3,0,0,0,114,3,0,0, + 0,114,6,0,0,0,218,18,102,105,110,100,95,100,105,115, + 116,114,105,98,117,116,105,111,110,115,90,5,0,0,115,14, + 0,0,0,0,10,8,1,12,1,8,1,6,1,22,1,12, + 1,122,29,80,97,116,104,70,105,110,100,101,114,46,102,105, + 110,100,95,100,105,115,116,114,105,98,117,116,105,111,110,115, + 99,3,0,0,0,0,0,0,0,0,0,0,0,4,0,0, + 0,6,0,0,0,3,0,0,0,115,44,0,0,0,100,1, + 100,2,108,0,125,3,124,3,106,1,160,2,135,0,135,1, + 102,2,100,3,100,4,132,8,116,3,136,0,106,4,124,2, + 131,2,68,0,131,1,161,1,83,0,41,5,122,49,70,105, + 110,100,32,109,101,116,97,100,97,116,97,32,100,105,114,101, + 99,116,111,114,105,101,115,32,105,110,32,112,97,116,104,115, + 32,104,101,117,114,105,115,116,105,99,97,108,108,121,46,114, + 73,0,0,0,78,99,1,0,0,0,0,0,0,0,0,0, + 0,0,2,0,0,0,5,0,0,0,51,0,0,0,115,26, + 0,0,0,124,0,93,18,125,1,136,0,160,0,124,1,136, + 1,161,2,86,0,1,0,113,2,100,0,83,0,114,110,0, + 0,0,41,1,218,12,95,115,101,97,114,99,104,95,112,97, + 116,104,41,2,114,32,0,0,0,114,44,0,0,0,169,2, + 114,193,0,0,0,114,63,1,0,0,114,3,0,0,0,114, + 6,0,0,0,114,19,1,0,0,112,5,0,0,115,4,0, + 0,0,4,2,2,255,122,43,80,97,116,104,70,105,110,100, + 101,114,46,95,115,101,97,114,99,104,95,112,97,116,104,115, + 46,60,108,111,99,97,108,115,62,46,60,103,101,110,101,120, + 112,114,62,41,5,218,9,105,116,101,114,116,111,111,108,115, + 90,5,99,104,97,105,110,90,13,102,114,111,109,95,105,116, + 101,114,97,98,108,101,114,62,1,0,0,218,12,95,115,119, + 105,116,99,104,95,112,97,116,104,41,4,114,193,0,0,0, + 114,63,1,0,0,90,5,112,97,116,104,115,114,67,1,0, + 0,114,3,0,0,0,114,66,1,0,0,114,6,0,0,0, + 114,61,1,0,0,108,5,0,0,115,8,0,0,0,0,3, + 8,1,18,2,10,254,122,24,80,97,116,104,70,105,110,100, + 101,114,46,95,115,101,97,114,99,104,95,112,97,116,104,115, + 99,1,0,0,0,0,0,0,0,0,0,0,0,5,0,0, + 0,10,0,0,0,67,0,0,0,115,96,0,0,0,100,1, + 100,2,108,0,109,1,125,1,1,0,100,1,100,0,108,2, + 125,2,100,1,100,0,108,3,125,3,100,3,125,4,124,4, + 114,48,116,4,106,5,160,6,124,0,161,1,114,86,124,1, + 116,7,131,1,143,24,1,0,124,2,160,8,124,0,161,1, + 87,0,2,0,53,0,81,0,82,0,163,0,83,0,81,0, + 82,0,88,0,124,3,160,8,124,0,161,1,83,0,41,4, + 78,114,73,0,0,0,41,1,218,8,115,117,112,112,114,101, + 115,115,70,41,9,90,10,99,111,110,116,101,120,116,108,105, + 98,114,69,1,0,0,218,7,122,105,112,102,105,108,101,218, + 7,112,97,116,104,108,105,98,90,2,111,115,114,44,0,0, + 0,90,6,105,115,102,105,108,101,218,9,69,120,99,101,112, + 116,105,111,110,90,4,80,97,116,104,41,5,114,44,0,0, + 0,114,69,1,0,0,114,70,1,0,0,114,71,1,0,0, + 90,13,80,89,80,89,95,79,80,69,78,95,66,85,71,114, + 3,0,0,0,114,3,0,0,0,114,6,0,0,0,114,68, + 1,0,0,117,5,0,0,115,16,0,0,0,0,2,12,1, + 8,1,8,1,4,1,16,1,10,1,28,1,122,23,80,97, + 116,104,70,105,110,100,101,114,46,95,115,119,105,116,99,104, + 95,112,97,116,104,99,3,0,0,0,0,0,0,0,0,0, + 0,0,6,0,0,0,5,0,0,0,67,0,0,0,115,44, + 0,0,0,100,1,100,0,108,0,125,3,100,2,125,4,124, + 4,106,1,124,1,100,3,141,1,125,5,124,3,106,2,124, + 5,124,2,106,3,124,3,106,4,100,4,141,3,83,0,41, + 5,78,114,73,0,0,0,122,32,123,112,97,116,116,101,114, + 110,125,40,45,46,42,41,63,92,46,40,100,105,115,116,124, + 101,103,103,41,45,105,110,102,111,169,1,114,63,1,0,0, + 169,1,114,83,0,0,0,41,5,114,60,1,0,0,114,62, + 0,0,0,90,5,109,97,116,99,104,114,117,0,0,0,218, + 10,73,71,78,79,82,69,67,65,83,69,169,6,114,193,0, + 0,0,218,10,110,111,114,109,97,108,105,122,101,100,114,41, + 1,0,0,114,60,1,0,0,90,8,116,101,109,112,108,97, + 116,101,90,8,109,97,110,105,102,101,115,116,114,3,0,0, + 0,114,3,0,0,0,114,6,0,0,0,218,13,95,109,97, + 116,99,104,101,115,95,105,110,102,111,128,5,0,0,115,8, + 0,0,0,0,2,8,1,4,1,12,1,122,24,80,97,116, + 104,70,105,110,100,101,114,46,95,109,97,116,99,104,101,115, + 95,105,110,102,111,99,3,0,0,0,0,0,0,0,0,0, + 0,0,6,0,0,0,5,0,0,0,67,0,0,0,115,46, + 0,0,0,100,1,100,0,108,0,125,3,100,2,125,4,124, + 4,106,1,124,1,100,3,141,1,125,5,124,3,106,2,124, + 5,116,3,124,2,131,1,124,3,106,4,100,4,141,3,83, + 0,41,5,78,114,73,0,0,0,122,30,123,112,97,116,116, + 101,114,110,125,45,46,42,92,46,101,103,103,91,92,92,47, + 93,69,71,71,45,73,78,70,79,114,73,1,0,0,114,74, + 1,0,0,41,5,114,60,1,0,0,114,62,0,0,0,90, + 6,115,101,97,114,99,104,114,85,0,0,0,114,75,1,0, + 0,114,76,1,0,0,114,3,0,0,0,114,3,0,0,0, + 114,6,0,0,0,218,15,95,109,97,116,99,104,101,115,95, + 108,101,103,97,99,121,135,5,0,0,115,8,0,0,0,0, + 2,8,1,4,1,12,1,122,26,80,97,116,104,70,105,110, + 100,101,114,46,95,109,97,116,99,104,101,115,95,108,101,103, + 97,99,121,99,3,0,0,0,0,0,0,0,0,0,0,0, + 3,0,0,0,4,0,0,0,3,0,0,0,115,48,0,0, + 0,124,1,160,0,161,0,115,12,100,1,83,0,124,2,160, + 1,100,2,100,3,161,2,137,1,135,0,135,1,102,2,100, + 4,100,5,132,8,124,1,160,2,161,0,68,0,131,1,83, + 0,41,6,78,114,3,0,0,0,250,1,45,114,45,0,0, + 0,99,1,0,0,0,0,0,0,0,0,0,0,0,2,0, + 0,0,5,0,0,0,51,0,0,0,115,42,0,0,0,124, + 0,93,34,125,1,136,0,160,0,136,1,124,1,161,2,115, + 30,136,0,160,1,136,1,124,1,161,2,114,2,124,1,86, + 0,1,0,113,2,100,0,83,0,114,110,0,0,0,41,2, + 114,78,1,0,0,114,79,1,0,0,41,2,114,32,0,0, + 0,114,41,1,0,0,169,2,114,193,0,0,0,114,77,1, + 0,0,114,3,0,0,0,114,6,0,0,0,114,19,1,0, + 0,147,5,0,0,115,8,0,0,0,4,0,2,1,12,1, + 12,254,122,42,80,97,116,104,70,105,110,100,101,114,46,95, + 115,101,97,114,99,104,95,112,97,116,104,46,60,108,111,99, + 97,108,115,62,46,60,103,101,110,101,120,112,114,62,41,3, + 90,6,105,115,95,100,105,114,114,67,0,0,0,90,7,105, + 116,101,114,100,105,114,41,3,114,193,0,0,0,90,4,114, + 111,111,116,114,63,1,0,0,114,3,0,0,0,114,81,1, + 0,0,114,6,0,0,0,114,65,1,0,0,142,5,0,0, + 115,8,0,0,0,0,2,8,1,4,1,12,1,122,23,80, 97,116,104,70,105,110,100,101,114,46,95,115,101,97,114,99, - 104,95,112,97,116,104,115,99,1,0,0,0,0,0,0,0, - 0,0,0,0,4,0,0,0,10,0,0,0,67,0,0,0, - 115,78,0,0,0,100,1,100,2,108,0,109,1,125,1,1, - 0,100,1,100,0,108,2,125,2,100,1,100,3,108,3,109, - 4,125,3,1,0,124,1,116,5,131,1,143,24,1,0,124, - 2,160,4,124,0,161,1,87,0,2,0,53,0,81,0,82, - 0,163,0,83,0,81,0,82,0,88,0,124,3,124,0,131, - 1,83,0,41,4,78,114,73,0,0,0,41,1,218,8,115, - 117,112,112,114,101,115,115,41,1,218,4,80,97,116,104,41, - 6,90,10,99,111,110,116,101,120,116,108,105,98,114,69,1, - 0,0,218,7,122,105,112,102,105,108,101,90,7,112,97,116, - 104,108,105,98,114,70,1,0,0,218,9,69,120,99,101,112, - 116,105,111,110,41,4,114,44,0,0,0,114,69,1,0,0, - 114,71,1,0,0,114,70,1,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,68,1,0,0,119,5, - 0,0,115,12,0,0,0,0,2,12,1,8,1,12,1,10, - 1,28,1,122,23,80,97,116,104,70,105,110,100,101,114,46, - 95,115,119,105,116,99,104,95,112,97,116,104,99,4,0,0, - 0,0,0,0,0,0,0,0,0,5,0,0,0,5,0,0, - 0,67,0,0,0,115,32,0,0,0,100,1,100,0,108,0, - 125,4,124,4,106,1,124,1,116,2,124,3,106,3,131,1, - 124,4,106,4,100,2,141,3,83,0,41,3,78,114,73,0, - 0,0,41,1,114,83,0,0,0,41,5,114,60,1,0,0, - 90,5,109,97,116,99,104,114,85,0,0,0,114,117,0,0, - 0,90,10,73,71,78,79,82,69,67,65,83,69,41,5,114, - 193,0,0,0,114,63,1,0,0,218,4,114,111,111,116,114, - 41,1,0,0,114,60,1,0,0,114,3,0,0,0,114,3, - 0,0,0,114,6,0,0,0,218,10,95,112,114,101,100,105, - 99,97,116,101,128,5,0,0,115,4,0,0,0,0,2,8, - 1,122,21,80,97,116,104,70,105,110,100,101,114,46,95,112, - 114,101,100,105,99,97,116,101,99,3,0,0,0,0,0,0, - 0,0,0,0,0,4,0,0,0,4,0,0,0,3,0,0, - 0,115,64,0,0,0,136,2,160,0,161,0,115,12,100,1, - 83,0,124,2,160,1,100,2,100,3,161,2,125,3,136,0, - 106,2,106,3,124,3,100,4,141,1,137,1,135,0,135,1, - 135,2,102,3,100,5,100,6,132,8,136,2,160,4,161,0, - 68,0,131,1,83,0,41,7,78,114,3,0,0,0,250,1, - 45,114,45,0,0,0,41,1,114,63,1,0,0,99,1,0, - 0,0,0,0,0,0,0,0,0,0,2,0,0,0,6,0, - 0,0,51,0,0,0,115,32,0,0,0,124,0,93,24,125, - 1,136,0,160,0,136,1,136,2,124,1,161,3,114,2,124, - 1,86,0,1,0,113,2,100,0,83,0,114,110,0,0,0, - 41,1,114,74,1,0,0,41,2,114,32,0,0,0,114,41, - 1,0,0,169,3,114,193,0,0,0,90,7,109,97,116,99, - 104,101,114,114,73,1,0,0,114,3,0,0,0,114,6,0, - 0,0,114,19,1,0,0,139,5,0,0,115,6,0,0,0, - 4,0,2,1,14,255,122,42,80,97,116,104,70,105,110,100, - 101,114,46,95,115,101,97,114,99,104,95,112,97,116,104,46, - 60,108,111,99,97,108,115,62,46,60,103,101,110,101,120,112, - 114,62,41,5,90,6,105,115,95,100,105,114,114,67,0,0, - 0,218,15,115,101,97,114,99,104,95,116,101,109,112,108,97, - 116,101,114,62,0,0,0,90,7,105,116,101,114,100,105,114, - 41,4,114,193,0,0,0,114,73,1,0,0,114,63,1,0, - 0,90,10,110,111,114,109,97,108,105,122,101,100,114,3,0, - 0,0,114,76,1,0,0,114,6,0,0,0,114,65,1,0, - 0,133,5,0,0,115,10,0,0,0,0,2,8,1,4,1, - 12,1,14,1,122,23,80,97,116,104,70,105,110,100,101,114, - 46,95,115,101,97,114,99,104,95,112,97,116,104,41,1,78, - 41,2,78,78,41,1,78,41,2,78,78,41,19,114,125,0, - 0,0,114,124,0,0,0,114,126,0,0,0,114,127,0,0, - 0,114,207,0,0,0,114,46,1,0,0,114,52,1,0,0, - 114,54,1,0,0,114,55,1,0,0,114,58,1,0,0,114, - 203,0,0,0,114,206,0,0,0,114,77,1,0,0,114,64, - 1,0,0,114,61,1,0,0,218,12,115,116,97,116,105,99, - 109,101,116,104,111,100,114,68,1,0,0,114,74,1,0,0, - 114,65,1,0,0,114,3,0,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,45,1,0,0,213,4, - 0,0,115,52,0,0,0,8,2,4,2,2,1,10,9,2, - 1,10,12,2,1,10,21,2,1,10,14,2,1,12,31,2, - 1,12,23,2,1,12,12,4,2,2,1,12,17,2,1,10, - 8,2,1,10,8,2,1,10,4,2,1,114,45,1,0,0, - 99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,3,0,0,0,64,0,0,0,115,90,0,0,0,101,0, - 90,1,100,0,90,2,100,1,90,3,100,2,100,3,132,0, - 90,4,100,4,100,5,132,0,90,5,101,6,90,7,100,6, - 100,7,132,0,90,8,100,8,100,9,132,0,90,9,100,19, - 100,11,100,12,132,1,90,10,100,13,100,14,132,0,90,11, - 101,12,100,15,100,16,132,0,131,1,90,13,100,17,100,18, - 132,0,90,14,100,10,83,0,41,20,218,10,70,105,108,101, - 70,105,110,100,101,114,122,172,70,105,108,101,45,98,97,115, - 101,100,32,102,105,110,100,101,114,46,10,10,32,32,32,32, - 73,110,116,101,114,97,99,116,105,111,110,115,32,119,105,116, - 104,32,116,104,101,32,102,105,108,101,32,115,121,115,116,101, - 109,32,97,114,101,32,99,97,99,104,101,100,32,102,111,114, - 32,112,101,114,102,111,114,109,97,110,99,101,44,32,98,101, - 105,110,103,10,32,32,32,32,114,101,102,114,101,115,104,101, - 100,32,119,104,101,110,32,116,104,101,32,100,105,114,101,99, - 116,111,114,121,32,116,104,101,32,102,105,110,100,101,114,32, - 105,115,32,104,97,110,100,108,105,110,103,32,104,97,115,32, - 98,101,101,110,32,109,111,100,105,102,105,101,100,46,10,10, - 32,32,32,32,99,2,0,0,0,0,0,0,0,0,0,0, - 0,5,0,0,0,6,0,0,0,7,0,0,0,115,84,0, - 0,0,103,0,125,3,124,2,68,0,93,32,92,2,137,0, - 125,4,124,3,160,0,135,0,102,1,100,1,100,2,132,8, - 124,4,68,0,131,1,161,1,1,0,113,8,124,3,124,0, - 95,1,124,1,112,54,100,3,124,0,95,2,100,4,124,0, - 95,3,116,4,131,0,124,0,95,5,116,4,131,0,124,0, - 95,6,100,5,83,0,41,6,122,154,73,110,105,116,105,97, - 108,105,122,101,32,119,105,116,104,32,116,104,101,32,112,97, - 116,104,32,116,111,32,115,101,97,114,99,104,32,111,110,32, - 97,110,100,32,97,32,118,97,114,105,97,98,108,101,32,110, - 117,109,98,101,114,32,111,102,10,32,32,32,32,32,32,32, - 32,50,45,116,117,112,108,101,115,32,99,111,110,116,97,105, - 110,105,110,103,32,116,104,101,32,108,111,97,100,101,114,32, - 97,110,100,32,116,104,101,32,102,105,108,101,32,115,117,102, - 102,105,120,101,115,32,116,104,101,32,108,111,97,100,101,114, - 10,32,32,32,32,32,32,32,32,114,101,99,111,103,110,105, - 122,101,115,46,99,1,0,0,0,0,0,0,0,0,0,0, - 0,2,0,0,0,3,0,0,0,51,0,0,0,115,22,0, - 0,0,124,0,93,14,125,1,124,1,136,0,102,2,86,0, - 1,0,113,2,100,0,83,0,114,110,0,0,0,114,3,0, - 0,0,114,16,1,0,0,169,1,114,140,0,0,0,114,3, - 0,0,0,114,6,0,0,0,114,19,1,0,0,158,5,0, - 0,115,4,0,0,0,4,0,2,0,122,38,70,105,108,101, - 70,105,110,100,101,114,46,95,95,105,110,105,116,95,95,46, - 60,108,111,99,97,108,115,62,46,60,103,101,110,101,120,112, - 114,62,114,71,0,0,0,114,105,0,0,0,78,41,7,114, - 167,0,0,0,218,8,95,108,111,97,100,101,114,115,114,44, - 0,0,0,218,11,95,112,97,116,104,95,109,116,105,109,101, - 218,3,115,101,116,218,11,95,112,97,116,104,95,99,97,99, - 104,101,218,19,95,114,101,108,97,120,101,100,95,112,97,116, - 104,95,99,97,99,104,101,41,5,114,119,0,0,0,114,44, - 0,0,0,218,14,108,111,97,100,101,114,95,100,101,116,97, - 105,108,115,90,7,108,111,97,100,101,114,115,114,189,0,0, - 0,114,3,0,0,0,114,80,1,0,0,114,6,0,0,0, - 114,209,0,0,0,152,5,0,0,115,16,0,0,0,0,4, - 4,1,12,1,26,1,6,2,10,1,6,1,8,1,122,19, - 70,105,108,101,70,105,110,100,101,114,46,95,95,105,110,105, - 116,95,95,99,1,0,0,0,0,0,0,0,0,0,0,0, - 1,0,0,0,2,0,0,0,67,0,0,0,115,10,0,0, - 0,100,1,124,0,95,0,100,2,83,0,41,3,122,31,73, - 110,118,97,108,105,100,97,116,101,32,116,104,101,32,100,105, - 114,101,99,116,111,114,121,32,109,116,105,109,101,46,114,105, - 0,0,0,78,41,1,114,82,1,0,0,114,246,0,0,0, - 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 46,1,0,0,166,5,0,0,115,2,0,0,0,0,2,122, - 28,70,105,108,101,70,105,110,100,101,114,46,105,110,118,97, - 108,105,100,97,116,101,95,99,97,99,104,101,115,99,2,0, - 0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0, - 0,0,67,0,0,0,115,42,0,0,0,124,0,160,0,124, - 1,161,1,125,2,124,2,100,1,107,8,114,26,100,1,103, - 0,102,2,83,0,124,2,106,1,124,2,106,2,112,38,103, - 0,102,2,83,0,41,2,122,197,84,114,121,32,116,111,32, - 102,105,110,100,32,97,32,108,111,97,100,101,114,32,102,111, - 114,32,116,104,101,32,115,112,101,99,105,102,105,101,100,32, - 109,111,100,117,108,101,44,32,111,114,32,116,104,101,32,110, - 97,109,101,115,112,97,99,101,10,32,32,32,32,32,32,32, - 32,112,97,99,107,97,103,101,32,112,111,114,116,105,111,110, - 115,46,32,82,101,116,117,114,110,115,32,40,108,111,97,100, - 101,114,44,32,108,105,115,116,45,111,102,45,112,111,114,116, - 105,111,110,115,41,46,10,10,32,32,32,32,32,32,32,32, - 84,104,105,115,32,109,101,116,104,111,100,32,105,115,32,100, - 101,112,114,101,99,97,116,101,100,46,32,32,85,115,101,32, - 102,105,110,100,95,115,112,101,99,40,41,32,105,110,115,116, - 101,97,100,46,10,10,32,32,32,32,32,32,32,32,78,41, - 3,114,203,0,0,0,114,140,0,0,0,114,178,0,0,0, - 41,3,114,119,0,0,0,114,139,0,0,0,114,187,0,0, - 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,137,0,0,0,172,5,0,0,115,8,0,0,0,0,7, - 10,1,8,1,8,1,122,22,70,105,108,101,70,105,110,100, - 101,114,46,102,105,110,100,95,108,111,97,100,101,114,99,6, - 0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,6, - 0,0,0,67,0,0,0,115,26,0,0,0,124,1,124,2, - 124,3,131,2,125,6,116,0,124,2,124,3,124,6,124,4, - 100,1,141,4,83,0,41,2,78,114,177,0,0,0,41,1, - 114,190,0,0,0,41,7,114,119,0,0,0,114,188,0,0, - 0,114,139,0,0,0,114,44,0,0,0,90,4,115,109,115, - 108,114,202,0,0,0,114,140,0,0,0,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,58,1,0,0,184, - 5,0,0,115,8,0,0,0,0,1,10,1,8,1,2,255, - 122,20,70,105,108,101,70,105,110,100,101,114,46,95,103,101, - 116,95,115,112,101,99,78,99,3,0,0,0,0,0,0,0, - 0,0,0,0,14,0,0,0,8,0,0,0,67,0,0,0, - 115,98,1,0,0,100,1,125,3,124,1,160,0,100,2,161, - 1,100,3,25,0,125,4,122,24,116,1,124,0,106,2,112, - 34,116,3,160,4,161,0,131,1,106,5,125,5,87,0,110, - 24,4,0,116,6,107,10,114,66,1,0,1,0,1,0,100, - 4,125,5,89,0,110,2,88,0,124,5,124,0,106,7,107, - 3,114,92,124,0,160,8,161,0,1,0,124,5,124,0,95, - 7,116,9,131,0,114,114,124,0,106,10,125,6,124,4,160, - 11,161,0,125,7,110,10,124,0,106,12,125,6,124,4,125, - 7,124,7,124,6,107,6,114,218,116,13,124,0,106,2,124, - 4,131,2,125,8,124,0,106,14,68,0,93,58,92,2,125, - 9,125,10,100,5,124,9,23,0,125,11,116,13,124,8,124, - 11,131,2,125,12,116,15,124,12,131,1,114,150,124,0,160, - 16,124,10,124,1,124,12,124,8,103,1,124,2,161,5,2, - 0,1,0,83,0,113,150,116,17,124,8,131,1,125,3,124, - 0,106,14,68,0,93,82,92,2,125,9,125,10,116,13,124, - 0,106,2,124,4,124,9,23,0,131,2,125,12,116,18,106, - 19,100,6,124,12,100,3,100,7,141,3,1,0,124,7,124, - 9,23,0,124,6,107,6,114,224,116,15,124,12,131,1,114, - 224,124,0,160,16,124,10,124,1,124,12,100,8,124,2,161, - 5,2,0,1,0,83,0,113,224,124,3,144,1,114,94,116, - 18,160,19,100,9,124,8,161,2,1,0,116,18,160,20,124, - 1,100,8,161,2,125,13,124,8,103,1,124,13,95,21,124, - 13,83,0,100,8,83,0,41,10,122,111,84,114,121,32,116, - 111,32,102,105,110,100,32,97,32,115,112,101,99,32,102,111, - 114,32,116,104,101,32,115,112,101,99,105,102,105,101,100,32, - 109,111,100,117,108,101,46,10,10,32,32,32,32,32,32,32, - 32,82,101,116,117,114,110,115,32,116,104,101,32,109,97,116, - 99,104,105,110,103,32,115,112,101,99,44,32,111,114,32,78, - 111,110,101,32,105,102,32,110,111,116,32,102,111,117,110,100, - 46,10,32,32,32,32,32,32,32,32,70,114,71,0,0,0, - 114,28,0,0,0,114,105,0,0,0,114,209,0,0,0,122, - 9,116,114,121,105,110,103,32,123,125,41,1,90,9,118,101, - 114,98,111,115,105,116,121,78,122,25,112,111,115,115,105,98, - 108,101,32,110,97,109,101,115,112,97,99,101,32,102,111,114, - 32,123,125,41,22,114,41,0,0,0,114,49,0,0,0,114, - 44,0,0,0,114,2,0,0,0,114,55,0,0,0,114,10, - 1,0,0,114,50,0,0,0,114,82,1,0,0,218,11,95, - 102,105,108,108,95,99,97,99,104,101,114,7,0,0,0,114, - 85,1,0,0,114,106,0,0,0,114,84,1,0,0,114,38, - 0,0,0,114,81,1,0,0,114,54,0,0,0,114,58,1, - 0,0,114,56,0,0,0,114,134,0,0,0,114,149,0,0, - 0,114,183,0,0,0,114,178,0,0,0,41,14,114,119,0, - 0,0,114,139,0,0,0,114,202,0,0,0,90,12,105,115, - 95,110,97,109,101,115,112,97,99,101,90,11,116,97,105,108, - 95,109,111,100,117,108,101,114,169,0,0,0,90,5,99,97, - 99,104,101,90,12,99,97,99,104,101,95,109,111,100,117,108, - 101,90,9,98,97,115,101,95,112,97,116,104,114,17,1,0, - 0,114,188,0,0,0,90,13,105,110,105,116,95,102,105,108, - 101,110,97,109,101,90,9,102,117,108,108,95,112,97,116,104, - 114,187,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,114,203,0,0,0,189,5,0,0,115,74,0, - 0,0,0,5,4,1,14,1,2,1,24,1,14,1,10,1, - 10,1,8,1,6,2,6,1,6,1,10,2,6,1,4,2, - 8,1,12,1,14,1,8,1,10,1,8,1,26,4,8,2, - 14,1,16,1,16,1,12,1,8,1,10,1,2,0,2,255, - 10,2,6,1,12,1,12,1,8,1,4,1,122,20,70,105, - 108,101,70,105,110,100,101,114,46,102,105,110,100,95,115,112, - 101,99,99,1,0,0,0,0,0,0,0,0,0,0,0,9, - 0,0,0,10,0,0,0,67,0,0,0,115,190,0,0,0, - 124,0,106,0,125,1,122,22,116,1,160,2,124,1,112,22, - 116,1,160,3,161,0,161,1,125,2,87,0,110,30,4,0, - 116,4,116,5,116,6,102,3,107,10,114,58,1,0,1,0, - 1,0,103,0,125,2,89,0,110,2,88,0,116,7,106,8, - 160,9,100,1,161,1,115,84,116,10,124,2,131,1,124,0, - 95,11,110,74,116,10,131,0,125,3,124,2,68,0,93,56, - 125,4,124,4,160,12,100,2,161,1,92,3,125,5,125,6, - 125,7,124,6,114,136,100,3,160,13,124,5,124,7,160,14, - 161,0,161,2,125,8,110,4,124,5,125,8,124,3,160,15, - 124,8,161,1,1,0,113,94,124,3,124,0,95,11,116,7, - 106,8,160,9,116,16,161,1,114,186,100,4,100,5,132,0, - 124,2,68,0,131,1,124,0,95,17,100,6,83,0,41,7, - 122,68,70,105,108,108,32,116,104,101,32,99,97,99,104,101, - 32,111,102,32,112,111,116,101,110,116,105,97,108,32,109,111, - 100,117,108,101,115,32,97,110,100,32,112,97,99,107,97,103, - 101,115,32,102,111,114,32,116,104,105,115,32,100,105,114,101, - 99,116,111,114,121,46,114,0,0,0,0,114,71,0,0,0, - 114,61,0,0,0,99,1,0,0,0,0,0,0,0,0,0, - 0,0,2,0,0,0,4,0,0,0,83,0,0,0,115,20, - 0,0,0,104,0,124,0,93,12,125,1,124,1,160,0,161, - 0,146,2,113,4,83,0,114,3,0,0,0,41,1,114,106, - 0,0,0,41,2,114,32,0,0,0,90,2,102,110,114,3, - 0,0,0,114,3,0,0,0,114,6,0,0,0,218,9,60, - 115,101,116,99,111,109,112,62,10,6,0,0,115,4,0,0, - 0,6,0,2,0,122,41,70,105,108,101,70,105,110,100,101, - 114,46,95,102,105,108,108,95,99,97,99,104,101,46,60,108, - 111,99,97,108,115,62,46,60,115,101,116,99,111,109,112,62, - 78,41,18,114,44,0,0,0,114,2,0,0,0,114,7,1, - 0,0,114,55,0,0,0,114,3,1,0,0,218,15,80,101, - 114,109,105,115,115,105,111,110,69,114,114,111,114,218,18,78, - 111,116,65,68,105,114,101,99,116,111,114,121,69,114,114,111, - 114,114,8,0,0,0,114,9,0,0,0,114,10,0,0,0, - 114,83,1,0,0,114,84,1,0,0,114,101,0,0,0,114, - 62,0,0,0,114,106,0,0,0,218,3,97,100,100,114,11, - 0,0,0,114,85,1,0,0,41,9,114,119,0,0,0,114, - 44,0,0,0,114,8,1,0,0,90,21,108,111,119,101,114, - 95,115,117,102,102,105,120,95,99,111,110,116,101,110,116,115, - 114,41,1,0,0,114,117,0,0,0,114,29,1,0,0,114, - 17,1,0,0,90,8,110,101,119,95,110,97,109,101,114,3, - 0,0,0,114,3,0,0,0,114,6,0,0,0,114,87,1, - 0,0,237,5,0,0,115,34,0,0,0,0,2,6,1,2, - 1,22,1,20,3,10,3,12,1,12,7,6,1,8,1,16, - 1,4,1,18,2,4,1,12,1,6,1,12,1,122,22,70, - 105,108,101,70,105,110,100,101,114,46,95,102,105,108,108,95, - 99,97,99,104,101,99,1,0,0,0,0,0,0,0,0,0, - 0,0,3,0,0,0,3,0,0,0,7,0,0,0,115,18, - 0,0,0,135,0,135,1,102,2,100,1,100,2,132,8,125, - 2,124,2,83,0,41,3,97,20,1,0,0,65,32,99,108, - 97,115,115,32,109,101,116,104,111,100,32,119,104,105,99,104, - 32,114,101,116,117,114,110,115,32,97,32,99,108,111,115,117, - 114,101,32,116,111,32,117,115,101,32,111,110,32,115,121,115, - 46,112,97,116,104,95,104,111,111,107,10,32,32,32,32,32, - 32,32,32,119,104,105,99,104,32,119,105,108,108,32,114,101, - 116,117,114,110,32,97,110,32,105,110,115,116,97,110,99,101, - 32,117,115,105,110,103,32,116,104,101,32,115,112,101,99,105, - 102,105,101,100,32,108,111,97,100,101,114,115,32,97,110,100, - 32,116,104,101,32,112,97,116,104,10,32,32,32,32,32,32, - 32,32,99,97,108,108,101,100,32,111,110,32,116,104,101,32, - 99,108,111,115,117,114,101,46,10,10,32,32,32,32,32,32, - 32,32,73,102,32,116,104,101,32,112,97,116,104,32,99,97, - 108,108,101,100,32,111,110,32,116,104,101,32,99,108,111,115, - 117,114,101,32,105,115,32,110,111,116,32,97,32,100,105,114, - 101,99,116,111,114,121,44,32,73,109,112,111,114,116,69,114, - 114,111,114,32,105,115,10,32,32,32,32,32,32,32,32,114, - 97,105,115,101,100,46,10,10,32,32,32,32,32,32,32,32, - 99,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0, - 0,4,0,0,0,19,0,0,0,115,34,0,0,0,116,0, - 124,0,131,1,115,20,116,1,100,1,124,0,100,2,141,2, - 130,1,136,0,124,0,102,1,136,1,158,2,142,0,83,0, - 41,3,122,45,80,97,116,104,32,104,111,111,107,32,102,111, - 114,32,105,109,112,111,114,116,108,105,98,46,109,97,99,104, - 105,110,101,114,121,46,70,105,108,101,70,105,110,100,101,114, - 46,122,30,111,110,108,121,32,100,105,114,101,99,116,111,114, - 105,101,115,32,97,114,101,32,115,117,112,112,111,114,116,101, - 100,114,48,0,0,0,41,2,114,56,0,0,0,114,118,0, - 0,0,114,48,0,0,0,169,2,114,193,0,0,0,114,86, - 1,0,0,114,3,0,0,0,114,6,0,0,0,218,24,112, - 97,116,104,95,104,111,111,107,95,102,111,114,95,70,105,108, - 101,70,105,110,100,101,114,22,6,0,0,115,6,0,0,0, - 0,2,8,1,12,1,122,54,70,105,108,101,70,105,110,100, - 101,114,46,112,97,116,104,95,104,111,111,107,46,60,108,111, - 99,97,108,115,62,46,112,97,116,104,95,104,111,111,107,95, - 102,111,114,95,70,105,108,101,70,105,110,100,101,114,114,3, - 0,0,0,41,3,114,193,0,0,0,114,86,1,0,0,114, - 93,1,0,0,114,3,0,0,0,114,92,1,0,0,114,6, - 0,0,0,218,9,112,97,116,104,95,104,111,111,107,12,6, - 0,0,115,4,0,0,0,0,10,14,6,122,20,70,105,108, - 101,70,105,110,100,101,114,46,112,97,116,104,95,104,111,111, - 107,99,1,0,0,0,0,0,0,0,0,0,0,0,1,0, - 0,0,3,0,0,0,67,0,0,0,115,12,0,0,0,100, - 1,160,0,124,0,106,1,161,1,83,0,41,2,78,122,16, - 70,105,108,101,70,105,110,100,101,114,40,123,33,114,125,41, - 41,2,114,62,0,0,0,114,44,0,0,0,114,246,0,0, - 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,39,1,0,0,30,6,0,0,115,2,0,0,0,0,1, - 122,19,70,105,108,101,70,105,110,100,101,114,46,95,95,114, - 101,112,114,95,95,41,1,78,41,15,114,125,0,0,0,114, - 124,0,0,0,114,126,0,0,0,114,127,0,0,0,114,209, - 0,0,0,114,46,1,0,0,114,143,0,0,0,114,206,0, - 0,0,114,137,0,0,0,114,58,1,0,0,114,203,0,0, - 0,114,87,1,0,0,114,207,0,0,0,114,94,1,0,0, - 114,39,1,0,0,114,3,0,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,79,1,0,0,143,5, - 0,0,115,22,0,0,0,8,2,4,7,8,14,8,4,4, - 2,8,12,8,5,10,48,8,31,2,1,10,17,114,79,1, - 0,0,99,4,0,0,0,0,0,0,0,0,0,0,0,6, - 0,0,0,8,0,0,0,67,0,0,0,115,146,0,0,0, - 124,0,160,0,100,1,161,1,125,4,124,0,160,0,100,2, - 161,1,125,5,124,4,115,66,124,5,114,36,124,5,106,1, - 125,4,110,30,124,2,124,3,107,2,114,56,116,2,124,1, - 124,2,131,2,125,4,110,10,116,3,124,1,124,2,131,2, - 125,4,124,5,115,84,116,4,124,1,124,2,124,4,100,3, - 141,3,125,5,122,36,124,5,124,0,100,2,60,0,124,4, - 124,0,100,1,60,0,124,2,124,0,100,4,60,0,124,3, - 124,0,100,5,60,0,87,0,110,20,4,0,116,5,107,10, - 114,140,1,0,1,0,1,0,89,0,110,2,88,0,100,0, - 83,0,41,6,78,218,10,95,95,108,111,97,100,101,114,95, - 95,218,8,95,95,115,112,101,99,95,95,114,80,1,0,0, - 90,8,95,95,102,105,108,101,95,95,90,10,95,95,99,97, - 99,104,101,100,95,95,41,6,218,3,103,101,116,114,140,0, - 0,0,114,15,1,0,0,114,9,1,0,0,114,190,0,0, - 0,114,72,1,0,0,41,6,90,2,110,115,114,117,0,0, - 0,90,8,112,97,116,104,110,97,109,101,90,9,99,112,97, - 116,104,110,97,109,101,114,140,0,0,0,114,187,0,0,0, - 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 14,95,102,105,120,95,117,112,95,109,111,100,117,108,101,36, - 6,0,0,115,34,0,0,0,0,2,10,1,10,1,4,1, - 4,1,8,1,8,1,12,2,10,1,4,1,14,1,2,1, - 8,1,8,1,8,1,12,1,14,2,114,98,1,0,0,99, + 104,95,112,97,116,104,41,1,78,41,2,78,78,41,1,78, + 41,2,78,78,41,19,114,125,0,0,0,114,124,0,0,0, + 114,126,0,0,0,114,127,0,0,0,114,207,0,0,0,114, + 46,1,0,0,114,52,1,0,0,114,54,1,0,0,114,55, + 1,0,0,114,58,1,0,0,114,203,0,0,0,114,206,0, + 0,0,114,64,1,0,0,114,61,1,0,0,218,12,115,116, + 97,116,105,99,109,101,116,104,111,100,114,68,1,0,0,114, + 78,1,0,0,114,79,1,0,0,114,65,1,0,0,114,3, + 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, + 0,0,114,45,1,0,0,213,4,0,0,115,54,0,0,0, + 8,2,4,2,2,1,10,9,2,1,10,12,2,1,10,21, + 2,1,10,14,2,1,12,31,2,1,12,23,2,1,12,12, + 2,1,12,17,2,1,10,8,2,1,10,10,2,1,10,6, + 2,1,10,6,2,1,114,45,1,0,0,99,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0, - 3,0,0,0,67,0,0,0,115,38,0,0,0,116,0,116, - 1,160,2,161,0,102,2,125,0,116,3,116,4,102,2,125, - 1,116,5,116,6,102,2,125,2,124,0,124,1,124,2,103, - 3,83,0,41,1,122,95,82,101,116,117,114,110,115,32,97, - 32,108,105,115,116,32,111,102,32,102,105,108,101,45,98,97, - 115,101,100,32,109,111,100,117,108,101,32,108,111,97,100,101, - 114,115,46,10,10,32,32,32,32,69,97,99,104,32,105,116, - 101,109,32,105,115,32,97,32,116,117,112,108,101,32,40,108, - 111,97,100,101,114,44,32,115,117,102,102,105,120,101,115,41, - 46,10,32,32,32,32,41,7,114,252,0,0,0,114,163,0, - 0,0,218,18,101,120,116,101,110,115,105,111,110,95,115,117, - 102,102,105,120,101,115,114,9,1,0,0,114,102,0,0,0, - 114,15,1,0,0,114,89,0,0,0,41,3,90,10,101,120, - 116,101,110,115,105,111,110,115,90,6,115,111,117,114,99,101, - 90,8,98,121,116,101,99,111,100,101,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,184,0,0,0,59,6, - 0,0,115,8,0,0,0,0,5,12,1,8,1,8,1,114, - 184,0,0,0,99,1,0,0,0,0,0,0,0,0,0,0, - 0,12,0,0,0,9,0,0,0,67,0,0,0,115,178,1, - 0,0,124,0,97,0,116,0,106,1,97,1,116,0,106,2, - 97,2,116,1,106,3,116,4,25,0,125,1,100,1,68,0, - 93,48,125,2,124,2,116,1,106,3,107,7,114,56,116,0, - 160,5,124,2,161,1,125,3,110,10,116,1,106,3,124,2, - 25,0,125,3,116,6,124,1,124,2,124,3,131,3,1,0, - 113,30,100,2,100,3,103,1,102,2,100,4,100,5,100,3, - 103,2,102,2,102,2,125,4,124,4,68,0,93,110,92,2, - 125,5,125,6,116,7,100,6,100,7,132,0,124,6,68,0, - 131,1,131,1,115,136,116,8,130,1,124,6,100,8,25,0, - 125,7,124,5,116,1,106,3,107,6,114,170,116,1,106,3, - 124,5,25,0,125,8,1,0,113,226,113,106,122,20,116,0, - 160,5,124,5,161,1,125,8,87,0,1,0,113,226,87,0, - 113,106,4,0,116,9,107,10,114,214,1,0,1,0,1,0, - 89,0,113,106,89,0,113,106,88,0,113,106,116,9,100,9, - 131,1,130,1,116,6,124,1,100,10,124,8,131,3,1,0, - 116,6,124,1,100,11,124,7,131,3,1,0,116,6,124,1, - 100,12,100,13,160,10,124,6,161,1,131,3,1,0,116,6, - 124,1,100,14,100,15,100,16,132,0,124,6,68,0,131,1, - 131,3,1,0,116,0,160,5,100,17,161,1,125,9,116,6, - 124,1,100,17,124,9,131,3,1,0,116,0,160,5,100,18, - 161,1,125,10,116,6,124,1,100,18,124,10,131,3,1,0, - 124,5,100,4,107,2,144,1,114,110,116,0,160,5,100,19, - 161,1,125,11,116,6,124,1,100,20,124,11,131,3,1,0, - 116,6,124,1,100,21,116,11,131,0,131,3,1,0,116,12, - 160,13,116,2,160,14,161,0,161,1,1,0,124,5,100,4, - 107,2,144,1,114,174,116,15,160,16,100,22,161,1,1,0, - 100,23,116,12,107,6,144,1,114,174,100,24,116,17,95,18, - 100,25,83,0,41,26,122,205,83,101,116,117,112,32,116,104, - 101,32,112,97,116,104,45,98,97,115,101,100,32,105,109,112, - 111,114,116,101,114,115,32,102,111,114,32,105,109,112,111,114, - 116,108,105,98,32,98,121,32,105,109,112,111,114,116,105,110, - 103,32,110,101,101,100,101,100,10,32,32,32,32,98,117,105, - 108,116,45,105,110,32,109,111,100,117,108,101,115,32,97,110, - 100,32,105,110,106,101,99,116,105,110,103,32,116,104,101,109, - 32,105,110,116,111,32,116,104,101,32,103,108,111,98,97,108, - 32,110,97,109,101,115,112,97,99,101,46,10,10,32,32,32, - 32,79,116,104,101,114,32,99,111,109,112,111,110,101,110,116, - 115,32,97,114,101,32,101,120,116,114,97,99,116,101,100,32, - 102,114,111,109,32,116,104,101,32,99,111,114,101,32,98,111, - 111,116,115,116,114,97,112,32,109,111,100,117,108,101,46,10, - 10,32,32,32,32,41,4,114,64,0,0,0,114,75,0,0, - 0,218,8,98,117,105,108,116,105,110,115,114,160,0,0,0, - 90,5,112,111,115,105,120,250,1,47,90,2,110,116,250,1, - 92,99,1,0,0,0,0,0,0,0,0,0,0,0,2,0, - 0,0,3,0,0,0,115,0,0,0,115,26,0,0,0,124, - 0,93,18,125,1,116,0,124,1,131,1,100,0,107,2,86, - 0,1,0,113,2,100,1,83,0,41,2,114,39,0,0,0, - 78,41,1,114,22,0,0,0,41,2,114,32,0,0,0,114, - 95,0,0,0,114,3,0,0,0,114,3,0,0,0,114,6, - 0,0,0,114,19,1,0,0,95,6,0,0,115,4,0,0, - 0,4,0,2,0,122,25,95,115,101,116,117,112,46,60,108, - 111,99,97,108,115,62,46,60,103,101,110,101,120,112,114,62, - 114,73,0,0,0,122,30,105,109,112,111,114,116,108,105,98, - 32,114,101,113,117,105,114,101,115,32,112,111,115,105,120,32, - 111,114,32,110,116,114,2,0,0,0,114,35,0,0,0,114, - 31,0,0,0,114,40,0,0,0,114,58,0,0,0,99,1, - 0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,4, - 0,0,0,83,0,0,0,115,22,0,0,0,104,0,124,0, - 93,14,125,1,100,0,124,1,155,0,157,2,146,2,113,4, - 83,0,41,1,114,74,0,0,0,114,3,0,0,0,41,2, - 114,32,0,0,0,218,1,115,114,3,0,0,0,114,3,0, - 0,0,114,6,0,0,0,114,88,1,0,0,111,6,0,0, - 115,4,0,0,0,6,0,2,0,122,25,95,115,101,116,117, - 112,46,60,108,111,99,97,108,115,62,46,60,115,101,116,99, - 111,109,112,62,90,7,95,116,104,114,101,97,100,90,8,95, - 119,101,97,107,114,101,102,90,6,119,105,110,114,101,103,114, - 192,0,0,0,114,7,0,0,0,122,4,46,112,121,119,122, - 6,95,100,46,112,121,100,84,78,41,19,114,134,0,0,0, - 114,8,0,0,0,114,163,0,0,0,114,31,1,0,0,114, - 125,0,0,0,90,18,95,98,117,105,108,116,105,110,95,102, - 114,111,109,95,110,97,109,101,114,129,0,0,0,218,3,97, - 108,108,114,23,0,0,0,114,118,0,0,0,114,36,0,0, - 0,114,13,0,0,0,114,21,1,0,0,114,167,0,0,0, - 114,99,1,0,0,114,102,0,0,0,114,186,0,0,0,114, - 191,0,0,0,114,195,0,0,0,41,12,218,17,95,98,111, - 111,116,115,116,114,97,112,95,109,111,100,117,108,101,90,11, - 115,101,108,102,95,109,111,100,117,108,101,90,12,98,117,105, - 108,116,105,110,95,110,97,109,101,90,14,98,117,105,108,116, - 105,110,95,109,111,100,117,108,101,90,10,111,115,95,100,101, - 116,97,105,108,115,90,10,98,117,105,108,116,105,110,95,111, - 115,114,31,0,0,0,114,35,0,0,0,90,9,111,115,95, - 109,111,100,117,108,101,90,13,116,104,114,101,97,100,95,109, - 111,100,117,108,101,90,14,119,101,97,107,114,101,102,95,109, - 111,100,117,108,101,90,13,119,105,110,114,101,103,95,109,111, - 100,117,108,101,114,3,0,0,0,114,3,0,0,0,114,6, - 0,0,0,218,6,95,115,101,116,117,112,70,6,0,0,115, - 78,0,0,0,0,8,4,1,6,1,6,3,10,1,8,1, - 10,1,12,2,10,1,14,3,22,1,12,2,22,1,8,1, - 10,1,10,1,6,2,2,1,10,1,10,1,14,1,12,2, - 8,1,12,1,12,1,18,1,22,3,10,1,12,3,10,1, - 12,3,10,1,10,1,12,3,14,1,14,1,10,1,10,1, - 10,1,114,106,1,0,0,99,1,0,0,0,0,0,0,0, - 0,0,0,0,2,0,0,0,4,0,0,0,67,0,0,0, - 115,50,0,0,0,116,0,124,0,131,1,1,0,116,1,131, - 0,125,1,116,2,106,3,160,4,116,5,106,6,124,1,142, - 0,103,1,161,1,1,0,116,2,106,7,160,8,116,9,161, - 1,1,0,100,1,83,0,41,2,122,41,73,110,115,116,97, - 108,108,32,116,104,101,32,112,97,116,104,45,98,97,115,101, - 100,32,105,109,112,111,114,116,32,99,111,109,112,111,110,101, - 110,116,115,46,78,41,10,114,106,1,0,0,114,184,0,0, - 0,114,8,0,0,0,114,51,1,0,0,114,167,0,0,0, - 114,79,1,0,0,114,94,1,0,0,218,9,109,101,116,97, - 95,112,97,116,104,114,186,0,0,0,114,45,1,0,0,41, - 2,114,105,1,0,0,90,17,115,117,112,112,111,114,116,101, - 100,95,108,111,97,100,101,114,115,114,3,0,0,0,114,3, - 0,0,0,114,6,0,0,0,218,8,95,105,110,115,116,97, - 108,108,135,6,0,0,115,8,0,0,0,0,2,8,1,6, - 1,20,1,114,108,1,0,0,41,1,114,60,0,0,0,41, - 1,78,41,3,78,78,78,41,2,114,73,0,0,0,114,73, - 0,0,0,41,1,84,41,1,78,41,1,78,41,63,114,127, - 0,0,0,114,12,0,0,0,90,37,95,67,65,83,69,95, - 73,78,83,69,78,83,73,84,73,86,69,95,80,76,65,84, - 70,79,82,77,83,95,66,89,84,69,83,95,75,69,89,114, - 11,0,0,0,114,13,0,0,0,114,20,0,0,0,114,27, - 0,0,0,114,29,0,0,0,114,38,0,0,0,114,47,0, - 0,0,114,49,0,0,0,114,53,0,0,0,114,54,0,0, - 0,114,56,0,0,0,114,59,0,0,0,114,69,0,0,0, - 218,4,116,121,112,101,218,8,95,95,99,111,100,101,95,95, - 114,162,0,0,0,114,18,0,0,0,114,148,0,0,0,114, - 17,0,0,0,114,24,0,0,0,114,236,0,0,0,114,92, - 0,0,0,114,88,0,0,0,114,102,0,0,0,114,89,0, - 0,0,90,23,68,69,66,85,71,95,66,89,84,69,67,79, - 68,69,95,83,85,70,70,73,88,69,83,90,27,79,80,84, - 73,77,73,90,69,68,95,66,89,84,69,67,79,68,69,95, - 83,85,70,70,73,88,69,83,114,98,0,0,0,114,103,0, - 0,0,114,109,0,0,0,114,113,0,0,0,114,115,0,0, - 0,114,136,0,0,0,114,143,0,0,0,114,152,0,0,0, - 114,156,0,0,0,114,158,0,0,0,114,165,0,0,0,114, - 170,0,0,0,114,171,0,0,0,114,176,0,0,0,218,6, - 111,98,106,101,99,116,114,185,0,0,0,114,190,0,0,0, - 114,191,0,0,0,114,208,0,0,0,114,221,0,0,0,114, - 239,0,0,0,114,9,1,0,0,114,15,1,0,0,114,21, - 1,0,0,114,252,0,0,0,114,22,1,0,0,114,43,1, - 0,0,114,45,1,0,0,114,79,1,0,0,114,98,1,0, - 0,114,184,0,0,0,114,106,1,0,0,114,108,1,0,0, + 64,0,0,0,115,90,0,0,0,101,0,90,1,100,0,90, + 2,100,1,90,3,100,2,100,3,132,0,90,4,100,4,100, + 5,132,0,90,5,101,6,90,7,100,6,100,7,132,0,90, + 8,100,8,100,9,132,0,90,9,100,19,100,11,100,12,132, + 1,90,10,100,13,100,14,132,0,90,11,101,12,100,15,100, + 16,132,0,131,1,90,13,100,17,100,18,132,0,90,14,100, + 10,83,0,41,20,218,10,70,105,108,101,70,105,110,100,101, + 114,122,172,70,105,108,101,45,98,97,115,101,100,32,102,105, + 110,100,101,114,46,10,10,32,32,32,32,73,110,116,101,114, + 97,99,116,105,111,110,115,32,119,105,116,104,32,116,104,101, + 32,102,105,108,101,32,115,121,115,116,101,109,32,97,114,101, + 32,99,97,99,104,101,100,32,102,111,114,32,112,101,114,102, + 111,114,109,97,110,99,101,44,32,98,101,105,110,103,10,32, + 32,32,32,114,101,102,114,101,115,104,101,100,32,119,104,101, + 110,32,116,104,101,32,100,105,114,101,99,116,111,114,121,32, + 116,104,101,32,102,105,110,100,101,114,32,105,115,32,104,97, + 110,100,108,105,110,103,32,104,97,115,32,98,101,101,110,32, + 109,111,100,105,102,105,101,100,46,10,10,32,32,32,32,99, + 2,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0, + 6,0,0,0,7,0,0,0,115,84,0,0,0,103,0,125, + 3,124,2,68,0,93,32,92,2,137,0,125,4,124,3,160, + 0,135,0,102,1,100,1,100,2,132,8,124,4,68,0,131, + 1,161,1,1,0,113,8,124,3,124,0,95,1,124,1,112, + 54,100,3,124,0,95,2,100,4,124,0,95,3,116,4,131, + 0,124,0,95,5,116,4,131,0,124,0,95,6,100,5,83, + 0,41,6,122,154,73,110,105,116,105,97,108,105,122,101,32, + 119,105,116,104,32,116,104,101,32,112,97,116,104,32,116,111, + 32,115,101,97,114,99,104,32,111,110,32,97,110,100,32,97, + 32,118,97,114,105,97,98,108,101,32,110,117,109,98,101,114, + 32,111,102,10,32,32,32,32,32,32,32,32,50,45,116,117, + 112,108,101,115,32,99,111,110,116,97,105,110,105,110,103,32, + 116,104,101,32,108,111,97,100,101,114,32,97,110,100,32,116, + 104,101,32,102,105,108,101,32,115,117,102,102,105,120,101,115, + 32,116,104,101,32,108,111,97,100,101,114,10,32,32,32,32, + 32,32,32,32,114,101,99,111,103,110,105,122,101,115,46,99, + 1,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, + 3,0,0,0,51,0,0,0,115,22,0,0,0,124,0,93, + 14,125,1,124,1,136,0,102,2,86,0,1,0,113,2,100, + 0,83,0,114,110,0,0,0,114,3,0,0,0,114,16,1, + 0,0,169,1,114,140,0,0,0,114,3,0,0,0,114,6, + 0,0,0,114,19,1,0,0,167,5,0,0,115,4,0,0, + 0,4,0,2,0,122,38,70,105,108,101,70,105,110,100,101, + 114,46,95,95,105,110,105,116,95,95,46,60,108,111,99,97, + 108,115,62,46,60,103,101,110,101,120,112,114,62,114,71,0, + 0,0,114,105,0,0,0,78,41,7,114,167,0,0,0,218, + 8,95,108,111,97,100,101,114,115,114,44,0,0,0,218,11, + 95,112,97,116,104,95,109,116,105,109,101,218,3,115,101,116, + 218,11,95,112,97,116,104,95,99,97,99,104,101,218,19,95, + 114,101,108,97,120,101,100,95,112,97,116,104,95,99,97,99, + 104,101,41,5,114,119,0,0,0,114,44,0,0,0,218,14, + 108,111,97,100,101,114,95,100,101,116,97,105,108,115,90,7, + 108,111,97,100,101,114,115,114,189,0,0,0,114,3,0,0, + 0,114,84,1,0,0,114,6,0,0,0,114,209,0,0,0, + 161,5,0,0,115,16,0,0,0,0,4,4,1,12,1,26, + 1,6,2,10,1,6,1,8,1,122,19,70,105,108,101,70, + 105,110,100,101,114,46,95,95,105,110,105,116,95,95,99,1, + 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2, + 0,0,0,67,0,0,0,115,10,0,0,0,100,1,124,0, + 95,0,100,2,83,0,41,3,122,31,73,110,118,97,108,105, + 100,97,116,101,32,116,104,101,32,100,105,114,101,99,116,111, + 114,121,32,109,116,105,109,101,46,114,105,0,0,0,78,41, + 1,114,86,1,0,0,114,246,0,0,0,114,3,0,0,0, + 114,3,0,0,0,114,6,0,0,0,114,46,1,0,0,175, + 5,0,0,115,2,0,0,0,0,2,122,28,70,105,108,101, + 70,105,110,100,101,114,46,105,110,118,97,108,105,100,97,116, + 101,95,99,97,99,104,101,115,99,2,0,0,0,0,0,0, + 0,0,0,0,0,3,0,0,0,3,0,0,0,67,0,0, + 0,115,42,0,0,0,124,0,160,0,124,1,161,1,125,2, + 124,2,100,1,107,8,114,26,100,1,103,0,102,2,83,0, + 124,2,106,1,124,2,106,2,112,38,103,0,102,2,83,0, + 41,2,122,197,84,114,121,32,116,111,32,102,105,110,100,32, + 97,32,108,111,97,100,101,114,32,102,111,114,32,116,104,101, + 32,115,112,101,99,105,102,105,101,100,32,109,111,100,117,108, + 101,44,32,111,114,32,116,104,101,32,110,97,109,101,115,112, + 97,99,101,10,32,32,32,32,32,32,32,32,112,97,99,107, + 97,103,101,32,112,111,114,116,105,111,110,115,46,32,82,101, + 116,117,114,110,115,32,40,108,111,97,100,101,114,44,32,108, + 105,115,116,45,111,102,45,112,111,114,116,105,111,110,115,41, + 46,10,10,32,32,32,32,32,32,32,32,84,104,105,115,32, + 109,101,116,104,111,100,32,105,115,32,100,101,112,114,101,99, + 97,116,101,100,46,32,32,85,115,101,32,102,105,110,100,95, + 115,112,101,99,40,41,32,105,110,115,116,101,97,100,46,10, + 10,32,32,32,32,32,32,32,32,78,41,3,114,203,0,0, + 0,114,140,0,0,0,114,178,0,0,0,41,3,114,119,0, + 0,0,114,139,0,0,0,114,187,0,0,0,114,3,0,0, + 0,114,3,0,0,0,114,6,0,0,0,114,137,0,0,0, + 181,5,0,0,115,8,0,0,0,0,7,10,1,8,1,8, + 1,122,22,70,105,108,101,70,105,110,100,101,114,46,102,105, + 110,100,95,108,111,97,100,101,114,99,6,0,0,0,0,0, + 0,0,0,0,0,0,7,0,0,0,6,0,0,0,67,0, + 0,0,115,26,0,0,0,124,1,124,2,124,3,131,2,125, + 6,116,0,124,2,124,3,124,6,124,4,100,1,141,4,83, + 0,41,2,78,114,177,0,0,0,41,1,114,190,0,0,0, + 41,7,114,119,0,0,0,114,188,0,0,0,114,139,0,0, + 0,114,44,0,0,0,90,4,115,109,115,108,114,202,0,0, + 0,114,140,0,0,0,114,3,0,0,0,114,3,0,0,0, + 114,6,0,0,0,114,58,1,0,0,193,5,0,0,115,8, + 0,0,0,0,1,10,1,8,1,2,255,122,20,70,105,108, + 101,70,105,110,100,101,114,46,95,103,101,116,95,115,112,101, + 99,78,99,3,0,0,0,0,0,0,0,0,0,0,0,14, + 0,0,0,8,0,0,0,67,0,0,0,115,98,1,0,0, + 100,1,125,3,124,1,160,0,100,2,161,1,100,3,25,0, + 125,4,122,24,116,1,124,0,106,2,112,34,116,3,160,4, + 161,0,131,1,106,5,125,5,87,0,110,24,4,0,116,6, + 107,10,114,66,1,0,1,0,1,0,100,4,125,5,89,0, + 110,2,88,0,124,5,124,0,106,7,107,3,114,92,124,0, + 160,8,161,0,1,0,124,5,124,0,95,7,116,9,131,0, + 114,114,124,0,106,10,125,6,124,4,160,11,161,0,125,7, + 110,10,124,0,106,12,125,6,124,4,125,7,124,7,124,6, + 107,6,114,218,116,13,124,0,106,2,124,4,131,2,125,8, + 124,0,106,14,68,0,93,58,92,2,125,9,125,10,100,5, + 124,9,23,0,125,11,116,13,124,8,124,11,131,2,125,12, + 116,15,124,12,131,1,114,150,124,0,160,16,124,10,124,1, + 124,12,124,8,103,1,124,2,161,5,2,0,1,0,83,0, + 113,150,116,17,124,8,131,1,125,3,124,0,106,14,68,0, + 93,82,92,2,125,9,125,10,116,13,124,0,106,2,124,4, + 124,9,23,0,131,2,125,12,116,18,106,19,100,6,124,12, + 100,3,100,7,141,3,1,0,124,7,124,9,23,0,124,6, + 107,6,114,224,116,15,124,12,131,1,114,224,124,0,160,16, + 124,10,124,1,124,12,100,8,124,2,161,5,2,0,1,0, + 83,0,113,224,124,3,144,1,114,94,116,18,160,19,100,9, + 124,8,161,2,1,0,116,18,160,20,124,1,100,8,161,2, + 125,13,124,8,103,1,124,13,95,21,124,13,83,0,100,8, + 83,0,41,10,122,111,84,114,121,32,116,111,32,102,105,110, + 100,32,97,32,115,112,101,99,32,102,111,114,32,116,104,101, + 32,115,112,101,99,105,102,105,101,100,32,109,111,100,117,108, + 101,46,10,10,32,32,32,32,32,32,32,32,82,101,116,117, + 114,110,115,32,116,104,101,32,109,97,116,99,104,105,110,103, + 32,115,112,101,99,44,32,111,114,32,78,111,110,101,32,105, + 102,32,110,111,116,32,102,111,117,110,100,46,10,32,32,32, + 32,32,32,32,32,70,114,71,0,0,0,114,28,0,0,0, + 114,105,0,0,0,114,209,0,0,0,122,9,116,114,121,105, + 110,103,32,123,125,41,1,90,9,118,101,114,98,111,115,105, + 116,121,78,122,25,112,111,115,115,105,98,108,101,32,110,97, + 109,101,115,112,97,99,101,32,102,111,114,32,123,125,41,22, + 114,41,0,0,0,114,49,0,0,0,114,44,0,0,0,114, + 2,0,0,0,114,55,0,0,0,114,10,1,0,0,114,50, + 0,0,0,114,86,1,0,0,218,11,95,102,105,108,108,95, + 99,97,99,104,101,114,7,0,0,0,114,89,1,0,0,114, + 106,0,0,0,114,88,1,0,0,114,38,0,0,0,114,85, + 1,0,0,114,54,0,0,0,114,58,1,0,0,114,56,0, + 0,0,114,134,0,0,0,114,149,0,0,0,114,183,0,0, + 0,114,178,0,0,0,41,14,114,119,0,0,0,114,139,0, + 0,0,114,202,0,0,0,90,12,105,115,95,110,97,109,101, + 115,112,97,99,101,90,11,116,97,105,108,95,109,111,100,117, + 108,101,114,169,0,0,0,90,5,99,97,99,104,101,90,12, + 99,97,99,104,101,95,109,111,100,117,108,101,90,9,98,97, + 115,101,95,112,97,116,104,114,17,1,0,0,114,188,0,0, + 0,90,13,105,110,105,116,95,102,105,108,101,110,97,109,101, + 90,9,102,117,108,108,95,112,97,116,104,114,187,0,0,0, + 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, + 203,0,0,0,198,5,0,0,115,74,0,0,0,0,5,4, + 1,14,1,2,1,24,1,14,1,10,1,10,1,8,1,6, + 2,6,1,6,1,10,2,6,1,4,2,8,1,12,1,14, + 1,8,1,10,1,8,1,26,4,8,2,14,1,16,1,16, + 1,12,1,8,1,10,1,2,0,2,255,10,2,6,1,12, + 1,12,1,8,1,4,1,122,20,70,105,108,101,70,105,110, + 100,101,114,46,102,105,110,100,95,115,112,101,99,99,1,0, + 0,0,0,0,0,0,0,0,0,0,9,0,0,0,10,0, + 0,0,67,0,0,0,115,190,0,0,0,124,0,106,0,125, + 1,122,22,116,1,160,2,124,1,112,22,116,1,160,3,161, + 0,161,1,125,2,87,0,110,30,4,0,116,4,116,5,116, + 6,102,3,107,10,114,58,1,0,1,0,1,0,103,0,125, + 2,89,0,110,2,88,0,116,7,106,8,160,9,100,1,161, + 1,115,84,116,10,124,2,131,1,124,0,95,11,110,74,116, + 10,131,0,125,3,124,2,68,0,93,56,125,4,124,4,160, + 12,100,2,161,1,92,3,125,5,125,6,125,7,124,6,114, + 136,100,3,160,13,124,5,124,7,160,14,161,0,161,2,125, + 8,110,4,124,5,125,8,124,3,160,15,124,8,161,1,1, + 0,113,94,124,3,124,0,95,11,116,7,106,8,160,9,116, + 16,161,1,114,186,100,4,100,5,132,0,124,2,68,0,131, + 1,124,0,95,17,100,6,83,0,41,7,122,68,70,105,108, + 108,32,116,104,101,32,99,97,99,104,101,32,111,102,32,112, + 111,116,101,110,116,105,97,108,32,109,111,100,117,108,101,115, + 32,97,110,100,32,112,97,99,107,97,103,101,115,32,102,111, + 114,32,116,104,105,115,32,100,105,114,101,99,116,111,114,121, + 46,114,0,0,0,0,114,71,0,0,0,114,61,0,0,0, + 99,1,0,0,0,0,0,0,0,0,0,0,0,2,0,0, + 0,4,0,0,0,83,0,0,0,115,20,0,0,0,104,0, + 124,0,93,12,125,1,124,1,160,0,161,0,146,2,113,4, + 83,0,114,3,0,0,0,41,1,114,106,0,0,0,41,2, + 114,32,0,0,0,90,2,102,110,114,3,0,0,0,114,3, + 0,0,0,114,6,0,0,0,218,9,60,115,101,116,99,111, + 109,112,62,19,6,0,0,115,4,0,0,0,6,0,2,0, + 122,41,70,105,108,101,70,105,110,100,101,114,46,95,102,105, + 108,108,95,99,97,99,104,101,46,60,108,111,99,97,108,115, + 62,46,60,115,101,116,99,111,109,112,62,78,41,18,114,44, + 0,0,0,114,2,0,0,0,114,7,1,0,0,114,55,0, + 0,0,114,3,1,0,0,218,15,80,101,114,109,105,115,115, + 105,111,110,69,114,114,111,114,218,18,78,111,116,65,68,105, + 114,101,99,116,111,114,121,69,114,114,111,114,114,8,0,0, + 0,114,9,0,0,0,114,10,0,0,0,114,87,1,0,0, + 114,88,1,0,0,114,101,0,0,0,114,62,0,0,0,114, + 106,0,0,0,218,3,97,100,100,114,11,0,0,0,114,89, + 1,0,0,41,9,114,119,0,0,0,114,44,0,0,0,114, + 8,1,0,0,90,21,108,111,119,101,114,95,115,117,102,102, + 105,120,95,99,111,110,116,101,110,116,115,114,41,1,0,0, + 114,117,0,0,0,114,29,1,0,0,114,17,1,0,0,90, + 8,110,101,119,95,110,97,109,101,114,3,0,0,0,114,3, + 0,0,0,114,6,0,0,0,114,91,1,0,0,246,5,0, + 0,115,34,0,0,0,0,2,6,1,2,1,22,1,20,3, + 10,3,12,1,12,7,6,1,8,1,16,1,4,1,18,2, + 4,1,12,1,6,1,12,1,122,22,70,105,108,101,70,105, + 110,100,101,114,46,95,102,105,108,108,95,99,97,99,104,101, + 99,1,0,0,0,0,0,0,0,0,0,0,0,3,0,0, + 0,3,0,0,0,7,0,0,0,115,18,0,0,0,135,0, + 135,1,102,2,100,1,100,2,132,8,125,2,124,2,83,0, + 41,3,97,20,1,0,0,65,32,99,108,97,115,115,32,109, + 101,116,104,111,100,32,119,104,105,99,104,32,114,101,116,117, + 114,110,115,32,97,32,99,108,111,115,117,114,101,32,116,111, + 32,117,115,101,32,111,110,32,115,121,115,46,112,97,116,104, + 95,104,111,111,107,10,32,32,32,32,32,32,32,32,119,104, + 105,99,104,32,119,105,108,108,32,114,101,116,117,114,110,32, + 97,110,32,105,110,115,116,97,110,99,101,32,117,115,105,110, + 103,32,116,104,101,32,115,112,101,99,105,102,105,101,100,32, + 108,111,97,100,101,114,115,32,97,110,100,32,116,104,101,32, + 112,97,116,104,10,32,32,32,32,32,32,32,32,99,97,108, + 108,101,100,32,111,110,32,116,104,101,32,99,108,111,115,117, + 114,101,46,10,10,32,32,32,32,32,32,32,32,73,102,32, + 116,104,101,32,112,97,116,104,32,99,97,108,108,101,100,32, + 111,110,32,116,104,101,32,99,108,111,115,117,114,101,32,105, + 115,32,110,111,116,32,97,32,100,105,114,101,99,116,111,114, + 121,44,32,73,109,112,111,114,116,69,114,114,111,114,32,105, + 115,10,32,32,32,32,32,32,32,32,114,97,105,115,101,100, + 46,10,10,32,32,32,32,32,32,32,32,99,1,0,0,0, + 0,0,0,0,0,0,0,0,1,0,0,0,4,0,0,0, + 19,0,0,0,115,34,0,0,0,116,0,124,0,131,1,115, + 20,116,1,100,1,124,0,100,2,141,2,130,1,136,0,124, + 0,102,1,136,1,158,2,142,0,83,0,41,3,122,45,80, + 97,116,104,32,104,111,111,107,32,102,111,114,32,105,109,112, + 111,114,116,108,105,98,46,109,97,99,104,105,110,101,114,121, + 46,70,105,108,101,70,105,110,100,101,114,46,122,30,111,110, + 108,121,32,100,105,114,101,99,116,111,114,105,101,115,32,97, + 114,101,32,115,117,112,112,111,114,116,101,100,114,48,0,0, + 0,41,2,114,56,0,0,0,114,118,0,0,0,114,48,0, + 0,0,169,2,114,193,0,0,0,114,90,1,0,0,114,3, + 0,0,0,114,6,0,0,0,218,24,112,97,116,104,95,104, + 111,111,107,95,102,111,114,95,70,105,108,101,70,105,110,100, + 101,114,31,6,0,0,115,6,0,0,0,0,2,8,1,12, + 1,122,54,70,105,108,101,70,105,110,100,101,114,46,112,97, + 116,104,95,104,111,111,107,46,60,108,111,99,97,108,115,62, + 46,112,97,116,104,95,104,111,111,107,95,102,111,114,95,70, + 105,108,101,70,105,110,100,101,114,114,3,0,0,0,41,3, + 114,193,0,0,0,114,90,1,0,0,114,97,1,0,0,114, + 3,0,0,0,114,96,1,0,0,114,6,0,0,0,218,9, + 112,97,116,104,95,104,111,111,107,21,6,0,0,115,4,0, + 0,0,0,10,14,6,122,20,70,105,108,101,70,105,110,100, + 101,114,46,112,97,116,104,95,104,111,111,107,99,1,0,0, + 0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0, + 0,67,0,0,0,115,12,0,0,0,100,1,160,0,124,0, + 106,1,161,1,83,0,41,2,78,122,16,70,105,108,101,70, + 105,110,100,101,114,40,123,33,114,125,41,41,2,114,62,0, + 0,0,114,44,0,0,0,114,246,0,0,0,114,3,0,0, + 0,114,3,0,0,0,114,6,0,0,0,114,39,1,0,0, + 39,6,0,0,115,2,0,0,0,0,1,122,19,70,105,108, + 101,70,105,110,100,101,114,46,95,95,114,101,112,114,95,95, + 41,1,78,41,15,114,125,0,0,0,114,124,0,0,0,114, + 126,0,0,0,114,127,0,0,0,114,209,0,0,0,114,46, + 1,0,0,114,143,0,0,0,114,206,0,0,0,114,137,0, + 0,0,114,58,1,0,0,114,203,0,0,0,114,91,1,0, + 0,114,207,0,0,0,114,98,1,0,0,114,39,1,0,0, 114,3,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,218,8,60,109,111,100,117,108,101,62,1,0, - 0,0,115,126,0,0,0,4,22,4,1,4,1,2,1,2, - 255,4,4,8,17,8,5,8,5,8,6,8,6,8,12,8, - 10,8,9,8,5,8,7,8,9,10,22,10,127,0,12,16, - 1,12,2,4,1,4,2,6,2,6,2,8,2,16,71,8, - 40,8,19,8,12,8,12,8,28,8,17,8,33,8,28,8, - 24,10,13,10,10,10,11,8,14,6,3,4,1,2,255,12, - 68,14,64,14,29,16,127,0,17,14,72,18,45,18,26,4, - 3,18,53,14,63,14,42,14,127,0,59,14,127,0,22,10, - 23,8,11,8,65, + 6,0,0,0,114,83,1,0,0,152,5,0,0,115,22,0, + 0,0,8,2,4,7,8,14,8,4,4,2,8,12,8,5, + 10,48,8,31,2,1,10,17,114,83,1,0,0,99,4,0, + 0,0,0,0,0,0,0,0,0,0,6,0,0,0,8,0, + 0,0,67,0,0,0,115,146,0,0,0,124,0,160,0,100, + 1,161,1,125,4,124,0,160,0,100,2,161,1,125,5,124, + 4,115,66,124,5,114,36,124,5,106,1,125,4,110,30,124, + 2,124,3,107,2,114,56,116,2,124,1,124,2,131,2,125, + 4,110,10,116,3,124,1,124,2,131,2,125,4,124,5,115, + 84,116,4,124,1,124,2,124,4,100,3,141,3,125,5,122, + 36,124,5,124,0,100,2,60,0,124,4,124,0,100,1,60, + 0,124,2,124,0,100,4,60,0,124,3,124,0,100,5,60, + 0,87,0,110,20,4,0,116,5,107,10,114,140,1,0,1, + 0,1,0,89,0,110,2,88,0,100,0,83,0,41,6,78, + 218,10,95,95,108,111,97,100,101,114,95,95,218,8,95,95, + 115,112,101,99,95,95,114,84,1,0,0,90,8,95,95,102, + 105,108,101,95,95,90,10,95,95,99,97,99,104,101,100,95, + 95,41,6,218,3,103,101,116,114,140,0,0,0,114,15,1, + 0,0,114,9,1,0,0,114,190,0,0,0,114,72,1,0, + 0,41,6,90,2,110,115,114,117,0,0,0,90,8,112,97, + 116,104,110,97,109,101,90,9,99,112,97,116,104,110,97,109, + 101,114,140,0,0,0,114,187,0,0,0,114,3,0,0,0, + 114,3,0,0,0,114,6,0,0,0,218,14,95,102,105,120, + 95,117,112,95,109,111,100,117,108,101,45,6,0,0,115,34, + 0,0,0,0,2,10,1,10,1,4,1,4,1,8,1,8, + 1,12,2,10,1,4,1,14,1,2,1,8,1,8,1,8, + 1,12,1,14,2,114,102,1,0,0,99,0,0,0,0,0, + 0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,67, + 0,0,0,115,38,0,0,0,116,0,116,1,160,2,161,0, + 102,2,125,0,116,3,116,4,102,2,125,1,116,5,116,6, + 102,2,125,2,124,0,124,1,124,2,103,3,83,0,41,1, + 122,95,82,101,116,117,114,110,115,32,97,32,108,105,115,116, + 32,111,102,32,102,105,108,101,45,98,97,115,101,100,32,109, + 111,100,117,108,101,32,108,111,97,100,101,114,115,46,10,10, + 32,32,32,32,69,97,99,104,32,105,116,101,109,32,105,115, + 32,97,32,116,117,112,108,101,32,40,108,111,97,100,101,114, + 44,32,115,117,102,102,105,120,101,115,41,46,10,32,32,32, + 32,41,7,114,252,0,0,0,114,163,0,0,0,218,18,101, + 120,116,101,110,115,105,111,110,95,115,117,102,102,105,120,101, + 115,114,9,1,0,0,114,102,0,0,0,114,15,1,0,0, + 114,89,0,0,0,41,3,90,10,101,120,116,101,110,115,105, + 111,110,115,90,6,115,111,117,114,99,101,90,8,98,121,116, + 101,99,111,100,101,114,3,0,0,0,114,3,0,0,0,114, + 6,0,0,0,114,184,0,0,0,68,6,0,0,115,8,0, + 0,0,0,5,12,1,8,1,8,1,114,184,0,0,0,99, + 1,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0, + 9,0,0,0,67,0,0,0,115,178,1,0,0,124,0,97, + 0,116,0,106,1,97,1,116,0,106,2,97,2,116,1,106, + 3,116,4,25,0,125,1,100,1,68,0,93,48,125,2,124, + 2,116,1,106,3,107,7,114,56,116,0,160,5,124,2,161, + 1,125,3,110,10,116,1,106,3,124,2,25,0,125,3,116, + 6,124,1,124,2,124,3,131,3,1,0,113,30,100,2,100, + 3,103,1,102,2,100,4,100,5,100,3,103,2,102,2,102, + 2,125,4,124,4,68,0,93,110,92,2,125,5,125,6,116, + 7,100,6,100,7,132,0,124,6,68,0,131,1,131,1,115, + 136,116,8,130,1,124,6,100,8,25,0,125,7,124,5,116, + 1,106,3,107,6,114,170,116,1,106,3,124,5,25,0,125, + 8,1,0,113,226,113,106,122,20,116,0,160,5,124,5,161, + 1,125,8,87,0,1,0,113,226,87,0,113,106,4,0,116, + 9,107,10,114,214,1,0,1,0,1,0,89,0,113,106,89, + 0,113,106,88,0,113,106,116,9,100,9,131,1,130,1,116, + 6,124,1,100,10,124,8,131,3,1,0,116,6,124,1,100, + 11,124,7,131,3,1,0,116,6,124,1,100,12,100,13,160, + 10,124,6,161,1,131,3,1,0,116,6,124,1,100,14,100, + 15,100,16,132,0,124,6,68,0,131,1,131,3,1,0,116, + 0,160,5,100,17,161,1,125,9,116,6,124,1,100,17,124, + 9,131,3,1,0,116,0,160,5,100,18,161,1,125,10,116, + 6,124,1,100,18,124,10,131,3,1,0,124,5,100,4,107, + 2,144,1,114,110,116,0,160,5,100,19,161,1,125,11,116, + 6,124,1,100,20,124,11,131,3,1,0,116,6,124,1,100, + 21,116,11,131,0,131,3,1,0,116,12,160,13,116,2,160, + 14,161,0,161,1,1,0,124,5,100,4,107,2,144,1,114, + 174,116,15,160,16,100,22,161,1,1,0,100,23,116,12,107, + 6,144,1,114,174,100,24,116,17,95,18,100,25,83,0,41, + 26,122,205,83,101,116,117,112,32,116,104,101,32,112,97,116, + 104,45,98,97,115,101,100,32,105,109,112,111,114,116,101,114, + 115,32,102,111,114,32,105,109,112,111,114,116,108,105,98,32, + 98,121,32,105,109,112,111,114,116,105,110,103,32,110,101,101, + 100,101,100,10,32,32,32,32,98,117,105,108,116,45,105,110, + 32,109,111,100,117,108,101,115,32,97,110,100,32,105,110,106, + 101,99,116,105,110,103,32,116,104,101,109,32,105,110,116,111, + 32,116,104,101,32,103,108,111,98,97,108,32,110,97,109,101, + 115,112,97,99,101,46,10,10,32,32,32,32,79,116,104,101, + 114,32,99,111,109,112,111,110,101,110,116,115,32,97,114,101, + 32,101,120,116,114,97,99,116,101,100,32,102,114,111,109,32, + 116,104,101,32,99,111,114,101,32,98,111,111,116,115,116,114, + 97,112,32,109,111,100,117,108,101,46,10,10,32,32,32,32, + 41,4,114,64,0,0,0,114,75,0,0,0,218,8,98,117, + 105,108,116,105,110,115,114,160,0,0,0,90,5,112,111,115, + 105,120,250,1,47,90,2,110,116,250,1,92,99,1,0,0, + 0,0,0,0,0,0,0,0,0,2,0,0,0,3,0,0, + 0,115,0,0,0,115,26,0,0,0,124,0,93,18,125,1, + 116,0,124,1,131,1,100,0,107,2,86,0,1,0,113,2, + 100,1,83,0,41,2,114,39,0,0,0,78,41,1,114,22, + 0,0,0,41,2,114,32,0,0,0,114,95,0,0,0,114, + 3,0,0,0,114,3,0,0,0,114,6,0,0,0,114,19, + 1,0,0,104,6,0,0,115,4,0,0,0,4,0,2,0, + 122,25,95,115,101,116,117,112,46,60,108,111,99,97,108,115, + 62,46,60,103,101,110,101,120,112,114,62,114,73,0,0,0, + 122,30,105,109,112,111,114,116,108,105,98,32,114,101,113,117, + 105,114,101,115,32,112,111,115,105,120,32,111,114,32,110,116, + 114,2,0,0,0,114,35,0,0,0,114,31,0,0,0,114, + 40,0,0,0,114,58,0,0,0,99,1,0,0,0,0,0, + 0,0,0,0,0,0,2,0,0,0,4,0,0,0,83,0, + 0,0,115,22,0,0,0,104,0,124,0,93,14,125,1,100, + 0,124,1,155,0,157,2,146,2,113,4,83,0,41,1,114, + 74,0,0,0,114,3,0,0,0,41,2,114,32,0,0,0, + 218,1,115,114,3,0,0,0,114,3,0,0,0,114,6,0, + 0,0,114,92,1,0,0,120,6,0,0,115,4,0,0,0, + 6,0,2,0,122,25,95,115,101,116,117,112,46,60,108,111, + 99,97,108,115,62,46,60,115,101,116,99,111,109,112,62,90, + 7,95,116,104,114,101,97,100,90,8,95,119,101,97,107,114, + 101,102,90,6,119,105,110,114,101,103,114,192,0,0,0,114, + 7,0,0,0,122,4,46,112,121,119,122,6,95,100,46,112, + 121,100,84,78,41,19,114,134,0,0,0,114,8,0,0,0, + 114,163,0,0,0,114,31,1,0,0,114,125,0,0,0,90, + 18,95,98,117,105,108,116,105,110,95,102,114,111,109,95,110, + 97,109,101,114,129,0,0,0,218,3,97,108,108,114,23,0, + 0,0,114,118,0,0,0,114,36,0,0,0,114,13,0,0, + 0,114,21,1,0,0,114,167,0,0,0,114,103,1,0,0, + 114,102,0,0,0,114,186,0,0,0,114,191,0,0,0,114, + 195,0,0,0,41,12,218,17,95,98,111,111,116,115,116,114, + 97,112,95,109,111,100,117,108,101,90,11,115,101,108,102,95, + 109,111,100,117,108,101,90,12,98,117,105,108,116,105,110,95, + 110,97,109,101,90,14,98,117,105,108,116,105,110,95,109,111, + 100,117,108,101,90,10,111,115,95,100,101,116,97,105,108,115, + 90,10,98,117,105,108,116,105,110,95,111,115,114,31,0,0, + 0,114,35,0,0,0,90,9,111,115,95,109,111,100,117,108, + 101,90,13,116,104,114,101,97,100,95,109,111,100,117,108,101, + 90,14,119,101,97,107,114,101,102,95,109,111,100,117,108,101, + 90,13,119,105,110,114,101,103,95,109,111,100,117,108,101,114, + 3,0,0,0,114,3,0,0,0,114,6,0,0,0,218,6, + 95,115,101,116,117,112,79,6,0,0,115,78,0,0,0,0, + 8,4,1,6,1,6,3,10,1,8,1,10,1,12,2,10, + 1,14,3,22,1,12,2,22,1,8,1,10,1,10,1,6, + 2,2,1,10,1,10,1,14,1,12,2,8,1,12,1,12, + 1,18,1,22,3,10,1,12,3,10,1,12,3,10,1,10, + 1,12,3,14,1,14,1,10,1,10,1,10,1,114,110,1, + 0,0,99,1,0,0,0,0,0,0,0,0,0,0,0,2, + 0,0,0,4,0,0,0,67,0,0,0,115,50,0,0,0, + 116,0,124,0,131,1,1,0,116,1,131,0,125,1,116,2, + 106,3,160,4,116,5,106,6,124,1,142,0,103,1,161,1, + 1,0,116,2,106,7,160,8,116,9,161,1,1,0,100,1, + 83,0,41,2,122,41,73,110,115,116,97,108,108,32,116,104, + 101,32,112,97,116,104,45,98,97,115,101,100,32,105,109,112, + 111,114,116,32,99,111,109,112,111,110,101,110,116,115,46,78, + 41,10,114,110,1,0,0,114,184,0,0,0,114,8,0,0, + 0,114,51,1,0,0,114,167,0,0,0,114,83,1,0,0, + 114,98,1,0,0,218,9,109,101,116,97,95,112,97,116,104, + 114,186,0,0,0,114,45,1,0,0,41,2,114,109,1,0, + 0,90,17,115,117,112,112,111,114,116,101,100,95,108,111,97, + 100,101,114,115,114,3,0,0,0,114,3,0,0,0,114,6, + 0,0,0,218,8,95,105,110,115,116,97,108,108,144,6,0, + 0,115,8,0,0,0,0,2,8,1,6,1,20,1,114,112, + 1,0,0,41,1,114,60,0,0,0,41,1,78,41,3,78, + 78,78,41,2,114,73,0,0,0,114,73,0,0,0,41,1, + 84,41,1,78,41,1,78,41,63,114,127,0,0,0,114,12, + 0,0,0,90,37,95,67,65,83,69,95,73,78,83,69,78, + 83,73,84,73,86,69,95,80,76,65,84,70,79,82,77,83, + 95,66,89,84,69,83,95,75,69,89,114,11,0,0,0,114, + 13,0,0,0,114,20,0,0,0,114,27,0,0,0,114,29, + 0,0,0,114,38,0,0,0,114,47,0,0,0,114,49,0, + 0,0,114,53,0,0,0,114,54,0,0,0,114,56,0,0, + 0,114,59,0,0,0,114,69,0,0,0,218,4,116,121,112, + 101,218,8,95,95,99,111,100,101,95,95,114,162,0,0,0, + 114,18,0,0,0,114,148,0,0,0,114,17,0,0,0,114, + 24,0,0,0,114,236,0,0,0,114,92,0,0,0,114,88, + 0,0,0,114,102,0,0,0,114,89,0,0,0,90,23,68, + 69,66,85,71,95,66,89,84,69,67,79,68,69,95,83,85, + 70,70,73,88,69,83,90,27,79,80,84,73,77,73,90,69, + 68,95,66,89,84,69,67,79,68,69,95,83,85,70,70,73, + 88,69,83,114,98,0,0,0,114,103,0,0,0,114,109,0, + 0,0,114,113,0,0,0,114,115,0,0,0,114,136,0,0, + 0,114,143,0,0,0,114,152,0,0,0,114,156,0,0,0, + 114,158,0,0,0,114,165,0,0,0,114,170,0,0,0,114, + 171,0,0,0,114,176,0,0,0,218,6,111,98,106,101,99, + 116,114,185,0,0,0,114,190,0,0,0,114,191,0,0,0, + 114,208,0,0,0,114,221,0,0,0,114,239,0,0,0,114, + 9,1,0,0,114,15,1,0,0,114,21,1,0,0,114,252, + 0,0,0,114,22,1,0,0,114,43,1,0,0,114,45,1, + 0,0,114,83,1,0,0,114,102,1,0,0,114,184,0,0, + 0,114,110,1,0,0,114,112,1,0,0,114,3,0,0,0, + 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, + 8,60,109,111,100,117,108,101,62,1,0,0,0,115,126,0, + 0,0,4,22,4,1,4,1,2,1,2,255,4,4,8,17, + 8,5,8,5,8,6,8,6,8,12,8,10,8,9,8,5, + 8,7,8,9,10,22,10,127,0,12,16,1,12,2,4,1, + 4,2,6,2,6,2,8,2,16,71,8,40,8,19,8,12, + 8,12,8,28,8,17,8,33,8,28,8,24,10,13,10,10, + 10,11,8,14,6,3,4,1,2,255,12,68,14,64,14,29, + 16,127,0,17,14,72,18,45,18,26,4,3,18,53,14,63, + 14,42,14,127,0,68,14,127,0,22,10,23,8,11,8,65, }; From webhook-mailer at python.org Sun Jul 28 15:45:50 2019 From: webhook-mailer at python.org (Jason R. Coombs) Date: Sun, 28 Jul 2019 19:45:50 -0000 Subject: [Python-checkins] bpo-37697: Sync with importlib_metadata 0.19 (GH-14993) (GH-14995) Message-ID: https://github.com/python/cpython/commit/f96334c17946683dd4fb5a84e86a7a4caa4b487d commit: f96334c17946683dd4fb5a84e86a7a4caa4b487d branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Jason R. Coombs date: 2019-07-28T15:45:46-04:00 summary: bpo-37697: Sync with importlib_metadata 0.19 (GH-14993) (GH-14995) * bpo-37697: Sync with importlib_metadata 0.19 * Run make regen-importlib * ?? Added by blurb_it. (cherry picked from commit 049460da9c7b5f51732e2966195c44713af9dc4c) Co-authored-by: Jason R. Coombs files: A Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst M Lib/importlib/_bootstrap_external.py M Lib/importlib/metadata/__init__.py M Lib/test/test_importlib/fixtures.py M Lib/test/test_importlib/test_main.py M Lib/test/test_importlib/test_zip.py M Python/importlib_external.h diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 5aac048060c2..badd7a74cb90 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1367,8 +1367,6 @@ def find_module(cls, fullname, path=None): return None return spec.loader - search_template = r'(?:{pattern}(-.*)?\.(dist|egg)-info|EGG-INFO)' - @classmethod def find_distributions(cls, name=None, path=None): """ @@ -1400,24 +1398,35 @@ def _search_paths(cls, pattern, paths): def _switch_path(path): from contextlib import suppress import zipfile - from pathlib import Path - with suppress(Exception): - return zipfile.Path(path) - return Path(path) + import pathlib + PYPY_OPEN_BUG = False + if not PYPY_OPEN_BUG or os.path.isfile(path): # pragma: no branch + with suppress(Exception): + return zipfile.Path(path) + return pathlib.Path(path) + + @classmethod + def _matches_info(cls, normalized, item): + import re + template = r'{pattern}(-.*)?\.(dist|egg)-info' + manifest = template.format(pattern=normalized) + return re.match(manifest, item.name, flags=re.IGNORECASE) @classmethod - def _predicate(cls, pattern, root, item): + def _matches_legacy(cls, normalized, item): import re - return re.match(pattern, str(item.name), flags=re.IGNORECASE) + template = r'{pattern}-.*\.egg[\\/]EGG-INFO' + manifest = template.format(pattern=normalized) + return re.search(manifest, str(item), flags=re.IGNORECASE) @classmethod def _search_path(cls, root, pattern): if not root.is_dir(): return () normalized = pattern.replace('-', '_') - matcher = cls.search_template.format(pattern=normalized) return (item for item in root.iterdir() - if cls._predicate(matcher, root, item)) + if cls._matches_info(normalized, item) + or cls._matches_legacy(normalized, item)) class FileFinder: diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py index 944dbb5fdf7e..e80f4fa6409b 100644 --- a/Lib/importlib/metadata/__init__.py +++ b/Lib/importlib/metadata/__init__.py @@ -88,7 +88,7 @@ def _from_config(cls, config): @classmethod def _from_text(cls, text): - config = ConfigParser() + config = ConfigParser(delimiters='=') # case sensitive: https://stackoverflow.com/q/1611799/812183 config.optionxform = str try: diff --git a/Lib/test/test_importlib/fixtures.py b/Lib/test/test_importlib/fixtures.py index 3b926ba26df7..0b4ce18d5a6c 100644 --- a/Lib/test/test_importlib/fixtures.py +++ b/Lib/test/test_importlib/fixtures.py @@ -83,6 +83,7 @@ class DistInfoPkg(OnSysPath, SiteDir): "entry_points.txt": """ [entries] main = mod:main + ns:sub = mod:main """ }, "mod.py": """ diff --git a/Lib/test/test_importlib/test_main.py b/Lib/test/test_importlib/test_main.py index 5e2cb264e28a..bc42b83ee291 100644 --- a/Lib/test/test_importlib/test_main.py +++ b/Lib/test/test_importlib/test_main.py @@ -32,7 +32,7 @@ def test_new_style_classes(self): class ImportTests(fixtures.DistInfoPkg, unittest.TestCase): def test_import_nonexistent_module(self): # Ensure that the MetadataPathFinder does not crash an import of a - # nonexistent module. + # non-existant module. with self.assertRaises(ImportError): importlib.import_module('does_not_exist') @@ -41,6 +41,11 @@ def test_resolve(self): ep = entries['main'] self.assertEqual(ep.load().__name__, "main") + def test_entrypoint_with_colon_in_name(self): + entries = dict(entry_points()['entries']) + ep = entries['ns:sub'] + self.assertEqual(ep.value, 'mod:main') + def test_resolve_without_attr(self): ep = EntryPoint( name='ep', @@ -159,8 +164,16 @@ def test_package_discovery(self): class DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): - def test(self): + def test_egg_info(self): # make an `EGG-INFO` directory that's unrelated self.site_dir.joinpath('EGG-INFO').mkdir() # used to crash with `IsADirectoryError` - self.assertIsNone(version('unknown-package')) + with self.assertRaises(PackageNotFoundError): + version('unknown-package') + + def test_egg(self): + egg = self.site_dir.joinpath('foo-3.6.egg') + egg.mkdir() + with self.add_sys_path(egg): + with self.assertRaises(PackageNotFoundError): + version('foo') diff --git a/Lib/test/test_importlib/test_zip.py b/Lib/test/test_importlib/test_zip.py index bcf7cf3618d7..9568c226af03 100644 --- a/Lib/test/test_importlib/test_zip.py +++ b/Lib/test/test_importlib/test_zip.py @@ -2,7 +2,9 @@ import unittest from contextlib import ExitStack -from importlib.metadata import distribution, entry_points, files, version +from importlib.metadata import ( + distribution, entry_points, files, PackageNotFoundError, version, +) from importlib.resources import path @@ -22,6 +24,10 @@ def setUp(self): def test_zip_version(self): self.assertEqual(version('example'), '21.12') + def test_zip_version_does_not_match(self): + with self.assertRaises(PackageNotFoundError): + version('definitely-not-installed') + def test_zip_entry_points(self): scripts = dict(entry_points()['console_scripts']) entry_point = scripts['example'] diff --git a/Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst b/Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst new file mode 100644 index 000000000000..6c78d7cd17c4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst @@ -0,0 +1 @@ +Syncronize ``importlib.metadata`` with `importlib_metadata 0.19 `_, improving handling of EGG-INFO files and fixing a crash when entry point names contained colons. \ No newline at end of file diff --git a/Python/importlib_external.h b/Python/importlib_external.h index 4a3cb24e37b9..987ba5f33866 100644 --- a/Python/importlib_external.h +++ b/Python/importlib_external.h @@ -2049,789 +2049,806 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = { 0,0,8,1,8,3,2,1,10,8,8,3,8,3,8,3, 8,3,8,3,114,43,1,0,0,99,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,4,0,0,0,64,0, - 0,0,115,172,0,0,0,101,0,90,1,100,0,90,2,100, + 0,0,115,180,0,0,0,101,0,90,1,100,0,90,2,100, 1,90,3,101,4,100,2,100,3,132,0,131,1,90,5,101, 4,100,4,100,5,132,0,131,1,90,6,101,4,100,6,100, 7,132,0,131,1,90,7,101,4,100,8,100,9,132,0,131, - 1,90,8,101,4,100,28,100,11,100,12,132,1,131,1,90, - 9,101,4,100,29,100,13,100,14,132,1,131,1,90,10,101, - 4,100,30,100,15,100,16,132,1,131,1,90,11,100,17,90, - 12,101,4,100,31,100,18,100,19,132,1,131,1,90,13,101, - 4,100,20,100,21,132,0,131,1,90,14,101,15,100,22,100, - 23,132,0,131,1,90,16,101,4,100,24,100,25,132,0,131, - 1,90,17,101,4,100,26,100,27,132,0,131,1,90,18,100, - 10,83,0,41,32,218,10,80,97,116,104,70,105,110,100,101, - 114,122,62,77,101,116,97,32,112,97,116,104,32,102,105,110, - 100,101,114,32,102,111,114,32,115,121,115,46,112,97,116,104, - 32,97,110,100,32,112,97,99,107,97,103,101,32,95,95,112, - 97,116,104,95,95,32,97,116,116,114,105,98,117,116,101,115, - 46,99,1,0,0,0,0,0,0,0,0,0,0,0,3,0, - 0,0,4,0,0,0,67,0,0,0,115,64,0,0,0,116, - 0,116,1,106,2,160,3,161,0,131,1,68,0,93,44,92, - 2,125,1,125,2,124,2,100,1,107,8,114,40,116,1,106, - 2,124,1,61,0,113,14,116,4,124,2,100,2,131,2,114, - 14,124,2,160,5,161,0,1,0,113,14,100,1,83,0,41, - 3,122,125,67,97,108,108,32,116,104,101,32,105,110,118,97, - 108,105,100,97,116,101,95,99,97,99,104,101,115,40,41,32, - 109,101,116,104,111,100,32,111,110,32,97,108,108,32,112,97, - 116,104,32,101,110,116,114,121,32,102,105,110,100,101,114,115, - 10,32,32,32,32,32,32,32,32,115,116,111,114,101,100,32, - 105,110,32,115,121,115,46,112,97,116,104,95,105,109,112,111, - 114,116,101,114,95,99,97,99,104,101,115,32,40,119,104,101, - 114,101,32,105,109,112,108,101,109,101,110,116,101,100,41,46, - 78,218,17,105,110,118,97,108,105,100,97,116,101,95,99,97, - 99,104,101,115,41,6,218,4,108,105,115,116,114,8,0,0, - 0,218,19,112,97,116,104,95,105,109,112,111,114,116,101,114, - 95,99,97,99,104,101,218,5,105,116,101,109,115,114,128,0, - 0,0,114,46,1,0,0,41,3,114,193,0,0,0,114,117, - 0,0,0,218,6,102,105,110,100,101,114,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,46,1,0,0,217, - 4,0,0,115,10,0,0,0,0,4,22,1,8,1,10,1, - 10,1,122,28,80,97,116,104,70,105,110,100,101,114,46,105, - 110,118,97,108,105,100,97,116,101,95,99,97,99,104,101,115, - 99,2,0,0,0,0,0,0,0,0,0,0,0,3,0,0, - 0,9,0,0,0,67,0,0,0,115,84,0,0,0,116,0, - 106,1,100,1,107,9,114,28,116,0,106,1,115,28,116,2, - 160,3,100,2,116,4,161,2,1,0,116,0,106,1,68,0, - 93,44,125,2,122,14,124,2,124,1,131,1,87,0,2,0, - 1,0,83,0,4,0,116,5,107,10,114,76,1,0,1,0, - 1,0,89,0,113,34,89,0,113,34,88,0,113,34,100,1, - 83,0,41,3,122,46,83,101,97,114,99,104,32,115,121,115, - 46,112,97,116,104,95,104,111,111,107,115,32,102,111,114,32, - 97,32,102,105,110,100,101,114,32,102,111,114,32,39,112,97, - 116,104,39,46,78,122,23,115,121,115,46,112,97,116,104,95, - 104,111,111,107,115,32,105,115,32,101,109,112,116,121,41,6, - 114,8,0,0,0,218,10,112,97,116,104,95,104,111,111,107, - 115,114,75,0,0,0,114,76,0,0,0,114,138,0,0,0, - 114,118,0,0,0,41,3,114,193,0,0,0,114,44,0,0, - 0,90,4,104,111,111,107,114,3,0,0,0,114,3,0,0, - 0,114,6,0,0,0,218,11,95,112,97,116,104,95,104,111, - 111,107,115,227,4,0,0,115,16,0,0,0,0,3,16,1, - 12,1,10,1,2,1,14,1,14,1,12,2,122,22,80,97, - 116,104,70,105,110,100,101,114,46,95,112,97,116,104,95,104, - 111,111,107,115,99,2,0,0,0,0,0,0,0,0,0,0, - 0,3,0,0,0,8,0,0,0,67,0,0,0,115,104,0, - 0,0,124,1,100,1,107,2,114,44,122,12,116,0,160,1, - 161,0,125,1,87,0,110,22,4,0,116,2,107,10,114,42, - 1,0,1,0,1,0,89,0,100,2,83,0,88,0,122,14, - 116,3,106,4,124,1,25,0,125,2,87,0,110,40,4,0, - 116,5,107,10,114,98,1,0,1,0,1,0,124,0,160,6, - 124,1,161,1,125,2,124,2,116,3,106,4,124,1,60,0, - 89,0,110,2,88,0,124,2,83,0,41,3,122,210,71,101, - 116,32,116,104,101,32,102,105,110,100,101,114,32,102,111,114, - 32,116,104,101,32,112,97,116,104,32,101,110,116,114,121,32, - 102,114,111,109,32,115,121,115,46,112,97,116,104,95,105,109, - 112,111,114,116,101,114,95,99,97,99,104,101,46,10,10,32, - 32,32,32,32,32,32,32,73,102,32,116,104,101,32,112,97, - 116,104,32,101,110,116,114,121,32,105,115,32,110,111,116,32, - 105,110,32,116,104,101,32,99,97,99,104,101,44,32,102,105, - 110,100,32,116,104,101,32,97,112,112,114,111,112,114,105,97, - 116,101,32,102,105,110,100,101,114,10,32,32,32,32,32,32, - 32,32,97,110,100,32,99,97,99,104,101,32,105,116,46,32, - 73,102,32,110,111,32,102,105,110,100,101,114,32,105,115,32, - 97,118,97,105,108,97,98,108,101,44,32,115,116,111,114,101, - 32,78,111,110,101,46,10,10,32,32,32,32,32,32,32,32, - 114,40,0,0,0,78,41,7,114,2,0,0,0,114,55,0, - 0,0,114,3,1,0,0,114,8,0,0,0,114,48,1,0, - 0,218,8,75,101,121,69,114,114,111,114,114,52,1,0,0, - 41,3,114,193,0,0,0,114,44,0,0,0,114,50,1,0, - 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 218,20,95,112,97,116,104,95,105,109,112,111,114,116,101,114, - 95,99,97,99,104,101,240,4,0,0,115,22,0,0,0,0, - 8,8,1,2,1,12,1,14,3,8,1,2,1,14,1,14, - 1,10,1,16,1,122,31,80,97,116,104,70,105,110,100,101, - 114,46,95,112,97,116,104,95,105,109,112,111,114,116,101,114, - 95,99,97,99,104,101,99,3,0,0,0,0,0,0,0,0, - 0,0,0,6,0,0,0,4,0,0,0,67,0,0,0,115, - 82,0,0,0,116,0,124,2,100,1,131,2,114,26,124,2, - 160,1,124,1,161,1,92,2,125,3,125,4,110,14,124,2, - 160,2,124,1,161,1,125,3,103,0,125,4,124,3,100,0, - 107,9,114,60,116,3,160,4,124,1,124,3,161,2,83,0, - 116,3,160,5,124,1,100,0,161,2,125,5,124,4,124,5, - 95,6,124,5,83,0,41,2,78,114,137,0,0,0,41,7, - 114,128,0,0,0,114,137,0,0,0,114,206,0,0,0,114, - 134,0,0,0,114,201,0,0,0,114,183,0,0,0,114,178, - 0,0,0,41,6,114,193,0,0,0,114,139,0,0,0,114, - 50,1,0,0,114,140,0,0,0,114,141,0,0,0,114,187, - 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, - 0,0,218,16,95,108,101,103,97,99,121,95,103,101,116,95, - 115,112,101,99,6,5,0,0,115,18,0,0,0,0,4,10, - 1,16,2,10,1,4,1,8,1,12,1,12,1,6,1,122, - 27,80,97,116,104,70,105,110,100,101,114,46,95,108,101,103, - 97,99,121,95,103,101,116,95,115,112,101,99,78,99,4,0, - 0,0,0,0,0,0,0,0,0,0,9,0,0,0,5,0, - 0,0,67,0,0,0,115,166,0,0,0,103,0,125,4,124, - 2,68,0,93,134,125,5,116,0,124,5,116,1,116,2,102, - 2,131,2,115,28,113,8,124,0,160,3,124,5,161,1,125, - 6,124,6,100,1,107,9,114,8,116,4,124,6,100,2,131, - 2,114,70,124,6,160,5,124,1,124,3,161,2,125,7,110, - 12,124,0,160,6,124,1,124,6,161,2,125,7,124,7,100, - 1,107,8,114,92,113,8,124,7,106,7,100,1,107,9,114, - 110,124,7,2,0,1,0,83,0,124,7,106,8,125,8,124, - 8,100,1,107,8,114,132,116,9,100,3,131,1,130,1,124, - 4,160,10,124,8,161,1,1,0,113,8,116,11,160,12,124, - 1,100,1,161,2,125,7,124,4,124,7,95,8,124,7,83, - 0,41,4,122,63,70,105,110,100,32,116,104,101,32,108,111, - 97,100,101,114,32,111,114,32,110,97,109,101,115,112,97,99, - 101,95,112,97,116,104,32,102,111,114,32,116,104,105,115,32, - 109,111,100,117,108,101,47,112,97,99,107,97,103,101,32,110, - 97,109,101,46,78,114,203,0,0,0,122,19,115,112,101,99, - 32,109,105,115,115,105,110,103,32,108,111,97,100,101,114,41, - 13,114,161,0,0,0,114,85,0,0,0,218,5,98,121,116, - 101,115,114,54,1,0,0,114,128,0,0,0,114,203,0,0, - 0,114,55,1,0,0,114,140,0,0,0,114,178,0,0,0, - 114,118,0,0,0,114,167,0,0,0,114,134,0,0,0,114, - 183,0,0,0,41,9,114,193,0,0,0,114,139,0,0,0, - 114,44,0,0,0,114,202,0,0,0,218,14,110,97,109,101, - 115,112,97,99,101,95,112,97,116,104,90,5,101,110,116,114, - 121,114,50,1,0,0,114,187,0,0,0,114,141,0,0,0, - 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 9,95,103,101,116,95,115,112,101,99,21,5,0,0,115,40, - 0,0,0,0,5,4,1,8,1,14,1,2,1,10,1,8, - 1,10,1,14,2,12,1,8,1,2,1,10,1,8,1,6, - 1,8,1,8,5,12,2,12,1,6,1,122,20,80,97,116, - 104,70,105,110,100,101,114,46,95,103,101,116,95,115,112,101, - 99,99,4,0,0,0,0,0,0,0,0,0,0,0,6,0, - 0,0,5,0,0,0,67,0,0,0,115,100,0,0,0,124, - 2,100,1,107,8,114,14,116,0,106,1,125,2,124,0,160, - 2,124,1,124,2,124,3,161,3,125,4,124,4,100,1,107, - 8,114,40,100,1,83,0,124,4,106,3,100,1,107,8,114, - 92,124,4,106,4,125,5,124,5,114,86,100,1,124,4,95, - 5,116,6,124,1,124,5,124,0,106,2,131,3,124,4,95, - 4,124,4,83,0,100,1,83,0,110,4,124,4,83,0,100, - 1,83,0,41,2,122,141,84,114,121,32,116,111,32,102,105, - 110,100,32,97,32,115,112,101,99,32,102,111,114,32,39,102, - 117,108,108,110,97,109,101,39,32,111,110,32,115,121,115,46, - 112,97,116,104,32,111,114,32,39,112,97,116,104,39,46,10, - 10,32,32,32,32,32,32,32,32,84,104,101,32,115,101,97, - 114,99,104,32,105,115,32,98,97,115,101,100,32,111,110,32, - 115,121,115,46,112,97,116,104,95,104,111,111,107,115,32,97, - 110,100,32,115,121,115,46,112,97,116,104,95,105,109,112,111, - 114,116,101,114,95,99,97,99,104,101,46,10,32,32,32,32, - 32,32,32,32,78,41,7,114,8,0,0,0,114,44,0,0, - 0,114,58,1,0,0,114,140,0,0,0,114,178,0,0,0, - 114,181,0,0,0,114,22,1,0,0,41,6,114,193,0,0, - 0,114,139,0,0,0,114,44,0,0,0,114,202,0,0,0, - 114,187,0,0,0,114,57,1,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,203,0,0,0,53,5, - 0,0,115,26,0,0,0,0,6,8,1,6,1,14,1,8, - 1,4,1,10,1,6,1,4,3,6,1,16,1,4,2,6, - 2,122,20,80,97,116,104,70,105,110,100,101,114,46,102,105, - 110,100,95,115,112,101,99,99,3,0,0,0,0,0,0,0, - 0,0,0,0,4,0,0,0,4,0,0,0,67,0,0,0, - 115,30,0,0,0,124,0,160,0,124,1,124,2,161,2,125, - 3,124,3,100,1,107,8,114,24,100,1,83,0,124,3,106, - 1,83,0,41,2,122,170,102,105,110,100,32,116,104,101,32, - 109,111,100,117,108,101,32,111,110,32,115,121,115,46,112,97, - 116,104,32,111,114,32,39,112,97,116,104,39,32,98,97,115, - 101,100,32,111,110,32,115,121,115,46,112,97,116,104,95,104, - 111,111,107,115,32,97,110,100,10,32,32,32,32,32,32,32, - 32,115,121,115,46,112,97,116,104,95,105,109,112,111,114,116, - 101,114,95,99,97,99,104,101,46,10,10,32,32,32,32,32, - 32,32,32,84,104,105,115,32,109,101,116,104,111,100,32,105, - 115,32,100,101,112,114,101,99,97,116,101,100,46,32,32,85, - 115,101,32,102,105,110,100,95,115,112,101,99,40,41,32,105, - 110,115,116,101,97,100,46,10,10,32,32,32,32,32,32,32, - 32,78,114,204,0,0,0,114,205,0,0,0,114,3,0,0, - 0,114,3,0,0,0,114,6,0,0,0,114,206,0,0,0, - 77,5,0,0,115,8,0,0,0,0,8,12,1,8,1,4, - 1,122,22,80,97,116,104,70,105,110,100,101,114,46,102,105, - 110,100,95,109,111,100,117,108,101,122,45,40,63,58,123,112, - 97,116,116,101,114,110,125,40,45,46,42,41,63,92,46,40, - 100,105,115,116,124,101,103,103,41,45,105,110,102,111,124,69, - 71,71,45,73,78,70,79,41,99,3,0,0,0,0,0,0, - 0,0,0,0,0,7,0,0,0,4,0,0,0,67,0,0, - 0,115,78,0,0,0,100,1,100,2,108,0,125,3,100,1, - 100,3,108,1,109,2,125,4,1,0,124,2,100,2,107,8, - 114,34,116,3,106,4,125,2,124,1,100,2,107,8,114,46, - 100,4,110,8,124,3,160,5,124,1,161,1,125,5,124,0, - 160,6,124,5,124,2,161,2,125,6,116,7,124,4,124,6, - 131,2,83,0,41,5,97,37,1,0,0,10,32,32,32,32, - 32,32,32,32,70,105,110,100,32,100,105,115,116,114,105,98, - 117,116,105,111,110,115,46,10,10,32,32,32,32,32,32,32, - 32,82,101,116,117,114,110,32,97,110,32,105,116,101,114,97, - 98,108,101,32,111,102,32,97,108,108,32,68,105,115,116,114, - 105,98,117,116,105,111,110,32,105,110,115,116,97,110,99,101, - 115,32,99,97,112,97,98,108,101,32,111,102,10,32,32,32, - 32,32,32,32,32,108,111,97,100,105,110,103,32,116,104,101, - 32,109,101,116,97,100,97,116,97,32,102,111,114,32,112,97, - 99,107,97,103,101,115,32,109,97,116,99,104,105,110,103,32, - 116,104,101,32,96,96,110,97,109,101,96,96,10,32,32,32, - 32,32,32,32,32,40,111,114,32,97,108,108,32,110,97,109, - 101,115,32,105,102,32,110,111,116,32,115,117,112,112,108,105, - 101,100,41,32,97,108,111,110,103,32,116,104,101,32,112,97, - 116,104,115,32,105,110,32,116,104,101,32,108,105,115,116,10, - 32,32,32,32,32,32,32,32,111,102,32,100,105,114,101,99, - 116,111,114,105,101,115,32,96,96,112,97,116,104,96,96,32, - 40,100,101,102,97,117,108,116,115,32,116,111,32,115,121,115, - 46,112,97,116,104,41,46,10,32,32,32,32,32,32,32,32, - 114,73,0,0,0,78,41,1,218,16,80,97,116,104,68,105, - 115,116,114,105,98,117,116,105,111,110,122,2,46,42,41,8, - 218,2,114,101,90,18,105,109,112,111,114,116,108,105,98,46, - 109,101,116,97,100,97,116,97,114,59,1,0,0,114,8,0, - 0,0,114,44,0,0,0,90,6,101,115,99,97,112,101,218, - 13,95,115,101,97,114,99,104,95,112,97,116,104,115,218,3, - 109,97,112,41,7,114,193,0,0,0,114,117,0,0,0,114, - 44,0,0,0,114,60,1,0,0,114,59,1,0,0,218,7, - 112,97,116,116,101,114,110,90,5,102,111,117,110,100,114,3, - 0,0,0,114,3,0,0,0,114,6,0,0,0,218,18,102, - 105,110,100,95,100,105,115,116,114,105,98,117,116,105,111,110, - 115,92,5,0,0,115,14,0,0,0,0,10,8,1,12,1, - 8,1,6,1,22,1,12,1,122,29,80,97,116,104,70,105, - 110,100,101,114,46,102,105,110,100,95,100,105,115,116,114,105, - 98,117,116,105,111,110,115,99,3,0,0,0,0,0,0,0, - 0,0,0,0,4,0,0,0,6,0,0,0,3,0,0,0, - 115,44,0,0,0,100,1,100,2,108,0,125,3,124,3,106, - 1,160,2,135,0,135,1,102,2,100,3,100,4,132,8,116, - 3,136,0,106,4,124,2,131,2,68,0,131,1,161,1,83, - 0,41,5,122,49,70,105,110,100,32,109,101,116,97,100,97, - 116,97,32,100,105,114,101,99,116,111,114,105,101,115,32,105, - 110,32,112,97,116,104,115,32,104,101,117,114,105,115,116,105, - 99,97,108,108,121,46,114,73,0,0,0,78,99,1,0,0, - 0,0,0,0,0,0,0,0,0,2,0,0,0,5,0,0, - 0,51,0,0,0,115,26,0,0,0,124,0,93,18,125,1, - 136,0,160,0,124,1,136,1,161,2,86,0,1,0,113,2, - 100,0,83,0,114,110,0,0,0,41,1,218,12,95,115,101, - 97,114,99,104,95,112,97,116,104,41,2,114,32,0,0,0, - 114,44,0,0,0,169,2,114,193,0,0,0,114,63,1,0, - 0,114,3,0,0,0,114,6,0,0,0,114,19,1,0,0, - 114,5,0,0,115,4,0,0,0,4,2,2,255,122,43,80, - 97,116,104,70,105,110,100,101,114,46,95,115,101,97,114,99, - 104,95,112,97,116,104,115,46,60,108,111,99,97,108,115,62, - 46,60,103,101,110,101,120,112,114,62,41,5,218,9,105,116, - 101,114,116,111,111,108,115,90,5,99,104,97,105,110,90,13, - 102,114,111,109,95,105,116,101,114,97,98,108,101,114,62,1, - 0,0,218,12,95,115,119,105,116,99,104,95,112,97,116,104, - 41,4,114,193,0,0,0,114,63,1,0,0,90,5,112,97, - 116,104,115,114,67,1,0,0,114,3,0,0,0,114,66,1, - 0,0,114,6,0,0,0,114,61,1,0,0,110,5,0,0, - 115,8,0,0,0,0,3,8,1,18,2,10,254,122,24,80, + 1,90,8,101,4,100,29,100,11,100,12,132,1,131,1,90, + 9,101,4,100,30,100,13,100,14,132,1,131,1,90,10,101, + 4,100,31,100,15,100,16,132,1,131,1,90,11,101,4,100, + 32,100,17,100,18,132,1,131,1,90,12,101,4,100,19,100, + 20,132,0,131,1,90,13,101,14,100,21,100,22,132,0,131, + 1,90,15,101,4,100,23,100,24,132,0,131,1,90,16,101, + 4,100,25,100,26,132,0,131,1,90,17,101,4,100,27,100, + 28,132,0,131,1,90,18,100,10,83,0,41,33,218,10,80, + 97,116,104,70,105,110,100,101,114,122,62,77,101,116,97,32, + 112,97,116,104,32,102,105,110,100,101,114,32,102,111,114,32, + 115,121,115,46,112,97,116,104,32,97,110,100,32,112,97,99, + 107,97,103,101,32,95,95,112,97,116,104,95,95,32,97,116, + 116,114,105,98,117,116,101,115,46,99,1,0,0,0,0,0, + 0,0,0,0,0,0,3,0,0,0,4,0,0,0,67,0, + 0,0,115,64,0,0,0,116,0,116,1,106,2,160,3,161, + 0,131,1,68,0,93,44,92,2,125,1,125,2,124,2,100, + 1,107,8,114,40,116,1,106,2,124,1,61,0,113,14,116, + 4,124,2,100,2,131,2,114,14,124,2,160,5,161,0,1, + 0,113,14,100,1,83,0,41,3,122,125,67,97,108,108,32, + 116,104,101,32,105,110,118,97,108,105,100,97,116,101,95,99, + 97,99,104,101,115,40,41,32,109,101,116,104,111,100,32,111, + 110,32,97,108,108,32,112,97,116,104,32,101,110,116,114,121, + 32,102,105,110,100,101,114,115,10,32,32,32,32,32,32,32, + 32,115,116,111,114,101,100,32,105,110,32,115,121,115,46,112, + 97,116,104,95,105,109,112,111,114,116,101,114,95,99,97,99, + 104,101,115,32,40,119,104,101,114,101,32,105,109,112,108,101, + 109,101,110,116,101,100,41,46,78,218,17,105,110,118,97,108, + 105,100,97,116,101,95,99,97,99,104,101,115,41,6,218,4, + 108,105,115,116,114,8,0,0,0,218,19,112,97,116,104,95, + 105,109,112,111,114,116,101,114,95,99,97,99,104,101,218,5, + 105,116,101,109,115,114,128,0,0,0,114,46,1,0,0,41, + 3,114,193,0,0,0,114,117,0,0,0,218,6,102,105,110, + 100,101,114,114,3,0,0,0,114,3,0,0,0,114,6,0, + 0,0,114,46,1,0,0,217,4,0,0,115,10,0,0,0, + 0,4,22,1,8,1,10,1,10,1,122,28,80,97,116,104, + 70,105,110,100,101,114,46,105,110,118,97,108,105,100,97,116, + 101,95,99,97,99,104,101,115,99,2,0,0,0,0,0,0, + 0,0,0,0,0,3,0,0,0,9,0,0,0,67,0,0, + 0,115,84,0,0,0,116,0,106,1,100,1,107,9,114,28, + 116,0,106,1,115,28,116,2,160,3,100,2,116,4,161,2, + 1,0,116,0,106,1,68,0,93,44,125,2,122,14,124,2, + 124,1,131,1,87,0,2,0,1,0,83,0,4,0,116,5, + 107,10,114,76,1,0,1,0,1,0,89,0,113,34,89,0, + 113,34,88,0,113,34,100,1,83,0,41,3,122,46,83,101, + 97,114,99,104,32,115,121,115,46,112,97,116,104,95,104,111, + 111,107,115,32,102,111,114,32,97,32,102,105,110,100,101,114, + 32,102,111,114,32,39,112,97,116,104,39,46,78,122,23,115, + 121,115,46,112,97,116,104,95,104,111,111,107,115,32,105,115, + 32,101,109,112,116,121,41,6,114,8,0,0,0,218,10,112, + 97,116,104,95,104,111,111,107,115,114,75,0,0,0,114,76, + 0,0,0,114,138,0,0,0,114,118,0,0,0,41,3,114, + 193,0,0,0,114,44,0,0,0,90,4,104,111,111,107,114, + 3,0,0,0,114,3,0,0,0,114,6,0,0,0,218,11, + 95,112,97,116,104,95,104,111,111,107,115,227,4,0,0,115, + 16,0,0,0,0,3,16,1,12,1,10,1,2,1,14,1, + 14,1,12,2,122,22,80,97,116,104,70,105,110,100,101,114, + 46,95,112,97,116,104,95,104,111,111,107,115,99,2,0,0, + 0,0,0,0,0,0,0,0,0,3,0,0,0,8,0,0, + 0,67,0,0,0,115,104,0,0,0,124,1,100,1,107,2, + 114,44,122,12,116,0,160,1,161,0,125,1,87,0,110,22, + 4,0,116,2,107,10,114,42,1,0,1,0,1,0,89,0, + 100,2,83,0,88,0,122,14,116,3,106,4,124,1,25,0, + 125,2,87,0,110,40,4,0,116,5,107,10,114,98,1,0, + 1,0,1,0,124,0,160,6,124,1,161,1,125,2,124,2, + 116,3,106,4,124,1,60,0,89,0,110,2,88,0,124,2, + 83,0,41,3,122,210,71,101,116,32,116,104,101,32,102,105, + 110,100,101,114,32,102,111,114,32,116,104,101,32,112,97,116, + 104,32,101,110,116,114,121,32,102,114,111,109,32,115,121,115, + 46,112,97,116,104,95,105,109,112,111,114,116,101,114,95,99, + 97,99,104,101,46,10,10,32,32,32,32,32,32,32,32,73, + 102,32,116,104,101,32,112,97,116,104,32,101,110,116,114,121, + 32,105,115,32,110,111,116,32,105,110,32,116,104,101,32,99, + 97,99,104,101,44,32,102,105,110,100,32,116,104,101,32,97, + 112,112,114,111,112,114,105,97,116,101,32,102,105,110,100,101, + 114,10,32,32,32,32,32,32,32,32,97,110,100,32,99,97, + 99,104,101,32,105,116,46,32,73,102,32,110,111,32,102,105, + 110,100,101,114,32,105,115,32,97,118,97,105,108,97,98,108, + 101,44,32,115,116,111,114,101,32,78,111,110,101,46,10,10, + 32,32,32,32,32,32,32,32,114,40,0,0,0,78,41,7, + 114,2,0,0,0,114,55,0,0,0,114,3,1,0,0,114, + 8,0,0,0,114,48,1,0,0,218,8,75,101,121,69,114, + 114,111,114,114,52,1,0,0,41,3,114,193,0,0,0,114, + 44,0,0,0,114,50,1,0,0,114,3,0,0,0,114,3, + 0,0,0,114,6,0,0,0,218,20,95,112,97,116,104,95, + 105,109,112,111,114,116,101,114,95,99,97,99,104,101,240,4, + 0,0,115,22,0,0,0,0,8,8,1,2,1,12,1,14, + 3,8,1,2,1,14,1,14,1,10,1,16,1,122,31,80, + 97,116,104,70,105,110,100,101,114,46,95,112,97,116,104,95, + 105,109,112,111,114,116,101,114,95,99,97,99,104,101,99,3, + 0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,4, + 0,0,0,67,0,0,0,115,82,0,0,0,116,0,124,2, + 100,1,131,2,114,26,124,2,160,1,124,1,161,1,92,2, + 125,3,125,4,110,14,124,2,160,2,124,1,161,1,125,3, + 103,0,125,4,124,3,100,0,107,9,114,60,116,3,160,4, + 124,1,124,3,161,2,83,0,116,3,160,5,124,1,100,0, + 161,2,125,5,124,4,124,5,95,6,124,5,83,0,41,2, + 78,114,137,0,0,0,41,7,114,128,0,0,0,114,137,0, + 0,0,114,206,0,0,0,114,134,0,0,0,114,201,0,0, + 0,114,183,0,0,0,114,178,0,0,0,41,6,114,193,0, + 0,0,114,139,0,0,0,114,50,1,0,0,114,140,0,0, + 0,114,141,0,0,0,114,187,0,0,0,114,3,0,0,0, + 114,3,0,0,0,114,6,0,0,0,218,16,95,108,101,103, + 97,99,121,95,103,101,116,95,115,112,101,99,6,5,0,0, + 115,18,0,0,0,0,4,10,1,16,2,10,1,4,1,8, + 1,12,1,12,1,6,1,122,27,80,97,116,104,70,105,110, + 100,101,114,46,95,108,101,103,97,99,121,95,103,101,116,95, + 115,112,101,99,78,99,4,0,0,0,0,0,0,0,0,0, + 0,0,9,0,0,0,5,0,0,0,67,0,0,0,115,166, + 0,0,0,103,0,125,4,124,2,68,0,93,134,125,5,116, + 0,124,5,116,1,116,2,102,2,131,2,115,28,113,8,124, + 0,160,3,124,5,161,1,125,6,124,6,100,1,107,9,114, + 8,116,4,124,6,100,2,131,2,114,70,124,6,160,5,124, + 1,124,3,161,2,125,7,110,12,124,0,160,6,124,1,124, + 6,161,2,125,7,124,7,100,1,107,8,114,92,113,8,124, + 7,106,7,100,1,107,9,114,110,124,7,2,0,1,0,83, + 0,124,7,106,8,125,8,124,8,100,1,107,8,114,132,116, + 9,100,3,131,1,130,1,124,4,160,10,124,8,161,1,1, + 0,113,8,116,11,160,12,124,1,100,1,161,2,125,7,124, + 4,124,7,95,8,124,7,83,0,41,4,122,63,70,105,110, + 100,32,116,104,101,32,108,111,97,100,101,114,32,111,114,32, + 110,97,109,101,115,112,97,99,101,95,112,97,116,104,32,102, + 111,114,32,116,104,105,115,32,109,111,100,117,108,101,47,112, + 97,99,107,97,103,101,32,110,97,109,101,46,78,114,203,0, + 0,0,122,19,115,112,101,99,32,109,105,115,115,105,110,103, + 32,108,111,97,100,101,114,41,13,114,161,0,0,0,114,85, + 0,0,0,218,5,98,121,116,101,115,114,54,1,0,0,114, + 128,0,0,0,114,203,0,0,0,114,55,1,0,0,114,140, + 0,0,0,114,178,0,0,0,114,118,0,0,0,114,167,0, + 0,0,114,134,0,0,0,114,183,0,0,0,41,9,114,193, + 0,0,0,114,139,0,0,0,114,44,0,0,0,114,202,0, + 0,0,218,14,110,97,109,101,115,112,97,99,101,95,112,97, + 116,104,90,5,101,110,116,114,121,114,50,1,0,0,114,187, + 0,0,0,114,141,0,0,0,114,3,0,0,0,114,3,0, + 0,0,114,6,0,0,0,218,9,95,103,101,116,95,115,112, + 101,99,21,5,0,0,115,40,0,0,0,0,5,4,1,8, + 1,14,1,2,1,10,1,8,1,10,1,14,2,12,1,8, + 1,2,1,10,1,8,1,6,1,8,1,8,5,12,2,12, + 1,6,1,122,20,80,97,116,104,70,105,110,100,101,114,46, + 95,103,101,116,95,115,112,101,99,99,4,0,0,0,0,0, + 0,0,0,0,0,0,6,0,0,0,5,0,0,0,67,0, + 0,0,115,100,0,0,0,124,2,100,1,107,8,114,14,116, + 0,106,1,125,2,124,0,160,2,124,1,124,2,124,3,161, + 3,125,4,124,4,100,1,107,8,114,40,100,1,83,0,124, + 4,106,3,100,1,107,8,114,92,124,4,106,4,125,5,124, + 5,114,86,100,1,124,4,95,5,116,6,124,1,124,5,124, + 0,106,2,131,3,124,4,95,4,124,4,83,0,100,1,83, + 0,110,4,124,4,83,0,100,1,83,0,41,2,122,141,84, + 114,121,32,116,111,32,102,105,110,100,32,97,32,115,112,101, + 99,32,102,111,114,32,39,102,117,108,108,110,97,109,101,39, + 32,111,110,32,115,121,115,46,112,97,116,104,32,111,114,32, + 39,112,97,116,104,39,46,10,10,32,32,32,32,32,32,32, + 32,84,104,101,32,115,101,97,114,99,104,32,105,115,32,98, + 97,115,101,100,32,111,110,32,115,121,115,46,112,97,116,104, + 95,104,111,111,107,115,32,97,110,100,32,115,121,115,46,112, + 97,116,104,95,105,109,112,111,114,116,101,114,95,99,97,99, + 104,101,46,10,32,32,32,32,32,32,32,32,78,41,7,114, + 8,0,0,0,114,44,0,0,0,114,58,1,0,0,114,140, + 0,0,0,114,178,0,0,0,114,181,0,0,0,114,22,1, + 0,0,41,6,114,193,0,0,0,114,139,0,0,0,114,44, + 0,0,0,114,202,0,0,0,114,187,0,0,0,114,57,1, + 0,0,114,3,0,0,0,114,3,0,0,0,114,6,0,0, + 0,114,203,0,0,0,53,5,0,0,115,26,0,0,0,0, + 6,8,1,6,1,14,1,8,1,4,1,10,1,6,1,4, + 3,6,1,16,1,4,2,6,2,122,20,80,97,116,104,70, + 105,110,100,101,114,46,102,105,110,100,95,115,112,101,99,99, + 3,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0, + 4,0,0,0,67,0,0,0,115,30,0,0,0,124,0,160, + 0,124,1,124,2,161,2,125,3,124,3,100,1,107,8,114, + 24,100,1,83,0,124,3,106,1,83,0,41,2,122,170,102, + 105,110,100,32,116,104,101,32,109,111,100,117,108,101,32,111, + 110,32,115,121,115,46,112,97,116,104,32,111,114,32,39,112, + 97,116,104,39,32,98,97,115,101,100,32,111,110,32,115,121, + 115,46,112,97,116,104,95,104,111,111,107,115,32,97,110,100, + 10,32,32,32,32,32,32,32,32,115,121,115,46,112,97,116, + 104,95,105,109,112,111,114,116,101,114,95,99,97,99,104,101, + 46,10,10,32,32,32,32,32,32,32,32,84,104,105,115,32, + 109,101,116,104,111,100,32,105,115,32,100,101,112,114,101,99, + 97,116,101,100,46,32,32,85,115,101,32,102,105,110,100,95, + 115,112,101,99,40,41,32,105,110,115,116,101,97,100,46,10, + 10,32,32,32,32,32,32,32,32,78,114,204,0,0,0,114, + 205,0,0,0,114,3,0,0,0,114,3,0,0,0,114,6, + 0,0,0,114,206,0,0,0,77,5,0,0,115,8,0,0, + 0,0,8,12,1,8,1,4,1,122,22,80,97,116,104,70, + 105,110,100,101,114,46,102,105,110,100,95,109,111,100,117,108, + 101,99,3,0,0,0,0,0,0,0,0,0,0,0,7,0, + 0,0,4,0,0,0,67,0,0,0,115,78,0,0,0,100, + 1,100,2,108,0,125,3,100,1,100,3,108,1,109,2,125, + 4,1,0,124,2,100,2,107,8,114,34,116,3,106,4,125, + 2,124,1,100,2,107,8,114,46,100,4,110,8,124,3,160, + 5,124,1,161,1,125,5,124,0,160,6,124,5,124,2,161, + 2,125,6,116,7,124,4,124,6,131,2,83,0,41,5,97, + 37,1,0,0,10,32,32,32,32,32,32,32,32,70,105,110, + 100,32,100,105,115,116,114,105,98,117,116,105,111,110,115,46, + 10,10,32,32,32,32,32,32,32,32,82,101,116,117,114,110, + 32,97,110,32,105,116,101,114,97,98,108,101,32,111,102,32, + 97,108,108,32,68,105,115,116,114,105,98,117,116,105,111,110, + 32,105,110,115,116,97,110,99,101,115,32,99,97,112,97,98, + 108,101,32,111,102,10,32,32,32,32,32,32,32,32,108,111, + 97,100,105,110,103,32,116,104,101,32,109,101,116,97,100,97, + 116,97,32,102,111,114,32,112,97,99,107,97,103,101,115,32, + 109,97,116,99,104,105,110,103,32,116,104,101,32,96,96,110, + 97,109,101,96,96,10,32,32,32,32,32,32,32,32,40,111, + 114,32,97,108,108,32,110,97,109,101,115,32,105,102,32,110, + 111,116,32,115,117,112,112,108,105,101,100,41,32,97,108,111, + 110,103,32,116,104,101,32,112,97,116,104,115,32,105,110,32, + 116,104,101,32,108,105,115,116,10,32,32,32,32,32,32,32, + 32,111,102,32,100,105,114,101,99,116,111,114,105,101,115,32, + 96,96,112,97,116,104,96,96,32,40,100,101,102,97,117,108, + 116,115,32,116,111,32,115,121,115,46,112,97,116,104,41,46, + 10,32,32,32,32,32,32,32,32,114,73,0,0,0,78,41, + 1,218,16,80,97,116,104,68,105,115,116,114,105,98,117,116, + 105,111,110,122,2,46,42,41,8,218,2,114,101,90,18,105, + 109,112,111,114,116,108,105,98,46,109,101,116,97,100,97,116, + 97,114,59,1,0,0,114,8,0,0,0,114,44,0,0,0, + 90,6,101,115,99,97,112,101,218,13,95,115,101,97,114,99, + 104,95,112,97,116,104,115,218,3,109,97,112,41,7,114,193, + 0,0,0,114,117,0,0,0,114,44,0,0,0,114,60,1, + 0,0,114,59,1,0,0,218,7,112,97,116,116,101,114,110, + 90,5,102,111,117,110,100,114,3,0,0,0,114,3,0,0, + 0,114,6,0,0,0,218,18,102,105,110,100,95,100,105,115, + 116,114,105,98,117,116,105,111,110,115,90,5,0,0,115,14, + 0,0,0,0,10,8,1,12,1,8,1,6,1,22,1,12, + 1,122,29,80,97,116,104,70,105,110,100,101,114,46,102,105, + 110,100,95,100,105,115,116,114,105,98,117,116,105,111,110,115, + 99,3,0,0,0,0,0,0,0,0,0,0,0,4,0,0, + 0,6,0,0,0,3,0,0,0,115,44,0,0,0,100,1, + 100,2,108,0,125,3,124,3,106,1,160,2,135,0,135,1, + 102,2,100,3,100,4,132,8,116,3,136,0,106,4,124,2, + 131,2,68,0,131,1,161,1,83,0,41,5,122,49,70,105, + 110,100,32,109,101,116,97,100,97,116,97,32,100,105,114,101, + 99,116,111,114,105,101,115,32,105,110,32,112,97,116,104,115, + 32,104,101,117,114,105,115,116,105,99,97,108,108,121,46,114, + 73,0,0,0,78,99,1,0,0,0,0,0,0,0,0,0, + 0,0,2,0,0,0,5,0,0,0,51,0,0,0,115,26, + 0,0,0,124,0,93,18,125,1,136,0,160,0,124,1,136, + 1,161,2,86,0,1,0,113,2,100,0,83,0,114,110,0, + 0,0,41,1,218,12,95,115,101,97,114,99,104,95,112,97, + 116,104,41,2,114,32,0,0,0,114,44,0,0,0,169,2, + 114,193,0,0,0,114,63,1,0,0,114,3,0,0,0,114, + 6,0,0,0,114,19,1,0,0,112,5,0,0,115,4,0, + 0,0,4,2,2,255,122,43,80,97,116,104,70,105,110,100, + 101,114,46,95,115,101,97,114,99,104,95,112,97,116,104,115, + 46,60,108,111,99,97,108,115,62,46,60,103,101,110,101,120, + 112,114,62,41,5,218,9,105,116,101,114,116,111,111,108,115, + 90,5,99,104,97,105,110,90,13,102,114,111,109,95,105,116, + 101,114,97,98,108,101,114,62,1,0,0,218,12,95,115,119, + 105,116,99,104,95,112,97,116,104,41,4,114,193,0,0,0, + 114,63,1,0,0,90,5,112,97,116,104,115,114,67,1,0, + 0,114,3,0,0,0,114,66,1,0,0,114,6,0,0,0, + 114,61,1,0,0,108,5,0,0,115,8,0,0,0,0,3, + 8,1,18,2,10,254,122,24,80,97,116,104,70,105,110,100, + 101,114,46,95,115,101,97,114,99,104,95,112,97,116,104,115, + 99,1,0,0,0,0,0,0,0,0,0,0,0,5,0,0, + 0,10,0,0,0,67,0,0,0,115,96,0,0,0,100,1, + 100,2,108,0,109,1,125,1,1,0,100,1,100,0,108,2, + 125,2,100,1,100,0,108,3,125,3,100,3,125,4,124,4, + 114,48,116,4,106,5,160,6,124,0,161,1,114,86,124,1, + 116,7,131,1,143,24,1,0,124,2,160,8,124,0,161,1, + 87,0,2,0,53,0,81,0,82,0,163,0,83,0,81,0, + 82,0,88,0,124,3,160,8,124,0,161,1,83,0,41,4, + 78,114,73,0,0,0,41,1,218,8,115,117,112,112,114,101, + 115,115,70,41,9,90,10,99,111,110,116,101,120,116,108,105, + 98,114,69,1,0,0,218,7,122,105,112,102,105,108,101,218, + 7,112,97,116,104,108,105,98,90,2,111,115,114,44,0,0, + 0,90,6,105,115,102,105,108,101,218,9,69,120,99,101,112, + 116,105,111,110,90,4,80,97,116,104,41,5,114,44,0,0, + 0,114,69,1,0,0,114,70,1,0,0,114,71,1,0,0, + 90,13,80,89,80,89,95,79,80,69,78,95,66,85,71,114, + 3,0,0,0,114,3,0,0,0,114,6,0,0,0,114,68, + 1,0,0,117,5,0,0,115,16,0,0,0,0,2,12,1, + 8,1,8,1,4,1,16,1,10,1,28,1,122,23,80,97, + 116,104,70,105,110,100,101,114,46,95,115,119,105,116,99,104, + 95,112,97,116,104,99,3,0,0,0,0,0,0,0,0,0, + 0,0,6,0,0,0,5,0,0,0,67,0,0,0,115,44, + 0,0,0,100,1,100,0,108,0,125,3,100,2,125,4,124, + 4,106,1,124,1,100,3,141,1,125,5,124,3,106,2,124, + 5,124,2,106,3,124,3,106,4,100,4,141,3,83,0,41, + 5,78,114,73,0,0,0,122,32,123,112,97,116,116,101,114, + 110,125,40,45,46,42,41,63,92,46,40,100,105,115,116,124, + 101,103,103,41,45,105,110,102,111,169,1,114,63,1,0,0, + 169,1,114,83,0,0,0,41,5,114,60,1,0,0,114,62, + 0,0,0,90,5,109,97,116,99,104,114,117,0,0,0,218, + 10,73,71,78,79,82,69,67,65,83,69,169,6,114,193,0, + 0,0,218,10,110,111,114,109,97,108,105,122,101,100,114,41, + 1,0,0,114,60,1,0,0,90,8,116,101,109,112,108,97, + 116,101,90,8,109,97,110,105,102,101,115,116,114,3,0,0, + 0,114,3,0,0,0,114,6,0,0,0,218,13,95,109,97, + 116,99,104,101,115,95,105,110,102,111,128,5,0,0,115,8, + 0,0,0,0,2,8,1,4,1,12,1,122,24,80,97,116, + 104,70,105,110,100,101,114,46,95,109,97,116,99,104,101,115, + 95,105,110,102,111,99,3,0,0,0,0,0,0,0,0,0, + 0,0,6,0,0,0,5,0,0,0,67,0,0,0,115,46, + 0,0,0,100,1,100,0,108,0,125,3,100,2,125,4,124, + 4,106,1,124,1,100,3,141,1,125,5,124,3,106,2,124, + 5,116,3,124,2,131,1,124,3,106,4,100,4,141,3,83, + 0,41,5,78,114,73,0,0,0,122,30,123,112,97,116,116, + 101,114,110,125,45,46,42,92,46,101,103,103,91,92,92,47, + 93,69,71,71,45,73,78,70,79,114,73,1,0,0,114,74, + 1,0,0,41,5,114,60,1,0,0,114,62,0,0,0,90, + 6,115,101,97,114,99,104,114,85,0,0,0,114,75,1,0, + 0,114,76,1,0,0,114,3,0,0,0,114,3,0,0,0, + 114,6,0,0,0,218,15,95,109,97,116,99,104,101,115,95, + 108,101,103,97,99,121,135,5,0,0,115,8,0,0,0,0, + 2,8,1,4,1,12,1,122,26,80,97,116,104,70,105,110, + 100,101,114,46,95,109,97,116,99,104,101,115,95,108,101,103, + 97,99,121,99,3,0,0,0,0,0,0,0,0,0,0,0, + 3,0,0,0,4,0,0,0,3,0,0,0,115,48,0,0, + 0,124,1,160,0,161,0,115,12,100,1,83,0,124,2,160, + 1,100,2,100,3,161,2,137,1,135,0,135,1,102,2,100, + 4,100,5,132,8,124,1,160,2,161,0,68,0,131,1,83, + 0,41,6,78,114,3,0,0,0,250,1,45,114,45,0,0, + 0,99,1,0,0,0,0,0,0,0,0,0,0,0,2,0, + 0,0,5,0,0,0,51,0,0,0,115,42,0,0,0,124, + 0,93,34,125,1,136,0,160,0,136,1,124,1,161,2,115, + 30,136,0,160,1,136,1,124,1,161,2,114,2,124,1,86, + 0,1,0,113,2,100,0,83,0,114,110,0,0,0,41,2, + 114,78,1,0,0,114,79,1,0,0,41,2,114,32,0,0, + 0,114,41,1,0,0,169,2,114,193,0,0,0,114,77,1, + 0,0,114,3,0,0,0,114,6,0,0,0,114,19,1,0, + 0,147,5,0,0,115,8,0,0,0,4,0,2,1,12,1, + 12,254,122,42,80,97,116,104,70,105,110,100,101,114,46,95, + 115,101,97,114,99,104,95,112,97,116,104,46,60,108,111,99, + 97,108,115,62,46,60,103,101,110,101,120,112,114,62,41,3, + 90,6,105,115,95,100,105,114,114,67,0,0,0,90,7,105, + 116,101,114,100,105,114,41,3,114,193,0,0,0,90,4,114, + 111,111,116,114,63,1,0,0,114,3,0,0,0,114,81,1, + 0,0,114,6,0,0,0,114,65,1,0,0,142,5,0,0, + 115,8,0,0,0,0,2,8,1,4,1,12,1,122,23,80, 97,116,104,70,105,110,100,101,114,46,95,115,101,97,114,99, - 104,95,112,97,116,104,115,99,1,0,0,0,0,0,0,0, - 0,0,0,0,4,0,0,0,10,0,0,0,67,0,0,0, - 115,78,0,0,0,100,1,100,2,108,0,109,1,125,1,1, - 0,100,1,100,0,108,2,125,2,100,1,100,3,108,3,109, - 4,125,3,1,0,124,1,116,5,131,1,143,24,1,0,124, - 2,160,4,124,0,161,1,87,0,2,0,53,0,81,0,82, - 0,163,0,83,0,81,0,82,0,88,0,124,3,124,0,131, - 1,83,0,41,4,78,114,73,0,0,0,41,1,218,8,115, - 117,112,112,114,101,115,115,41,1,218,4,80,97,116,104,41, - 6,90,10,99,111,110,116,101,120,116,108,105,98,114,69,1, - 0,0,218,7,122,105,112,102,105,108,101,90,7,112,97,116, - 104,108,105,98,114,70,1,0,0,218,9,69,120,99,101,112, - 116,105,111,110,41,4,114,44,0,0,0,114,69,1,0,0, - 114,71,1,0,0,114,70,1,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,68,1,0,0,119,5, - 0,0,115,12,0,0,0,0,2,12,1,8,1,12,1,10, - 1,28,1,122,23,80,97,116,104,70,105,110,100,101,114,46, - 95,115,119,105,116,99,104,95,112,97,116,104,99,4,0,0, - 0,0,0,0,0,0,0,0,0,5,0,0,0,5,0,0, - 0,67,0,0,0,115,32,0,0,0,100,1,100,0,108,0, - 125,4,124,4,106,1,124,1,116,2,124,3,106,3,131,1, - 124,4,106,4,100,2,141,3,83,0,41,3,78,114,73,0, - 0,0,41,1,114,83,0,0,0,41,5,114,60,1,0,0, - 90,5,109,97,116,99,104,114,85,0,0,0,114,117,0,0, - 0,90,10,73,71,78,79,82,69,67,65,83,69,41,5,114, - 193,0,0,0,114,63,1,0,0,218,4,114,111,111,116,114, - 41,1,0,0,114,60,1,0,0,114,3,0,0,0,114,3, - 0,0,0,114,6,0,0,0,218,10,95,112,114,101,100,105, - 99,97,116,101,128,5,0,0,115,4,0,0,0,0,2,8, - 1,122,21,80,97,116,104,70,105,110,100,101,114,46,95,112, - 114,101,100,105,99,97,116,101,99,3,0,0,0,0,0,0, - 0,0,0,0,0,4,0,0,0,4,0,0,0,3,0,0, - 0,115,64,0,0,0,136,2,160,0,161,0,115,12,100,1, - 83,0,124,2,160,1,100,2,100,3,161,2,125,3,136,0, - 106,2,106,3,124,3,100,4,141,1,137,1,135,0,135,1, - 135,2,102,3,100,5,100,6,132,8,136,2,160,4,161,0, - 68,0,131,1,83,0,41,7,78,114,3,0,0,0,250,1, - 45,114,45,0,0,0,41,1,114,63,1,0,0,99,1,0, - 0,0,0,0,0,0,0,0,0,0,2,0,0,0,6,0, - 0,0,51,0,0,0,115,32,0,0,0,124,0,93,24,125, - 1,136,0,160,0,136,1,136,2,124,1,161,3,114,2,124, - 1,86,0,1,0,113,2,100,0,83,0,114,110,0,0,0, - 41,1,114,74,1,0,0,41,2,114,32,0,0,0,114,41, - 1,0,0,169,3,114,193,0,0,0,90,7,109,97,116,99, - 104,101,114,114,73,1,0,0,114,3,0,0,0,114,6,0, - 0,0,114,19,1,0,0,139,5,0,0,115,6,0,0,0, - 4,0,2,1,14,255,122,42,80,97,116,104,70,105,110,100, - 101,114,46,95,115,101,97,114,99,104,95,112,97,116,104,46, - 60,108,111,99,97,108,115,62,46,60,103,101,110,101,120,112, - 114,62,41,5,90,6,105,115,95,100,105,114,114,67,0,0, - 0,218,15,115,101,97,114,99,104,95,116,101,109,112,108,97, - 116,101,114,62,0,0,0,90,7,105,116,101,114,100,105,114, - 41,4,114,193,0,0,0,114,73,1,0,0,114,63,1,0, - 0,90,10,110,111,114,109,97,108,105,122,101,100,114,3,0, - 0,0,114,76,1,0,0,114,6,0,0,0,114,65,1,0, - 0,133,5,0,0,115,10,0,0,0,0,2,8,1,4,1, - 12,1,14,1,122,23,80,97,116,104,70,105,110,100,101,114, - 46,95,115,101,97,114,99,104,95,112,97,116,104,41,1,78, - 41,2,78,78,41,1,78,41,2,78,78,41,19,114,125,0, - 0,0,114,124,0,0,0,114,126,0,0,0,114,127,0,0, - 0,114,207,0,0,0,114,46,1,0,0,114,52,1,0,0, - 114,54,1,0,0,114,55,1,0,0,114,58,1,0,0,114, - 203,0,0,0,114,206,0,0,0,114,77,1,0,0,114,64, - 1,0,0,114,61,1,0,0,218,12,115,116,97,116,105,99, - 109,101,116,104,111,100,114,68,1,0,0,114,74,1,0,0, - 114,65,1,0,0,114,3,0,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,45,1,0,0,213,4, - 0,0,115,52,0,0,0,8,2,4,2,2,1,10,9,2, - 1,10,12,2,1,10,21,2,1,10,14,2,1,12,31,2, - 1,12,23,2,1,12,12,4,2,2,1,12,17,2,1,10, - 8,2,1,10,8,2,1,10,4,2,1,114,45,1,0,0, - 99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,3,0,0,0,64,0,0,0,115,90,0,0,0,101,0, - 90,1,100,0,90,2,100,1,90,3,100,2,100,3,132,0, - 90,4,100,4,100,5,132,0,90,5,101,6,90,7,100,6, - 100,7,132,0,90,8,100,8,100,9,132,0,90,9,100,19, - 100,11,100,12,132,1,90,10,100,13,100,14,132,0,90,11, - 101,12,100,15,100,16,132,0,131,1,90,13,100,17,100,18, - 132,0,90,14,100,10,83,0,41,20,218,10,70,105,108,101, - 70,105,110,100,101,114,122,172,70,105,108,101,45,98,97,115, - 101,100,32,102,105,110,100,101,114,46,10,10,32,32,32,32, - 73,110,116,101,114,97,99,116,105,111,110,115,32,119,105,116, - 104,32,116,104,101,32,102,105,108,101,32,115,121,115,116,101, - 109,32,97,114,101,32,99,97,99,104,101,100,32,102,111,114, - 32,112,101,114,102,111,114,109,97,110,99,101,44,32,98,101, - 105,110,103,10,32,32,32,32,114,101,102,114,101,115,104,101, - 100,32,119,104,101,110,32,116,104,101,32,100,105,114,101,99, - 116,111,114,121,32,116,104,101,32,102,105,110,100,101,114,32, - 105,115,32,104,97,110,100,108,105,110,103,32,104,97,115,32, - 98,101,101,110,32,109,111,100,105,102,105,101,100,46,10,10, - 32,32,32,32,99,2,0,0,0,0,0,0,0,0,0,0, - 0,5,0,0,0,6,0,0,0,7,0,0,0,115,84,0, - 0,0,103,0,125,3,124,2,68,0,93,32,92,2,137,0, - 125,4,124,3,160,0,135,0,102,1,100,1,100,2,132,8, - 124,4,68,0,131,1,161,1,1,0,113,8,124,3,124,0, - 95,1,124,1,112,54,100,3,124,0,95,2,100,4,124,0, - 95,3,116,4,131,0,124,0,95,5,116,4,131,0,124,0, - 95,6,100,5,83,0,41,6,122,154,73,110,105,116,105,97, - 108,105,122,101,32,119,105,116,104,32,116,104,101,32,112,97, - 116,104,32,116,111,32,115,101,97,114,99,104,32,111,110,32, - 97,110,100,32,97,32,118,97,114,105,97,98,108,101,32,110, - 117,109,98,101,114,32,111,102,10,32,32,32,32,32,32,32, - 32,50,45,116,117,112,108,101,115,32,99,111,110,116,97,105, - 110,105,110,103,32,116,104,101,32,108,111,97,100,101,114,32, - 97,110,100,32,116,104,101,32,102,105,108,101,32,115,117,102, - 102,105,120,101,115,32,116,104,101,32,108,111,97,100,101,114, - 10,32,32,32,32,32,32,32,32,114,101,99,111,103,110,105, - 122,101,115,46,99,1,0,0,0,0,0,0,0,0,0,0, - 0,2,0,0,0,3,0,0,0,51,0,0,0,115,22,0, - 0,0,124,0,93,14,125,1,124,1,136,0,102,2,86,0, - 1,0,113,2,100,0,83,0,114,110,0,0,0,114,3,0, - 0,0,114,16,1,0,0,169,1,114,140,0,0,0,114,3, - 0,0,0,114,6,0,0,0,114,19,1,0,0,158,5,0, - 0,115,4,0,0,0,4,0,2,0,122,38,70,105,108,101, - 70,105,110,100,101,114,46,95,95,105,110,105,116,95,95,46, - 60,108,111,99,97,108,115,62,46,60,103,101,110,101,120,112, - 114,62,114,71,0,0,0,114,105,0,0,0,78,41,7,114, - 167,0,0,0,218,8,95,108,111,97,100,101,114,115,114,44, - 0,0,0,218,11,95,112,97,116,104,95,109,116,105,109,101, - 218,3,115,101,116,218,11,95,112,97,116,104,95,99,97,99, - 104,101,218,19,95,114,101,108,97,120,101,100,95,112,97,116, - 104,95,99,97,99,104,101,41,5,114,119,0,0,0,114,44, - 0,0,0,218,14,108,111,97,100,101,114,95,100,101,116,97, - 105,108,115,90,7,108,111,97,100,101,114,115,114,189,0,0, - 0,114,3,0,0,0,114,80,1,0,0,114,6,0,0,0, - 114,209,0,0,0,152,5,0,0,115,16,0,0,0,0,4, - 4,1,12,1,26,1,6,2,10,1,6,1,8,1,122,19, - 70,105,108,101,70,105,110,100,101,114,46,95,95,105,110,105, - 116,95,95,99,1,0,0,0,0,0,0,0,0,0,0,0, - 1,0,0,0,2,0,0,0,67,0,0,0,115,10,0,0, - 0,100,1,124,0,95,0,100,2,83,0,41,3,122,31,73, - 110,118,97,108,105,100,97,116,101,32,116,104,101,32,100,105, - 114,101,99,116,111,114,121,32,109,116,105,109,101,46,114,105, - 0,0,0,78,41,1,114,82,1,0,0,114,246,0,0,0, - 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, - 46,1,0,0,166,5,0,0,115,2,0,0,0,0,2,122, - 28,70,105,108,101,70,105,110,100,101,114,46,105,110,118,97, - 108,105,100,97,116,101,95,99,97,99,104,101,115,99,2,0, - 0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0, - 0,0,67,0,0,0,115,42,0,0,0,124,0,160,0,124, - 1,161,1,125,2,124,2,100,1,107,8,114,26,100,1,103, - 0,102,2,83,0,124,2,106,1,124,2,106,2,112,38,103, - 0,102,2,83,0,41,2,122,197,84,114,121,32,116,111,32, - 102,105,110,100,32,97,32,108,111,97,100,101,114,32,102,111, - 114,32,116,104,101,32,115,112,101,99,105,102,105,101,100,32, - 109,111,100,117,108,101,44,32,111,114,32,116,104,101,32,110, - 97,109,101,115,112,97,99,101,10,32,32,32,32,32,32,32, - 32,112,97,99,107,97,103,101,32,112,111,114,116,105,111,110, - 115,46,32,82,101,116,117,114,110,115,32,40,108,111,97,100, - 101,114,44,32,108,105,115,116,45,111,102,45,112,111,114,116, - 105,111,110,115,41,46,10,10,32,32,32,32,32,32,32,32, - 84,104,105,115,32,109,101,116,104,111,100,32,105,115,32,100, - 101,112,114,101,99,97,116,101,100,46,32,32,85,115,101,32, - 102,105,110,100,95,115,112,101,99,40,41,32,105,110,115,116, - 101,97,100,46,10,10,32,32,32,32,32,32,32,32,78,41, - 3,114,203,0,0,0,114,140,0,0,0,114,178,0,0,0, - 41,3,114,119,0,0,0,114,139,0,0,0,114,187,0,0, - 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,137,0,0,0,172,5,0,0,115,8,0,0,0,0,7, - 10,1,8,1,8,1,122,22,70,105,108,101,70,105,110,100, - 101,114,46,102,105,110,100,95,108,111,97,100,101,114,99,6, - 0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,6, - 0,0,0,67,0,0,0,115,26,0,0,0,124,1,124,2, - 124,3,131,2,125,6,116,0,124,2,124,3,124,6,124,4, - 100,1,141,4,83,0,41,2,78,114,177,0,0,0,41,1, - 114,190,0,0,0,41,7,114,119,0,0,0,114,188,0,0, - 0,114,139,0,0,0,114,44,0,0,0,90,4,115,109,115, - 108,114,202,0,0,0,114,140,0,0,0,114,3,0,0,0, - 114,3,0,0,0,114,6,0,0,0,114,58,1,0,0,184, - 5,0,0,115,8,0,0,0,0,1,10,1,8,1,2,255, - 122,20,70,105,108,101,70,105,110,100,101,114,46,95,103,101, - 116,95,115,112,101,99,78,99,3,0,0,0,0,0,0,0, - 0,0,0,0,14,0,0,0,8,0,0,0,67,0,0,0, - 115,98,1,0,0,100,1,125,3,124,1,160,0,100,2,161, - 1,100,3,25,0,125,4,122,24,116,1,124,0,106,2,112, - 34,116,3,160,4,161,0,131,1,106,5,125,5,87,0,110, - 24,4,0,116,6,107,10,114,66,1,0,1,0,1,0,100, - 4,125,5,89,0,110,2,88,0,124,5,124,0,106,7,107, - 3,114,92,124,0,160,8,161,0,1,0,124,5,124,0,95, - 7,116,9,131,0,114,114,124,0,106,10,125,6,124,4,160, - 11,161,0,125,7,110,10,124,0,106,12,125,6,124,4,125, - 7,124,7,124,6,107,6,114,218,116,13,124,0,106,2,124, - 4,131,2,125,8,124,0,106,14,68,0,93,58,92,2,125, - 9,125,10,100,5,124,9,23,0,125,11,116,13,124,8,124, - 11,131,2,125,12,116,15,124,12,131,1,114,150,124,0,160, - 16,124,10,124,1,124,12,124,8,103,1,124,2,161,5,2, - 0,1,0,83,0,113,150,116,17,124,8,131,1,125,3,124, - 0,106,14,68,0,93,82,92,2,125,9,125,10,116,13,124, - 0,106,2,124,4,124,9,23,0,131,2,125,12,116,18,106, - 19,100,6,124,12,100,3,100,7,141,3,1,0,124,7,124, - 9,23,0,124,6,107,6,114,224,116,15,124,12,131,1,114, - 224,124,0,160,16,124,10,124,1,124,12,100,8,124,2,161, - 5,2,0,1,0,83,0,113,224,124,3,144,1,114,94,116, - 18,160,19,100,9,124,8,161,2,1,0,116,18,160,20,124, - 1,100,8,161,2,125,13,124,8,103,1,124,13,95,21,124, - 13,83,0,100,8,83,0,41,10,122,111,84,114,121,32,116, - 111,32,102,105,110,100,32,97,32,115,112,101,99,32,102,111, - 114,32,116,104,101,32,115,112,101,99,105,102,105,101,100,32, - 109,111,100,117,108,101,46,10,10,32,32,32,32,32,32,32, - 32,82,101,116,117,114,110,115,32,116,104,101,32,109,97,116, - 99,104,105,110,103,32,115,112,101,99,44,32,111,114,32,78, - 111,110,101,32,105,102,32,110,111,116,32,102,111,117,110,100, - 46,10,32,32,32,32,32,32,32,32,70,114,71,0,0,0, - 114,28,0,0,0,114,105,0,0,0,114,209,0,0,0,122, - 9,116,114,121,105,110,103,32,123,125,41,1,90,9,118,101, - 114,98,111,115,105,116,121,78,122,25,112,111,115,115,105,98, - 108,101,32,110,97,109,101,115,112,97,99,101,32,102,111,114, - 32,123,125,41,22,114,41,0,0,0,114,49,0,0,0,114, - 44,0,0,0,114,2,0,0,0,114,55,0,0,0,114,10, - 1,0,0,114,50,0,0,0,114,82,1,0,0,218,11,95, - 102,105,108,108,95,99,97,99,104,101,114,7,0,0,0,114, - 85,1,0,0,114,106,0,0,0,114,84,1,0,0,114,38, - 0,0,0,114,81,1,0,0,114,54,0,0,0,114,58,1, - 0,0,114,56,0,0,0,114,134,0,0,0,114,149,0,0, - 0,114,183,0,0,0,114,178,0,0,0,41,14,114,119,0, - 0,0,114,139,0,0,0,114,202,0,0,0,90,12,105,115, - 95,110,97,109,101,115,112,97,99,101,90,11,116,97,105,108, - 95,109,111,100,117,108,101,114,169,0,0,0,90,5,99,97, - 99,104,101,90,12,99,97,99,104,101,95,109,111,100,117,108, - 101,90,9,98,97,115,101,95,112,97,116,104,114,17,1,0, - 0,114,188,0,0,0,90,13,105,110,105,116,95,102,105,108, - 101,110,97,109,101,90,9,102,117,108,108,95,112,97,116,104, - 114,187,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,114,203,0,0,0,189,5,0,0,115,74,0, - 0,0,0,5,4,1,14,1,2,1,24,1,14,1,10,1, - 10,1,8,1,6,2,6,1,6,1,10,2,6,1,4,2, - 8,1,12,1,14,1,8,1,10,1,8,1,26,4,8,2, - 14,1,16,1,16,1,12,1,8,1,10,1,2,0,2,255, - 10,2,6,1,12,1,12,1,8,1,4,1,122,20,70,105, - 108,101,70,105,110,100,101,114,46,102,105,110,100,95,115,112, - 101,99,99,1,0,0,0,0,0,0,0,0,0,0,0,9, - 0,0,0,10,0,0,0,67,0,0,0,115,190,0,0,0, - 124,0,106,0,125,1,122,22,116,1,160,2,124,1,112,22, - 116,1,160,3,161,0,161,1,125,2,87,0,110,30,4,0, - 116,4,116,5,116,6,102,3,107,10,114,58,1,0,1,0, - 1,0,103,0,125,2,89,0,110,2,88,0,116,7,106,8, - 160,9,100,1,161,1,115,84,116,10,124,2,131,1,124,0, - 95,11,110,74,116,10,131,0,125,3,124,2,68,0,93,56, - 125,4,124,4,160,12,100,2,161,1,92,3,125,5,125,6, - 125,7,124,6,114,136,100,3,160,13,124,5,124,7,160,14, - 161,0,161,2,125,8,110,4,124,5,125,8,124,3,160,15, - 124,8,161,1,1,0,113,94,124,3,124,0,95,11,116,7, - 106,8,160,9,116,16,161,1,114,186,100,4,100,5,132,0, - 124,2,68,0,131,1,124,0,95,17,100,6,83,0,41,7, - 122,68,70,105,108,108,32,116,104,101,32,99,97,99,104,101, - 32,111,102,32,112,111,116,101,110,116,105,97,108,32,109,111, - 100,117,108,101,115,32,97,110,100,32,112,97,99,107,97,103, - 101,115,32,102,111,114,32,116,104,105,115,32,100,105,114,101, - 99,116,111,114,121,46,114,0,0,0,0,114,71,0,0,0, - 114,61,0,0,0,99,1,0,0,0,0,0,0,0,0,0, - 0,0,2,0,0,0,4,0,0,0,83,0,0,0,115,20, - 0,0,0,104,0,124,0,93,12,125,1,124,1,160,0,161, - 0,146,2,113,4,83,0,114,3,0,0,0,41,1,114,106, - 0,0,0,41,2,114,32,0,0,0,90,2,102,110,114,3, - 0,0,0,114,3,0,0,0,114,6,0,0,0,218,9,60, - 115,101,116,99,111,109,112,62,10,6,0,0,115,4,0,0, - 0,6,0,2,0,122,41,70,105,108,101,70,105,110,100,101, - 114,46,95,102,105,108,108,95,99,97,99,104,101,46,60,108, - 111,99,97,108,115,62,46,60,115,101,116,99,111,109,112,62, - 78,41,18,114,44,0,0,0,114,2,0,0,0,114,7,1, - 0,0,114,55,0,0,0,114,3,1,0,0,218,15,80,101, - 114,109,105,115,115,105,111,110,69,114,114,111,114,218,18,78, - 111,116,65,68,105,114,101,99,116,111,114,121,69,114,114,111, - 114,114,8,0,0,0,114,9,0,0,0,114,10,0,0,0, - 114,83,1,0,0,114,84,1,0,0,114,101,0,0,0,114, - 62,0,0,0,114,106,0,0,0,218,3,97,100,100,114,11, - 0,0,0,114,85,1,0,0,41,9,114,119,0,0,0,114, - 44,0,0,0,114,8,1,0,0,90,21,108,111,119,101,114, - 95,115,117,102,102,105,120,95,99,111,110,116,101,110,116,115, - 114,41,1,0,0,114,117,0,0,0,114,29,1,0,0,114, - 17,1,0,0,90,8,110,101,119,95,110,97,109,101,114,3, - 0,0,0,114,3,0,0,0,114,6,0,0,0,114,87,1, - 0,0,237,5,0,0,115,34,0,0,0,0,2,6,1,2, - 1,22,1,20,3,10,3,12,1,12,7,6,1,8,1,16, - 1,4,1,18,2,4,1,12,1,6,1,12,1,122,22,70, - 105,108,101,70,105,110,100,101,114,46,95,102,105,108,108,95, - 99,97,99,104,101,99,1,0,0,0,0,0,0,0,0,0, - 0,0,3,0,0,0,3,0,0,0,7,0,0,0,115,18, - 0,0,0,135,0,135,1,102,2,100,1,100,2,132,8,125, - 2,124,2,83,0,41,3,97,20,1,0,0,65,32,99,108, - 97,115,115,32,109,101,116,104,111,100,32,119,104,105,99,104, - 32,114,101,116,117,114,110,115,32,97,32,99,108,111,115,117, - 114,101,32,116,111,32,117,115,101,32,111,110,32,115,121,115, - 46,112,97,116,104,95,104,111,111,107,10,32,32,32,32,32, - 32,32,32,119,104,105,99,104,32,119,105,108,108,32,114,101, - 116,117,114,110,32,97,110,32,105,110,115,116,97,110,99,101, - 32,117,115,105,110,103,32,116,104,101,32,115,112,101,99,105, - 102,105,101,100,32,108,111,97,100,101,114,115,32,97,110,100, - 32,116,104,101,32,112,97,116,104,10,32,32,32,32,32,32, - 32,32,99,97,108,108,101,100,32,111,110,32,116,104,101,32, - 99,108,111,115,117,114,101,46,10,10,32,32,32,32,32,32, - 32,32,73,102,32,116,104,101,32,112,97,116,104,32,99,97, - 108,108,101,100,32,111,110,32,116,104,101,32,99,108,111,115, - 117,114,101,32,105,115,32,110,111,116,32,97,32,100,105,114, - 101,99,116,111,114,121,44,32,73,109,112,111,114,116,69,114, - 114,111,114,32,105,115,10,32,32,32,32,32,32,32,32,114, - 97,105,115,101,100,46,10,10,32,32,32,32,32,32,32,32, - 99,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0, - 0,4,0,0,0,19,0,0,0,115,34,0,0,0,116,0, - 124,0,131,1,115,20,116,1,100,1,124,0,100,2,141,2, - 130,1,136,0,124,0,102,1,136,1,158,2,142,0,83,0, - 41,3,122,45,80,97,116,104,32,104,111,111,107,32,102,111, - 114,32,105,109,112,111,114,116,108,105,98,46,109,97,99,104, - 105,110,101,114,121,46,70,105,108,101,70,105,110,100,101,114, - 46,122,30,111,110,108,121,32,100,105,114,101,99,116,111,114, - 105,101,115,32,97,114,101,32,115,117,112,112,111,114,116,101, - 100,114,48,0,0,0,41,2,114,56,0,0,0,114,118,0, - 0,0,114,48,0,0,0,169,2,114,193,0,0,0,114,86, - 1,0,0,114,3,0,0,0,114,6,0,0,0,218,24,112, - 97,116,104,95,104,111,111,107,95,102,111,114,95,70,105,108, - 101,70,105,110,100,101,114,22,6,0,0,115,6,0,0,0, - 0,2,8,1,12,1,122,54,70,105,108,101,70,105,110,100, - 101,114,46,112,97,116,104,95,104,111,111,107,46,60,108,111, - 99,97,108,115,62,46,112,97,116,104,95,104,111,111,107,95, - 102,111,114,95,70,105,108,101,70,105,110,100,101,114,114,3, - 0,0,0,41,3,114,193,0,0,0,114,86,1,0,0,114, - 93,1,0,0,114,3,0,0,0,114,92,1,0,0,114,6, - 0,0,0,218,9,112,97,116,104,95,104,111,111,107,12,6, - 0,0,115,4,0,0,0,0,10,14,6,122,20,70,105,108, - 101,70,105,110,100,101,114,46,112,97,116,104,95,104,111,111, - 107,99,1,0,0,0,0,0,0,0,0,0,0,0,1,0, - 0,0,3,0,0,0,67,0,0,0,115,12,0,0,0,100, - 1,160,0,124,0,106,1,161,1,83,0,41,2,78,122,16, - 70,105,108,101,70,105,110,100,101,114,40,123,33,114,125,41, - 41,2,114,62,0,0,0,114,44,0,0,0,114,246,0,0, - 0,114,3,0,0,0,114,3,0,0,0,114,6,0,0,0, - 114,39,1,0,0,30,6,0,0,115,2,0,0,0,0,1, - 122,19,70,105,108,101,70,105,110,100,101,114,46,95,95,114, - 101,112,114,95,95,41,1,78,41,15,114,125,0,0,0,114, - 124,0,0,0,114,126,0,0,0,114,127,0,0,0,114,209, - 0,0,0,114,46,1,0,0,114,143,0,0,0,114,206,0, - 0,0,114,137,0,0,0,114,58,1,0,0,114,203,0,0, - 0,114,87,1,0,0,114,207,0,0,0,114,94,1,0,0, - 114,39,1,0,0,114,3,0,0,0,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,79,1,0,0,143,5, - 0,0,115,22,0,0,0,8,2,4,7,8,14,8,4,4, - 2,8,12,8,5,10,48,8,31,2,1,10,17,114,79,1, - 0,0,99,4,0,0,0,0,0,0,0,0,0,0,0,6, - 0,0,0,8,0,0,0,67,0,0,0,115,146,0,0,0, - 124,0,160,0,100,1,161,1,125,4,124,0,160,0,100,2, - 161,1,125,5,124,4,115,66,124,5,114,36,124,5,106,1, - 125,4,110,30,124,2,124,3,107,2,114,56,116,2,124,1, - 124,2,131,2,125,4,110,10,116,3,124,1,124,2,131,2, - 125,4,124,5,115,84,116,4,124,1,124,2,124,4,100,3, - 141,3,125,5,122,36,124,5,124,0,100,2,60,0,124,4, - 124,0,100,1,60,0,124,2,124,0,100,4,60,0,124,3, - 124,0,100,5,60,0,87,0,110,20,4,0,116,5,107,10, - 114,140,1,0,1,0,1,0,89,0,110,2,88,0,100,0, - 83,0,41,6,78,218,10,95,95,108,111,97,100,101,114,95, - 95,218,8,95,95,115,112,101,99,95,95,114,80,1,0,0, - 90,8,95,95,102,105,108,101,95,95,90,10,95,95,99,97, - 99,104,101,100,95,95,41,6,218,3,103,101,116,114,140,0, - 0,0,114,15,1,0,0,114,9,1,0,0,114,190,0,0, - 0,114,72,1,0,0,41,6,90,2,110,115,114,117,0,0, - 0,90,8,112,97,116,104,110,97,109,101,90,9,99,112,97, - 116,104,110,97,109,101,114,140,0,0,0,114,187,0,0,0, - 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, - 14,95,102,105,120,95,117,112,95,109,111,100,117,108,101,36, - 6,0,0,115,34,0,0,0,0,2,10,1,10,1,4,1, - 4,1,8,1,8,1,12,2,10,1,4,1,14,1,2,1, - 8,1,8,1,8,1,12,1,14,2,114,98,1,0,0,99, + 104,95,112,97,116,104,41,1,78,41,2,78,78,41,1,78, + 41,2,78,78,41,19,114,125,0,0,0,114,124,0,0,0, + 114,126,0,0,0,114,127,0,0,0,114,207,0,0,0,114, + 46,1,0,0,114,52,1,0,0,114,54,1,0,0,114,55, + 1,0,0,114,58,1,0,0,114,203,0,0,0,114,206,0, + 0,0,114,64,1,0,0,114,61,1,0,0,218,12,115,116, + 97,116,105,99,109,101,116,104,111,100,114,68,1,0,0,114, + 78,1,0,0,114,79,1,0,0,114,65,1,0,0,114,3, + 0,0,0,114,3,0,0,0,114,3,0,0,0,114,6,0, + 0,0,114,45,1,0,0,213,4,0,0,115,54,0,0,0, + 8,2,4,2,2,1,10,9,2,1,10,12,2,1,10,21, + 2,1,10,14,2,1,12,31,2,1,12,23,2,1,12,12, + 2,1,12,17,2,1,10,8,2,1,10,10,2,1,10,6, + 2,1,10,6,2,1,114,45,1,0,0,99,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0, - 3,0,0,0,67,0,0,0,115,38,0,0,0,116,0,116, - 1,160,2,161,0,102,2,125,0,116,3,116,4,102,2,125, - 1,116,5,116,6,102,2,125,2,124,0,124,1,124,2,103, - 3,83,0,41,1,122,95,82,101,116,117,114,110,115,32,97, - 32,108,105,115,116,32,111,102,32,102,105,108,101,45,98,97, - 115,101,100,32,109,111,100,117,108,101,32,108,111,97,100,101, - 114,115,46,10,10,32,32,32,32,69,97,99,104,32,105,116, - 101,109,32,105,115,32,97,32,116,117,112,108,101,32,40,108, - 111,97,100,101,114,44,32,115,117,102,102,105,120,101,115,41, - 46,10,32,32,32,32,41,7,114,252,0,0,0,114,163,0, - 0,0,218,18,101,120,116,101,110,115,105,111,110,95,115,117, - 102,102,105,120,101,115,114,9,1,0,0,114,102,0,0,0, - 114,15,1,0,0,114,89,0,0,0,41,3,90,10,101,120, - 116,101,110,115,105,111,110,115,90,6,115,111,117,114,99,101, - 90,8,98,121,116,101,99,111,100,101,114,3,0,0,0,114, - 3,0,0,0,114,6,0,0,0,114,184,0,0,0,59,6, - 0,0,115,8,0,0,0,0,5,12,1,8,1,8,1,114, - 184,0,0,0,99,1,0,0,0,0,0,0,0,0,0,0, - 0,12,0,0,0,9,0,0,0,67,0,0,0,115,178,1, - 0,0,124,0,97,0,116,0,106,1,97,1,116,0,106,2, - 97,2,116,1,106,3,116,4,25,0,125,1,100,1,68,0, - 93,48,125,2,124,2,116,1,106,3,107,7,114,56,116,0, - 160,5,124,2,161,1,125,3,110,10,116,1,106,3,124,2, - 25,0,125,3,116,6,124,1,124,2,124,3,131,3,1,0, - 113,30,100,2,100,3,103,1,102,2,100,4,100,5,100,3, - 103,2,102,2,102,2,125,4,124,4,68,0,93,110,92,2, - 125,5,125,6,116,7,100,6,100,7,132,0,124,6,68,0, - 131,1,131,1,115,136,116,8,130,1,124,6,100,8,25,0, - 125,7,124,5,116,1,106,3,107,6,114,170,116,1,106,3, - 124,5,25,0,125,8,1,0,113,226,113,106,122,20,116,0, - 160,5,124,5,161,1,125,8,87,0,1,0,113,226,87,0, - 113,106,4,0,116,9,107,10,114,214,1,0,1,0,1,0, - 89,0,113,106,89,0,113,106,88,0,113,106,116,9,100,9, - 131,1,130,1,116,6,124,1,100,10,124,8,131,3,1,0, - 116,6,124,1,100,11,124,7,131,3,1,0,116,6,124,1, - 100,12,100,13,160,10,124,6,161,1,131,3,1,0,116,6, - 124,1,100,14,100,15,100,16,132,0,124,6,68,0,131,1, - 131,3,1,0,116,0,160,5,100,17,161,1,125,9,116,6, - 124,1,100,17,124,9,131,3,1,0,116,0,160,5,100,18, - 161,1,125,10,116,6,124,1,100,18,124,10,131,3,1,0, - 124,5,100,4,107,2,144,1,114,110,116,0,160,5,100,19, - 161,1,125,11,116,6,124,1,100,20,124,11,131,3,1,0, - 116,6,124,1,100,21,116,11,131,0,131,3,1,0,116,12, - 160,13,116,2,160,14,161,0,161,1,1,0,124,5,100,4, - 107,2,144,1,114,174,116,15,160,16,100,22,161,1,1,0, - 100,23,116,12,107,6,144,1,114,174,100,24,116,17,95,18, - 100,25,83,0,41,26,122,205,83,101,116,117,112,32,116,104, - 101,32,112,97,116,104,45,98,97,115,101,100,32,105,109,112, - 111,114,116,101,114,115,32,102,111,114,32,105,109,112,111,114, - 116,108,105,98,32,98,121,32,105,109,112,111,114,116,105,110, - 103,32,110,101,101,100,101,100,10,32,32,32,32,98,117,105, - 108,116,45,105,110,32,109,111,100,117,108,101,115,32,97,110, - 100,32,105,110,106,101,99,116,105,110,103,32,116,104,101,109, - 32,105,110,116,111,32,116,104,101,32,103,108,111,98,97,108, - 32,110,97,109,101,115,112,97,99,101,46,10,10,32,32,32, - 32,79,116,104,101,114,32,99,111,109,112,111,110,101,110,116, - 115,32,97,114,101,32,101,120,116,114,97,99,116,101,100,32, - 102,114,111,109,32,116,104,101,32,99,111,114,101,32,98,111, - 111,116,115,116,114,97,112,32,109,111,100,117,108,101,46,10, - 10,32,32,32,32,41,4,114,64,0,0,0,114,75,0,0, - 0,218,8,98,117,105,108,116,105,110,115,114,160,0,0,0, - 90,5,112,111,115,105,120,250,1,47,90,2,110,116,250,1, - 92,99,1,0,0,0,0,0,0,0,0,0,0,0,2,0, - 0,0,3,0,0,0,115,0,0,0,115,26,0,0,0,124, - 0,93,18,125,1,116,0,124,1,131,1,100,0,107,2,86, - 0,1,0,113,2,100,1,83,0,41,2,114,39,0,0,0, - 78,41,1,114,22,0,0,0,41,2,114,32,0,0,0,114, - 95,0,0,0,114,3,0,0,0,114,3,0,0,0,114,6, - 0,0,0,114,19,1,0,0,95,6,0,0,115,4,0,0, - 0,4,0,2,0,122,25,95,115,101,116,117,112,46,60,108, - 111,99,97,108,115,62,46,60,103,101,110,101,120,112,114,62, - 114,73,0,0,0,122,30,105,109,112,111,114,116,108,105,98, - 32,114,101,113,117,105,114,101,115,32,112,111,115,105,120,32, - 111,114,32,110,116,114,2,0,0,0,114,35,0,0,0,114, - 31,0,0,0,114,40,0,0,0,114,58,0,0,0,99,1, - 0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,4, - 0,0,0,83,0,0,0,115,22,0,0,0,104,0,124,0, - 93,14,125,1,100,0,124,1,155,0,157,2,146,2,113,4, - 83,0,41,1,114,74,0,0,0,114,3,0,0,0,41,2, - 114,32,0,0,0,218,1,115,114,3,0,0,0,114,3,0, - 0,0,114,6,0,0,0,114,88,1,0,0,111,6,0,0, - 115,4,0,0,0,6,0,2,0,122,25,95,115,101,116,117, - 112,46,60,108,111,99,97,108,115,62,46,60,115,101,116,99, - 111,109,112,62,90,7,95,116,104,114,101,97,100,90,8,95, - 119,101,97,107,114,101,102,90,6,119,105,110,114,101,103,114, - 192,0,0,0,114,7,0,0,0,122,4,46,112,121,119,122, - 6,95,100,46,112,121,100,84,78,41,19,114,134,0,0,0, - 114,8,0,0,0,114,163,0,0,0,114,31,1,0,0,114, - 125,0,0,0,90,18,95,98,117,105,108,116,105,110,95,102, - 114,111,109,95,110,97,109,101,114,129,0,0,0,218,3,97, - 108,108,114,23,0,0,0,114,118,0,0,0,114,36,0,0, - 0,114,13,0,0,0,114,21,1,0,0,114,167,0,0,0, - 114,99,1,0,0,114,102,0,0,0,114,186,0,0,0,114, - 191,0,0,0,114,195,0,0,0,41,12,218,17,95,98,111, - 111,116,115,116,114,97,112,95,109,111,100,117,108,101,90,11, - 115,101,108,102,95,109,111,100,117,108,101,90,12,98,117,105, - 108,116,105,110,95,110,97,109,101,90,14,98,117,105,108,116, - 105,110,95,109,111,100,117,108,101,90,10,111,115,95,100,101, - 116,97,105,108,115,90,10,98,117,105,108,116,105,110,95,111, - 115,114,31,0,0,0,114,35,0,0,0,90,9,111,115,95, - 109,111,100,117,108,101,90,13,116,104,114,101,97,100,95,109, - 111,100,117,108,101,90,14,119,101,97,107,114,101,102,95,109, - 111,100,117,108,101,90,13,119,105,110,114,101,103,95,109,111, - 100,117,108,101,114,3,0,0,0,114,3,0,0,0,114,6, - 0,0,0,218,6,95,115,101,116,117,112,70,6,0,0,115, - 78,0,0,0,0,8,4,1,6,1,6,3,10,1,8,1, - 10,1,12,2,10,1,14,3,22,1,12,2,22,1,8,1, - 10,1,10,1,6,2,2,1,10,1,10,1,14,1,12,2, - 8,1,12,1,12,1,18,1,22,3,10,1,12,3,10,1, - 12,3,10,1,10,1,12,3,14,1,14,1,10,1,10,1, - 10,1,114,106,1,0,0,99,1,0,0,0,0,0,0,0, - 0,0,0,0,2,0,0,0,4,0,0,0,67,0,0,0, - 115,50,0,0,0,116,0,124,0,131,1,1,0,116,1,131, - 0,125,1,116,2,106,3,160,4,116,5,106,6,124,1,142, - 0,103,1,161,1,1,0,116,2,106,7,160,8,116,9,161, - 1,1,0,100,1,83,0,41,2,122,41,73,110,115,116,97, - 108,108,32,116,104,101,32,112,97,116,104,45,98,97,115,101, - 100,32,105,109,112,111,114,116,32,99,111,109,112,111,110,101, - 110,116,115,46,78,41,10,114,106,1,0,0,114,184,0,0, - 0,114,8,0,0,0,114,51,1,0,0,114,167,0,0,0, - 114,79,1,0,0,114,94,1,0,0,218,9,109,101,116,97, - 95,112,97,116,104,114,186,0,0,0,114,45,1,0,0,41, - 2,114,105,1,0,0,90,17,115,117,112,112,111,114,116,101, - 100,95,108,111,97,100,101,114,115,114,3,0,0,0,114,3, - 0,0,0,114,6,0,0,0,218,8,95,105,110,115,116,97, - 108,108,135,6,0,0,115,8,0,0,0,0,2,8,1,6, - 1,20,1,114,108,1,0,0,41,1,114,60,0,0,0,41, - 1,78,41,3,78,78,78,41,2,114,73,0,0,0,114,73, - 0,0,0,41,1,84,41,1,78,41,1,78,41,63,114,127, - 0,0,0,114,12,0,0,0,90,37,95,67,65,83,69,95, - 73,78,83,69,78,83,73,84,73,86,69,95,80,76,65,84, - 70,79,82,77,83,95,66,89,84,69,83,95,75,69,89,114, - 11,0,0,0,114,13,0,0,0,114,20,0,0,0,114,27, - 0,0,0,114,29,0,0,0,114,38,0,0,0,114,47,0, - 0,0,114,49,0,0,0,114,53,0,0,0,114,54,0,0, - 0,114,56,0,0,0,114,59,0,0,0,114,69,0,0,0, - 218,4,116,121,112,101,218,8,95,95,99,111,100,101,95,95, - 114,162,0,0,0,114,18,0,0,0,114,148,0,0,0,114, - 17,0,0,0,114,24,0,0,0,114,236,0,0,0,114,92, - 0,0,0,114,88,0,0,0,114,102,0,0,0,114,89,0, - 0,0,90,23,68,69,66,85,71,95,66,89,84,69,67,79, - 68,69,95,83,85,70,70,73,88,69,83,90,27,79,80,84, - 73,77,73,90,69,68,95,66,89,84,69,67,79,68,69,95, - 83,85,70,70,73,88,69,83,114,98,0,0,0,114,103,0, - 0,0,114,109,0,0,0,114,113,0,0,0,114,115,0,0, - 0,114,136,0,0,0,114,143,0,0,0,114,152,0,0,0, - 114,156,0,0,0,114,158,0,0,0,114,165,0,0,0,114, - 170,0,0,0,114,171,0,0,0,114,176,0,0,0,218,6, - 111,98,106,101,99,116,114,185,0,0,0,114,190,0,0,0, - 114,191,0,0,0,114,208,0,0,0,114,221,0,0,0,114, - 239,0,0,0,114,9,1,0,0,114,15,1,0,0,114,21, - 1,0,0,114,252,0,0,0,114,22,1,0,0,114,43,1, - 0,0,114,45,1,0,0,114,79,1,0,0,114,98,1,0, - 0,114,184,0,0,0,114,106,1,0,0,114,108,1,0,0, + 64,0,0,0,115,90,0,0,0,101,0,90,1,100,0,90, + 2,100,1,90,3,100,2,100,3,132,0,90,4,100,4,100, + 5,132,0,90,5,101,6,90,7,100,6,100,7,132,0,90, + 8,100,8,100,9,132,0,90,9,100,19,100,11,100,12,132, + 1,90,10,100,13,100,14,132,0,90,11,101,12,100,15,100, + 16,132,0,131,1,90,13,100,17,100,18,132,0,90,14,100, + 10,83,0,41,20,218,10,70,105,108,101,70,105,110,100,101, + 114,122,172,70,105,108,101,45,98,97,115,101,100,32,102,105, + 110,100,101,114,46,10,10,32,32,32,32,73,110,116,101,114, + 97,99,116,105,111,110,115,32,119,105,116,104,32,116,104,101, + 32,102,105,108,101,32,115,121,115,116,101,109,32,97,114,101, + 32,99,97,99,104,101,100,32,102,111,114,32,112,101,114,102, + 111,114,109,97,110,99,101,44,32,98,101,105,110,103,10,32, + 32,32,32,114,101,102,114,101,115,104,101,100,32,119,104,101, + 110,32,116,104,101,32,100,105,114,101,99,116,111,114,121,32, + 116,104,101,32,102,105,110,100,101,114,32,105,115,32,104,97, + 110,100,108,105,110,103,32,104,97,115,32,98,101,101,110,32, + 109,111,100,105,102,105,101,100,46,10,10,32,32,32,32,99, + 2,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0, + 6,0,0,0,7,0,0,0,115,84,0,0,0,103,0,125, + 3,124,2,68,0,93,32,92,2,137,0,125,4,124,3,160, + 0,135,0,102,1,100,1,100,2,132,8,124,4,68,0,131, + 1,161,1,1,0,113,8,124,3,124,0,95,1,124,1,112, + 54,100,3,124,0,95,2,100,4,124,0,95,3,116,4,131, + 0,124,0,95,5,116,4,131,0,124,0,95,6,100,5,83, + 0,41,6,122,154,73,110,105,116,105,97,108,105,122,101,32, + 119,105,116,104,32,116,104,101,32,112,97,116,104,32,116,111, + 32,115,101,97,114,99,104,32,111,110,32,97,110,100,32,97, + 32,118,97,114,105,97,98,108,101,32,110,117,109,98,101,114, + 32,111,102,10,32,32,32,32,32,32,32,32,50,45,116,117, + 112,108,101,115,32,99,111,110,116,97,105,110,105,110,103,32, + 116,104,101,32,108,111,97,100,101,114,32,97,110,100,32,116, + 104,101,32,102,105,108,101,32,115,117,102,102,105,120,101,115, + 32,116,104,101,32,108,111,97,100,101,114,10,32,32,32,32, + 32,32,32,32,114,101,99,111,103,110,105,122,101,115,46,99, + 1,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, + 3,0,0,0,51,0,0,0,115,22,0,0,0,124,0,93, + 14,125,1,124,1,136,0,102,2,86,0,1,0,113,2,100, + 0,83,0,114,110,0,0,0,114,3,0,0,0,114,16,1, + 0,0,169,1,114,140,0,0,0,114,3,0,0,0,114,6, + 0,0,0,114,19,1,0,0,167,5,0,0,115,4,0,0, + 0,4,0,2,0,122,38,70,105,108,101,70,105,110,100,101, + 114,46,95,95,105,110,105,116,95,95,46,60,108,111,99,97, + 108,115,62,46,60,103,101,110,101,120,112,114,62,114,71,0, + 0,0,114,105,0,0,0,78,41,7,114,167,0,0,0,218, + 8,95,108,111,97,100,101,114,115,114,44,0,0,0,218,11, + 95,112,97,116,104,95,109,116,105,109,101,218,3,115,101,116, + 218,11,95,112,97,116,104,95,99,97,99,104,101,218,19,95, + 114,101,108,97,120,101,100,95,112,97,116,104,95,99,97,99, + 104,101,41,5,114,119,0,0,0,114,44,0,0,0,218,14, + 108,111,97,100,101,114,95,100,101,116,97,105,108,115,90,7, + 108,111,97,100,101,114,115,114,189,0,0,0,114,3,0,0, + 0,114,84,1,0,0,114,6,0,0,0,114,209,0,0,0, + 161,5,0,0,115,16,0,0,0,0,4,4,1,12,1,26, + 1,6,2,10,1,6,1,8,1,122,19,70,105,108,101,70, + 105,110,100,101,114,46,95,95,105,110,105,116,95,95,99,1, + 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2, + 0,0,0,67,0,0,0,115,10,0,0,0,100,1,124,0, + 95,0,100,2,83,0,41,3,122,31,73,110,118,97,108,105, + 100,97,116,101,32,116,104,101,32,100,105,114,101,99,116,111, + 114,121,32,109,116,105,109,101,46,114,105,0,0,0,78,41, + 1,114,86,1,0,0,114,246,0,0,0,114,3,0,0,0, + 114,3,0,0,0,114,6,0,0,0,114,46,1,0,0,175, + 5,0,0,115,2,0,0,0,0,2,122,28,70,105,108,101, + 70,105,110,100,101,114,46,105,110,118,97,108,105,100,97,116, + 101,95,99,97,99,104,101,115,99,2,0,0,0,0,0,0, + 0,0,0,0,0,3,0,0,0,3,0,0,0,67,0,0, + 0,115,42,0,0,0,124,0,160,0,124,1,161,1,125,2, + 124,2,100,1,107,8,114,26,100,1,103,0,102,2,83,0, + 124,2,106,1,124,2,106,2,112,38,103,0,102,2,83,0, + 41,2,122,197,84,114,121,32,116,111,32,102,105,110,100,32, + 97,32,108,111,97,100,101,114,32,102,111,114,32,116,104,101, + 32,115,112,101,99,105,102,105,101,100,32,109,111,100,117,108, + 101,44,32,111,114,32,116,104,101,32,110,97,109,101,115,112, + 97,99,101,10,32,32,32,32,32,32,32,32,112,97,99,107, + 97,103,101,32,112,111,114,116,105,111,110,115,46,32,82,101, + 116,117,114,110,115,32,40,108,111,97,100,101,114,44,32,108, + 105,115,116,45,111,102,45,112,111,114,116,105,111,110,115,41, + 46,10,10,32,32,32,32,32,32,32,32,84,104,105,115,32, + 109,101,116,104,111,100,32,105,115,32,100,101,112,114,101,99, + 97,116,101,100,46,32,32,85,115,101,32,102,105,110,100,95, + 115,112,101,99,40,41,32,105,110,115,116,101,97,100,46,10, + 10,32,32,32,32,32,32,32,32,78,41,3,114,203,0,0, + 0,114,140,0,0,0,114,178,0,0,0,41,3,114,119,0, + 0,0,114,139,0,0,0,114,187,0,0,0,114,3,0,0, + 0,114,3,0,0,0,114,6,0,0,0,114,137,0,0,0, + 181,5,0,0,115,8,0,0,0,0,7,10,1,8,1,8, + 1,122,22,70,105,108,101,70,105,110,100,101,114,46,102,105, + 110,100,95,108,111,97,100,101,114,99,6,0,0,0,0,0, + 0,0,0,0,0,0,7,0,0,0,6,0,0,0,67,0, + 0,0,115,26,0,0,0,124,1,124,2,124,3,131,2,125, + 6,116,0,124,2,124,3,124,6,124,4,100,1,141,4,83, + 0,41,2,78,114,177,0,0,0,41,1,114,190,0,0,0, + 41,7,114,119,0,0,0,114,188,0,0,0,114,139,0,0, + 0,114,44,0,0,0,90,4,115,109,115,108,114,202,0,0, + 0,114,140,0,0,0,114,3,0,0,0,114,3,0,0,0, + 114,6,0,0,0,114,58,1,0,0,193,5,0,0,115,8, + 0,0,0,0,1,10,1,8,1,2,255,122,20,70,105,108, + 101,70,105,110,100,101,114,46,95,103,101,116,95,115,112,101, + 99,78,99,3,0,0,0,0,0,0,0,0,0,0,0,14, + 0,0,0,8,0,0,0,67,0,0,0,115,98,1,0,0, + 100,1,125,3,124,1,160,0,100,2,161,1,100,3,25,0, + 125,4,122,24,116,1,124,0,106,2,112,34,116,3,160,4, + 161,0,131,1,106,5,125,5,87,0,110,24,4,0,116,6, + 107,10,114,66,1,0,1,0,1,0,100,4,125,5,89,0, + 110,2,88,0,124,5,124,0,106,7,107,3,114,92,124,0, + 160,8,161,0,1,0,124,5,124,0,95,7,116,9,131,0, + 114,114,124,0,106,10,125,6,124,4,160,11,161,0,125,7, + 110,10,124,0,106,12,125,6,124,4,125,7,124,7,124,6, + 107,6,114,218,116,13,124,0,106,2,124,4,131,2,125,8, + 124,0,106,14,68,0,93,58,92,2,125,9,125,10,100,5, + 124,9,23,0,125,11,116,13,124,8,124,11,131,2,125,12, + 116,15,124,12,131,1,114,150,124,0,160,16,124,10,124,1, + 124,12,124,8,103,1,124,2,161,5,2,0,1,0,83,0, + 113,150,116,17,124,8,131,1,125,3,124,0,106,14,68,0, + 93,82,92,2,125,9,125,10,116,13,124,0,106,2,124,4, + 124,9,23,0,131,2,125,12,116,18,106,19,100,6,124,12, + 100,3,100,7,141,3,1,0,124,7,124,9,23,0,124,6, + 107,6,114,224,116,15,124,12,131,1,114,224,124,0,160,16, + 124,10,124,1,124,12,100,8,124,2,161,5,2,0,1,0, + 83,0,113,224,124,3,144,1,114,94,116,18,160,19,100,9, + 124,8,161,2,1,0,116,18,160,20,124,1,100,8,161,2, + 125,13,124,8,103,1,124,13,95,21,124,13,83,0,100,8, + 83,0,41,10,122,111,84,114,121,32,116,111,32,102,105,110, + 100,32,97,32,115,112,101,99,32,102,111,114,32,116,104,101, + 32,115,112,101,99,105,102,105,101,100,32,109,111,100,117,108, + 101,46,10,10,32,32,32,32,32,32,32,32,82,101,116,117, + 114,110,115,32,116,104,101,32,109,97,116,99,104,105,110,103, + 32,115,112,101,99,44,32,111,114,32,78,111,110,101,32,105, + 102,32,110,111,116,32,102,111,117,110,100,46,10,32,32,32, + 32,32,32,32,32,70,114,71,0,0,0,114,28,0,0,0, + 114,105,0,0,0,114,209,0,0,0,122,9,116,114,121,105, + 110,103,32,123,125,41,1,90,9,118,101,114,98,111,115,105, + 116,121,78,122,25,112,111,115,115,105,98,108,101,32,110,97, + 109,101,115,112,97,99,101,32,102,111,114,32,123,125,41,22, + 114,41,0,0,0,114,49,0,0,0,114,44,0,0,0,114, + 2,0,0,0,114,55,0,0,0,114,10,1,0,0,114,50, + 0,0,0,114,86,1,0,0,218,11,95,102,105,108,108,95, + 99,97,99,104,101,114,7,0,0,0,114,89,1,0,0,114, + 106,0,0,0,114,88,1,0,0,114,38,0,0,0,114,85, + 1,0,0,114,54,0,0,0,114,58,1,0,0,114,56,0, + 0,0,114,134,0,0,0,114,149,0,0,0,114,183,0,0, + 0,114,178,0,0,0,41,14,114,119,0,0,0,114,139,0, + 0,0,114,202,0,0,0,90,12,105,115,95,110,97,109,101, + 115,112,97,99,101,90,11,116,97,105,108,95,109,111,100,117, + 108,101,114,169,0,0,0,90,5,99,97,99,104,101,90,12, + 99,97,99,104,101,95,109,111,100,117,108,101,90,9,98,97, + 115,101,95,112,97,116,104,114,17,1,0,0,114,188,0,0, + 0,90,13,105,110,105,116,95,102,105,108,101,110,97,109,101, + 90,9,102,117,108,108,95,112,97,116,104,114,187,0,0,0, + 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,114, + 203,0,0,0,198,5,0,0,115,74,0,0,0,0,5,4, + 1,14,1,2,1,24,1,14,1,10,1,10,1,8,1,6, + 2,6,1,6,1,10,2,6,1,4,2,8,1,12,1,14, + 1,8,1,10,1,8,1,26,4,8,2,14,1,16,1,16, + 1,12,1,8,1,10,1,2,0,2,255,10,2,6,1,12, + 1,12,1,8,1,4,1,122,20,70,105,108,101,70,105,110, + 100,101,114,46,102,105,110,100,95,115,112,101,99,99,1,0, + 0,0,0,0,0,0,0,0,0,0,9,0,0,0,10,0, + 0,0,67,0,0,0,115,190,0,0,0,124,0,106,0,125, + 1,122,22,116,1,160,2,124,1,112,22,116,1,160,3,161, + 0,161,1,125,2,87,0,110,30,4,0,116,4,116,5,116, + 6,102,3,107,10,114,58,1,0,1,0,1,0,103,0,125, + 2,89,0,110,2,88,0,116,7,106,8,160,9,100,1,161, + 1,115,84,116,10,124,2,131,1,124,0,95,11,110,74,116, + 10,131,0,125,3,124,2,68,0,93,56,125,4,124,4,160, + 12,100,2,161,1,92,3,125,5,125,6,125,7,124,6,114, + 136,100,3,160,13,124,5,124,7,160,14,161,0,161,2,125, + 8,110,4,124,5,125,8,124,3,160,15,124,8,161,1,1, + 0,113,94,124,3,124,0,95,11,116,7,106,8,160,9,116, + 16,161,1,114,186,100,4,100,5,132,0,124,2,68,0,131, + 1,124,0,95,17,100,6,83,0,41,7,122,68,70,105,108, + 108,32,116,104,101,32,99,97,99,104,101,32,111,102,32,112, + 111,116,101,110,116,105,97,108,32,109,111,100,117,108,101,115, + 32,97,110,100,32,112,97,99,107,97,103,101,115,32,102,111, + 114,32,116,104,105,115,32,100,105,114,101,99,116,111,114,121, + 46,114,0,0,0,0,114,71,0,0,0,114,61,0,0,0, + 99,1,0,0,0,0,0,0,0,0,0,0,0,2,0,0, + 0,4,0,0,0,83,0,0,0,115,20,0,0,0,104,0, + 124,0,93,12,125,1,124,1,160,0,161,0,146,2,113,4, + 83,0,114,3,0,0,0,41,1,114,106,0,0,0,41,2, + 114,32,0,0,0,90,2,102,110,114,3,0,0,0,114,3, + 0,0,0,114,6,0,0,0,218,9,60,115,101,116,99,111, + 109,112,62,19,6,0,0,115,4,0,0,0,6,0,2,0, + 122,41,70,105,108,101,70,105,110,100,101,114,46,95,102,105, + 108,108,95,99,97,99,104,101,46,60,108,111,99,97,108,115, + 62,46,60,115,101,116,99,111,109,112,62,78,41,18,114,44, + 0,0,0,114,2,0,0,0,114,7,1,0,0,114,55,0, + 0,0,114,3,1,0,0,218,15,80,101,114,109,105,115,115, + 105,111,110,69,114,114,111,114,218,18,78,111,116,65,68,105, + 114,101,99,116,111,114,121,69,114,114,111,114,114,8,0,0, + 0,114,9,0,0,0,114,10,0,0,0,114,87,1,0,0, + 114,88,1,0,0,114,101,0,0,0,114,62,0,0,0,114, + 106,0,0,0,218,3,97,100,100,114,11,0,0,0,114,89, + 1,0,0,41,9,114,119,0,0,0,114,44,0,0,0,114, + 8,1,0,0,90,21,108,111,119,101,114,95,115,117,102,102, + 105,120,95,99,111,110,116,101,110,116,115,114,41,1,0,0, + 114,117,0,0,0,114,29,1,0,0,114,17,1,0,0,90, + 8,110,101,119,95,110,97,109,101,114,3,0,0,0,114,3, + 0,0,0,114,6,0,0,0,114,91,1,0,0,246,5,0, + 0,115,34,0,0,0,0,2,6,1,2,1,22,1,20,3, + 10,3,12,1,12,7,6,1,8,1,16,1,4,1,18,2, + 4,1,12,1,6,1,12,1,122,22,70,105,108,101,70,105, + 110,100,101,114,46,95,102,105,108,108,95,99,97,99,104,101, + 99,1,0,0,0,0,0,0,0,0,0,0,0,3,0,0, + 0,3,0,0,0,7,0,0,0,115,18,0,0,0,135,0, + 135,1,102,2,100,1,100,2,132,8,125,2,124,2,83,0, + 41,3,97,20,1,0,0,65,32,99,108,97,115,115,32,109, + 101,116,104,111,100,32,119,104,105,99,104,32,114,101,116,117, + 114,110,115,32,97,32,99,108,111,115,117,114,101,32,116,111, + 32,117,115,101,32,111,110,32,115,121,115,46,112,97,116,104, + 95,104,111,111,107,10,32,32,32,32,32,32,32,32,119,104, + 105,99,104,32,119,105,108,108,32,114,101,116,117,114,110,32, + 97,110,32,105,110,115,116,97,110,99,101,32,117,115,105,110, + 103,32,116,104,101,32,115,112,101,99,105,102,105,101,100,32, + 108,111,97,100,101,114,115,32,97,110,100,32,116,104,101,32, + 112,97,116,104,10,32,32,32,32,32,32,32,32,99,97,108, + 108,101,100,32,111,110,32,116,104,101,32,99,108,111,115,117, + 114,101,46,10,10,32,32,32,32,32,32,32,32,73,102,32, + 116,104,101,32,112,97,116,104,32,99,97,108,108,101,100,32, + 111,110,32,116,104,101,32,99,108,111,115,117,114,101,32,105, + 115,32,110,111,116,32,97,32,100,105,114,101,99,116,111,114, + 121,44,32,73,109,112,111,114,116,69,114,114,111,114,32,105, + 115,10,32,32,32,32,32,32,32,32,114,97,105,115,101,100, + 46,10,10,32,32,32,32,32,32,32,32,99,1,0,0,0, + 0,0,0,0,0,0,0,0,1,0,0,0,4,0,0,0, + 19,0,0,0,115,34,0,0,0,116,0,124,0,131,1,115, + 20,116,1,100,1,124,0,100,2,141,2,130,1,136,0,124, + 0,102,1,136,1,158,2,142,0,83,0,41,3,122,45,80, + 97,116,104,32,104,111,111,107,32,102,111,114,32,105,109,112, + 111,114,116,108,105,98,46,109,97,99,104,105,110,101,114,121, + 46,70,105,108,101,70,105,110,100,101,114,46,122,30,111,110, + 108,121,32,100,105,114,101,99,116,111,114,105,101,115,32,97, + 114,101,32,115,117,112,112,111,114,116,101,100,114,48,0,0, + 0,41,2,114,56,0,0,0,114,118,0,0,0,114,48,0, + 0,0,169,2,114,193,0,0,0,114,90,1,0,0,114,3, + 0,0,0,114,6,0,0,0,218,24,112,97,116,104,95,104, + 111,111,107,95,102,111,114,95,70,105,108,101,70,105,110,100, + 101,114,31,6,0,0,115,6,0,0,0,0,2,8,1,12, + 1,122,54,70,105,108,101,70,105,110,100,101,114,46,112,97, + 116,104,95,104,111,111,107,46,60,108,111,99,97,108,115,62, + 46,112,97,116,104,95,104,111,111,107,95,102,111,114,95,70, + 105,108,101,70,105,110,100,101,114,114,3,0,0,0,41,3, + 114,193,0,0,0,114,90,1,0,0,114,97,1,0,0,114, + 3,0,0,0,114,96,1,0,0,114,6,0,0,0,218,9, + 112,97,116,104,95,104,111,111,107,21,6,0,0,115,4,0, + 0,0,0,10,14,6,122,20,70,105,108,101,70,105,110,100, + 101,114,46,112,97,116,104,95,104,111,111,107,99,1,0,0, + 0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0, + 0,67,0,0,0,115,12,0,0,0,100,1,160,0,124,0, + 106,1,161,1,83,0,41,2,78,122,16,70,105,108,101,70, + 105,110,100,101,114,40,123,33,114,125,41,41,2,114,62,0, + 0,0,114,44,0,0,0,114,246,0,0,0,114,3,0,0, + 0,114,3,0,0,0,114,6,0,0,0,114,39,1,0,0, + 39,6,0,0,115,2,0,0,0,0,1,122,19,70,105,108, + 101,70,105,110,100,101,114,46,95,95,114,101,112,114,95,95, + 41,1,78,41,15,114,125,0,0,0,114,124,0,0,0,114, + 126,0,0,0,114,127,0,0,0,114,209,0,0,0,114,46, + 1,0,0,114,143,0,0,0,114,206,0,0,0,114,137,0, + 0,0,114,58,1,0,0,114,203,0,0,0,114,91,1,0, + 0,114,207,0,0,0,114,98,1,0,0,114,39,1,0,0, 114,3,0,0,0,114,3,0,0,0,114,3,0,0,0,114, - 6,0,0,0,218,8,60,109,111,100,117,108,101,62,1,0, - 0,0,115,126,0,0,0,4,22,4,1,4,1,2,1,2, - 255,4,4,8,17,8,5,8,5,8,6,8,6,8,12,8, - 10,8,9,8,5,8,7,8,9,10,22,10,127,0,12,16, - 1,12,2,4,1,4,2,6,2,6,2,8,2,16,71,8, - 40,8,19,8,12,8,12,8,28,8,17,8,33,8,28,8, - 24,10,13,10,10,10,11,8,14,6,3,4,1,2,255,12, - 68,14,64,14,29,16,127,0,17,14,72,18,45,18,26,4, - 3,18,53,14,63,14,42,14,127,0,59,14,127,0,22,10, - 23,8,11,8,65, + 6,0,0,0,114,83,1,0,0,152,5,0,0,115,22,0, + 0,0,8,2,4,7,8,14,8,4,4,2,8,12,8,5, + 10,48,8,31,2,1,10,17,114,83,1,0,0,99,4,0, + 0,0,0,0,0,0,0,0,0,0,6,0,0,0,8,0, + 0,0,67,0,0,0,115,146,0,0,0,124,0,160,0,100, + 1,161,1,125,4,124,0,160,0,100,2,161,1,125,5,124, + 4,115,66,124,5,114,36,124,5,106,1,125,4,110,30,124, + 2,124,3,107,2,114,56,116,2,124,1,124,2,131,2,125, + 4,110,10,116,3,124,1,124,2,131,2,125,4,124,5,115, + 84,116,4,124,1,124,2,124,4,100,3,141,3,125,5,122, + 36,124,5,124,0,100,2,60,0,124,4,124,0,100,1,60, + 0,124,2,124,0,100,4,60,0,124,3,124,0,100,5,60, + 0,87,0,110,20,4,0,116,5,107,10,114,140,1,0,1, + 0,1,0,89,0,110,2,88,0,100,0,83,0,41,6,78, + 218,10,95,95,108,111,97,100,101,114,95,95,218,8,95,95, + 115,112,101,99,95,95,114,84,1,0,0,90,8,95,95,102, + 105,108,101,95,95,90,10,95,95,99,97,99,104,101,100,95, + 95,41,6,218,3,103,101,116,114,140,0,0,0,114,15,1, + 0,0,114,9,1,0,0,114,190,0,0,0,114,72,1,0, + 0,41,6,90,2,110,115,114,117,0,0,0,90,8,112,97, + 116,104,110,97,109,101,90,9,99,112,97,116,104,110,97,109, + 101,114,140,0,0,0,114,187,0,0,0,114,3,0,0,0, + 114,3,0,0,0,114,6,0,0,0,218,14,95,102,105,120, + 95,117,112,95,109,111,100,117,108,101,45,6,0,0,115,34, + 0,0,0,0,2,10,1,10,1,4,1,4,1,8,1,8, + 1,12,2,10,1,4,1,14,1,2,1,8,1,8,1,8, + 1,12,1,14,2,114,102,1,0,0,99,0,0,0,0,0, + 0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,67, + 0,0,0,115,38,0,0,0,116,0,116,1,160,2,161,0, + 102,2,125,0,116,3,116,4,102,2,125,1,116,5,116,6, + 102,2,125,2,124,0,124,1,124,2,103,3,83,0,41,1, + 122,95,82,101,116,117,114,110,115,32,97,32,108,105,115,116, + 32,111,102,32,102,105,108,101,45,98,97,115,101,100,32,109, + 111,100,117,108,101,32,108,111,97,100,101,114,115,46,10,10, + 32,32,32,32,69,97,99,104,32,105,116,101,109,32,105,115, + 32,97,32,116,117,112,108,101,32,40,108,111,97,100,101,114, + 44,32,115,117,102,102,105,120,101,115,41,46,10,32,32,32, + 32,41,7,114,252,0,0,0,114,163,0,0,0,218,18,101, + 120,116,101,110,115,105,111,110,95,115,117,102,102,105,120,101, + 115,114,9,1,0,0,114,102,0,0,0,114,15,1,0,0, + 114,89,0,0,0,41,3,90,10,101,120,116,101,110,115,105, + 111,110,115,90,6,115,111,117,114,99,101,90,8,98,121,116, + 101,99,111,100,101,114,3,0,0,0,114,3,0,0,0,114, + 6,0,0,0,114,184,0,0,0,68,6,0,0,115,8,0, + 0,0,0,5,12,1,8,1,8,1,114,184,0,0,0,99, + 1,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0, + 9,0,0,0,67,0,0,0,115,178,1,0,0,124,0,97, + 0,116,0,106,1,97,1,116,0,106,2,97,2,116,1,106, + 3,116,4,25,0,125,1,100,1,68,0,93,48,125,2,124, + 2,116,1,106,3,107,7,114,56,116,0,160,5,124,2,161, + 1,125,3,110,10,116,1,106,3,124,2,25,0,125,3,116, + 6,124,1,124,2,124,3,131,3,1,0,113,30,100,2,100, + 3,103,1,102,2,100,4,100,5,100,3,103,2,102,2,102, + 2,125,4,124,4,68,0,93,110,92,2,125,5,125,6,116, + 7,100,6,100,7,132,0,124,6,68,0,131,1,131,1,115, + 136,116,8,130,1,124,6,100,8,25,0,125,7,124,5,116, + 1,106,3,107,6,114,170,116,1,106,3,124,5,25,0,125, + 8,1,0,113,226,113,106,122,20,116,0,160,5,124,5,161, + 1,125,8,87,0,1,0,113,226,87,0,113,106,4,0,116, + 9,107,10,114,214,1,0,1,0,1,0,89,0,113,106,89, + 0,113,106,88,0,113,106,116,9,100,9,131,1,130,1,116, + 6,124,1,100,10,124,8,131,3,1,0,116,6,124,1,100, + 11,124,7,131,3,1,0,116,6,124,1,100,12,100,13,160, + 10,124,6,161,1,131,3,1,0,116,6,124,1,100,14,100, + 15,100,16,132,0,124,6,68,0,131,1,131,3,1,0,116, + 0,160,5,100,17,161,1,125,9,116,6,124,1,100,17,124, + 9,131,3,1,0,116,0,160,5,100,18,161,1,125,10,116, + 6,124,1,100,18,124,10,131,3,1,0,124,5,100,4,107, + 2,144,1,114,110,116,0,160,5,100,19,161,1,125,11,116, + 6,124,1,100,20,124,11,131,3,1,0,116,6,124,1,100, + 21,116,11,131,0,131,3,1,0,116,12,160,13,116,2,160, + 14,161,0,161,1,1,0,124,5,100,4,107,2,144,1,114, + 174,116,15,160,16,100,22,161,1,1,0,100,23,116,12,107, + 6,144,1,114,174,100,24,116,17,95,18,100,25,83,0,41, + 26,122,205,83,101,116,117,112,32,116,104,101,32,112,97,116, + 104,45,98,97,115,101,100,32,105,109,112,111,114,116,101,114, + 115,32,102,111,114,32,105,109,112,111,114,116,108,105,98,32, + 98,121,32,105,109,112,111,114,116,105,110,103,32,110,101,101, + 100,101,100,10,32,32,32,32,98,117,105,108,116,45,105,110, + 32,109,111,100,117,108,101,115,32,97,110,100,32,105,110,106, + 101,99,116,105,110,103,32,116,104,101,109,32,105,110,116,111, + 32,116,104,101,32,103,108,111,98,97,108,32,110,97,109,101, + 115,112,97,99,101,46,10,10,32,32,32,32,79,116,104,101, + 114,32,99,111,109,112,111,110,101,110,116,115,32,97,114,101, + 32,101,120,116,114,97,99,116,101,100,32,102,114,111,109,32, + 116,104,101,32,99,111,114,101,32,98,111,111,116,115,116,114, + 97,112,32,109,111,100,117,108,101,46,10,10,32,32,32,32, + 41,4,114,64,0,0,0,114,75,0,0,0,218,8,98,117, + 105,108,116,105,110,115,114,160,0,0,0,90,5,112,111,115, + 105,120,250,1,47,90,2,110,116,250,1,92,99,1,0,0, + 0,0,0,0,0,0,0,0,0,2,0,0,0,3,0,0, + 0,115,0,0,0,115,26,0,0,0,124,0,93,18,125,1, + 116,0,124,1,131,1,100,0,107,2,86,0,1,0,113,2, + 100,1,83,0,41,2,114,39,0,0,0,78,41,1,114,22, + 0,0,0,41,2,114,32,0,0,0,114,95,0,0,0,114, + 3,0,0,0,114,3,0,0,0,114,6,0,0,0,114,19, + 1,0,0,104,6,0,0,115,4,0,0,0,4,0,2,0, + 122,25,95,115,101,116,117,112,46,60,108,111,99,97,108,115, + 62,46,60,103,101,110,101,120,112,114,62,114,73,0,0,0, + 122,30,105,109,112,111,114,116,108,105,98,32,114,101,113,117, + 105,114,101,115,32,112,111,115,105,120,32,111,114,32,110,116, + 114,2,0,0,0,114,35,0,0,0,114,31,0,0,0,114, + 40,0,0,0,114,58,0,0,0,99,1,0,0,0,0,0, + 0,0,0,0,0,0,2,0,0,0,4,0,0,0,83,0, + 0,0,115,22,0,0,0,104,0,124,0,93,14,125,1,100, + 0,124,1,155,0,157,2,146,2,113,4,83,0,41,1,114, + 74,0,0,0,114,3,0,0,0,41,2,114,32,0,0,0, + 218,1,115,114,3,0,0,0,114,3,0,0,0,114,6,0, + 0,0,114,92,1,0,0,120,6,0,0,115,4,0,0,0, + 6,0,2,0,122,25,95,115,101,116,117,112,46,60,108,111, + 99,97,108,115,62,46,60,115,101,116,99,111,109,112,62,90, + 7,95,116,104,114,101,97,100,90,8,95,119,101,97,107,114, + 101,102,90,6,119,105,110,114,101,103,114,192,0,0,0,114, + 7,0,0,0,122,4,46,112,121,119,122,6,95,100,46,112, + 121,100,84,78,41,19,114,134,0,0,0,114,8,0,0,0, + 114,163,0,0,0,114,31,1,0,0,114,125,0,0,0,90, + 18,95,98,117,105,108,116,105,110,95,102,114,111,109,95,110, + 97,109,101,114,129,0,0,0,218,3,97,108,108,114,23,0, + 0,0,114,118,0,0,0,114,36,0,0,0,114,13,0,0, + 0,114,21,1,0,0,114,167,0,0,0,114,103,1,0,0, + 114,102,0,0,0,114,186,0,0,0,114,191,0,0,0,114, + 195,0,0,0,41,12,218,17,95,98,111,111,116,115,116,114, + 97,112,95,109,111,100,117,108,101,90,11,115,101,108,102,95, + 109,111,100,117,108,101,90,12,98,117,105,108,116,105,110,95, + 110,97,109,101,90,14,98,117,105,108,116,105,110,95,109,111, + 100,117,108,101,90,10,111,115,95,100,101,116,97,105,108,115, + 90,10,98,117,105,108,116,105,110,95,111,115,114,31,0,0, + 0,114,35,0,0,0,90,9,111,115,95,109,111,100,117,108, + 101,90,13,116,104,114,101,97,100,95,109,111,100,117,108,101, + 90,14,119,101,97,107,114,101,102,95,109,111,100,117,108,101, + 90,13,119,105,110,114,101,103,95,109,111,100,117,108,101,114, + 3,0,0,0,114,3,0,0,0,114,6,0,0,0,218,6, + 95,115,101,116,117,112,79,6,0,0,115,78,0,0,0,0, + 8,4,1,6,1,6,3,10,1,8,1,10,1,12,2,10, + 1,14,3,22,1,12,2,22,1,8,1,10,1,10,1,6, + 2,2,1,10,1,10,1,14,1,12,2,8,1,12,1,12, + 1,18,1,22,3,10,1,12,3,10,1,12,3,10,1,10, + 1,12,3,14,1,14,1,10,1,10,1,10,1,114,110,1, + 0,0,99,1,0,0,0,0,0,0,0,0,0,0,0,2, + 0,0,0,4,0,0,0,67,0,0,0,115,50,0,0,0, + 116,0,124,0,131,1,1,0,116,1,131,0,125,1,116,2, + 106,3,160,4,116,5,106,6,124,1,142,0,103,1,161,1, + 1,0,116,2,106,7,160,8,116,9,161,1,1,0,100,1, + 83,0,41,2,122,41,73,110,115,116,97,108,108,32,116,104, + 101,32,112,97,116,104,45,98,97,115,101,100,32,105,109,112, + 111,114,116,32,99,111,109,112,111,110,101,110,116,115,46,78, + 41,10,114,110,1,0,0,114,184,0,0,0,114,8,0,0, + 0,114,51,1,0,0,114,167,0,0,0,114,83,1,0,0, + 114,98,1,0,0,218,9,109,101,116,97,95,112,97,116,104, + 114,186,0,0,0,114,45,1,0,0,41,2,114,109,1,0, + 0,90,17,115,117,112,112,111,114,116,101,100,95,108,111,97, + 100,101,114,115,114,3,0,0,0,114,3,0,0,0,114,6, + 0,0,0,218,8,95,105,110,115,116,97,108,108,144,6,0, + 0,115,8,0,0,0,0,2,8,1,6,1,20,1,114,112, + 1,0,0,41,1,114,60,0,0,0,41,1,78,41,3,78, + 78,78,41,2,114,73,0,0,0,114,73,0,0,0,41,1, + 84,41,1,78,41,1,78,41,63,114,127,0,0,0,114,12, + 0,0,0,90,37,95,67,65,83,69,95,73,78,83,69,78, + 83,73,84,73,86,69,95,80,76,65,84,70,79,82,77,83, + 95,66,89,84,69,83,95,75,69,89,114,11,0,0,0,114, + 13,0,0,0,114,20,0,0,0,114,27,0,0,0,114,29, + 0,0,0,114,38,0,0,0,114,47,0,0,0,114,49,0, + 0,0,114,53,0,0,0,114,54,0,0,0,114,56,0,0, + 0,114,59,0,0,0,114,69,0,0,0,218,4,116,121,112, + 101,218,8,95,95,99,111,100,101,95,95,114,162,0,0,0, + 114,18,0,0,0,114,148,0,0,0,114,17,0,0,0,114, + 24,0,0,0,114,236,0,0,0,114,92,0,0,0,114,88, + 0,0,0,114,102,0,0,0,114,89,0,0,0,90,23,68, + 69,66,85,71,95,66,89,84,69,67,79,68,69,95,83,85, + 70,70,73,88,69,83,90,27,79,80,84,73,77,73,90,69, + 68,95,66,89,84,69,67,79,68,69,95,83,85,70,70,73, + 88,69,83,114,98,0,0,0,114,103,0,0,0,114,109,0, + 0,0,114,113,0,0,0,114,115,0,0,0,114,136,0,0, + 0,114,143,0,0,0,114,152,0,0,0,114,156,0,0,0, + 114,158,0,0,0,114,165,0,0,0,114,170,0,0,0,114, + 171,0,0,0,114,176,0,0,0,218,6,111,98,106,101,99, + 116,114,185,0,0,0,114,190,0,0,0,114,191,0,0,0, + 114,208,0,0,0,114,221,0,0,0,114,239,0,0,0,114, + 9,1,0,0,114,15,1,0,0,114,21,1,0,0,114,252, + 0,0,0,114,22,1,0,0,114,43,1,0,0,114,45,1, + 0,0,114,83,1,0,0,114,102,1,0,0,114,184,0,0, + 0,114,110,1,0,0,114,112,1,0,0,114,3,0,0,0, + 114,3,0,0,0,114,3,0,0,0,114,6,0,0,0,218, + 8,60,109,111,100,117,108,101,62,1,0,0,0,115,126,0, + 0,0,4,22,4,1,4,1,2,1,2,255,4,4,8,17, + 8,5,8,5,8,6,8,6,8,12,8,10,8,9,8,5, + 8,7,8,9,10,22,10,127,0,12,16,1,12,2,4,1, + 4,2,6,2,6,2,8,2,16,71,8,40,8,19,8,12, + 8,12,8,28,8,17,8,33,8,28,8,24,10,13,10,10, + 10,11,8,14,6,3,4,1,2,255,12,68,14,64,14,29, + 16,127,0,17,14,72,18,45,18,26,4,3,18,53,14,63, + 14,42,14,127,0,68,14,127,0,22,10,23,8,11,8,65, }; From webhook-mailer at python.org Sun Jul 28 21:01:17 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 01:01:17 -0000 Subject: [Python-checkins] bpo-36044: Avoid warnings in Windows PGO build and add lzma, bz2 and sqlite coverage (GH-14985) Message-ID: https://github.com/python/cpython/commit/e1b900247227dad49d8231f1d028872412230ab4 commit: e1b900247227dad49d8231f1d028872412230ab4 branch: master author: Steve Dower committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-28T18:01:12-07:00 summary: bpo-36044: Avoid warnings in Windows PGO build and add lzma, bz2 and sqlite coverage (GH-14985) https://bugs.python.org/issue36044 Automerge-Triggered-By: @zooba files: M Lib/test/libregrtest/pgo.py M PCbuild/_msi.vcxproj M PCbuild/winsound.vcxproj diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py index 327f19374c3f..379ff05fb955 100644 --- a/Lib/test/libregrtest/pgo.py +++ b/Lib/test/libregrtest/pgo.py @@ -10,6 +10,7 @@ 'test_binop', 'test_bisect', 'test_bytes', + 'test_bz2', 'test_cmath', 'test_codecs', 'test_collections', @@ -29,6 +30,7 @@ 'test_itertools', 'test_json', 'test_long', + 'test_lzma', 'test_math', 'test_memoryview', 'test_operator', @@ -37,6 +39,7 @@ 'test_pprint', 'test_re', 'test_set', + 'test_sqlite', 'test_statistics', 'test_struct', 'test_tabnanny', diff --git a/PCbuild/_msi.vcxproj b/PCbuild/_msi.vcxproj index 9f089ac7f52f..720eb2931be7 100644 --- a/PCbuild/_msi.vcxproj +++ b/PCbuild/_msi.vcxproj @@ -70,6 +70,7 @@ {31FFC478-7B4A-43E8-9954-8D03E2187E9C} _msi Win32Proj + false diff --git a/PCbuild/winsound.vcxproj b/PCbuild/winsound.vcxproj index 56da96a85528..32cedc9b4449 100644 --- a/PCbuild/winsound.vcxproj +++ b/PCbuild/winsound.vcxproj @@ -70,6 +70,7 @@ {28B5D777-DDF2-4B6B-B34F-31D938813856} winsound Win32Proj + false From webhook-mailer at python.org Sun Jul 28 21:20:12 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 01:20:12 -0000 Subject: [Python-checkins] bpo-36044: Avoid warnings in Windows PGO build and add lzma, bz2 and sqlite coverage (GH-14985) Message-ID: https://github.com/python/cpython/commit/36fd7b6f01127bc6a8b4a37a363e0aa9cfd76506 commit: 36fd7b6f01127bc6a8b4a37a363e0aa9cfd76506 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-28T18:20:02-07:00 summary: bpo-36044: Avoid warnings in Windows PGO build and add lzma, bz2 and sqlite coverage (GH-14985) https://bugs.python.org/issue36044 Automerge-Triggered-By: @zooba (cherry picked from commit e1b900247227dad49d8231f1d028872412230ab4) Co-authored-by: Steve Dower files: M Lib/test/libregrtest/pgo.py M PCbuild/_msi.vcxproj M PCbuild/winsound.vcxproj diff --git a/Lib/test/libregrtest/pgo.py b/Lib/test/libregrtest/pgo.py index 327f19374c3f..379ff05fb955 100644 --- a/Lib/test/libregrtest/pgo.py +++ b/Lib/test/libregrtest/pgo.py @@ -10,6 +10,7 @@ 'test_binop', 'test_bisect', 'test_bytes', + 'test_bz2', 'test_cmath', 'test_codecs', 'test_collections', @@ -29,6 +30,7 @@ 'test_itertools', 'test_json', 'test_long', + 'test_lzma', 'test_math', 'test_memoryview', 'test_operator', @@ -37,6 +39,7 @@ 'test_pprint', 'test_re', 'test_set', + 'test_sqlite', 'test_statistics', 'test_struct', 'test_tabnanny', diff --git a/PCbuild/_msi.vcxproj b/PCbuild/_msi.vcxproj index 9f089ac7f52f..720eb2931be7 100644 --- a/PCbuild/_msi.vcxproj +++ b/PCbuild/_msi.vcxproj @@ -70,6 +70,7 @@ {31FFC478-7B4A-43E8-9954-8D03E2187E9C} _msi Win32Proj + false diff --git a/PCbuild/winsound.vcxproj b/PCbuild/winsound.vcxproj index 56da96a85528..32cedc9b4449 100644 --- a/PCbuild/winsound.vcxproj +++ b/PCbuild/winsound.vcxproj @@ -70,6 +70,7 @@ {28B5D777-DDF2-4B6B-B34F-31D938813856} winsound Win32Proj + false From webhook-mailer at python.org Mon Jul 29 09:59:37 2019 From: webhook-mailer at python.org (Pablo Galindo) Date: Mon, 29 Jul 2019 13:59:37 -0000 Subject: [Python-checkins] Fix `SyntaxError` indicator printing too many spaces for multi-line strings (GH-14433) Message-ID: https://github.com/python/cpython/commit/5b94f3578c662d5f1ee90c0e6b81481d9ec82d89 commit: 5b94f3578c662d5f1ee90c0e6b81481d9ec82d89 branch: master author: Anthony Sottile committer: Pablo Galindo date: 2019-07-29T14:59:13+01:00 summary: Fix `SyntaxError` indicator printing too many spaces for multi-line strings (GH-14433) files: A Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst M Lib/test/test_cmd_line_script.py M Parser/tokenizer.c diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index 4677e60c8116..633e0fd746f5 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -613,6 +613,20 @@ def test_syntaxerror_indented_caret_position(self): self.assertNotIn("\f", text) self.assertIn("\n 1 + 1 = 2\n ^", text) + def test_syntaxerror_multi_line_fstring(self): + script = 'foo = f"""{}\nfoo"""\n' + with support.temp_dir() as script_dir: + script_name = _make_test_script(script_dir, 'script', script) + exitcode, stdout, stderr = assert_python_failure(script_name) + self.assertEqual( + stderr.splitlines()[-3:], + [ + b' foo = f"""{}', + b' ^', + b'SyntaxError: f-string: empty expression not allowed', + ], + ) + def test_consistent_sys_path_for_direct_execution(self): # This test case ensures that the following all give the same # sys.path configuration: diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst new file mode 100644 index 000000000000..794ddbbfec73 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst @@ -0,0 +1 @@ +Fix ``SyntaxError`` indicator printing too many spaces for multi-line strings - by Anthony Sottile. diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index c2ec659fed88..31fe970c9003 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -956,6 +956,7 @@ tok_nextc(struct tok_state *tok) while (!done) { Py_ssize_t curstart = tok->start == NULL ? -1 : tok->start - tok->buf; + Py_ssize_t cur_multi_line_start = tok->multi_line_start - tok->buf; Py_ssize_t curvalid = tok->inp - tok->buf; Py_ssize_t newsize = curvalid + BUFSIZ; char *newbuf = tok->buf; @@ -968,6 +969,7 @@ tok_nextc(struct tok_state *tok) } tok->buf = newbuf; tok->cur = tok->buf + cur; + tok->multi_line_start = tok->buf + cur_multi_line_start; tok->line_start = tok->cur; tok->inp = tok->buf + curvalid; tok->end = tok->buf + newsize; From webhook-mailer at python.org Mon Jul 29 10:19:09 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 14:19:09 -0000 Subject: [Python-checkins] Fix `SyntaxError` indicator printing too many spaces for multi-line strings (GH-14433) Message-ID: https://github.com/python/cpython/commit/cf52bd0b9b1c1b7ecdd91e1ebebd15ae5c213a2c commit: cf52bd0b9b1c1b7ecdd91e1ebebd15ae5c213a2c branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-29T07:18:47-07:00 summary: Fix `SyntaxError` indicator printing too many spaces for multi-line strings (GH-14433) (cherry picked from commit 5b94f3578c662d5f1ee90c0e6b81481d9ec82d89) Co-authored-by: Anthony Sottile files: A Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst M Lib/test/test_cmd_line_script.py M Parser/tokenizer.c diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index d138ca027c68..4cc265e60b66 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -601,6 +601,20 @@ def test_syntaxerror_indented_caret_position(self): self.assertNotIn("\f", text) self.assertIn("\n 1 + 1 = 2\n ^", text) + def test_syntaxerror_multi_line_fstring(self): + script = 'foo = f"""{}\nfoo"""\n' + with support.temp_dir() as script_dir: + script_name = _make_test_script(script_dir, 'script', script) + exitcode, stdout, stderr = assert_python_failure(script_name) + self.assertEqual( + stderr.splitlines()[-3:], + [ + b' foo = f"""{}', + b' ^', + b'SyntaxError: f-string: empty expression not allowed', + ], + ) + def test_consistent_sys_path_for_direct_execution(self): # This test case ensures that the following all give the same # sys.path configuration: diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst new file mode 100644 index 000000000000..794ddbbfec73 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst @@ -0,0 +1 @@ +Fix ``SyntaxError`` indicator printing too many spaces for multi-line strings - by Anthony Sottile. diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index c2ec659fed88..31fe970c9003 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -956,6 +956,7 @@ tok_nextc(struct tok_state *tok) while (!done) { Py_ssize_t curstart = tok->start == NULL ? -1 : tok->start - tok->buf; + Py_ssize_t cur_multi_line_start = tok->multi_line_start - tok->buf; Py_ssize_t curvalid = tok->inp - tok->buf; Py_ssize_t newsize = curvalid + BUFSIZ; char *newbuf = tok->buf; @@ -968,6 +969,7 @@ tok_nextc(struct tok_state *tok) } tok->buf = newbuf; tok->cur = tok->buf + cur; + tok->multi_line_start = tok->buf + cur_multi_line_start; tok->line_start = tok->cur; tok->inp = tok->buf + curvalid; tok->end = tok->buf + newsize; From webhook-mailer at python.org Mon Jul 29 10:47:36 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 14:47:36 -0000 Subject: [Python-checkins] bpo-37500: Make sure dead code does not generate bytecode but also detect syntax errors (GH-14612) Message-ID: https://github.com/python/cpython/commit/9ea738e580f58c3d2f9b0d56561d57b9e9412973 commit: 9ea738e580f58c3d2f9b0d56561d57b9e9412973 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-29T07:47:30-07:00 summary: bpo-37500: Make sure dead code does not generate bytecode but also detect syntax errors (GH-14612) https://bugs.python.org/issue37500 Add a new field to the compiler structure that allows to be configured so no bytecode is emitted. In this way is possible to detect errors by walking the nodes while preserving optimizations. https://bugs.python.org/issue37500 (cherry picked from commit 18c5f9d44dde37c0fae5585a604c6027825252d2) Co-authored-by: Pablo Galindo files: M Lib/test/test_compile.py M Lib/test/test_syntax.py M Lib/test/test_sys_settrace.py M Python/compile.c diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 56f73f631534..9d77f7af05d6 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -697,6 +697,40 @@ def test_stack_overflow(self): # complex statements. compile("if a: b\n" * 200000, "", "exec") + # Multiple users rely on the fact that CPython does not generate + # bytecode for dead code blocks. See bpo-37500 for more context. + @support.cpython_only + def test_dead_blocks_do_not_generate_bytecode(self): + def unused_block_if(): + if 0: + return 42 + + def unused_block_while(): + while 0: + return 42 + + def unused_block_if_else(): + if 1: + return None + else: + return 42 + + def unused_block_while_else(): + while 1: + return None + else: + return 42 + + funcs = [unused_block_if, unused_block_while, + unused_block_if_else, unused_block_while_else] + + for func in funcs: + opcodes = list(dis.get_instructions(func)) + self.assertEqual(2, len(opcodes)) + self.assertEqual('LOAD_CONST', opcodes[0].opname) + self.assertEqual(None, opcodes[0].argval) + self.assertEqual('RETURN_VALUE', opcodes[1].opname) + class TestExpressionStackSize(unittest.TestCase): # These tests check that the computed stack size for a code object diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 8451c072f642..3829746f1799 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -697,18 +697,47 @@ def test_break_outside_loop(self): self._check_error("break", "outside loop") def test_yield_outside_function(self): - self._check_error("if 0: yield", "outside function") - self._check_error("class C:\n if 0: yield", "outside function") + self._check_error("if 0: yield", "outside function") + self._check_error("if 0: yield\nelse: x=1", "outside function") + self._check_error("if 1: pass\nelse: yield", "outside function") + self._check_error("while 0: yield", "outside function") + self._check_error("while 0: yield\nelse: x=1", "outside function") + self._check_error("class C:\n if 0: yield", "outside function") + self._check_error("class C:\n if 1: pass\n else: yield", + "outside function") + self._check_error("class C:\n while 0: yield", "outside function") + self._check_error("class C:\n while 0: yield\n else: x = 1", + "outside function") def test_return_outside_function(self): - self._check_error("if 0: return", "outside function") - self._check_error("class C:\n if 0: return", "outside function") + self._check_error("if 0: return", "outside function") + self._check_error("if 0: return\nelse: x=1", "outside function") + self._check_error("if 1: pass\nelse: return", "outside function") + self._check_error("while 0: return", "outside function") + self._check_error("class C:\n if 0: return", "outside function") + self._check_error("class C:\n while 0: return", "outside function") + self._check_error("class C:\n while 0: return\n else: x=1", + "outside function") + self._check_error("class C:\n if 0: return\n else: x= 1", + "outside function") + self._check_error("class C:\n if 1: pass\n else: return", + "outside function") def test_break_outside_loop(self): - self._check_error("if 0: break", "outside loop") + self._check_error("if 0: break", "outside loop") + self._check_error("if 0: break\nelse: x=1", "outside loop") + self._check_error("if 1: pass\nelse: break", "outside loop") + self._check_error("class C:\n if 0: break", "outside loop") + self._check_error("class C:\n if 1: pass\n else: break", + "outside loop") def test_continue_outside_loop(self): - self._check_error("if 0: continue", "not properly in loop") + self._check_error("if 0: continue", "not properly in loop") + self._check_error("if 0: continue\nelse: x=1", "not properly in loop") + self._check_error("if 1: pass\nelse: continue", "not properly in loop") + self._check_error("class C:\n if 0: continue", "not properly in loop") + self._check_error("class C:\n if 1: pass\n else: continue", + "not properly in loop") def test_unexpected_indent(self): self._check_error("foo()\n bar()\n", "unexpected indent", diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 112ea877205a..fdd789475d04 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -53,22 +53,52 @@ def basic(): # following that clause? -# The entire "while 0:" statement is optimized away. No code -# exists for it, so the line numbers skip directly from "del x" -# to "x = 1". -def arigo_example(): +# Some constructs like "while 0:", "if 0:" or "if 1:...else:..." are optimized +# away. No code # exists for them, so the line numbers skip directly from +# "del x" to "x = 1". +def arigo_example0(): x = 1 del x while 0: pass x = 1 -arigo_example.events = [(0, 'call'), +arigo_example0.events = [(0, 'call'), (1, 'line'), (2, 'line'), (5, 'line'), (5, 'return')] +def arigo_example1(): + x = 1 + del x + if 0: + pass + x = 1 + +arigo_example1.events = [(0, 'call'), + (1, 'line'), + (2, 'line'), + (5, 'line'), + (5, 'return')] + +def arigo_example2(): + x = 1 + del x + if 1: + x = 1 + else: + pass + return None + +arigo_example2.events = [(0, 'call'), + (1, 'line'), + (2, 'line'), + (4, 'line'), + (7, 'line'), + (7, 'return')] + + # check that lines consisting of just one instruction get traced: def one_instr_line(): x = 1 @@ -349,8 +379,12 @@ def fn(*args): def test_01_basic(self): self.run_test(basic) - def test_02_arigo(self): - self.run_test(arigo_example) + def test_02_arigo0(self): + self.run_test(arigo_example0) + def test_02_arigo1(self): + self.run_test(arigo_example1) + def test_02_arigo2(self): + self.run_test(arigo_example2) def test_03_one_instr(self): self.run_test(one_instr_line) def test_04_no_pop_blocks(self): diff --git a/Python/compile.c b/Python/compile.c index 9cce8aeb4e1f..0336959d3da2 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -161,6 +161,11 @@ struct compiler { int c_optimize; /* optimization level */ int c_interactive; /* true if in interactive mode */ int c_nestlevel; + int c_do_not_emit_bytecode; /* The compiler won't emit any bytecode + if this value is different from zero. + This can be used to temporarily visit + nodes without emitting bytecode to + check only errors. */ PyObject *c_const_cache; /* Python dict holding all constants, including names tuple */ @@ -340,6 +345,7 @@ PyAST_CompileObject(mod_ty mod, PyObject *filename, PyCompilerFlags *flags, c.c_flags = flags; c.c_optimize = (optimize == -1) ? config->optimization_level : optimize; c.c_nestlevel = 0; + c.c_do_not_emit_bytecode = 0; if (!_PyAST_Optimize(mod, arena, c.c_optimize)) { goto finally; @@ -1152,6 +1158,9 @@ compiler_addop(struct compiler *c, int opcode) struct instr *i; int off; assert(!HAS_ARG(opcode)); + if (c->c_do_not_emit_bytecode) { + return 1; + } off = compiler_next_instr(c, c->u->u_curblock); if (off < 0) return 0; @@ -1305,6 +1314,10 @@ merge_consts_recursive(struct compiler *c, PyObject *o) static Py_ssize_t compiler_add_const(struct compiler *c, PyObject *o) { + if (c->c_do_not_emit_bytecode) { + return 0; + } + PyObject *key = merge_consts_recursive(c, o); if (key == NULL) { return -1; @@ -1318,6 +1331,10 @@ compiler_add_const(struct compiler *c, PyObject *o) static int compiler_addop_load_const(struct compiler *c, PyObject *o) { + if (c->c_do_not_emit_bytecode) { + return 1; + } + Py_ssize_t arg = compiler_add_const(c, o); if (arg < 0) return 0; @@ -1328,6 +1345,10 @@ static int compiler_addop_o(struct compiler *c, int opcode, PyObject *dict, PyObject *o) { + if (c->c_do_not_emit_bytecode) { + return 1; + } + Py_ssize_t arg = compiler_add_o(c, dict, o); if (arg < 0) return 0; @@ -1339,6 +1360,11 @@ compiler_addop_name(struct compiler *c, int opcode, PyObject *dict, PyObject *o) { Py_ssize_t arg; + + if (c->c_do_not_emit_bytecode) { + return 1; + } + PyObject *mangled = _Py_Mangle(c->u->u_private, o); if (!mangled) return 0; @@ -1359,6 +1385,10 @@ compiler_addop_i(struct compiler *c, int opcode, Py_ssize_t oparg) struct instr *i; int off; + if (c->c_do_not_emit_bytecode) { + return 1; + } + /* oparg value is unsigned, but a signed C int is usually used to store it in the C code (like Python/ceval.c). @@ -1385,6 +1415,10 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) struct instr *i; int off; + if (c->c_do_not_emit_bytecode) { + return 1; + } + assert(HAS_ARG(opcode)); assert(b != NULL); off = compiler_next_instr(c, c->u->u_curblock); @@ -1519,6 +1553,17 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) } \ } +/* These macros allows to check only for errors and not emmit bytecode + * while visiting nodes. +*/ + +#define BEGIN_DO_NOT_EMIT_BYTECODE { \ + c->c_do_not_emit_bytecode++; + +#define END_DO_NOT_EMIT_BYTECODE \ + c->c_do_not_emit_bytecode--; \ +} + /* Search if variable annotations are present statically in a block. */ static int @@ -2546,13 +2591,23 @@ compiler_if(struct compiler *c, stmt_ty s) return 0; constant = expr_constant(s->v.If.test); - /* constant = 0: "if 0" Leave the optimizations to - * the pephole optimizer to check for syntax errors - * in the block. + /* constant = 0: "if 0" * constant = 1: "if 1", "if 2", ... * constant = -1: rest */ - if (constant == 1) { + if (constant == 0) { + BEGIN_DO_NOT_EMIT_BYTECODE + VISIT_SEQ(c, stmt, s->v.If.body); + END_DO_NOT_EMIT_BYTECODE + if (s->v.If.orelse) { + VISIT_SEQ(c, stmt, s->v.If.orelse); + } + } else if (constant == 1) { VISIT_SEQ(c, stmt, s->v.If.body); + if (s->v.If.orelse) { + BEGIN_DO_NOT_EMIT_BYTECODE + VISIT_SEQ(c, stmt, s->v.If.orelse); + END_DO_NOT_EMIT_BYTECODE + } } else { if (asdl_seq_LEN(s->v.If.orelse)) { next = compiler_new_block(c); @@ -2662,8 +2717,12 @@ compiler_while(struct compiler *c, stmt_ty s) int constant = expr_constant(s->v.While.test); if (constant == 0) { - if (s->v.While.orelse) + BEGIN_DO_NOT_EMIT_BYTECODE + VISIT_SEQ(c, stmt, s->v.While.body); + END_DO_NOT_EMIT_BYTECODE + if (s->v.While.orelse) { VISIT_SEQ(c, stmt, s->v.While.orelse); + } return 1; } loop = compiler_new_block(c); From webhook-mailer at python.org Mon Jul 29 11:06:25 2019 From: webhook-mailer at python.org (Pablo Galindo) Date: Mon, 29 Jul 2019 15:06:25 -0000 Subject: [Python-checkins] Add additional test for multi-line SyntaxError (GH-15003) Message-ID: https://github.com/python/cpython/commit/44212ec8111febfe5fc6c6ed231d4ef2d98bd7e2 commit: 44212ec8111febfe5fc6c6ed231d4ef2d98bd7e2 branch: master author: Anthony Sottile committer: Pablo Galindo date: 2019-07-29T16:05:55+01:00 summary: Add additional test for multi-line SyntaxError (GH-15003) files: M Lib/test/test_cmd_line_script.py diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index 633e0fd746f5..80198f81a1c5 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -627,6 +627,22 @@ def test_syntaxerror_multi_line_fstring(self): ], ) + def test_syntaxerror_invalid_escape_sequence_multi_line(self): + script = 'foo = """\\q\n"""\n' + with support.temp_dir() as script_dir: + script_name = _make_test_script(script_dir, 'script', script) + exitcode, stdout, stderr = assert_python_failure( + '-Werror', script_name, + ) + self.assertEqual( + stderr.splitlines()[-3:], + [ + b' foo = """\\q', + b' ^', + b'SyntaxError: invalid escape sequence \\q', + ], + ) + def test_consistent_sys_path_for_direct_execution(self): # This test case ensures that the following all give the same # sys.path configuration: From webhook-mailer at python.org Mon Jul 29 11:26:34 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 15:26:34 -0000 Subject: [Python-checkins] Add additional test for multi-line SyntaxError (GH-15003) Message-ID: https://github.com/python/cpython/commit/bf0b8a6cb2dcdb2f19111628eec5bb32f7fa1166 commit: bf0b8a6cb2dcdb2f19111628eec5bb32f7fa1166 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-29T08:26:29-07:00 summary: Add additional test for multi-line SyntaxError (GH-15003) (cherry picked from commit 44212ec8111febfe5fc6c6ed231d4ef2d98bd7e2) Co-authored-by: Anthony Sottile files: M Lib/test/test_cmd_line_script.py diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index 4cc265e60b66..b74eeba81e04 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -615,6 +615,22 @@ def test_syntaxerror_multi_line_fstring(self): ], ) + def test_syntaxerror_invalid_escape_sequence_multi_line(self): + script = 'foo = """\\q\n"""\n' + with support.temp_dir() as script_dir: + script_name = _make_test_script(script_dir, 'script', script) + exitcode, stdout, stderr = assert_python_failure( + '-Werror', script_name, + ) + self.assertEqual( + stderr.splitlines()[-3:], + [ + b' foo = """\\q', + b' ^', + b'SyntaxError: invalid escape sequence \\q', + ], + ) + def test_consistent_sys_path_for_direct_execution(self): # This test case ensures that the following all give the same # sys.path configuration: From webhook-mailer at python.org Mon Jul 29 14:22:53 2019 From: webhook-mailer at python.org (Steve Dower) Date: Mon, 29 Jul 2019 18:22:53 -0000 Subject: [Python-checkins] Fix publishing of Windows release (GH-15006) Message-ID: https://github.com/python/cpython/commit/fe330fc4ad3b8218a84216a824af7d7007dcb85b commit: fe330fc4ad3b8218a84216a824af7d7007dcb85b branch: master author: Steve Dower committer: GitHub date: 2019-07-29T11:22:27-07:00 summary: Fix publishing of Windows release (GH-15006) files: M .azure-pipelines/windows-release/stage-publish-nugetorg.yml M Tools/msi/uploadrelease.ps1 diff --git a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml index 296eb28648b9..570cdb3ec57f 100644 --- a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml +++ b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml @@ -36,6 +36,6 @@ jobs: condition: and(succeeded(), eq(variables['SigningCertificate'], variables['__RealSigningCertificate'])) inputs: command: push - packagesToPush: $(Build.BinariesDirectory)\nuget\*.nupkg' + packagesToPush: '$(Build.BinariesDirectory)\nuget\*.nupkg' nuGetFeedType: external publishFeedCredentials: 'Python on Nuget' diff --git a/Tools/msi/uploadrelease.ps1 b/Tools/msi/uploadrelease.ps1 index 469a96818a1f..d3673b458298 100644 --- a/Tools/msi/uploadrelease.ps1 +++ b/Tools/msi/uploadrelease.ps1 @@ -92,6 +92,7 @@ if (-not $skipupload) { & $plink -batch $user@$server chgrp downloads $d & $plink -batch $user@$server chmod g-x,o+rx $d & $pscp -batch $chm.FullName "$user@${server}:$d" + if (-not $?) { throw "Failed to upload $chm" } $dirs = gci "$build" -Directory if ($embed) { @@ -107,6 +108,7 @@ if (-not $skipupload) { if ($exe) { & $pscp -batch $exe.FullName "$user@${server}:$d" + if (-not $?) { throw "Failed to upload $exe" } } if ($msi) { @@ -115,6 +117,7 @@ if (-not $skipupload) { & $plink -batch $user@$server chgrp downloads $sd & $plink -batch $user@$server chmod g-x,o+rx $sd & $pscp -batch $msi.FullName "$user@${server}:$sd" + if (-not $?) { throw "Failed to upload $msi" } & $plink -batch $user@$server chgrp downloads $sd* & $plink -batch $user@$server chmod g-x,o+r $sd* } @@ -122,6 +125,7 @@ if (-not $skipupload) { & $plink -batch $user@$server chgrp downloads $d* & $plink -batch $user@$server chmod g-x,o+r $d* + & $pscp -ls "$user@${server}:$d" } if (-not $skippurge) { From webhook-mailer at python.org Mon Jul 29 14:41:42 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 18:41:42 -0000 Subject: [Python-checkins] Fix publishing of Windows release (GH-15006) Message-ID: https://github.com/python/cpython/commit/494ed69bcf53975b7329de3761ca3cb261419422 commit: 494ed69bcf53975b7329de3761ca3cb261419422 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-29T11:41:37-07:00 summary: Fix publishing of Windows release (GH-15006) (cherry picked from commit fe330fc4ad3b8218a84216a824af7d7007dcb85b) Co-authored-by: Steve Dower files: M .azure-pipelines/windows-release/stage-publish-nugetorg.yml M Tools/msi/uploadrelease.ps1 diff --git a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml index 296eb28648b9..570cdb3ec57f 100644 --- a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml +++ b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml @@ -36,6 +36,6 @@ jobs: condition: and(succeeded(), eq(variables['SigningCertificate'], variables['__RealSigningCertificate'])) inputs: command: push - packagesToPush: $(Build.BinariesDirectory)\nuget\*.nupkg' + packagesToPush: '$(Build.BinariesDirectory)\nuget\*.nupkg' nuGetFeedType: external publishFeedCredentials: 'Python on Nuget' diff --git a/Tools/msi/uploadrelease.ps1 b/Tools/msi/uploadrelease.ps1 index 469a96818a1f..d3673b458298 100644 --- a/Tools/msi/uploadrelease.ps1 +++ b/Tools/msi/uploadrelease.ps1 @@ -92,6 +92,7 @@ if (-not $skipupload) { & $plink -batch $user@$server chgrp downloads $d & $plink -batch $user@$server chmod g-x,o+rx $d & $pscp -batch $chm.FullName "$user@${server}:$d" + if (-not $?) { throw "Failed to upload $chm" } $dirs = gci "$build" -Directory if ($embed) { @@ -107,6 +108,7 @@ if (-not $skipupload) { if ($exe) { & $pscp -batch $exe.FullName "$user@${server}:$d" + if (-not $?) { throw "Failed to upload $exe" } } if ($msi) { @@ -115,6 +117,7 @@ if (-not $skipupload) { & $plink -batch $user@$server chgrp downloads $sd & $plink -batch $user@$server chmod g-x,o+rx $sd & $pscp -batch $msi.FullName "$user@${server}:$sd" + if (-not $?) { throw "Failed to upload $msi" } & $plink -batch $user@$server chgrp downloads $sd* & $plink -batch $user@$server chmod g-x,o+r $sd* } @@ -122,6 +125,7 @@ if (-not $skipupload) { & $plink -batch $user@$server chgrp downloads $d* & $plink -batch $user@$server chmod g-x,o+r $d* + & $pscp -ls "$user@${server}:$d" } if (-not $skippurge) { From webhook-mailer at python.org Mon Jul 29 17:57:41 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Mon, 29 Jul 2019 21:57:41 -0000 Subject: [Python-checkins] bpo-37706: Disable 3 IDLE scrollbar tests on Mac. (#15010) Message-ID: https://github.com/python/cpython/commit/e8874b85b4e3bbb735467b0beaa933dcef362004 commit: e8874b85b4e3bbb735467b0beaa933dcef362004 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-29T17:57:36-04:00 summary: bpo-37706: Disable 3 IDLE scrollbar tests on Mac. (#15010) They pass with tk 8.5.9 (Azure) but fail with the 8.6.x we install. files: M Lib/idlelib/idle_test/test_sidebar.py diff --git a/Lib/idlelib/idle_test/test_sidebar.py b/Lib/idlelib/idle_test/test_sidebar.py index 8c98a0c0cbde..07e8f2e96b90 100644 --- a/Lib/idlelib/idle_test/test_sidebar.py +++ b/Lib/idlelib/idle_test/test_sidebar.py @@ -1,4 +1,6 @@ """Test sidebar, coverage 93%""" +import idlelib.sidebar +from sys import platform from itertools import chain import unittest import unittest.mock @@ -7,7 +9,6 @@ from idlelib.delegator import Delegator from idlelib.percolator import Percolator -import idlelib.sidebar class Dummy_editwin: @@ -239,6 +240,7 @@ def get_width(): self.assert_sidebar_n_lines(1) self.assertEqual(get_width(), 1) + @unittest.skipIf(platform == 'darwin', 'test tk version dependent') def test_click_selection(self): self.linenumber.show_sidebar() self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') @@ -252,14 +254,15 @@ def test_click_selection(self): self.assertEqual(self.get_selection(), ('2.0', '3.0')) - def test_drag_selection(self): + @unittest.skipIf(platform == 'darwin', 'test tk version dependent') + def test_drag_selection_down(self): self.linenumber.show_sidebar() - self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') + self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') self.root.update() - # Drag from the first line to the third line. - start_x, start_y = self.get_line_screen_position(1) - end_x, end_y = self.get_line_screen_position(3) + # Drag from the second line to the fourth line. + start_x, start_y = self.get_line_screen_position(2) + end_x, end_y = self.get_line_screen_position(4) self.linenumber.sidebar_text.event_generate('', x=start_x, y=start_y) self.linenumber.sidebar_text.event_generate('', @@ -269,8 +272,27 @@ def test_drag_selection(self): self.linenumber.sidebar_text.event_generate('', x=end_x, y=end_y) self.root.update() + self.assertEqual(self.get_selection(), ('2.0', '5.0')) + + @unittest.skipIf(platform == 'darwin', 'test tk version dependent') + def test_drag_selection_up(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') + self.root.update() - self.assertEqual(self.get_selection(), ('1.0', '4.0')) + # Drag from the fourth line to the second line. + start_x, start_y = self.get_line_screen_position(4) + end_x, end_y = self.get_line_screen_position(2) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.root.update() + self.assertEqual(self.get_selection(), ('2.0', '5.0')) def test_scroll(self): self.linenumber.show_sidebar() From webhook-mailer at python.org Mon Jul 29 18:12:28 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Mon, 29 Jul 2019 22:12:28 -0000 Subject: [Python-checkins] bpo-34162: Update idlelib/news.txt. (#15011) Message-ID: https://github.com/python/cpython/commit/f35c51d2eadd297bcf06d4f7c536bd1d8682b724 commit: f35c51d2eadd297bcf06d4f7c536bd1d8682b724 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-29T18:12:14-04:00 summary: bpo-34162: Update idlelib/news.txt. (#15011) files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 5d4a936d63f5..109b2d523a6e 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,30 @@ Released on 2019-10-20? ====================================== +bpo-37692: Improve highlight config sample with example shell +interaction and better labels for shell elements. + +bpo-37628: Settings dialog no longer expands with font size. +The font and highlight sample boxes gain scrollbars instead. + +bpo-17535: Add optional line numbers for IDLE editor windows. + +bpo-37627: Initialize the Customize Run dialog with the command line +arguments most recently entered before. The user can optionally edit +before submitting them. + +bpo-33610: Code context always shows the correct context when toggled on. + +bpo-36390: Gather Format menu functions into format.py. Combine +paragraph.py, rstrip.py, and format methods from editor.py. + +bpo-37530: Optimize code context to reduce unneeded background activity. +Font and highlight changes now occur along with text changes instead +of after a random delay. + +bpo-27452: Cleanup config.py by inlining RemoveFile and simplifying +the handling of __file__ in CreateConfigHandlers/ + bpo-26806: To compensate for stack frames added by IDLE and avoid possible problems with low recursion limits, add 30 to limits in the user code execution process. Subtract 30 when reporting recursion From webhook-mailer at python.org Mon Jul 29 18:15:55 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 22:15:55 -0000 Subject: [Python-checkins] bpo-37706: Disable 3 IDLE scrollbar tests on Mac. (GH-15010) Message-ID: https://github.com/python/cpython/commit/8513b90e114a815ebf4b389bcdfb08b49207a751 commit: 8513b90e114a815ebf4b389bcdfb08b49207a751 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-29T15:15:50-07:00 summary: bpo-37706: Disable 3 IDLE scrollbar tests on Mac. (GH-15010) They pass with tk 8.5.9 (Azure) but fail with the 8.6.x we install. (cherry picked from commit e8874b85b4e3bbb735467b0beaa933dcef362004) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/idle_test/test_sidebar.py diff --git a/Lib/idlelib/idle_test/test_sidebar.py b/Lib/idlelib/idle_test/test_sidebar.py index 8c98a0c0cbde..07e8f2e96b90 100644 --- a/Lib/idlelib/idle_test/test_sidebar.py +++ b/Lib/idlelib/idle_test/test_sidebar.py @@ -1,4 +1,6 @@ """Test sidebar, coverage 93%""" +import idlelib.sidebar +from sys import platform from itertools import chain import unittest import unittest.mock @@ -7,7 +9,6 @@ from idlelib.delegator import Delegator from idlelib.percolator import Percolator -import idlelib.sidebar class Dummy_editwin: @@ -239,6 +240,7 @@ def get_width(): self.assert_sidebar_n_lines(1) self.assertEqual(get_width(), 1) + @unittest.skipIf(platform == 'darwin', 'test tk version dependent') def test_click_selection(self): self.linenumber.show_sidebar() self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') @@ -252,14 +254,15 @@ def test_click_selection(self): self.assertEqual(self.get_selection(), ('2.0', '3.0')) - def test_drag_selection(self): + @unittest.skipIf(platform == 'darwin', 'test tk version dependent') + def test_drag_selection_down(self): self.linenumber.show_sidebar() - self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') + self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') self.root.update() - # Drag from the first line to the third line. - start_x, start_y = self.get_line_screen_position(1) - end_x, end_y = self.get_line_screen_position(3) + # Drag from the second line to the fourth line. + start_x, start_y = self.get_line_screen_position(2) + end_x, end_y = self.get_line_screen_position(4) self.linenumber.sidebar_text.event_generate('', x=start_x, y=start_y) self.linenumber.sidebar_text.event_generate('', @@ -269,8 +272,27 @@ def test_drag_selection(self): self.linenumber.sidebar_text.event_generate('', x=end_x, y=end_y) self.root.update() + self.assertEqual(self.get_selection(), ('2.0', '5.0')) + + @unittest.skipIf(platform == 'darwin', 'test tk version dependent') + def test_drag_selection_up(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') + self.root.update() - self.assertEqual(self.get_selection(), ('1.0', '4.0')) + # Drag from the fourth line to the second line. + start_x, start_y = self.get_line_screen_position(4) + end_x, end_y = self.get_line_screen_position(2) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.root.update() + self.assertEqual(self.get_selection(), ('2.0', '5.0')) def test_scroll(self): self.linenumber.show_sidebar() From webhook-mailer at python.org Mon Jul 29 18:23:03 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 22:23:03 -0000 Subject: [Python-checkins] bpo-37706: Disable 3 IDLE scrollbar tests on Mac. (GH-15010) Message-ID: https://github.com/python/cpython/commit/ad36d21223fd8118af8dbcf55ead03ee5905e7c3 commit: ad36d21223fd8118af8dbcf55ead03ee5905e7c3 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-29T15:22:59-07:00 summary: bpo-37706: Disable 3 IDLE scrollbar tests on Mac. (GH-15010) They pass with tk 8.5.9 (Azure) but fail with the 8.6.x we install. (cherry picked from commit e8874b85b4e3bbb735467b0beaa933dcef362004) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/idle_test/test_sidebar.py diff --git a/Lib/idlelib/idle_test/test_sidebar.py b/Lib/idlelib/idle_test/test_sidebar.py index 8c98a0c0cbde..07e8f2e96b90 100644 --- a/Lib/idlelib/idle_test/test_sidebar.py +++ b/Lib/idlelib/idle_test/test_sidebar.py @@ -1,4 +1,6 @@ """Test sidebar, coverage 93%""" +import idlelib.sidebar +from sys import platform from itertools import chain import unittest import unittest.mock @@ -7,7 +9,6 @@ from idlelib.delegator import Delegator from idlelib.percolator import Percolator -import idlelib.sidebar class Dummy_editwin: @@ -239,6 +240,7 @@ def get_width(): self.assert_sidebar_n_lines(1) self.assertEqual(get_width(), 1) + @unittest.skipIf(platform == 'darwin', 'test tk version dependent') def test_click_selection(self): self.linenumber.show_sidebar() self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') @@ -252,14 +254,15 @@ def test_click_selection(self): self.assertEqual(self.get_selection(), ('2.0', '3.0')) - def test_drag_selection(self): + @unittest.skipIf(platform == 'darwin', 'test tk version dependent') + def test_drag_selection_down(self): self.linenumber.show_sidebar() - self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') + self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') self.root.update() - # Drag from the first line to the third line. - start_x, start_y = self.get_line_screen_position(1) - end_x, end_y = self.get_line_screen_position(3) + # Drag from the second line to the fourth line. + start_x, start_y = self.get_line_screen_position(2) + end_x, end_y = self.get_line_screen_position(4) self.linenumber.sidebar_text.event_generate('', x=start_x, y=start_y) self.linenumber.sidebar_text.event_generate('', @@ -269,8 +272,27 @@ def test_drag_selection(self): self.linenumber.sidebar_text.event_generate('', x=end_x, y=end_y) self.root.update() + self.assertEqual(self.get_selection(), ('2.0', '5.0')) + + @unittest.skipIf(platform == 'darwin', 'test tk version dependent') + def test_drag_selection_up(self): + self.linenumber.show_sidebar() + self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') + self.root.update() - self.assertEqual(self.get_selection(), ('1.0', '4.0')) + # Drag from the fourth line to the second line. + start_x, start_y = self.get_line_screen_position(4) + end_x, end_y = self.get_line_screen_position(2) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=start_x, y=start_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.linenumber.sidebar_text.event_generate('', + x=end_x, y=end_y) + self.root.update() + self.assertEqual(self.get_selection(), ('2.0', '5.0')) def test_scroll(self): self.linenumber.show_sidebar() From webhook-mailer at python.org Mon Jul 29 18:36:48 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 22:36:48 -0000 Subject: [Python-checkins] bpo-34162: Update idlelib/news.txt. (GH-15011) Message-ID: https://github.com/python/cpython/commit/6c9f46fa3a90eeb02c35ba0e92e7f75edc67e51d commit: 6c9f46fa3a90eeb02c35ba0e92e7f75edc67e51d branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-29T15:36:20-07:00 summary: bpo-34162: Update idlelib/news.txt. (GH-15011) (cherry picked from commit f35c51d2eadd297bcf06d4f7c536bd1d8682b724) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 4fad45b2de9c..fc9f0fdee2c3 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,30 @@ Released on 2019-06-24? ====================================== +bpo-37692: Improve highlight config sample with example shell +interaction and better labels for shell elements. + +bpo-37628: Settings dialog no longer expands with font size. +The font and highlight sample boxes gain scrollbars instead. + +bpo-17535: Add optional line numbers for IDLE editor windows. + +bpo-37627: Initialize the Customize Run dialog with the command line +arguments most recently entered before. The user can optionally edit +before submitting them. + +bpo-33610: Code context always shows the correct context when toggled on. + +bpo-36390: Gather Format menu functions into format.py. Combine +paragraph.py, rstrip.py, and format methods from editor.py. + +bpo-37530: Optimize code context to reduce unneeded background activity. +Font and highlight changes now occur along with text changes instead +of after a random delay. + +bpo-27452: Cleanup config.py by inlining RemoveFile and simplifying +the handling of __file__ in CreateConfigHandlers/ + bpo-26806: To compensate for stack frames added by IDLE and avoid possible problems with low recursion limits, add 30 to limits in the user code execution process. Subtract 30 when reporting recursion From webhook-mailer at python.org Mon Jul 29 18:40:01 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 29 Jul 2019 22:40:01 -0000 Subject: [Python-checkins] bpo-34162: Update idlelib/news.txt. (GH-15011) Message-ID: https://github.com/python/cpython/commit/8b50e3e2729190d4b65ee9510d81f01bd31f2f7c commit: 8b50e3e2729190d4b65ee9510d81f01bd31f2f7c branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-29T15:39:49-07:00 summary: bpo-34162: Update idlelib/news.txt. (GH-15011) (cherry picked from commit f35c51d2eadd297bcf06d4f7c536bd1d8682b724) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 5d4a936d63f5..109b2d523a6e 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,30 @@ Released on 2019-10-20? ====================================== +bpo-37692: Improve highlight config sample with example shell +interaction and better labels for shell elements. + +bpo-37628: Settings dialog no longer expands with font size. +The font and highlight sample boxes gain scrollbars instead. + +bpo-17535: Add optional line numbers for IDLE editor windows. + +bpo-37627: Initialize the Customize Run dialog with the command line +arguments most recently entered before. The user can optionally edit +before submitting them. + +bpo-33610: Code context always shows the correct context when toggled on. + +bpo-36390: Gather Format menu functions into format.py. Combine +paragraph.py, rstrip.py, and format methods from editor.py. + +bpo-37530: Optimize code context to reduce unneeded background activity. +Font and highlight changes now occur along with text changes instead +of after a random delay. + +bpo-27452: Cleanup config.py by inlining RemoveFile and simplifying +the handling of __file__ in CreateConfigHandlers/ + bpo-26806: To compensate for stack frames added by IDLE and avoid possible problems with low recursion limits, add 30 to limits in the user code execution process. Subtract 30 when reporting recursion From webhook-mailer at python.org Mon Jul 29 19:21:26 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Mon, 29 Jul 2019 23:21:26 -0000 Subject: [Python-checkins] bpo-34162: Update idlelib/news.txt for 3.7.5 (GH-15016) Message-ID: https://github.com/python/cpython/commit/e941cbcf8c2fce59a9afa8043948f9a147e09177 commit: e941cbcf8c2fce59a9afa8043948f9a147e09177 branch: 3.7 author: Terry Jan Reedy committer: GitHub date: 2019-07-29T19:21:11-04:00 summary: bpo-34162: Update idlelib/news.txt for 3.7.5 (GH-15016) files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index fc9f0fdee2c3..2b223e1c8b7a 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,5 +1,5 @@ -What's New in IDLE 3.7.4 -Released on 2019-06-24? +What's New in IDLE 3.7.5 +Released on 2019-09-30? ====================================== @@ -27,6 +27,11 @@ of after a random delay. bpo-27452: Cleanup config.py by inlining RemoveFile and simplifying the handling of __file__ in CreateConfigHandlers/ + +What's New in IDLE 3.7.4 +Released on 2019-07-08 +====================================== + bpo-26806: To compensate for stack frames added by IDLE and avoid possible problems with low recursion limits, add 30 to limits in the user code execution process. Subtract 30 when reporting recursion From webhook-mailer at python.org Tue Jul 30 07:04:24 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 30 Jul 2019 11:04:24 -0000 Subject: [Python-checkins] bpo-37268: Add deprecation notice and a DeprecationWarning for the parser module (GH-15017) Message-ID: https://github.com/python/cpython/commit/9211e2fd81fe1db6f73ded70752b144cc9691ab6 commit: 9211e2fd81fe1db6f73ded70752b144cc9691ab6 branch: master author: Pablo Galindo committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2019-07-30T04:04:01-07:00 summary: bpo-37268: Add deprecation notice and a DeprecationWarning for the parser module (GH-15017) Deprecate the parser module and add a deprecation warning triggered on import and a warning block in the documentation. https://bugs.python.org/issue37268 Automerge-Triggered-By: @pablogsal files: A Misc/NEWS.d/next/Library/2019-07-30-01-27-29.bpo-37268.QDmA44.rst M Doc/library/parser.rst M Doc/whatsnew/3.9.rst M Lib/test/test_parser.py M Modules/parsermodule.c diff --git a/Doc/library/parser.rst b/Doc/library/parser.rst index a302681eca05..c55cd653b371 100644 --- a/Doc/library/parser.rst +++ b/Doc/library/parser.rst @@ -25,11 +25,11 @@ from this. This is better than trying to parse and modify an arbitrary Python code fragment as a string because parsing is performed in a manner identical to the code forming the application. It is also faster. -.. note:: +.. warning:: - From Python 2.5 onward, it's much more convenient to cut in at the Abstract - Syntax Tree (AST) generation and compilation stage, using the :mod:`ast` - module. + The parser module is deprecated and will be removed in future versions of + Python. For the majority of use cases you can leverage the Abstract Syntax + Tree (AST) generation and compilation stage, using the :mod:`ast` module. There are a few things to note about this module which are important to making use of the data structures created. This is not a tutorial on editing the parse diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 85e254f061eb..273fd2b50d59 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -142,6 +142,10 @@ Deprecated Python versions it will raise a :exc:`TypeError` for all floats. (Contributed by Serhiy Storchaka in :issue:`37315`.) +* The :mod:`parser` module is deprecated and will be removed in future versions + of Python. For the majority of use cases users can leverage the Abstract Syntax + Tree (AST) generation and compilation stage, using the :mod:`ast` module. + Removed ======= diff --git a/Lib/test/test_parser.py b/Lib/test/test_parser.py index e5285c636022..ec1845d7fe9a 100644 --- a/Lib/test/test_parser.py +++ b/Lib/test/test_parser.py @@ -6,6 +6,7 @@ import struct from test import support from test.support.script_helper import assert_python_failure +from test.support.script_helper import assert_python_ok # # First, we test that we can generate trees from valid source fragments, @@ -987,5 +988,13 @@ def test_two_args_to_expr(self): with self.assertRaises(TypeError): parser.expr("a", "b") + +class TestDeprecation(unittest.TestCase): + def test_deprecation_message(self): + code = "def f():\n import parser\n\nf()" + rc, out, err = assert_python_ok('-c', code) + self.assertIn(b':2: DeprecationWarning', err) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2019-07-30-01-27-29.bpo-37268.QDmA44.rst b/Misc/NEWS.d/next/Library/2019-07-30-01-27-29.bpo-37268.QDmA44.rst new file mode 100644 index 000000000000..d5c7b1d2fc66 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-30-01-27-29.bpo-37268.QDmA44.rst @@ -0,0 +1,2 @@ +The :mod:`parser` module is deprecated and will be removed in future +versions of Python. diff --git a/Modules/parsermodule.c b/Modules/parsermodule.c index 079d00f32aa6..b2495fc8b322 100644 --- a/Modules/parsermodule.c +++ b/Modules/parsermodule.c @@ -1158,6 +1158,12 @@ PyInit_parser(void) { PyObject *module, *copyreg; + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "The parser module is deprecated and will be removed " + "in future versions of Python", 7) != 0) { + return NULL; + } + if (PyType_Ready(&PyST_Type) < 0) return NULL; module = PyModule_Create(&parsermodule); From webhook-mailer at python.org Tue Jul 30 10:16:50 2019 From: webhook-mailer at python.org (Nick Coghlan) Date: Tue, 30 Jul 2019 14:16:50 -0000 Subject: [Python-checkins] bpo-37587: Make json.loads faster for long strings (GH-14752) Message-ID: https://github.com/python/cpython/commit/8a758f5b99c5fc3fd32edeac049d7d4a4b7cc163 commit: 8a758f5b99c5fc3fd32edeac049d7d4a4b7cc163 branch: master author: Marco Paolini committer: Nick Coghlan date: 2019-07-31T00:16:34+10:00 summary: bpo-37587: Make json.loads faster for long strings (GH-14752) When scanning the string, most characters are valid, so checking for invalid characters first means never needing to check the value of strict on valid strings, and only needing to check it on invalid characters when doing non-strict parsing of invalid strings. This provides a measurable reduction in per-character processing time (~11% in the pre-merge patch testing). files: A Misc/NEWS.d/next/Library/2019-07-13-16-02-48.bpo-37587.fd-1aF.rst M Modules/_json.c diff --git a/Misc/NEWS.d/next/Library/2019-07-13-16-02-48.bpo-37587.fd-1aF.rst b/Misc/NEWS.d/next/Library/2019-07-13-16-02-48.bpo-37587.fd-1aF.rst new file mode 100644 index 000000000000..80a89feab0ce --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-13-16-02-48.bpo-37587.fd-1aF.rst @@ -0,0 +1 @@ +Make json.loads faster for long strings. (Patch by Marco Paolini) diff --git a/Modules/_json.c b/Modules/_json.c index 38beb6f50d2e..76da1d345e9d 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -439,7 +439,7 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next if (c == '"' || c == '\\') { break; } - else if (strict && c <= 0x1f) { + else if (c <= 0x1f && strict) { raise_errmsg("Invalid control character at", pystr, next); goto bail; } From webhook-mailer at python.org Tue Jul 30 10:37:51 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 30 Jul 2019 14:37:51 -0000 Subject: [Python-checkins] bpo-37587: Make json.loads faster for long strings (GH-14752) Message-ID: https://github.com/python/cpython/commit/9265a877426af4fa5c44cc8482e0198806889350 commit: 9265a877426af4fa5c44cc8482e0198806889350 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T07:37:28-07:00 summary: bpo-37587: Make json.loads faster for long strings (GH-14752) When scanning the string, most characters are valid, so checking for invalid characters first means never needing to check the value of strict on valid strings, and only needing to check it on invalid characters when doing non-strict parsing of invalid strings. This provides a measurable reduction in per-character processing time (~11% in the pre-merge patch testing). (cherry picked from commit 8a758f5b99c5fc3fd32edeac049d7d4a4b7cc163) Co-authored-by: Marco Paolini files: A Misc/NEWS.d/next/Library/2019-07-13-16-02-48.bpo-37587.fd-1aF.rst M Modules/_json.c diff --git a/Misc/NEWS.d/next/Library/2019-07-13-16-02-48.bpo-37587.fd-1aF.rst b/Misc/NEWS.d/next/Library/2019-07-13-16-02-48.bpo-37587.fd-1aF.rst new file mode 100644 index 000000000000..80a89feab0ce --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-13-16-02-48.bpo-37587.fd-1aF.rst @@ -0,0 +1 @@ +Make json.loads faster for long strings. (Patch by Marco Paolini) diff --git a/Modules/_json.c b/Modules/_json.c index e3aa997598fc..048a9654ce18 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -439,7 +439,7 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next if (c == '"' || c == '\\') { break; } - else if (strict && c <= 0x1f) { + else if (c <= 0x1f && strict) { raise_errmsg("Invalid control character at", pystr, next); goto bail; } From webhook-mailer at python.org Tue Jul 30 11:45:34 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 30 Jul 2019 15:45:34 -0000 Subject: [Python-checkins] bpo-37704: Remove Tools/scripts/h2py.py (GH-15000) Message-ID: https://github.com/python/cpython/commit/27eeaf0f2c9bd53a0fbdfdf38ee759e971221f8d commit: 27eeaf0f2c9bd53a0fbdfdf38ee759e971221f8d branch: master author: Victor Stinner committer: GitHub date: 2019-07-30T17:45:09+02:00 summary: bpo-37704: Remove Tools/scripts/h2py.py (GH-15000) Use cffi to access a C API in Python. files: A Misc/NEWS.d/next/Tools-Demos/2019-07-29-13-59-19.bpo-37704.xxGUz_.rst D Tools/scripts/h2py.py M Lib/test/test_tools/test_sundry.py M Tools/scripts/README diff --git a/Lib/test/test_tools/test_sundry.py b/Lib/test/test_tools/test_sundry.py index f5fed01491e3..10eb6941b3be 100644 --- a/Lib/test/test_tools/test_sundry.py +++ b/Lib/test/test_tools/test_sundry.py @@ -2,7 +2,7 @@ This file contains extremely basic regression tests for the scripts found in the Tools directory of a Python checkout or tarball which don't have separate -tests of their own, such as h2py.py. +tests of their own. """ import os diff --git a/Misc/NEWS.d/next/Tools-Demos/2019-07-29-13-59-19.bpo-37704.xxGUz_.rst b/Misc/NEWS.d/next/Tools-Demos/2019-07-29-13-59-19.bpo-37704.xxGUz_.rst new file mode 100644 index 000000000000..52ca4895dee9 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2019-07-29-13-59-19.bpo-37704.xxGUz_.rst @@ -0,0 +1 @@ +Remove ``Tools/scripts/h2py.py``: use cffi to access a C API in Python. diff --git a/Tools/scripts/README b/Tools/scripts/README index d4ac2ade25ef..bee5b0bd9b20 100644 --- a/Tools/scripts/README +++ b/Tools/scripts/README @@ -30,7 +30,6 @@ ftpmirror.py FTP mirror script get-remote-certificate.py Fetch the certificate that the server(s) are providing in PEM form google.py Open a webbrowser with Google gprof2html.py Transform gprof(1) output into useful HTML -h2py.py Translate #define's into Python assignments highlight.py Python syntax highlighting with HTML output idle3 Main program to start IDLE ifdef.py Remove #if(n)def groups from C sources diff --git a/Tools/scripts/h2py.py b/Tools/scripts/h2py.py deleted file mode 100755 index ea37c04d4c56..000000000000 --- a/Tools/scripts/h2py.py +++ /dev/null @@ -1,171 +0,0 @@ -#! /usr/bin/env python3 - -# Read #define's and translate to Python code. -# Handle #include statements. -# Handle #define macros with one argument. -# Anything that isn't recognized or doesn't translate into valid -# Python is ignored. - -# Without filename arguments, acts as a filter. -# If one or more filenames are given, output is written to corresponding -# filenames in the local directory, translated to all uppercase, with -# the extension replaced by ".py". - -# By passing one or more options of the form "-i regular_expression" -# you can specify additional strings to be ignored. This is useful -# e.g. to ignore casts to u_long: simply specify "-i '(u_long)'". - -# XXX To do: -# - turn trailing C comments into Python comments -# - turn C Boolean operators "&& || !" into Python "and or not" -# - what to do about #if(def)? -# - what to do about macros with multiple parameters? - -import sys, re, getopt, os - -p_define = re.compile(r'^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+') - -p_macro = re.compile( - r'^[\t ]*#[\t ]*define[\t ]+' - r'([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+') - -p_include = re.compile(r'^[\t ]*#[\t ]*include[\t ]+<([^>\n]+)>') - -p_comment = re.compile(r'/\*([^*]+|\*+[^/])*(\*+/)?') -p_cpp_comment = re.compile('//.*') - -ignores = [p_comment, p_cpp_comment] - -p_char = re.compile(r"'(\\.[^\\]*|[^\\])'") - -p_hex = re.compile(r"0x([0-9a-fA-F]+)L?") - -filedict = {} -importable = {} - -try: - searchdirs=os.environ['include'].split(';') -except KeyError: - try: - searchdirs=os.environ['INCLUDE'].split(';') - except KeyError: - searchdirs=['/usr/include'] - try: - searchdirs.insert(0, os.path.join('/usr/include', - os.environ['MULTIARCH'])) - except KeyError: - pass - -def main(): - global filedict - opts, args = getopt.getopt(sys.argv[1:], 'i:') - for o, a in opts: - if o == '-i': - ignores.append(re.compile(a)) - if not args: - args = ['-'] - for filename in args: - if filename == '-': - sys.stdout.write('# Generated by h2py from stdin\n') - process(sys.stdin, sys.stdout) - else: - with open(filename) as fp: - outfile = os.path.basename(filename) - i = outfile.rfind('.') - if i > 0: outfile = outfile[:i] - modname = outfile.upper() - outfile = modname + '.py' - with open(outfile, 'w') as outfp: - outfp.write('# Generated by h2py from %s\n' % filename) - filedict = {} - for dir in searchdirs: - if filename[:len(dir)] == dir: - filedict[filename[len(dir)+1:]] = None # no '/' trailing - importable[filename[len(dir)+1:]] = modname - break - process(fp, outfp) - -def pytify(body): - # replace ignored patterns by spaces - for p in ignores: - body = p.sub(' ', body) - # replace char literals by ord(...) - body = p_char.sub("ord('\\1')", body) - # Compute negative hexadecimal constants - start = 0 - UMAX = 2*(sys.maxsize+1) - while 1: - m = p_hex.search(body, start) - if not m: break - s,e = m.span() - val = int(body[slice(*m.span(1))], 16) - if val > sys.maxsize: - val -= UMAX - body = body[:s] + "(" + str(val) + ")" + body[e:] - start = s + 1 - return body - -def process(fp, outfp, env = {}): - lineno = 0 - while 1: - line = fp.readline() - if not line: break - lineno = lineno + 1 - match = p_define.match(line) - if match: - # gobble up continuation lines - while line[-2:] == '\\\n': - nextline = fp.readline() - if not nextline: break - lineno = lineno + 1 - line = line + nextline - name = match.group(1) - body = line[match.end():] - body = pytify(body) - ok = 0 - stmt = '%s = %s\n' % (name, body.strip()) - try: - exec(stmt, env) - except: - sys.stderr.write('Skipping: %s' % stmt) - else: - outfp.write(stmt) - match = p_macro.match(line) - if match: - macro, arg = match.group(1, 2) - body = line[match.end():] - body = pytify(body) - stmt = 'def %s(%s): return %s\n' % (macro, arg, body) - try: - exec(stmt, env) - except: - sys.stderr.write('Skipping: %s' % stmt) - else: - outfp.write(stmt) - match = p_include.match(line) - if match: - regs = match.regs - a, b = regs[1] - filename = line[a:b] - if filename in importable: - outfp.write('from %s import *\n' % importable[filename]) - elif filename not in filedict: - filedict[filename] = None - inclfp = None - for dir in searchdirs: - try: - inclfp = open(dir + '/' + filename) - break - except IOError: - pass - if inclfp: - with inclfp: - outfp.write( - '\n# Included from %s\n' % filename) - process(inclfp, outfp, env) - else: - sys.stderr.write('Warning - could not find file %s\n' % - filename) - -if __name__ == '__main__': - main() From webhook-mailer at python.org Tue Jul 30 14:08:27 2019 From: webhook-mailer at python.org (Neil Schemenauer) Date: Tue, 30 Jul 2019 18:08:27 -0000 Subject: [Python-checkins] bpo-37707: Exclude expensive unit tests from PGO task (GH-15009) Message-ID: https://github.com/python/cpython/commit/52a48e62c6a94577152f9301bbe5f3bc806cfcf1 commit: 52a48e62c6a94577152f9301bbe5f3bc806cfcf1 branch: master author: Neil Schemenauer committer: GitHub date: 2019-07-30T11:08:18-07:00 summary: bpo-37707: Exclude expensive unit tests from PGO task (GH-15009) Mark some individual tests to skip when --pgo is used. The tests marked increase the PGO task time significantly and likely don't help improve optimization of the final executable. files: A Misc/NEWS.d/next/Build/2019-07-29-11-36-16.bpo-37707.Sm-dGk.rst M Lib/test/libregrtest/main.py M Lib/test/pickletester.py M Lib/test/support/__init__.py M Lib/test/test_bz2.py M Lib/test/test_itertools.py M Lib/test/test_lzma.py M Lib/test/test_statistics.py diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 2b6607df15dd..87278d0c76a2 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -643,6 +643,7 @@ def _main(self, tests, kwargs): input("Press any key to continue...") support.PGO = self.ns.pgo + support.PGO_EXTENDED = self.ns.pgo_extended setup_tests(self.ns) diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 2bc99e6becfd..5d402a4711bf 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2281,6 +2281,7 @@ def test_setitems_on_non_dicts(self): FRAME_SIZE_MIN = 4 FRAME_SIZE_TARGET = 64 * 1024 + @support.skip_if_pgo_task def check_frame_opcodes(self, pickled): """ Check the arguments of FRAME opcodes in a protocol 4+ pickle. @@ -2328,6 +2329,7 @@ def check_frame_opcodes(self, pickled): elif frameless_start is not None: self.assertLess(pos - frameless_start, self.FRAME_SIZE_MIN) + @support.skip_if_pgo_task def test_framing_many_objects(self): obj = list(range(10**5)) for proto in range(4, pickle.HIGHEST_PROTOCOL + 1): @@ -2417,6 +2419,7 @@ def remove_frames(pickled, keep_frame=None): count_opcode(pickle.FRAME, pickled)) self.assertEqual(obj, self.loads(some_frames_pickle)) + @support.skip_if_pgo_task def test_framed_write_sizes_with_delayed_writer(self): class ChunkAccumulator: """Accumulate pickler output in a list of raw chunks.""" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 9efb3d91705f..dbbbdb0ee339 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -973,6 +973,10 @@ def dec(*args, **kwargs): # useful for PGO PGO = False +# Set by libregrtest/main.py if we are running the extended (time consuming) +# PGO task. If this is True, PGO is also True. +PGO_EXTENDED = False + @contextlib.contextmanager def temp_dir(path=None, quiet=False): """Return a context manager that creates a temporary directory. @@ -2638,6 +2642,12 @@ def skip_unless_xattr(test): msg = "no non-broken extended attribute support" return test if ok else unittest.skip(msg)(test) +def skip_if_pgo_task(test): + """Skip decorator for tests not run in (non-extended) PGO task""" + ok = not PGO or PGO_EXTENDED + msg = "Not run for (non-extended) PGO task" + return test if ok else unittest.skip(msg)(test) + _bind_nix_socket_error = None def skip_unless_bind_unix_socket(test): """Decorator for tests requiring a functional bind() for unix sockets.""" diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index f158b901b9c9..eb2f72ee4a5d 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -643,6 +643,7 @@ def testCompressChunks10(self): data += bz2c.flush() self.assertEqual(ext_decompress(data), self.TEXT) + @support.skip_if_pgo_task @bigmemtest(size=_4G + 100, memuse=2) def testCompress4G(self, size): # "Test BZ2Compressor.compress()/flush() with >4GiB input" @@ -701,6 +702,7 @@ def testEOFError(self): self.assertRaises(EOFError, bz2d.decompress, b"anything") self.assertRaises(EOFError, bz2d.decompress, b"") + @support.skip_if_pgo_task @bigmemtest(size=_4G + 100, memuse=3.3) def testDecompress4G(self, size): # "Test BZ2Decompressor.decompress() with >4GiB input" diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index ea060a98a5ee..573739fde14c 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -2062,6 +2062,7 @@ def gen2(x): self.assertRaises(AssertionError, list, cycle(gen1())) self.assertEqual(hist, [0,1]) + @support.skip_if_pgo_task def test_long_chain_of_empty_iterables(self): # Make sure itertools.chain doesn't run into recursion limits when # dealing with long chains of empty iterables. Even with a high diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index 3dc2c1e7e3b7..117de0a1b554 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -333,6 +333,7 @@ def test_decompressor_multistream(self): # Test with inputs larger than 4GiB. + @support.skip_if_pgo_task @bigmemtest(size=_4G + 100, memuse=2) def test_compressor_bigmem(self, size): lzc = LZMACompressor() @@ -344,6 +345,7 @@ def test_compressor_bigmem(self, size): finally: ddata = None + @support.skip_if_pgo_task @bigmemtest(size=_4G + 100, memuse=3) def test_decompressor_bigmem(self, size): lzd = LZMADecompressor() diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index ed2f6579b0b9..104718ed7db6 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -14,6 +14,7 @@ import random import sys import unittest +from test import support from decimal import Decimal from fractions import Fraction @@ -2462,6 +2463,7 @@ def test_cdf(self): self.assertEqual(X.cdf(float('Inf')), 1.0) self.assertTrue(math.isnan(X.cdf(float('NaN')))) + @support.skip_if_pgo_task def test_inv_cdf(self): NormalDist = statistics.NormalDist diff --git a/Misc/NEWS.d/next/Build/2019-07-29-11-36-16.bpo-37707.Sm-dGk.rst b/Misc/NEWS.d/next/Build/2019-07-29-11-36-16.bpo-37707.Sm-dGk.rst new file mode 100644 index 000000000000..c0d58ab747b2 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2019-07-29-11-36-16.bpo-37707.Sm-dGk.rst @@ -0,0 +1,3 @@ +Mark some individual tests to skip when --pgo is used. The tests marked +increase the PGO task time significantly and likely don't help improve +optimization of the final executable. From webhook-mailer at python.org Tue Jul 30 14:34:49 2019 From: webhook-mailer at python.org (Neil Schemenauer) Date: Tue, 30 Jul 2019 18:34:49 -0000 Subject: [Python-checkins] bpo-37707: Exclude expensive unit tests from PGO task (GH-15009) (#15024) Message-ID: https://github.com/python/cpython/commit/382cb85401bb010ead411c0532499ffe16c3cf27 commit: 382cb85401bb010ead411c0532499ffe16c3cf27 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Neil Schemenauer date: 2019-07-30T11:34:32-07:00 summary: bpo-37707: Exclude expensive unit tests from PGO task (GH-15009) (#15024) Mark some individual tests to skip when --pgo is used. The tests marked increase the PGO task time significantly and likely don't help improve optimization of the final executable. (cherry picked from commit 52a48e62c6a94577152f9301bbe5f3bc806cfcf1) Co-authored-by: Neil Schemenauer files: A Misc/NEWS.d/next/Build/2019-07-29-11-36-16.bpo-37707.Sm-dGk.rst M Lib/test/libregrtest/main.py M Lib/test/pickletester.py M Lib/test/support/__init__.py M Lib/test/test_bz2.py M Lib/test/test_itertools.py M Lib/test/test_lzma.py M Lib/test/test_statistics.py diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 2b6607df15dd..87278d0c76a2 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -643,6 +643,7 @@ def _main(self, tests, kwargs): input("Press any key to continue...") support.PGO = self.ns.pgo + support.PGO_EXTENDED = self.ns.pgo_extended setup_tests(self.ns) diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 2bc99e6becfd..5d402a4711bf 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2281,6 +2281,7 @@ def test_setitems_on_non_dicts(self): FRAME_SIZE_MIN = 4 FRAME_SIZE_TARGET = 64 * 1024 + @support.skip_if_pgo_task def check_frame_opcodes(self, pickled): """ Check the arguments of FRAME opcodes in a protocol 4+ pickle. @@ -2328,6 +2329,7 @@ def check_frame_opcodes(self, pickled): elif frameless_start is not None: self.assertLess(pos - frameless_start, self.FRAME_SIZE_MIN) + @support.skip_if_pgo_task def test_framing_many_objects(self): obj = list(range(10**5)) for proto in range(4, pickle.HIGHEST_PROTOCOL + 1): @@ -2417,6 +2419,7 @@ def remove_frames(pickled, keep_frame=None): count_opcode(pickle.FRAME, pickled)) self.assertEqual(obj, self.loads(some_frames_pickle)) + @support.skip_if_pgo_task def test_framed_write_sizes_with_delayed_writer(self): class ChunkAccumulator: """Accumulate pickler output in a list of raw chunks.""" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 1ada1f927d9c..b88eb5804a96 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -971,6 +971,10 @@ def dec(*args, **kwargs): # useful for PGO PGO = False +# Set by libregrtest/main.py if we are running the extended (time consuming) +# PGO task. If this is True, PGO is also True. +PGO_EXTENDED = False + @contextlib.contextmanager def temp_dir(path=None, quiet=False): """Return a context manager that creates a temporary directory. @@ -2636,6 +2640,12 @@ def skip_unless_xattr(test): msg = "no non-broken extended attribute support" return test if ok else unittest.skip(msg)(test) +def skip_if_pgo_task(test): + """Skip decorator for tests not run in (non-extended) PGO task""" + ok = not PGO or PGO_EXTENDED + msg = "Not run for (non-extended) PGO task" + return test if ok else unittest.skip(msg)(test) + _bind_nix_socket_error = None def skip_unless_bind_unix_socket(test): """Decorator for tests requiring a functional bind() for unix sockets.""" diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index f158b901b9c9..eb2f72ee4a5d 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -643,6 +643,7 @@ def testCompressChunks10(self): data += bz2c.flush() self.assertEqual(ext_decompress(data), self.TEXT) + @support.skip_if_pgo_task @bigmemtest(size=_4G + 100, memuse=2) def testCompress4G(self, size): # "Test BZ2Compressor.compress()/flush() with >4GiB input" @@ -701,6 +702,7 @@ def testEOFError(self): self.assertRaises(EOFError, bz2d.decompress, b"anything") self.assertRaises(EOFError, bz2d.decompress, b"") + @support.skip_if_pgo_task @bigmemtest(size=_4G + 100, memuse=3.3) def testDecompress4G(self, size): # "Test BZ2Decompressor.decompress() with >4GiB input" diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index ea060a98a5ee..573739fde14c 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -2062,6 +2062,7 @@ def gen2(x): self.assertRaises(AssertionError, list, cycle(gen1())) self.assertEqual(hist, [0,1]) + @support.skip_if_pgo_task def test_long_chain_of_empty_iterables(self): # Make sure itertools.chain doesn't run into recursion limits when # dealing with long chains of empty iterables. Even with a high diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index 3dc2c1e7e3b7..117de0a1b554 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -333,6 +333,7 @@ def test_decompressor_multistream(self): # Test with inputs larger than 4GiB. + @support.skip_if_pgo_task @bigmemtest(size=_4G + 100, memuse=2) def test_compressor_bigmem(self, size): lzc = LZMACompressor() @@ -344,6 +345,7 @@ def test_compressor_bigmem(self, size): finally: ddata = None + @support.skip_if_pgo_task @bigmemtest(size=_4G + 100, memuse=3) def test_decompressor_bigmem(self, size): lzd = LZMADecompressor() diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index ed2f6579b0b9..104718ed7db6 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -14,6 +14,7 @@ import random import sys import unittest +from test import support from decimal import Decimal from fractions import Fraction @@ -2462,6 +2463,7 @@ def test_cdf(self): self.assertEqual(X.cdf(float('Inf')), 1.0) self.assertTrue(math.isnan(X.cdf(float('NaN')))) + @support.skip_if_pgo_task def test_inv_cdf(self): NormalDist = statistics.NormalDist diff --git a/Misc/NEWS.d/next/Build/2019-07-29-11-36-16.bpo-37707.Sm-dGk.rst b/Misc/NEWS.d/next/Build/2019-07-29-11-36-16.bpo-37707.Sm-dGk.rst new file mode 100644 index 000000000000..c0d58ab747b2 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2019-07-29-11-36-16.bpo-37707.Sm-dGk.rst @@ -0,0 +1,3 @@ +Mark some individual tests to skip when --pgo is used. The tests marked +increase the PGO task time significantly and likely don't help improve +optimization of the final executable. From webhook-mailer at python.org Tue Jul 30 15:08:29 2019 From: webhook-mailer at python.org (Neil Schemenauer) Date: Tue, 30 Jul 2019 19:08:29 -0000 Subject: [Python-checkins] Don't skip pickle check_frame_opcodes() (GH-15025) Message-ID: https://github.com/python/cpython/commit/eca7ffc61cf925eae5def8c64b05d47f24e60e1f commit: eca7ffc61cf925eae5def8c64b05d47f24e60e1f branch: master author: Neil Schemenauer committer: GitHub date: 2019-07-30T12:08:17-07:00 summary: Don't skip pickle check_frame_opcodes() (GH-15025) This looks like the only place that proto 4 framing gets exercised so leave it as part of the PGO task. files: M Lib/test/pickletester.py diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 5d402a4711bf..c9f374678ae3 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2281,7 +2281,6 @@ def test_setitems_on_non_dicts(self): FRAME_SIZE_MIN = 4 FRAME_SIZE_TARGET = 64 * 1024 - @support.skip_if_pgo_task def check_frame_opcodes(self, pickled): """ Check the arguments of FRAME opcodes in a protocol 4+ pickle. From webhook-mailer at python.org Tue Jul 30 16:10:40 2019 From: webhook-mailer at python.org (Neil Schemenauer) Date: Tue, 30 Jul 2019 20:10:40 -0000 Subject: [Python-checkins] Don't skip pickle check_frame_opcodes() (GH-15027) Message-ID: https://github.com/python/cpython/commit/0f9efbcf9a051198dc9e226528d5c5d24a52d23a commit: 0f9efbcf9a051198dc9e226528d5c5d24a52d23a branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Neil Schemenauer date: 2019-07-30T13:10:15-07:00 summary: Don't skip pickle check_frame_opcodes() (GH-15027) This looks like the only place that proto 4 framing gets exercised so leave it as part of the PGO task. (cherry picked from commit eca7ffc61cf925eae5def8c64b05d47f24e60e1f) Co-authored-by: Neil Schemenauer files: M Lib/test/pickletester.py diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 5d402a4711bf..c9f374678ae3 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2281,7 +2281,6 @@ def test_setitems_on_non_dicts(self): FRAME_SIZE_MIN = 4 FRAME_SIZE_TARGET = 64 * 1024 - @support.skip_if_pgo_task def check_frame_opcodes(self, pickled): """ Check the arguments of FRAME opcodes in a protocol 4+ pickle. From webhook-mailer at python.org Tue Jul 30 17:42:13 2019 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 30 Jul 2019 21:42:13 -0000 Subject: [Python-checkins] bpo-36084: Add threading Native ID information to What's New documentation (GH-14845) Message-ID: https://github.com/python/cpython/commit/84846b0187919551b1b08dca447658bbbbb710b1 commit: 84846b0187919551b1b08dca447658bbbbb710b1 branch: master author: Jake Tesler committer: Victor Stinner date: 2019-07-30T23:41:46+02:00 summary: bpo-36084: Add threading Native ID information to What's New documentation (GH-14845) files: M Doc/library/threading.rst M Doc/whatsnew/3.8.rst diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index f80eb22e18fc..7de12fbf9dcd 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -371,7 +371,7 @@ since it is impossible to detect the termination of alien threads. system-wide) from the time the thread is created until the thread has been terminated. - .. availability:: Require :func:`get_native_id` function. + .. availability:: Requires :func:`get_native_id` function. .. versionadded:: 3.8 diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 3f84e092827b..8442e4ef2e3c 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -768,10 +768,18 @@ in a standardized and extensible format, and offers several other benefits. threading --------- -Add a new :func:`threading.excepthook` function which handles uncaught -:meth:`threading.Thread.run` exception. It can be overridden to control how -uncaught :meth:`threading.Thread.run` exceptions are handled. -(Contributed by Victor Stinner in :issue:`1230540`.) +* Add a new :func:`threading.excepthook` function which handles uncaught + :meth:`threading.Thread.run` exception. It can be overridden to control how + uncaught :meth:`threading.Thread.run` exceptions are handled. + (Contributed by Victor Stinner in :issue:`1230540`.) + +* Add a new + :func:`threading.get_native_id` function and a :data:`~threading.Thread.native_id` + attribute to the :class:`threading.Thread` class. These return the native + integral Thread ID of the current thread assigned by the kernel. + This feature is only available on certain platforms, see + :func:`get_native_id ` for more information. + (Contributed by Jake Tesler in :issue:`36084`.) tokenize From webhook-mailer at python.org Tue Jul 30 17:49:27 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 30 Jul 2019 21:49:27 -0000 Subject: [Python-checkins] bpo-36084: Add threading Native ID information to What's New documentation (GH-14845) Message-ID: https://github.com/python/cpython/commit/7026737d77836657c3b713000c3154cfdb7451db commit: 7026737d77836657c3b713000c3154cfdb7451db branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T14:49:23-07:00 summary: bpo-36084: Add threading Native ID information to What's New documentation (GH-14845) (cherry picked from commit 84846b0187919551b1b08dca447658bbbbb710b1) Co-authored-by: Jake Tesler files: M Doc/library/threading.rst M Doc/whatsnew/3.8.rst diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index 9ffd5cd58179..eb9da2b91197 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -363,7 +363,7 @@ since it is impossible to detect the termination of alien threads. system-wide) from the time the thread is created until the thread has been terminated. - .. availability:: Require :func:`get_native_id` function. + .. availability:: Requires :func:`get_native_id` function. .. versionadded:: 3.8 diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index f8cc3304b207..8cfe88085de8 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -765,10 +765,18 @@ in a standardized and extensible format, and offers several other benefits. threading --------- -Add a new :func:`threading.excepthook` function which handles uncaught -:meth:`threading.Thread.run` exception. It can be overridden to control how -uncaught :meth:`threading.Thread.run` exceptions are handled. -(Contributed by Victor Stinner in :issue:`1230540`.) +* Add a new :func:`threading.excepthook` function which handles uncaught + :meth:`threading.Thread.run` exception. It can be overridden to control how + uncaught :meth:`threading.Thread.run` exceptions are handled. + (Contributed by Victor Stinner in :issue:`1230540`.) + +* Add a new + :func:`threading.get_native_id` function and a :data:`~threading.Thread.native_id` + attribute to the :class:`threading.Thread` class. These return the native + integral Thread ID of the current thread assigned by the kernel. + This feature is only available on certain platforms, see + :func:`get_native_id ` for more information. + (Contributed by Jake Tesler in :issue:`36084`.) tokenize From webhook-mailer at python.org Tue Jul 30 18:15:24 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 30 Jul 2019 22:15:24 -0000 Subject: [Python-checkins] Fix idlelib typos discovered by min ho, pr 15018. (GH-15029) Message-ID: https://github.com/python/cpython/commit/0acb646b8e405864224bfd6d7d5089980dea63ac commit: 0acb646b8e405864224bfd6d7d5089980dea63ac branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-30T18:14:58-04:00 summary: Fix idlelib typos discovered by min ho, pr 15018. (GH-15029) files: M Lib/idlelib/NEWS.txt M Lib/idlelib/README.txt M Lib/idlelib/configdialog.py M Lib/idlelib/history.py M Lib/idlelib/idle_test/test_pyparse.py M Lib/idlelib/pyparse.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 109b2d523a6e..e9a9b0255b0d 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -38,7 +38,7 @@ run dialogs. bpo-37321: Both subprocess connection error messages now refer to the 'Startup failure' section of the IDLE doc. -bpo-37039: Adjust "Zoom Height" to individual screens by momemtarily +bpo-37039: Adjust "Zoom Height" to individual screens by momentarily maximizing the window on first use with a particular screen. Changing screen settings may invalidate the saved height. While a window is maximized, "Zoom Height" has no effect. diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt index 8912f3970499..42c3506699ba 100644 --- a/Lib/idlelib/README.txt +++ b/Lib/idlelib/README.txt @@ -68,7 +68,7 @@ pyshell.py # Start IDLE, manage shell, complete editor window query.py # Query user for information redirector.py # Intercept widget subcommands (for percolator) (nim). replace.py # Search and replace pattern in text. -rpc.py # Commuicate between idle and user processes (nim). +rpc.py # Communicate between idle and user processes (nim). rstrip.py # Strip trailing whitespace. run.py # Manage user code execution subprocess. runscript.py # Check and run user code. @@ -97,7 +97,7 @@ Text CREDITS.txt # not maintained, displayed by About IDLE HISTORY.txt # NEWS up to July 2001 NEWS.txt # commits, displayed by About IDLE -README.txt # this file, displeyed by About IDLE +README.txt # this file, displayed by About IDLE TODO.txt # needs review extend.txt # about writing extensions help.html # copy of idle.html in docs, displayed by IDLE Help @@ -166,7 +166,7 @@ Shell # pyshell Debug (Shell only) Go to File/Line - debugger # debugger, debugger_r, PyShell.toggle_debuger + debugger # debugger, debugger_r, PyShell.toggle_debugger Stack Viewer # stackviewer, PyShell.open_stack_viewer Auto-open Stack Viewer # stackviewer diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index bba17d34938d..df216582fe60 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -1793,7 +1793,7 @@ def create_page_general(self): """Return frame of widgets for General tab. Enable users to provisionally change general options. Function - load_general_cfg intializes tk variables and helplist using + load_general_cfg initializes tk variables and helplist using idleConf. Radiobuttons startup_shell_on and startup_editor_on set var startup_edit. Radiobuttons save_ask_on and save_auto_on set var autosave. Entry boxes win_width_int and win_height_int diff --git a/Lib/idlelib/history.py b/Lib/idlelib/history.py index 56f53a0f2fb9..ad44a96a9de2 100644 --- a/Lib/idlelib/history.py +++ b/Lib/idlelib/history.py @@ -39,7 +39,7 @@ def history_prev(self, event): return "break" def fetch(self, reverse): - '''Fetch statememt and replace current line in text widget. + '''Fetch statement and replace current line in text widget. Set prefix and pointer as needed for successive fetches. Reset them to None, None when returning to the start line. diff --git a/Lib/idlelib/idle_test/test_pyparse.py b/Lib/idlelib/idle_test/test_pyparse.py index 479b84a216b0..f7154e6ded95 100644 --- a/Lib/idlelib/idle_test/test_pyparse.py +++ b/Lib/idlelib/idle_test/test_pyparse.py @@ -206,8 +206,8 @@ def test_study2(self): 'openbracket', 'bracketing']) tests = ( TestInfo('', 0, 0, '', None, ((0, 0),)), - TestInfo("'''This is a multiline continutation docstring.\n\n", - 0, 49, "'", None, ((0, 0), (0, 1), (49, 0))), + TestInfo("'''This is a multiline continuation docstring.\n\n", + 0, 48, "'", None, ((0, 0), (0, 1), (48, 0))), TestInfo(' # Comment\\\n', 0, 12, '', None, ((0, 0), (1, 1), (12, 0))), # A comment without a space is a special case diff --git a/Lib/idlelib/pyparse.py b/Lib/idlelib/pyparse.py index 81e7f539803c..feb57cbb7405 100644 --- a/Lib/idlelib/pyparse.py +++ b/Lib/idlelib/pyparse.py @@ -575,7 +575,7 @@ def get_base_indent_string(self): return code[i:j] def is_block_opener(self): - "Return True if the last interesting statemtent opens a block." + "Return True if the last interesting statement opens a block." self._study2() return self.lastch == ':' From webhook-mailer at python.org Tue Jul 30 18:16:19 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Tue, 30 Jul 2019 22:16:19 -0000 Subject: [Python-checkins] Fix typos in comments, docs and test names (#15018) Message-ID: https://github.com/python/cpython/commit/c4cacc8c5eab50db8da3140353596f38a01115ca commit: c4cacc8c5eab50db8da3140353596f38a01115ca branch: master author: Min ho Kim committer: Terry Jan Reedy date: 2019-07-30T18:16:13-04:00 summary: Fix typos in comments, docs and test names (#15018) * Fix typos in comments, docs and test names * Update test_pyparse.py account for change in string length * Apply suggestion: splitable -> splittable Co-Authored-By: Terry Jan Reedy * Apply suggestion: splitable -> splittable Co-Authored-By: Terry Jan Reedy * Apply suggestion: Dealloccte -> Deallocate Co-Authored-By: Terry Jan Reedy * Update posixmodule checksum. * Reverse idlelib changes. files: M Doc/library/email.encoders.rst M Doc/library/statistics.rst M Include/abstract.h M Include/ast.h M Include/pymath.h M Lib/_pyio.py M Lib/asyncio/streams.py M Lib/asyncio/subprocess.py M Lib/imaplib.py M Lib/lib2to3/fixer_util.py M Lib/lib2to3/tests/test_parser.py M Lib/test/libregrtest/win_utils.py M Lib/test/test_ast.py M Lib/test/test_cmd_line_script.py M Lib/test/test_collections.py M Lib/test/test_compileall.py M Lib/test/test_doctest.py M Lib/test/test_email/test__header_value_parser.py M Lib/test/test_email/test_email.py M Lib/test/test_importlib/util.py M Lib/test/test_mailbox.py M Lib/test/test_pdb.py M Lib/test/test_pprint.py M Lib/test/test_subprocess.py M Lib/test/test_threading.py M Lib/test/test_trace.py M Lib/test/test_turtle.py M Lib/test/test_types.py M Lib/test/test_venv.py M Lib/unittest/result.py M Lib/unittest/test/testmock/testmock.py M Misc/HISTORY M Misc/NEWS.d/3.5.0a1.rst M Misc/NEWS.d/3.7.0a1.rst M Misc/NEWS.d/3.8.0a1.rst M Misc/NEWS.d/3.8.0a4.rst M Misc/NEWS.d/next/Core and Builtins/2019-06-10-23-18-31.bpo-37219.jPSufq.rst M Misc/NEWS.d/next/IDLE/2019-06-04-23-27-33.bpo-37039.FN_fBf.rst M Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst M Misc/NEWS.d/next/Tests/2019-06-04-18-30-39.bpo-37153.711INB.rst M Modules/_ctypes/libffi_osx/x86/x86-ffi64.c M Modules/_struct.c M Modules/cjkcodecs/README M Modules/clinic/posixmodule.c.h M Modules/expat/expat.h M Modules/getpath.c M Modules/posixmodule.c M Objects/exceptions.c M Objects/object.c M Objects/stringlib/unicode_format.h M Objects/typeobject.c M PC/bdist_wininst/install.c M PCbuild/readme.txt M Python/ast.c M Python/bootstrap_hash.c M Python/compile.c M Python/getopt.c M Python/hamt.c M Python/import.c M Python/initconfig.c M Python/pystate.c M Python/sysmodule.c M Tools/ssl/make_ssl_data.py diff --git a/Doc/library/email.encoders.rst b/Doc/library/email.encoders.rst index e4752a5edf84..5d68b104f3a4 100644 --- a/Doc/library/email.encoders.rst +++ b/Doc/library/email.encoders.rst @@ -15,7 +15,7 @@ the :meth:`~email.message.EmailMessage.set_content` method. This module is deprecated in Python 3. The functions provided here should not be called explicitly since the :class:`~email.mime.text.MIMEText` class sets the content type and CTE header using the *_subtype* and *_charset* -values passed during the instaniation of that class. +values passed during the instantiation of that class. The remaining text in this section is the original documentation of the module. diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst index a906a591e62c..3a2a1f94db47 100644 --- a/Doc/library/statistics.rst +++ b/Doc/library/statistics.rst @@ -554,7 +554,7 @@ However, for reading convenience, most of the examples show sorted sequences. >>> [round(q, 1) for q in quantiles(data, n=10)] [81.0, 86.2, 89.0, 99.4, 102.5, 103.6, 106.0, 109.8, 111.0] - >>> # Quartile cut points for the standard normal distibution + >>> # Quartile cut points for the standard normal distribution >>> Z = NormalDist() >>> [round(q, 4) for q in quantiles(Z, n=4)] [-0.6745, 0.0, 0.6745] diff --git a/Include/abstract.h b/Include/abstract.h index f36fafb43c9e..8951f638a4ec 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -702,7 +702,7 @@ PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m); (PyList_Check(o) ? PyList_GET_ITEM(o, i) : PyTuple_GET_ITEM(o, i)) /* Return a pointer to the underlying item array for - an object retured by PySequence_Fast */ + an object returned by PySequence_Fast */ #define PySequence_Fast_ITEMS(sf) \ (PyList_Check(sf) ? ((PyListObject *)(sf))->ob_item \ : ((PyTupleObject *)(sf))->ob_item) diff --git a/Include/ast.h b/Include/ast.h index f1d734852eca..1347fcffcd75 100644 --- a/Include/ast.h +++ b/Include/ast.h @@ -25,7 +25,7 @@ PyAPI_FUNC(mod_ty) PyAST_FromNodeObject( PyAPI_FUNC(PyObject *) _PyAST_ExprAsUnicode(expr_ty); /* Return the borrowed reference to the first literal string in the - sequence of statemnts or NULL if it doesn't start from a literal string. + sequence of statements or NULL if it doesn't start from a literal string. Doesn't set exception. */ PyAPI_FUNC(PyObject *) _PyAST_GetDocString(asdl_seq *); diff --git a/Include/pymath.h b/Include/pymath.h index 6cf69f98acf9..f869724334a4 100644 --- a/Include/pymath.h +++ b/Include/pymath.h @@ -125,7 +125,7 @@ PyAPI_FUNC(void) _Py_set_387controlword(unsigned short); /* Py_IS_FINITE(X) * Return 1 if float or double arg is neither infinite nor NAN, else 0. - * Some compilers (e.g. VisualStudio) have intrisics for this, so a special + * Some compilers (e.g. VisualStudio) have intrinsics for this, so a special * macro for this particular test is useful * Note: PC/pyconfig.h defines Py_IS_FINITE as _finite */ diff --git a/Lib/_pyio.py b/Lib/_pyio.py index c35516430589..40e0c9f9e2c6 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1573,7 +1573,7 @@ def __init__(self, file, mode='r', closefd=True, opener=None): raise IsADirectoryError(errno.EISDIR, os.strerror(errno.EISDIR), file) except AttributeError: - # Ignore the AttribueError if stat.S_ISDIR or errno.EISDIR + # Ignore the AttributeError if stat.S_ISDIR or errno.EISDIR # don't exist. pass self._blksize = getattr(fdfstat, 'st_blksize', 0) diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 204eaf7394c5..e56e2efe5cf1 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -556,7 +556,7 @@ def __init__(self, loop=None, *, _asyncio_internal=False): # Avoid inheritance from FlowControlMixin # Copy-paste the code to your project # if you need flow control helpers - warnings.warn(f"{self.__class__} should be instaniated " + warnings.warn(f"{self.__class__} should be instantiated " "by asyncio internals only, " "please avoid its creation from user code", DeprecationWarning) diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py index e6bec71d6c7d..2a68c9e4cb06 100644 --- a/Lib/asyncio/subprocess.py +++ b/Lib/asyncio/subprocess.py @@ -130,7 +130,7 @@ def _get_close_waiter(self, stream): class Process: def __init__(self, transport, protocol, loop, *, _asyncio_internal=False): if not _asyncio_internal: - warnings.warn(f"{self.__class__} should be instaniated " + warnings.warn(f"{self.__class__} should be instantiated " "by asyncio internals only, " "please avoid its creation from user code", DeprecationWarning) diff --git a/Lib/imaplib.py b/Lib/imaplib.py index 822d9d6fdd8f..a4f499383efa 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -502,7 +502,7 @@ def deleteacl(self, mailbox, who): def enable(self, capability): """Send an RFC5161 enable string to the server. - (typ, [data]) = .enable(capability) + (typ, [data]) = .enable(capability) """ if 'ENABLE' not in self.capabilities: raise IMAP4.error("Server does not support ENABLE") diff --git a/Lib/lib2to3/fixer_util.py b/Lib/lib2to3/fixer_util.py index babe6cb3f662..c2a3a47f5032 100644 --- a/Lib/lib2to3/fixer_util.py +++ b/Lib/lib2to3/fixer_util.py @@ -412,7 +412,7 @@ def _find(name, node): return None def _is_import_binding(node, name, package=None): - """ Will reuturn node if node will import name, or node + """ Will return node if node will import name, or node will import * from package. None is returned otherwise. See test cases for examples. """ diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py index 01b2b51e4ab6..f22d399b3403 100644 --- a/Lib/lib2to3/tests/test_parser.py +++ b/Lib/lib2to3/tests/test_parser.py @@ -531,7 +531,7 @@ def test_4(self): # Adapted from Python 3's Lib/test/test_unicode_identifiers.py and # Lib/test/test_tokenize.py:TokenizeTest.test_non_ascii_identifiers -class TestIdentfier(GrammarTest): +class TestIdentifier(GrammarTest): def test_non_ascii_identifiers(self): self.validate("?rter = 'places'\ngr?n = 'green'") self.validate("? = a? = ?? = 1") diff --git a/Lib/test/libregrtest/win_utils.py b/Lib/test/libregrtest/win_utils.py index adfe278ba39b..0e6bfa8e10f7 100644 --- a/Lib/test/libregrtest/win_utils.py +++ b/Lib/test/libregrtest/win_utils.py @@ -18,7 +18,7 @@ class WindowsLoadTracker(): """ This class asynchronously interacts with the `typeperf` command to read - the system load on Windows. Mulitprocessing and threads can't be used + the system load on Windows. Multiprocessing and threads can't be used here because they interfere with the test suite's cases for those modules. """ diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 5c37a5fbed6e..f35d9e6f5451 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -551,7 +551,7 @@ def test_invalid_sum(self): compile(m, "", "exec") self.assertIn("but got <_ast.expr", str(cm.exception)) - def test_invalid_identitifer(self): + def test_invalid_identifier(self): m = ast.Module([ast.Expr(ast.Name(42, ast.Load()))], []) ast.fix_missing_locations(m) with self.assertRaises(TypeError) as cm: diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index 80198f81a1c5..c8bf8af964f8 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -714,7 +714,7 @@ def test_consistent_sys_path_for_module_execution(self): def test_nonexisting_script(self): # bpo-34783: "./python script.py" must not crash # if the script file doesn't exist. - # (Skip test for macOS framework builds because sys.excutable name + # (Skip test for macOS framework builds because sys.executable name # is not the actual Python executable file name. script = 'nonexistingscript.py' self.assertFalse(os.path.exists(script)) diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index e532be6eeaf0..0119c77ac02a 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -424,8 +424,8 @@ def test_tupleness(self): self.assertIsInstance(p, tuple) self.assertEqual(p, (11, 22)) # matches a real tuple - self.assertEqual(tuple(p), (11, 22)) # coercable to a real tuple - self.assertEqual(list(p), [11, 22]) # coercable to a list + self.assertEqual(tuple(p), (11, 22)) # coercible to a real tuple + self.assertEqual(list(p), [11, 22]) # coercible to a list self.assertEqual(max(p), 22) # iterable self.assertEqual(max(*p), 22) # star-able x, y = p diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 04f6e1e049dd..99d843704fc7 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -578,14 +578,14 @@ def test_workers_available_cores(self, compile_dir): self.assertEqual(compile_dir.call_args[-1]['workers'], 0) -class CommmandLineTestsWithSourceEpoch(CommandLineTestsBase, +class CommandLineTestsWithSourceEpoch(CommandLineTestsBase, unittest.TestCase, metaclass=SourceDateEpochTestMeta, source_date_epoch=True): pass -class CommmandLineTestsNoSourceEpoch(CommandLineTestsBase, +class CommandLineTestsNoSourceEpoch(CommandLineTestsBase, unittest.TestCase, metaclass=SourceDateEpochTestMeta, source_date_epoch=False): diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 5ea18f52c4fc..f7c399e526d1 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -2485,7 +2485,7 @@ def test_unittest_reportflags(): def test_testfile(): r""" Tests for the `testfile()` function. This function runs all the -doctest examples in a given file. In its simple invokation, it is +doctest examples in a given file. In its simple invocation, it is called with the name of a file, which is taken to be relative to the calling module. The return value is (#failures, #tests). diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 877cd3effe1d..f6e5886f7571 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -297,7 +297,7 @@ def test_get_unstructured_ew_with_internal_leading_ws(self): [], '') - def test_get_unstructured_invaild_ew(self): + def test_get_unstructured_invalid_ew(self): self._test_get_x(self._get_unst, '=?test val', '=?test val', diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index aa775881c552..ae9625845646 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -1008,7 +1008,7 @@ def test_last_split_chunk_does_not_fit(self): Subject: the first part of this is short, but_the_second_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself""") - def test_splittable_leading_char_followed_by_overlong_unsplitable(self): + def test_splittable_leading_char_followed_by_overlong_unsplittable(self): eq = self.ndiffAssertEqual h = Header(', but_the_second' '_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line' @@ -1017,7 +1017,7 @@ def test_splittable_leading_char_followed_by_overlong_unsplitable(self): , but_the_second_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself""") - def test_multiple_splittable_leading_char_followed_by_overlong_unsplitable(self): + def test_multiple_splittable_leading_char_followed_by_overlong_unsplittable(self): eq = self.ndiffAssertEqual h = Header(', , but_the_second' '_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line' @@ -1026,14 +1026,14 @@ def test_multiple_splittable_leading_char_followed_by_overlong_unsplitable(self) , , but_the_second_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself""") - def test_trailing_splitable_on_overlong_unsplitable(self): + def test_trailing_splittable_on_overlong_unsplittable(self): eq = self.ndiffAssertEqual h = Header('this_part_does_not_fit_within_maxlinelen_and_thus_should_' 'be_on_a_line_all_by_itself;') eq(h.encode(), "this_part_does_not_fit_within_maxlinelen_and_thus_should_" "be_on_a_line_all_by_itself;") - def test_trailing_splitable_on_overlong_unsplitable_with_leading_splitable(self): + def test_trailing_splittable_on_overlong_unsplittable_with_leading_splittable(self): eq = self.ndiffAssertEqual h = Header('; ' 'this_part_does_not_fit_within_maxlinelen_and_thus_should_' @@ -1466,7 +1466,7 @@ def test_mangled_from_with_bad_bytes(self): g.flatten(msg) self.assertEqual(b.getvalue(), source + b'>From R\xc3\xb6lli\n') - def test_mutltipart_with_bad_bytes_in_cte(self): + def test_multipart_with_bad_bytes_in_cte(self): # bpo30835 source = textwrap.dedent("""\ From: aperson at example.com diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py index 196ea1c9d4dd..913db4bb3b71 100644 --- a/Lib/test/test_importlib/util.py +++ b/Lib/test/test_importlib/util.py @@ -488,7 +488,7 @@ def test_absolute_path(self): self.execute(data01, full_path) def test_relative_path(self): - # A reative path is a ValueError. + # A relative path is a ValueError. with self.assertRaises(ValueError): self.execute(data01, '../data01/utf-8.file') diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 0995b1e386d0..36a265390e98 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -1420,7 +1420,7 @@ def test_initialize_incorrectly(self): # Initialize with invalid argument self.assertRaises(TypeError, lambda: self._factory(object())) - def test_all_eMM_attribues_exist(self): + def test_all_eMM_attributes_exist(self): # Issue 12537 eMM = email.message_from_string(_sample_message) msg = self._factory(_sample_message) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index d03f1b284300..1e464df28340 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -983,7 +983,7 @@ def test_pdb_return_command_for_coroutine(): def test_pdb_until_command_for_generator(): """Testing no unwindng stack on yield for generators - for "until" command if target breakpoing is not reached + for "until" command if target breakpoint is not reached >>> def test_gen(): ... yield 0 @@ -1027,7 +1027,7 @@ def test_pdb_until_command_for_generator(): def test_pdb_until_command_for_coroutine(): """Testing no unwindng stack for coroutines - for "until" command if target breakpoing is not reached + for "until" command if target breakpoint is not reached >>> import asyncio diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index b3b8715a5f75..cf3e4f093b16 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -481,7 +481,7 @@ def test_set_of_sets_reprs(self): # Consequently, this test is fragile and # implementation-dependent. Small changes to Python's sort # algorithm cause the test to fail when it should pass. - # XXX Or changes to the dictionary implmentation... + # XXX Or changes to the dictionary implementation... cube_repr_tgt = """\ {frozenset(): frozenset({frozenset({2}), frozenset({0}), frozenset({1})}), diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index e58d0925df3b..4fe74bf504b4 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1500,7 +1500,7 @@ def test_run_kwargs(self): def test_run_with_pathlike_path(self): # bpo-31961: test run(pathlike_object) # the name of a command that can be run without - # any argumenets that exit fast + # any arguments that exit fast prog = 'tree.com' if mswindows else 'ls' path = shutil.which(prog) if path is None: diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index a04d496001e3..a99b8eca2be7 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -288,7 +288,7 @@ def fail_new_thread(*args): finally: threading._start_new_thread = _start_new_thread - def test_finalize_runnning_thread(self): + def test_finalize_running_thread(self): # Issue 1402: the PyGILState_Ensure / _Release functions may be called # very late on python exit: on deallocation of a running thread for # example. diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index 912badb409d9..7cda546b8b98 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -180,7 +180,7 @@ def test_trace_list_comprehension(self): firstlineno_called = get_firstlineno(traced_doubler) expected = { (self.my_py_filename, firstlineno_calling + 1): 1, - # List compehentions work differently in 3.x, so the count + # List comprehensions work differently in 3.x, so the count # below changed compared to 2.x. (self.my_py_filename, firstlineno_calling + 2): 12, (self.my_py_filename, firstlineno_calling + 3): 1, diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py index 2fd10ccd505f..38448c791be6 100644 --- a/Lib/test/test_turtle.py +++ b/Lib/test/test_turtle.py @@ -85,7 +85,7 @@ def test_config_dict(self): self.assertEqual(parsed_cfg, expected) - def test_partial_config_dict_with_commments(self): + def test_partial_config_dict_with_comments(self): cfg_name = self.get_cfg_file(test_config_two) parsed_cfg = turtle.config_dict(cfg_name) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 56848c1bf87e..7b45b7a58950 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -466,7 +466,7 @@ def test(f, format_spec, result): # No format code means use g, but must have a decimal # and a number after the decimal. This is tricky, because - # a totaly empty format specifier means something else. + # a totally empty format specifier means something else. # So, just use a sign flag test(1e200, '+g', '+1e+200') test(1e200, '+', '+1e+200') diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 228aa8d68ed7..9724d9ef57bc 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -345,7 +345,7 @@ def test_multiprocessing(self): """ Test that the multiprocessing is able to spawn. """ - # Issue bpo-36342: Instanciation of a Pool object imports the + # Issue bpo-36342: Instantiation of a Pool object imports the # multiprocessing.synchronize module. Skip the test if this module # cannot be imported. import_module('multiprocessing.synchronize') diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py index c7e3206d749b..111317b329a8 100644 --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -161,7 +161,7 @@ def wasSuccessful(self): """Tells whether or not this result was a success.""" # The hasattr check is for test_result's OldResult test. That # way this method works on objects that lack the attribute. - # (where would such result intances come from? old stored pickles?) + # (where would such result instances come from? old stored pickles?) return ((len(self.failures) == len(self.errors) == 0) and (not hasattr(self, 'unexpectedSuccesses') or len(self.unexpectedSuccesses) == 0)) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 090da45fb660..18efd311f9e6 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -262,7 +262,7 @@ def test_call(self): ret_val = mock(sentinel.Arg) self.assertTrue(mock.called, "called not set") - self.assertEqual(mock.call_count, 1, "call_count incoreect") + self.assertEqual(mock.call_count, 1, "call_count incorrect") self.assertEqual(mock.call_args, ((sentinel.Arg,), {}), "call_args not set") self.assertEqual(mock.call_args.args, (sentinel.Arg,), diff --git a/Misc/HISTORY b/Misc/HISTORY index f4b756cf0a46..f49960c95ada 100644 --- a/Misc/HISTORY +++ b/Misc/HISTORY @@ -2113,7 +2113,7 @@ Build - Issue #21285: Refactor and fix curses configure check to always search in a ncursesw directory. -- Issue #15234: For BerkelyDB and Sqlite, only add the found library and +- Issue #15234: For BerkeleyDB and Sqlite, only add the found library and include directories if they aren't already being searched. This avoids an explicit runtime library dependency. diff --git a/Misc/NEWS.d/3.5.0a1.rst b/Misc/NEWS.d/3.5.0a1.rst index 62406e1aa008..99f2d1d36dbf 100644 --- a/Misc/NEWS.d/3.5.0a1.rst +++ b/Misc/NEWS.d/3.5.0a1.rst @@ -4992,7 +4992,7 @@ directory. .. nonce: vlM720 .. section: Build -For BerkelyDB and Sqlite, only add the found library and include directories +For BerkeleyDB and Sqlite, only add the found library and include directories if they aren't already being searched. This avoids an explicit runtime library dependency. diff --git a/Misc/NEWS.d/3.7.0a1.rst b/Misc/NEWS.d/3.7.0a1.rst index f9cd59c8d4bd..bbd72d7f3ff8 100644 --- a/Misc/NEWS.d/3.7.0a1.rst +++ b/Misc/NEWS.d/3.7.0a1.rst @@ -3067,7 +3067,7 @@ Sped up reading encrypted ZIP files by 2 times. .. section: Library Element.getiterator() and the html parameter of XMLParser() were deprecated -only in the documentation (since Python 3.2 and 3.4 correspondintly). Now +only in the documentation (since Python 3.2 and 3.4 correspondingly). Now using them emits a deprecation warning. .. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index 3d376693d380..68217d31193e 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -2054,7 +2054,7 @@ Speed-up building enums by value, e.g. http.HTTPStatus(200). .. section: Library random.gammavariate(1.0, beta) now computes the same result as -random.expovariate(1.0 / beta). This synchonizes the two algorithms and +random.expovariate(1.0 / beta). This synchronizes the two algorithms and eliminates some idiosyncrasies in the old implementation. It does however produce a difference stream of random variables than it used to. diff --git a/Misc/NEWS.d/3.8.0a4.rst b/Misc/NEWS.d/3.8.0a4.rst index 80e01d97a043..894ce0b5a341 100644 --- a/Misc/NEWS.d/3.8.0a4.rst +++ b/Misc/NEWS.d/3.8.0a4.rst @@ -904,7 +904,7 @@ only enabled by ``--findleaks``. The check now also works with .. nonce: B8-ghi .. section: Tests -When using mulitprocessing mode (-jN), regrtest now better reports errors if +When using multiprocessing mode (-jN), regrtest now better reports errors if a worker process fails, and it exits immediately on a worker thread failure or when interrupted. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-10-23-18-31.bpo-37219.jPSufq.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-10-23-18-31.bpo-37219.jPSufq.rst index ef8f52dce678..d45f08610b21 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-10-23-18-31.bpo-37219.jPSufq.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2019-06-10-23-18-31.bpo-37219.jPSufq.rst @@ -1 +1 @@ -Remove errorneous optimization for empty set differences. +Remove erroneous optimization for empty set differences. diff --git a/Misc/NEWS.d/next/IDLE/2019-06-04-23-27-33.bpo-37039.FN_fBf.rst b/Misc/NEWS.d/next/IDLE/2019-06-04-23-27-33.bpo-37039.FN_fBf.rst index 71c8c892ba6a..68d80ae4eaea 100644 --- a/Misc/NEWS.d/next/IDLE/2019-06-04-23-27-33.bpo-37039.FN_fBf.rst +++ b/Misc/NEWS.d/next/IDLE/2019-06-04-23-27-33.bpo-37039.FN_fBf.rst @@ -1,4 +1,4 @@ -Adjust "Zoom Height" to individual screens by momemtarily maximizing the +Adjust "Zoom Height" to individual screens by momentarily maximizing the window on first use with a particular screen. Changing screen settings may invalidate the saved height. While a window is maximized, "Zoom Height" has no effect. diff --git a/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst index 4bfd350c0b40..9d47578c6277 100644 --- a/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst +++ b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst @@ -1,2 +1,2 @@ -Fix an inifite loop when parsing specially crafted email headers. Patch by +Fix an infinite loop when parsing specially crafted email headers. Patch by Abhilash Raj. diff --git a/Misc/NEWS.d/next/Tests/2019-06-04-18-30-39.bpo-37153.711INB.rst b/Misc/NEWS.d/next/Tests/2019-06-04-18-30-39.bpo-37153.711INB.rst index 138a22f6acc2..706ebaa20b7c 100644 --- a/Misc/NEWS.d/next/Tests/2019-06-04-18-30-39.bpo-37153.711INB.rst +++ b/Misc/NEWS.d/next/Tests/2019-06-04-18-30-39.bpo-37153.711INB.rst @@ -1,2 +1,2 @@ -``test_venv.test_mutiprocessing()`` now explicitly calls +``test_venv.test_multiprocessing()`` now explicitly calls ``pool.terminate()`` to wait until the pool completes. diff --git a/Modules/_ctypes/libffi_osx/x86/x86-ffi64.c b/Modules/_ctypes/libffi_osx/x86/x86-ffi64.c index f2610c16c783..8e7d01648802 100644 --- a/Modules/_ctypes/libffi_osx/x86/x86-ffi64.c +++ b/Modules/_ctypes/libffi_osx/x86/x86-ffi64.c @@ -57,7 +57,7 @@ ffi_call_unix64( of SSESF, SSEDF classes, that are basically SSE class, just gcc will use SF or DFmode move instead of DImode to avoid reformating penalties. - Similary we play games with INTEGERSI_CLASS to use cheaper SImode moves + Similarly we play games with INTEGERSI_CLASS to use cheaper SImode moves whenever possible (upper half does contain padding). */ enum x86_64_reg_class { diff --git a/Modules/_struct.c b/Modules/_struct.c index d1c635a31600..ba8f9cd8e966 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -361,8 +361,8 @@ _range_error(const formatdef *f, int is_unsigned) [bln][up]_TYPE - [bln] distiguishes among big-endian, little-endian and native. - [pu] distiguishes between pack (to struct) and unpack (from struct). + [bln] distinguishes among big-endian, little-endian and native. + [pu] distinguishes between pack (to struct) and unpack (from struct). TYPE is one of char, byte, ubyte, etc. */ diff --git a/Modules/cjkcodecs/README b/Modules/cjkcodecs/README index b2370bc298f7..8f08f2dd4169 100644 --- a/Modules/cjkcodecs/README +++ b/Modules/cjkcodecs/README @@ -6,7 +6,7 @@ subdirectory of CJKCodecs' distribution. -Notes on implmentation characteristics of each codecs +Notes on implementation characteristics of each codecs ----------------------------------------------------- 1) Big5 codec diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 22cb94761de5..aa1ab79bd96a 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -2817,7 +2817,7 @@ PyDoc_STRVAR(os_sched_getscheduler__doc__, "sched_getscheduler($module, pid, /)\n" "--\n" "\n" -"Get the scheduling policy for the process identifiedy by pid.\n" +"Get the scheduling policy for the process identified by pid.\n" "\n" "Passing 0 for pid returns the scheduling policy for the calling process."); @@ -8741,4 +8741,4 @@ os__remove_dll_directory(PyObject *module, PyObject *const *args, Py_ssize_t nar #ifndef OS__REMOVE_DLL_DIRECTORY_METHODDEF #define OS__REMOVE_DLL_DIRECTORY_METHODDEF #endif /* !defined(OS__REMOVE_DLL_DIRECTORY_METHODDEF) */ -/*[clinic end generated code: output=b3ae8afd275ea5cd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1e001c855e011720 input=a9049054013a1b77]*/ diff --git a/Modules/expat/expat.h b/Modules/expat/expat.h index c050f1d918ce..56399da717bb 100644 --- a/Modules/expat/expat.h +++ b/Modules/expat/expat.h @@ -265,7 +265,7 @@ XML_ParserCreate_MM(const XML_Char *encoding, /* Prepare a parser object to be re-used. This is particularly valuable when memory allocation overhead is disproportionately high, - such as when a large number of small documnents need to be parsed. + such as when a large number of small documents need to be parsed. All handlers are cleared from the parser, except for the unknownEncodingHandler. The parser's external state is re-initialized except for the values of ns and ns_triplets. diff --git a/Modules/getpath.c b/Modules/getpath.c index 4ddb6638fa85..2372172a7b9f 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -95,7 +95,7 @@ * process to find the installed Python tree. * * An embedding application can use Py_SetPath() to override all of - * these authomatic path computations. + * these automatic path computations. * * NOTE: Windows MSVC builds use PC/getpathp.c instead! */ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 777e933cab59..4f8c074a6716 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6112,14 +6112,14 @@ os.sched_getscheduler pid: pid_t / -Get the scheduling policy for the process identifiedy by pid. +Get the scheduling policy for the process identified by pid. Passing 0 for pid returns the scheduling policy for the calling process. [clinic start generated code]*/ static PyObject * os_sched_getscheduler_impl(PyObject *module, pid_t pid) -/*[clinic end generated code: output=dce4c0bd3f1b34c8 input=5f14cfd1f189e1a0]*/ +/*[clinic end generated code: output=dce4c0bd3f1b34c8 input=8d99dac505485ac8]*/ { int policy; diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 568d4959e3a0..ef9dd512dc9c 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -405,7 +405,7 @@ static PyTypeObject _PyExc_BaseException = { BaseException_new, /* tp_new */ }; /* the CPython API expects exceptions to be (PyObject *) - both a hold-over -from the previous implmentation and also allowing Python objects to be used +from the previous implementation and also allowing Python objects to be used in the API */ PyObject *PyExc_BaseException = (PyObject *)&_PyExc_BaseException; diff --git a/Objects/object.c b/Objects/object.c index ee2050656a04..7f2c23a9ff8c 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2072,7 +2072,7 @@ _PyTrash_thread_deposit_object(PyObject *op) tstate->trash_delete_later = op; } -/* Dealloccate all the objects in the _PyTrash_delete_later list. Called when +/* Deallocate all the objects in the _PyTrash_delete_later list. Called when * the call-stack unwinds again. */ void diff --git a/Objects/stringlib/unicode_format.h b/Objects/stringlib/unicode_format.h index ddf1e2644869..b526ad21b820 100644 --- a/Objects/stringlib/unicode_format.h +++ b/Objects/stringlib/unicode_format.h @@ -828,7 +828,7 @@ output_markup(SubString *field_name, SubString *format_spec, tmp = NULL; } - /* if needed, recurively compute the format_spec */ + /* if needed, recursively compute the format_spec */ if (format_spec_needs_expanding) { tmp = build_string(format_spec, args, kwargs, recursion_depth-1, auto_number); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 8acf678fc9a4..9e5709a74f78 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1369,7 +1369,7 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b) return 0; } else - /* a is not completely initilized yet; follow tp_base */ + /* a is not completely initialized yet; follow tp_base */ return type_is_subtype_base_chain(a, b); } diff --git a/PC/bdist_wininst/install.c b/PC/bdist_wininst/install.c index 6d01ad5c2d50..0219a195bc98 100644 --- a/PC/bdist_wininst/install.c +++ b/PC/bdist_wininst/install.c @@ -2540,7 +2540,7 @@ int DoUninstall(int argc, char **argv) if (!lines) return SystemError(0, "Out of memory"); - /* Read the whole logfile, realloacting the buffer */ + /* Read the whole logfile, reallocating the buffer */ while (fgets(buffer, sizeof(buffer), logfile)) { int len = strlen(buffer); /* remove trailing white space */ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 8cbb5d8f46ea..90aac3aa0cab 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -285,4 +285,4 @@ The pyproject property file defines all of the build settings for each project, with some projects overriding certain specific values. The GUI doesn't always reflect the correct settings and may confuse the user with false information, especially for settings that automatically adapt -for diffirent configurations. +for different configurations. diff --git a/Python/ast.c b/Python/ast.c index ce3b4927bc04..976be70d4d70 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -5256,7 +5256,7 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl, *str += 1; /* If we're in = mode (detected by non-NULL expr_text), and have no format - spec and no explict conversion, set the conversion to 'r'. */ + spec and no explicit conversion, set the conversion to 'r'. */ if (*expr_text && format_spec == NULL && conversion == -1) { conversion = 'r'; } diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c index 43f5264d8625..aa3a3dfd964e 100644 --- a/Python/bootstrap_hash.c +++ b/Python/bootstrap_hash.c @@ -163,7 +163,7 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise) } /* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system urandom - is not initialiazed yet. For _PyRandom_Init(), we ignore the + is not initialized yet. For _PyRandom_Init(), we ignore the error and fall back on reading /dev/urandom which never blocks, even if the system urandom is not initialized yet: see the PEP 524. */ diff --git a/Python/compile.c b/Python/compile.c index 0336959d3da2..d2de7a72a811 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1868,7 +1868,7 @@ compiler_make_closure(struct compiler *c, PyCodeObject *co, Py_ssize_t flags, Py free variable that has the same name as a method, the name will be considered free *and* local in the class. It should be handled by the closure, as - well as by the normal name loookup logic. + well as by the normal name lookup logic. */ reftype = get_ref_type(c, name); if (reftype == CELL) diff --git a/Python/getopt.c b/Python/getopt.c index 1a7db3fce888..89f773417e31 100644 --- a/Python/getopt.c +++ b/Python/getopt.c @@ -20,7 +20,7 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Nevertheless, I would like to know about bugs in this library or - * suggestions for improvment. Send bug reports and feedback to + * suggestions for improvement. Send bug reports and feedback to * davegottner at delphi.com. *---------------------------------------------------------------------------*/ diff --git a/Python/hamt.c b/Python/hamt.c index b3cbf9ac8208..28b4e1ef6cd2 100644 --- a/Python/hamt.c +++ b/Python/hamt.c @@ -830,7 +830,7 @@ hamt_node_bitmap_assoc(PyHamtNode_Bitmap *self, Instead we start using an Array node, which has simpler (faster) implementation at the expense of - having prealocated 32 pointers for its keys/values + having preallocated 32 pointers for its keys/values pairs. Small hamt objects (<30 keys) usually don't have any diff --git a/Python/import.c b/Python/import.c index 15f1d9417600..9f5ec284ae15 100644 --- a/Python/import.c +++ b/Python/import.c @@ -833,7 +833,7 @@ import_add_module(PyThreadState *tstate, PyObject *name) } else { m = PyObject_GetItem(modules, name); - // For backward-comaptibility we copy the behavior + // For backward-compatibility we copy the behavior // of PyDict_GetItemWithError(). if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { _PyErr_Clear(tstate); diff --git a/Python/initconfig.c b/Python/initconfig.c index 924744302890..5bd7d4fcf889 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -1068,7 +1068,7 @@ config_init_program_name(PyConfig *config) or rather, to work around Apple's overly strict requirements of the process name. However, we still need a usable sys.executable, so the actual executable path is passed in an environment variable. - See Lib/plat-mac/bundlebuiler.py for details about the bootstrap + See Lib/plat-mac/bundlebuilder.py for details about the bootstrap script. */ const char *p = config_get_env(config, "PYTHONEXECUTABLE"); if (p != NULL) { diff --git a/Python/pystate.c b/Python/pystate.c index 1c3c0f44ed3f..3b2adf54be45 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1499,7 +1499,7 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data) // Switch to the original interpreter. PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interp); if (interp == NULL) { - // The intepreter was already destroyed. + // The interpreter was already destroyed. if (data->free != NULL) { // XXX Someone leaked some memory... } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 103a11152412..a89ebceb66c5 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1162,7 +1162,7 @@ static PyTypeObject AsyncGenHooksType; PyDoc_STRVAR(asyncgen_hooks_doc, "asyncgen_hooks\n\ \n\ -A struct sequence providing information about asynhronous\n\ +A struct sequence providing information about asynchronous\n\ generators hooks. The attributes are read only."); static PyStructSequence_Field asyncgen_hooks_fields[] = { diff --git a/Tools/ssl/make_ssl_data.py b/Tools/ssl/make_ssl_data.py index 3fb49852f4c2..fdabd5d732ca 100755 --- a/Tools/ssl/make_ssl_data.py +++ b/Tools/ssl/make_ssl_data.py @@ -2,7 +2,7 @@ """ This script should be called *manually* when we want to upgrade SSLError -`library` and `reason` mnemnonics to a more recent OpenSSL version. +`library` and `reason` mnemonics to a more recent OpenSSL version. It takes two arguments: - the path to the OpenSSL source tree (e.g. git checkout) From webhook-mailer at python.org Tue Jul 30 18:33:34 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 30 Jul 2019 22:33:34 -0000 Subject: [Python-checkins] Fix idlelib typos discovered by min ho, pr 15018. (GH-15029) Message-ID: https://github.com/python/cpython/commit/362217c7d68776dcfcfe77df7c9882b7ee0f8700 commit: 362217c7d68776dcfcfe77df7c9882b7ee0f8700 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T15:33:30-07:00 summary: Fix idlelib typos discovered by min ho, pr 15018. (GH-15029) (cherry picked from commit 0acb646b8e405864224bfd6d7d5089980dea63ac) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/NEWS.txt M Lib/idlelib/README.txt M Lib/idlelib/configdialog.py M Lib/idlelib/history.py M Lib/idlelib/idle_test/test_pyparse.py M Lib/idlelib/pyparse.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 2b223e1c8b7a..f37c8922143e 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -43,7 +43,7 @@ run dialogs. bpo-37321: Both subprocess connection error messages now refer to the 'Startup failure' section of the IDLE doc. -bpo-37039: Adjust "Zoom Height" to individual screens by momemtarily +bpo-37039: Adjust "Zoom Height" to individual screens by momentarily maximizing the window on first use with a particular screen. Changing screen settings may invalidate the saved height. While a window is maximized, "Zoom Height" has no effect. diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt index 8912f3970499..42c3506699ba 100644 --- a/Lib/idlelib/README.txt +++ b/Lib/idlelib/README.txt @@ -68,7 +68,7 @@ pyshell.py # Start IDLE, manage shell, complete editor window query.py # Query user for information redirector.py # Intercept widget subcommands (for percolator) (nim). replace.py # Search and replace pattern in text. -rpc.py # Commuicate between idle and user processes (nim). +rpc.py # Communicate between idle and user processes (nim). rstrip.py # Strip trailing whitespace. run.py # Manage user code execution subprocess. runscript.py # Check and run user code. @@ -97,7 +97,7 @@ Text CREDITS.txt # not maintained, displayed by About IDLE HISTORY.txt # NEWS up to July 2001 NEWS.txt # commits, displayed by About IDLE -README.txt # this file, displeyed by About IDLE +README.txt # this file, displayed by About IDLE TODO.txt # needs review extend.txt # about writing extensions help.html # copy of idle.html in docs, displayed by IDLE Help @@ -166,7 +166,7 @@ Shell # pyshell Debug (Shell only) Go to File/Line - debugger # debugger, debugger_r, PyShell.toggle_debuger + debugger # debugger, debugger_r, PyShell.toggle_debugger Stack Viewer # stackviewer, PyShell.open_stack_viewer Auto-open Stack Viewer # stackviewer diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index bba17d34938d..df216582fe60 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -1793,7 +1793,7 @@ def create_page_general(self): """Return frame of widgets for General tab. Enable users to provisionally change general options. Function - load_general_cfg intializes tk variables and helplist using + load_general_cfg initializes tk variables and helplist using idleConf. Radiobuttons startup_shell_on and startup_editor_on set var startup_edit. Radiobuttons save_ask_on and save_auto_on set var autosave. Entry boxes win_width_int and win_height_int diff --git a/Lib/idlelib/history.py b/Lib/idlelib/history.py index 56f53a0f2fb9..ad44a96a9de2 100644 --- a/Lib/idlelib/history.py +++ b/Lib/idlelib/history.py @@ -39,7 +39,7 @@ def history_prev(self, event): return "break" def fetch(self, reverse): - '''Fetch statememt and replace current line in text widget. + '''Fetch statement and replace current line in text widget. Set prefix and pointer as needed for successive fetches. Reset them to None, None when returning to the start line. diff --git a/Lib/idlelib/idle_test/test_pyparse.py b/Lib/idlelib/idle_test/test_pyparse.py index 479b84a216b0..f7154e6ded95 100644 --- a/Lib/idlelib/idle_test/test_pyparse.py +++ b/Lib/idlelib/idle_test/test_pyparse.py @@ -206,8 +206,8 @@ def test_study2(self): 'openbracket', 'bracketing']) tests = ( TestInfo('', 0, 0, '', None, ((0, 0),)), - TestInfo("'''This is a multiline continutation docstring.\n\n", - 0, 49, "'", None, ((0, 0), (0, 1), (49, 0))), + TestInfo("'''This is a multiline continuation docstring.\n\n", + 0, 48, "'", None, ((0, 0), (0, 1), (48, 0))), TestInfo(' # Comment\\\n', 0, 12, '', None, ((0, 0), (1, 1), (12, 0))), # A comment without a space is a special case diff --git a/Lib/idlelib/pyparse.py b/Lib/idlelib/pyparse.py index 81e7f539803c..feb57cbb7405 100644 --- a/Lib/idlelib/pyparse.py +++ b/Lib/idlelib/pyparse.py @@ -575,7 +575,7 @@ def get_base_indent_string(self): return code[i:j] def is_block_opener(self): - "Return True if the last interesting statemtent opens a block." + "Return True if the last interesting statement opens a block." self._study2() return self.lastch == ':' From webhook-mailer at python.org Tue Jul 30 18:34:23 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 30 Jul 2019 22:34:23 -0000 Subject: [Python-checkins] Fix idlelib typos discovered by min ho, pr 15018. (GH-15029) Message-ID: https://github.com/python/cpython/commit/01c62c9f6bf4988212744af0edc8b3a2732dd503 commit: 01c62c9f6bf4988212744af0edc8b3a2732dd503 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T15:34:20-07:00 summary: Fix idlelib typos discovered by min ho, pr 15018. (GH-15029) (cherry picked from commit 0acb646b8e405864224bfd6d7d5089980dea63ac) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/NEWS.txt M Lib/idlelib/README.txt M Lib/idlelib/configdialog.py M Lib/idlelib/history.py M Lib/idlelib/idle_test/test_pyparse.py M Lib/idlelib/pyparse.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 109b2d523a6e..e9a9b0255b0d 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -38,7 +38,7 @@ run dialogs. bpo-37321: Both subprocess connection error messages now refer to the 'Startup failure' section of the IDLE doc. -bpo-37039: Adjust "Zoom Height" to individual screens by momemtarily +bpo-37039: Adjust "Zoom Height" to individual screens by momentarily maximizing the window on first use with a particular screen. Changing screen settings may invalidate the saved height. While a window is maximized, "Zoom Height" has no effect. diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt index 8912f3970499..42c3506699ba 100644 --- a/Lib/idlelib/README.txt +++ b/Lib/idlelib/README.txt @@ -68,7 +68,7 @@ pyshell.py # Start IDLE, manage shell, complete editor window query.py # Query user for information redirector.py # Intercept widget subcommands (for percolator) (nim). replace.py # Search and replace pattern in text. -rpc.py # Commuicate between idle and user processes (nim). +rpc.py # Communicate between idle and user processes (nim). rstrip.py # Strip trailing whitespace. run.py # Manage user code execution subprocess. runscript.py # Check and run user code. @@ -97,7 +97,7 @@ Text CREDITS.txt # not maintained, displayed by About IDLE HISTORY.txt # NEWS up to July 2001 NEWS.txt # commits, displayed by About IDLE -README.txt # this file, displeyed by About IDLE +README.txt # this file, displayed by About IDLE TODO.txt # needs review extend.txt # about writing extensions help.html # copy of idle.html in docs, displayed by IDLE Help @@ -166,7 +166,7 @@ Shell # pyshell Debug (Shell only) Go to File/Line - debugger # debugger, debugger_r, PyShell.toggle_debuger + debugger # debugger, debugger_r, PyShell.toggle_debugger Stack Viewer # stackviewer, PyShell.open_stack_viewer Auto-open Stack Viewer # stackviewer diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index bba17d34938d..df216582fe60 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -1793,7 +1793,7 @@ def create_page_general(self): """Return frame of widgets for General tab. Enable users to provisionally change general options. Function - load_general_cfg intializes tk variables and helplist using + load_general_cfg initializes tk variables and helplist using idleConf. Radiobuttons startup_shell_on and startup_editor_on set var startup_edit. Radiobuttons save_ask_on and save_auto_on set var autosave. Entry boxes win_width_int and win_height_int diff --git a/Lib/idlelib/history.py b/Lib/idlelib/history.py index 56f53a0f2fb9..ad44a96a9de2 100644 --- a/Lib/idlelib/history.py +++ b/Lib/idlelib/history.py @@ -39,7 +39,7 @@ def history_prev(self, event): return "break" def fetch(self, reverse): - '''Fetch statememt and replace current line in text widget. + '''Fetch statement and replace current line in text widget. Set prefix and pointer as needed for successive fetches. Reset them to None, None when returning to the start line. diff --git a/Lib/idlelib/idle_test/test_pyparse.py b/Lib/idlelib/idle_test/test_pyparse.py index 479b84a216b0..f7154e6ded95 100644 --- a/Lib/idlelib/idle_test/test_pyparse.py +++ b/Lib/idlelib/idle_test/test_pyparse.py @@ -206,8 +206,8 @@ def test_study2(self): 'openbracket', 'bracketing']) tests = ( TestInfo('', 0, 0, '', None, ((0, 0),)), - TestInfo("'''This is a multiline continutation docstring.\n\n", - 0, 49, "'", None, ((0, 0), (0, 1), (49, 0))), + TestInfo("'''This is a multiline continuation docstring.\n\n", + 0, 48, "'", None, ((0, 0), (0, 1), (48, 0))), TestInfo(' # Comment\\\n', 0, 12, '', None, ((0, 0), (1, 1), (12, 0))), # A comment without a space is a special case diff --git a/Lib/idlelib/pyparse.py b/Lib/idlelib/pyparse.py index 81e7f539803c..feb57cbb7405 100644 --- a/Lib/idlelib/pyparse.py +++ b/Lib/idlelib/pyparse.py @@ -575,7 +575,7 @@ def get_base_indent_string(self): return code[i:j] def is_block_opener(self): - "Return True if the last interesting statemtent opens a block." + "Return True if the last interesting statement opens a block." self._study2() return self.lastch == ':' From webhook-mailer at python.org Wed Jul 31 00:58:49 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Wed, 31 Jul 2019 04:58:49 -0000 Subject: [Python-checkins] bpo-34162: Add missing items to idlelib/NEWS.txt. (GH-15034) Message-ID: https://github.com/python/cpython/commit/fff5cb21ae270d8572741e18030765580c7ae361 commit: fff5cb21ae270d8572741e18030765580c7ae361 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-31T00:58:45-04:00 summary: bpo-34162: Add missing items to idlelib/NEWS.txt. (GH-15034) files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index e9a9b0255b0d..e3e92bd75f09 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -38,6 +38,10 @@ run dialogs. bpo-37321: Both subprocess connection error messages now refer to the 'Startup failure' section of the IDLE doc. +bpo-37177: Properly attach search dialogs to their main window so +that they behave like other dialogs and do not get hidden behind +their main window. + bpo-37039: Adjust "Zoom Height" to individual screens by momentarily maximizing the window on first use with a particular screen. Changing screen settings may invalidate the saved height. While a window is @@ -46,6 +50,10 @@ maximized, "Zoom Height" has no effect. bpo-35763: Make calltip reminder about '/' meaning positional-only less obtrusive by only adding it when there is room on the first line. +bpo-5680: Add 'Run Customized' to the Run menu to run a module with +customized settings. Any command line arguments entered are added +to sys.argv. One can suppress the normal Shell main module restart. + bpo-35610: Replace now redundant editor.context_use_ps1 with .prompt_last_line. This finishes change started in bpo-31858. @@ -85,6 +93,9 @@ None or False since 2007. bpo-36096: Make colorizer state variables instance-only. +bpo-32129: Avoid blurry IDLE application icon on macOS with Tk 8.6. +Patch by Kevin Walzer. + bpo-24310: Document settings dialog font tab sample. bpo-35689: Add docstrings and tests for colorizer. From webhook-mailer at python.org Wed Jul 31 01:03:57 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Wed, 31 Jul 2019 05:03:57 -0000 Subject: [Python-checkins] bpo-33822: Add IDLE section of What's New 3.8 (#15035) Message-ID: https://github.com/python/cpython/commit/a72ca90eb9f13ee2abc7e19b669974d2d0b3d63e commit: a72ca90eb9f13ee2abc7e19b669974d2d0b3d63e branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-31T01:03:53-04:00 summary: bpo-33822: Add IDLE section of What's New 3.8 (#15035) * bpo-33822: Add IDLE section of What's New 3.8 * Fix role. files: M Doc/whatsnew/3.8.rst diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 8442e4ef2e3c..0455688841fb 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -468,6 +468,32 @@ gettext Added :func:`~gettext.pgettext` and its variants. (Contributed by Franz Glasner, ?ric Araujo, and Cheryl Sabella in :issue:`2504`.) + +idlelib and IDLE +---------------- + +Output over N lines (50 by default) is squeezed down to a button. +N can be changed in the PyShell section of the General page of the +Settings dialog. Fewer, but possibly extra long, lines can be squeezed by +right clicking on the output. Squeezed output can be expanded in place +by double-clicking the button or into the clipboard or a separate window +by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.) + +Add "Run Customized" to the Run menu to run a module with customized +settings. Any command line arguments entered are added to sys.argv. +They also re-appear in the box for the next customized run. One can also +suppress the normal Shell main module restart. (Contributed by Cheryl +Sabella, Terry Jan Reedy, and others in :issue:`5680` and :issue:`37627`.) + +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. Line numbers for an existing +window are shown and hidden in the Options menu. +(Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) + +The changes above have been backported to 3.7 maintenance releases. + + inspect ------- @@ -483,6 +509,7 @@ for :func:`property`, :func:`classmethod`, and :func:`staticmethod`:: self.bit_rate = round(bit_rate / 1000.0, 1) self.duration = ceil(duration) + io -- From webhook-mailer at python.org Wed Jul 31 01:04:33 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Wed, 31 Jul 2019 05:04:33 -0000 Subject: [Python-checkins] bpo-33821: Update IDLE section of What's New 3.7 (#15036) Message-ID: https://github.com/python/cpython/commit/5982b7201b84bfd24a1c2b2836401afee1cad8a7 commit: 5982b7201b84bfd24a1c2b2836401afee1cad8a7 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-31T01:04:29-04:00 summary: bpo-33821: Update IDLE section of What's New 3.7 (#15036) * bpo-33821: Update IDLE section of What's New 3.7 * Fix roles. files: M Doc/whatsnew/3.7.rst diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 46834ec42d9c..af7e22d9faa9 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1017,11 +1017,20 @@ by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.) The changes above have been backported to 3.6 maintenance releases. +NEW in 3.7.4: + +Add "Run Customized" to the Run menu to run a module with customized +settings. Any command line arguments entered are added to sys.argv. +They re-appear in the box for the next customized run. One can also +suppress the normal Shell main module restart. (Contributed by Cheryl +Sabella, Terry Jan Reedy, and others in :issue:`5680` and :issue:`37627`.) + New in 3.7.5: Add optional line numbers for IDLE editor windows. Windows open without line numbers unless set otherwise in the General -tab of the configuration dialog. +tab of the configuration dialog. Line numbers for an existing +window are shown and hidden in the Options menu. (Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) From webhook-mailer at python.org Wed Jul 31 01:16:49 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 05:16:49 -0000 Subject: [Python-checkins] bpo-33822: Add IDLE section of What's New 3.8 (GH-15035) Message-ID: https://github.com/python/cpython/commit/c7236b498fa2fda2240308064605a8ab5eb0fdf8 commit: c7236b498fa2fda2240308064605a8ab5eb0fdf8 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T22:16:45-07:00 summary: bpo-33822: Add IDLE section of What's New 3.8 (GH-15035) * bpo-33822: Add IDLE section of What's New 3.8 * Fix role. (cherry picked from commit a72ca90eb9f13ee2abc7e19b669974d2d0b3d63e) Co-authored-by: Terry Jan Reedy files: M Doc/whatsnew/3.8.rst diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 8cfe88085de8e..967b3f2d68b95 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -465,6 +465,32 @@ gettext Added :func:`~gettext.pgettext` and its variants. (Contributed by Franz Glasner, ?ric Araujo, and Cheryl Sabella in :issue:`2504`.) + +idlelib and IDLE +---------------- + +Output over N lines (50 by default) is squeezed down to a button. +N can be changed in the PyShell section of the General page of the +Settings dialog. Fewer, but possibly extra long, lines can be squeezed by +right clicking on the output. Squeezed output can be expanded in place +by double-clicking the button or into the clipboard or a separate window +by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.) + +Add "Run Customized" to the Run menu to run a module with customized +settings. Any command line arguments entered are added to sys.argv. +They also re-appear in the box for the next customized run. One can also +suppress the normal Shell main module restart. (Contributed by Cheryl +Sabella, Terry Jan Reedy, and others in :issue:`5680` and :issue:`37627`.) + +Add optional line numbers for IDLE editor windows. Windows +open without line numbers unless set otherwise in the General +tab of the configuration dialog. Line numbers for an existing +window are shown and hidden in the Options menu. +(Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) + +The changes above have been backported to 3.7 maintenance releases. + + inspect ------- @@ -480,6 +506,7 @@ for :func:`property`, :func:`classmethod`, and :func:`staticmethod`:: self.bit_rate = round(bit_rate / 1000.0, 1) self.duration = ceil(duration) + io -- From webhook-mailer at python.org Wed Jul 31 01:19:32 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 05:19:32 -0000 Subject: [Python-checkins] bpo-34162: Add missing items to idlelib/NEWS.txt. (GH-15034) Message-ID: https://github.com/python/cpython/commit/34de5dcf218badfece4ef78587733bbb903ab7b9 commit: 34de5dcf218badfece4ef78587733bbb903ab7b9 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T22:19:28-07:00 summary: bpo-34162: Add missing items to idlelib/NEWS.txt. (GH-15034) (cherry picked from commit fff5cb21ae270d8572741e18030765580c7ae361) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index e9a9b0255b0d..e3e92bd75f09 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -38,6 +38,10 @@ run dialogs. bpo-37321: Both subprocess connection error messages now refer to the 'Startup failure' section of the IDLE doc. +bpo-37177: Properly attach search dialogs to their main window so +that they behave like other dialogs and do not get hidden behind +their main window. + bpo-37039: Adjust "Zoom Height" to individual screens by momentarily maximizing the window on first use with a particular screen. Changing screen settings may invalidate the saved height. While a window is @@ -46,6 +50,10 @@ maximized, "Zoom Height" has no effect. bpo-35763: Make calltip reminder about '/' meaning positional-only less obtrusive by only adding it when there is room on the first line. +bpo-5680: Add 'Run Customized' to the Run menu to run a module with +customized settings. Any command line arguments entered are added +to sys.argv. One can suppress the normal Shell main module restart. + bpo-35610: Replace now redundant editor.context_use_ps1 with .prompt_last_line. This finishes change started in bpo-31858. @@ -85,6 +93,9 @@ None or False since 2007. bpo-36096: Make colorizer state variables instance-only. +bpo-32129: Avoid blurry IDLE application icon on macOS with Tk 8.6. +Patch by Kevin Walzer. + bpo-24310: Document settings dialog font tab sample. bpo-35689: Add docstrings and tests for colorizer. From webhook-mailer at python.org Wed Jul 31 01:21:18 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 05:21:18 -0000 Subject: [Python-checkins] bpo-34162: Add missing items to idlelib/NEWS.txt. (GH-15034) Message-ID: https://github.com/python/cpython/commit/ac7cb7b9beaf907a4a484991e467913813bb4971 commit: ac7cb7b9beaf907a4a484991e467913813bb4971 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T22:21:14-07:00 summary: bpo-34162: Add missing items to idlelib/NEWS.txt. (GH-15034) (cherry picked from commit fff5cb21ae270d8572741e18030765580c7ae361) Co-authored-by: Terry Jan Reedy files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index f37c8922143e..cf545dcc6745 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -43,6 +43,10 @@ run dialogs. bpo-37321: Both subprocess connection error messages now refer to the 'Startup failure' section of the IDLE doc. +bpo-37177: Properly attach search dialogs to their main window so +that they behave like other dialogs and do not get hidden behind +their main window. + bpo-37039: Adjust "Zoom Height" to individual screens by momentarily maximizing the window on first use with a particular screen. Changing screen settings may invalidate the saved height. While a window is @@ -51,6 +55,10 @@ maximized, "Zoom Height" has no effect. bpo-35763: Make calltip reminder about '/' meaning positional-only less obtrusive by only adding it when there is room on the first line. +bpo-5680: Add 'Run Customized' to the Run menu to run a module with +customized settings. Any command line arguments entered are added +to sys.argv. One can suppress the normal Shell main module restart. + bpo-35610: Replace now redundant editor.context_use_ps1 with .prompt_last_line. This finishes change started in bpo-31858. @@ -95,6 +103,9 @@ None or False since 2007. bpo-36096: Make colorizer state variables instance-only. +bpo-32129: Avoid blurry IDLE application icon on macOS with Tk 8.6. +Patch by Kevin Walzer. + bpo-24310: Document settings dialog font tab sample. bpo-35689: Add docstrings and tests for colorizer. From webhook-mailer at python.org Wed Jul 31 01:23:09 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 05:23:09 -0000 Subject: [Python-checkins] bpo-33821: Update IDLE section of What's New 3.7 (GH-15036) Message-ID: https://github.com/python/cpython/commit/9e044ddefd5e02cf0ffac52bcbae6a21a677d257 commit: 9e044ddefd5e02cf0ffac52bcbae6a21a677d257 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T22:23:05-07:00 summary: bpo-33821: Update IDLE section of What's New 3.7 (GH-15036) * bpo-33821: Update IDLE section of What's New 3.7 * Fix roles. (cherry picked from commit 5982b7201b84bfd24a1c2b2836401afee1cad8a7) Co-authored-by: Terry Jan Reedy files: M Doc/whatsnew/3.7.rst diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 46834ec42d9c..af7e22d9faa9 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1017,11 +1017,20 @@ by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.) The changes above have been backported to 3.6 maintenance releases. +NEW in 3.7.4: + +Add "Run Customized" to the Run menu to run a module with customized +settings. Any command line arguments entered are added to sys.argv. +They re-appear in the box for the next customized run. One can also +suppress the normal Shell main module restart. (Contributed by Cheryl +Sabella, Terry Jan Reedy, and others in :issue:`5680` and :issue:`37627`.) + New in 3.7.5: Add optional line numbers for IDLE editor windows. Windows open without line numbers unless set otherwise in the General -tab of the configuration dialog. +tab of the configuration dialog. Line numbers for an existing +window are shown and hidden in the Options menu. (Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) From webhook-mailer at python.org Wed Jul 31 01:24:47 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 05:24:47 -0000 Subject: [Python-checkins] bpo-33821: Update IDLE section of What's New 3.7 (GH-15036) Message-ID: https://github.com/python/cpython/commit/8c6e35d2d21928d3e611c39399099e69532952a9 commit: 8c6e35d2d21928d3e611c39399099e69532952a9 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T22:24:43-07:00 summary: bpo-33821: Update IDLE section of What's New 3.7 (GH-15036) * bpo-33821: Update IDLE section of What's New 3.7 * Fix roles. (cherry picked from commit 5982b7201b84bfd24a1c2b2836401afee1cad8a7) Co-authored-by: Terry Jan Reedy files: M Doc/whatsnew/3.7.rst diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 934a5693ebf2..54c7b8f37da9 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1017,11 +1017,20 @@ by right-clicking the button. (Contributed by Tal Einat in :issue:`1529353`.) The changes above have been backported to 3.6 maintenance releases. +NEW in 3.7.4: + +Add "Run Customized" to the Run menu to run a module with customized +settings. Any command line arguments entered are added to sys.argv. +They re-appear in the box for the next customized run. One can also +suppress the normal Shell main module restart. (Contributed by Cheryl +Sabella, Terry Jan Reedy, and others in :issue:`5680` and :issue:`37627`.) + New in 3.7.5: Add optional line numbers for IDLE editor windows. Windows open without line numbers unless set otherwise in the General -tab of the configuration dialog. +tab of the configuration dialog. Line numbers for an existing +window are shown and hidden in the Options menu. (Contributed by Tal Einat and Saimadhav Heblikar in :issue:`17535`.) From webhook-mailer at python.org Wed Jul 31 01:35:35 2019 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Wed, 31 Jul 2019 05:35:35 -0000 Subject: [Python-checkins] IDLE: changelog correction and addition (GH-15042) Message-ID: https://github.com/python/cpython/commit/d04f8907baaa6620be7347ba14dd84bc4ef33814 commit: d04f8907baaa6620be7347ba14dd84bc4ef33814 branch: master author: Terry Jan Reedy committer: GitHub date: 2019-07-31T01:35:30-04:00 summary: IDLE: changelog correction and addition (GH-15042) files: M Misc/NEWS.d/3.8.0a3.rst M Misc/NEWS.d/3.8.0b1.rst diff --git a/Misc/NEWS.d/3.8.0a3.rst b/Misc/NEWS.d/3.8.0a3.rst index fc37b22ddb19..66308c6bca2e 100644 --- a/Misc/NEWS.d/3.8.0a3.rst +++ b/Misc/NEWS.d/3.8.0a3.rst @@ -830,7 +830,8 @@ Refactor class variables to instance variables in colorizer. .. nonce: WbaRJW .. section: IDLE -Increase test coverage of idlelib.autocomplete by 30%. +Increase test coverage of idlelib.autocomplete by 30%. Patch by Louie +Lu .. diff --git a/Misc/NEWS.d/3.8.0b1.rst b/Misc/NEWS.d/3.8.0b1.rst index 3cebcfb587c4..609aa338e844 100644 --- a/Misc/NEWS.d/3.8.0b1.rst +++ b/Misc/NEWS.d/3.8.0b1.rst @@ -1951,7 +1951,7 @@ sys.exit(). .. -.. bpo: 13102 +.. bpo: 36807 .. date: 2019-05-05-16-27-53 .. nonce: AGNWYJ .. section: IDLE From webhook-mailer at python.org Wed Jul 31 01:42:22 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 05:42:22 -0000 Subject: [Python-checkins] IDLE: changelog correction and addition (GH-15042) Message-ID: https://github.com/python/cpython/commit/8194a2050dcc11cca40a619a7bafcc0abf23bc4e commit: 8194a2050dcc11cca40a619a7bafcc0abf23bc4e branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-30T22:42:17-07:00 summary: IDLE: changelog correction and addition (GH-15042) (cherry picked from commit d04f8907baaa6620be7347ba14dd84bc4ef33814) Co-authored-by: Terry Jan Reedy files: M Misc/NEWS.d/3.8.0a3.rst M Misc/NEWS.d/3.8.0b1.rst diff --git a/Misc/NEWS.d/3.8.0a3.rst b/Misc/NEWS.d/3.8.0a3.rst index fc37b22ddb19..66308c6bca2e 100644 --- a/Misc/NEWS.d/3.8.0a3.rst +++ b/Misc/NEWS.d/3.8.0a3.rst @@ -830,7 +830,8 @@ Refactor class variables to instance variables in colorizer. .. nonce: WbaRJW .. section: IDLE -Increase test coverage of idlelib.autocomplete by 30%. +Increase test coverage of idlelib.autocomplete by 30%. Patch by Louie +Lu .. diff --git a/Misc/NEWS.d/3.8.0b1.rst b/Misc/NEWS.d/3.8.0b1.rst index 3cebcfb587c4..609aa338e844 100644 --- a/Misc/NEWS.d/3.8.0b1.rst +++ b/Misc/NEWS.d/3.8.0b1.rst @@ -1951,7 +1951,7 @@ sys.exit(). .. -.. bpo: 13102 +.. bpo: 36807 .. date: 2019-05-05-16-27-53 .. nonce: AGNWYJ .. section: IDLE From webhook-mailer at python.org Wed Jul 31 02:36:56 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Wed, 31 Jul 2019 06:36:56 -0000 Subject: [Python-checkins] Refined Qt GUI example in the logging cookbook. (GH-15045) Message-ID: https://github.com/python/cpython/commit/472eced6770c2fabab3031e4e16cd32e32b8a0cc commit: 472eced6770c2fabab3031e4e16cd32e32b8a0cc branch: master author: Vinay Sajip committer: GitHub date: 2019-07-31T07:36:45+01:00 summary: Refined Qt GUI example in the logging cookbook. (GH-15045) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index ef9ad447f801..9f52780af4fc 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2760,8 +2760,8 @@ The following example shows how to log to a Qt GUI. This introduces a simple ``QtHandler`` class which takes a callable, which should be a slot in the main thread that does GUI updates. A worker thread is also created to show how you can log to the GUI from both the UI itself (via a button for manual logging) -as well as a worker thread doing work in the background (here, just random -short delays). +as well as a worker thread doing work in the background (here, just logging +messages at random levels with random short delays in between). The worker thread is implemented using Qt's ``QThread`` class rather than the :mod:`threading` module, as there are circumstances where one has to use @@ -2769,7 +2769,7 @@ The worker thread is implemented using Qt's ``QThread`` class rather than the The code should work with recent releases of either ``PySide2`` or ``PyQt5``. You should be able to adapt the approach to earlier versions of Qt. Please -refer to the comments in the code for more detailed information. +refer to the comments in the code snippet for more detailed information. .. code-block:: python3 @@ -2789,22 +2789,24 @@ refer to the comments in the code for more detailed information. Signal = QtCore.pyqtSignal Slot = QtCore.pyqtSlot + logger = logging.getLogger(__name__) + # # Signals need to be contained in a QObject or subclass in order to be correctly # initialized. # class Signaller(QtCore.QObject): - signal = Signal(str) + signal = Signal(str, logging.LogRecord) # # Output to a Qt GUI is only supposed to happen on the main thread. So, this # handler is designed to take a slot function which is set up to run in the main - # thread. In this example, the function takes a single argument which is a - # formatted log message. You can attach a formatter instance which formats a - # LogRecord however you like, or change the slot function to take some other - # value derived from the LogRecord. + # thread. In this example, the function takes a string argument which is a + # formatted log message, and the log record which generated it. The formatted + # string is just a convenience - you could format a string for output any way + # you like in the slot function itself. # # You specify the slot function to do whatever GUI updates you want. The handler # doesn't know or care about specific UI elements. @@ -2817,7 +2819,7 @@ refer to the comments in the code for more detailed information. def emit(self, record): s = self.format(record) - self.signaller.signal.emit(s) + self.signaller.signal.emit(s, record) # # This example uses QThreads, which means that the threads at the Python level @@ -2827,6 +2829,13 @@ refer to the comments in the code for more detailed information. def ctname(): return QtCore.QThread.currentThread().objectName() + + # + # Used to generate random levels for logging. + # + LEVELS = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, + logging.CRITICAL) + # # This worker class represents work that is done in a thread separate to the # main thread. The way the thread is kicked off to do work is via a button press @@ -2851,7 +2860,8 @@ refer to the comments in the code for more detailed information. while not QtCore.QThread.currentThread().isInterruptionRequested(): delay = 0.5 + random.random() * 2 time.sleep(delay) - logger.debug('Message after delay of %3.1f: %d', delay, i, extra=extra) + level = random.choice(LEVELS) + logger.log(level, 'Message after delay of %3.1f: %d', delay, i, extra=extra) i += 1 # @@ -2864,10 +2874,18 @@ refer to the comments in the code for more detailed information. # class Window(QtWidgets.QWidget): + COLORS = { + logging.DEBUG: 'black', + logging.INFO: 'blue', + logging.WARNING: 'orange', + logging.ERROR: 'red', + logging.CRITICAL: 'purple', + } + def __init__(self, app): super(Window, self).__init__() self.app = app - self.textedit = te = QtWidgets.QTextEdit(self) + self.textedit = te = QtWidgets.QPlainTextEdit(self) # Set whatever the default monospace font is for the platform f = QtGui.QFont('nosuchfont') f.setStyleHint(f.Monospace) @@ -2880,7 +2898,7 @@ refer to the comments in the code for more detailed information. self.handler = h = QtHandler(self.update_status) # Remember to use qThreadName rather than threadName in the format string. fs = '%(asctime)s %(qThreadName)-12s %(levelname)-8s %(message)s' - formatter = logging.Formatter(f) + formatter = logging.Formatter(fs) h.setFormatter(formatter) logger.addHandler(h) # Set up to terminate the QThread when we exit @@ -2932,14 +2950,17 @@ refer to the comments in the code for more detailed information. # that's where the slots are set up @Slot(str) - def update_status(self, status): - self.textedit.append(status) + def update_status(self, status, record): + color = self.COLORS.get(record.levelno, 'black') + s = '
    %s
    ' % (color, status) + self.textedit.appendHtml(s) @Slot() def manual_update(self): - levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, - logging.CRITICAL) - level = random.choice(levels) + # This function uses the formatted message passed in, but also uses + # information from the record to format the message in an appropriate + # color according to its severity (level). + level = random.choice(LEVELS) extra = {'qThreadName': ctname() } logger.log(level, 'Manually logged!', extra=extra) @@ -2947,6 +2968,7 @@ refer to the comments in the code for more detailed information. def clear_display(self): self.textedit.clear() + def main(): QtCore.QThread.currentThread().setObjectName('MainThread') logging.getLogger().setLevel(logging.DEBUG) From webhook-mailer at python.org Wed Jul 31 02:59:00 2019 From: webhook-mailer at python.org (Vinay Sajip) Date: Wed, 31 Jul 2019 06:59:00 -0000 Subject: [Python-checkins] Refined Qt GUI example in the logging cookbook. (GH-15045) (GH-15046) Message-ID: https://github.com/python/cpython/commit/9b9cac4e5de975fe2417d8231822bc1d2ca2d273 commit: 9b9cac4e5de975fe2417d8231822bc1d2ca2d273 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Vinay Sajip date: 2019-07-31T07:58:46+01:00 summary: Refined Qt GUI example in the logging cookbook. (GH-15045) (GH-15046) (cherry picked from commit 472eced6770c2fabab3031e4e16cd32e32b8a0cc) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index ef9ad447f801..9f52780af4fc 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -2760,8 +2760,8 @@ The following example shows how to log to a Qt GUI. This introduces a simple ``QtHandler`` class which takes a callable, which should be a slot in the main thread that does GUI updates. A worker thread is also created to show how you can log to the GUI from both the UI itself (via a button for manual logging) -as well as a worker thread doing work in the background (here, just random -short delays). +as well as a worker thread doing work in the background (here, just logging +messages at random levels with random short delays in between). The worker thread is implemented using Qt's ``QThread`` class rather than the :mod:`threading` module, as there are circumstances where one has to use @@ -2769,7 +2769,7 @@ The worker thread is implemented using Qt's ``QThread`` class rather than the The code should work with recent releases of either ``PySide2`` or ``PyQt5``. You should be able to adapt the approach to earlier versions of Qt. Please -refer to the comments in the code for more detailed information. +refer to the comments in the code snippet for more detailed information. .. code-block:: python3 @@ -2789,22 +2789,24 @@ refer to the comments in the code for more detailed information. Signal = QtCore.pyqtSignal Slot = QtCore.pyqtSlot + logger = logging.getLogger(__name__) + # # Signals need to be contained in a QObject or subclass in order to be correctly # initialized. # class Signaller(QtCore.QObject): - signal = Signal(str) + signal = Signal(str, logging.LogRecord) # # Output to a Qt GUI is only supposed to happen on the main thread. So, this # handler is designed to take a slot function which is set up to run in the main - # thread. In this example, the function takes a single argument which is a - # formatted log message. You can attach a formatter instance which formats a - # LogRecord however you like, or change the slot function to take some other - # value derived from the LogRecord. + # thread. In this example, the function takes a string argument which is a + # formatted log message, and the log record which generated it. The formatted + # string is just a convenience - you could format a string for output any way + # you like in the slot function itself. # # You specify the slot function to do whatever GUI updates you want. The handler # doesn't know or care about specific UI elements. @@ -2817,7 +2819,7 @@ refer to the comments in the code for more detailed information. def emit(self, record): s = self.format(record) - self.signaller.signal.emit(s) + self.signaller.signal.emit(s, record) # # This example uses QThreads, which means that the threads at the Python level @@ -2827,6 +2829,13 @@ refer to the comments in the code for more detailed information. def ctname(): return QtCore.QThread.currentThread().objectName() + + # + # Used to generate random levels for logging. + # + LEVELS = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, + logging.CRITICAL) + # # This worker class represents work that is done in a thread separate to the # main thread. The way the thread is kicked off to do work is via a button press @@ -2851,7 +2860,8 @@ refer to the comments in the code for more detailed information. while not QtCore.QThread.currentThread().isInterruptionRequested(): delay = 0.5 + random.random() * 2 time.sleep(delay) - logger.debug('Message after delay of %3.1f: %d', delay, i, extra=extra) + level = random.choice(LEVELS) + logger.log(level, 'Message after delay of %3.1f: %d', delay, i, extra=extra) i += 1 # @@ -2864,10 +2874,18 @@ refer to the comments in the code for more detailed information. # class Window(QtWidgets.QWidget): + COLORS = { + logging.DEBUG: 'black', + logging.INFO: 'blue', + logging.WARNING: 'orange', + logging.ERROR: 'red', + logging.CRITICAL: 'purple', + } + def __init__(self, app): super(Window, self).__init__() self.app = app - self.textedit = te = QtWidgets.QTextEdit(self) + self.textedit = te = QtWidgets.QPlainTextEdit(self) # Set whatever the default monospace font is for the platform f = QtGui.QFont('nosuchfont') f.setStyleHint(f.Monospace) @@ -2880,7 +2898,7 @@ refer to the comments in the code for more detailed information. self.handler = h = QtHandler(self.update_status) # Remember to use qThreadName rather than threadName in the format string. fs = '%(asctime)s %(qThreadName)-12s %(levelname)-8s %(message)s' - formatter = logging.Formatter(f) + formatter = logging.Formatter(fs) h.setFormatter(formatter) logger.addHandler(h) # Set up to terminate the QThread when we exit @@ -2932,14 +2950,17 @@ refer to the comments in the code for more detailed information. # that's where the slots are set up @Slot(str) - def update_status(self, status): - self.textedit.append(status) + def update_status(self, status, record): + color = self.COLORS.get(record.levelno, 'black') + s = '
    %s
    ' % (color, status) + self.textedit.appendHtml(s) @Slot() def manual_update(self): - levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, - logging.CRITICAL) - level = random.choice(levels) + # This function uses the formatted message passed in, but also uses + # information from the record to format the message in an appropriate + # color according to its severity (level). + level = random.choice(LEVELS) extra = {'qThreadName': ctname() } logger.log(level, 'Manually logged!', extra=extra) @@ -2947,6 +2968,7 @@ refer to the comments in the code for more detailed information. def clear_display(self): self.textedit.clear() + def main(): QtCore.QThread.currentThread().setObjectName('MainThread') logging.getLogger().setLevel(logging.DEBUG) From webhook-mailer at python.org Wed Jul 31 05:10:44 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 09:10:44 -0000 Subject: [Python-checkins] bpo-37085: Expose SocketCAN bcm_msg_head flags (GH-13646) Message-ID: https://github.com/python/cpython/commit/d8b914a30b0849476345a19ce0a0ab1ade171b19 commit: d8b914a30b0849476345a19ce0a0ab1ade171b19 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-31T02:10:38-07:00 summary: bpo-37085: Expose SocketCAN bcm_msg_head flags (GH-13646) Expose the CAN_BCM SocketCAN constants used in the bcm_msg_head struct flags (provided by ) under the socket library. This adds the following constants with a CAN_BCM prefix: * SETTIMER * STARTTIMER * TX_COUNTEVT * TX_ANNOUNCE * TX_CP_CAN_ID * RX_FILTER_ID * RX_CHECK_DLC * RX_NO_AUTOTIMER * RX_ANNOUNCE_RESUME * TX_RESET_MULTI_IDX * RX_RTR_FRAME * CAN_FD_FRAME The CAN_FD_FRAME flag was introduced in the 4.8 kernel, while the other ones were present since SocketCAN drivers were mainlined in 2.6.25. As such, it is probably unnecessary to guard against these constants being missing. (cherry picked from commit 31c4fd2a10d90beaa37d630e5f74a471e14e089d) Co-authored-by: karl ding files: A Misc/NEWS.d/next/Library/2019-06-18-16-29-31.bpo-37085.GeYaD6.rst M Doc/library/socket.rst M Lib/test/test_socket.py M Misc/ACKS M Modules/socketmodule.c diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index f1010fb35a47..12416e0f4a9f 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -374,6 +374,9 @@ Constants .. availability:: Linux >= 2.6.25. + .. note:: + The :data:`CAN_BCM_CAN_FD_FRAME` flag is only available on Linux >= 4.8. + .. versionadded:: 3.4 .. data:: CAN_RAW_FD_FRAMES diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index db525642d6af..c472be135e39 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1872,6 +1872,19 @@ def testBCMConstants(self): socket.CAN_BCM_RX_TIMEOUT # cyclic message is absent socket.CAN_BCM_RX_CHANGED # updated CAN frame (detected content change) + # flags + socket.CAN_BCM_SETTIMER + socket.CAN_BCM_STARTTIMER + socket.CAN_BCM_TX_COUNTEVT + socket.CAN_BCM_TX_ANNOUNCE + socket.CAN_BCM_TX_CP_CAN_ID + socket.CAN_BCM_RX_FILTER_ID + socket.CAN_BCM_RX_CHECK_DLC + socket.CAN_BCM_RX_NO_AUTOTIMER + socket.CAN_BCM_RX_ANNOUNCE_RESUME + socket.CAN_BCM_TX_RESET_MULTI_IDX + socket.CAN_BCM_RX_RTR_FRAME + def testCreateSocket(self): with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s: pass diff --git a/Misc/ACKS b/Misc/ACKS index a4973a89d414..829aa796c847 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -395,6 +395,7 @@ Alon Diamant Toby Dickenson Mark Dickinson Jack Diederich +Karl Ding Daniel Diniz Humberto Diogenes Yves Dionne diff --git a/Misc/NEWS.d/next/Library/2019-06-18-16-29-31.bpo-37085.GeYaD6.rst b/Misc/NEWS.d/next/Library/2019-06-18-16-29-31.bpo-37085.GeYaD6.rst new file mode 100644 index 000000000000..e8db521d7aba --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-18-16-29-31.bpo-37085.GeYaD6.rst @@ -0,0 +1,2 @@ +Add the optional Linux SocketCAN Broadcast Manager constants, used as flags +to configure the BCM behaviour, in the socket module. Patch by Karl Ding. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 36d13e768cd3..0470f387e3bc 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7656,6 +7656,8 @@ PyInit__socket(void) #endif #ifdef HAVE_LINUX_CAN_BCM_H PyModule_AddIntMacro(m, CAN_BCM); + + /* BCM opcodes */ PyModule_AddIntConstant(m, "CAN_BCM_TX_SETUP", TX_SETUP); PyModule_AddIntConstant(m, "CAN_BCM_TX_DELETE", TX_DELETE); PyModule_AddIntConstant(m, "CAN_BCM_TX_READ", TX_READ); @@ -7668,6 +7670,23 @@ PyInit__socket(void) PyModule_AddIntConstant(m, "CAN_BCM_RX_STATUS", RX_STATUS); PyModule_AddIntConstant(m, "CAN_BCM_RX_TIMEOUT", RX_TIMEOUT); PyModule_AddIntConstant(m, "CAN_BCM_RX_CHANGED", RX_CHANGED); + + /* BCM flags */ + PyModule_AddIntConstant(m, "CAN_BCM_SETTIMER", SETTIMER); + PyModule_AddIntConstant(m, "CAN_BCM_STARTTIMER", STARTTIMER); + PyModule_AddIntConstant(m, "CAN_BCM_TX_COUNTEVT", TX_COUNTEVT); + PyModule_AddIntConstant(m, "CAN_BCM_TX_ANNOUNCE", TX_ANNOUNCE); + PyModule_AddIntConstant(m, "CAN_BCM_TX_CP_CAN_ID", TX_CP_CAN_ID); + PyModule_AddIntConstant(m, "CAN_BCM_RX_FILTER_ID", RX_FILTER_ID); + PyModule_AddIntConstant(m, "CAN_BCM_RX_CHECK_DLC", RX_CHECK_DLC); + PyModule_AddIntConstant(m, "CAN_BCM_RX_NO_AUTOTIMER", RX_NO_AUTOTIMER); + PyModule_AddIntConstant(m, "CAN_BCM_RX_ANNOUNCE_RESUME", RX_ANNOUNCE_RESUME); + PyModule_AddIntConstant(m, "CAN_BCM_TX_RESET_MULTI_IDX", TX_RESET_MULTI_IDX); + PyModule_AddIntConstant(m, "CAN_BCM_RX_RTR_FRAME", RX_RTR_FRAME); +#ifdef CAN_FD_FRAME + /* CAN_FD_FRAME was only introduced in the 4.8.x kernel series */ + PyModule_AddIntConstant(m, "CAN_BCM_CAN_FD_FRAME", CAN_FD_FRAME); +#endif #endif #ifdef SOL_RDS PyModule_AddIntMacro(m, SOL_RDS); From webhook-mailer at python.org Wed Jul 31 08:11:48 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 31 Jul 2019 12:11:48 -0000 Subject: [Python-checkins] bpo-37695: Correct unget_wch error message. (GH-14986) Message-ID: https://github.com/python/cpython/commit/c9345e382c630ddcc2b148b30954640e0e435c8a commit: c9345e382c630ddcc2b148b30954640e0e435c8a branch: master author: Anthony Sottile committer: Serhiy Storchaka date: 2019-07-31T15:11:24+03:00 summary: bpo-37695: Correct unget_wch error message. (GH-14986) files: A Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst M Modules/_cursesmodule.c diff --git a/Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst b/Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst new file mode 100644 index 000000000000..ca6c11641ed6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst @@ -0,0 +1 @@ +Correct :func:`curses.unget_wch` error message. Patch by Anthony Sottile. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 8595b6282c1b..dbe3c99d7d59 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -4176,7 +4176,7 @@ PyCurses_ConvertToWchar_t(PyObject *obj, wchar_t buffer[2]; if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) { PyErr_Format(PyExc_TypeError, - "expect bytes or str of length 1, or int, " + "expect str of length 1 or int, " "got a str of length %zi", PyUnicode_GET_LENGTH(obj)); return 0; @@ -4203,7 +4203,7 @@ PyCurses_ConvertToWchar_t(PyObject *obj, } else { PyErr_Format(PyExc_TypeError, - "expect bytes or str of length 1, or int, got %s", + "expect str of length 1 or int, got %s", Py_TYPE(obj)->tp_name); return 0; } From webhook-mailer at python.org Wed Jul 31 10:48:39 2019 From: webhook-mailer at python.org (Antoine Pitrou) Date: Wed, 31 Jul 2019 14:48:39 -0000 Subject: [Python-checkins] bpo-34101: Add doc of PyBuffer_GetPointer (GH-14994) Message-ID: https://github.com/python/cpython/commit/1b29af83bc17e773b0c0d117f5fe1018fde46b0d commit: 1b29af83bc17e773b0c0d117f5fe1018fde46b0d branch: master author: Hai Shi committer: Antoine Pitrou date: 2019-07-31T16:48:15+02:00 summary: bpo-34101: Add doc of PyBuffer_GetPointer (GH-14994) files: M Doc/c-api/buffer.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 7553efc2cfbc..fdb8bd19d218 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -475,6 +475,12 @@ Buffer-related functions (*order* is ``'A'``). Return ``0`` otherwise. This function always succeeds. +.. c:function:: void* PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices) + + Get the memory area pointed to by the *indices* inside the given *view*. + *indices* must point to an array of ``view->ndim`` indices. + + .. c:function:: int PyBuffer_ToContiguous(void *buf, Py_buffer *src, Py_ssize_t len, char order) Copy *len* bytes from *src* to its contiguous representation in *buf*. From webhook-mailer at python.org Wed Jul 31 10:57:15 2019 From: webhook-mailer at python.org (Antoine Pitrou) Date: Wed, 31 Jul 2019 14:57:15 -0000 Subject: [Python-checkins] bpo-34101: Add doc of PyBuffer_GetPointer (GH-14994) (GH-15055) Message-ID: https://github.com/python/cpython/commit/353306137176985f1a2995ff964b0c00eccd1434 commit: 353306137176985f1a2995ff964b0c00eccd1434 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Antoine Pitrou date: 2019-07-31T16:57:09+02:00 summary: bpo-34101: Add doc of PyBuffer_GetPointer (GH-14994) (GH-15055) (cherry picked from commit 1b29af83bc17e773b0c0d117f5fe1018fde46b0d) Co-authored-by: Hai Shi files: M Doc/c-api/buffer.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 1d4ac457e54c..71e5b32f1fe5 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -475,6 +475,12 @@ Buffer-related functions (*order* is ``'A'``). Return ``0`` otherwise. This function always succeeds. +.. c:function:: void* PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices) + + Get the memory area pointed to by the *indices* inside the given *view*. + *indices* must point to an array of ``view->ndim`` indices. + + .. c:function:: int PyBuffer_ToContiguous(void *buf, Py_buffer *src, Py_ssize_t len, char order) Copy *len* bytes from *src* to its contiguous representation in *buf*. From webhook-mailer at python.org Wed Jul 31 11:04:56 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 15:04:56 -0000 Subject: [Python-checkins] bpo-34101: Add doc of PyBuffer_GetPointer (GH-14994) Message-ID: https://github.com/python/cpython/commit/29a3a33d99dae0106c6af9d0fd75a11bef26d15c commit: 29a3a33d99dae0106c6af9d0fd75a11bef26d15c branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-31T08:04:31-07:00 summary: bpo-34101: Add doc of PyBuffer_GetPointer (GH-14994) (cherry picked from commit 1b29af83bc17e773b0c0d117f5fe1018fde46b0d) Co-authored-by: Hai Shi files: M Doc/c-api/buffer.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 7553efc2cfbc..fdb8bd19d218 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -475,6 +475,12 @@ Buffer-related functions (*order* is ``'A'``). Return ``0`` otherwise. This function always succeeds. +.. c:function:: void* PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices) + + Get the memory area pointed to by the *indices* inside the given *view*. + *indices* must point to an array of ``view->ndim`` indices. + + .. c:function:: int PyBuffer_ToContiguous(void *buf, Py_buffer *src, Py_ssize_t len, char order) Copy *len* bytes from *src* to its contiguous representation in *buf*. From webhook-mailer at python.org Wed Jul 31 14:51:07 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 31 Jul 2019 18:51:07 -0000 Subject: [Python-checkins] bpo-37723: Fix performance regression on regular expression parsing. (GH-15030) Message-ID: https://github.com/python/cpython/commit/9f55551f3df238e58315e724e50cb0d574d75b94 commit: 9f55551f3df238e58315e724e50cb0d574d75b94 branch: master author: yannvgn committer: Serhiy Storchaka date: 2019-07-31T21:50:39+03:00 summary: bpo-37723: Fix performance regression on regular expression parsing. (GH-15030) Improve performance of sre_parse._uniq function. files: A Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst M Lib/sre_parse.py M Misc/ACKS diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index 84c912573e7f..83119168e637 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -430,13 +430,7 @@ def _escape(source, escape, state): raise source.error("bad escape %s" % escape, len(escape)) def _uniq(items): - if len(set(items)) == len(items): - return items - newitems = [] - for item in items: - if item not in newitems: - newitems.append(item) - return newitems + return list(dict.fromkeys(items)) def _parse_sub(source, state, verbose, nested): # parse an alternation: a|b|c diff --git a/Misc/ACKS b/Misc/ACKS index 9ff74ec0cca1..6463b535e17c 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1708,6 +1708,7 @@ Michael Urman Hector Urtubia Lukas Vacek Ville Vainio +Yann Vaginay Andi Vajda Case Van Horsen John Mark Vandenberg diff --git a/Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst b/Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst new file mode 100644 index 000000000000..65507bd0dc91 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst @@ -0,0 +1,2 @@ +Fix performance regression on regular expression parsing with huge +character sets. Patch by Yann Vaginay. \ No newline at end of file From webhook-mailer at python.org Wed Jul 31 16:22:39 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 20:22:39 -0000 Subject: [Python-checkins] bpo-37723: Fix performance regression on regular expression parsing. (GH-15030) Message-ID: https://github.com/python/cpython/commit/33b700ba8cbb128519442eeed8c8747ff73f4524 commit: 33b700ba8cbb128519442eeed8c8747ff73f4524 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-31T13:22:19-07:00 summary: bpo-37723: Fix performance regression on regular expression parsing. (GH-15030) Improve performance of sre_parse._uniq function. (cherry picked from commit 9f55551f3df238e58315e724e50cb0d574d75b94) Co-authored-by: yannvgn files: A Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst M Lib/sre_parse.py M Misc/ACKS diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index a53735b07ded..cb2c4c3281c9 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -406,13 +406,7 @@ def _escape(source, escape, state): raise source.error("bad escape %s" % escape, len(escape)) def _uniq(items): - if len(set(items)) == len(items): - return items - newitems = [] - for item in items: - if item not in newitems: - newitems.append(item) - return newitems + return list(dict.fromkeys(items)) def _parse_sub(source, state, verbose, nested): # parse an alternation: a|b|c diff --git a/Misc/ACKS b/Misc/ACKS index 1b54d7ef7f18..29b6690f2174 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1672,6 +1672,7 @@ Michael Urman Hector Urtubia Lukas Vacek Ville Vainio +Yann Vaginay Andi Vajda Case Van Horsen John Mark Vandenberg diff --git a/Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst b/Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst new file mode 100644 index 000000000000..65507bd0dc91 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst @@ -0,0 +1,2 @@ +Fix performance regression on regular expression parsing with huge +character sets. Patch by Yann Vaginay. \ No newline at end of file From webhook-mailer at python.org Wed Jul 31 16:22:39 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 20:22:39 -0000 Subject: [Python-checkins] bpo-37723: Fix performance regression on regular expression parsing. (GH-15030) Message-ID: https://github.com/python/cpython/commit/77fcccb5321137456549b7f55b819f2c8a4c78a4 commit: 77fcccb5321137456549b7f55b819f2c8a4c78a4 branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-31T13:22:09-07:00 summary: bpo-37723: Fix performance regression on regular expression parsing. (GH-15030) Improve performance of sre_parse._uniq function. (cherry picked from commit 9f55551f3df238e58315e724e50cb0d574d75b94) Co-authored-by: yannvgn files: A Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst M Lib/sre_parse.py M Misc/ACKS diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index 84c912573e7f..83119168e637 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -430,13 +430,7 @@ def _escape(source, escape, state): raise source.error("bad escape %s" % escape, len(escape)) def _uniq(items): - if len(set(items)) == len(items): - return items - newitems = [] - for item in items: - if item not in newitems: - newitems.append(item) - return newitems + return list(dict.fromkeys(items)) def _parse_sub(source, state, verbose, nested): # parse an alternation: a|b|c diff --git a/Misc/ACKS b/Misc/ACKS index 829aa796c847..ad2e0a1996aa 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1702,6 +1702,7 @@ Michael Urman Hector Urtubia Lukas Vacek Ville Vainio +Yann Vaginay Andi Vajda Case Van Horsen John Mark Vandenberg diff --git a/Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst b/Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst new file mode 100644 index 000000000000..65507bd0dc91 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-31-16-49-01.bpo-37723.zq6tw8.rst @@ -0,0 +1,2 @@ +Fix performance regression on regular expression parsing with huge +character sets. Patch by Yann Vaginay. \ No newline at end of file From webhook-mailer at python.org Wed Jul 31 16:25:56 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 20:25:56 -0000 Subject: [Python-checkins] bpo-37695: Correct unget_wch error message. (GH-14986) Message-ID: https://github.com/python/cpython/commit/35d9c37e271c35b87d64cc7422600e573f3ee244 commit: 35d9c37e271c35b87d64cc7422600e573f3ee244 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-31T13:25:45-07:00 summary: bpo-37695: Correct unget_wch error message. (GH-14986) (cherry picked from commit c9345e382c630ddcc2b148b30954640e0e435c8a) Co-authored-by: Anthony Sottile files: A Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst M Modules/_cursesmodule.c diff --git a/Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst b/Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst new file mode 100644 index 000000000000..ca6c11641ed6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst @@ -0,0 +1 @@ +Correct :func:`curses.unget_wch` error message. Patch by Anthony Sottile. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 931c5ceed062..45decf96a352 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -3120,7 +3120,7 @@ PyCurses_ConvertToWchar_t(PyObject *obj, wchar_t buffer[2]; if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) { PyErr_Format(PyExc_TypeError, - "expect bytes or str of length 1, or int, " + "expect str of length 1 or int, " "got a str of length %zi", PyUnicode_GET_LENGTH(obj)); return 0; @@ -3147,7 +3147,7 @@ PyCurses_ConvertToWchar_t(PyObject *obj, } else { PyErr_Format(PyExc_TypeError, - "expect bytes or str of length 1, or int, got %s", + "expect str of length 1 or int, got %s", Py_TYPE(obj)->tp_name); return 0; } From webhook-mailer at python.org Wed Jul 31 16:45:19 2019 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 31 Jul 2019 20:45:19 -0000 Subject: [Python-checkins] bpo-37695: Correct unget_wch error message. (GH-14986) Message-ID: https://github.com/python/cpython/commit/462f07040b39b778aa782d0f62050b70416dec7b commit: 462f07040b39b778aa782d0f62050b70416dec7b branch: 3.8 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2019-07-31T13:44:59-07:00 summary: bpo-37695: Correct unget_wch error message. (GH-14986) (cherry picked from commit c9345e382c630ddcc2b148b30954640e0e435c8a) Co-authored-by: Anthony Sottile files: A Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst M Modules/_cursesmodule.c diff --git a/Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst b/Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst new file mode 100644 index 000000000000..ca6c11641ed6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-27-20-21-03.bpo-37695.QANdvg.rst @@ -0,0 +1 @@ +Correct :func:`curses.unget_wch` error message. Patch by Anthony Sottile. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 2435e1c12955..8fca7fcf1c18 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -4176,7 +4176,7 @@ PyCurses_ConvertToWchar_t(PyObject *obj, wchar_t buffer[2]; if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) { PyErr_Format(PyExc_TypeError, - "expect bytes or str of length 1, or int, " + "expect str of length 1 or int, " "got a str of length %zi", PyUnicode_GET_LENGTH(obj)); return 0; @@ -4203,7 +4203,7 @@ PyCurses_ConvertToWchar_t(PyObject *obj, } else { PyErr_Format(PyExc_TypeError, - "expect bytes or str of length 1, or int, got %s", + "expect str of length 1 or int, got %s", Py_TYPE(obj)->tp_name); return 0; } From webhook-mailer at python.org Wed Jul 31 18:50:03 2019 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 31 Jul 2019 22:50:03 -0000 Subject: [Python-checkins] bpo-37730: Fix usage of NotImplemented instead of NotImplementedError in docs. (GH-15062) Message-ID: https://github.com/python/cpython/commit/ed5e8e06cbf766e89d6c58a882ee024abb5b2ed7 commit: ed5e8e06cbf766e89d6c58a882ee024abb5b2ed7 branch: master author: David H committer: Serhiy Storchaka date: 2019-08-01T01:49:55+03:00 summary: bpo-37730: Fix usage of NotImplemented instead of NotImplementedError in docs. (GH-15062) files: M Doc/library/winreg.rst M PC/clinic/winreg.c.h M PC/winreg.c diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index e9c026102273..cb67f2f39d76 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -456,7 +456,7 @@ This module offers the following functions: *key* is an already open key, or one of the predefined :ref:`HKEY_* constants `. - Will generally raise :exc:`NotImplemented` if executed on a 32-bit operating + Will generally raise :exc:`NotImplementedError` if executed on a 32-bit operating system. If the key is not on the reflection list, the function succeeds but has no @@ -471,7 +471,7 @@ This module offers the following functions: *key* is an already open key, or one of the predefined :ref:`HKEY_* constants `. - Will generally raise :exc:`NotImplemented` if executed on a 32-bit operating + Will generally raise :exc:`NotImplementedError` if executed on a 32-bit operating system. Restoring reflection for a key does not affect reflection of any subkeys. @@ -486,7 +486,7 @@ This module offers the following functions: Returns ``True`` if reflection is disabled. - Will generally raise :exc:`NotImplemented` if executed on a 32-bit + Will generally raise :exc:`NotImplementedError` if executed on a 32-bit operating system. diff --git a/PC/clinic/winreg.c.h b/PC/clinic/winreg.c.h index 50210250ed19..b7af1855ac54 100644 --- a/PC/clinic/winreg.c.h +++ b/PC/clinic/winreg.c.h @@ -1029,7 +1029,7 @@ PyDoc_STRVAR(winreg_DisableReflectionKey__doc__, " key\n" " An already open key, or any one of the predefined HKEY_* constants.\n" "\n" -"Will generally raise NotImplemented if executed on a 32bit OS.\n" +"Will generally raise NotImplementedError if executed on a 32bit OS.\n" "\n" "If the key is not on the reflection list, the function succeeds but has\n" "no effect. Disabling reflection for a key does not affect reflection\n" @@ -1065,7 +1065,7 @@ PyDoc_STRVAR(winreg_EnableReflectionKey__doc__, " key\n" " An already open key, or any one of the predefined HKEY_* constants.\n" "\n" -"Will generally raise NotImplemented if executed on a 32bit OS.\n" +"Will generally raise NotImplementedError if executed on a 32bit OS.\n" "Restoring reflection for a key does not affect reflection of any\n" "subkeys."); @@ -1099,7 +1099,7 @@ PyDoc_STRVAR(winreg_QueryReflectionKey__doc__, " key\n" " An already open key, or any one of the predefined HKEY_* constants.\n" "\n" -"Will generally raise NotImplemented if executed on a 32bit OS."); +"Will generally raise NotImplementedError if executed on a 32bit OS."); #define WINREG_QUERYREFLECTIONKEY_METHODDEF \ {"QueryReflectionKey", (PyCFunction)winreg_QueryReflectionKey, METH_O, winreg_QueryReflectionKey__doc__}, @@ -1121,4 +1121,4 @@ winreg_QueryReflectionKey(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=1204d20c543b5b4a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=015afbbd690eb59d input=a9049054013a1b77]*/ diff --git a/PC/winreg.c b/PC/winreg.c index 5f5fc85d2250..d0df7ef0ad47 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -1702,7 +1702,7 @@ winreg.DisableReflectionKey Disables registry reflection for 32bit processes running on a 64bit OS. -Will generally raise NotImplemented if executed on a 32bit OS. +Will generally raise NotImplementedError if executed on a 32bit OS. If the key is not on the reflection list, the function succeeds but has no effect. Disabling reflection for a key does not affect reflection @@ -1711,7 +1711,7 @@ of any subkeys. static PyObject * winreg_DisableReflectionKey_impl(PyObject *module, HKEY key) -/*[clinic end generated code: output=830cce504cc764b4 input=a6c9e5ca5410193c]*/ +/*[clinic end generated code: output=830cce504cc764b4 input=70bece2dee02e073]*/ { HMODULE hMod; typedef LONG (WINAPI *RDRKFunc)(HKEY); @@ -1749,14 +1749,14 @@ winreg.EnableReflectionKey Restores registry reflection for the specified disabled key. -Will generally raise NotImplemented if executed on a 32bit OS. +Will generally raise NotImplementedError if executed on a 32bit OS. Restoring reflection for a key does not affect reflection of any subkeys. [clinic start generated code]*/ static PyObject * winreg_EnableReflectionKey_impl(PyObject *module, HKEY key) -/*[clinic end generated code: output=86fa1385fdd9ce57 input=7748abbacd1e166a]*/ +/*[clinic end generated code: output=86fa1385fdd9ce57 input=eeae770c6eb9f559]*/ { HMODULE hMod; typedef LONG (WINAPI *RERKFunc)(HKEY); @@ -1794,12 +1794,12 @@ winreg.QueryReflectionKey Returns the reflection state for the specified key as a bool. -Will generally raise NotImplemented if executed on a 32bit OS. +Will generally raise NotImplementedError if executed on a 32bit OS. [clinic start generated code]*/ static PyObject * winreg_QueryReflectionKey_impl(PyObject *module, HKEY key) -/*[clinic end generated code: output=4e774af288c3ebb9 input=9f325eacb5a65d88]*/ +/*[clinic end generated code: output=4e774af288c3ebb9 input=a98fa51d55ade186]*/ { HMODULE hMod; typedef LONG (WINAPI *RQRKFunc)(HKEY, BOOL *); From webhook-mailer at python.org Tue Jul 23 06:39:56 2019 From: webhook-mailer at python.org (=?utf-8?q?=C5=81ukasz?= Langa) Date: Tue, 23 Jul 2019 10:39:56 -0000 Subject: [Python-checkins] (no subject) Message-ID: To: python-checkins at python.org Subject: [3.8] bpo-36974: separate vectorcall functions for each calling convention (GH-13781) (#14782) Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 https://github.com/python/cpython/commit/bf8e82f976b37856c7d35cdf88a238cb6f57= fe65 commit: bf8e82f976b37856c7d35cdf88a238cb6f57fe65 branch: 3.8 author: Jeroen Demeyer committer: =C5=81ukasz Langa date: 2019-07-23T12:39:51+02:00 summary: [3.8] bpo-36974: separate vectorcall functions for each calling convention (G= H-13781) (#14782) files: A Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst M Include/descrobject.h M Include/methodobject.h M Lib/test/test_call.py M Lib/test/test_gdb.py M Objects/call.c M Objects/descrobject.c M Objects/methodobject.c M Python/ceval.c M Tools/gdb/libpython.py diff --git a/Include/descrobject.h b/Include/descrobject.h index d7114852c1e2..ead269d1d2f7 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -91,9 +91,6 @@ PyAPI_FUNC(PyObject *) PyDescr_NewMember(PyTypeObject *, PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *, struct PyGetSetDef *); #ifndef Py_LIMITED_API - -PyAPI_FUNC(PyObject *) _PyMethodDescr_Vectorcall( - PyObject *descrobj, PyObject *const *args, size_t nargsf, PyObject *= kwnames); PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *, struct wrapperbase *, void *= ); #define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set !=3D NULL) diff --git a/Include/methodobject.h b/Include/methodobject.h index e92adde7bf6b..ba3b8878a6c4 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -46,11 +46,6 @@ PyAPI_FUNC(PyObject *) _PyCFunction_FastCallDict(PyObject = *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs); - -PyAPI_FUNC(PyObject *) _PyCFunction_Vectorcall(PyObject *func, - PyObject *const *stack, - size_t nargsf, - PyObject *kwnames); #endif =20 struct PyMethodDef { diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index b252ca1076ad..0bff7ded4670 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -586,6 +586,8 @@ def __call__(self, *args): return super().__call__(*args) =20 calls +=3D [ + (dict.update, ({},), {"key":True}, None), + ({}.update, ({},), {"key":True}, None), (MethodDescriptorHeap(), (0,), {}, True), (MethodDescriptorOverridden(), (0,), {}, 'new'), (MethodDescriptorSuper(), (0,), {}, True), diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 1c5e18b678ca..e07d3273a455 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -850,10 +850,10 @@ def test_pycfunction(self): # called, so test a variety of calling conventions. for py_name, py_args, c_name, expected_frame_number in ( ('gmtime', '', 'time_gmtime', 1), # METH_VARARGS - ('len', '[]', 'builtin_len', 2), # METH_O - ('locals', '', 'builtin_locals', 2), # METH_NOARGS - ('iter', '[]', 'builtin_iter', 2), # METH_FASTCALL - ('sorted', '[]', 'builtin_sorted', 2), # METH_FASTCALL|METH_KEY= WORDS + ('len', '[]', 'builtin_len', 1), # METH_O + ('locals', '', 'builtin_locals', 1), # METH_NOARGS + ('iter', '[]', 'builtin_iter', 1), # METH_FASTCALL + ('sorted', '[]', 'builtin_sorted', 1), # METH_FASTCALL|METH_KEY= WORDS ): with self.subTest(c_name): cmd =3D ('from time import gmtime\n' # (not always needed) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974= .bVYmSA.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-3697= 4.bVYmSA.rst new file mode 100644 index 000000000000..6080ef361814 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA= .rst=09 @@ -0,0 +1,2 @@ +Implemented separate vectorcall functions for every calling convention of +builtin functions and methods. This improves performance for calls. diff --git a/Objects/call.c b/Objects/call.c index 1f41f5cb6bbf..c66389854d8b 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -206,7 +206,7 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, Py= Object *kwargs) Py_DECREF(kwnames); } =20 - return result; + return _Py_CheckFunctionResult(callable, result, NULL); } =20 =20 @@ -723,25 +723,6 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, Py= Object *self, } =20 =20 -PyObject * -_PyCFunction_Vectorcall(PyObject *func, - PyObject *const *args, size_t nargsf, - PyObject *kwnames) -{ - PyObject *result; - - assert(func !=3D NULL); - assert(PyCFunction_Check(func)); - Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); - - result =3D _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->= m_ml, - PyCFunction_GET_SELF(func), - args, nargs, kwnames); - result =3D _Py_CheckFunctionResult(func, result, NULL); - return result; -} - - static PyObject * cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs) { diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 6c95a8726c42..dbb44de846fd 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -226,80 +226,199 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, = PyObject *value) return -1; } =20 -static PyObject * -methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwarg= s) -{ - Py_ssize_t nargs; - PyObject *self, *result; =20 - /* Make sure that the first argument is acceptable as 'self' */ - assert(PyTuple_Check(args)); - nargs =3D PyTuple_GET_SIZE(args); +/* Vectorcall functions for each of the PyMethodDescr calling conventions. + * + * First, common helpers + */ +static const char * +get_name(PyObject *func) { + assert(PyObject_TypeCheck(func, &PyMethodDescr_Type)); + return ((PyMethodDescrObject *)func)->d_method->ml_name; +} + +typedef void (*funcptr)(void); + +static inline int +method_check_args(PyObject *func, PyObject *const *args, Py_ssize_t nargs, P= yObject *kwnames) +{ + assert(!PyErr_Occurred()); + assert(PyObject_TypeCheck(func, &PyMethodDescr_Type)); if (nargs < 1) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' of '%.100s' " + "descriptor '%.200s' of '%.100s' " "object needs an argument", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name); - return NULL; + get_name(func), PyDescr_TYPE(func)->tp_name); + return -1; } - self =3D PyTuple_GET_ITEM(args, 0); + PyObject *self =3D args[0]; if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), - (PyObject *)PyDescr_TYPE(descr))) { + (PyObject *)PyDescr_TYPE(func))) + { PyErr_Format(PyExc_TypeError, - "descriptor '%V' for '%.100s' objects " + "descriptor '%.200s' for '%.100s' objects " "doesn't apply to a '%.100s' object", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name, - self->ob_type->tp_name); + get_name(func), PyDescr_TYPE(func)->tp_name, + Py_TYPE(self)->tp_name); + return -1; + } + if (kwnames && PyTuple_GET_SIZE(kwnames)) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", get_name(func)); + return -1; + } + return 0; +} + +static inline funcptr +method_enter_call(PyObject *func) +{ + if (Py_EnterRecursiveCall(" while calling a Python object")) { return NULL; } + return (funcptr)((PyMethodDescrObject *)func)->d_method->ml_meth; +} =20 - result =3D _PyMethodDef_RawFastCallDict(descr->d_method, self, - &_PyTuple_ITEMS(args)[1], nargs - = 1, - kwargs); - result =3D _Py_CheckFunctionResult((PyObject *)descr, result, NULL); +/* Now the actual vectorcall functions */ +static PyObject * +method_vectorcall_VARARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, kwnames)) { + return NULL; + } + PyObject *argstuple =3D _PyTuple_FromArray(args+1, nargs-1); + if (argstuple =3D=3D NULL) { + return NULL; + } + PyCFunction meth =3D (PyCFunction)method_enter_call(func); + if (meth =3D=3D NULL) { + Py_DECREF(argstuple); + return NULL; + } + PyObject *result =3D meth(args[0], argstuple); + Py_DECREF(argstuple); + Py_LeaveRecursiveCall(); return result; } =20 -// same to methoddescr_call(), but use FASTCALL convention. -PyObject * -_PyMethodDescr_Vectorcall(PyObject *descrobj, - PyObject *const *args, size_t nargsf, - PyObject *kwnames) +static PyObject * +method_vectorcall_VARARGS_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - assert(Py_TYPE(descrobj) =3D=3D &PyMethodDescr_Type); - PyMethodDescrObject *descr =3D (PyMethodDescrObject *)descrobj; - PyObject *self, *result; + Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, NULL)) { + return NULL; + } + PyObject *argstuple =3D _PyTuple_FromArray(args+1, nargs-1); + if (argstuple =3D=3D NULL) { + return NULL; + } + PyObject *result =3D NULL; + /* Create a temporary dict for keyword arguments */ + PyObject *kwdict =3D NULL; + if (kwnames !=3D NULL && PyTuple_GET_SIZE(kwnames) > 0) { + kwdict =3D _PyStack_AsDict(args + nargs, kwnames); + if (kwdict =3D=3D NULL) { + goto exit; + } + } + PyCFunctionWithKeywords meth =3D (PyCFunctionWithKeywords) + method_enter_call(func); + if (meth =3D=3D NULL) { + goto exit; + } + result =3D meth(args[0], argstuple, kwdict); + Py_LeaveRecursiveCall(); +exit: + Py_DECREF(argstuple); + Py_XDECREF(kwdict); + return result; +} =20 +static PyObject * +method_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); - /* Make sure that the first argument is acceptable as 'self' */ - if (nargs < 1) { - PyErr_Format(PyExc_TypeError, - "descriptor '%V' of '%.100s' " - "object needs an argument", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name); + if (method_check_args(func, args, nargs, kwnames)) { return NULL; } - self =3D args[0]; - if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), - (PyObject *)PyDescr_TYPE(descr))) { + _PyCFunctionFast meth =3D (_PyCFunctionFast) + method_enter_call(func); + if (meth =3D=3D NULL) { + return NULL; + } + PyObject *result =3D meth(args[0], args+1, nargs-1); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +method_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, NULL)) { + return NULL; + } + _PyCFunctionFastWithKeywords meth =3D (_PyCFunctionFastWithKeywords) + method_enter_call(func); + if (meth =3D=3D NULL) { + return NULL; + } + PyObject *result =3D meth(args[0], args+1, nargs-1, kwnames); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +method_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, kwnames)) { + return NULL; + } + if (nargs !=3D 1) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' for '%.100s' objects " - "doesn't apply to a '%.100s' object", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name, - self->ob_type->tp_name); + "%.200s() takes no arguments (%zd given)", get_name(func), nargs= -1); return NULL; } + PyCFunction meth =3D (PyCFunction)method_enter_call(func); + if (meth =3D=3D NULL) { + return NULL; + } + PyObject *result =3D meth(args[0], NULL); + Py_LeaveRecursiveCall(); + return result; +} =20 - result =3D _PyMethodDef_RawFastCallKeywords(descr->d_method, self, - args+1, nargs-1, kwnames); - result =3D _Py_CheckFunctionResult((PyObject *)descr, result, NULL); +static PyObject * +method_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, kwnames)) { + return NULL; + } + if (nargs !=3D 2) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + get_name(func), nargs-1); + return NULL; + } + PyCFunction meth =3D (PyCFunction)method_enter_call(func); + if (meth =3D=3D NULL) { + return NULL; + } + PyObject *result =3D meth(args[0], args[1]); + Py_LeaveRecursiveCall(); return result; } =20 + static PyObject * classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) @@ -552,7 +671,7 @@ PyTypeObject PyMethodDescr_Type =3D { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)methoddescr_call, /* tp_call */ + PyVectorcall_Call, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ @@ -750,13 +869,40 @@ descr_new(PyTypeObject *descrtype, PyTypeObject *type, = const char *name) PyObject * PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method) { + /* Figure out correct vectorcall function to use */ + vectorcallfunc vectorcall; + switch (method->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS |= METH_O | METH_KEYWORDS)) + { + case METH_VARARGS: + vectorcall =3D method_vectorcall_VARARGS; + break; + case METH_VARARGS | METH_KEYWORDS: + vectorcall =3D method_vectorcall_VARARGS_KEYWORDS; + break; + case METH_FASTCALL: + vectorcall =3D method_vectorcall_FASTCALL; + break; + case METH_FASTCALL | METH_KEYWORDS: + vectorcall =3D method_vectorcall_FASTCALL_KEYWORDS; + break; + case METH_NOARGS: + vectorcall =3D method_vectorcall_NOARGS; + break; + case METH_O: + vectorcall =3D method_vectorcall_O; + break; + default: + PyErr_SetString(PyExc_SystemError, "bad call flags"); + return NULL; + } + PyMethodDescrObject *descr; =20 descr =3D (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type, type, method->ml_name); if (descr !=3D NULL) { descr->d_method =3D method; - descr->vectorcall =3D _PyMethodDescr_Vectorcall; + descr->vectorcall =3D vectorcall; } return (PyObject *)descr; } diff --git a/Objects/methodobject.c b/Objects/methodobject.c index c3bc0184796e..3494f11d80fe 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -19,6 +19,17 @@ static int numfree =3D 0; /* undefine macro trampoline to PyCFunction_NewEx */ #undef PyCFunction_New =20 +/* Forward declarations */ +static PyObject * cfunction_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); + + PyObject * PyCFunction_New(PyMethodDef *ml, PyObject *self) { @@ -28,6 +39,33 @@ PyCFunction_New(PyMethodDef *ml, PyObject *self) PyObject * PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) { + /* Figure out correct vectorcall function to use */ + vectorcallfunc vectorcall; + switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | MET= H_O | METH_KEYWORDS)) + { + case METH_VARARGS: + case METH_VARARGS | METH_KEYWORDS: + /* For METH_VARARGS functions, it's more efficient to use tp_call + * instead of vectorcall. */ + vectorcall =3D NULL; + break; + case METH_FASTCALL: + vectorcall =3D cfunction_vectorcall_FASTCALL; + break; + case METH_FASTCALL | METH_KEYWORDS: + vectorcall =3D cfunction_vectorcall_FASTCALL_KEYWORDS; + break; + case METH_NOARGS: + vectorcall =3D cfunction_vectorcall_NOARGS; + break; + case METH_O: + vectorcall =3D cfunction_vectorcall_O; + break; + default: + PyErr_SetString(PyExc_SystemError, "bad call flags"); + return NULL; + } + PyCFunctionObject *op; op =3D free_list; if (op !=3D NULL) { @@ -46,14 +84,7 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObjec= t *module) op->m_self =3D self; Py_XINCREF(module); op->m_module =3D module; - if (ml->ml_flags & METH_VARARGS) { - /* For METH_VARARGS functions, it's more efficient to use tp_call - * instead of vectorcall. */ - op->vectorcall =3D NULL; - } - else { - op->vectorcall =3D _PyCFunction_Vectorcall; - } + op->vectorcall =3D vectorcall; _PyObject_GC_TRACK(op); return (PyObject *)op; } @@ -333,3 +364,121 @@ _PyCFunction_DebugMallocStats(FILE *out) "free PyCFunctionObject", numfree, sizeof(PyCFunctionObject)); } + + +/* Vectorcall functions for each of the PyCFunction calling conventions, + * except for METH_VARARGS (possibly combined with METH_KEYWORDS) which + * doesn't use vectorcall. + * + * First, common helpers + */ +static const char * +get_name(PyObject *func) +{ + assert(PyCFunction_Check(func)); + PyMethodDef *method =3D ((PyCFunctionObject *)func)->m_ml; + return method->ml_name; +} + +typedef void (*funcptr)(void); + +static inline int +cfunction_check_kwargs(PyObject *func, PyObject *kwnames) +{ + assert(!PyErr_Occurred()); + assert(PyCFunction_Check(func)); + if (kwnames && PyTuple_GET_SIZE(kwnames)) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", get_name(func)); + return -1; + } + return 0; +} + +static inline funcptr +cfunction_enter_call(PyObject *func) +{ + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } + return (funcptr)PyCFunction_GET_FUNCTION(func); +} + +/* Now the actual vectorcall functions */ +static PyObject * +cfunction_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (cfunction_check_kwargs(func, kwnames)) { + return NULL; + } + Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); + _PyCFunctionFast meth =3D (_PyCFunctionFast) + cfunction_enter_call(func); + if (meth =3D=3D NULL) { + return NULL; + } + PyObject *result =3D meth(PyCFunction_GET_SELF(func), args, nargs); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +cfunction_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); + _PyCFunctionFastWithKeywords meth =3D (_PyCFunctionFastWithKeywords) + cfunction_enter_call(func); + if (meth =3D=3D NULL) { + return NULL; + } + PyObject *result =3D meth(PyCFunction_GET_SELF(func), args, nargs, kwnam= es); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +cfunction_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (cfunction_check_kwargs(func, kwnames)) { + return NULL; + } + Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); + if (nargs !=3D 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", get_name(func), nargs= ); + return NULL; + } + PyCFunction meth =3D (PyCFunction)cfunction_enter_call(func); + if (meth =3D=3D NULL) { + return NULL; + } + PyObject *result =3D meth(PyCFunction_GET_SELF(func), NULL); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +cfunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (cfunction_check_kwargs(func, kwnames)) { + return NULL; + } + Py_ssize_t nargs =3D PyVectorcall_NARGS(nargsf); + if (nargs !=3D 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + get_name(func), nargs); + return NULL; + } + PyCFunction meth =3D (PyCFunction)cfunction_enter_call(func); + if (meth =3D=3D NULL) { + return NULL; + } + PyObject *result =3D meth(PyCFunction_GET_SELF(func), args[0]); + Py_LeaveRecursiveCall(); + return result; +} diff --git a/Python/ceval.c b/Python/ceval.c index eddcc8d8b4d4..546a4264d8ad 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4943,7 +4943,7 @@ trace_call_function(PyThreadState *tstate, { PyObject *x; if (PyCFunction_Check(func)) { - C_TRACE(x, _PyCFunction_Vectorcall(func, args, nargs, kwnames)); + C_TRACE(x, _PyObject_Vectorcall(func, args, nargs, kwnames)); return x; } else if (Py_TYPE(func) =3D=3D &PyMethodDescr_Type && nargs > 0) { @@ -4959,9 +4959,9 @@ trace_call_function(PyThreadState *tstate, if (func =3D=3D NULL) { return NULL; } - C_TRACE(x, _PyCFunction_Vectorcall(func, - args+1, nargs-1, - kwnames)); + C_TRACE(x, _PyObject_Vectorcall(func, + args+1, nargs-1, + kwnames)); Py_DECREF(func); return x; } @@ -5023,10 +5023,10 @@ do_call_core(PyThreadState *tstate, PyObject *func, P= yObject *callargs, PyObject return NULL; } =20 - C_TRACE(result, _PyCFunction_FastCallDict(func, - &_PyTuple_ITEMS(callar= gs)[1], - nargs - 1, - kwdict)); + C_TRACE(result, _PyObject_FastCallDict(func, + &_PyTuple_ITEMS(callargs)= [1], + nargs - 1, + kwdict)); Py_DECREF(func); return result; } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 93f720ab7e2a..e40f79b5c3c7 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1563,9 +1563,8 @@ def is_other_python_frame(self): if not caller: return False =20 - if caller in ('_PyCFunction_FastCallDict', - '_PyCFunction_Vectorcall', - 'cfunction_call_varargs'): + if (caller.startswith('cfunction_vectorcall_') or + caller =3D=3D 'cfunction_call_varargs'): arg_name =3D 'func' # Within that frame: # "func" is the local containing the PyObject* of the From webhook-mailer at python.org Mon Jul 29 16:54:23 2019 From: webhook-mailer at python.org (=?utf-8?q?=C5=81ukasz?= Langa) Date: Mon, 29 Jul 2019 20:54:23 -0000 Subject: [Python-checkins] (no subject) Message-ID: To: python-checkins at python.org Subject: Python 3.8.0b3 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 https://github.com/python/cpython/commit/4336222407f4aab5944b8c90a08d9cf644db= 7aa2 commit: 4336222407f4aab5944b8c90a08d9cf644db7aa2 branch: 3.8 author: =C5=81ukasz Langa committer: =C5=81ukasz Langa date: 2019-07-29T15:26:01+02:00 summary: Python 3.8.0b3 files: A Misc/NEWS.d/3.8.0b3.rst D Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst D Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst D Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH.rst D Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst D Misc/NEWS.d/next/Documentation/2019-07-06-02-19-02.bpo-37149.NumHn3.rst D Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst D Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst D Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst D Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst D Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst D Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst D Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst D Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst D Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst D Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst D Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst D Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst D Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst D Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst D Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst D Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst D Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst D Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst D Misc/NEWS.d/next/Library/2019-07-02-13-08-30.bpo-37481.hd5k09.rst D Misc/NEWS.d/next/Library/2019-07-03-12-47-52.bpo-37421.gR5hC8.rst D Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst D Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst D Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst D Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst D Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst D Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst D Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst D Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst D Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst D Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst D Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst D Misc/NEWS.d/next/Tests/2019-07-05-14-47-55.bpo-37421.n8o2to.rst D Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst D Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst D Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst D Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst D Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst M Include/patchlevel.h M Lib/pydoc_data/topics.py M README.rst diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 00870f9ba29e..60fb85ee84d1 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 8 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_BETA -#define PY_RELEASE_SERIAL 2 +#define PY_RELEASE_SERIAL 3 =20 /* Version as a string */ -#define PY_VERSION "3.8.0b2+" +#define PY_VERSION "3.8.0b3" /*--end constants--*/ =20 /* Version as a single 4-byte hex number, e.g. 0x010502B2 =3D=3D 1.5.2b2. diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 636219108342..c3049b95207c 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Thu Jul 4 12:44:09 2019 +# Autogenerated by Sphinx on Mon Jul 29 15:22:27 2019 topics =3D {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -5099,7 +5099,7 @@ 'Meaning = ' '|\n' ' ' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D|\n' ' | "\'<\'" | Forces the field to be left-aligned ' 'within the available |\n' ' | | space (this is the default for most ' @@ -5148,7 +5148,7 @@ 'Meaning = ' '|\n' ' ' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D|\n' ' | "\'+\'" | indicates that a sign should be used f= or ' 'both positive as |\n' ' | | well as negative ' @@ -5252,7 +5252,7 @@ 'Meaning = ' '|\n' ' ' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D|\n' ' | "\'s\'" | String format. This is the default typ= e ' 'for strings and |\n' ' | | may be ' @@ -5272,7 +5272,7 @@ 'Meaning = ' '|\n' ' ' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D|\n' ' | "\'b\'" | Binary format. Outputs the number in ' 'base 2. |\n' ' ' @@ -5334,7 +5334,7 @@ 'Meaning = ' '|\n' ' ' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D|\n' ' | "\'e\'" | Exponent notation. Prints the number i= n ' 'scientific |\n' ' | | notation using the letter =E2=80=98e=E2= =80=99 to indicate ' @@ -7045,7 +7045,7 @@ '+-------------------------------------------------+---= ------------------------------------+\n' '| Operator | ' 'Description |\n' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|\n' '| "lambda" | ' 'Lambda expression |\n' '+-------------------------------------------------+---= ------------------------------------+\n' @@ -10289,7 +10289,7 @@ ' | Representation | ' 'Description |\n' ' ' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|\n' ' | "\\n" | Line ' 'Feed |\n' ' ' @@ -10628,7 +10628,7 @@ '+-------------------+-----------------------------------+------= ---+\n' '| Escape Sequence | Meaning | Notes= ' '|\n' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D=3D|\n' '| "\\newline" | Backslash and newline ignored ' '| |\n' '+-------------------+-----------------------------------+------= ---+\n' @@ -10674,7 +10674,7 @@ '+-------------------+-----------------------------------+------= ---+\n' '| Escape Sequence | Meaning | Notes= ' '|\n' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D=3D|\n' '| "\\N{name}" | Character named *name* in the | ' '(4) |\n' '| | Unicode database | = ' @@ -11312,7 +11312,7 @@ ' | Attribute | Meaning = ' '| |\n' ' ' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D|\n' ' | "__doc__" | The function=E2=80=99s docume= ntation ' '| Writable |\n' ' | | string, or "None" if = ' @@ -12586,7 +12586,7 @@ '+----------------------------+--------------------------------= --+------------+\n' '| Operation | Result = ' '| Notes |\n' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D|\n' '| "x in s" | "True" if an item of *s* is = ' '| (1) |\n' '| | equal to *x*, else "False" = ' @@ -12815,7 +12815,7 @@ '+--------------------------------+----------------------------= ------+-----------------------+\n' '| Operation | ' 'Result | Notes |\n' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|\n' '| "s[i] =3D x" | item *i* of *s* is replac= ed ' 'by | |\n' '| | ' @@ -13277,7 +13277,7 @@ '| Operation | ' 'Result | Notes = ' '|\n' - '+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+\n' + '|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D|\n' '| "s[i] =3D x" | item *i* of *s* i= s ' 'replaced by | |\n' '| | ' diff --git a/Misc/NEWS.d/3.8.0b3.rst b/Misc/NEWS.d/3.8.0b3.rst new file mode 100644 index 000000000000..5a35160970ad --- /dev/null +++ b/Misc/NEWS.d/3.8.0b3.rst @@ -0,0 +1,437 @@ +.. bpo: 37461 +.. date: 2019-07-16-08-11-00 +.. nonce: 1Ahz7O +.. release date: 2019-07-29 +.. section: Security + +Fix an inifite loop when parsing specially crafted email headers. Patch by +Abhilash Raj. + +.. + +.. bpo: 37593 +.. date: 2019-07-14-23-57-27 +.. nonce: yHSTwH +.. section: Core and Builtins + +Swap the positions of the *posonlyargs* and *args* parameters in the +constructor of :class:`ast.parameters` nodes. + +.. + +.. bpo: 36974 +.. date: 2019-06-11-12-59-38 +.. nonce: bVYmSA +.. section: Core and Builtins + +Implemented separate vectorcall functions for every calling convention of +builtin functions and methods. This improves performance for calls. + +.. + +.. bpo: 37697 +.. date: 2019-07-28-17-44-21 +.. nonce: 7UV5d0 +.. section: Library + +Syncronize ``importlib.metadata`` with `importlib_metadata 0.19 +`_, +improving handling of EGG-INFO files and fixing a crash when entry point +names contained colons. + +.. + +.. bpo: 37691 +.. date: 2019-07-26-22-30-01 +.. nonce: 1Li3rx +.. section: Library + +Let math.dist() accept coordinates as sequences (or iterables) rather than +just tuples. + +.. + +.. bpo: 37664 +.. date: 2019-07-24-18-27-44 +.. nonce: o-GYZC +.. section: Library + +Update wheels bundled with ensurepip (pip 19.2.1 and setuptools 41.0.1) + +.. + +.. bpo: 36324 +.. date: 2019-07-19-22-44-41 +.. nonce: 1VjywS +.. section: Library + +Make internal attributes for statistics.NormalDist() private. + +.. + +.. bpo: 37491 +.. date: 2019-07-17-06-54-43 +.. nonce: op0aMs +.. section: Library + +Fix ``IndexError`` when parsing email headers with unexpectedly ending +bare-quoted string value. Patch by Abhilash Raj. + +.. + +.. bpo: 37579 +.. date: 2019-07-13-10-59-43 +.. nonce: B1Tq9i +.. section: Library + +Return :exc:`NotImplemented` in Python implementation of ``__eq__`` for +:class:`~datetime.timedelta` and :class:`~datetime.time` when the other +object being compared is not of the same type to match C implementation. +Patch by Karthikeyan Singaravelan. + +.. + +.. bpo: 21478 +.. date: 2019-07-10-23-07-11 +.. nonce: cCw9rF +.. section: Library + +Record calls to parent when autospecced object is attached to a mock using +:func:`unittest.mock.attach_mock`. Patch by Karthikeyan Singaravelan. + +.. + +.. bpo: 37502 +.. date: 2019-07-08-03-15-04 +.. nonce: qZGC4g +.. section: Library + +pickle.loads() no longer raises TypeError when the buffers argument is set +to None + +.. + +.. bpo: 37520 +.. date: 2019-07-07-21-09-08 +.. nonce: Gg0KD6 +.. section: Library + +Correct behavior for zipfile.Path.parent when the path object identifies a +subdirectory. + +.. + +.. bpo: 18374 +.. date: 2019-07-05-21-46-45 +.. nonce: qgE0H3 +.. section: Library + +Fix the ``.col_offset`` attribute of nested :class:`ast.BinOp` instances +which had a too large value in some situations. + +.. + +.. bpo: 37421 +.. date: 2019-07-03-12-47-52 +.. nonce: gR5hC8 +.. section: Library + +Fix :func:`multiprocessing.util.get_temp_dir` finalizer: clear also the +'tempdir' configuration of the current process, so next call to +``get_temp_dir()`` will create a new temporary directory, rather than +reusing the removed temporary directory. + +.. + +.. bpo: 37481 +.. date: 2019-07-02-13-08-30 +.. nonce: hd5k09 +.. section: Library + +The distutils ``bdist_wininst`` command is deprecated in Python 3.8, use +``bdist_wheel`` (wheel packages) instead. + +.. + +.. bpo: 26967 +.. date: 2019-06-23-12-46-10 +.. nonce: xEuem1 +.. section: Library + +An :class:`~argparse.ArgumentParser` with ``allow_abbrev=3DFalse`` no longer +disables grouping of short flags, such as ``-vv``, but only disables +abbreviation of long flags as documented. Patch by Zac Hatfield-Dodds. + +.. + +.. bpo: 37347 +.. date: 2019-06-20-14-23-48 +.. nonce: Gf9yYI +.. section: Library + +:meth:`sqlite3.Connection.create_aggregate`, +:meth:`sqlite3.Connection.create_function`, +:meth:`sqlite3.Connection.set_authorizer`, +:meth:`sqlite3.Connection.set_progress_handler` +:meth:`sqlite3.Connection.set_trace_callback` methods lead to segfaults if +some of these methods are called twice with an equal object but not the +same. Now callbacks are stored more carefully. Patch by Aleksandr Balezin. + +.. + +.. bpo: 36564 +.. date: 2019-04-08-13-00-13 +.. nonce: _n67m_ +.. section: Library + +Fix infinite loop in email header folding logic that would be triggered when +an email policy's max_line_length is not long enough to include the required +markup and any values in the message. Patch by Paul Ganssle + +.. + +.. bpo: 32910 +.. date: 2019-07-25-10-30-32 +.. nonce: caLLAe +.. section: Documentation + +Remove implementation-specific behaviour of how venv's Deactivate works. + +.. + +.. bpo: 37284 +.. date: 2019-07-13-12-58-20 +.. nonce: rP8WpB +.. section: Documentation + +Add a brief note to indicate that any new ``sys.implementation`` required +attributes must go through the PEP process. + +.. + +.. bpo: 30088 +.. date: 2019-07-13-12-43-01 +.. nonce: CIcBjy +.. section: Documentation + +Documented that :class:`mailbox.Maildir` constructor doesn't attempt to +verify the maildir folder layout correctness. Patch by Sviatoslav Sydorenko. + +.. + +.. bpo: 37521 +.. date: 2019-07-12-15-09-56 +.. nonce: 7tiFR- +.. section: Documentation + +Fix `importlib` examples to insert any newly created modules via +importlib.util.module_from_spec() immediately into sys.modules instead of +after calling loader.exec_module(). + +Thanks to Benjamin Mintz for finding the bug. + +.. + +.. bpo: 37456 +.. date: 2019-07-06-17-51-36 +.. nonce: lgAQHn +.. section: Documentation + +Slash ('/') is now part of syntax. + +.. + +.. bpo: 37487 +.. date: 2019-07-06-17-19-26 +.. nonce: QagfZ5 +.. section: Documentation + +Fix PyList_GetItem index description to include 0. + +.. + +.. bpo: 37149 +.. date: 2019-07-06-02-19-02 +.. nonce: NumHn3 +.. section: Documentation + +Replace the dead link to the Tkinter 8.5 reference by John Shipman, New +Mexico Tech, with a link to the archive.org copy. + +.. + +.. bpo: 37478 +.. date: 2019-07-06-00-57-27 +.. nonce: B0ioLw +.. section: Documentation + +Added possible exceptions to the description of os.chdir(). + +.. + +.. bpo: 37558 +.. date: 2019-07-11-10-33-56 +.. nonce: SKHRsL +.. section: Tests + +Fix test_shared_memory_cleaned_after_process_termination name handling + +.. + +.. bpo: 37526 +.. date: 2019-07-09-12-33-18 +.. nonce: vmm5y7 +.. section: Tests + +Add :func:`test.support.catch_threading_exception`: context manager catching +:class:`threading.Thread` exception using :func:`threading.excepthook`. + +.. + +.. bpo: 37421 +.. date: 2019-07-08-10-11-36 +.. nonce: OY77go +.. section: Tests + +test_concurrent_futures now explicitly stops the ForkServer instance if it's +running. + +.. + +.. bpo: 37421 +.. date: 2019-07-05-14-47-55 +.. nonce: n8o2to +.. section: Tests + +multiprocessing tests now stop the ForkServer instance if it's running: +close the "alive" file descriptor to ask the server to stop and then remove +its UNIX address. + +.. + +.. bpo: 36044 +.. date: 2019-07-11-01-28-24 +.. nonce: gIgfiJ +.. section: Build + +Reduce the number of unit tests run for the PGO generation task. This +speeds up the task by a factor of about 15x. Running the full unit test +suite is slow. This change may result in a slightly less optimized build +since not as many code branches will be executed. If you are willing to +wait for the much slower build, the old behavior can be restored using +'./configure [..] PROFILE_TASK=3D"-m test --pgo-extended"'. We make no +guarantees as to which PGO task set produces a faster build. Users who care +should run their own relevant benchmarks as results can depend on the +environment, workload, and compiler tool chain. + +.. + +.. bpo: 37672 +.. date: 2019-07-24-14-36-28 +.. nonce: uKEVHN +.. section: Windows + +Switch Windows Store package's pip to use bundled :file:`pip.ini` instead of +:envvar:`PIP_USER` variable. + +.. + +.. bpo: 37692 +.. date: 2019-07-27-15-14-20 +.. nonce: TRHGjD +.. section: IDLE + +Improve highlight config sample with example shell interaction and better +labels for shell elements. + +.. + +.. bpo: 37628 +.. date: 2019-07-26-17-51-13 +.. nonce: kX4AUF +.. section: IDLE + +Settings dialog no longer expands with font size. + +.. + +.. bpo: 37627 +.. date: 2019-07-20-23-33-53 +.. nonce: dQhUNB +.. section: IDLE + +Initialize the Customize Run dialog with the command line arguments most +recently entered before. The user can optionally edit before submitting +them. + +.. + +.. bpo: 33610 +.. date: 2019-07-18-10-11-36 +.. nonce: xYqMLg +.. section: IDLE + +Fix code context not showing the correct context when first toggled on. + +.. + +.. bpo: 37530 +.. date: 2019-07-11-00-05-31 +.. nonce: AuyCyD +.. section: IDLE + +Optimize code context to reduce unneeded background activity. Font and +highlight changes now occur along with text changes instead of after a +random delay. + +.. + +.. bpo: 27452 +.. date: 2019-07-03-22-47-44 +.. nonce: nePPLi +.. section: IDLE + +Cleanup ``config.py`` by inlining ``RemoveFile`` and simplifying the +handling of ``file`` in ``CreateConfigHandlers``. + +.. + +.. bpo: 17535 +.. date: 2019-06-13-01-07-20 +.. nonce: K8i2St +.. section: IDLE + +Add optional line numbers for IDLE editor windows. Windows open without +line numbers unless set otherwise in the General tab of the configuration +dialog. + +.. + +.. bpo: 26806 +.. date: 2019-06-10-22-48-50 +.. nonce: Zltkum +.. section: IDLE + +To compensate for stack frames added by IDLE and avoid possible problems +with low recursion limits, add 30 to limits in the user code execution +process. Subtract 30 when reporting recursion limits to make this addition +mostly transparent. + +.. + +.. bpo: 36390 +.. date: 2019-03-21-08-35-00 +.. nonce: OdDCGk +.. section: IDLE + +Gather Format menu functions into format.py. Combine paragraph.py, +rstrip.py, and format methods from editor.py. + +.. + +.. bpo: 37675 +.. date: 2019-07-24-16-20-54 +.. nonce: 951Cvf +.. section: Tools/Demos + +2to3 now works when run from a zipped standard library. diff --git a/Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst = b/Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst deleted file mode 100644 index 177c4cb6d17c..000000000000 --- a/Misc/NEWS.d/next/Build/2019-07-11-01-28-24.bpo-36044.gIgfiJ.rst +++ /dev/null @@ -1,9 +0,0 @@ -Reduce the number of unit tests run for the PGO generation task. This -speeds up the task by a factor of about 15x. Running the full unit test -suite is slow. This change may result in a slightly less optimized build -since not as many code branches will be executed. If you are willing to -wait for the much slower build, the old behavior can be restored using -'./configure [..] PROFILE_TASK=3D"-m test --pgo-extended"'. We make no -guarantees as to which PGO task set produces a faster build. Users who -care should run their own relevant benchmarks as results can depend on -the environment, workload, and compiler tool chain. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974= .bVYmSA.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-3697= 4.bVYmSA.rst deleted file mode 100644 index 6080ef361814..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA= .rst=09 +++ /dev/null @@ -1,2 +0,0 @@ -Implemented separate vectorcall functions for every calling convention of -builtin functions and methods. This improves performance for calls. diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593= .yHSTwH.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-3759= 3.yHSTwH.rst deleted file mode 100644 index 5ec9bba2d2b9..000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2019-07-14-23-57-27.bpo-37593.yHSTwH= .rst=09 +++ /dev/null @@ -1,2 +0,0 @@ -Swap the positions of the *posonlyargs* and *args* parameters in the -constructor of :class:`ast.parameters` nodes. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0i= oLw.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw= .rst deleted file mode 100644 index 55b136621762..000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-07-06-00-57-27.bpo-37478.B0ioLw.rst +++ /dev/null @@ -1 +0,0 @@ -Added possible exceptions to the description of os.chdir(). \ No newline at end of file diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-02-19-02.bpo-37149.Num= Hn3.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-02-19-02.bpo-37149.NumHn3= .rst deleted file mode 100644 index f9b88dc0bfe0..000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-07-06-02-19-02.bpo-37149.NumHn3.rst +++ /dev/null @@ -1,2 +0,0 @@ -Replace the dead link to the Tkinter 8.5 reference by John Shipman, New -Mexico Tech, with a link to the archive.org copy. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.Qag= fZ5.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5= .rst deleted file mode 100644 index 605d08c3c040..000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-07-06-17-19-26.bpo-37487.QagfZ5.rst +++ /dev/null @@ -1 +0,0 @@ -Fix PyList_GetItem index description to include 0. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgA= QHn.rst b/Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn= .rst deleted file mode 100644 index 4d158733b0ea..000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-07-06-17-51-36.bpo-37456.lgAQHn.rst +++ /dev/null @@ -1 +0,0 @@ -Slash ('/') is now part of syntax. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7ti= FR-.rst b/Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-= .rst deleted file mode 100644 index 35d7f56f732f..000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-07-12-15-09-56.bpo-37521.7tiFR-.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fix `importlib` examples to insert any newly created modules via -importlib.util.module_from_spec() immediately into sys.modules instead of -after calling loader.exec_module(). - -Thanks to Benjamin Mintz for finding the bug. diff --git a/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIc= Bjy.rst b/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy= .rst deleted file mode 100644 index a39fe3fbbcb6..000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-07-13-12-43-01.bpo-30088.CIcBjy.rst +++ /dev/null @@ -1 +0,0 @@ -Documented that :class:`mailbox.Maildir` constructor doesn't attempt to veri= fy the maildir folder layout correctness. Patch by Sviatoslav Sydorenko. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8= WpB.rst b/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB= .rst deleted file mode 100644 index f875791c9a93..000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-07-13-12-58-20.bpo-37284.rP8WpB.rst +++ /dev/null @@ -1 +0,0 @@ -Add a brief note to indicate that any new ``sys.implementation`` required at= tributes must go through the PEP process. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caL= LAe.rst b/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe= .rst deleted file mode 100644 index 60386a196e7c..000000000000 --- a/Misc/NEWS.d/next/Documentation/2019-07-25-10-30-32.bpo-32910.caLLAe.rst +++ /dev/null @@ -1 +0,0 @@ -Remove implementation-specific behaviour of how venv's Deactivate works. diff --git a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst b= /Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst deleted file mode 100644 index 74bbda340249..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-03-21-08-35-00.bpo-36390.OdDCGk.rst +++ /dev/null @@ -1,2 +0,0 @@ -Gather Format menu functions into format.py. Combine -paragraph.py, rstrip.py, and format methods from editor.py. diff --git a/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst b= /Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst deleted file mode 100644 index 8514bb9292fe..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-06-10-22-48-50.bpo-26806.Zltkum.rst +++ /dev/null @@ -1,4 +0,0 @@ -To compensate for stack frames added by IDLE and avoid possible problems -with low recursion limits, add 30 to limits in the user code execution -process. Subtract 30 when reporting recursion limits to make this addition -mostly transparent. diff --git a/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst b= /Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst deleted file mode 100644 index 201a413b42a3..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-06-13-01-07-20.bpo-17535.K8i2St.rst +++ /dev/null @@ -1,4 +0,0 @@ -Add optional line numbers for IDLE editor windows. Windows -open without line numbers unless set otherwise in the General -tab of the configuration dialog.=20 - diff --git a/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst b= /Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst deleted file mode 100644 index ddd37bb86505..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-07-03-22-47-44.bpo-27452.nePPLi.rst +++ /dev/null @@ -1 +0,0 @@ -Cleanup ``config.py`` by inlining ``RemoveFile`` and simplifying the handlin= g of ``file`` in ``CreateConfigHandlers``. \ No newline at end of file diff --git a/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst b= /Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst deleted file mode 100644 index 0b80860b8fc2..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-07-11-00-05-31.bpo-37530.AuyCyD.rst +++ /dev/null @@ -1,3 +0,0 @@ -Optimize code context to reduce unneeded background activity. -Font and highlight changes now occur along with text changes -instead of after a random delay. diff --git a/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst b= /Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst deleted file mode 100644 index 6775b04378b0..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-07-18-10-11-36.bpo-33610.xYqMLg.rst +++ /dev/null @@ -1 +0,0 @@ -Fix code context not showing the correct context when first toggled on. diff --git a/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst b= /Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst deleted file mode 100644 index d864d07f60ef..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-07-20-23-33-53.bpo-37627.dQhUNB.rst +++ /dev/null @@ -1,3 +0,0 @@ -Initialize the Customize Run dialog with the command line arguments -most recently entered before. The user can optionally edit before -submitting them. diff --git a/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst b= /Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst deleted file mode 100644 index 60910c47e65b..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-07-26-17-51-13.bpo-37628.kX4AUF.rst +++ /dev/null @@ -1 +0,0 @@ -Settings dialog no longer expands with font size. diff --git a/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst b= /Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst deleted file mode 100644 index b3beadc1e026..000000000000 --- a/Misc/NEWS.d/next/IDLE/2019-07-27-15-14-20.bpo-37692.TRHGjD.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve highlight config sample with example shell interaction and better -labels for shell elements. diff --git a/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rs= t b/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst deleted file mode 100644 index ddd17aec1dd8..000000000000 --- a/Misc/NEWS.d/next/Library/2019-04-08-13-00-13.bpo-36564._n67m_.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix infinite loop in email header folding logic that would be triggered when -an email policy's max_line_length is not long enough to include the required -markup and any values in the message. Patch by Paul Ganssle diff --git a/Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rs= t b/Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst deleted file mode 100644 index 1e61f5e0b3db..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-20-14-23-48.bpo-37347.Gf9yYI.rst +++ /dev/null @@ -1,6 +0,0 @@ -:meth:`sqlite3.Connection.create_aggregate`, -:meth:`sqlite3.Connection.create_function`, -:meth:`sqlite3.Connection.set_authorizer`, -:meth:`sqlite3.Connection.set_progress_handler`=20 -:meth:`sqlite3.Connection.set_trace_callback`=20 -methods lead to segfaults if some of these methods are called twice with an = equal object but not the same. Now callbacks are stored more carefully. Patch= by Aleksandr Balezin. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rs= t b/Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst deleted file mode 100644 index c5852f614215..000000000000 --- a/Misc/NEWS.d/next/Library/2019-06-23-12-46-10.bpo-26967.xEuem1.rst +++ /dev/null @@ -1,3 +0,0 @@ -An :class:`~argparse.ArgumentParser` with ``allow_abbrev=3DFalse`` no longer -disables grouping of short flags, such as ``-vv``, but only disables -abbreviation of long flags as documented. Patch by Zac Hatfield-Dodds. diff --git a/Misc/NEWS.d/next/Library/2019-07-02-13-08-30.bpo-37481.hd5k09.rs= t b/Misc/NEWS.d/next/Library/2019-07-02-13-08-30.bpo-37481.hd5k09.rst deleted file mode 100644 index a3faecdd9151..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-02-13-08-30.bpo-37481.hd5k09.rst +++ /dev/null @@ -1,2 +0,0 @@ -The distutils ``bdist_wininst`` command is deprecated in Python 3.8, use -``bdist_wheel`` (wheel packages) instead. diff --git a/Misc/NEWS.d/next/Library/2019-07-03-12-47-52.bpo-37421.gR5hC8.rs= t b/Misc/NEWS.d/next/Library/2019-07-03-12-47-52.bpo-37421.gR5hC8.rst deleted file mode 100644 index 450d76f07289..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-03-12-47-52.bpo-37421.gR5hC8.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix :func:`multiprocessing.util.get_temp_dir` finalizer: clear also the -'tempdir' configuration of the current process, so next call to -``get_temp_dir()`` will create a new temporary directory, rather than -reusing the removed temporary directory. diff --git a/Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rs= t b/Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst deleted file mode 100644 index f9e99e0474a4..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-05-21-46-45.bpo-18374.qgE0H3.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix the ``.col_offset`` attribute of nested :class:`ast.BinOp` instances -which had a too large value in some situations. diff --git a/Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rs= t b/Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst deleted file mode 100644 index 6584d3ebe1ed..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-07-21-09-08.bpo-37520.Gg0KD6.rst +++ /dev/null @@ -1 +0,0 @@ -Correct behavior for zipfile.Path.parent when the path object identifies a s= ubdirectory. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rs= t b/Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst deleted file mode 100644 index 4eb6d0974bde..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-08-03-15-04.bpo-37502.qZGC4g.rst +++ /dev/null @@ -1 +0,0 @@ -pickle.loads() no longer raises TypeError when the buffers argument is set t= o None diff --git a/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rs= t b/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst deleted file mode 100644 index 0ac9b8eadcab..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst +++ /dev/null @@ -1,2 +0,0 @@ -Record calls to parent when autospecced object is attached to a mock using -:func:`unittest.mock.attach_mock`. Patch by Karthikeyan Singaravelan. diff --git a/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rs= t b/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst deleted file mode 100644 index ad52cf2a06cc..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-13-10-59-43.bpo-37579.B1Tq9i.rst +++ /dev/null @@ -1,4 +0,0 @@ -Return :exc:`NotImplemented` in Python implementation of ``__eq__`` for -:class:`~datetime.timedelta` and :class:`~datetime.time` when the other -object being compared is not of the same type to match C implementation. -Patch by Karthikeyan Singaravelan. diff --git a/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rs= t b/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst deleted file mode 100644 index af4287ba478b..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-17-06-54-43.bpo-37491.op0aMs.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix ``IndexError`` when parsing email headers with unexpectedly ending -bare-quoted string value. Patch by Abhilash Raj. diff --git a/Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rs= t b/Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst deleted file mode 100644 index 2e41211c685e..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-19-22-44-41.bpo-36324.1VjywS.rst +++ /dev/null @@ -1 +0,0 @@ -Make internal attributes for statistics.NormalDist() private. diff --git a/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rs= t b/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst deleted file mode 100644 index 3343d9100946..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-24-18-27-44.bpo-37664.o-GYZC.rst +++ /dev/null @@ -1 +0,0 @@ -Update wheels bundled with ensurepip (pip 19.2.1 and setuptools 41.0.1) diff --git a/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rs= t b/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst deleted file mode 100644 index 048478c008e8..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst +++ /dev/null @@ -1,2 +0,0 @@ -Let math.dist() accept coordinates as sequences (or iterables) rather than -just tuples. diff --git a/Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rs= t b/Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst deleted file mode 100644 index 6c78d7cd17c4..000000000000 --- a/Misc/NEWS.d/next/Library/2019-07-28-17-44-21.bpo-37697.7UV5d0.rst +++ /dev/null @@ -1 +0,0 @@ -Syncronize ``importlib.metadata`` with `importlib_metadata 0.19 `_, improving handling= of EGG-INFO files and fixing a crash when entry point names contained colons. \ No newline at end of file diff --git a/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.r= st b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst deleted file mode 100644 index 4bfd350c0b40..000000000000 --- a/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix an inifite loop when parsing specially crafted email headers. Patch by -Abhilash Raj. diff --git a/Misc/NEWS.d/next/Tests/2019-07-05-14-47-55.bpo-37421.n8o2to.rst = b/Misc/NEWS.d/next/Tests/2019-07-05-14-47-55.bpo-37421.n8o2to.rst deleted file mode 100644 index 136faa22d47a..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-07-05-14-47-55.bpo-37421.n8o2to.rst +++ /dev/null @@ -1,3 +0,0 @@ -multiprocessing tests now stop the ForkServer instance if it's running: close -the "alive" file descriptor to ask the server to stop and then remove its UN= IX -address. diff --git a/Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst = b/Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst deleted file mode 100644 index 0766d70f6eda..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-07-08-10-11-36.bpo-37421.OY77go.rst +++ /dev/null @@ -1,2 +0,0 @@ -test_concurrent_futures now explicitly stops the ForkServer instance if it's -running. diff --git a/Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst = b/Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst deleted file mode 100644 index aff6b6d1f12c..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-07-09-12-33-18.bpo-37526.vmm5y7.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :func:`test.support.catch_threading_exception`: context manager catching -:class:`threading.Thread` exception using :func:`threading.excepthook`. diff --git a/Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst = b/Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst deleted file mode 100644 index 9f393d71a3f2..000000000000 --- a/Misc/NEWS.d/next/Tests/2019-07-11-10-33-56.bpo-37558.SKHRsL.rst +++ /dev/null @@ -1 +0,0 @@ -Fix test_shared_memory_cleaned_after_process_termination name handling diff --git a/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cv= f.rst b/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst deleted file mode 100644 index e28fa207f918..000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2019-07-24-16-20-54.bpo-37675.951Cvf.rst +++ /dev/null @@ -1 +0,0 @@ -2to3 now works when run from a zipped standard library. diff --git a/Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rs= t b/Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst deleted file mode 100644 index 78b51c191825..000000000000 --- a/Misc/NEWS.d/next/Windows/2019-07-24-14-36-28.bpo-37672.uKEVHN.rst +++ /dev/null @@ -1,2 +0,0 @@ -Switch Windows Store package's pip to use bundled :file:`pip.ini` instead of -:envvar:`PIP_USER` variable. diff --git a/README.rst b/README.rst index e1d89b3d811a..12ecdf67ed1c 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.8.0 beta 2 +This is Python version 3.8.0 beta 3 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 .. image:: https://travis-ci.org/python/cpython.svg?branch=3Dmaster From webhook-mailer at python.org Wed Jul 31 04:47:40 2019 From: webhook-mailer at python.org (=?utf-8?q?=C5=81ukasz?= Langa) Date: Wed, 31 Jul 2019 08:47:40 -0000 Subject: [Python-checkins] (no subject) Message-ID: To: python-checkins at python.org Subject: bpo-37085: Expose SocketCAN bcm_msg_head flags (#13646) Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 https://github.com/python/cpython/commit/31c4fd2a10d90beaa37d630e5f74a471e14e= 089d commit: 31c4fd2a10d90beaa37d630e5f74a471e14e089d branch: master author: karl ding committer: =C5=81ukasz Langa date: 2019-07-31T10:47:16+02:00 summary: bpo-37085: Expose SocketCAN bcm_msg_head flags (#13646) Expose the CAN_BCM SocketCAN constants used in the bcm_msg_head struct flags (provided by ) under the socket library. This adds the following constants with a CAN_BCM prefix: * SETTIMER * STARTTIMER * TX_COUNTEVT * TX_ANNOUNCE * TX_CP_CAN_ID * RX_FILTER_ID * RX_CHECK_DLC * RX_NO_AUTOTIMER * RX_ANNOUNCE_RESUME * TX_RESET_MULTI_IDX * RX_RTR_FRAME * CAN_FD_FRAME The CAN_FD_FRAME flag was introduced in the 4.8 kernel, while the other ones were present since SocketCAN drivers were mainlined in 2.6.25. As such, it is probably unnecessary to guard against these constants being missing. files: A Misc/NEWS.d/next/Library/2019-06-18-16-29-31.bpo-37085.GeYaD6.rst M Doc/library/socket.rst M Lib/test/test_socket.py M Misc/ACKS M Modules/socketmodule.c diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index e3ddebdcbc0c..eebbe810abe4 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -391,6 +391,9 @@ Constants =20 .. availability:: Linux >=3D 2.6.25. =20 + .. note:: + The :data:`CAN_BCM_CAN_FD_FRAME` flag is only available on Linux >=3D = 4.8. + .. versionadded:: 3.4 =20 .. data:: CAN_RAW_FD_FRAMES diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 2705eff4794e..11b2a38ad76d 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1908,6 +1908,19 @@ def testBCMConstants(self): socket.CAN_BCM_RX_TIMEOUT # cyclic message is absent socket.CAN_BCM_RX_CHANGED # updated CAN frame (detected content ch= ange) =20 + # flags + socket.CAN_BCM_SETTIMER + socket.CAN_BCM_STARTTIMER + socket.CAN_BCM_TX_COUNTEVT + socket.CAN_BCM_TX_ANNOUNCE + socket.CAN_BCM_TX_CP_CAN_ID + socket.CAN_BCM_RX_FILTER_ID + socket.CAN_BCM_RX_CHECK_DLC + socket.CAN_BCM_RX_NO_AUTOTIMER + socket.CAN_BCM_RX_ANNOUNCE_RESUME + socket.CAN_BCM_TX_RESET_MULTI_IDX + socket.CAN_BCM_RX_RTR_FRAME + def testCreateSocket(self): with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) a= s s: pass diff --git a/Misc/ACKS b/Misc/ACKS index e02e8e1fa515..9ff74ec0cca1 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -397,6 +397,7 @@ Alon Diamant Toby Dickenson Mark Dickinson Jack Diederich +Karl Ding Daniel Diniz Humberto Diogenes Yves Dionne diff --git a/Misc/NEWS.d/next/Library/2019-06-18-16-29-31.bpo-37085.GeYaD6.rs= t b/Misc/NEWS.d/next/Library/2019-06-18-16-29-31.bpo-37085.GeYaD6.rst new file mode 100644 index 000000000000..e8db521d7aba --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-18-16-29-31.bpo-37085.GeYaD6.rst @@ -0,0 +1,2 @@ +Add the optional Linux SocketCAN Broadcast Manager constants, used as flags +to configure the BCM behaviour, in the socket module. Patch by Karl Ding. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 73e64f235a6f..99e350b4333b 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7656,6 +7656,8 @@ PyInit__socket(void) #endif #ifdef HAVE_LINUX_CAN_BCM_H PyModule_AddIntMacro(m, CAN_BCM); + + /* BCM opcodes */ PyModule_AddIntConstant(m, "CAN_BCM_TX_SETUP", TX_SETUP); PyModule_AddIntConstant(m, "CAN_BCM_TX_DELETE", TX_DELETE); PyModule_AddIntConstant(m, "CAN_BCM_TX_READ", TX_READ); @@ -7668,6 +7670,23 @@ PyInit__socket(void) PyModule_AddIntConstant(m, "CAN_BCM_RX_STATUS", RX_STATUS); PyModule_AddIntConstant(m, "CAN_BCM_RX_TIMEOUT", RX_TIMEOUT); PyModule_AddIntConstant(m, "CAN_BCM_RX_CHANGED", RX_CHANGED); + + /* BCM flags */ + PyModule_AddIntConstant(m, "CAN_BCM_SETTIMER", SETTIMER); + PyModule_AddIntConstant(m, "CAN_BCM_STARTTIMER", STARTTIMER); + PyModule_AddIntConstant(m, "CAN_BCM_TX_COUNTEVT", TX_COUNTEVT); + PyModule_AddIntConstant(m, "CAN_BCM_TX_ANNOUNCE", TX_ANNOUNCE); + PyModule_AddIntConstant(m, "CAN_BCM_TX_CP_CAN_ID", TX_CP_CAN_ID); + PyModule_AddIntConstant(m, "CAN_BCM_RX_FILTER_ID", RX_FILTER_ID); + PyModule_AddIntConstant(m, "CAN_BCM_RX_CHECK_DLC", RX_CHECK_DLC); + PyModule_AddIntConstant(m, "CAN_BCM_RX_NO_AUTOTIMER", RX_NO_AUTOTIMER); + PyModule_AddIntConstant(m, "CAN_BCM_RX_ANNOUNCE_RESUME", RX_ANNOUNCE_RES= UME); + PyModule_AddIntConstant(m, "CAN_BCM_TX_RESET_MULTI_IDX", TX_RESET_MULTI_= IDX); + PyModule_AddIntConstant(m, "CAN_BCM_RX_RTR_FRAME", RX_RTR_FRAME); +#ifdef CAN_FD_FRAME + /* CAN_FD_FRAME was only introduced in the 4.8.x kernel series */ + PyModule_AddIntConstant(m, "CAN_BCM_CAN_FD_FRAME", CAN_FD_FRAME); +#endif #endif #ifdef SOL_RDS PyModule_AddIntMacro(m, SOL_RDS);