From solipsis at pitrou.net Sat Dec 1 04:10:32 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 01 Dec 2018 09:10:32 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=6 Message-ID: <20181201091032.1.3B7A03DFDB880DD1@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_functools leaked [0, 3, 1] memory blocks, sum=4 test_logging leaked [26, -26, 1] memory blocks, sum=1 test_multiprocessing_spawn leaked [2, -1, 0] memory blocks, sum=1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflog5I4EeV', '--timeout', '7200'] From webhook-mailer at python.org Sat Dec 1 05:04:07 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Sat, 01 Dec 2018 10:04:07 -0000 Subject: [Python-checkins] bpo-31177: Skip deleted attributes while calling reset_mock (GH-9302) Message-ID: https://github.com/python/cpython/commit/edeca92c84a3b08902ecdfe987cde00c7e617887 commit: edeca92c84a3b08902ecdfe987cde00c7e617887 branch: master author: Xtreak committer: Victor Stinner date: 2018-12-01T11:03:54+01:00 summary: bpo-31177: Skip deleted attributes while calling reset_mock (GH-9302) files: A Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.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 a9c82dcb5d3e..9547b1a1fada 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -542,7 +542,7 @@ def reset_mock(self, visited=None,*, return_value=False, side_effect=False): self._mock_side_effect = None for child in self._mock_children.values(): - if isinstance(child, _SpecState): + if isinstance(child, _SpecState) or child is _deleted: continue child.reset_mock(visited) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 8cd284a6522d..ac6eea3720b8 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1596,6 +1596,16 @@ def test_attribute_deletion(self): self.assertRaises(AttributeError, getattr, mock, 'f') + def test_reset_mock_does_not_raise_on_attr_deletion(self): + # bpo-31177: reset_mock should not raise AttributeError when attributes + # were deleted in a mock instance + mock = Mock() + mock.child = True + del mock.child + mock.reset_mock() + self.assertFalse(hasattr(mock, 'child')) + + def test_class_assignable(self): for mock in Mock(), MagicMock(): self.assertNotIsInstance(mock, int) diff --git a/Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.rst b/Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.rst new file mode 100644 index 000000000000..f385571e99cc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.rst @@ -0,0 +1,2 @@ +Fix bug that prevented using :meth:`reset_mock ` +on mock instances with deleted attributes From webhook-mailer at python.org Sat Dec 1 05:16:30 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 01 Dec 2018 10:16:30 -0000 Subject: [Python-checkins] bpo-31177: Skip deleted attributes while calling reset_mock (GH-9302) Message-ID: https://github.com/python/cpython/commit/c0566e0ff6c2dd1a8b814ecd65649605c090527b commit: c0566e0ff6c2dd1a8b814ecd65649605c090527b branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-01T02:16:27-08:00 summary: bpo-31177: Skip deleted attributes while calling reset_mock (GH-9302) (cherry picked from commit edeca92c84a3b08902ecdfe987cde00c7e617887) Co-authored-by: Xtreak files: A Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.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 31b198516161..4bb0c32c745b 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -543,7 +543,7 @@ def reset_mock(self, visited=None,*, return_value=False, side_effect=False): self._mock_side_effect = None for child in self._mock_children.values(): - if isinstance(child, _SpecState): + if isinstance(child, _SpecState) or child is _deleted: continue child.reset_mock(visited) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index b64c8663d212..4601136eff9c 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1556,6 +1556,16 @@ def test_attribute_deletion(self): self.assertRaises(AttributeError, getattr, mock, 'f') + def test_reset_mock_does_not_raise_on_attr_deletion(self): + # bpo-31177: reset_mock should not raise AttributeError when attributes + # were deleted in a mock instance + mock = Mock() + mock.child = True + del mock.child + mock.reset_mock() + self.assertFalse(hasattr(mock, 'child')) + + def test_class_assignable(self): for mock in Mock(), MagicMock(): self.assertNotIsInstance(mock, int) diff --git a/Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.rst b/Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.rst new file mode 100644 index 000000000000..f385571e99cc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.rst @@ -0,0 +1,2 @@ +Fix bug that prevented using :meth:`reset_mock ` +on mock instances with deleted attributes From webhook-mailer at python.org Sat Dec 1 05:24:53 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 01 Dec 2018 10:24:53 -0000 Subject: [Python-checkins] bpo-31177: Skip deleted attributes while calling reset_mock (GH-9302) Message-ID: https://github.com/python/cpython/commit/422c1658b7d34fdc73c5fc895b135862103d1983 commit: 422c1658b7d34fdc73c5fc895b135862103d1983 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-01T02:24:47-08:00 summary: bpo-31177: Skip deleted attributes while calling reset_mock (GH-9302) (cherry picked from commit edeca92c84a3b08902ecdfe987cde00c7e617887) Co-authored-by: Xtreak files: A Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.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 b0c03c03bd6f..6ba186fb6631 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -541,7 +541,7 @@ def reset_mock(self, visited=None,*, return_value=False, side_effect=False): self._mock_side_effect = None for child in self._mock_children.values(): - if isinstance(child, _SpecState): + if isinstance(child, _SpecState) or child is _deleted: continue child.reset_mock(visited) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index c7bfa277b511..cc45f02409ef 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1566,6 +1566,16 @@ def test_attribute_deletion(self): self.assertRaises(AttributeError, getattr, mock, 'f') + def test_reset_mock_does_not_raise_on_attr_deletion(self): + # bpo-31177: reset_mock should not raise AttributeError when attributes + # were deleted in a mock instance + mock = Mock() + mock.child = True + del mock.child + mock.reset_mock() + self.assertFalse(hasattr(mock, 'child')) + + def test_class_assignable(self): for mock in Mock(), MagicMock(): self.assertNotIsInstance(mock, int) diff --git a/Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.rst b/Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.rst new file mode 100644 index 000000000000..f385571e99cc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-14-10-38-18.bpo-31177.Sv91TN.rst @@ -0,0 +1,2 @@ +Fix bug that prevented using :meth:`reset_mock ` +on mock instances with deleted attributes From webhook-mailer at python.org Sat Dec 1 07:16:03 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sat, 01 Dec 2018 12:16:03 -0000 Subject: [Python-checkins] [2.7] Fix signature of xml.dom.minidom.Document.toprettyxml(). (GH-10814). (GH-10827) Message-ID: https://github.com/python/cpython/commit/dfd4a1d8414ea54a3c56e909167983a503e51067 commit: dfd4a1d8414ea54a3c56e909167983a503e51067 branch: 2.7 author: E Kawashima committer: Serhiy Storchaka date: 2018-12-01T14:16:00+02:00 summary: [2.7] Fix signature of xml.dom.minidom.Document.toprettyxml(). (GH-10814). (GH-10827) (cherry picked from commit b7c2182604d5796b5af4c837991aa0b8c8a2d41f) files: M Doc/library/xml.dom.minidom.rst diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index f91259a8fb5c..2f4022428db0 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -169,7 +169,7 @@ module documentation. This section lists the differences between the API and the *encoding* argument was introduced; see :meth:`writexml`. -.. method:: Node.toprettyxml([indent=""[, newl=""[, encoding=""]]]) +.. method:: Node.toprettyxml(indent="\t", newl="\n", encoding=None) Return a pretty-printed version of the document. *indent* specifies the indentation string and defaults to a tabulator; *newl* specifies the string From webhook-mailer at python.org Sat Dec 1 07:19:42 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sat, 01 Dec 2018 12:19:42 -0000 Subject: [Python-checkins] [3.7] Fix signature of xml.dom.minidom.Document.toprettyxml(). (GH-10814). (GH-10824) Message-ID: https://github.com/python/cpython/commit/7da9755021a27075f5856c6ffff4c949e790bbee commit: 7da9755021a27075f5856c6ffff4c949e790bbee branch: 3.7 author: E Kawashima committer: Serhiy Storchaka date: 2018-12-01T14:19:39+02:00 summary: [3.7] Fix signature of xml.dom.minidom.Document.toprettyxml(). (GH-10814). (GH-10824) (cherry picked from commit b7c2182604d5796b5af4c837991aa0b8c8a2d41f) files: M Doc/library/xml.dom.minidom.rst diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index 40470e8736e7..d5d7b20efe60 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -156,7 +156,7 @@ module documentation. This section lists the differences between the API and encoding. Encoding this string in an encoding other than UTF-8 is likely incorrect, since UTF-8 is the default encoding of XML. -.. method:: Node.toprettyxml(indent="", newl="", encoding="") +.. method:: Node.toprettyxml(indent="\t", newl="\n", encoding=None) Return a pretty-printed version of the document. *indent* specifies the indentation string and defaults to a tabulator; *newl* specifies the string From webhook-mailer at python.org Sat Dec 1 07:21:04 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sat, 01 Dec 2018 12:21:04 -0000 Subject: [Python-checkins] [3.6] Fix signature of xml.dom.minidom.Document.toprettyxml(). (GH-10814). (GH-10825) Message-ID: https://github.com/python/cpython/commit/1e28daf0b58c9c3a08e98b3a3caebb53acf9a617 commit: 1e28daf0b58c9c3a08e98b3a3caebb53acf9a617 branch: 3.6 author: E Kawashima committer: Serhiy Storchaka date: 2018-12-01T14:21:00+02:00 summary: [3.6] Fix signature of xml.dom.minidom.Document.toprettyxml(). (GH-10814). (GH-10825) (cherry picked from commit b7c2182604d5796b5af4c837991aa0b8c8a2d41f) files: M Doc/library/xml.dom.minidom.rst diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index 40470e8736e7..d5d7b20efe60 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -156,7 +156,7 @@ module documentation. This section lists the differences between the API and encoding. Encoding this string in an encoding other than UTF-8 is likely incorrect, since UTF-8 is the default encoding of XML. -.. method:: Node.toprettyxml(indent="", newl="", encoding="") +.. method:: Node.toprettyxml(indent="\t", newl="\n", encoding=None) Return a pretty-printed version of the document. *indent* specifies the indentation string and defaults to a tabulator; *newl* specifies the string From webhook-mailer at python.org Sat Dec 1 07:30:23 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sat, 01 Dec 2018 12:30:23 -0000 Subject: [Python-checkins] bpo-35371: Fix possible crash in os.utime() on Windows. (GH-10844) Message-ID: https://github.com/python/cpython/commit/32bc11c33cf5ccea165b5f4ac3799f02fdf9c76a commit: 32bc11c33cf5ccea165b5f4ac3799f02fdf9c76a branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-01T14:30:20+02:00 summary: bpo-35371: Fix possible crash in os.utime() on Windows. (GH-10844) files: A Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst M Lib/test/test_os.py M Modules/posixmodule.c diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 3f6e48f0c8e6..aca445f91620 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -634,6 +634,29 @@ def test_utime_invalid_arguments(self): # seconds and nanoseconds parameters are mutually exclusive with self.assertRaises(ValueError): os.utime(self.fname, (5, 5), ns=(5, 5)) + with self.assertRaises(TypeError): + os.utime(self.fname, [5, 5]) + with self.assertRaises(TypeError): + os.utime(self.fname, (5,)) + with self.assertRaises(TypeError): + os.utime(self.fname, (5, 5, 5)) + with self.assertRaises(TypeError): + os.utime(self.fname, ns=[5, 5]) + with self.assertRaises(TypeError): + os.utime(self.fname, ns=(5,)) + with self.assertRaises(TypeError): + os.utime(self.fname, ns=(5, 5, 5)) + + if os.utime not in os.supports_follow_symlinks: + with self.assertRaises(NotImplementedError): + os.utime(self.fname, (5, 5), follow_symlinks=False) + if os.utime not in os.supports_fd: + with open(self.fname, 'wb', 0) as fp: + with self.assertRaises(TypeError): + os.utime(fp.fileno(), (5, 5)) + if os.utime not in os.supports_dir_fd: + with self.assertRaises(NotImplementedError): + os.utime(self.fname, (5, 5), dir_fd=0) @support.cpython_only def test_issue31577(self): diff --git a/Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst b/Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst new file mode 100644 index 000000000000..f40d13939311 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst @@ -0,0 +1,2 @@ +Fixed possible crash in ``os.utime()`` on Windows when pass incorrect +arguments. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d42e40f243e2..7571385ae51d 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4689,7 +4689,6 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, int result; #endif - PyObject *return_value = NULL; utime_t utime; memset(&utime, 0, sizeof(utime_t)); @@ -4698,7 +4697,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, PyErr_SetString(PyExc_ValueError, "utime: you may specify either 'times'" " or 'ns' but not both"); - goto exit; + return NULL; } if (times && (times != Py_None)) { @@ -4708,14 +4707,14 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, PyErr_SetString(PyExc_TypeError, "utime: 'times' must be either" " a tuple of two ints or None"); - goto exit; + return NULL; } utime.now = 0; if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0), &a_sec, &a_nsec, _PyTime_ROUND_FLOOR) == -1 || _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1), &m_sec, &m_nsec, _PyTime_ROUND_FLOOR) == -1) { - goto exit; + return NULL; } utime.atime_s = a_sec; utime.atime_ns = a_nsec; @@ -4726,14 +4725,14 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) { PyErr_SetString(PyExc_TypeError, "utime: 'ns' must be a tuple of two ints"); - goto exit; + return NULL; } utime.now = 0; if (!split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 0), &utime.atime_s, &utime.atime_ns) || !split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 1), &utime.mtime_s, &utime.mtime_ns)) { - goto exit; + return NULL; } } else { @@ -4743,20 +4742,20 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, #if !defined(UTIME_HAVE_NOFOLLOW_SYMLINKS) if (follow_symlinks_specified("utime", follow_symlinks)) - goto exit; + return NULL; #endif if (path_and_dir_fd_invalid("utime", path, dir_fd) || dir_fd_and_fd_invalid("utime", dir_fd, path->fd) || fd_and_follow_symlinks_invalid("utime", path->fd, follow_symlinks)) - goto exit; + return NULL; #if !defined(HAVE_UTIMENSAT) if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) { PyErr_SetString(PyExc_ValueError, "utime: cannot use dir_fd and follow_symlinks " "together on this platform"); - goto exit; + return NULL; } #endif @@ -4768,7 +4767,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, Py_END_ALLOW_THREADS if (hFile == INVALID_HANDLE_VALUE) { path_error(path); - goto exit; + return NULL; } if (utime.now) { @@ -4785,8 +4784,10 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, something is wrong with the file, when it also could be the time stamp that gives a problem. */ PyErr_SetFromWindowsErr(0); - goto exit; + CloseHandle(hFile); + return NULL; } + CloseHandle(hFile); #else /* MS_WINDOWS */ Py_BEGIN_ALLOW_THREADS @@ -4814,21 +4815,13 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, if (result < 0) { /* see previous comment about not putting filename in error here */ - return_value = posix_error(); - goto exit; + posix_error(); + return NULL; } #endif /* MS_WINDOWS */ - Py_INCREF(Py_None); - return_value = Py_None; - -exit: -#ifdef MS_WINDOWS - if (hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); -#endif - return return_value; + Py_RETURN_NONE; } /* Process operations */ From webhook-mailer at python.org Sat Dec 1 07:52:07 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 01 Dec 2018 12:52:07 -0000 Subject: [Python-checkins] bpo-35371: Fix possible crash in os.utime() on Windows. (GH-10844) Message-ID: https://github.com/python/cpython/commit/265b41996aa3f604624a8046d1c314a1aee4b590 commit: 265b41996aa3f604624a8046d1c314a1aee4b590 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-01T04:52:04-08:00 summary: bpo-35371: Fix possible crash in os.utime() on Windows. (GH-10844) (cherry picked from commit 32bc11c33cf5ccea165b5f4ac3799f02fdf9c76a) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst M Lib/test/test_os.py M Modules/posixmodule.c diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 098d1d44fa35..fe7261dd76e9 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -634,6 +634,29 @@ def test_utime_invalid_arguments(self): # seconds and nanoseconds parameters are mutually exclusive with self.assertRaises(ValueError): os.utime(self.fname, (5, 5), ns=(5, 5)) + with self.assertRaises(TypeError): + os.utime(self.fname, [5, 5]) + with self.assertRaises(TypeError): + os.utime(self.fname, (5,)) + with self.assertRaises(TypeError): + os.utime(self.fname, (5, 5, 5)) + with self.assertRaises(TypeError): + os.utime(self.fname, ns=[5, 5]) + with self.assertRaises(TypeError): + os.utime(self.fname, ns=(5,)) + with self.assertRaises(TypeError): + os.utime(self.fname, ns=(5, 5, 5)) + + if os.utime not in os.supports_follow_symlinks: + with self.assertRaises(NotImplementedError): + os.utime(self.fname, (5, 5), follow_symlinks=False) + if os.utime not in os.supports_fd: + with open(self.fname, 'wb', 0) as fp: + with self.assertRaises(TypeError): + os.utime(fp.fileno(), (5, 5)) + if os.utime not in os.supports_dir_fd: + with self.assertRaises(NotImplementedError): + os.utime(self.fname, (5, 5), dir_fd=0) @support.cpython_only def test_issue31577(self): diff --git a/Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst b/Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst new file mode 100644 index 000000000000..f40d13939311 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst @@ -0,0 +1,2 @@ +Fixed possible crash in ``os.utime()`` on Windows when pass incorrect +arguments. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index dbd534cab06a..cab30c21025c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4645,7 +4645,6 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, int result; #endif - PyObject *return_value = NULL; utime_t utime; memset(&utime, 0, sizeof(utime_t)); @@ -4654,7 +4653,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, PyErr_SetString(PyExc_ValueError, "utime: you may specify either 'times'" " or 'ns' but not both"); - goto exit; + return NULL; } if (times && (times != Py_None)) { @@ -4664,14 +4663,14 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, PyErr_SetString(PyExc_TypeError, "utime: 'times' must be either" " a tuple of two ints or None"); - goto exit; + return NULL; } utime.now = 0; if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0), &a_sec, &a_nsec, _PyTime_ROUND_FLOOR) == -1 || _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1), &m_sec, &m_nsec, _PyTime_ROUND_FLOOR) == -1) { - goto exit; + return NULL; } utime.atime_s = a_sec; utime.atime_ns = a_nsec; @@ -4682,14 +4681,14 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) { PyErr_SetString(PyExc_TypeError, "utime: 'ns' must be a tuple of two ints"); - goto exit; + return NULL; } utime.now = 0; if (!split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 0), &utime.atime_s, &utime.atime_ns) || !split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 1), &utime.mtime_s, &utime.mtime_ns)) { - goto exit; + return NULL; } } else { @@ -4699,20 +4698,20 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, #if !defined(UTIME_HAVE_NOFOLLOW_SYMLINKS) if (follow_symlinks_specified("utime", follow_symlinks)) - goto exit; + return NULL; #endif if (path_and_dir_fd_invalid("utime", path, dir_fd) || dir_fd_and_fd_invalid("utime", dir_fd, path->fd) || fd_and_follow_symlinks_invalid("utime", path->fd, follow_symlinks)) - goto exit; + return NULL; #if !defined(HAVE_UTIMENSAT) if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) { PyErr_SetString(PyExc_ValueError, "utime: cannot use dir_fd and follow_symlinks " "together on this platform"); - goto exit; + return NULL; } #endif @@ -4724,7 +4723,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, Py_END_ALLOW_THREADS if (hFile == INVALID_HANDLE_VALUE) { path_error(path); - goto exit; + return NULL; } if (utime.now) { @@ -4741,8 +4740,10 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, something is wrong with the file, when it also could be the time stamp that gives a problem. */ PyErr_SetFromWindowsErr(0); - goto exit; + CloseHandle(hFile); + return NULL; } + CloseHandle(hFile); #else /* MS_WINDOWS */ Py_BEGIN_ALLOW_THREADS @@ -4770,21 +4771,13 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, if (result < 0) { /* see previous comment about not putting filename in error here */ - return_value = posix_error(); - goto exit; + posix_error(); + return NULL; } #endif /* MS_WINDOWS */ - Py_INCREF(Py_None); - return_value = Py_None; - -exit: -#ifdef MS_WINDOWS - if (hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); -#endif - return return_value; + Py_RETURN_NONE; } /* Process operations */ From webhook-mailer at python.org Sat Dec 1 07:53:40 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 01 Dec 2018 12:53:40 -0000 Subject: [Python-checkins] bpo-35371: Fix possible crash in os.utime() on Windows. (GH-10844) Message-ID: https://github.com/python/cpython/commit/013832ff964a0b3b59e04a07a33bae65c1c3ae84 commit: 013832ff964a0b3b59e04a07a33bae65c1c3ae84 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-01T04:53:36-08:00 summary: bpo-35371: Fix possible crash in os.utime() on Windows. (GH-10844) (cherry picked from commit 32bc11c33cf5ccea165b5f4ac3799f02fdf9c76a) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst M Lib/test/test_os.py M Modules/posixmodule.c diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 9e35d55b8056..7a839c83fe4b 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -660,6 +660,29 @@ def test_utime_invalid_arguments(self): # seconds and nanoseconds parameters are mutually exclusive with self.assertRaises(ValueError): os.utime(self.fname, (5, 5), ns=(5, 5)) + with self.assertRaises(TypeError): + os.utime(self.fname, [5, 5]) + with self.assertRaises(TypeError): + os.utime(self.fname, (5,)) + with self.assertRaises(TypeError): + os.utime(self.fname, (5, 5, 5)) + with self.assertRaises(TypeError): + os.utime(self.fname, ns=[5, 5]) + with self.assertRaises(TypeError): + os.utime(self.fname, ns=(5,)) + with self.assertRaises(TypeError): + os.utime(self.fname, ns=(5, 5, 5)) + + if os.utime not in os.supports_follow_symlinks: + with self.assertRaises(NotImplementedError): + os.utime(self.fname, (5, 5), follow_symlinks=False) + if os.utime not in os.supports_fd: + with open(self.fname, 'wb', 0) as fp: + with self.assertRaises(TypeError): + os.utime(fp.fileno(), (5, 5)) + if os.utime not in os.supports_dir_fd: + with self.assertRaises(NotImplementedError): + os.utime(self.fname, (5, 5), dir_fd=0) from test import mapping_tests diff --git a/Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst b/Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst new file mode 100644 index 000000000000..f40d13939311 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-01-13-44-12.bpo-35371.fTAwlX.rst @@ -0,0 +1,2 @@ +Fixed possible crash in ``os.utime()`` on Windows when pass incorrect +arguments. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b7091ca4c234..03825c3228ca 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4589,7 +4589,6 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, int result; #endif - PyObject *return_value = NULL; utime_t utime; memset(&utime, 0, sizeof(utime_t)); @@ -4598,7 +4597,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, PyErr_SetString(PyExc_ValueError, "utime: you may specify either 'times'" " or 'ns' but not both"); - goto exit; + return NULL; } if (times && (times != Py_None)) { @@ -4608,14 +4607,14 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, PyErr_SetString(PyExc_TypeError, "utime: 'times' must be either" " a tuple of two ints or None"); - goto exit; + return NULL; } utime.now = 0; if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0), &a_sec, &a_nsec, _PyTime_ROUND_FLOOR) == -1 || _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1), &m_sec, &m_nsec, _PyTime_ROUND_FLOOR) == -1) { - goto exit; + return NULL; } utime.atime_s = a_sec; utime.atime_ns = a_nsec; @@ -4626,14 +4625,14 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) { PyErr_SetString(PyExc_TypeError, "utime: 'ns' must be a tuple of two ints"); - goto exit; + return NULL; } utime.now = 0; if (!split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 0), &utime.atime_s, &utime.atime_ns) || !split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 1), &utime.mtime_s, &utime.mtime_ns)) { - goto exit; + return NULL; } } else { @@ -4643,20 +4642,20 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, #if !defined(UTIME_HAVE_NOFOLLOW_SYMLINKS) if (follow_symlinks_specified("utime", follow_symlinks)) - goto exit; + return NULL; #endif if (path_and_dir_fd_invalid("utime", path, dir_fd) || dir_fd_and_fd_invalid("utime", dir_fd, path->fd) || fd_and_follow_symlinks_invalid("utime", path->fd, follow_symlinks)) - goto exit; + return NULL; #if !defined(HAVE_UTIMENSAT) if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) { PyErr_SetString(PyExc_ValueError, "utime: cannot use dir_fd and follow_symlinks " "together on this platform"); - goto exit; + return NULL; } #endif @@ -4668,7 +4667,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, Py_END_ALLOW_THREADS if (hFile == INVALID_HANDLE_VALUE) { path_error(path); - goto exit; + return NULL; } if (utime.now) { @@ -4685,8 +4684,10 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, something is wrong with the file, when it also could be the time stamp that gives a problem. */ PyErr_SetFromWindowsErr(0); - goto exit; + CloseHandle(hFile); + return NULL; } + CloseHandle(hFile); #else /* MS_WINDOWS */ Py_BEGIN_ALLOW_THREADS @@ -4714,21 +4715,13 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, if (result < 0) { /* see previous comment about not putting filename in error here */ - return_value = posix_error(); - goto exit; + posix_error(); + return NULL; } #endif /* MS_WINDOWS */ - Py_INCREF(Py_None); - return_value = Py_None; - -exit: -#ifdef MS_WINDOWS - if (hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); -#endif - return return_value; + Py_RETURN_NONE; } /* Process operations */ From solipsis at pitrou.net Sun Dec 2 04:09:55 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 02 Dec 2018 09:09:55 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=4 Message-ID: <20181202090955.1.B61C2D2011B18F06@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_functools leaked [0, 3, 1] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflog8smGO9', '--timeout', '7200'] From webhook-mailer at python.org Sun Dec 2 10:53:18 2018 From: webhook-mailer at python.org (Ivan Levkivskyi) Date: Sun, 02 Dec 2018 15:53:18 -0000 Subject: [Python-checkins] bpo-35341: Add generic version of OrderedDict to typing (GH-10850) Message-ID: https://github.com/python/cpython/commit/68b56d02ef20479b87c65e523cf3dec1b7b77d40 commit: 68b56d02ef20479b87c65e523cf3dec1b7b77d40 branch: master author: Ismo Toijala committer: Ivan Levkivskyi date: 2018-12-02T15:53:14Z summary: bpo-35341: Add generic version of OrderedDict to typing (GH-10850) files: A Misc/NEWS.d/next/Library/2018-12-02-13-50-52.bpo-35341.32E8T_.rst M Doc/library/typing.rst M Lib/test/test_typing.py M Lib/typing.py diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 4469e88b2248..47ae4213f3c0 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -689,6 +689,12 @@ The module defines the following classes, functions and decorators: .. versionadded:: 3.5.2 +.. class:: OrderedDict(collections.OrderedDict, MutableMapping[KT, VT]) + + A generic version of :class:`collections.OrderedDict`. + + .. versionadded:: 3.7.2 + .. class:: Counter(collections.Counter, Dict[T, int]) A generic version of :class:`collections.Counter`. diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 6d8cc5319fb1..0d66ebbd1845 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2075,6 +2075,22 @@ class MyDefDict(typing.DefaultDict[str, int]): self.assertIsSubclass(MyDefDict, collections.defaultdict) self.assertNotIsSubclass(collections.defaultdict, MyDefDict) + def test_ordereddict_instantiation(self): + self.assertIs(type(typing.OrderedDict()), collections.OrderedDict) + self.assertIs(type(typing.OrderedDict[KT, VT]()), collections.OrderedDict) + self.assertIs(type(typing.OrderedDict[str, int]()), collections.OrderedDict) + + def test_ordereddict_subclass(self): + + class MyOrdDict(typing.OrderedDict[str, int]): + pass + + od = MyOrdDict() + self.assertIsInstance(od, MyOrdDict) + + self.assertIsSubclass(MyOrdDict, collections.OrderedDict) + self.assertNotIsSubclass(collections.OrderedDict, MyOrdDict) + @skipUnless(sys.version_info >= (3, 3), 'ChainMap was added in 3.3') def test_chainmap_instantiation(self): self.assertIs(type(typing.ChainMap()), collections.ChainMap) diff --git a/Lib/typing.py b/Lib/typing.py index 4f9e04506fb5..3243e2af1119 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1241,6 +1241,7 @@ def _alias(origin, params, inst=True): AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, T_co) Dict = _alias(dict, (KT, VT), inst=False) DefaultDict = _alias(collections.defaultdict, (KT, VT)) +OrderedDict = _alias(collections.OrderedDict, (KT, VT)) Counter = _alias(collections.Counter, T) ChainMap = _alias(collections.ChainMap, (KT, VT)) Generator = _alias(collections.abc.Generator, (T_co, T_contra, V_co)) diff --git a/Misc/NEWS.d/next/Library/2018-12-02-13-50-52.bpo-35341.32E8T_.rst b/Misc/NEWS.d/next/Library/2018-12-02-13-50-52.bpo-35341.32E8T_.rst new file mode 100644 index 000000000000..43aa9956c1f7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-02-13-50-52.bpo-35341.32E8T_.rst @@ -0,0 +1 @@ +Add generic version of ``collections.OrderedDict`` to the ``typing`` module. Patch by Ismo Toijala. From webhook-mailer at python.org Sun Dec 2 11:14:48 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 02 Dec 2018 16:14:48 -0000 Subject: [Python-checkins] bpo-35341: Add generic version of OrderedDict to typing (GH-10850) Message-ID: https://github.com/python/cpython/commit/6cb0486ce861903448bd6ba1095685b6cd48e3bd commit: 6cb0486ce861903448bd6ba1095685b6cd48e3bd branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-02T08:14:44-08:00 summary: bpo-35341: Add generic version of OrderedDict to typing (GH-10850) (cherry picked from commit 68b56d02ef20479b87c65e523cf3dec1b7b77d40) Co-authored-by: Ismo Toijala files: A Misc/NEWS.d/next/Library/2018-12-02-13-50-52.bpo-35341.32E8T_.rst M Doc/library/typing.rst M Lib/test/test_typing.py M Lib/typing.py diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index f6d67d1ff9c5..0f37fc60896f 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -689,6 +689,12 @@ The module defines the following classes, functions and decorators: .. versionadded:: 3.5.2 +.. class:: OrderedDict(collections.OrderedDict, MutableMapping[KT, VT]) + + A generic version of :class:`collections.OrderedDict`. + + .. versionadded:: 3.7.2 + .. class:: Counter(collections.Counter, Dict[T, int]) A generic version of :class:`collections.Counter`. diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 6d8cc5319fb1..0d66ebbd1845 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2075,6 +2075,22 @@ class MyDefDict(typing.DefaultDict[str, int]): self.assertIsSubclass(MyDefDict, collections.defaultdict) self.assertNotIsSubclass(collections.defaultdict, MyDefDict) + def test_ordereddict_instantiation(self): + self.assertIs(type(typing.OrderedDict()), collections.OrderedDict) + self.assertIs(type(typing.OrderedDict[KT, VT]()), collections.OrderedDict) + self.assertIs(type(typing.OrderedDict[str, int]()), collections.OrderedDict) + + def test_ordereddict_subclass(self): + + class MyOrdDict(typing.OrderedDict[str, int]): + pass + + od = MyOrdDict() + self.assertIsInstance(od, MyOrdDict) + + self.assertIsSubclass(MyOrdDict, collections.OrderedDict) + self.assertNotIsSubclass(collections.OrderedDict, MyOrdDict) + @skipUnless(sys.version_info >= (3, 3), 'ChainMap was added in 3.3') def test_chainmap_instantiation(self): self.assertIs(type(typing.ChainMap()), collections.ChainMap) diff --git a/Lib/typing.py b/Lib/typing.py index cfcbb3b76328..8cf0d00bceaf 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1242,6 +1242,7 @@ def _alias(origin, params, inst=True): AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, T_co) Dict = _alias(dict, (KT, VT), inst=False) DefaultDict = _alias(collections.defaultdict, (KT, VT)) +OrderedDict = _alias(collections.OrderedDict, (KT, VT)) Counter = _alias(collections.Counter, T) ChainMap = _alias(collections.ChainMap, (KT, VT)) Generator = _alias(collections.abc.Generator, (T_co, T_contra, V_co)) diff --git a/Misc/NEWS.d/next/Library/2018-12-02-13-50-52.bpo-35341.32E8T_.rst b/Misc/NEWS.d/next/Library/2018-12-02-13-50-52.bpo-35341.32E8T_.rst new file mode 100644 index 000000000000..43aa9956c1f7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-02-13-50-52.bpo-35341.32E8T_.rst @@ -0,0 +1 @@ +Add generic version of ``collections.OrderedDict`` to the ``typing`` module. Patch by Ismo Toijala. From webhook-mailer at python.org Mon Dec 3 02:58:26 2018 From: webhook-mailer at python.org (Chris Withers) Date: Mon, 03 Dec 2018 07:58:26 -0000 Subject: [Python-checkins] bpo-32153: Add unit test for create_autospec with partial function returned in getattr (#10398) Message-ID: https://github.com/python/cpython/commit/c667b094ae37799a7e42ba5cd2ad501cc7920888 commit: c667b094ae37799a7e42ba5cd2ad501cc7920888 branch: master author: Xtreak committer: Chris Withers date: 2018-12-03T07:58:15Z summary: bpo-32153: Add unit test for create_autospec with partial function returned in getattr (#10398) * Add create_autospec with partial function returned in getattr * Use self.assertFalse instead of assert * Use different names and remove object files: M Lib/unittest/test/testmock/testhelpers.py diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 7919482ae99c..9edebf551660 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -8,6 +8,7 @@ ) from datetime import datetime +from functools import partial class SomeClass(object): def one(self, a, b): @@ -871,6 +872,19 @@ def test_autospec_on_bound_builtin_function(self): mocked.assert_called_once_with(4, 5, 6) + def test_autospec_getattr_partial_function(self): + # bpo-32153 : getattr returning partial functions without + # __name__ should not create AttributeError in create_autospec + class Foo: + + def __getattr__(self, attribute): + return partial(lambda name: name, attribute) + + proxy = Foo() + autospec = create_autospec(proxy) + self.assertFalse(hasattr(autospec, '__name__')) + + class TestCallList(unittest.TestCase): def test_args_list_contains_call_list(self): From webhook-mailer at python.org Mon Dec 3 03:26:11 2018 From: webhook-mailer at python.org (Chris Withers) Date: Mon, 03 Dec 2018 08:26:11 -0000 Subject: [Python-checkins] bpo-32153: Add unit test for create_autospec with partial function returned in getattr (GH-10398) (#10855) Message-ID: https://github.com/python/cpython/commit/1ef06c62d3c05cbba6448c56af30a09c551c9ec2 commit: 1ef06c62d3c05cbba6448c56af30a09c551c9ec2 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-03T08:26:06Z summary: bpo-32153: Add unit test for create_autospec with partial function returned in getattr (GH-10398) (#10855) * Add create_autospec with partial function returned in getattr * Use self.assertFalse instead of assert * Use different names and remove object (cherry picked from commit c667b094ae37799a7e42ba5cd2ad501cc7920888) Co-authored-by: Xtreak files: M Lib/unittest/test/testmock/testhelpers.py diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 7919482ae99c..9edebf551660 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -8,6 +8,7 @@ ) from datetime import datetime +from functools import partial class SomeClass(object): def one(self, a, b): @@ -871,6 +872,19 @@ def test_autospec_on_bound_builtin_function(self): mocked.assert_called_once_with(4, 5, 6) + def test_autospec_getattr_partial_function(self): + # bpo-32153 : getattr returning partial functions without + # __name__ should not create AttributeError in create_autospec + class Foo: + + def __getattr__(self, attribute): + return partial(lambda name: name, attribute) + + proxy = Foo() + autospec = create_autospec(proxy) + self.assertFalse(hasattr(autospec, '__name__')) + + class TestCallList(unittest.TestCase): def test_args_list_contains_call_list(self): From webhook-mailer at python.org Mon Dec 3 03:31:38 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Mon, 03 Dec 2018 08:31:38 -0000 Subject: [Python-checkins] bpo-10320: Replace nonstandard sprintf() length modifier in ctypes' PyCArg_repr(). (GH-10853) Message-ID: https://github.com/python/cpython/commit/062cbb67726f26794b1b461853e40696b4a0b220 commit: 062cbb67726f26794b1b461853e40696b4a0b220 branch: master author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-03T10:31:35+02:00 summary: bpo-10320: Replace nonstandard sprintf() length modifier in ctypes' PyCArg_repr(). (GH-10853) Use "ll" instead of the nonstandard "q". files: M Modules/_ctypes/callproc.c diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index d485c58e8c01..ad40ca1c5247 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -484,7 +484,7 @@ PyCArg_repr(PyCArgObject *self) #ifdef MS_WIN32 "", #else - "", + "", #endif self->tag, self->value.q); break; From webhook-mailer at python.org Mon Dec 3 03:36:48 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Mon, 03 Dec 2018 08:36:48 -0000 Subject: [Python-checkins] bpo-35372: Fix the code page decoder for input > 2 GiB. (GH-10848) Message-ID: https://github.com/python/cpython/commit/4013c179117754b039957db4730880bf3285919d commit: 4013c179117754b039957db4730880bf3285919d branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-03T10:36:45+02:00 summary: bpo-35372: Fix the code page decoder for input > 2 GiB. (GH-10848) files: A Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst M Lib/test/test_codecs.py M Objects/unicodeobject.c diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 8c92556e8429..79cddb8715b2 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3190,6 +3190,24 @@ def test_mbcs_alias(self): codec = codecs.lookup('cp123') self.assertEqual(codec.name, 'mbcs') + @support.bigmemtest(size=2**31, memuse=7, dry_run=False) + def test_large_input(self): + # Test input longer than INT_MAX. + # Input should contain undecodable bytes before and after + # the INT_MAX limit. + encoded = (b'01234567' * (2**28-1) + + b'\x85\x86\xea\xeb\xec\xef\xfc\xfd\xfe\xff') + self.assertEqual(len(encoded), 2**31+2) + decoded = codecs.code_page_decode(932, encoded, 'surrogateescape', True) + self.assertEqual(decoded[1], len(encoded)) + del encoded + self.assertEqual(len(decoded[0]), decoded[1]) + self.assertEqual(decoded[0][:10], '0123456701') + self.assertEqual(decoded[0][-20:], + '6701234567' + '\udc85\udc86\udcea\udceb\udcec' + '\udcef\udcfc\udcfd\udcfe\udcff') + class ASCIITest(unittest.TestCase): def test_encode(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst new file mode 100644 index 000000000000..dc2de44b4f63 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst @@ -0,0 +1,2 @@ +Fixed the code page decoder for input longer than 2 GiB containing +undecodable bytes. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index bc98c44c7407..1351eece8e92 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7211,7 +7211,7 @@ decode_code_page_errors(UINT code_page, "in the target code page."; /* each step cannot decode more than 1 character, but a character can be represented as a surrogate pair */ - wchar_t buffer[2], *startout, *out; + wchar_t buffer[2], *out; int insize; Py_ssize_t outsize; PyObject *errorHandler = NULL; @@ -7248,7 +7248,7 @@ decode_code_page_errors(UINT code_page, *v = (PyObject*)_PyUnicode_New(size * Py_ARRAY_LENGTH(buffer)); if (*v == NULL) goto error; - startout = PyUnicode_AS_UNICODE(*v); + out = PyUnicode_AS_UNICODE(*v); } else { /* Extend unicode object */ @@ -7259,11 +7259,10 @@ decode_code_page_errors(UINT code_page, } if (unicode_resize(v, n + size * Py_ARRAY_LENGTH(buffer)) < 0) goto error; - startout = PyUnicode_AS_UNICODE(*v) + n; + out = PyUnicode_AS_UNICODE(*v) + n; } /* Decode the byte string character per character */ - out = startout; while (in < endin) { /* Decode a character */ @@ -7318,7 +7317,7 @@ decode_code_page_errors(UINT code_page, *out = 0; /* Extend unicode object */ - outsize = out - startout; + outsize = out - PyUnicode_AS_UNICODE(*v); assert(outsize <= PyUnicode_WSTR_LENGTH(*v)); if (unicode_resize(v, outsize) < 0) goto error; From solipsis at pitrou.net Mon Dec 3 04:08:51 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 03 Dec 2018 09:08:51 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=5 Message-ID: <20181203090851.1.15BB8933AF7934A1@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [7, -7, 1] memory blocks, sum=1 test_functools leaked [0, 3, 1] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogB9HhnK', '--timeout', '7200'] From webhook-mailer at python.org Mon Dec 3 04:09:18 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 03 Dec 2018 09:09:18 -0000 Subject: [Python-checkins] bpo-35372: Fix the code page decoder for input > 2 GiB. (GH-10848) Message-ID: https://github.com/python/cpython/commit/bdeb56cd21ef3f4f086c93045d80f2a753823379 commit: bdeb56cd21ef3f4f086c93045d80f2a753823379 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-03T01:09:11-08:00 summary: bpo-35372: Fix the code page decoder for input > 2 GiB. (GH-10848) (cherry picked from commit 4013c179117754b039957db4730880bf3285919d) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst M Lib/test/test_codecs.py M Objects/unicodeobject.c diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index a59a5e21358e..5c2de212b199 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3185,6 +3185,24 @@ def test_mbcs_alias(self): codec = codecs.lookup('cp123') self.assertEqual(codec.name, 'mbcs') + @support.bigmemtest(size=2**31, memuse=7, dry_run=False) + def test_large_input(self): + # Test input longer than INT_MAX. + # Input should contain undecodable bytes before and after + # the INT_MAX limit. + encoded = (b'01234567' * (2**28-1) + + b'\x85\x86\xea\xeb\xec\xef\xfc\xfd\xfe\xff') + self.assertEqual(len(encoded), 2**31+2) + decoded = codecs.code_page_decode(932, encoded, 'surrogateescape', True) + self.assertEqual(decoded[1], len(encoded)) + del encoded + self.assertEqual(len(decoded[0]), decoded[1]) + self.assertEqual(decoded[0][:10], '0123456701') + self.assertEqual(decoded[0][-20:], + '6701234567' + '\udc85\udc86\udcea\udceb\udcec' + '\udcef\udcfc\udcfd\udcfe\udcff') + class ASCIITest(unittest.TestCase): def test_encode(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst new file mode 100644 index 000000000000..dc2de44b4f63 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst @@ -0,0 +1,2 @@ +Fixed the code page decoder for input longer than 2 GiB containing +undecodable bytes. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index bd3f151c6a50..d46ab2a1e2ab 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7178,7 +7178,7 @@ decode_code_page_errors(UINT code_page, "in the target code page."; /* each step cannot decode more than 1 character, but a character can be represented as a surrogate pair */ - wchar_t buffer[2], *startout, *out; + wchar_t buffer[2], *out; int insize; Py_ssize_t outsize; PyObject *errorHandler = NULL; @@ -7215,7 +7215,7 @@ decode_code_page_errors(UINT code_page, *v = (PyObject*)_PyUnicode_New(size * Py_ARRAY_LENGTH(buffer)); if (*v == NULL) goto error; - startout = PyUnicode_AS_UNICODE(*v); + out = PyUnicode_AS_UNICODE(*v); } else { /* Extend unicode object */ @@ -7226,11 +7226,10 @@ decode_code_page_errors(UINT code_page, } if (unicode_resize(v, n + size * Py_ARRAY_LENGTH(buffer)) < 0) goto error; - startout = PyUnicode_AS_UNICODE(*v) + n; + out = PyUnicode_AS_UNICODE(*v) + n; } /* Decode the byte string character per character */ - out = startout; while (in < endin) { /* Decode a character */ @@ -7285,7 +7284,7 @@ decode_code_page_errors(UINT code_page, *out = 0; /* Extend unicode object */ - outsize = out - startout; + outsize = out - PyUnicode_AS_UNICODE(*v); assert(outsize <= PyUnicode_WSTR_LENGTH(*v)); if (unicode_resize(v, outsize) < 0) goto error; From webhook-mailer at python.org Mon Dec 3 04:11:34 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 03 Dec 2018 09:11:34 -0000 Subject: [Python-checkins] bpo-10320: Replace nonstandard sprintf() length modifier in ctypes' PyCArg_repr(). (GH-10853) Message-ID: https://github.com/python/cpython/commit/a9f435e5d856fb62516b70a78217e40b90bec233 commit: a9f435e5d856fb62516b70a78217e40b90bec233 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-03T01:11:30-08:00 summary: bpo-10320: Replace nonstandard sprintf() length modifier in ctypes' PyCArg_repr(). (GH-10853) Use "ll" instead of the nonstandard "q". (cherry picked from commit 062cbb67726f26794b1b461853e40696b4a0b220) Co-authored-by: Zackery Spytz files: M Modules/_ctypes/callproc.c diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index d485c58e8c01..ad40ca1c5247 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -484,7 +484,7 @@ PyCArg_repr(PyCArgObject *self) #ifdef MS_WIN32 "", #else - "", + "", #endif self->tag, self->value.q); break; From webhook-mailer at python.org Mon Dec 3 04:11:40 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 03 Dec 2018 09:11:40 -0000 Subject: [Python-checkins] bpo-10320: Replace nonstandard sprintf() length modifier in ctypes' PyCArg_repr(). (GH-10853) Message-ID: https://github.com/python/cpython/commit/f65ede3f8fa08493facf48177540d0ec26e59560 commit: f65ede3f8fa08493facf48177540d0ec26e59560 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-03T01:11:37-08:00 summary: bpo-10320: Replace nonstandard sprintf() length modifier in ctypes' PyCArg_repr(). (GH-10853) Use "ll" instead of the nonstandard "q". (cherry picked from commit 062cbb67726f26794b1b461853e40696b4a0b220) Co-authored-by: Zackery Spytz files: M Modules/_ctypes/callproc.c diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index e6079e9938ce..04fbc010ca7d 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -484,7 +484,7 @@ PyCArg_repr(PyCArgObject *self) #ifdef MS_WIN32 "", #else - "", + "", #endif self->tag, self->value.q); break; From webhook-mailer at python.org Mon Dec 3 04:15:06 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 03 Dec 2018 09:15:06 -0000 Subject: [Python-checkins] bpo-35372: Fix the code page decoder for input > 2 GiB. (GH-10848) Message-ID: https://github.com/python/cpython/commit/0f9b6687eb8b26dd804abcc6efd4d6430ae16f24 commit: 0f9b6687eb8b26dd804abcc6efd4d6430ae16f24 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-03T01:15:02-08:00 summary: bpo-35372: Fix the code page decoder for input > 2 GiB. (GH-10848) (cherry picked from commit 4013c179117754b039957db4730880bf3285919d) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst M Lib/test/test_codecs.py M Objects/unicodeobject.c diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index eb21a3915b93..56485de3f6e9 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -3191,6 +3191,24 @@ def _get_fake_codepage(*a): finally: _bootlocale.getpreferredencoding = old_getpreferredencoding + @support.bigmemtest(size=2**31, memuse=7, dry_run=False) + def test_large_input(self): + # Test input longer than INT_MAX. + # Input should contain undecodable bytes before and after + # the INT_MAX limit. + encoded = (b'01234567' * (2**28-1) + + b'\x85\x86\xea\xeb\xec\xef\xfc\xfd\xfe\xff') + self.assertEqual(len(encoded), 2**31+2) + decoded = codecs.code_page_decode(932, encoded, 'surrogateescape', True) + self.assertEqual(decoded[1], len(encoded)) + del encoded + self.assertEqual(len(decoded[0]), decoded[1]) + self.assertEqual(decoded[0][:10], '0123456701') + self.assertEqual(decoded[0][-20:], + '6701234567' + '\udc85\udc86\udcea\udceb\udcec' + '\udcef\udcfc\udcfd\udcfe\udcff') + class ASCIITest(unittest.TestCase): def test_encode(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst new file mode 100644 index 000000000000..dc2de44b4f63 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-01-19-20-53.bpo-35372.RwVJjZ.rst @@ -0,0 +1,2 @@ +Fixed the code page decoder for input longer than 2 GiB containing +undecodable bytes. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 29b019887ac0..0e64bf943db3 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7285,7 +7285,7 @@ decode_code_page_errors(UINT code_page, "in the target code page."; /* each step cannot decode more than 1 character, but a character can be represented as a surrogate pair */ - wchar_t buffer[2], *startout, *out; + wchar_t buffer[2], *out; int insize; Py_ssize_t outsize; PyObject *errorHandler = NULL; @@ -7322,7 +7322,7 @@ decode_code_page_errors(UINT code_page, *v = (PyObject*)_PyUnicode_New(size * Py_ARRAY_LENGTH(buffer)); if (*v == NULL) goto error; - startout = PyUnicode_AS_UNICODE(*v); + out = PyUnicode_AS_UNICODE(*v); } else { /* Extend unicode object */ @@ -7333,11 +7333,10 @@ decode_code_page_errors(UINT code_page, } if (unicode_resize(v, n + size * Py_ARRAY_LENGTH(buffer)) < 0) goto error; - startout = PyUnicode_AS_UNICODE(*v) + n; + out = PyUnicode_AS_UNICODE(*v) + n; } /* Decode the byte string character per character */ - out = startout; while (in < endin) { /* Decode a character */ @@ -7392,7 +7391,7 @@ decode_code_page_errors(UINT code_page, *out = 0; /* Extend unicode object */ - outsize = out - startout; + outsize = out - PyUnicode_AS_UNICODE(*v); assert(outsize <= PyUnicode_WSTR_LENGTH(*v)); if (unicode_resize(v, outsize) < 0) goto error; From webhook-mailer at python.org Mon Dec 3 06:02:48 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 03 Dec 2018 11:02:48 -0000 Subject: [Python-checkins] bpo-35373: Fix PyInit_timezone() if HAVE_DECL_TZNAME is defined (GH-10861) Message-ID: https://github.com/python/cpython/commit/ab6614969301b238fcc27f43923a0189a57a2a3c commit: ab6614969301b238fcc27f43923a0189a57a2a3c branch: master author: Victor Stinner committer: GitHub date: 2018-12-03T12:02:43+01:00 summary: bpo-35373: Fix PyInit_timezone() if HAVE_DECL_TZNAME is defined (GH-10861) If HAVE_DECL_TZNAME, PyInit_timezone() now returns -1 on error. files: M Modules/timemodule.c diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 188f1e6ef571..61041c90b87e 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1581,16 +1581,17 @@ PyInit_timezone(PyObject *m) PyModule_AddIntConstant(m, "daylight", daylight); otz0 = PyUnicode_DecodeLocale(tzname[0], "surrogateescape"); if (otz0 == NULL) { - return; + return -1; } otz1 = PyUnicode_DecodeLocale(tzname[1], "surrogateescape"); if (otz1 == NULL) { Py_DECREF(otz0); - return; + return -1; } PyObject *tzname_obj = Py_BuildValue("(NN)", otz0, otz1); - if (tzname_obj == NULL) - return; + if (tzname_obj == NULL) { + return -1; + } PyModule_AddObject(m, "tzname", tzname_obj); #else // !HAVE_DECL_TZNAME static const time_t YEAR = (365 * 24 + 6) * 3600; From webhook-mailer at python.org Mon Dec 3 06:29:32 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 03 Dec 2018 11:29:32 -0000 Subject: [Python-checkins] bpo-35368: Make PyMem_Malloc() thread-safe in debug mode (GH-10828) Message-ID: https://github.com/python/cpython/commit/c275be54411d425c90e7c679ddb5321ba458f61d commit: c275be54411d425c90e7c679ddb5321ba458f61d branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-12-03T12:29:29+01:00 summary: bpo-35368: Make PyMem_Malloc() thread-safe in debug mode (GH-10828) When Python is compiled in debug mode, PyMem_Malloc() uses debug hooks, but it also uses pymalloc allocator instead of malloc(). Problem: pymalloc is not thread-safe, whereas PyMem_Malloc() is thread-safe in release mode (it's a thin wrapper to malloc() in this case). Modify the debug hook to use malloc() for PyMem_Malloc(). files: A Misc/NEWS.d/next/Core and Builtins/2018-11-30-17-50-28.bpo-35368.DNaDao.rst M Objects/obmalloc.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-11-30-17-50-28.bpo-35368.DNaDao.rst b/Misc/NEWS.d/next/Core and Builtins/2018-11-30-17-50-28.bpo-35368.DNaDao.rst new file mode 100644 index 000000000000..4bcf608fdd44 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-11-30-17-50-28.bpo-35368.DNaDao.rst @@ -0,0 +1 @@ +:c:func:`PyMem_Malloc` is now also thread-safe in debug mode. diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 9adcff7a27ef..0778c851faf0 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -1413,6 +1413,38 @@ pool_is_in_list(const poolp target, poolp list) #endif /* Py_DEBUG */ +static void * +_PyMem_Malloc(size_t nbytes) +{ + if (nbytes > (size_t)PY_SSIZE_T_MAX) { + return NULL; + } + if (nbytes == 0) { + nbytes = 1; + } + return malloc(nbytes); +} + +static void * +_PyMem_Realloc(void *p, size_t nbytes) +{ + if (nbytes > (size_t)PY_SSIZE_T_MAX) { + return NULL; + } + if (nbytes == 0) { + nbytes = 1; + } + return realloc(p, nbytes); +} + + +static void +_PyMem_Free(void *p) +{ + free(p); +} + + /* Let S = sizeof(size_t). The debug malloc asks for 4*S extra bytes and fills them with useful stuff, here calling the underlying malloc's result p: @@ -1479,7 +1511,7 @@ _PyObject_DebugCheckAddress(const void *p) /* generic debug memory api, with an "id" to identify the API in use */ void * -_PyObject_DebugMallocApi(char id, size_t nbytes) +_PyObject_DebugMallocApi(char api, size_t nbytes) { uchar *p; /* base address of malloc'ed block */ uchar *tail; /* p + 2*SST + nbytes == pointer to tail pad bytes */ @@ -1491,13 +1523,18 @@ _PyObject_DebugMallocApi(char id, size_t nbytes) /* overflow: can't represent total as a size_t */ return NULL; - p = (uchar *)PyObject_Malloc(total); + if (api == _PYMALLOC_OBJ_ID) { + p = (uchar *)PyObject_Malloc(total); + } + else { + p = (uchar *)_PyMem_Malloc(total); + } if (p == NULL) return NULL; - /* at p, write size (SST bytes), id (1 byte), pad (SST-1 bytes) */ + /* at p, write size (SST bytes), api (1 byte), pad (SST-1 bytes) */ write_size_t(p, nbytes); - p[SST] = (uchar)id; + p[SST] = (uchar)api; memset(p + SST + 1 , FORBIDDENBYTE, SST-1); if (nbytes > 0) @@ -1529,7 +1566,12 @@ _PyObject_DebugFreeApi(char api, void *p) nbytes += 4*SST; if (nbytes > 0) memset(q, DEADBYTE, nbytes); - PyObject_Free(q); + if (api == _PYMALLOC_OBJ_ID) { + PyObject_Free(q); + } + else { + _PyMem_Free(q); + } } void * @@ -1561,7 +1603,12 @@ _PyObject_DebugReallocApi(char api, void *p, size_t nbytes) * case we didn't get the chance to mark the old memory with DEADBYTE, * but we live with that. */ - q = (uchar *)PyObject_Realloc(q - 2*SST, total); + if (api == _PYMALLOC_OBJ_ID) { + q = (uchar *)PyObject_Realloc(q - 2*SST, total); + } + else { + q = (uchar *)_PyMem_Realloc(q - 2*SST, total); + } if (q == NULL) { if (nbytes <= original_nbytes) { /* bpo-31626: the memset() above expects that realloc never fails From webhook-mailer at python.org Mon Dec 3 07:45:42 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 03 Dec 2018 12:45:42 -0000 Subject: [Python-checkins] bpo-35373: Fix PyInit_time() error handling (GH-10865) Message-ID: https://github.com/python/cpython/commit/3bb150d8148e3cc08418077a58f43e064b9fde61 commit: 3bb150d8148e3cc08418077a58f43e064b9fde61 branch: master author: Victor Stinner committer: GitHub date: 2018-12-03T13:45:38+01:00 summary: bpo-35373: Fix PyInit_time() error handling (GH-10865) * PyInit_time() now returns NULL if an exception is raised. * Rename PyInit_timezone() to init_timezone(). "PyInit_" prefix is a special prefix for function initializing a module. init_timezone() doesn't initialize a module and the function is not exported. files: M Modules/timemodule.c diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 61041c90b87e..01bb430ece60 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1019,7 +1019,7 @@ of the timezone or altzone attributes on the time module."); #endif /* HAVE_MKTIME */ #ifdef HAVE_WORKING_TZSET -static int PyInit_timezone(PyObject *module); +static int init_timezone(PyObject *module); static PyObject * time_tzset(PyObject *self, PyObject *unused) @@ -1034,7 +1034,7 @@ time_tzset(PyObject *self, PyObject *unused) tzset(); /* Reset timezone, altzone, daylight and tzname */ - if (PyInit_timezone(m) < 0) { + if (init_timezone(m) < 0) { return NULL; } Py_DECREF(m); @@ -1549,7 +1549,7 @@ get_gmtoff(time_t t, struct tm *p) #endif // !HAVE_DECL_TZNAME static int -PyInit_timezone(PyObject *m) +init_timezone(PyObject *m) { assert(!PyErr_Occurred()); @@ -1745,7 +1745,7 @@ PyInit_time(void) return NULL; /* Set, or reset, module variables like time.timezone */ - if (PyInit_timezone(m) < 0) { + if (init_timezone(m) < 0) { return NULL; } @@ -1798,6 +1798,9 @@ PyInit_time(void) utc_string = tm.tm_zone; #endif + if (PyErr_Occurred()) { + return NULL; + } return m; } From webhook-mailer at python.org Mon Dec 3 10:49:35 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 03 Dec 2018 15:49:35 -0000 Subject: [Python-checkins] bpo-26544: Make platform.libc_ver() less slow (GH-10868) Message-ID: https://github.com/python/cpython/commit/8687bd86e6f138ef0699a1e9f3f9555765949b51 commit: 8687bd86e6f138ef0699a1e9f3f9555765949b51 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-12-03T16:49:24+01:00 summary: bpo-26544: Make platform.libc_ver() less slow (GH-10868) Coarse benchmark on Fedora 29: 1.6 sec => 0.1 sec. Co-Authored-By: Antoine Pitrou (cherry-picked from commit ba7c226095703f63c78b00e56f1db8d99ac3a54a) files: M Lib/platform.py diff --git a/Lib/platform.py b/Lib/platform.py index 62a5476a8f4d..e04d87f258a7 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -194,7 +194,10 @@ def libc_ver(executable=sys.executable,lib='',version='', chunksize=2048): binary = f.read(chunksize) pos = 0 while pos < len(binary): - m = _libc_search.search(binary,pos) + if 'libc' in binary or 'GLIBC' in binary: + m = _libc_search.search(binary, pos) + else: + m = None if not m or m.end() == len(binary): chunk = f.read(chunksize) if chunk: From webhook-mailer at python.org Mon Dec 3 14:08:16 2018 From: webhook-mailer at python.org (Andrew Svetlov) Date: Mon, 03 Dec 2018 19:08:16 -0000 Subject: [Python-checkins] bpo-35380: Enable TCP_NODELAY for proactor event loop (#10867) Message-ID: https://github.com/python/cpython/commit/3bc0ebab17bf5a2c29d2214743c82034f82e6573 commit: 3bc0ebab17bf5a2c29d2214743c82034f82e6573 branch: master author: Andrew Svetlov committer: GitHub date: 2018-12-03T21:08:13+02:00 summary: bpo-35380: Enable TCP_NODELAY for proactor event loop (#10867) files: A Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst M Lib/asyncio/base_events.py M Lib/asyncio/proactor_events.py M Lib/asyncio/selector_events.py M Lib/test/test_asyncio/test_base_events.py M Lib/test/test_asyncio/test_selector_events.py diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index f5ab6e7b2d21..60a189bdfb7e 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -168,6 +168,17 @@ def _run_until_complete_cb(fut): futures._get_loop(fut).stop() +if hasattr(socket, 'TCP_NODELAY'): + def _set_nodelay(sock): + if (sock.family in {socket.AF_INET, socket.AF_INET6} and + sock.type == socket.SOCK_STREAM and + sock.proto == socket.IPPROTO_TCP): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) +else: + def _set_nodelay(sock): + pass + + class _SendfileFallbackProtocol(protocols.Protocol): def __init__(self, transp): if not isinstance(transp, transports._FlowControlMixin): diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index ad23918802fa..69d96a81035e 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -13,7 +13,6 @@ from . import base_events from . import constants -from . import events from . import futures from . import exceptions from . import protocols @@ -445,6 +444,11 @@ class _ProactorSocketTransport(_ProactorReadPipeTransport, _sendfile_compatible = constants._SendfileMode.TRY_NATIVE + def __init__(self, loop, sock, protocol, waiter=None, + extra=None, server=None): + super().__init__(loop, sock, protocol, waiter, extra, server) + base_events._set_nodelay(sock) + def _set_extra(self, sock): self._extra['socket'] = sock diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index ad093001dd9a..112c4b15b8d8 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -39,17 +39,6 @@ def _test_selector_event(selector, fd, event): return bool(key.events & event) -if hasattr(socket, 'TCP_NODELAY'): - def _set_nodelay(sock): - if (sock.family in {socket.AF_INET, socket.AF_INET6} and - sock.type == socket.SOCK_STREAM and - sock.proto == socket.IPPROTO_TCP): - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) -else: - def _set_nodelay(sock): - pass - - class BaseSelectorEventLoop(base_events.BaseEventLoop): """Selector event loop. @@ -742,7 +731,7 @@ def __init__(self, loop, sock, protocol, waiter=None, # Disable the Nagle algorithm -- small writes will be # sent without waiting for the TCP ACK. This generally # decreases the latency (in some cases significantly.) - _set_nodelay(self._sock) + base_events._set_nodelay(self._sock) self._loop.call_soon(self._protocol.connection_made, self) # only start reading when connection_made() has been called diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 6d544d1eda86..53854758a27d 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -2,7 +2,6 @@ import concurrent.futures import errno -import logging import math import os import socket @@ -15,7 +14,6 @@ import asyncio from asyncio import base_events from asyncio import constants -from asyncio import events from test.test_asyncio import utils as test_utils from test import support from test.support.script_helper import assert_python_ok @@ -288,7 +286,7 @@ def cb(): loop.set_debug(debug) if debug: msg = ("Non-thread-safe operation invoked on an event loop other " - "than the current one") + "than the current one") with self.assertRaisesRegex(RuntimeError, msg): loop.call_soon(cb) with self.assertRaisesRegex(RuntimeError, msg): @@ -2075,5 +2073,31 @@ def test_negative_offset(self): self.run_loop(self.loop.sock_sendfile(sock, self.file, -1)) +class TestSelectorUtils(test_utils.TestCase): + def check_set_nodelay(self, sock): + opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) + self.assertFalse(opt) + + base_events._set_nodelay(sock) + + opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) + self.assertTrue(opt) + + @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'), + 'need socket.TCP_NODELAY') + def test_set_nodelay(self): + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, + proto=socket.IPPROTO_TCP) + with sock: + self.check_set_nodelay(sock) + + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, + proto=socket.IPPROTO_TCP) + with sock: + sock.setblocking(False) + self.check_set_nodelay(sock) + + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index b99e8e696070..d0d171a9853a 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -14,7 +14,6 @@ from asyncio.selector_events import _SelectorTransport from asyncio.selector_events import _SelectorSocketTransport from asyncio.selector_events import _SelectorDatagramTransport -from asyncio.selector_events import _set_nodelay from test.test_asyncio import utils as test_utils @@ -1344,30 +1343,5 @@ def test_fatal_error_connected(self, m_exc): exc_info=(ConnectionRefusedError, MOCK_ANY, MOCK_ANY)) -class TestSelectorUtils(test_utils.TestCase): - def check_set_nodelay(self, sock): - opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) - self.assertFalse(opt) - - _set_nodelay(sock) - - opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) - self.assertTrue(opt) - - @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'), - 'need socket.TCP_NODELAY') - def test_set_nodelay(self): - sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, - proto=socket.IPPROTO_TCP) - with sock: - self.check_set_nodelay(sock) - - sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, - proto=socket.IPPROTO_TCP) - with sock: - sock.setblocking(False) - self.check_set_nodelay(sock) - - if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst b/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst new file mode 100644 index 000000000000..91f86e604ea8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst @@ -0,0 +1 @@ +Enable TCP_NODELAY on Windows for proactor asyncio event loop. From webhook-mailer at python.org Mon Dec 3 16:11:47 2018 From: webhook-mailer at python.org (Andrew Svetlov) Date: Mon, 03 Dec 2018 21:11:47 -0000 Subject: [Python-checkins] [3.7] bpo-35380: Enable TCP_NODELAY for proactor event loop (GH-10867) (GH-10872) Message-ID: https://github.com/python/cpython/commit/fe91e9ba08a8854e2149398386702828fe3c0038 commit: fe91e9ba08a8854e2149398386702828fe3c0038 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Andrew Svetlov date: 2018-12-03T23:11:41+02:00 summary: [3.7] bpo-35380: Enable TCP_NODELAY for proactor event loop (GH-10867) (GH-10872) * bpo-35380: Enable TCP_NODELAY for proactor event loop (GH-10867) (cherry picked from commit 3bc0ebab17bf5a2c29d2214743c82034f82e6573) Co-authored-by: Andrew Svetlov files: A Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst M Lib/asyncio/base_events.py M Lib/asyncio/proactor_events.py M Lib/asyncio/selector_events.py M Lib/test/test_asyncio/test_base_events.py M Lib/test/test_asyncio/test_selector_events.py diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index b3b07554ec19..65e0529e1d90 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -165,6 +165,17 @@ def _run_until_complete_cb(fut): futures._get_loop(fut).stop() +if hasattr(socket, 'TCP_NODELAY'): + def _set_nodelay(sock): + if (sock.family in {socket.AF_INET, socket.AF_INET6} and + sock.type == socket.SOCK_STREAM and + sock.proto == socket.IPPROTO_TCP): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) +else: + def _set_nodelay(sock): + pass + + class _SendfileFallbackProtocol(protocols.Protocol): def __init__(self, transp): if not isinstance(transp, transports._FlowControlMixin): diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 66bfb0ab11ee..e350e8bc0c24 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -444,6 +444,11 @@ class _ProactorSocketTransport(_ProactorReadPipeTransport, _sendfile_compatible = constants._SendfileMode.TRY_NATIVE + def __init__(self, loop, sock, protocol, waiter=None, + extra=None, server=None): + super().__init__(loop, sock, protocol, waiter, extra, server) + base_events._set_nodelay(sock) + def _set_extra(self, sock): self._extra['socket'] = sock diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 116c08d6ff7f..d2861d34332a 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -39,17 +39,6 @@ def _test_selector_event(selector, fd, event): return bool(key.events & event) -if hasattr(socket, 'TCP_NODELAY'): - def _set_nodelay(sock): - if (sock.family in {socket.AF_INET, socket.AF_INET6} and - sock.type == socket.SOCK_STREAM and - sock.proto == socket.IPPROTO_TCP): - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) -else: - def _set_nodelay(sock): - pass - - class BaseSelectorEventLoop(base_events.BaseEventLoop): """Selector event loop. @@ -733,7 +722,7 @@ def __init__(self, loop, sock, protocol, waiter=None, # Disable the Nagle algorithm -- small writes will be # sent without waiting for the TCP ACK. This generally # decreases the latency (in some cases significantly.) - _set_nodelay(self._sock) + base_events._set_nodelay(self._sock) self._loop.call_soon(self._protocol.connection_made, self) # only start reading when connection_made() has been called diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 59d321e1c691..559ed3ec5279 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -272,7 +272,7 @@ def cb(): loop.set_debug(debug) if debug: msg = ("Non-thread-safe operation invoked on an event loop other " - "than the current one") + "than the current one") with self.assertRaisesRegex(RuntimeError, msg): loop.call_soon(cb) with self.assertRaisesRegex(RuntimeError, msg): @@ -2056,5 +2056,31 @@ def test_negative_offset(self): self.run_loop(self.loop.sock_sendfile(sock, self.file, -1)) +class TestSelectorUtils(test_utils.TestCase): + def check_set_nodelay(self, sock): + opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) + self.assertFalse(opt) + + base_events._set_nodelay(sock) + + opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) + self.assertTrue(opt) + + @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'), + 'need socket.TCP_NODELAY') + def test_set_nodelay(self): + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, + proto=socket.IPPROTO_TCP) + with sock: + self.check_set_nodelay(sock) + + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, + proto=socket.IPPROTO_TCP) + with sock: + sock.setblocking(False) + self.check_set_nodelay(sock) + + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index 68b6ee9abbf1..e94af28bade3 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -15,7 +15,6 @@ from asyncio.selector_events import _SelectorTransport from asyncio.selector_events import _SelectorSocketTransport from asyncio.selector_events import _SelectorDatagramTransport -from asyncio.selector_events import _set_nodelay from test.test_asyncio import utils as test_utils @@ -1746,30 +1745,5 @@ def test_fatal_error_connected(self, m_exc): exc_info=(ConnectionRefusedError, MOCK_ANY, MOCK_ANY)) -class TestSelectorUtils(test_utils.TestCase): - def check_set_nodelay(self, sock): - opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) - self.assertFalse(opt) - - _set_nodelay(sock) - - opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) - self.assertTrue(opt) - - @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'), - 'need socket.TCP_NODELAY') - def test_set_nodelay(self): - sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, - proto=socket.IPPROTO_TCP) - with sock: - self.check_set_nodelay(sock) - - sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, - proto=socket.IPPROTO_TCP) - with sock: - sock.setblocking(False) - self.check_set_nodelay(sock) - - if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst b/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst new file mode 100644 index 000000000000..91f86e604ea8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst @@ -0,0 +1 @@ +Enable TCP_NODELAY on Windows for proactor asyncio event loop. From webhook-mailer at python.org Mon Dec 3 16:31:41 2018 From: webhook-mailer at python.org (Chris Withers) Date: Mon, 03 Dec 2018 21:31:41 -0000 Subject: [Python-checkins] bpo-35226: Fix equality for nested unittest.mock.call objects. (#10555) Message-ID: https://github.com/python/cpython/commit/8ca0fa9d2f4de6e69f0902790432e0ab2f37ba68 commit: 8ca0fa9d2f4de6e69f0902790432e0ab2f37ba68 branch: master author: Chris Withers committer: GitHub date: 2018-12-03T21:31:37Z summary: bpo-35226: Fix equality for nested unittest.mock.call objects. (#10555) Also refactor the call recording imolementation and add some notes about its limitations. files: A Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst M Doc/library/unittest.mock-examples.rst M Doc/library/unittest.mock.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testhelpers.py M Lib/unittest/test/testmock/testmock.py diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst index 60db4c2ba4da..16690f349822 100644 --- a/Doc/library/unittest.mock-examples.rst +++ b/Doc/library/unittest.mock-examples.rst @@ -166,6 +166,15 @@ You use the :data:`call` object to construct lists for comparing with >>> mock.mock_calls == expected True +However, parameters to calls that return mocks are not recorded, which means it is not +possible to track nested calls where the parameters used to create ancestors are important: + + >>> m = Mock() + >>> m.factory(important=True).deliver() + + >>> m.mock_calls[-1] == call.factory(important=False).deliver() + True + Setting Return Values and Attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index 0ae29546586a..bfab00eb7514 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -702,6 +702,19 @@ the *new_callable* argument to :func:`patch`. unpacked as tuples to get at the individual arguments. See :ref:`calls as tuples `. + .. note:: + + The way :attr:`mock_calls` are recorded means that where nested + calls are made, the parameters of ancestor calls are not recorded + and so will always compare equal: + + >>> mock = MagicMock() + >>> mock.top(a=3).bottom() + + >>> mock.mock_calls + [call.top(a=3), call.top().bottom()] + >>> mock.mock_calls[-1] == call.top(a=-1).bottom() + True .. attribute:: __class__ diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 9547b1a1fada..d13f74982086 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -977,46 +977,51 @@ def _mock_call(_mock_self, *args, **kwargs): self = _mock_self self.called = True self.call_count += 1 - _new_name = self._mock_new_name - _new_parent = self._mock_new_parent + # handle call_args _call = _Call((args, kwargs), two=True) self.call_args = _call self.call_args_list.append(_call) - self.mock_calls.append(_Call(('', args, kwargs))) seen = set() - skip_next_dot = _new_name == '()' + + # initial stuff for method_calls: do_method_calls = self._mock_parent is not None - name = self._mock_name - while _new_parent is not None: - this_mock_call = _Call((_new_name, args, kwargs)) - if _new_parent._mock_new_name: - dot = '.' - if skip_next_dot: - dot = '' + method_call_name = self._mock_name - skip_next_dot = False - if _new_parent._mock_new_name == '()': - skip_next_dot = True + # initial stuff for mock_calls: + mock_call_name = self._mock_new_name + is_a_call = mock_call_name == '()' + self.mock_calls.append(_Call(('', args, kwargs))) - _new_name = _new_parent._mock_new_name + dot + _new_name + # follow up the chain of mocks: + _new_parent = self._mock_new_parent + while _new_parent is not None: + # handle method_calls: if do_method_calls: - if _new_name == name: - this_method_call = this_mock_call - else: - this_method_call = _Call((name, args, kwargs)) - _new_parent.method_calls.append(this_method_call) - + _new_parent.method_calls.append(_Call((method_call_name, args, kwargs))) do_method_calls = _new_parent._mock_parent is not None if do_method_calls: - name = _new_parent._mock_name + '.' + name + method_call_name = _new_parent._mock_name + '.' + method_call_name + # handle mock_calls: + this_mock_call = _Call((mock_call_name, args, kwargs)) _new_parent.mock_calls.append(this_mock_call) + + if _new_parent._mock_new_name: + if is_a_call: + dot = '' + else: + dot = '.' + is_a_call = _new_parent._mock_new_name == '()' + mock_call_name = _new_parent._mock_new_name + dot + mock_call_name + + # follow the parental chain: _new_parent = _new_parent._mock_new_parent - # use ids here so as not to call __hash__ on the mocks + # check we're not in an infinite loop: + # ( use ids here so as not to call __hash__ on the mocks) _new_parent_id = id(_new_parent) if _new_parent_id in seen: break @@ -2054,6 +2059,10 @@ def __eq__(self, other): else: self_name, self_args, self_kwargs = self + if (getattr(self, 'parent', None) and getattr(other, 'parent', None) + and self.parent != other.parent): + return False + other_name = '' if len_other == 0: other_args, other_kwargs = (), {} diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 9edebf551660..9388311e3e25 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -270,6 +270,22 @@ def test_extended_call(self): self.assertEqual(mock.mock_calls, last_call.call_list()) + def test_extended_not_equal(self): + a = call(x=1).foo + b = call(x=2).foo + self.assertEqual(a, a) + self.assertEqual(b, b) + self.assertNotEqual(a, b) + + + def test_nested_calls_not_equal(self): + a = call(x=1).foo().bar + b = call(x=2).foo().bar + self.assertEqual(a, a) + self.assertEqual(b, b) + self.assertNotEqual(a, b) + + def test_call_list(self): mock = MagicMock() mock(1) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index ac6eea3720b8..e46ef7bd5f0f 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -925,6 +925,57 @@ def test_mock_calls(self): call().__int__().call_list()) + def test_child_mock_call_equal(self): + m = Mock() + result = m() + result.wibble() + # parent looks like this: + self.assertEqual(m.mock_calls, [call(), call().wibble()]) + # but child should look like this: + self.assertEqual(result.mock_calls, [call.wibble()]) + + + def test_mock_call_not_equal_leaf(self): + m = Mock() + m.foo().something() + self.assertNotEqual(m.mock_calls[1], call.foo().different()) + self.assertEqual(m.mock_calls[0], call.foo()) + + + def test_mock_call_not_equal_non_leaf(self): + m = Mock() + m.foo().bar() + self.assertNotEqual(m.mock_calls[1], call.baz().bar()) + self.assertNotEqual(m.mock_calls[0], call.baz()) + + + def test_mock_call_not_equal_non_leaf_params_different(self): + m = Mock() + m.foo(x=1).bar() + # This isn't ideal, but there's no way to fix it without breaking backwards compatibility: + self.assertEqual(m.mock_calls[1], call.foo(x=2).bar()) + + + def test_mock_call_not_equal_non_leaf_attr(self): + m = Mock() + m.foo.bar() + self.assertNotEqual(m.mock_calls[0], call.baz.bar()) + + + def test_mock_call_not_equal_non_leaf_call_versus_attr(self): + m = Mock() + m.foo.bar() + self.assertNotEqual(m.mock_calls[0], call.foo().bar()) + + + def test_mock_call_repr(self): + m = Mock() + m.foo().bar().baz.bob() + self.assertEqual(repr(m.mock_calls[0]), 'call.foo()') + self.assertEqual(repr(m.mock_calls[1]), 'call.foo().bar()') + self.assertEqual(repr(m.mock_calls[2]), 'call.foo().bar().baz.bob()') + + def test_subclassing(self): class Subclass(Mock): pass diff --git a/Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst b/Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst new file mode 100644 index 000000000000..b95cc979573e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst @@ -0,0 +1,3 @@ +Recursively check arguments when testing for equality of +:class:`unittest.mock.call` objects and add note that tracking of parameters +used to create ancestors of mocks in ``mock_calls`` is not possible. From webhook-mailer at python.org Mon Dec 3 16:54:26 2018 From: webhook-mailer at python.org (Chris Withers) Date: Mon, 03 Dec 2018 21:54:26 -0000 Subject: [Python-checkins] bpo-35226: Fix equality for nested unittest.mock.call objects. (GH-10555) Message-ID: https://github.com/python/cpython/commit/67e6136a6d5c07141d4dba820c450a70db7aedd5 commit: 67e6136a6d5c07141d4dba820c450a70db7aedd5 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-03T21:54:22Z summary: bpo-35226: Fix equality for nested unittest.mock.call objects. (GH-10555) Also refactor the call recording implementation and add some notes about its limitations. (cherry picked from commit 8ca0fa9d2f4de6e69f0902790432e0ab2f37ba68) Co-authored-by: Chris Withers files: A Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst M Doc/library/unittest.mock-examples.rst M Doc/library/unittest.mock.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testhelpers.py M Lib/unittest/test/testmock/testmock.py diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst index 05f33740d752..cfba72795cc6 100644 --- a/Doc/library/unittest.mock-examples.rst +++ b/Doc/library/unittest.mock-examples.rst @@ -153,6 +153,15 @@ You use the :data:`call` object to construct lists for comparing with >>> mock.mock_calls == expected True +However, parameters to calls that return mocks are not recorded, which means it is not +possible to track nested calls where the parameters used to create ancestors are important: + + >>> m = Mock() + >>> m.factory(important=True).deliver() + + >>> m.mock_calls[-1] == call.factory(important=False).deliver() + True + Setting Return Values and Attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index 6841ef8ecc2b..01f5a6454cf7 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -680,6 +680,19 @@ the *new_callable* argument to :func:`patch`. unpacked as tuples to get at the individual arguments. See :ref:`calls as tuples `. + .. note:: + + The way :attr:`mock_calls` are recorded means that where nested + calls are made, the parameters of ancestor calls are not recorded + and so will always compare equal: + + >>> mock = MagicMock() + >>> mock.top(a=3).bottom() + + >>> mock.mock_calls + [call.top(a=3), call.top().bottom()] + >>> mock.mock_calls[-1] == call.top(a=-1).bottom() + True .. attribute:: __class__ diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 4bb0c32c745b..a8f28cefc7a9 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -943,46 +943,51 @@ def _mock_call(_mock_self, *args, **kwargs): self = _mock_self self.called = True self.call_count += 1 - _new_name = self._mock_new_name - _new_parent = self._mock_new_parent + # handle call_args _call = _Call((args, kwargs), two=True) self.call_args = _call self.call_args_list.append(_call) - self.mock_calls.append(_Call(('', args, kwargs))) seen = set() - skip_next_dot = _new_name == '()' + + # initial stuff for method_calls: do_method_calls = self._mock_parent is not None - name = self._mock_name - while _new_parent is not None: - this_mock_call = _Call((_new_name, args, kwargs)) - if _new_parent._mock_new_name: - dot = '.' - if skip_next_dot: - dot = '' + method_call_name = self._mock_name - skip_next_dot = False - if _new_parent._mock_new_name == '()': - skip_next_dot = True + # initial stuff for mock_calls: + mock_call_name = self._mock_new_name + is_a_call = mock_call_name == '()' + self.mock_calls.append(_Call(('', args, kwargs))) - _new_name = _new_parent._mock_new_name + dot + _new_name + # follow up the chain of mocks: + _new_parent = self._mock_new_parent + while _new_parent is not None: + # handle method_calls: if do_method_calls: - if _new_name == name: - this_method_call = this_mock_call - else: - this_method_call = _Call((name, args, kwargs)) - _new_parent.method_calls.append(this_method_call) - + _new_parent.method_calls.append(_Call((method_call_name, args, kwargs))) do_method_calls = _new_parent._mock_parent is not None if do_method_calls: - name = _new_parent._mock_name + '.' + name + method_call_name = _new_parent._mock_name + '.' + method_call_name + # handle mock_calls: + this_mock_call = _Call((mock_call_name, args, kwargs)) _new_parent.mock_calls.append(this_mock_call) + + if _new_parent._mock_new_name: + if is_a_call: + dot = '' + else: + dot = '.' + is_a_call = _new_parent._mock_new_name == '()' + mock_call_name = _new_parent._mock_new_name + dot + mock_call_name + + # follow the parental chain: _new_parent = _new_parent._mock_new_parent - # use ids here so as not to call __hash__ on the mocks + # check we're not in an infinite loop: + # ( use ids here so as not to call __hash__ on the mocks) _new_parent_id = id(_new_parent) if _new_parent_id in seen: break @@ -2018,6 +2023,10 @@ def __eq__(self, other): else: self_name, self_args, self_kwargs = self + if (getattr(self, 'parent', None) and getattr(other, 'parent', None) + and self.parent != other.parent): + return False + other_name = '' if len_other == 0: other_args, other_kwargs = (), {} diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 7919482ae99c..0a4289086040 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -269,6 +269,22 @@ def test_extended_call(self): self.assertEqual(mock.mock_calls, last_call.call_list()) + def test_extended_not_equal(self): + a = call(x=1).foo + b = call(x=2).foo + self.assertEqual(a, a) + self.assertEqual(b, b) + self.assertNotEqual(a, b) + + + def test_nested_calls_not_equal(self): + a = call(x=1).foo().bar + b = call(x=2).foo().bar + self.assertEqual(a, a) + self.assertEqual(b, b) + self.assertNotEqual(a, b) + + def test_call_list(self): mock = MagicMock() mock(1) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 4601136eff9c..c5c8f5ccbd81 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -916,6 +916,57 @@ def test_mock_calls(self): call().__int__().call_list()) + def test_child_mock_call_equal(self): + m = Mock() + result = m() + result.wibble() + # parent looks like this: + self.assertEqual(m.mock_calls, [call(), call().wibble()]) + # but child should look like this: + self.assertEqual(result.mock_calls, [call.wibble()]) + + + def test_mock_call_not_equal_leaf(self): + m = Mock() + m.foo().something() + self.assertNotEqual(m.mock_calls[1], call.foo().different()) + self.assertEqual(m.mock_calls[0], call.foo()) + + + def test_mock_call_not_equal_non_leaf(self): + m = Mock() + m.foo().bar() + self.assertNotEqual(m.mock_calls[1], call.baz().bar()) + self.assertNotEqual(m.mock_calls[0], call.baz()) + + + def test_mock_call_not_equal_non_leaf_params_different(self): + m = Mock() + m.foo(x=1).bar() + # This isn't ideal, but there's no way to fix it without breaking backwards compatibility: + self.assertEqual(m.mock_calls[1], call.foo(x=2).bar()) + + + def test_mock_call_not_equal_non_leaf_attr(self): + m = Mock() + m.foo.bar() + self.assertNotEqual(m.mock_calls[0], call.baz.bar()) + + + def test_mock_call_not_equal_non_leaf_call_versus_attr(self): + m = Mock() + m.foo.bar() + self.assertNotEqual(m.mock_calls[0], call.foo().bar()) + + + def test_mock_call_repr(self): + m = Mock() + m.foo().bar().baz.bob() + self.assertEqual(repr(m.mock_calls[0]), 'call.foo()') + self.assertEqual(repr(m.mock_calls[1]), 'call.foo().bar()') + self.assertEqual(repr(m.mock_calls[2]), 'call.foo().bar().baz.bob()') + + def test_subclassing(self): class Subclass(Mock): pass diff --git a/Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst b/Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst new file mode 100644 index 000000000000..b95cc979573e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst @@ -0,0 +1,3 @@ +Recursively check arguments when testing for equality of +:class:`unittest.mock.call` objects and add note that tracking of parameters +used to create ancestors of mocks in ``mock_calls`` is not possible. From webhook-mailer at python.org Mon Dec 3 16:54:48 2018 From: webhook-mailer at python.org (Chris Withers) Date: Mon, 03 Dec 2018 21:54:48 -0000 Subject: [Python-checkins] bpo-35226: Fix equality for nested unittest.mock.call objects. (GH-10555) Message-ID: https://github.com/python/cpython/commit/e8f9e4785caeef8a68bb7859280e91a4cb424b79 commit: e8f9e4785caeef8a68bb7859280e91a4cb424b79 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-03T21:54:44Z summary: bpo-35226: Fix equality for nested unittest.mock.call objects. (GH-10555) Also refactor the call recording imolementation and add some notes about its limitations. (cherry picked from commit 8ca0fa9d2f4de6e69f0902790432e0ab2f37ba68) Co-authored-by: Chris Withers files: A Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst M Doc/library/unittest.mock-examples.rst M Doc/library/unittest.mock.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testhelpers.py M Lib/unittest/test/testmock/testmock.py diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst index 65dee7c0eb2d..00b9bc166a7f 100644 --- a/Doc/library/unittest.mock-examples.rst +++ b/Doc/library/unittest.mock-examples.rst @@ -153,6 +153,15 @@ You use the :data:`call` object to construct lists for comparing with >>> mock.mock_calls == expected True +However, parameters to calls that return mocks are not recorded, which means it is not +possible to track nested calls where the parameters used to create ancestors are important: + + >>> m = Mock() + >>> m.factory(important=True).deliver() + + >>> m.mock_calls[-1] == call.factory(important=False).deliver() + True + Setting Return Values and Attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index 91633b505f56..f1b25958a670 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -680,6 +680,19 @@ the *new_callable* argument to :func:`patch`. unpacked as tuples to get at the individual arguments. See :ref:`calls as tuples `. + .. note:: + + The way :attr:`mock_calls` are recorded means that where nested + calls are made, the parameters of ancestor calls are not recorded + and so will always compare equal: + + >>> mock = MagicMock() + >>> mock.top(a=3).bottom() + + >>> mock.mock_calls + [call.top(a=3), call.top().bottom()] + >>> mock.mock_calls[-1] == call.top(a=-1).bottom() + True .. attribute:: __class__ diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 6ba186fb6631..69a08ece919b 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -955,46 +955,51 @@ def _mock_call(_mock_self, *args, **kwargs): self = _mock_self self.called = True self.call_count += 1 - _new_name = self._mock_new_name - _new_parent = self._mock_new_parent + # handle call_args _call = _Call((args, kwargs), two=True) self.call_args = _call self.call_args_list.append(_call) - self.mock_calls.append(_Call(('', args, kwargs))) seen = set() - skip_next_dot = _new_name == '()' + + # initial stuff for method_calls: do_method_calls = self._mock_parent is not None - name = self._mock_name - while _new_parent is not None: - this_mock_call = _Call((_new_name, args, kwargs)) - if _new_parent._mock_new_name: - dot = '.' - if skip_next_dot: - dot = '' + method_call_name = self._mock_name - skip_next_dot = False - if _new_parent._mock_new_name == '()': - skip_next_dot = True + # initial stuff for mock_calls: + mock_call_name = self._mock_new_name + is_a_call = mock_call_name == '()' + self.mock_calls.append(_Call(('', args, kwargs))) - _new_name = _new_parent._mock_new_name + dot + _new_name + # follow up the chain of mocks: + _new_parent = self._mock_new_parent + while _new_parent is not None: + # handle method_calls: if do_method_calls: - if _new_name == name: - this_method_call = this_mock_call - else: - this_method_call = _Call((name, args, kwargs)) - _new_parent.method_calls.append(this_method_call) - + _new_parent.method_calls.append(_Call((method_call_name, args, kwargs))) do_method_calls = _new_parent._mock_parent is not None if do_method_calls: - name = _new_parent._mock_name + '.' + name + method_call_name = _new_parent._mock_name + '.' + method_call_name + # handle mock_calls: + this_mock_call = _Call((mock_call_name, args, kwargs)) _new_parent.mock_calls.append(this_mock_call) + + if _new_parent._mock_new_name: + if is_a_call: + dot = '' + else: + dot = '.' + is_a_call = _new_parent._mock_new_name == '()' + mock_call_name = _new_parent._mock_new_name + dot + mock_call_name + + # follow the parental chain: _new_parent = _new_parent._mock_new_parent - # use ids here so as not to call __hash__ on the mocks + # check we're not in an infinite loop: + # ( use ids here so as not to call __hash__ on the mocks) _new_parent_id = id(_new_parent) if _new_parent_id in seen: break @@ -2030,6 +2035,10 @@ def __eq__(self, other): else: self_name, self_args, self_kwargs = self + if (getattr(self, 'parent', None) and getattr(other, 'parent', None) + and self.parent != other.parent): + return False + other_name = '' if len_other == 0: other_args, other_kwargs = (), {} diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 9edebf551660..9388311e3e25 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -270,6 +270,22 @@ def test_extended_call(self): self.assertEqual(mock.mock_calls, last_call.call_list()) + def test_extended_not_equal(self): + a = call(x=1).foo + b = call(x=2).foo + self.assertEqual(a, a) + self.assertEqual(b, b) + self.assertNotEqual(a, b) + + + def test_nested_calls_not_equal(self): + a = call(x=1).foo().bar + b = call(x=2).foo().bar + self.assertEqual(a, a) + self.assertEqual(b, b) + self.assertNotEqual(a, b) + + def test_call_list(self): mock = MagicMock() mock(1) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index cc45f02409ef..fa2ac34fc3d1 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -916,6 +916,57 @@ def test_mock_calls(self): call().__int__().call_list()) + def test_child_mock_call_equal(self): + m = Mock() + result = m() + result.wibble() + # parent looks like this: + self.assertEqual(m.mock_calls, [call(), call().wibble()]) + # but child should look like this: + self.assertEqual(result.mock_calls, [call.wibble()]) + + + def test_mock_call_not_equal_leaf(self): + m = Mock() + m.foo().something() + self.assertNotEqual(m.mock_calls[1], call.foo().different()) + self.assertEqual(m.mock_calls[0], call.foo()) + + + def test_mock_call_not_equal_non_leaf(self): + m = Mock() + m.foo().bar() + self.assertNotEqual(m.mock_calls[1], call.baz().bar()) + self.assertNotEqual(m.mock_calls[0], call.baz()) + + + def test_mock_call_not_equal_non_leaf_params_different(self): + m = Mock() + m.foo(x=1).bar() + # This isn't ideal, but there's no way to fix it without breaking backwards compatibility: + self.assertEqual(m.mock_calls[1], call.foo(x=2).bar()) + + + def test_mock_call_not_equal_non_leaf_attr(self): + m = Mock() + m.foo.bar() + self.assertNotEqual(m.mock_calls[0], call.baz.bar()) + + + def test_mock_call_not_equal_non_leaf_call_versus_attr(self): + m = Mock() + m.foo.bar() + self.assertNotEqual(m.mock_calls[0], call.foo().bar()) + + + def test_mock_call_repr(self): + m = Mock() + m.foo().bar().baz.bob() + self.assertEqual(repr(m.mock_calls[0]), 'call.foo()') + self.assertEqual(repr(m.mock_calls[1]), 'call.foo().bar()') + self.assertEqual(repr(m.mock_calls[2]), 'call.foo().bar().baz.bob()') + + def test_subclassing(self): class Subclass(Mock): pass diff --git a/Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst b/Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst new file mode 100644 index 000000000000..b95cc979573e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-11-15-07-14-32.bpo-35226.wJPEEe.rst @@ -0,0 +1,3 @@ +Recursively check arguments when testing for equality of +:class:`unittest.mock.call` objects and add note that tracking of parameters +used to create ancestors of mocks in ``mock_calls`` is not possible. From webhook-mailer at python.org Mon Dec 3 18:09:07 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 03 Dec 2018 23:09:07 -0000 Subject: [Python-checkins] [3.7] bpo-35373: Fix PyInit_timezone() error handling (GH-10864) Message-ID: https://github.com/python/cpython/commit/5eb78c75128187a36d8e983027632fa51cc2ff4d commit: 5eb78c75128187a36d8e983027632fa51cc2ff4d branch: 3.7 author: Victor Stinner committer: GitHub date: 2018-12-04T00:09:02+01:00 summary: [3.7] bpo-35373: Fix PyInit_timezone() error handling (GH-10864) * bpo-35373: Fix PyInit_timezone() error handling PyInit_timezone() now returns -1 at exit if an exception is raised. Check also explicitly PyUnicode_DecodeLocale() and Py_BuildValue() errors. * bpo-35373: Fix PyInit_time() error handling (GH-10865) * PyInit_time() now returns NULL if an exception is raised. * Rename PyInit_timezone() to init_timezone(). "PyInit_" prefix is a special prefix for function initializing a module. init_timezone() doesn't initialize a module and the function is not exported. (cherry picked from commit 3bb150d8148e3cc08418077a58f43e064b9fde61) files: M Modules/timemodule.c diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 6cf9e7c641e3..13a174a4ea87 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -994,7 +994,7 @@ of the timezone or altzone attributes on the time module."); #endif /* HAVE_MKTIME */ #ifdef HAVE_WORKING_TZSET -static int PyInit_timezone(PyObject *module); +static int init_timezone(PyObject *module); static PyObject * time_tzset(PyObject *self, PyObject *unused) @@ -1009,7 +1009,7 @@ time_tzset(PyObject *self, PyObject *unused) tzset(); /* Reset timezone, altzone, daylight and tzname */ - if (PyInit_timezone(m) < 0) { + if (init_timezone(m) < 0) { return NULL; } Py_DECREF(m); @@ -1524,7 +1524,7 @@ get_gmtoff(time_t t, struct tm *p) #endif /* !defined(HAVE_TZNAME) || defined(__GLIBC__) || defined(__CYGWIN__) */ static int -PyInit_timezone(PyObject *m) +init_timezone(PyObject *m) { assert(!PyErr_Occurred()); @@ -1555,8 +1555,19 @@ PyInit_timezone(PyObject *m) #endif PyModule_AddIntConstant(m, "daylight", daylight); otz0 = PyUnicode_DecodeLocale(tzname[0], "surrogateescape"); + if (otz0 == NULL) { + return -1; + } otz1 = PyUnicode_DecodeLocale(tzname[1], "surrogateescape"); - PyModule_AddObject(m, "tzname", Py_BuildValue("(NN)", otz0, otz1)); + if (otz1 == NULL) { + Py_DECREF(otz0); + return -1; + } + PyObject *tzname_obj = Py_BuildValue("(NN)", otz0, otz1); + if (tzname_obj == NULL) { + return -1; + } + PyModule_AddObject(m, "tzname", tzname_obj); #else /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ { #define YEAR ((time_t)((365 * 24 + 6) * 3600)) @@ -1615,6 +1626,10 @@ PyInit_timezone(PyObject *m) Py_BuildValue("(zz)", _tzname[0], _tzname[1])); #endif /* __CYGWIN__ */ #endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ + + if (PyErr_Occurred()) { + return -1; + } return 0; } @@ -1716,7 +1731,7 @@ PyInit_time(void) return NULL; /* Set, or reset, module variables like time.timezone */ - if (PyInit_timezone(m) < 0) { + if (init_timezone(m) < 0) { return NULL; } @@ -1761,6 +1776,10 @@ PyInit_time(void) PyModule_AddIntConstant(m, "_STRUCT_TM_ITEMS", 11); PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType); initialized = 1; + + if (PyErr_Occurred()) { + return NULL; + } return m; } From webhook-mailer at python.org Mon Dec 3 18:22:39 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 03 Dec 2018 23:22:39 -0000 Subject: [Python-checkins] [3.7] bpo-35373: Fix PyInit_timezone() error handling (GH-10864) Message-ID: https://github.com/python/cpython/commit/f455353bc0d195e092f09fec92ed16e9be02b7b1 commit: f455353bc0d195e092f09fec92ed16e9be02b7b1 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-03T15:22:35-08:00 summary: [3.7] bpo-35373: Fix PyInit_timezone() error handling (GH-10864) * bpo-35373: Fix PyInit_timezone() error handling PyInit_timezone() now returns -1 at exit if an exception is raised. Check also explicitly PyUnicode_DecodeLocale() and Py_BuildValue() errors. * bpo-35373: Fix PyInit_time() error handling (GH-10865) * PyInit_time() now returns NULL if an exception is raised. * Rename PyInit_timezone() to init_timezone(). "PyInit_" prefix is a special prefix for function initializing a module. init_timezone() doesn't initialize a module and the function is not exported. (cherry picked from commit 3bb150d8148e3cc08418077a58f43e064b9fde61) (cherry picked from commit 5eb78c75128187a36d8e983027632fa51cc2ff4d) Co-authored-by: Victor Stinner files: M Modules/timemodule.c diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 0bc32083e006..a963bb1dd154 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -860,7 +860,7 @@ of the timezone or altzone attributes on the time module."); #endif /* HAVE_MKTIME */ #ifdef HAVE_WORKING_TZSET -static int PyInit_timezone(PyObject *module); +static int init_timezone(PyObject *module); static PyObject * time_tzset(PyObject *self, PyObject *unused) @@ -875,7 +875,7 @@ time_tzset(PyObject *self, PyObject *unused) tzset(); /* Reset timezone, altzone, daylight and tzname */ - if (PyInit_timezone(m) < 0) { + if (init_timezone(m) < 0) { return NULL; } Py_DECREF(m); @@ -1187,7 +1187,7 @@ get_gmtoff(time_t t, struct tm *p) } static int -PyInit_timezone(PyObject *m) +init_timezone(PyObject *m) { assert(!PyErr_Occurred()); @@ -1218,8 +1218,19 @@ PyInit_timezone(PyObject *m) #endif PyModule_AddIntConstant(m, "daylight", daylight); otz0 = PyUnicode_DecodeLocale(tzname[0], "surrogateescape"); + if (otz0 == NULL) { + return -1; + } otz1 = PyUnicode_DecodeLocale(tzname[1], "surrogateescape"); - PyModule_AddObject(m, "tzname", Py_BuildValue("(NN)", otz0, otz1)); + if (otz1 == NULL) { + Py_DECREF(otz0); + return -1; + } + PyObject *tzname_obj = Py_BuildValue("(NN)", otz0, otz1); + if (tzname_obj == NULL) { + return -1; + } + PyModule_AddObject(m, "tzname", tzname_obj); #else /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ { #define YEAR ((time_t)((365 * 24 + 6) * 3600)) @@ -1278,6 +1289,10 @@ PyInit_timezone(PyObject *m) Py_BuildValue("(zz)", _tzname[0], _tzname[1])); #endif /* __CYGWIN__ */ #endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ + + if (PyErr_Occurred()) { + return -1; + } return 0; } @@ -1366,7 +1381,7 @@ PyInit_time(void) return NULL; /* Set, or reset, module variables like time.timezone */ - if (PyInit_timezone(m) < 0) { + if (init_timezone(m) < 0) { return NULL; } @@ -1402,6 +1417,10 @@ PyInit_time(void) PyModule_AddIntConstant(m, "_STRUCT_TM_ITEMS", 11); PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType); initialized = 1; + + if (PyErr_Occurred()) { + return NULL; + } return m; } From webhook-mailer at python.org Tue Dec 4 02:31:20 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 04 Dec 2018 07:31:20 -0000 Subject: [Python-checkins] bpo-35395: fix typos in asyncio eventloop documentation (GH-10880) Message-ID: https://github.com/python/cpython/commit/17473347942353946fe455f797a2197cb89c1090 commit: 17473347942353946fe455f797a2197cb89c1090 branch: master author: Naglis committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-03T23:31:15-08:00 summary: bpo-35395: fix typos in asyncio eventloop documentation (GH-10880) Fixes `loop.add_writer` and `loop.add_signal_handler` method documentation to correctly reference the callback parameter from method signature. https://bugs.python.org/issue35395 files: M Doc/library/asyncio-eventloop.rst diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index d24413a4a7fb..da2339132a40 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -756,7 +756,7 @@ Watching file descriptors writing. Use :func:`functools.partial` :ref:`to pass keyword arguments - ` to *func*. + ` to *callback*. .. method:: loop.remove_writer(fd) @@ -970,7 +970,7 @@ Unix signals Raise :exc:`RuntimeError` if there is a problem setting up the handler. Use :func:`functools.partial` :ref:`to pass keyword arguments - ` to *func*. + ` to *callback*. .. method:: loop.remove_signal_handler(sig) From webhook-mailer at python.org Tue Dec 4 02:36:35 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 04 Dec 2018 07:36:35 -0000 Subject: [Python-checkins] bpo-35395: fix typos in asyncio eventloop documentation (GH-10880) Message-ID: https://github.com/python/cpython/commit/6627d3ae1e151095fda4ec2592c7cfb759f23669 commit: 6627d3ae1e151095fda4ec2592c7cfb759f23669 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-03T23:36:31-08:00 summary: bpo-35395: fix typos in asyncio eventloop documentation (GH-10880) Fixes `loop.add_writer` and `loop.add_signal_handler` method documentation to correctly reference the callback parameter from method signature. https://bugs.python.org/issue35395 (cherry picked from commit 17473347942353946fe455f797a2197cb89c1090) Co-authored-by: Naglis files: M Doc/library/asyncio-eventloop.rst diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 8215198ce91f..647b7fc5e7a5 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -750,7 +750,7 @@ Watching file descriptors writing. Use :func:`functools.partial` :ref:`to pass keyword arguments - ` to *func*. + ` to *callback*. .. method:: loop.remove_writer(fd) @@ -964,7 +964,7 @@ Unix signals Raise :exc:`RuntimeError` if there is a problem setting up the handler. Use :func:`functools.partial` :ref:`to pass keyword arguments - ` to *func*. + ` to *callback*. .. method:: loop.remove_signal_handler(sig) From webhook-mailer at python.org Tue Dec 4 03:13:43 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 04 Dec 2018 08:13:43 -0000 Subject: [Python-checkins] Add comments regarding speed/space/entropy trade-offs (GH-10885) Message-ID: https://github.com/python/cpython/commit/7fc633f5a56d9e672cd24133e2e1376347abac6c commit: 7fc633f5a56d9e672cd24133e2e1376347abac6c branch: master author: Raymond Hettinger committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-04T00:13:38-08:00 summary: Add comments regarding speed/space/entropy trade-offs (GH-10885) files: M Lib/random.py diff --git a/Lib/random.py b/Lib/random.py index 4b51b6696bfc..a7a86070c0a9 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -333,6 +333,19 @@ def sample(self, population, k): # preferred since the list takes less space than the # set and it doesn't suffer from frequent reselections. + # The number of calls to _randbelow() is kept at or near k, the + # theoretical minimum. This is important because running time + # is dominated by _randbelow() and because it extracts the + # least entropy from the underlying random number generators. + + # Memory requirements are kept to the smaller of a k-length + # set or an n-length list. + + # There are other sampling algorithms that do not require + # auxiliary memory, but they were rejected because they made + # too many calls to _randbelow(), making them slower and + # causing them to eat more entropy than necessary. + if isinstance(population, _Set): population = tuple(population) if not isinstance(population, _Sequence): From webhook-mailer at python.org Tue Dec 4 03:25:54 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 04 Dec 2018 08:25:54 -0000 Subject: [Python-checkins] bpo-35365: Use a wchar_t* buffer in the code page decoder. (GH-10837) Message-ID: https://github.com/python/cpython/commit/eeb719eac6347f5b6e85389aa13a386024766806 commit: eeb719eac6347f5b6e85389aa13a386024766806 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-04T10:25:50+02:00 summary: bpo-35365: Use a wchar_t* buffer in the code page decoder. (GH-10837) files: M Objects/unicodeobject.c diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 1351eece8e92..d0f0358cfc69 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -4059,6 +4059,21 @@ make_decode_exception(PyObject **exceptionObject, } #ifdef MS_WINDOWS +static int +widechar_resize(wchar_t **buf, Py_ssize_t *size, Py_ssize_t newsize) +{ + if (newsize > *size) { + wchar_t *newbuf = *buf; + if (PyMem_Resize(newbuf, wchar_t, newsize) == NULL) { + PyErr_NoMemory(); + return -1; + } + *buf = newbuf; + } + *size = newsize; + return 0; +} + /* error handling callback helper: build arguments, call the callback and check the arguments, if no exception occurred, copy the replacement to the output @@ -4072,7 +4087,7 @@ unicode_decode_call_errorhandler_wchar( const char *encoding, const char *reason, const char **input, const char **inend, Py_ssize_t *startinpos, Py_ssize_t *endinpos, PyObject **exceptionObject, const char **inptr, - PyObject **output, Py_ssize_t *outpos) + wchar_t **buf, Py_ssize_t *bufsize, Py_ssize_t *outpos) { static const char *argparse = "Un;decoding error handler must return (str, int) tuple"; @@ -4086,9 +4101,6 @@ unicode_decode_call_errorhandler_wchar( wchar_t *repwstr; Py_ssize_t repwlen; - assert (_PyUnicode_KIND(*output) == PyUnicode_WCHAR_KIND); - outsize = _PyUnicode_WSTR_LENGTH(*output); - if (*errorHandler == NULL) { *errorHandler = PyCodec_LookupError(errors); if (*errorHandler == NULL) @@ -4146,13 +4158,15 @@ unicode_decode_call_errorhandler_wchar( if (requiredsize > PY_SSIZE_T_MAX - (insize - newpos)) goto overflow; requiredsize += insize - newpos; + outsize = *bufsize; if (requiredsize > outsize) { if (outsize <= PY_SSIZE_T_MAX/2 && requiredsize < 2*outsize) requiredsize = 2*outsize; - if (unicode_resize(output, requiredsize) < 0) + if (widechar_resize(buf, bufsize, requiredsize) < 0) { goto onError; + } } - wcsncpy(_PyUnicode_WSTR(*output) + *outpos, repwstr, repwlen); + wcsncpy(*buf + *outpos, repwstr, repwlen); *outpos += repwlen; *endinpos = newpos; *inptr = *input + newpos; @@ -7146,7 +7160,8 @@ decode_code_page_flags(UINT code_page) */ static int decode_code_page_strict(UINT code_page, - PyObject **v, + wchar_t **buf, + Py_ssize_t *bufsize, const char *in, int insize) { @@ -7160,21 +7175,12 @@ decode_code_page_strict(UINT code_page, if (outsize <= 0) goto error; - if (*v == NULL) { - /* Create unicode object */ - /* FIXME: don't use _PyUnicode_New(), but allocate a wchar_t* buffer */ - *v = (PyObject*)_PyUnicode_New(outsize); - if (*v == NULL) - return -1; - out = PyUnicode_AS_UNICODE(*v); - } - else { - /* Extend unicode object */ - Py_ssize_t n = PyUnicode_GET_SIZE(*v); - if (unicode_resize(v, n + outsize) < 0) - return -1; - out = PyUnicode_AS_UNICODE(*v) + n; + /* Extend a wchar_t* buffer */ + Py_ssize_t n = *bufsize; /* Get the current length */ + if (widechar_resize(buf, bufsize, n + outsize) < 0) { + return -1; } + out = *buf + n; /* Do the conversion */ outsize = MultiByteToWideChar(code_page, flags, in, insize, out, outsize); @@ -7198,7 +7204,8 @@ decode_code_page_strict(UINT code_page, */ static int decode_code_page_errors(UINT code_page, - PyObject **v, + wchar_t **buf, + Py_ssize_t *bufsize, const char *in, const int size, const char *errors, int final) { @@ -7238,29 +7245,16 @@ decode_code_page_errors(UINT code_page, goto error; } - if (*v == NULL) { - /* Create unicode object */ - if (size > PY_SSIZE_T_MAX / (Py_ssize_t)Py_ARRAY_LENGTH(buffer)) { - PyErr_NoMemory(); - goto error; - } - /* FIXME: don't use _PyUnicode_New(), but allocate a wchar_t* buffer */ - *v = (PyObject*)_PyUnicode_New(size * Py_ARRAY_LENGTH(buffer)); - if (*v == NULL) - goto error; - out = PyUnicode_AS_UNICODE(*v); + /* Extend a wchar_t* buffer */ + Py_ssize_t n = *bufsize; /* Get the current length */ + if (size > (PY_SSIZE_T_MAX - n) / (Py_ssize_t)Py_ARRAY_LENGTH(buffer)) { + PyErr_NoMemory(); + goto error; } - else { - /* Extend unicode object */ - Py_ssize_t n = PyUnicode_GET_SIZE(*v); - if (size > (PY_SSIZE_T_MAX - n) / (Py_ssize_t)Py_ARRAY_LENGTH(buffer)) { - PyErr_NoMemory(); - goto error; - } - if (unicode_resize(v, n + size * Py_ARRAY_LENGTH(buffer)) < 0) - goto error; - out = PyUnicode_AS_UNICODE(*v) + n; + if (widechar_resize(buf, bufsize, n + size * Py_ARRAY_LENGTH(buffer)) < 0) { + goto error; } + out = *buf + n; /* Decode the byte string character per character */ while (in < endin) @@ -7295,16 +7289,16 @@ decode_code_page_errors(UINT code_page, startinpos = in - startin; endinpos = startinpos + 1; - outpos = out - PyUnicode_AS_UNICODE(*v); + outpos = out - *buf; if (unicode_decode_call_errorhandler_wchar( errors, &errorHandler, encoding, reason, &startin, &endin, &startinpos, &endinpos, &exc, &in, - v, &outpos)) + buf, bufsize, &outpos)) { goto error; } - out = PyUnicode_AS_UNICODE(*v) + outpos; + out = *buf + outpos; } else { in += insize; @@ -7313,14 +7307,9 @@ decode_code_page_errors(UINT code_page, } } - /* write a NUL character at the end */ - *out = 0; - - /* Extend unicode object */ - outsize = out - PyUnicode_AS_UNICODE(*v); - assert(outsize <= PyUnicode_WSTR_LENGTH(*v)); - if (unicode_resize(v, outsize) < 0) - goto error; + /* Shrink the buffer */ + assert(out - *buf <= *bufsize); + *bufsize = out - *buf; /* (in - startin) <= size and size is an int */ ret = Py_SAFE_DOWNCAST(in - startin, Py_ssize_t, int); @@ -7336,7 +7325,8 @@ decode_code_page_stateful(int code_page, const char *s, Py_ssize_t size, const char *errors, Py_ssize_t *consumed) { - PyObject *v = NULL; + wchar_t *buf = NULL; + Py_ssize_t bufsize = 0; int chunk_size, final, converted, done; if (code_page < 0) { @@ -7368,21 +7358,21 @@ decode_code_page_stateful(int code_page, } if (chunk_size == 0 && done) { - if (v != NULL) + if (buf != NULL) break; _Py_RETURN_UNICODE_EMPTY(); } - converted = decode_code_page_strict(code_page, &v, + converted = decode_code_page_strict(code_page, &buf, &bufsize, s, chunk_size); if (converted == -2) - converted = decode_code_page_errors(code_page, &v, + converted = decode_code_page_errors(code_page, &buf, &bufsize, s, chunk_size, errors, final); assert(converted != 0 || done); if (converted < 0) { - Py_XDECREF(v); + PyMem_Free(buf); return NULL; } @@ -7393,7 +7383,9 @@ decode_code_page_stateful(int code_page, size -= converted; } while (!done); - return unicode_result(v); + PyObject *v = PyUnicode_FromWideChar(buf, bufsize); + PyMem_Free(buf); + return v; } PyObject * From webhook-mailer at python.org Tue Dec 4 04:08:52 2018 From: webhook-mailer at python.org (Chris Withers) Date: Tue, 04 Dec 2018 09:08:52 -0000 Subject: [Python-checkins] bpo-35357: Add _mock_ prefix to name/parent/from_kall attributes of _Call/_MagicProxy. (#10873) Message-ID: https://github.com/python/cpython/commit/e63e617ebbe481c498bdf037a62e09f4f9f3963f commit: e63e617ebbe481c498bdf037a62e09f4f9f3963f branch: master author: Andrew Dunai committer: Chris Withers date: 2018-12-04T09:08:45Z summary: bpo-35357: Add _mock_ prefix to name/parent/from_kall attributes of _Call/_MagicProxy. (#10873) Fix minor typo in test function name. files: A Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testcallable.py M Lib/unittest/test/testmock/testmock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index d13f74982086..b75a1fa5cf1d 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2040,9 +2040,9 @@ def __new__(cls, value=(), name='', parent=None, two=False, def __init__(self, value=(), name=None, parent=None, two=False, from_kall=True): - self.name = name - self.parent = parent - self.from_kall = from_kall + self._mock_name = name + self._mock_parent = parent + self._mock_from_kall = from_kall def __eq__(self, other): @@ -2059,8 +2059,8 @@ def __eq__(self, other): else: self_name, self_args, self_kwargs = self - if (getattr(self, 'parent', None) and getattr(other, 'parent', None) - and self.parent != other.parent): + if (getattr(self, '_mock_parent', None) and getattr(other, '_mock_parent', None) + and self._mock_parent != other._mock_parent): return False other_name = '' @@ -2104,17 +2104,17 @@ def __eq__(self, other): def __call__(self, *args, **kwargs): - if self.name is None: + if self._mock_name is None: return _Call(('', args, kwargs), name='()') - name = self.name + '()' - return _Call((self.name, args, kwargs), name=name, parent=self) + name = self._mock_name + '()' + return _Call((self._mock_name, args, kwargs), name=name, parent=self) def __getattr__(self, attr): - if self.name is None: + if self._mock_name is None: return _Call(name=attr, from_kall=False) - name = '%s.%s' % (self.name, attr) + name = '%s.%s' % (self._mock_name, attr) return _Call(name=name, parent=self, from_kall=False) @@ -2125,8 +2125,8 @@ def index(self, *args, **kwargs): return self.__getattr__('index')(*args, **kwargs) def __repr__(self): - if not self.from_kall: - name = self.name or 'call' + if not self._mock_from_kall: + name = self._mock_name or 'call' if name.startswith('()'): name = 'call%s' % name return name @@ -2152,9 +2152,9 @@ def call_list(self): vals = [] thing = self while thing is not None: - if thing.from_kall: + if thing._mock_from_kall: vals.append(thing) - thing = thing.parent + thing = thing._mock_parent return _CallList(reversed(vals)) diff --git a/Lib/unittest/test/testmock/testcallable.py b/Lib/unittest/test/testmock/testcallable.py index af1ce7ebbae4..34474c4c816e 100644 --- a/Lib/unittest/test/testmock/testcallable.py +++ b/Lib/unittest/test/testmock/testcallable.py @@ -128,7 +128,7 @@ class Multi(SomeClass, Sub): result.foo.assert_called_once_with(3, 2, 1) - def test_create_autopsec(self): + def test_create_autospec(self): mock = create_autospec(X) instance = mock() self.assertRaises(TypeError, instance) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index e46ef7bd5f0f..0a638b7c2107 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -9,7 +9,7 @@ from unittest.mock import ( call, DEFAULT, patch, sentinel, MagicMock, Mock, NonCallableMock, - NonCallableMagicMock, _CallList, + NonCallableMagicMock, _Call, _CallList, create_autospec ) @@ -1665,6 +1665,20 @@ def test_class_assignable(self): self.assertIsInstance(mock, int) mock.foo + def test_name_attribute_of_call(self): + # bpo-35357: _Call should not disclose any attributes whose names + # may clash with popular ones (such as ".name") + self.assertIsNotNone(call.name) + self.assertEqual(type(call.name), _Call) + self.assertEqual(type(call.name().name), _Call) + + def test_parent_attribute_of_call(self): + # bpo-35357: _Call should not disclose any attributes whose names + # may clash with popular ones (such as ".parent") + self.assertIsNotNone(call.parent) + self.assertEqual(type(call.parent), _Call) + self.assertEqual(type(call.parent().parent), _Call) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst new file mode 100644 index 000000000000..1dade5baf4e0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst @@ -0,0 +1,4 @@ +Internal attributes' names of unittest.mock._Call and +unittest.mock.MagicProxy (name, parent & from_kall) are now prefixed with +_mock_ in order to prevent clashes with widely used object attributes. +Fixed minor typo in test function name. From solipsis at pitrou.net Tue Dec 4 04:16:12 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 04 Dec 2018 09:16:12 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=3 Message-ID: <20181204091612.1.87ED7302000F302E@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [-7, 8, 0] memory blocks, sum=1 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_fork leaked [-1, 2, -1] memory blocks, sum=0 test_multiprocessing_forkserver leaked [-2, 1, 0] memory blocks, sum=-1 test_multiprocessing_spawn leaked [1, 0, -2] memory blocks, sum=-1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflog4bQT1M', '--timeout', '7200'] From webhook-mailer at python.org Tue Dec 4 04:34:39 2018 From: webhook-mailer at python.org (Chris Withers) Date: Tue, 04 Dec 2018 09:34:39 -0000 Subject: [Python-checkins] bpo-35357: Add _mock_ prefix to name/parent/from_kall attributes of _Call/_MagicProxy. (GH-10873) (#10887) Message-ID: https://github.com/python/cpython/commit/12735c14134082584b899308af8dd8fcc9f15696 commit: 12735c14134082584b899308af8dd8fcc9f15696 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-04T09:34:34Z summary: bpo-35357: Add _mock_ prefix to name/parent/from_kall attributes of _Call/_MagicProxy. (GH-10873) (#10887) Fix minor typo in test function name. (cherry picked from commit e63e617ebbe481c498bdf037a62e09f4f9f3963f) Co-authored-by: Andrew Dunai files: A Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testcallable.py M Lib/unittest/test/testmock/testmock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 69a08ece919b..0a6535d0fc44 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2016,9 +2016,9 @@ def __new__(cls, value=(), name='', parent=None, two=False, def __init__(self, value=(), name=None, parent=None, two=False, from_kall=True): - self.name = name - self.parent = parent - self.from_kall = from_kall + self._mock_name = name + self._mock_parent = parent + self._mock_from_kall = from_kall def __eq__(self, other): @@ -2035,8 +2035,8 @@ def __eq__(self, other): else: self_name, self_args, self_kwargs = self - if (getattr(self, 'parent', None) and getattr(other, 'parent', None) - and self.parent != other.parent): + if (getattr(self, '_mock_parent', None) and getattr(other, '_mock_parent', None) + and self._mock_parent != other._mock_parent): return False other_name = '' @@ -2080,17 +2080,17 @@ def __eq__(self, other): def __call__(self, *args, **kwargs): - if self.name is None: + if self._mock_name is None: return _Call(('', args, kwargs), name='()') - name = self.name + '()' - return _Call((self.name, args, kwargs), name=name, parent=self) + name = self._mock_name + '()' + return _Call((self._mock_name, args, kwargs), name=name, parent=self) def __getattr__(self, attr): - if self.name is None: + if self._mock_name is None: return _Call(name=attr, from_kall=False) - name = '%s.%s' % (self.name, attr) + name = '%s.%s' % (self._mock_name, attr) return _Call(name=name, parent=self, from_kall=False) @@ -2101,8 +2101,8 @@ def index(self, *args, **kwargs): return self.__getattr__('index')(*args, **kwargs) def __repr__(self): - if not self.from_kall: - name = self.name or 'call' + if not self._mock_from_kall: + name = self._mock_name or 'call' if name.startswith('()'): name = 'call%s' % name return name @@ -2128,9 +2128,9 @@ def call_list(self): vals = [] thing = self while thing is not None: - if thing.from_kall: + if thing._mock_from_kall: vals.append(thing) - thing = thing.parent + thing = thing._mock_parent return _CallList(reversed(vals)) diff --git a/Lib/unittest/test/testmock/testcallable.py b/Lib/unittest/test/testmock/testcallable.py index af1ce7ebbae4..34474c4c816e 100644 --- a/Lib/unittest/test/testmock/testcallable.py +++ b/Lib/unittest/test/testmock/testcallable.py @@ -128,7 +128,7 @@ class Multi(SomeClass, Sub): result.foo.assert_called_once_with(3, 2, 1) - def test_create_autopsec(self): + def test_create_autospec(self): mock = create_autospec(X) instance = mock() self.assertRaises(TypeError, instance) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index fa2ac34fc3d1..401a01277b06 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -8,7 +8,7 @@ from unittest.mock import ( call, DEFAULT, patch, sentinel, MagicMock, Mock, NonCallableMock, - NonCallableMagicMock, _CallList, + NonCallableMagicMock, _Call, _CallList, create_autospec ) @@ -1635,6 +1635,20 @@ def test_class_assignable(self): self.assertIsInstance(mock, int) mock.foo + def test_name_attribute_of_call(self): + # bpo-35357: _Call should not disclose any attributes whose names + # may clash with popular ones (such as ".name") + self.assertIsNotNone(call.name) + self.assertEqual(type(call.name), _Call) + self.assertEqual(type(call.name().name), _Call) + + def test_parent_attribute_of_call(self): + # bpo-35357: _Call should not disclose any attributes whose names + # may clash with popular ones (such as ".parent") + self.assertIsNotNone(call.parent) + self.assertEqual(type(call.parent), _Call) + self.assertEqual(type(call.parent().parent), _Call) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst new file mode 100644 index 000000000000..1dade5baf4e0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst @@ -0,0 +1,4 @@ +Internal attributes' names of unittest.mock._Call and +unittest.mock.MagicProxy (name, parent & from_kall) are now prefixed with +_mock_ in order to prevent clashes with widely used object attributes. +Fixed minor typo in test function name. From webhook-mailer at python.org Tue Dec 4 04:34:51 2018 From: webhook-mailer at python.org (Chris Withers) Date: Tue, 04 Dec 2018 09:34:51 -0000 Subject: [Python-checkins] bpo-35357: Add _mock_ prefix to name/parent/from_kall attributes of _Call/_MagicProxy. (GH-10873) Message-ID: https://github.com/python/cpython/commit/70ca3fce9fe2bdd7bf97d5fe1299cfa5e32b3ad4 commit: 70ca3fce9fe2bdd7bf97d5fe1299cfa5e32b3ad4 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-04T09:34:48Z summary: bpo-35357: Add _mock_ prefix to name/parent/from_kall attributes of _Call/_MagicProxy. (GH-10873) Fix minor typo in test function name. (cherry picked from commit e63e617ebbe481c498bdf037a62e09f4f9f3963f) Co-authored-by: Andrew Dunai files: A Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testcallable.py M Lib/unittest/test/testmock/testmock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index a8f28cefc7a9..707ef0b734d8 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2004,9 +2004,9 @@ def __new__(cls, value=(), name='', parent=None, two=False, def __init__(self, value=(), name=None, parent=None, two=False, from_kall=True): - self.name = name - self.parent = parent - self.from_kall = from_kall + self._mock_name = name + self._mock_parent = parent + self._mock_from_kall = from_kall def __eq__(self, other): @@ -2023,8 +2023,8 @@ def __eq__(self, other): else: self_name, self_args, self_kwargs = self - if (getattr(self, 'parent', None) and getattr(other, 'parent', None) - and self.parent != other.parent): + if (getattr(self, '_mock_parent', None) and getattr(other, '_mock_parent', None) + and self._mock_parent != other._mock_parent): return False other_name = '' @@ -2068,17 +2068,17 @@ def __eq__(self, other): def __call__(self, *args, **kwargs): - if self.name is None: + if self._mock_name is None: return _Call(('', args, kwargs), name='()') - name = self.name + '()' - return _Call((self.name, args, kwargs), name=name, parent=self) + name = self._mock_name + '()' + return _Call((self._mock_name, args, kwargs), name=name, parent=self) def __getattr__(self, attr): - if self.name is None: + if self._mock_name is None: return _Call(name=attr, from_kall=False) - name = '%s.%s' % (self.name, attr) + name = '%s.%s' % (self._mock_name, attr) return _Call(name=name, parent=self, from_kall=False) @@ -2089,8 +2089,8 @@ def index(self, *args, **kwargs): return self.__getattr__('index')(*args, **kwargs) def __repr__(self): - if not self.from_kall: - name = self.name or 'call' + if not self._mock_from_kall: + name = self._mock_name or 'call' if name.startswith('()'): name = 'call%s' % name return name @@ -2116,9 +2116,9 @@ def call_list(self): vals = [] thing = self while thing is not None: - if thing.from_kall: + if thing._mock_from_kall: vals.append(thing) - thing = thing.parent + thing = thing._mock_parent return _CallList(reversed(vals)) diff --git a/Lib/unittest/test/testmock/testcallable.py b/Lib/unittest/test/testmock/testcallable.py index af1ce7ebbae4..34474c4c816e 100644 --- a/Lib/unittest/test/testmock/testcallable.py +++ b/Lib/unittest/test/testmock/testcallable.py @@ -128,7 +128,7 @@ class Multi(SomeClass, Sub): result.foo.assert_called_once_with(3, 2, 1) - def test_create_autopsec(self): + def test_create_autospec(self): mock = create_autospec(X) instance = mock() self.assertRaises(TypeError, instance) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index c5c8f5ccbd81..14b1ecc84b33 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -8,7 +8,7 @@ from unittest.mock import ( call, DEFAULT, patch, sentinel, MagicMock, Mock, NonCallableMock, - NonCallableMagicMock, _CallList, + NonCallableMagicMock, _Call, _CallList, create_autospec ) @@ -1625,6 +1625,20 @@ def test_class_assignable(self): self.assertIsInstance(mock, int) mock.foo + def test_name_attribute_of_call(self): + # bpo-35357: _Call should not disclose any attributes whose names + # may clash with popular ones (such as ".name") + self.assertIsNotNone(call.name) + self.assertEqual(type(call.name), _Call) + self.assertEqual(type(call.name().name), _Call) + + def test_parent_attribute_of_call(self): + # bpo-35357: _Call should not disclose any attributes whose names + # may clash with popular ones (such as ".parent") + self.assertIsNotNone(call.parent) + self.assertEqual(type(call.parent), _Call) + self.assertEqual(type(call.parent().parent), _Call) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst new file mode 100644 index 000000000000..1dade5baf4e0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-03-21-20-24.bpo-35357.rhhoiC.rst @@ -0,0 +1,4 @@ +Internal attributes' names of unittest.mock._Call and +unittest.mock.MagicProxy (name, parent & from_kall) are now prefixed with +_mock_ in order to prevent clashes with widely used object attributes. +Fixed minor typo in test function name. From webhook-mailer at python.org Tue Dec 4 05:03:00 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 04 Dec 2018 10:03:00 -0000 Subject: [Python-checkins] [2.7] bpo-25862: Fix several bugs in the _io module. (GH-8026) (GH-8033) Message-ID: https://github.com/python/cpython/commit/eab421bff954e4fb77516bfe6c98d30ced1412d0 commit: eab421bff954e4fb77516bfe6c98d30ced1412d0 branch: 2.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-04T12:02:48+02:00 summary: [2.7] bpo-25862: Fix several bugs in the _io module. (GH-8026) (GH-8033) They can be exposed when some C API calls fail due to lack of memory. * Failed Py_BuildValue() could cause an assertion error in the following TextIOWrapper.tell(). * initvalue could leak in StringIO.__getstate__() after failed PyDict_Copy(). (cherry picked from commit fdb5a50ef34f7951c3b01eb77b1359725a9ad670) files: M Modules/_io/stringio.c M Modules/_io/textio.c diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 83bb7acacbb8..c52ca27ad9d6 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -693,8 +693,10 @@ stringio_getstate(stringio *self) } else { dict = PyDict_Copy(self->dict); - if (dict == NULL) + if (dict == NULL) { + Py_DECREF(initvalue); return NULL; + } } state = Py_BuildValue("(OOnN)", initvalue, diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 825e84937c15..5501da4b3f5c 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1466,6 +1466,7 @@ textiowrapper_read_chunk(textio *self) /* At the snapshot point, len(dec_buffer) bytes before the read, the * next input to be decoded is dec_buffer + input_chunk. */ + PyObject *snapshot; PyObject *next_input = PyNumber_Add(dec_buffer, input_chunk); if (next_input == NULL) goto fail; @@ -1477,8 +1478,13 @@ textiowrapper_read_chunk(textio *self) Py_DECREF(next_input); goto fail; } + snapshot = Py_BuildValue("NN", dec_flags, next_input); + if (snapshot == NULL) { + dec_flags = NULL; + goto fail; + } + Py_XSETREF(self->snapshot, snapshot); Py_DECREF(dec_buffer); - Py_XSETREF(self->snapshot, Py_BuildValue("NN", dec_flags, next_input)); } Py_DECREF(input_chunk); @@ -2013,6 +2019,7 @@ textiowrapper_seek(textio *self, PyObject *args) int whence = 0; PyObject *res; int cmp; + PyObject *snapshot; CHECK_ATTACHED(self); @@ -2149,11 +2156,11 @@ textiowrapper_seek(textio *self, PyObject *args) goto fail; } - self->snapshot = Py_BuildValue("iN", cookie.dec_flags, input_chunk); - if (self->snapshot == NULL) { - Py_DECREF(input_chunk); + snapshot = Py_BuildValue("iN", cookie.dec_flags, input_chunk); + if (snapshot == NULL) { goto fail; } + Py_XSETREF(self->snapshot, snapshot); decoded = PyObject_CallMethod(self->decoder, "decode", "Oi", input_chunk, (int)cookie.need_eof); @@ -2171,9 +2178,10 @@ textiowrapper_seek(textio *self, PyObject *args) self->decoded_chars_used = cookie.chars_to_skip; } else { - self->snapshot = Py_BuildValue("is", cookie.dec_flags, ""); - if (self->snapshot == NULL) + snapshot = Py_BuildValue("is", cookie.dec_flags, ""); + if (snapshot == NULL) goto fail; + Py_XSETREF(self->snapshot, snapshot); } /* Finally, reset the encoder (merely useful for proper BOM handling) */ From webhook-mailer at python.org Tue Dec 4 05:38:11 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 04 Dec 2018 10:38:11 -0000 Subject: [Python-checkins] [2.7] bpo-16865: Support arrays >=2GB in ctypes. (GH-3006). (GH-7441) Message-ID: https://github.com/python/cpython/commit/93d7918f77278f973a4a106c1d01ad2d9805816d commit: 93d7918f77278f973a4a106c1d01ad2d9805816d branch: 2.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-04T12:38:07+02:00 summary: [2.7] bpo-16865: Support arrays >=2GB in ctypes. (GH-3006). (GH-7441) (cherry picked from commit 735abadd5bd91db4a9e6f4311969b0afacca0a1a) Co-Authored-By: Segev Finer files: A Misc/NEWS.d/next/Library/2017-09-29-16-40-38.bpo-16865.l-f6I_.rst M Lib/ctypes/test/test_arrays.py M Modules/_ctypes/_ctypes.c diff --git a/Lib/ctypes/test/test_arrays.py b/Lib/ctypes/test/test_arrays.py index 49aaab52b93b..53859a3e5e93 100644 --- a/Lib/ctypes/test/test_arrays.py +++ b/Lib/ctypes/test/test_arrays.py @@ -1,4 +1,6 @@ import unittest +from test.support import precisionbigmemtest, _2G +import sys from ctypes import * from ctypes.test import need_symbol @@ -132,5 +134,10 @@ class my_int(c_int): t2 = my_int * 1 self.assertIs(t1, t2) + @unittest.skipUnless(sys.maxsize > 2**32, 'requires 64bit platform') + @precisionbigmemtest(size=_2G, memuse=1, dry_run=False) + def test_large_array(self, size): + a = c_char * size + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2017-09-29-16-40-38.bpo-16865.l-f6I_.rst b/Misc/NEWS.d/next/Library/2017-09-29-16-40-38.bpo-16865.l-f6I_.rst new file mode 100644 index 000000000000..afaff736bf1c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-09-29-16-40-38.bpo-16865.l-f6I_.rst @@ -0,0 +1 @@ +Support arrays >=2GiB in :mod:`ctypes`. Patch by Segev Finer. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index fabbdf13e940..9b289af91be7 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1460,24 +1460,36 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyTypeObject *result; StgDictObject *stgdict; StgDictObject *itemdict; - PyObject *proto; + PyObject *proto, *length_attr; PyObject *typedict; - long length; - + Py_ssize_t length; Py_ssize_t itemsize, itemalign; typedict = PyTuple_GetItem(args, 2); if (!typedict) return NULL; - proto = PyDict_GetItemString(typedict, "_length_"); /* Borrowed ref */ - if (!proto || !PyInt_Check(proto)) { + length_attr = PyDict_GetItemString(typedict, "_length_"); /* Borrowed ref */ + if (!length_attr || !_PyAnyInt_Check(length_attr)) { PyErr_SetString(PyExc_AttributeError, "class must define a '_length_' attribute, " "which must be a positive integer"); return NULL; } - length = PyInt_AS_LONG(proto); + if (PyInt_Check(length_attr)) { + length = PyInt_AS_LONG(length_attr); + } + else { + assert(PyLong_Check(length_attr)); + length = PyLong_AsSsize_t(length_attr); + if (length == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + PyErr_SetString(PyExc_OverflowError, + "The '_length_' attribute is too large"); + } + return NULL; + } + } proto = PyDict_GetItemString(typedict, "_type_"); /* Borrowed ref */ if (!proto) { From webhook-mailer at python.org Tue Dec 4 09:54:07 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 04 Dec 2018 14:54:07 -0000 Subject: [Python-checkins] bpo-35351: Pass link time optimization flags to CFLAGS_NODIST (GH-10797) Message-ID: https://github.com/python/cpython/commit/f92c7aa1ae81efa475b5aecf66e4711ef0f52c4c commit: f92c7aa1ae81efa475b5aecf66e4711ef0f52c4c branch: master author: stratakis committer: Victor Stinner date: 2018-12-04T15:54:01+01:00 summary: bpo-35351: Pass link time optimization flags to CFLAGS_NODIST (GH-10797) When using link time optimizations, the -flto flag is passed to BASECFLAGS, which makes it propagate to distutils. Those flags should be reserved for the interpreter and the stdlib extension modules only, thus moving those flags to CFLAGS_NODIST. files: A Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst M configure M configure.ac diff --git a/Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst b/Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst new file mode 100644 index 000000000000..ee6c870b060f --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst @@ -0,0 +1,2 @@ +When building Python with clang and LTO, LTO flags are no longer passed into +CFLAGS to build third-party C extensions through distutils. diff --git a/configure b/configure index e9e33b3715d1..8d3336387c34 100755 --- a/configure +++ b/configure @@ -6626,7 +6626,7 @@ $as_echo "$as_me: llvm-ar found via xcrun: ${LLVM_AR}" >&6;} LTOFLAGS="$LTOFLAGS -g" fi - BASECFLAGS="$BASECFLAGS $LTOFLAGS" + CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" LDFLAGS="$LDFLAGS $LTOFLAGS" fi diff --git a/configure.ac b/configure.ac index 15d03ba38129..5c1c02192087 100644 --- a/configure.ac +++ b/configure.ac @@ -1357,7 +1357,7 @@ if test "$Py_LTO" = 'true' ; then LTOFLAGS="$LTOFLAGS -g" fi - BASECFLAGS="$BASECFLAGS $LTOFLAGS" + CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" LDFLAGS="$LDFLAGS $LTOFLAGS" fi From webhook-mailer at python.org Tue Dec 4 10:06:27 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 04 Dec 2018 15:06:27 -0000 Subject: [Python-checkins] bpo-35351: Pass link time optimization flags to CFLAGS_NODIST (GH-10797) Message-ID: https://github.com/python/cpython/commit/1751423686d05e3facdd6da2620202728e5d7917 commit: 1751423686d05e3facdd6da2620202728e5d7917 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-04T07:06:16-08:00 summary: bpo-35351: Pass link time optimization flags to CFLAGS_NODIST (GH-10797) When using link time optimizations, the -flto flag is passed to BASECFLAGS, which makes it propagate to distutils. Those flags should be reserved for the interpreter and the stdlib extension modules only, thus moving those flags to CFLAGS_NODIST. (cherry picked from commit f92c7aa1ae81efa475b5aecf66e4711ef0f52c4c) Co-authored-by: stratakis files: A Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst M configure M configure.ac diff --git a/Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst b/Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst new file mode 100644 index 000000000000..ee6c870b060f --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst @@ -0,0 +1,2 @@ +When building Python with clang and LTO, LTO flags are no longer passed into +CFLAGS to build third-party C extensions through distutils. diff --git a/configure b/configure index bccedd18b7f8..4714ae887a67 100755 --- a/configure +++ b/configure @@ -6670,7 +6670,7 @@ $as_echo "$as_me: llvm-ar found via xcrun: ${LLVM_AR}" >&6;} LTOFLAGS="$LTOFLAGS -g" fi - BASECFLAGS="$BASECFLAGS $LTOFLAGS" + CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" LDFLAGS="$LDFLAGS $LTOFLAGS" fi diff --git a/configure.ac b/configure.ac index 320671edada8..c35aea98e5b2 100644 --- a/configure.ac +++ b/configure.ac @@ -1394,7 +1394,7 @@ if test "$Py_LTO" = 'true' ; then LTOFLAGS="$LTOFLAGS -g" fi - BASECFLAGS="$BASECFLAGS $LTOFLAGS" + CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" LDFLAGS="$LDFLAGS $LTOFLAGS" fi From webhook-mailer at python.org Tue Dec 4 11:13:37 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 04 Dec 2018 16:13:37 -0000 Subject: [Python-checkins] bpo-35363, test_eintr: skip test_open() on macOS (GH-10896) Message-ID: https://github.com/python/cpython/commit/4752e65250bce60b97d5af702d586092d02fbf58 commit: 4752e65250bce60b97d5af702d586092d02fbf58 branch: master author: Victor Stinner committer: GitHub date: 2018-12-04T17:13:33+01:00 summary: bpo-35363, test_eintr: skip test_open() on macOS (GH-10896) files: M Lib/test/eintrdata/eintr_tester.py diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py index aa7cfd14d9f9..25c169bde500 100644 --- a/Lib/test/eintrdata/eintr_tester.py +++ b/Lib/test/eintrdata/eintr_tester.py @@ -349,16 +349,18 @@ def python_open(self, path): fp = open(path, 'w') fp.close() + @unittest.skipIf(sys.platform == "darwin", + "hangs under macOS; see bpo-25234, bpo-35363") def test_open(self): self._test_open("fp = open(path, 'r')\nfp.close()", self.python_open) - @unittest.skipIf(sys.platform == 'darwin', "hangs under OS X; see issue #25234") def os_open(self, path): fd = os.open(path, os.O_WRONLY) os.close(fd) - @unittest.skipIf(sys.platform == "darwin", "hangs under OS X; see issue #25234") + @unittest.skipIf(sys.platform == "darwin", + "hangs under macOS; see bpo-25234, bpo-35363") def test_os_open(self): self._test_open("fd = os.open(path, os.O_RDONLY)\nos.close(fd)", self.os_open) From webhook-mailer at python.org Tue Dec 4 11:18:16 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 04 Dec 2018 16:18:16 -0000 Subject: [Python-checkins] bpo-35346, platform: import subprocess in _syscmd_file() (GH-10892) Message-ID: https://github.com/python/cpython/commit/b8e689a6e8134e88f857a55e50b6a4977967e385 commit: b8e689a6e8134e88f857a55e50b6a4977967e385 branch: master author: Victor Stinner committer: GitHub date: 2018-12-04T17:18:12+01:00 summary: bpo-35346, platform: import subprocess in _syscmd_file() (GH-10892) Only platform._syscmd_file() uses subprocess. Move subprocess import inside this function to reduce the number of imports at Python startup. Remove also warnings import which is no longer needed. files: M Lib/platform.py diff --git a/Lib/platform.py b/Lib/platform.py index 98ee06f85ef1..74346c4cab09 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -113,9 +113,9 @@ __version__ = '1.0.8' import collections -import sys, os, re, subprocess - -import warnings +import os +import re +import sys ### Globals & Constants @@ -612,11 +612,13 @@ def _syscmd_file(target, default=''): if sys.platform in ('dos', 'win32', 'win16'): # XXX Others too ? return default + + import subprocess target = _follow_symlinks(target) try: proc = subprocess.Popen(['file', target], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) except (AttributeError, OSError): return default output = proc.communicate()[0].decode('latin-1') From webhook-mailer at python.org Tue Dec 4 15:26:01 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 04 Dec 2018 20:26:01 -0000 Subject: [Python-checkins] bpo-29564: warnings suggests to enable tracemalloc (GH-10486) (GH-10509) Message-ID: https://github.com/python/cpython/commit/0091f349cde179ea991f4ee4d095119cd1fc3802 commit: 0091f349cde179ea991f4ee4d095119cd1fc3802 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Victor Stinner date: 2018-12-04T21:25:57+01:00 summary: bpo-29564: warnings suggests to enable tracemalloc (GH-10486) (GH-10509) The warnings module now suggests to enable tracemalloc if the source is specified, tracemalloc module is available, but tracemalloc is not tracing memory allocations. (cherry picked from commit 2c07c493d2eb45101312e3eb3a77f94d0c9cad1f) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Library/2018-11-12-17-40-04.bpo-29564.SFNBT5.rst M Lib/test/test_warnings/__init__.py M Lib/warnings.py diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 7f3f3fffb5d2..a40a9a297c82 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -960,12 +960,27 @@ def func(): func() """)) - res = assert_python_ok('-Wd', '-X', 'tracemalloc=2', support.TESTFN) + def run(*args): + res = assert_python_ok(*args) + stderr = res.err.decode('ascii', 'replace') + stderr = '\n'.join(stderr.splitlines()) - stderr = res.err.decode('ascii', 'replace') - # normalize newlines - stderr = '\n'.join(stderr.splitlines()) - stderr = re.sub('<.*>', '<...>', stderr) + # normalize newlines + stderr = re.sub('<.*>', '<...>', stderr) + return stderr + + # tracemalloc disabled + stderr = run('-Wd', support.TESTFN) + expected = textwrap.dedent(''' + {fname}:5: ResourceWarning: unclosed file <...> + f = None + ResourceWarning: Enable tracemalloc to get the object allocation traceback + ''') + expected = expected.format(fname=support.TESTFN).strip() + self.assertEqual(stderr, expected) + + # tracemalloc enabled + stderr = run('-Wd', '-X', 'tracemalloc=2', support.TESTFN) expected = textwrap.dedent(''' {fname}:5: ResourceWarning: unclosed file <...> f = None diff --git a/Lib/warnings.py b/Lib/warnings.py index 81f98647786d..ae4295e120f6 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -33,9 +33,8 @@ def _showwarnmsg_impl(msg): pass def _formatwarnmsg_impl(msg): - s = ("%s:%s: %s: %s\n" - % (msg.filename, msg.lineno, msg.category.__name__, - msg.message)) + category = msg.category.__name__ + s = f"{msg.filename}:{msg.lineno}: {category}: {msg.message}\n" if msg.line is None: try: @@ -55,11 +54,20 @@ def _formatwarnmsg_impl(msg): if msg.source is not None: try: import tracemalloc - tb = tracemalloc.get_object_traceback(msg.source) + # Logging a warning should not raise a new exception: + # catch Exception, not only ImportError and RecursionError. except Exception: - # When a warning is logged during Python shutdown, tracemalloc - # and the import machinery don't work anymore + # don't suggest to enable tracemalloc if it's not available + tracing = True tb = None + else: + tracing = tracemalloc.is_tracing() + try: + tb = tracemalloc.get_object_traceback(msg.source) + except Exception: + # When a warning is logged during Python shutdown, tracemalloc + # and the import machinery don't work anymore + tb = None if tb is not None: s += 'Object allocated at (most recent call last):\n' @@ -77,6 +85,9 @@ def _formatwarnmsg_impl(msg): if line: line = line.strip() s += ' %s\n' % line + elif not tracing: + s += (f'{category}: Enable tracemalloc to get the object ' + f'allocation traceback\n') return s # Keep a reference to check if the function was replaced diff --git a/Misc/NEWS.d/next/Library/2018-11-12-17-40-04.bpo-29564.SFNBT5.rst b/Misc/NEWS.d/next/Library/2018-11-12-17-40-04.bpo-29564.SFNBT5.rst new file mode 100644 index 000000000000..7ef3adeb73b9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-11-12-17-40-04.bpo-29564.SFNBT5.rst @@ -0,0 +1,3 @@ +The warnings module now suggests to enable tracemalloc if the source is +specified, the tracemalloc module is available, but tracemalloc is not +tracing memory allocations. From webhook-mailer at python.org Tue Dec 4 15:28:33 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 04 Dec 2018 20:28:33 -0000 Subject: [Python-checkins] bpo-35296: make install now installs the internal API (GH-10665) (GH-10897) Message-ID: https://github.com/python/cpython/commit/b02774f42108aaf18eb19865472c8d5cd95b5f11 commit: b02774f42108aaf18eb19865472c8d5cd95b5f11 branch: 3.7 author: Victor Stinner committer: GitHub date: 2018-12-04T21:28:28+01:00 summary: bpo-35296: make install now installs the internal API (GH-10665) (GH-10897) * bpo-35296: make install now installs the internal API (GH-10665) make install now also installs the internal API: Include/internal/*.h header files. (cherry picked from commit f653fd4d950ac092719b6152e38d77c62b443125) * Windows installer now also install Include/internal/ The Windows installer (MSI) now also install header files of the Include/internal/ subdirectory. files: A Misc/NEWS.d/next/Build/2018-12-04-17-10-17.bpo-35296.2ktH40.rst A Misc/NEWS.d/next/C API/2018-11-22-18-34-23.bpo-35296.nxrIQt.rst M Makefile.pre.in M Tools/msi/dev/dev.wixproj diff --git a/Makefile.pre.in b/Makefile.pre.in index afbc8f8c9bea..7d9cbd5c8bf3 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1443,11 +1443,21 @@ inclinstall: else true; \ fi; \ done + @if test ! -d $(DESTDIR)$(INCLUDEPY)/internal; then \ + echo "Creating directory $(DESTDIR)$(INCLUDEPY)/internal"; \ + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(INCLUDEPY)/internal; \ + else true; \ + fi @for i in $(srcdir)/Include/*.h; \ do \ echo $(INSTALL_DATA) $$i $(INCLUDEPY); \ $(INSTALL_DATA) $$i $(DESTDIR)$(INCLUDEPY); \ done + @for i in $(srcdir)/Include/internal/*.h; \ + do \ + echo $(INSTALL_DATA) $$i $(INCLUDEPY)/internal; \ + $(INSTALL_DATA) $$i $(DESTDIR)$(INCLUDEPY)/internal; \ + done $(INSTALL_DATA) pyconfig.h $(DESTDIR)$(CONFINCLUDEPY)/pyconfig.h # Install the library and miscellaneous stuff needed for extending/embedding diff --git a/Misc/NEWS.d/next/Build/2018-12-04-17-10-17.bpo-35296.2ktH40.rst b/Misc/NEWS.d/next/Build/2018-12-04-17-10-17.bpo-35296.2ktH40.rst new file mode 100644 index 000000000000..e6eda2dcf3c5 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-04-17-10-17.bpo-35296.2ktH40.rst @@ -0,0 +1,2 @@ +The Windows installer (MSI) now also install internal header files +(``Include/internal/`` subdirectory). diff --git a/Misc/NEWS.d/next/C API/2018-11-22-18-34-23.bpo-35296.nxrIQt.rst b/Misc/NEWS.d/next/C API/2018-11-22-18-34-23.bpo-35296.nxrIQt.rst new file mode 100644 index 000000000000..c5f877a4e323 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2018-11-22-18-34-23.bpo-35296.nxrIQt.rst @@ -0,0 +1,2 @@ +``make install`` now also installs the internal API: +``Include/internal/*.h`` header files. diff --git a/Tools/msi/dev/dev.wixproj b/Tools/msi/dev/dev.wixproj index 682b66031f1e..bc3a19ce33ca 100644 --- a/Tools/msi/dev/dev.wixproj +++ b/Tools/msi/dev/dev.wixproj @@ -21,7 +21,7 @@ - + $(PySourcePath) !(bindpath.src) $(PySourcePath) @@ -29,7 +29,7 @@ dev_include - + - \ No newline at end of file + From webhook-mailer at python.org Tue Dec 4 17:53:18 2018 From: webhook-mailer at python.org (Raymond Hettinger) Date: Tue, 04 Dec 2018 22:53:18 -0000 Subject: [Python-checkins] Remove unnecessary and over-restrictive type check (GH-10905) Message-ID: https://github.com/python/cpython/commit/09473ac0636c16c0ee96c4caf59f3da8ba8b4a57 commit: 09473ac0636c16c0ee96c4caf59f3da8ba8b4a57 branch: master author: Raymond Hettinger committer: GitHub date: 2018-12-04T14:53:14-08:00 summary: Remove unnecessary and over-restrictive type check (GH-10905) files: M Lib/random.py diff --git a/Lib/random.py b/Lib/random.py index a7a86070c0a9..03c058a39d6e 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -718,8 +718,6 @@ def getrandbits(self, k): """getrandbits(k) -> x. Generates an int with k random bits.""" if k <= 0: raise ValueError('number of bits must be greater than zero') - if k != int(k): - raise TypeError('number of bits should be an integer') numbytes = (k + 7) // 8 # bits / 8 and rounded up x = int.from_bytes(_urandom(numbytes), 'big') return x >> (numbytes * 8 - k) # trim excess bits From webhook-mailer at python.org Tue Dec 4 19:58:42 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 00:58:42 -0000 Subject: [Python-checkins] bpo-35411: Skip test_urllib2net FTP tests on Travis CI (GH-10907) Message-ID: https://github.com/python/cpython/commit/c11b3b19a5b022c6c229043d37f9a9fd06f22500 commit: c11b3b19a5b022c6c229043d37f9a9fd06f22500 branch: master author: Victor Stinner committer: GitHub date: 2018-12-05T01:58:31+01:00 summary: bpo-35411: Skip test_urllib2net FTP tests on Travis CI (GH-10907) On Travis CI, FTP tests of test_urllib2net randomly fail with "425 Security: Bad IP connecting". test.pythoninfo now also logs TRAVIS environment variable. files: M Lib/test/pythoninfo.py M Lib/test/test_urllib2net.py diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 30e6f21c2b48..9befd12e4095 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -256,6 +256,7 @@ def format_groups(groups): "TIX_LIBRARY", "TMP", "TMPDIR", + "TRAVIS", "TZ", "USERPROFILE", "VIRTUAL_ENV", diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index 15f73de9f029..1aa64cbee1ca 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -27,6 +27,13 @@ def wrapped(*args, **kwargs): return _retry_thrice(func, exc, *args, **kwargs) return wrapped +# bpo-35411: FTP tests of test_urllib2net randomly fail +# with "425 Security: Bad IP connecting" on Travis CI +skip_ftp_test_on_travis = unittest.skipIf('TRAVIS' in os.environ, + 'bpo-35411: skip FTP test ' + 'on Travis CI') + + # Connecting to remote hosts is flaky. Make it more robust by retrying # the connection several times. _urlopen_with_retry = _wrap_with_retry_thrice(urllib.request.urlopen, @@ -95,6 +102,7 @@ def setUp(self): # XXX The rest of these tests aren't very good -- they don't check much. # They do sometimes catch some major disasters, though. + @skip_ftp_test_on_travis def test_ftp(self): urls = [ 'ftp://www.pythontest.net/README', @@ -290,6 +298,7 @@ def test_http_timeout(self): FTP_HOST = 'ftp://www.pythontest.net/' + @skip_ftp_test_on_travis def test_ftp_basic(self): self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST, timeout=None): @@ -297,6 +306,7 @@ def test_ftp_basic(self): self.addCleanup(u.close) self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) + @skip_ftp_test_on_travis def test_ftp_default_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): @@ -308,6 +318,7 @@ def test_ftp_default_timeout(self): socket.setdefaulttimeout(None) self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60) + @skip_ftp_test_on_travis def test_ftp_no_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): @@ -319,6 +330,7 @@ def test_ftp_no_timeout(self): socket.setdefaulttimeout(None) self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) + @skip_ftp_test_on_travis def test_ftp_timeout(self): with support.transient_internet(self.FTP_HOST): u = _urlopen_with_retry(self.FTP_HOST, timeout=60) From webhook-mailer at python.org Tue Dec 4 20:16:44 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 01:16:44 -0000 Subject: [Python-checkins] bpo-35411: Skip test_urllib2net FTP tests on Travis CI (GH-10907) Message-ID: https://github.com/python/cpython/commit/74a80e1ed0c9067ef47f0a637d7f718a51b4f34e commit: 74a80e1ed0c9067ef47f0a637d7f718a51b4f34e branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-04T17:16:39-08:00 summary: bpo-35411: Skip test_urllib2net FTP tests on Travis CI (GH-10907) On Travis CI, FTP tests of test_urllib2net randomly fail with "425 Security: Bad IP connecting". test.pythoninfo now also logs TRAVIS environment variable. (cherry picked from commit c11b3b19a5b022c6c229043d37f9a9fd06f22500) Co-authored-by: Victor Stinner files: M Lib/test/pythoninfo.py M Lib/test/test_urllib2net.py diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 30e6f21c2b48..9befd12e4095 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -256,6 +256,7 @@ def format_groups(groups): "TIX_LIBRARY", "TMP", "TMPDIR", + "TRAVIS", "TZ", "USERPROFILE", "VIRTUAL_ENV", diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index 15f73de9f029..1aa64cbee1ca 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -27,6 +27,13 @@ def wrapped(*args, **kwargs): return _retry_thrice(func, exc, *args, **kwargs) return wrapped +# bpo-35411: FTP tests of test_urllib2net randomly fail +# with "425 Security: Bad IP connecting" on Travis CI +skip_ftp_test_on_travis = unittest.skipIf('TRAVIS' in os.environ, + 'bpo-35411: skip FTP test ' + 'on Travis CI') + + # Connecting to remote hosts is flaky. Make it more robust by retrying # the connection several times. _urlopen_with_retry = _wrap_with_retry_thrice(urllib.request.urlopen, @@ -95,6 +102,7 @@ def setUp(self): # XXX The rest of these tests aren't very good -- they don't check much. # They do sometimes catch some major disasters, though. + @skip_ftp_test_on_travis def test_ftp(self): urls = [ 'ftp://www.pythontest.net/README', @@ -290,6 +298,7 @@ def test_http_timeout(self): FTP_HOST = 'ftp://www.pythontest.net/' + @skip_ftp_test_on_travis def test_ftp_basic(self): self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST, timeout=None): @@ -297,6 +306,7 @@ def test_ftp_basic(self): self.addCleanup(u.close) self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) + @skip_ftp_test_on_travis def test_ftp_default_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): @@ -308,6 +318,7 @@ def test_ftp_default_timeout(self): socket.setdefaulttimeout(None) self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60) + @skip_ftp_test_on_travis def test_ftp_no_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): @@ -319,6 +330,7 @@ def test_ftp_no_timeout(self): socket.setdefaulttimeout(None) self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) + @skip_ftp_test_on_travis def test_ftp_timeout(self): with support.transient_internet(self.FTP_HOST): u = _urlopen_with_retry(self.FTP_HOST, timeout=60) From webhook-mailer at python.org Tue Dec 4 20:19:00 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 01:19:00 -0000 Subject: [Python-checkins] bpo-35411: Skip test_urllib2net FTP tests on Travis CI (GH-10907) (GH-10909) Message-ID: https://github.com/python/cpython/commit/65a5eff67474703329477b070d9c081fee17c69c commit: 65a5eff67474703329477b070d9c081fee17c69c branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Victor Stinner date: 2018-12-05T02:18:57+01:00 summary: bpo-35411: Skip test_urllib2net FTP tests on Travis CI (GH-10907) (GH-10909) On Travis CI, FTP tests of test_urllib2net randomly fail with "425 Security: Bad IP connecting". test.pythoninfo now also logs TRAVIS environment variable. (cherry picked from commit c11b3b19a5b022c6c229043d37f9a9fd06f22500) Co-authored-by: Victor Stinner files: M Lib/test/pythoninfo.py M Lib/test/test_urllib2net.py diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 606d511433cf..c453340b80d7 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -256,6 +256,7 @@ def format_groups(groups): "TIX_LIBRARY", "TMP", "TMPDIR", + "TRAVIS", "TZ", "USERPROFILE", "VIRTUAL_ENV", diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index 15f73de9f029..1aa64cbee1ca 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -27,6 +27,13 @@ def wrapped(*args, **kwargs): return _retry_thrice(func, exc, *args, **kwargs) return wrapped +# bpo-35411: FTP tests of test_urllib2net randomly fail +# with "425 Security: Bad IP connecting" on Travis CI +skip_ftp_test_on_travis = unittest.skipIf('TRAVIS' in os.environ, + 'bpo-35411: skip FTP test ' + 'on Travis CI') + + # Connecting to remote hosts is flaky. Make it more robust by retrying # the connection several times. _urlopen_with_retry = _wrap_with_retry_thrice(urllib.request.urlopen, @@ -95,6 +102,7 @@ def setUp(self): # XXX The rest of these tests aren't very good -- they don't check much. # They do sometimes catch some major disasters, though. + @skip_ftp_test_on_travis def test_ftp(self): urls = [ 'ftp://www.pythontest.net/README', @@ -290,6 +298,7 @@ def test_http_timeout(self): FTP_HOST = 'ftp://www.pythontest.net/' + @skip_ftp_test_on_travis def test_ftp_basic(self): self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST, timeout=None): @@ -297,6 +306,7 @@ def test_ftp_basic(self): self.addCleanup(u.close) self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) + @skip_ftp_test_on_travis def test_ftp_default_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): @@ -308,6 +318,7 @@ def test_ftp_default_timeout(self): socket.setdefaulttimeout(None) self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60) + @skip_ftp_test_on_travis def test_ftp_no_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) with support.transient_internet(self.FTP_HOST): @@ -319,6 +330,7 @@ def test_ftp_no_timeout(self): socket.setdefaulttimeout(None) self.assertIsNone(u.fp.fp.raw._sock.gettimeout()) + @skip_ftp_test_on_travis def test_ftp_timeout(self): with support.transient_internet(self.FTP_HOST): u = _urlopen_with_retry(self.FTP_HOST, timeout=60) From webhook-mailer at python.org Tue Dec 4 20:22:06 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 01:22:06 -0000 Subject: [Python-checkins] bpo-35411: Skip test_urllib2net FTP tests on Travis CI (GH-10907) Message-ID: https://github.com/python/cpython/commit/c7976da5c262be818a06c344d43ac09b1083c80b commit: c7976da5c262be818a06c344d43ac09b1083c80b branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-04T17:22:02-08:00 summary: bpo-35411: Skip test_urllib2net FTP tests on Travis CI (GH-10907) On Travis CI, FTP tests of test_urllib2net randomly fail with "425 Security: Bad IP connecting". test.pythoninfo now also logs TRAVIS environment variable. (cherry picked from commit c11b3b19a5b022c6c229043d37f9a9fd06f22500) Co-authored-by: Victor Stinner files: M Lib/test/pythoninfo.py M Lib/test/test_urllib2net.py diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 693a13525ff3..ae1c07b43ac3 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -256,6 +256,7 @@ def format_groups(groups): "TIX_LIBRARY", "TMP", "TMPDIR", + "TRAVIS", "TZ", "USERPROFILE", "VIRTUAL_ENV", diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index 4ba79cbc31c3..ee0b7fe453e6 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -25,6 +25,13 @@ def wrapped(*args, **kwargs): return _retry_thrice(func, exc, *args, **kwargs) return wrapped +# bpo-35411: FTP tests of test_urllib2net randomly fail +# with "425 Security: Bad IP connecting" on Travis CI +skip_ftp_test_on_travis = unittest.skipIf('TRAVIS' in os.environ, + 'bpo-35411: skip FTP test ' + 'on Travis CI') + + # Connecting to remote hosts is flaky. Make it more robust by retrying # the connection several times. _urlopen_with_retry = _wrap_with_retry_thrice(urllib2.urlopen, urllib2.URLError) @@ -100,6 +107,7 @@ def setUp(self): # XXX The rest of these tests aren't very good -- they don't check much. # They do sometimes catch some major disasters, though. + @skip_ftp_test_on_travis def test_ftp(self): urls = [ 'ftp://www.pythontest.net/README', @@ -285,12 +293,14 @@ def test_http_timeout(self): FTP_HOST = 'ftp://www.pythontest.net/' + @skip_ftp_test_on_travis def test_ftp_basic(self): self.assertIsNone(socket.getdefaulttimeout()) with test_support.transient_internet(self.FTP_HOST, timeout=None): u = _urlopen_with_retry(self.FTP_HOST) self.assertIsNone(u.fp.fp._sock.gettimeout()) + @skip_ftp_test_on_travis def test_ftp_default_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) with test_support.transient_internet(self.FTP_HOST): @@ -301,6 +311,7 @@ def test_ftp_default_timeout(self): socket.setdefaulttimeout(None) self.assertEqual(u.fp.fp._sock.gettimeout(), 60) + @skip_ftp_test_on_travis def test_ftp_no_timeout(self): self.assertIsNone(socket.getdefaulttimeout(),) with test_support.transient_internet(self.FTP_HOST): @@ -311,6 +322,7 @@ def test_ftp_no_timeout(self): socket.setdefaulttimeout(None) self.assertIsNone(u.fp.fp._sock.gettimeout()) + @skip_ftp_test_on_travis def test_ftp_timeout(self): with test_support.transient_internet(self.FTP_HOST): u = _urlopen_with_retry(self.FTP_HOST, timeout=60) From webhook-mailer at python.org Tue Dec 4 21:03:26 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 02:03:26 -0000 Subject: [Python-checkins] bpo-35363, test_eintr: skip test_open() on macOS (GH-10896) (GH-10912) Message-ID: https://github.com/python/cpython/commit/93d038c91dc3075dd34b41ce6b6fb4ea07fbb8c3 commit: 93d038c91dc3075dd34b41ce6b6fb4ea07fbb8c3 branch: 3.6 author: Victor Stinner committer: GitHub date: 2018-12-05T03:03:21+01:00 summary: bpo-35363, test_eintr: skip test_open() on macOS (GH-10896) (GH-10912) (cherry picked from commit 4752e65250bce60b97d5af702d586092d02fbf58) files: M Lib/test/eintrdata/eintr_tester.py diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py index aa7cfd14d9f9..25c169bde500 100644 --- a/Lib/test/eintrdata/eintr_tester.py +++ b/Lib/test/eintrdata/eintr_tester.py @@ -349,16 +349,18 @@ def python_open(self, path): fp = open(path, 'w') fp.close() + @unittest.skipIf(sys.platform == "darwin", + "hangs under macOS; see bpo-25234, bpo-35363") def test_open(self): self._test_open("fp = open(path, 'r')\nfp.close()", self.python_open) - @unittest.skipIf(sys.platform == 'darwin', "hangs under OS X; see issue #25234") def os_open(self, path): fd = os.open(path, os.O_WRONLY) os.close(fd) - @unittest.skipIf(sys.platform == "darwin", "hangs under OS X; see issue #25234") + @unittest.skipIf(sys.platform == "darwin", + "hangs under macOS; see bpo-25234, bpo-35363") def test_os_open(self): self._test_open("fd = os.open(path, os.O_RDONLY)\nos.close(fd)", self.os_open) From webhook-mailer at python.org Tue Dec 4 21:03:31 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 02:03:31 -0000 Subject: [Python-checkins] bpo-35363, test_eintr: skip test_open() on macOS (GH-10896) (GH-10911) Message-ID: https://github.com/python/cpython/commit/c93e3b05d5672dc9e8d6e2f2dce332799d5b95d2 commit: c93e3b05d5672dc9e8d6e2f2dce332799d5b95d2 branch: 3.7 author: Victor Stinner committer: GitHub date: 2018-12-05T03:03:28+01:00 summary: bpo-35363, test_eintr: skip test_open() on macOS (GH-10896) (GH-10911) (cherry picked from commit 4752e65250bce60b97d5af702d586092d02fbf58) files: M Lib/test/eintrdata/eintr_tester.py diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py index aa7cfd14d9f9..25c169bde500 100644 --- a/Lib/test/eintrdata/eintr_tester.py +++ b/Lib/test/eintrdata/eintr_tester.py @@ -349,16 +349,18 @@ def python_open(self, path): fp = open(path, 'w') fp.close() + @unittest.skipIf(sys.platform == "darwin", + "hangs under macOS; see bpo-25234, bpo-35363") def test_open(self): self._test_open("fp = open(path, 'r')\nfp.close()", self.python_open) - @unittest.skipIf(sys.platform == 'darwin', "hangs under OS X; see issue #25234") def os_open(self, path): fd = os.open(path, os.O_WRONLY) os.close(fd) - @unittest.skipIf(sys.platform == "darwin", "hangs under OS X; see issue #25234") + @unittest.skipIf(sys.platform == "darwin", + "hangs under macOS; see bpo-25234, bpo-35363") def test_os_open(self): self._test_open("fd = os.open(path, os.O_RDONLY)\nos.close(fd)", self.os_open) From webhook-mailer at python.org Wed Dec 5 02:14:06 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 07:14:06 -0000 Subject: [Python-checkins] bpo-35414: Add a missing Py_INCREF(Py_None) in PyState_RemoveModule(). (GH-10914) Message-ID: https://github.com/python/cpython/commit/2a893430c9c8378cbdfac95895a64fa07aaff9ed commit: 2a893430c9c8378cbdfac95895a64fa07aaff9ed branch: master author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-05T09:14:00+02:00 summary: bpo-35414: Add a missing Py_INCREF(Py_None) in PyState_RemoveModule(). (GH-10914) files: M Python/pystate.c diff --git a/Python/pystate.c b/Python/pystate.c index f86f5a96f074..98882eb7589c 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -592,6 +592,7 @@ PyState_RemoveModule(struct PyModuleDef* def) Py_FatalError("PyState_RemoveModule: Module index out of bounds."); return -1; } + Py_INCREF(Py_None); return PyList_SetItem(state->modules_by_index, index, Py_None); } From webhook-mailer at python.org Wed Dec 5 02:51:17 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 07:51:17 -0000 Subject: [Python-checkins] bpo-35414: Add a missing Py_INCREF(Py_None) in PyState_RemoveModule(). (GH-10914) Message-ID: https://github.com/python/cpython/commit/2d594f857865a4719876ac545ddfd62f474522cd commit: 2d594f857865a4719876ac545ddfd62f474522cd branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-04T23:51:08-08:00 summary: bpo-35414: Add a missing Py_INCREF(Py_None) in PyState_RemoveModule(). (GH-10914) (cherry picked from commit 2a893430c9c8378cbdfac95895a64fa07aaff9ed) Co-authored-by: Zackery Spytz files: M Python/pystate.c diff --git a/Python/pystate.c b/Python/pystate.c index 15761e7da90f..8077a3ee7ab0 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -532,6 +532,7 @@ PyState_RemoveModule(struct PyModuleDef* def) Py_FatalError("PyState_RemoveModule: Module index out of bounds."); return -1; } + Py_INCREF(Py_None); return PyList_SetItem(state->modules_by_index, index, Py_None); } From webhook-mailer at python.org Wed Dec 5 02:51:19 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 07:51:19 -0000 Subject: [Python-checkins] bpo-35414: Add a missing Py_INCREF(Py_None) in PyState_RemoveModule(). (GH-10914) Message-ID: https://github.com/python/cpython/commit/afb07fccf013f20b89b33516f126695388be47b9 commit: afb07fccf013f20b89b33516f126695388be47b9 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-04T23:51:15-08:00 summary: bpo-35414: Add a missing Py_INCREF(Py_None) in PyState_RemoveModule(). (GH-10914) (cherry picked from commit 2a893430c9c8378cbdfac95895a64fa07aaff9ed) Co-authored-by: Zackery Spytz files: M Python/pystate.c diff --git a/Python/pystate.c b/Python/pystate.c index c0e088055a60..71494daa471a 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -377,6 +377,7 @@ PyState_RemoveModule(struct PyModuleDef* def) Py_FatalError("PyState_RemoveModule: Module index out of bounds."); return -1; } + Py_INCREF(Py_None); return PyList_SetItem(state->modules_by_index, index, Py_None); } From webhook-mailer at python.org Wed Dec 5 03:30:10 2018 From: webhook-mailer at python.org (Andrew Svetlov) Date: Wed, 05 Dec 2018 08:30:10 -0000 Subject: [Python-checkins] [3.6] bpo-35380: Enable TCP_NODELAY for proactor event loop (GH-10867). (GH-10874) Message-ID: https://github.com/python/cpython/commit/bfb881849f588cd2046776fb431c3045781c8214 commit: bfb881849f588cd2046776fb431c3045781c8214 branch: 3.6 author: Andrew Svetlov committer: GitHub date: 2018-12-05T10:30:06+02:00 summary: [3.6] bpo-35380: Enable TCP_NODELAY for proactor event loop (GH-10867). (GH-10874) * [3.6] bpo-35380: Enable TCP_NODELAY for proactor event loop (GH-10867). (cherry picked from commit 3bc0ebab17bf5a2c29d2214743c82034f82e6573) Co-authored-by: Andrew Svetlov files: A Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst M Lib/asyncio/base_events.py M Lib/asyncio/proactor_events.py M Lib/asyncio/selector_events.py M Lib/test/test_asyncio/test_base_events.py M Lib/test/test_asyncio/test_selector_events.py diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index accd669250cb..48dd1fc54ae4 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -182,6 +182,17 @@ def _ensure_resolved(address, *, family=0, type=socket.SOCK_STREAM, proto=0, proto=proto, flags=flags) +if hasattr(socket, 'TCP_NODELAY'): + def _set_nodelay(sock): + if (sock.family in {socket.AF_INET, socket.AF_INET6} and + _is_stream_socket(sock.type) and + sock.proto == socket.IPPROTO_TCP): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) +else: + def _set_nodelay(sock): + pass + + def _run_until_complete_cb(fut): exc = fut._exception if (isinstance(exc, BaseException) diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 6f621ef0cc4f..079b7a24db42 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -350,6 +350,11 @@ class _ProactorSocketTransport(_ProactorReadPipeTransport, transports.Transport): """Transport for connected sockets.""" + def __init__(self, loop, sock, protocol, waiter=None, + extra=None, server=None): + super().__init__(loop, sock, protocol, waiter, extra, server) + base_events._set_nodelay(sock) + def _set_extra(self, sock): self._extra['socket'] = sock try: diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index bc7c740cc2af..af21d5ee8914 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -40,17 +40,6 @@ def _test_selector_event(selector, fd, event): return bool(key.events & event) -if hasattr(socket, 'TCP_NODELAY'): - def _set_nodelay(sock): - if (sock.family in {socket.AF_INET, socket.AF_INET6} and - base_events._is_stream_socket(sock.type) and - sock.proto == socket.IPPROTO_TCP): - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) -else: - def _set_nodelay(sock): - pass - - class BaseSelectorEventLoop(base_events.BaseEventLoop): """Selector event loop. @@ -691,7 +680,7 @@ def __init__(self, loop, sock, protocol, waiter=None, # Disable the Nagle algorithm -- small writes will be # sent without waiting for the TCP ACK. This generally # decreases the latency (in some cases significantly.) - _set_nodelay(self._sock) + base_events._set_nodelay(self._sock) self._loop.call_soon(self._protocol.connection_made, self) # only start reading when connection_made() has been called diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 09f71f2f3900..052a559c3cbb 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -27,7 +27,6 @@ except ImportError: from asyncio.test_support import assert_python_ok - MOCK_ANY = mock.ANY PY34 = sys.version_info >= (3, 4) @@ -288,7 +287,7 @@ def cb(): loop.set_debug(debug) if debug: msg = ("Non-thread-safe operation invoked on an event loop other " - "than the current one") + "than the current one") with self.assertRaisesRegex(RuntimeError, msg): loop.call_soon(cb) with self.assertRaisesRegex(RuntimeError, msg): @@ -1839,5 +1838,30 @@ def runner(loop): +class TestSelectorUtils(test_utils.TestCase): + def check_set_nodelay(self, sock): + opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) + self.assertFalse(opt) + + base_events._set_nodelay(sock) + + opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) + self.assertTrue(opt) + + @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'), + 'need socket.TCP_NODELAY') + def test_set_nodelay(self): + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, + proto=socket.IPPROTO_TCP) + with sock: + self.check_set_nodelay(sock) + + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, + proto=socket.IPPROTO_TCP) + with sock: + sock.setblocking(False) + self.check_set_nodelay(sock) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index 533d2898e7fc..6716e8916cd5 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -17,7 +17,6 @@ from asyncio.selector_events import _SelectorSslTransport from asyncio.selector_events import _SelectorSocketTransport from asyncio.selector_events import _SelectorDatagramTransport -from asyncio.selector_events import _set_nodelay MOCK_ANY = mock.ANY @@ -1858,30 +1857,5 @@ def test_fatal_error_connected(self, m_exc): exc_info=(ConnectionRefusedError, MOCK_ANY, MOCK_ANY)) -class TestSelectorUtils(test_utils.TestCase): - def check_set_nodelay(self, sock): - opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) - self.assertFalse(opt) - - _set_nodelay(sock) - - opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) - self.assertTrue(opt) - - @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'), - 'need socket.TCP_NODELAY') - def test_set_nodelay(self): - sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, - proto=socket.IPPROTO_TCP) - with sock: - self.check_set_nodelay(sock) - - sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, - proto=socket.IPPROTO_TCP) - with sock: - sock.setblocking(False) - self.check_set_nodelay(sock) - - if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst b/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst new file mode 100644 index 000000000000..91f86e604ea8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst @@ -0,0 +1 @@ +Enable TCP_NODELAY on Windows for proactor asyncio event loop. From solipsis at pitrou.net Wed Dec 5 04:10:28 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 05 Dec 2018 09:10:28 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=5 Message-ID: <20181205091028.1.95010E1546E4245D@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_spawn leaked [-1, 2, 0] memory blocks, sum=1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogsEB5em', '--timeout', '7200'] From webhook-mailer at python.org Wed Dec 5 08:04:56 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 13:04:56 -0000 Subject: [Python-checkins] bpo-35389: platform.libc_ver() uses os.confstr() (GH-10891) Message-ID: https://github.com/python/cpython/commit/476b113ed8531b9fbb0bd023a05eb3af21996600 commit: 476b113ed8531b9fbb0bd023a05eb3af21996600 branch: master author: Victor Stinner committer: GitHub date: 2018-12-05T14:04:52+01:00 summary: bpo-35389: platform.libc_ver() uses os.confstr() (GH-10891) platform.libc_ver() now uses os.confstr('CS_GNU_LIBC_VERSION') if available and the *executable* parameter is not set. The default value of the libc_ver() *executable* parameter becomes None. Quick benchmark on Fedora 29: python3 -m perf command ./python -S -c 'import platform; platform.libc_ver()' 94.9 ms +- 4.3 ms -> 33.2 ms +- 1.4 ms: 2.86x faster (-65%) files: A Misc/NEWS.d/next/Library/2018-12-04-12-46-05.bpo-35389.CTZ9iA.rst M Lib/platform.py M Lib/test/test_platform.py diff --git a/Lib/platform.py b/Lib/platform.py index 74346c4cab09..f089a463ef9f 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -169,7 +169,7 @@ def _comparable_version(version): b'|' br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII) -def libc_ver(executable=sys.executable, lib='', version='', chunksize=16384): +def libc_ver(executable=None, lib='', version='', chunksize=16384): """ Tries to determine the libc version that the file executable (which defaults to the Python interpreter) is linked against. @@ -184,6 +184,19 @@ def libc_ver(executable=sys.executable, lib='', version='', chunksize=16384): The file is read and scanned in chunks of chunksize bytes. """ + if executable is None: + try: + ver = os.confstr('CS_GNU_LIBC_VERSION') + # parse 'glibc 2.28' as ('glibc', '2.28') + parts = ver.split(maxsplit=1) + if len(parts) == 2: + return tuple(parts) + except (AttributeError, ValueError, OSError): + # os.confstr() or CS_GNU_LIBC_VERSION value not available + pass + + executable = sys.executable + V = _comparable_version if hasattr(os.path, 'realpath'): # Python 2.2 introduced os.path.realpath(); it is used diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 686f454827fd..978d2f76ab68 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -3,7 +3,9 @@ import subprocess import sys import sysconfig +import tempfile import unittest +from unittest import mock from test import support @@ -263,19 +265,46 @@ def test_mac_ver_with_fork(self): self.assertEqual(sts, 0) def test_libc_ver(self): + # check that libc_ver(executable) doesn't raise an exception if os.path.isdir(sys.executable) and \ os.path.exists(sys.executable+'.exe'): # Cygwin horror executable = sys.executable + '.exe' else: executable = sys.executable - res = platform.libc_ver(executable) - - self.addCleanup(support.unlink, support.TESTFN) - with open(support.TESTFN, 'wb') as f: - f.write(b'x'*(16384-10)) + platform.libc_ver(executable) + + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + + with mock.patch('os.confstr', create=True, return_value='mock 1.0'): + # test os.confstr() code path + self.assertEqual(platform.libc_ver(), ('mock', '1.0')) + + # test the different regular expressions + for data, expected in ( + (b'__libc_init', ('libc', '')), + (b'GLIBC_2.9', ('glibc', '2.9')), + (b'libc.so.1.2.5', ('libc', '1.2.5')), + (b'libc_pthread.so.1.2.5', ('libc', '1.2.5_pthread')), + (b'', ('', '')), + ): + with open(filename, 'wb') as fp: + fp.write(b'[xxx%sxxx]' % data) + fp.flush() + + # os.confstr() must not be used if executable is set + self.assertEqual(platform.libc_ver(executable=filename), + expected) + + # binary containing multiple versions: get the most recent, + # make sure that 1.9 is seen as older than 1.23.4 + chunksize = 16384 + with open(filename, 'wb') as f: + # test match at chunk boundary + f.write(b'x'*(chunksize - 10)) f.write(b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0') - self.assertEqual(platform.libc_ver(support.TESTFN), + self.assertEqual(platform.libc_ver(filename, chunksize=chunksize), ('glibc', '1.23.4')) @support.cpython_only diff --git a/Misc/NEWS.d/next/Library/2018-12-04-12-46-05.bpo-35389.CTZ9iA.rst b/Misc/NEWS.d/next/Library/2018-12-04-12-46-05.bpo-35389.CTZ9iA.rst new file mode 100644 index 000000000000..8e2f9dd21cc0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-04-12-46-05.bpo-35389.CTZ9iA.rst @@ -0,0 +1,2 @@ +:func:`platform.libc_ver` now uses ``os.confstr('CS_GNU_LIBC_VERSION')`` if +available and the *executable* parameter is not set. From webhook-mailer at python.org Wed Dec 5 09:44:19 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 14:44:19 -0000 Subject: [Python-checkins] bpo-32787: Better error handling in ctypes. (#3727) Message-ID: https://github.com/python/cpython/commit/398bd27967690f2c1a8cbf8d47a5613edd9cfb2a commit: 398bd27967690f2c1a8cbf8d47a5613edd9cfb2a branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-05T16:44:14+02:00 summary: bpo-32787: Better error handling in ctypes. (#3727) * bpo-31572: Get rid of PyObject_HasAttrString() in ctypes. * Fix error handling for _pack_. * Don't silence errors when look up in a dict. * Use _PyObject_LookupAttrId(). * More changes. files: M Modules/_ctypes/_ctypes.c M Modules/_ctypes/callbacks.c M Modules/_ctypes/callproc.c M Modules/_ctypes/cfield.c M Modules/_ctypes/stgdict.c diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 36ef5d9b67fc..3debe3ace695 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -236,7 +236,7 @@ PyObject * PyDict_GetItemProxy(PyObject *dict, PyObject *key) { PyObject *result; - PyObject *item = PyDict_GetItem(dict, key); + PyObject *item = PyDict_GetItemWithError(dict, key); if (item == NULL) return NULL; @@ -426,6 +426,8 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt PyTypeObject *result; PyObject *fields; StgDictObject *dict; + _Py_IDENTIFIER(_abstract_); + _Py_IDENTIFIER(_fields_); /* create the new instance (which is a class, since we are a metatype!) */ @@ -434,8 +436,12 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt return NULL; /* keep this for bw compatibility */ - if (PyDict_GetItemString(result->tp_dict, "_abstract_")) + if (_PyDict_GetItemIdWithError(result->tp_dict, &PyId__abstract_)) return (PyObject *)result; + if (PyErr_Occurred()) { + Py_DECREF(result); + return NULL; + } dict = (StgDictObject *)_PyObject_CallNoArg((PyObject *)&PyCStgDict_Type); if (!dict) { @@ -458,8 +464,19 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt dict->paramfunc = StructUnionType_paramfunc; - fields = PyDict_GetItemString((PyObject *)dict, "_fields_"); - if (!fields) { + fields = _PyDict_GetItemIdWithError((PyObject *)dict, &PyId__fields_); + if (fields) { + if (_PyObject_SetAttrId((PyObject *)result, &PyId__fields_, fields) < 0) { + Py_DECREF(result); + return NULL; + } + return (PyObject *)result; + } + else if (PyErr_Occurred()) { + Py_DECREF(result); + return NULL; + } + else { StgDictObject *basedict = PyType_stgdict((PyObject *)result->tp_base); if (basedict == NULL) @@ -473,12 +490,6 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt basedict->flags |= DICTFLAG_FINAL; /* set the 'final' flag in the baseclass dict */ return (PyObject *)result; } - - if (-1 == PyObject_SetAttrString((PyObject *)result, "_fields_", fields)) { - Py_DECREF(result); - return NULL; - } - return (PyObject *)result; } static PyObject * @@ -693,6 +704,7 @@ static const char from_param_doc[] = static PyObject * CDataType_from_param(PyObject *type, PyObject *value) { + _Py_IDENTIFIER(_as_parameter_); PyObject *as_parameter; int res = PyObject_IsInstance(value, type); if (res == -1) @@ -726,7 +738,9 @@ CDataType_from_param(PyObject *type, PyObject *value) return NULL; } - as_parameter = PyObject_GetAttrString(value, "_as_parameter_"); + if (_PyObject_LookupAttrId(value, &PyId__as_parameter_, &as_parameter) < 0) { + return NULL; + } if (as_parameter) { value = CDataType_from_param(type, as_parameter); Py_DECREF(as_parameter); @@ -961,6 +975,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) StgDictObject *stgdict; PyObject *proto; PyObject *typedict; + _Py_IDENTIFIER(_type_); typedict = PyTuple_GetItem(args, 2); if (!typedict) @@ -980,15 +995,15 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict->paramfunc = PyCPointerType_paramfunc; stgdict->flags |= TYPEFLAG_ISPOINTER; - proto = PyDict_GetItemString(typedict, "_type_"); /* Borrowed ref */ - if (proto && -1 == PyCPointerType_SetProto(stgdict, proto)) { - Py_DECREF((PyObject *)stgdict); - return NULL; - } - + proto = _PyDict_GetItemIdWithError(typedict, &PyId__type_); /* Borrowed ref */ if (proto) { - StgDictObject *itemdict = PyType_stgdict(proto); + StgDictObject *itemdict; const char *current_format; + if (-1 == PyCPointerType_SetProto(stgdict, proto)) { + Py_DECREF((PyObject *)stgdict); + return NULL; + } + itemdict = PyType_stgdict(proto); /* PyCPointerType_SetProto has verified proto has a stgdict. */ assert(itemdict); /* If itemdict->format is NULL, then this is a pointer to an @@ -1009,6 +1024,10 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } } + else if (PyErr_Occurred()) { + Py_DECREF((PyObject *)stgdict); + return NULL; + } /* create the new instance (which is a class, since we are a metatype!) */ @@ -1034,6 +1053,7 @@ static PyObject * PyCPointerType_set_type(PyTypeObject *self, PyObject *type) { StgDictObject *dict; + _Py_IDENTIFIER(_type_); dict = PyType_stgdict((PyObject *)self); if (!dict) { @@ -1045,7 +1065,7 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type) if (-1 == PyCPointerType_SetProto(dict, type)) return NULL; - if (-1 == PyDict_SetItemString((PyObject *)dict, "_type_", type)) + if (-1 == _PyDict_SetItemId((PyObject *)dict, &PyId__type_, type)) return NULL; Py_RETURN_NONE; @@ -1386,6 +1406,8 @@ PyCArrayType_paramfunc(CDataObject *self) static PyObject * PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + _Py_IDENTIFIER(_length_); + _Py_IDENTIFIER(_type_); PyTypeObject *result; StgDictObject *stgdict; StgDictObject *itemdict; @@ -1404,12 +1426,12 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict = NULL; type_attr = NULL; - length_attr = PyObject_GetAttrString((PyObject *)result, "_length_"); + if (_PyObject_LookupAttrId((PyObject *)result, &PyId__length_, &length_attr) < 0) { + goto error; + } if (!length_attr) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_SetString(PyExc_AttributeError, - "class must define a '_length_' attribute"); - } + PyErr_SetString(PyExc_AttributeError, + "class must define a '_length_' attribute"); goto error; } @@ -1437,7 +1459,9 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) goto error; } - type_attr = PyObject_GetAttrString((PyObject *)result, "_type_"); + if (_PyObject_LookupAttrId((PyObject *)result, &PyId__type_, &type_attr) < 0) { + goto error; + } if (!type_attr) { PyErr_SetString(PyExc_AttributeError, "class must define a '_type_' attribute"); @@ -1580,6 +1604,7 @@ static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g"; static PyObject * c_wchar_p_from_param(PyObject *type, PyObject *value) { + _Py_IDENTIFIER(_as_parameter_); PyObject *as_parameter; int res; if (value == Py_None) { @@ -1629,7 +1654,9 @@ c_wchar_p_from_param(PyObject *type, PyObject *value) } } - as_parameter = PyObject_GetAttrString(value, "_as_parameter_"); + if (_PyObject_LookupAttrId(value, &PyId__as_parameter_, &as_parameter) < 0) { + return NULL; + } if (as_parameter) { value = c_wchar_p_from_param(type, as_parameter); Py_DECREF(as_parameter); @@ -1644,6 +1671,7 @@ c_wchar_p_from_param(PyObject *type, PyObject *value) static PyObject * c_char_p_from_param(PyObject *type, PyObject *value) { + _Py_IDENTIFIER(_as_parameter_); PyObject *as_parameter; int res; if (value == Py_None) { @@ -1693,7 +1721,9 @@ c_char_p_from_param(PyObject *type, PyObject *value) } } - as_parameter = PyObject_GetAttrString(value, "_as_parameter_"); + if (_PyObject_LookupAttrId(value, &PyId__as_parameter_, &as_parameter) < 0) { + return NULL; + } if (as_parameter) { value = c_char_p_from_param(type, as_parameter); Py_DECREF(as_parameter); @@ -1708,6 +1738,7 @@ c_char_p_from_param(PyObject *type, PyObject *value) static PyObject * c_void_p_from_param(PyObject *type, PyObject *value) { + _Py_IDENTIFIER(_as_parameter_); StgDictObject *stgd; PyObject *as_parameter; int res; @@ -1829,7 +1860,9 @@ c_void_p_from_param(PyObject *type, PyObject *value) } } - as_parameter = PyObject_GetAttrString(value, "_as_parameter_"); + if (_PyObject_LookupAttrId(value, &PyId__as_parameter_, &as_parameter) < 0) { + return NULL; + } if (as_parameter) { value = c_void_p_from_param(type, as_parameter); Py_DECREF(as_parameter); @@ -1946,6 +1979,7 @@ PyCSimpleType_paramfunc(CDataObject *self) static PyObject * PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + _Py_IDENTIFIER(_type_); PyTypeObject *result; StgDictObject *stgdict; PyObject *proto; @@ -1960,13 +1994,15 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (result == NULL) return NULL; - proto = PyObject_GetAttrString((PyObject *)result, "_type_"); /* new ref */ + if (_PyObject_LookupAttrId((PyObject *)result, &PyId__type_, &proto) < 0) { + return NULL; + } if (!proto) { PyErr_SetString(PyExc_AttributeError, "class must define a '_type_' attribute"); error: Py_XDECREF(proto); - Py_XDECREF(result); + Py_DECREF(result); return NULL; } if (PyUnicode_Check(proto)) { @@ -2128,6 +2164,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static PyObject * PyCSimpleType_from_param(PyObject *type, PyObject *value) { + _Py_IDENTIFIER(_as_parameter_); StgDictObject *dict; const char *fmt; PyCArgObject *parg; @@ -2171,7 +2208,9 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value) PyErr_Clear(); Py_DECREF(parg); - as_parameter = PyObject_GetAttrString(value, "_as_parameter_"); + if (_PyObject_LookupAttrId(value, &PyId__as_parameter_, &as_parameter) < 0) { + return NULL; + } if (as_parameter) { if (Py_EnterRecursiveCall("while processing _as_parameter_")) { Py_DECREF(as_parameter); @@ -2246,6 +2285,7 @@ PyTypeObject PyCSimpleType_Type = { static PyObject * converters_from_argtypes(PyObject *ob) { + _Py_IDENTIFIER(from_param); PyObject *converters; Py_ssize_t i; Py_ssize_t nArgs; @@ -2270,22 +2310,22 @@ converters_from_argtypes(PyObject *ob) */ for (i = 0; i < nArgs; ++i) { + PyObject *cnv; PyObject *tp = PyTuple_GET_ITEM(ob, i); - PyObject *cnv = PyObject_GetAttrString(tp, "from_param"); - if (!cnv) - goto argtypes_error_1; + if (_PyObject_LookupAttrId(tp, &PyId_from_param, &cnv) <= 0) { + Py_DECREF(converters); + Py_DECREF(ob); + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, + "item %zd in _argtypes_ has no from_param method", + i+1); + } + return NULL; + } PyTuple_SET_ITEM(converters, i, cnv); } Py_DECREF(ob); return converters; - - argtypes_error_1: - Py_XDECREF(converters); - Py_DECREF(ob); - PyErr_Format(PyExc_TypeError, - "item %zd in _argtypes_ has no from_param method", - i+1); - return NULL; } static int @@ -2293,6 +2333,10 @@ make_funcptrtype_dict(StgDictObject *stgdict) { PyObject *ob; PyObject *converters = NULL; + _Py_IDENTIFIER(_flags_); + _Py_IDENTIFIER(_argtypes_); + _Py_IDENTIFIER(_restype_); + _Py_IDENTIFIER(_check_retval_); stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; stgdict->length = 1; @@ -2301,26 +2345,31 @@ make_funcptrtype_dict(StgDictObject *stgdict) stgdict->getfunc = NULL; stgdict->ffi_type_pointer = ffi_type_pointer; - ob = PyDict_GetItemString((PyObject *)stgdict, "_flags_"); + ob = _PyDict_GetItemIdWithError((PyObject *)stgdict, &PyId__flags_); if (!ob || !PyLong_Check(ob)) { - PyErr_SetString(PyExc_TypeError, - "class must define _flags_ which must be an integer"); + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "class must define _flags_ which must be an integer"); + } return -1; } - stgdict->flags = PyLong_AS_LONG(ob) | TYPEFLAG_ISPOINTER; + stgdict->flags = PyLong_AsUnsignedLongMask(ob) | TYPEFLAG_ISPOINTER; /* _argtypes_ is optional... */ - ob = PyDict_GetItemString((PyObject *)stgdict, "_argtypes_"); + ob = _PyDict_GetItemIdWithError((PyObject *)stgdict, &PyId__argtypes_); if (ob) { converters = converters_from_argtypes(ob); if (!converters) - goto error; + return -1; Py_INCREF(ob); stgdict->argtypes = ob; stgdict->converters = converters; } + else if (PyErr_Occurred()) { + return -1; + } - ob = PyDict_GetItemString((PyObject *)stgdict, "_restype_"); + ob = _PyDict_GetItemIdWithError((PyObject *)stgdict, &PyId__restype_); if (ob) { if (ob != Py_None && !PyType_stgdict(ob) && !PyCallable_Check(ob)) { PyErr_SetString(PyExc_TypeError, @@ -2329,12 +2378,17 @@ make_funcptrtype_dict(StgDictObject *stgdict) } Py_INCREF(ob); stgdict->restype = ob; - stgdict->checker = PyObject_GetAttrString(ob, "_check_retval_"); - if (stgdict->checker == NULL) - PyErr_Clear(); + if (_PyObject_LookupAttrId(ob, &PyId__check_retval_, + &stgdict->checker) < 0) + { + return -1; + } + } + else if (PyErr_Occurred()) { + return -1; } /* XXX later, maybe. - ob = PyDict_GetItemString((PyObject *)stgdict, "_errcheck_"); + ob = _PyDict_GetItemIdWithError((PyObject *)stgdict, &PyId__errcheck_); if (ob) { if (!PyCallable_Check(ob)) { PyErr_SetString(PyExc_TypeError, @@ -2344,13 +2398,11 @@ make_funcptrtype_dict(StgDictObject *stgdict) Py_INCREF(ob); stgdict->errcheck = ob; } + else if (PyErr_Occurred()) { + return -1; + } */ return 0; - - error: - Py_XDECREF(converters); - return -1; - } static PyCArgObject * @@ -3085,9 +3137,13 @@ PyCFuncPtr_get_errcheck(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) static int PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(_check_retval_); + PyObject *checker, *oldchecker; if (ob == NULL) { + oldchecker = self->checker; + self->checker = NULL; Py_CLEAR(self->restype); - Py_CLEAR(self->checker); + Py_XDECREF(oldchecker); return 0; } if (ob != Py_None && !PyType_stgdict(ob) && !PyCallable_Check(ob)) { @@ -3095,11 +3151,14 @@ PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ign "restype must be a type, a callable, or None"); return -1; } + if (_PyObject_LookupAttrId(ob, &PyId__check_retval_, &checker) < 0) { + return -1; + } + oldchecker = self->checker; + self->checker = checker; Py_INCREF(ob); Py_XSETREF(self->restype, ob); - Py_XSETREF(self->checker, PyObject_GetAttrString(ob, "_check_retval_")); - if (self->checker == NULL) - PyErr_Clear(); + Py_XDECREF(oldchecker); return 0; } @@ -3526,9 +3585,12 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) like that. */ /* - if (kwds && PyDict_GetItemString(kwds, "options")) { + if (kwds && _PyDict_GetItemIdWithError(kwds, &PyId_options)) { ... } + else if (PyErr_Occurred()) { + return NULL; + } */ dict = PyType_stgdict((PyObject *)type); @@ -3605,10 +3667,16 @@ _get_arg(int *pindex, PyObject *name, PyObject *defval, PyObject *inargs, PyObje Py_INCREF(v); return v; } - if (kwds && name && (v = PyDict_GetItem(kwds, name))) { - ++*pindex; - Py_INCREF(v); - return v; + if (kwds && name) { + v = PyDict_GetItemWithError(kwds, name); + if (v) { + ++*pindex; + Py_INCREF(v); + return v; + } + else if (PyErr_Occurred()) { + return NULL; + } } if (defval) { Py_INCREF(defval); @@ -3685,7 +3753,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, for (i = 0; i < len; ++i) { PyObject *item = PyTuple_GET_ITEM(paramflags, i); PyObject *ob; - int flag; + unsigned int flag; PyObject *name = NULL; PyObject *defval = NULL; @@ -3693,7 +3761,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, calls below. */ /* We HAVE already checked that the tuple can be parsed with "i|ZO", so... */ Py_ssize_t tsize = PyTuple_GET_SIZE(item); - flag = PyLong_AS_LONG(PyTuple_GET_ITEM(item, 0)); + flag = PyLong_AsUnsignedLongMask(PyTuple_GET_ITEM(item, 0)); name = tsize > 1 ? PyTuple_GET_ITEM(item, 1) : NULL; defval = tsize > 2 ? PyTuple_GET_ITEM(item, 2) : NULL; @@ -3773,7 +3841,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, break; default: PyErr_Format(PyExc_ValueError, - "paramflag %d not yet implemented", flag); + "paramflag %u not yet implemented", flag); goto error; break; } @@ -4136,6 +4204,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type, StgDictObject *dict; PyObject *fields; Py_ssize_t i; + _Py_IDENTIFIER(_fields_); if (PyType_stgdict((PyObject *)type->tp_base)) { index = _init_pos_args(self, type->tp_base, @@ -4146,9 +4215,13 @@ _init_pos_args(PyObject *self, PyTypeObject *type, } dict = PyType_stgdict((PyObject *)type); - fields = PyDict_GetItemString((PyObject *)dict, "_fields_"); - if (fields == NULL) + fields = _PyDict_GetItemIdWithError((PyObject *)dict, &PyId__fields_); + if (fields == NULL) { + if (PyErr_Occurred()) { + return -1; + } return index; + } for (i = 0; i < dict->length && (i+index) < PyTuple_GET_SIZE(args); @@ -4164,13 +4237,20 @@ _init_pos_args(PyObject *self, PyTypeObject *type, return -1; } val = PyTuple_GET_ITEM(args, i + index); - if (kwds && PyDict_GetItem(kwds, name)) { - PyErr_Format(PyExc_TypeError, - "duplicate values for field %R", - name); - Py_DECREF(pair); - Py_DECREF(name); - return -1; + if (kwds) { + if (PyDict_GetItemWithError(kwds, name)) { + PyErr_Format(PyExc_TypeError, + "duplicate values for field %R", + name); + Py_DECREF(pair); + Py_DECREF(name); + return -1; + } + else if (PyErr_Occurred()) { + Py_DECREF(pair); + Py_DECREF(name); + return -1; + } } res = PyObject_SetAttr(self, name, val); @@ -4641,6 +4721,10 @@ PyCArrayType_from_ctype(PyObject *itemtype, Py_ssize_t length) Py_DECREF(key); return result; } + else if (PyErr_Occurred()) { + Py_DECREF(key); + return NULL; + } if (!PyType_Check(itemtype)) { PyErr_SetString(PyExc_TypeError, diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index d579291b62fe..871bc4f49458 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -107,9 +107,14 @@ static void TryAddRef(StgDictObject *dict, CDataObject *obj) { IUnknown *punk; + _Py_IDENTIFIER(_needs_com_addref_); - if (NULL == PyDict_GetItemString((PyObject *)dict, "_needs_com_addref_")) + if (!_PyDict_GetItemIdWithError((PyObject *)dict, &PyId__needs_com_addref_)) { + if (PyErr_Occurred()) { + PrintError("getting _needs_com_addref_"); + } return; + } punk = *(IUnknown **)obj->b_ptr; if (punk) diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index ad40ca1c5247..1185c9156ae9 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -146,7 +146,7 @@ _ctypes_get_errobj(int **pspace) if (error_object_name == NULL) return NULL; } - errobj = PyDict_GetItem(dict, error_object_name); + errobj = PyDict_GetItemWithError(dict, error_object_name); if (errobj) { if (!PyCapsule_IsValid(errobj, CTYPES_CAPSULE_NAME_PYMEM)) { PyErr_SetString(PyExc_RuntimeError, @@ -155,7 +155,7 @@ _ctypes_get_errobj(int **pspace) } Py_INCREF(errobj); } - else { + else if (!PyErr_Occurred()) { void *space = PyMem_Malloc(sizeof(int) * 2); if (space == NULL) return NULL; @@ -171,6 +171,9 @@ _ctypes_get_errobj(int **pspace) return NULL; } } + else { + return NULL; + } *pspace = (int *)PyCapsule_GetPointer(errobj, CTYPES_CAPSULE_NAME_PYMEM); return errobj; } @@ -685,8 +688,11 @@ static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa) #endif { + _Py_IDENTIFIER(_as_parameter_); PyObject *arg; - arg = PyObject_GetAttrString(obj, "_as_parameter_"); + if (_PyObject_LookupAttrId(obj, &PyId__as_parameter_, &arg) < 0) { + return -1; + } /* Which types should we exactly allow here? integers are required for using Python classes as parameters (they have to expose the '_as_parameter_' @@ -1685,11 +1691,14 @@ POINTER(PyObject *self, PyObject *cls) PyObject *key; char *buf; - result = PyDict_GetItem(_ctypes_ptrtype_cache, cls); + result = PyDict_GetItemWithError(_ctypes_ptrtype_cache, cls); if (result) { Py_INCREF(result); return result; } + else if (PyErr_Occurred()) { + return NULL; + } if (PyUnicode_CheckExact(cls)) { const char *name = PyUnicode_AsUTF8(cls); if (name == NULL) @@ -1745,12 +1754,16 @@ pointer(PyObject *self, PyObject *arg) PyObject *result; PyObject *typ; - typ = PyDict_GetItem(_ctypes_ptrtype_cache, (PyObject *)Py_TYPE(arg)); - if (typ) + typ = PyDict_GetItemWithError(_ctypes_ptrtype_cache, (PyObject *)Py_TYPE(arg)); + if (typ) { return PyObject_CallFunctionObjArgs(typ, arg, NULL); + } + else if (PyErr_Occurred()) { + return NULL; + } typ = POINTER(NULL, (PyObject *)Py_TYPE(arg)); if (typ == NULL) - return NULL; + return NULL; result = PyObject_CallFunctionObjArgs(typ, arg, NULL); Py_DECREF(typ); return result; diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index e2b9aa8ed153..5f194e21550f 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1147,7 +1147,7 @@ c_set(void *ptr, PyObject *value, Py_ssize_t size) } if (PyLong_Check(value)) { - long longval = PyLong_AS_LONG(value); + long longval = PyLong_AsLong(value); if (longval < 0 || longval >= 256) goto error; *(char *)ptr = (char)longval; diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 25656ff87875..3f8a0316616b 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -281,13 +281,15 @@ MakeFields(PyObject *type, CFieldObject *descr, static int MakeAnonFields(PyObject *type) { + _Py_IDENTIFIER(_anonymous_); PyObject *anon; PyObject *anon_names; Py_ssize_t i; - anon = PyObject_GetAttrString(type, "_anonymous_"); + if (_PyObject_LookupAttrId(type, &PyId__anonymous_, &anon) < 0) { + return -1; + } if (anon == NULL) { - PyErr_Clear(); return 0; } anon_names = PySequence_Fast(anon, "_anonymous_ must be a sequence"); @@ -335,13 +337,17 @@ MakeAnonFields(PyObject *type) int PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) { + _Py_IDENTIFIER(_swappedbytes_); + _Py_IDENTIFIER(_use_broken_old_ctypes_structure_semantics_); + _Py_IDENTIFIER(_pack_); StgDictObject *stgdict, *basedict; Py_ssize_t len, offset, size, align, i; Py_ssize_t union_size, total_align; Py_ssize_t field_size = 0; int bitofs; - PyObject *isPacked; - int pack = 0; + PyObject *tmp; + int isPacked; + int pack; Py_ssize_t ffi_ofs; int big_endian; @@ -356,32 +362,59 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct if (fields == NULL) return 0; -#ifdef WORDS_BIGENDIAN - big_endian = PyObject_HasAttrString(type, "_swappedbytes_") ? 0 : 1; -#else - big_endian = PyObject_HasAttrString(type, "_swappedbytes_") ? 1 : 0; -#endif + if (_PyObject_LookupAttrId(type, &PyId__swappedbytes_, &tmp) < 0) { + return -1; + } + if (tmp) { + Py_DECREF(tmp); + big_endian = !PY_BIG_ENDIAN; + } + else { + big_endian = PY_BIG_ENDIAN; + } - use_broken_old_ctypes_semantics = \ - PyObject_HasAttrString(type, "_use_broken_old_ctypes_structure_semantics_"); + if (_PyObject_LookupAttrId(type, + &PyId__use_broken_old_ctypes_structure_semantics_, &tmp) < 0) + { + return -1; + } + if (tmp) { + Py_DECREF(tmp); + use_broken_old_ctypes_semantics = 1; + } + else { + use_broken_old_ctypes_semantics = 0; + } - isPacked = PyObject_GetAttrString(type, "_pack_"); - if (isPacked) { - pack = _PyLong_AsInt(isPacked); - if (pack < 0 || PyErr_Occurred()) { - Py_XDECREF(isPacked); - PyErr_SetString(PyExc_ValueError, - "_pack_ must be a non-negative integer"); + if (_PyObject_LookupAttrId(type, &PyId__pack_, &tmp) < 0) { + return -1; + } + if (tmp) { + isPacked = 1; + pack = _PyLong_AsInt(tmp); + Py_DECREF(tmp); + if (pack < 0) { + if (!PyErr_Occurred() || + PyErr_ExceptionMatches(PyExc_TypeError) || + PyErr_ExceptionMatches(PyExc_OverflowError)) + { + PyErr_SetString(PyExc_ValueError, + "_pack_ must be a non-negative integer"); + } return -1; } - Py_DECREF(isPacked); - } else - PyErr_Clear(); + } + else { + isPacked = 0; + pack = 0; + } - len = PySequence_Length(fields); + len = PySequence_Size(fields); if (len == -1) { - PyErr_SetString(PyExc_TypeError, - "'_fields_' must be a sequence of pairs"); + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_SetString(PyExc_TypeError, + "'_fields_' must be a sequence of pairs"); + } return -1; } From webhook-mailer at python.org Wed Dec 5 10:49:40 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 15:49:40 -0000 Subject: [Python-checkins] bpo-10496: posixpath.expanduser() catchs pwd.getpwuid() error (GH-10919) Message-ID: https://github.com/python/cpython/commit/f2f4555d8287ad217a1dba7bbd93103ad4daf3a8 commit: f2f4555d8287ad217a1dba7bbd93103ad4daf3a8 branch: master author: Victor Stinner committer: GitHub date: 2018-12-05T16:49:35+01:00 summary: bpo-10496: posixpath.expanduser() catchs pwd.getpwuid() error (GH-10919) * posixpath.expanduser() now returns the input path unchanged if the HOME environment variable is not set and pwd.getpwuid() raises KeyError (the current user identifier doesn't exist in the password database). * Add test_no_home_directory() to test_site. files: A Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst M Lib/posixpath.py M Lib/test/test_posixpath.py M Lib/test/test_site.py diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 7e3f3db4b6d0..4914a1728ab5 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -246,7 +246,12 @@ def expanduser(path): if i == 1: if 'HOME' not in os.environ: import pwd - userhome = pwd.getpwuid(os.getuid()).pw_dir + try: + userhome = pwd.getpwuid(os.getuid()).pw_dir + except KeyError: + # bpo-10496: if the current user identifier doesn't exist in the + # password database, return the path unchanged + return path else: userhome = os.environ['HOME'] else: @@ -257,6 +262,8 @@ def expanduser(path): try: pwent = pwd.getpwnam(name) except KeyError: + # bpo-10496: if the user name from the path doesn't exist in the + # password database, return the path unchanged return path userhome = pwent.pw_dir if isinstance(path, bytes): diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index ae59ef5927be..983e2dd6ff27 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -5,6 +5,7 @@ from posixpath import realpath, abspath, dirname, basename from test import support, test_genericpath from test.support import FakePath +from unittest import mock try: import posix @@ -242,42 +243,61 @@ def fake_lstat(path): def test_expanduser(self): self.assertEqual(posixpath.expanduser("foo"), "foo") self.assertEqual(posixpath.expanduser(b"foo"), b"foo") + + def test_expanduser_home_envvar(self): with support.EnvironmentVarGuard() as env: + env['HOME'] = '/home/victor' + self.assertEqual(posixpath.expanduser("~"), "/home/victor") + + # expanduser() strips trailing slash + env['HOME'] = '/home/victor/' + self.assertEqual(posixpath.expanduser("~"), "/home/victor") + for home in '/', '', '//', '///': with self.subTest(home=home): env['HOME'] = home self.assertEqual(posixpath.expanduser("~"), "/") self.assertEqual(posixpath.expanduser("~/"), "/") self.assertEqual(posixpath.expanduser("~/foo"), "/foo") - try: - import pwd - except ImportError: - pass - else: - self.assertIsInstance(posixpath.expanduser("~/"), str) - self.assertIsInstance(posixpath.expanduser(b"~/"), bytes) - # if home directory == root directory, this test makes no sense - if posixpath.expanduser("~") != '/': - self.assertEqual( - posixpath.expanduser("~") + "/", - posixpath.expanduser("~/") - ) - self.assertEqual( - posixpath.expanduser(b"~") + b"/", - posixpath.expanduser(b"~/") - ) - self.assertIsInstance(posixpath.expanduser("~root/"), str) - self.assertIsInstance(posixpath.expanduser("~foo/"), str) - self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes) - self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes) - - with support.EnvironmentVarGuard() as env: - # expanduser should fall back to using the password database - del env['HOME'] - home = pwd.getpwuid(os.getuid()).pw_dir - # $HOME can end with a trailing /, so strip it (see #17809) - home = home.rstrip("/") or '/' - self.assertEqual(posixpath.expanduser("~"), home) + + def test_expanduser_pwd(self): + pwd = support.import_module('pwd') + + self.assertIsInstance(posixpath.expanduser("~/"), str) + self.assertIsInstance(posixpath.expanduser(b"~/"), bytes) + + # if home directory == root directory, this test makes no sense + if posixpath.expanduser("~") != '/': + self.assertEqual( + posixpath.expanduser("~") + "/", + posixpath.expanduser("~/") + ) + self.assertEqual( + posixpath.expanduser(b"~") + b"/", + posixpath.expanduser(b"~/") + ) + self.assertIsInstance(posixpath.expanduser("~root/"), str) + self.assertIsInstance(posixpath.expanduser("~foo/"), str) + self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes) + self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes) + + with support.EnvironmentVarGuard() as env: + # expanduser should fall back to using the password database + del env['HOME'] + + home = pwd.getpwuid(os.getuid()).pw_dir + # $HOME can end with a trailing /, so strip it (see #17809) + home = home.rstrip("/") or '/' + self.assertEqual(posixpath.expanduser("~"), home) + + # bpo-10496: If the HOME environment variable is not set and the + # user (current identifier or name in the path) doesn't exist in + # the password database (pwd.getuid() or pwd.getpwnam() fail), + # expanduser() must return the path unchanged. + with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError), \ + mock.patch.object(pwd, 'getpwnam', side_effect=KeyError): + for path in ('~', '~/.local', '~vstinner/'): + self.assertEqual(posixpath.expanduser(path), path) def test_normpath(self): self.assertEqual(posixpath.normpath(""), ".") diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 33a8f1a44ccc..f38e8d853ada 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -6,6 +6,7 @@ """ import unittest import test.support +from test import support from test.support import (captured_stderr, TESTFN, EnvironmentVarGuard, change_cwd) import builtins @@ -19,6 +20,7 @@ import subprocess import sysconfig import tempfile +from unittest import mock from copy import copy # These tests are not particularly useful if Python was invoked with -S. @@ -256,6 +258,7 @@ def test_getusersitepackages(self): # the call sets USER_BASE *and* USER_SITE self.assertEqual(site.USER_SITE, user_site) self.assertTrue(user_site.startswith(site.USER_BASE), user_site) + self.assertEqual(site.USER_BASE, site.getuserbase()) def test_getsitepackages(self): site.PREFIXES = ['xoxo'] @@ -274,6 +277,40 @@ def test_getsitepackages(self): wanted = os.path.join('xoxo', 'lib', 'site-packages') self.assertEqual(dirs[1], wanted) + def test_no_home_directory(self): + # bpo-10496: getuserbase() and getusersitepackages() must not fail if + # the current user has no home directory (if expanduser() returns the + # path unchanged). + site.USER_SITE = None + site.USER_BASE = None + + with EnvironmentVarGuard() as environ, \ + mock.patch('os.path.expanduser', lambda path: path): + + del environ['PYTHONUSERBASE'] + del environ['APPDATA'] + + user_base = site.getuserbase() + self.assertTrue(user_base.startswith('~' + os.sep), + user_base) + + user_site = site.getusersitepackages() + self.assertTrue(user_site.startswith(user_base), user_site) + + with mock.patch('os.path.isdir', return_value=False) as mock_isdir, \ + mock.patch.object(site, 'addsitedir') as mock_addsitedir, \ + support.swap_attr(site, 'ENABLE_USER_SITE', True): + + # addusersitepackages() must not add user_site to sys.path + # if it is not an existing directory + known_paths = set() + site.addusersitepackages(known_paths) + + mock_isdir.assert_called_once_with(user_site) + mock_addsitedir.assert_not_called() + self.assertFalse(known_paths) + + class PthFile(object): """Helper class for handling testing of .pth files""" diff --git a/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst b/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst new file mode 100644 index 000000000000..232fcc6503b0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst @@ -0,0 +1,5 @@ +:func:`posixpath.expanduser` now returns the input *path* unchanged if the +``HOME`` environment variable is not set and the current user has no home +directory (if the current user identifier doesn't exist in the password +database). This change fix the :mod:`site` module if the current user doesn't +exist in the password database (if the user has no home directory). From webhook-mailer at python.org Wed Dec 5 11:08:01 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 16:08:01 -0000 Subject: [Python-checkins] bpo-10496: posixpath.expanduser() catchs pwd.getpwuid() error (GH-10919) Message-ID: https://github.com/python/cpython/commit/983d1ab0e6f4280f954bcba87db76e11131f1c33 commit: 983d1ab0e6f4280f954bcba87db76e11131f1c33 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T08:07:57-08:00 summary: bpo-10496: posixpath.expanduser() catchs pwd.getpwuid() error (GH-10919) * posixpath.expanduser() now returns the input path unchanged if the HOME environment variable is not set and pwd.getpwuid() raises KeyError (the current user identifier doesn't exist in the password database). * Add test_no_home_directory() to test_site. (cherry picked from commit f2f4555d8287ad217a1dba7bbd93103ad4daf3a8) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst M Lib/posixpath.py M Lib/test/test_posixpath.py M Lib/test/test_site.py diff --git a/Lib/posixpath.py b/Lib/posixpath.py index e92186c64e0d..ca578a5df35c 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -246,7 +246,12 @@ def expanduser(path): if i == 1: if 'HOME' not in os.environ: import pwd - userhome = pwd.getpwuid(os.getuid()).pw_dir + try: + userhome = pwd.getpwuid(os.getuid()).pw_dir + except KeyError: + # bpo-10496: if the current user identifier doesn't exist in the + # password database, return the path unchanged + return path else: userhome = os.environ['HOME'] else: @@ -257,6 +262,8 @@ def expanduser(path): try: pwent = pwd.getpwnam(name) except KeyError: + # bpo-10496: if the user name from the path doesn't exist in the + # password database, return the path unchanged return path userhome = pwent.pw_dir if isinstance(path, bytes): diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 9476ede53193..e73b31cb648b 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -5,6 +5,7 @@ from posixpath import realpath, abspath, dirname, basename from test import support, test_genericpath from test.support import FakePath +from unittest import mock try: import posix @@ -230,42 +231,61 @@ def fake_lstat(path): def test_expanduser(self): self.assertEqual(posixpath.expanduser("foo"), "foo") self.assertEqual(posixpath.expanduser(b"foo"), b"foo") + + def test_expanduser_home_envvar(self): with support.EnvironmentVarGuard() as env: + env['HOME'] = '/home/victor' + self.assertEqual(posixpath.expanduser("~"), "/home/victor") + + # expanduser() strips trailing slash + env['HOME'] = '/home/victor/' + self.assertEqual(posixpath.expanduser("~"), "/home/victor") + for home in '/', '', '//', '///': with self.subTest(home=home): env['HOME'] = home self.assertEqual(posixpath.expanduser("~"), "/") self.assertEqual(posixpath.expanduser("~/"), "/") self.assertEqual(posixpath.expanduser("~/foo"), "/foo") - try: - import pwd - except ImportError: - pass - else: - self.assertIsInstance(posixpath.expanduser("~/"), str) - self.assertIsInstance(posixpath.expanduser(b"~/"), bytes) - # if home directory == root directory, this test makes no sense - if posixpath.expanduser("~") != '/': - self.assertEqual( - posixpath.expanduser("~") + "/", - posixpath.expanduser("~/") - ) - self.assertEqual( - posixpath.expanduser(b"~") + b"/", - posixpath.expanduser(b"~/") - ) - self.assertIsInstance(posixpath.expanduser("~root/"), str) - self.assertIsInstance(posixpath.expanduser("~foo/"), str) - self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes) - self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes) - - with support.EnvironmentVarGuard() as env: - # expanduser should fall back to using the password database - del env['HOME'] - home = pwd.getpwuid(os.getuid()).pw_dir - # $HOME can end with a trailing /, so strip it (see #17809) - home = home.rstrip("/") or '/' - self.assertEqual(posixpath.expanduser("~"), home) + + def test_expanduser_pwd(self): + pwd = support.import_module('pwd') + + self.assertIsInstance(posixpath.expanduser("~/"), str) + self.assertIsInstance(posixpath.expanduser(b"~/"), bytes) + + # if home directory == root directory, this test makes no sense + if posixpath.expanduser("~") != '/': + self.assertEqual( + posixpath.expanduser("~") + "/", + posixpath.expanduser("~/") + ) + self.assertEqual( + posixpath.expanduser(b"~") + b"/", + posixpath.expanduser(b"~/") + ) + self.assertIsInstance(posixpath.expanduser("~root/"), str) + self.assertIsInstance(posixpath.expanduser("~foo/"), str) + self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes) + self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes) + + with support.EnvironmentVarGuard() as env: + # expanduser should fall back to using the password database + del env['HOME'] + + home = pwd.getpwuid(os.getuid()).pw_dir + # $HOME can end with a trailing /, so strip it (see #17809) + home = home.rstrip("/") or '/' + self.assertEqual(posixpath.expanduser("~"), home) + + # bpo-10496: If the HOME environment variable is not set and the + # user (current identifier or name in the path) doesn't exist in + # the password database (pwd.getuid() or pwd.getpwnam() fail), + # expanduser() must return the path unchanged. + with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError), \ + mock.patch.object(pwd, 'getpwnam', side_effect=KeyError): + for path in ('~', '~/.local', '~vstinner/'): + self.assertEqual(posixpath.expanduser(path), path) def test_normpath(self): self.assertEqual(posixpath.normpath(""), ".") diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 6cea58d934fb..a30bd2f0067f 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -6,6 +6,7 @@ """ import unittest import test.support +from test import support from test.support import (captured_stderr, TESTFN, EnvironmentVarGuard, change_cwd) import builtins @@ -19,6 +20,7 @@ import subprocess import sysconfig import tempfile +from unittest import mock from copy import copy # These tests are not particularly useful if Python was invoked with -S. @@ -258,6 +260,7 @@ def test_getusersitepackages(self): # the call sets USER_BASE *and* USER_SITE self.assertEqual(site.USER_SITE, user_site) self.assertTrue(user_site.startswith(site.USER_BASE), user_site) + self.assertEqual(site.USER_BASE, site.getuserbase()) def test_getsitepackages(self): site.PREFIXES = ['xoxo'] @@ -276,6 +279,40 @@ def test_getsitepackages(self): wanted = os.path.join('xoxo', 'lib', 'site-packages') self.assertEqual(dirs[1], wanted) + def test_no_home_directory(self): + # bpo-10496: getuserbase() and getusersitepackages() must not fail if + # the current user has no home directory (if expanduser() returns the + # path unchanged). + site.USER_SITE = None + site.USER_BASE = None + + with EnvironmentVarGuard() as environ, \ + mock.patch('os.path.expanduser', lambda path: path): + + del environ['PYTHONUSERBASE'] + del environ['APPDATA'] + + user_base = site.getuserbase() + self.assertTrue(user_base.startswith('~' + os.sep), + user_base) + + user_site = site.getusersitepackages() + self.assertTrue(user_site.startswith(user_base), user_site) + + with mock.patch('os.path.isdir', return_value=False) as mock_isdir, \ + mock.patch.object(site, 'addsitedir') as mock_addsitedir, \ + support.swap_attr(site, 'ENABLE_USER_SITE', True): + + # addusersitepackages() must not add user_site to sys.path + # if it is not an existing directory + known_paths = set() + site.addusersitepackages(known_paths) + + mock_isdir.assert_called_once_with(user_site) + mock_addsitedir.assert_not_called() + self.assertFalse(known_paths) + + class PthFile(object): """Helper class for handling testing of .pth files""" diff --git a/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst b/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst new file mode 100644 index 000000000000..232fcc6503b0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst @@ -0,0 +1,5 @@ +:func:`posixpath.expanduser` now returns the input *path* unchanged if the +``HOME`` environment variable is not set and the current user has no home +directory (if the current user identifier doesn't exist in the password +database). This change fix the :mod:`site` module if the current user doesn't +exist in the password database (if the user has no home directory). From webhook-mailer at python.org Wed Dec 5 11:21:45 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 16:21:45 -0000 Subject: [Python-checkins] bpo-10496: posixpath.expanduser() catchs pwd.getpwuid() error (GH-10919) (GH-10925) Message-ID: https://github.com/python/cpython/commit/31b635dbf0c7108f18bb3ce382b895374cff77fb commit: 31b635dbf0c7108f18bb3ce382b895374cff77fb branch: 3.6 author: Victor Stinner committer: GitHub date: 2018-12-05T17:21:37+01:00 summary: bpo-10496: posixpath.expanduser() catchs pwd.getpwuid() error (GH-10919) (GH-10925) * posixpath.expanduser() now returns the input path unchanged if the HOME environment variable is not set and pwd.getpwuid() raises KeyError (the current user identifier doesn't exist in the password database). * Add test_no_home_directory() to test_site. (cherry picked from commit f2f4555d8287ad217a1dba7bbd93103ad4daf3a8) files: A Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst M Lib/posixpath.py M Lib/test/test_posixpath.py M Lib/test/test_site.py diff --git a/Lib/posixpath.py b/Lib/posixpath.py index e92186c64e0d..ca578a5df35c 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -246,7 +246,12 @@ def expanduser(path): if i == 1: if 'HOME' not in os.environ: import pwd - userhome = pwd.getpwuid(os.getuid()).pw_dir + try: + userhome = pwd.getpwuid(os.getuid()).pw_dir + except KeyError: + # bpo-10496: if the current user identifier doesn't exist in the + # password database, return the path unchanged + return path else: userhome = os.environ['HOME'] else: @@ -257,6 +262,8 @@ def expanduser(path): try: pwent = pwd.getpwnam(name) except KeyError: + # bpo-10496: if the user name from the path doesn't exist in the + # password database, return the path unchanged return path userhome = pwent.pw_dir if isinstance(path, bytes): diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 9476ede53193..e73b31cb648b 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -5,6 +5,7 @@ from posixpath import realpath, abspath, dirname, basename from test import support, test_genericpath from test.support import FakePath +from unittest import mock try: import posix @@ -230,42 +231,61 @@ def fake_lstat(path): def test_expanduser(self): self.assertEqual(posixpath.expanduser("foo"), "foo") self.assertEqual(posixpath.expanduser(b"foo"), b"foo") + + def test_expanduser_home_envvar(self): with support.EnvironmentVarGuard() as env: + env['HOME'] = '/home/victor' + self.assertEqual(posixpath.expanduser("~"), "/home/victor") + + # expanduser() strips trailing slash + env['HOME'] = '/home/victor/' + self.assertEqual(posixpath.expanduser("~"), "/home/victor") + for home in '/', '', '//', '///': with self.subTest(home=home): env['HOME'] = home self.assertEqual(posixpath.expanduser("~"), "/") self.assertEqual(posixpath.expanduser("~/"), "/") self.assertEqual(posixpath.expanduser("~/foo"), "/foo") - try: - import pwd - except ImportError: - pass - else: - self.assertIsInstance(posixpath.expanduser("~/"), str) - self.assertIsInstance(posixpath.expanduser(b"~/"), bytes) - # if home directory == root directory, this test makes no sense - if posixpath.expanduser("~") != '/': - self.assertEqual( - posixpath.expanduser("~") + "/", - posixpath.expanduser("~/") - ) - self.assertEqual( - posixpath.expanduser(b"~") + b"/", - posixpath.expanduser(b"~/") - ) - self.assertIsInstance(posixpath.expanduser("~root/"), str) - self.assertIsInstance(posixpath.expanduser("~foo/"), str) - self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes) - self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes) - - with support.EnvironmentVarGuard() as env: - # expanduser should fall back to using the password database - del env['HOME'] - home = pwd.getpwuid(os.getuid()).pw_dir - # $HOME can end with a trailing /, so strip it (see #17809) - home = home.rstrip("/") or '/' - self.assertEqual(posixpath.expanduser("~"), home) + + def test_expanduser_pwd(self): + pwd = support.import_module('pwd') + + self.assertIsInstance(posixpath.expanduser("~/"), str) + self.assertIsInstance(posixpath.expanduser(b"~/"), bytes) + + # if home directory == root directory, this test makes no sense + if posixpath.expanduser("~") != '/': + self.assertEqual( + posixpath.expanduser("~") + "/", + posixpath.expanduser("~/") + ) + self.assertEqual( + posixpath.expanduser(b"~") + b"/", + posixpath.expanduser(b"~/") + ) + self.assertIsInstance(posixpath.expanduser("~root/"), str) + self.assertIsInstance(posixpath.expanduser("~foo/"), str) + self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes) + self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes) + + with support.EnvironmentVarGuard() as env: + # expanduser should fall back to using the password database + del env['HOME'] + + home = pwd.getpwuid(os.getuid()).pw_dir + # $HOME can end with a trailing /, so strip it (see #17809) + home = home.rstrip("/") or '/' + self.assertEqual(posixpath.expanduser("~"), home) + + # bpo-10496: If the HOME environment variable is not set and the + # user (current identifier or name in the path) doesn't exist in + # the password database (pwd.getuid() or pwd.getpwnam() fail), + # expanduser() must return the path unchanged. + with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError), \ + mock.patch.object(pwd, 'getpwnam', side_effect=KeyError): + for path in ('~', '~/.local', '~vstinner/'): + self.assertEqual(posixpath.expanduser(path), path) def test_normpath(self): self.assertEqual(posixpath.normpath(""), ".") diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index afdcf36f394d..6964a849335a 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -6,6 +6,7 @@ """ import unittest import test.support +from test import support from test.support import (captured_stderr, TESTFN, EnvironmentVarGuard, change_cwd) import builtins @@ -18,6 +19,7 @@ import shutil import subprocess import sysconfig +from unittest import mock from copy import copy # These tests are not particularly useful if Python was invoked with -S. @@ -243,6 +245,7 @@ def test_getusersitepackages(self): # the call sets USER_BASE *and* USER_SITE self.assertEqual(site.USER_SITE, user_site) self.assertTrue(user_site.startswith(site.USER_BASE), user_site) + self.assertEqual(site.USER_BASE, site.getuserbase()) def test_getsitepackages(self): site.PREFIXES = ['xoxo'] @@ -273,6 +276,41 @@ def test_getsitepackages(self): wanted = os.path.join('xoxo', 'lib', 'site-packages') self.assertEqual(dirs[1], wanted) + def test_no_home_directory(self): + # bpo-10496: getuserbase() and getusersitepackages() must not fail if + # the current user has no home directory (if expanduser() returns the + # path unchanged). + site.USER_SITE = None + site.USER_BASE = None + sysconfig._CONFIG_VARS = None + + with EnvironmentVarGuard() as environ, \ + mock.patch('os.path.expanduser', lambda path: path): + + del environ['PYTHONUSERBASE'] + del environ['APPDATA'] + + user_base = site.getuserbase() + self.assertTrue(user_base.startswith('~' + os.sep), + user_base) + + user_site = site.getusersitepackages() + self.assertTrue(user_site.startswith(user_base), user_site) + + with mock.patch('os.path.isdir', return_value=False) as mock_isdir, \ + mock.patch.object(site, 'addsitedir') as mock_addsitedir, \ + support.swap_attr(site, 'ENABLE_USER_SITE', True): + + # addusersitepackages() must not add user_site to sys.path + # if it is not an existing directory + known_paths = set() + site.addusersitepackages(known_paths) + + mock_isdir.assert_called_once_with(user_site) + mock_addsitedir.assert_not_called() + self.assertFalse(known_paths) + + class PthFile(object): """Helper class for handling testing of .pth files""" diff --git a/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst b/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst new file mode 100644 index 000000000000..232fcc6503b0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst @@ -0,0 +1,5 @@ +:func:`posixpath.expanduser` now returns the input *path* unchanged if the +``HOME`` environment variable is not set and the current user has no home +directory (if the current user identifier doesn't exist in the password +database). This change fix the :mod:`site` module if the current user doesn't +exist in the password database (if the user has no home directory). From webhook-mailer at python.org Wed Dec 5 12:50:30 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 17:50:30 -0000 Subject: [Python-checkins] bpo-34052: Prevent SQLite functions from setting callbacks on exceptions. (GH-8113) Message-ID: https://github.com/python/cpython/commit/5b25f1d03100e2283c1b129d461ba68ac0169a14 commit: 5b25f1d03100e2283c1b129d461ba68ac0169a14 branch: master author: Sergey Fedoseev committer: Serhiy Storchaka date: 2018-12-05T19:50:26+02:00 summary: bpo-34052: Prevent SQLite functions from setting callbacks on exceptions. (GH-8113) files: A Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst M Lib/sqlite3/test/regression.py M Modules/_sqlite/connection.c diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 34cd233535dc..1c59a3cd31c6 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -256,24 +256,6 @@ def CheckPragmaAutocommit(self): cur.execute("pragma page_size") row = cur.fetchone() - def CheckSetDict(self): - """ - See http://bugs.python.org/issue7478 - - It was possible to successfully register callbacks that could not be - hashed. Return codes of PyDict_SetItem were not checked properly. - """ - class NotHashable: - def __call__(self, *args, **kw): - pass - def __hash__(self): - raise TypeError() - var = NotHashable() - self.assertRaises(TypeError, self.con.create_function, var) - self.assertRaises(TypeError, self.con.create_aggregate, var) - self.assertRaises(TypeError, self.con.set_authorizer, var) - self.assertRaises(TypeError, self.con.set_progress_handler, var) - def CheckConnectionCall(self): """ Call a connection with a non-string SQL request: check error handling @@ -398,9 +380,72 @@ def callback(*args): support.gc_collect() +class UnhashableFunc: + __hash__ = 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,)) + return unittest.TestSuite(( + regression_suite, + unittest.makeSuite(UnhashableCallbacksTestCase), + )) def test(): runner = unittest.TextTestRunner() diff --git a/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst b/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst new file mode 100644 index 000000000000..5aa3cc9a81d7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst @@ -0,0 +1,7 @@ +:meth:`sqlite3.Connection.create_aggregate`, +:meth:`sqlite3.Connection.create_function`, +:meth:`sqlite3.Connection.set_authorizer`, +:meth:`sqlite3.Connection.set_progress_handler` methods raises TypeError +when unhashable objects are passed as callable. These methods now don't pass +such objects to SQLite API. Previous behavior could lead to segfaults. Patch +by Sergey Fedoseev. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index b59d7d28cfd5..fe0d03bb2b02 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -843,7 +843,9 @@ 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, @@ -857,12 +859,8 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating function"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) - return NULL; - - Py_RETURN_NONE; } + Py_RETURN_NONE; } PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) @@ -883,17 +881,16 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje 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); if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) - return NULL; - - Py_RETURN_NONE; } + Py_RETURN_NONE; } static int _authorizer_callback(void* user_arg, int action, const char* arg1, const char* arg2 , const char* dbname, const char* access_attempt_source) @@ -1006,17 +1003,15 @@ 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"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) - return NULL; - - Py_RETURN_NONE; } + Py_RETURN_NONE; } static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) @@ -1039,9 +1034,9 @@ static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* s /* None clears the progress handler previously set */ sqlite3_progress_handler(self->db, 0, 0, (void*)0); } else { - sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); if (PyDict_SetItem(self->function_pinboard, progress_handler, Py_None) == -1) return NULL; + sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); } Py_RETURN_NONE; From webhook-mailer at python.org Wed Dec 5 13:29:26 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 18:29:26 -0000 Subject: [Python-checkins] bpo-34987: Fix a possible null pointer dereference in _pickle.c's save_reduce(). (GH-9886) Message-ID: https://github.com/python/cpython/commit/25d389789c59a52a31770f7c50ce9e02a8909190 commit: 25d389789c59a52a31770f7c50ce9e02a8909190 branch: master author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-05T20:29:20+02:00 summary: bpo-34987: Fix a possible null pointer dereference in _pickle.c's save_reduce(). (GH-9886) files: M Modules/_pickle.c diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 39f8b750ed64..c4fe349187c4 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -3840,7 +3840,10 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) if (obj != NULL) { obj_class = get_class(obj); - p = obj_class != cls; /* true iff a problem */ + if (obj_class == NULL) { + return -1; + } + p = obj_class != cls; Py_DECREF(obj_class); if (p) { PyErr_SetString(st->PicklingError, "args[0] from " From webhook-mailer at python.org Wed Dec 5 14:10:27 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 19:10:27 -0000 Subject: [Python-checkins] bpo-34604: Use %R because of invisible characters or trailing whitespaces. (GH-9165) Message-ID: https://github.com/python/cpython/commit/34c7f0c04e2b4e715b2c3df1875af8939fbe7d0b commit: 34c7f0c04e2b4e715b2c3df1875af8939fbe7d0b branch: master author: William Grzybowski committer: Serhiy Storchaka date: 2018-12-05T21:10:18+02:00 summary: bpo-34604: Use %R because of invisible characters or trailing whitespaces. (GH-9165) files: M Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst M Modules/grpmodule.c M Modules/pwdmodule.c diff --git a/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst b/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst index 562a69124b3d..958b74fd0da6 100644 --- a/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst +++ b/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst @@ -1,2 +1,3 @@ Fix possible mojibake in the error message of `pwd.getpwnam` and -`grp.getgrnam`. Patch by William Grzybowski. +`grp.getgrnam` using string representation because of invisible characters +or trailing whitespaces. Patch by William Grzybowski. diff --git a/Modules/grpmodule.c b/Modules/grpmodule.c index d426f083111e..ab766b985093 100644 --- a/Modules/grpmodule.c +++ b/Modules/grpmodule.c @@ -247,7 +247,7 @@ grp_getgrnam_impl(PyObject *module, PyObject *name) PyErr_NoMemory(); } else { - PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %S", name); + PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %R", name); } goto out; } diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index fd11f848b2ce..e0232b8d589b 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -257,7 +257,7 @@ pwd_getpwnam_impl(PyObject *module, PyObject *name) } else { PyErr_Format(PyExc_KeyError, - "getpwnam(): name not found: %S", name); + "getpwnam(): name not found: %R", name); } goto out; } From webhook-mailer at python.org Wed Dec 5 14:32:26 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 19:32:26 -0000 Subject: [Python-checkins] bpo-34185: Fix test module collision in test_bdb when ran as script. (GH-8537) Message-ID: https://github.com/python/cpython/commit/54fd45505b3a365e6d53441e6dd7e0d1ec13b46f commit: 54fd45505b3a365e6d53441e6dd7e0d1ec13b46f branch: master author: Alex H <1884912+lajarre at users.noreply.github.com> committer: Serhiy Storchaka date: 2018-12-05T21:32:16+02:00 summary: bpo-34185: Fix test module collision in test_bdb when ran as script. (GH-8537) When running test_bdb.py as a script, `import test_module` would be importing the existing Lib/test/test_modules.py instead of the tempcwd/test_module.py module which was dynamically created by test_bdb.py itself. files: M Lib/test/test_bdb.py diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py index 8a9c4dd4376c..751dd9f69597 100644 --- a/Lib/test/test_bdb.py +++ b/Lib/test/test_bdb.py @@ -549,11 +549,11 @@ def create_modules(modules): def break_in_func(funcname, fname=__file__, temporary=False, cond=None): return 'break', (fname, None, temporary, cond, funcname) -TEST_MODULE = 'test_module' +TEST_MODULE = 'test_module_for_bdb' TEST_MODULE_FNAME = TEST_MODULE + '.py' def tfunc_import(): - import test_module - test_module.main() + import test_module_for_bdb + test_module_for_bdb.main() def tfunc_main(): lno = 2 @@ -971,9 +971,9 @@ def main(): ('return', 3, 'main'), ('step', ), ('return', 1, ''), ('quit', ), ] - import test_module + import test_module_for_bdb with TracerRun(self) as tracer: - tracer.runeval('test_module.main()', globals(), locals()) + tracer.runeval('test_module_for_bdb.main()', globals(), locals()) class IssuesTestCase(BaseTestCase): """Test fixed bdb issues.""" @@ -983,7 +983,7 @@ def test_step_at_return_with_no_trace_in_caller(self): # Check that the tracer does step into the caller frame when the # trace function is not set in that frame. code_1 = """ - from test_module_2 import func + from test_module_for_bdb_2 import func def main(): func() lno = 5 @@ -994,12 +994,12 @@ def func(): """ modules = { TEST_MODULE: code_1, - 'test_module_2': code_2, + 'test_module_for_bdb_2': code_2, } with create_modules(modules): self.expect_set = [ ('line', 2, 'tfunc_import'), - break_in_func('func', 'test_module_2.py'), + break_in_func('func', 'test_module_for_bdb_2.py'), ('None', 2, 'tfunc_import'), ('continue', ), ('line', 3, 'func', ({1:1}, [])), ('step', ), ('return', 3, 'func'), ('step', ), From webhook-mailer at python.org Wed Dec 5 14:35:46 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 19:35:46 -0000 Subject: [Python-checkins] bpo-34987: Fix a possible null pointer dereference in _pickle.c's save_reduce(). (GH-9886) Message-ID: https://github.com/python/cpython/commit/e2f376f284b7bf1388d85e99dce646cabc507016 commit: e2f376f284b7bf1388d85e99dce646cabc507016 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T11:35:41-08:00 summary: bpo-34987: Fix a possible null pointer dereference in _pickle.c's save_reduce(). (GH-9886) (cherry picked from commit 25d389789c59a52a31770f7c50ce9e02a8909190) Co-authored-by: Zackery Spytz files: M Modules/_pickle.c diff --git a/Modules/_pickle.c b/Modules/_pickle.c index a1a90bd95cae..57bb771addde 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -3838,7 +3838,10 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) if (obj != NULL) { obj_class = get_class(obj); - p = obj_class != cls; /* true iff a problem */ + if (obj_class == NULL) { + return -1; + } + p = obj_class != cls; Py_DECREF(obj_class); if (p) { PyErr_SetString(st->PicklingError, "args[0] from " From webhook-mailer at python.org Wed Dec 5 14:35:51 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 19:35:51 -0000 Subject: [Python-checkins] bpo-34987: Fix a possible null pointer dereference in _pickle.c's save_reduce(). (GH-9886) Message-ID: https://github.com/python/cpython/commit/92d912c344e6c21de46da29f0dc45b7e476fa79d commit: 92d912c344e6c21de46da29f0dc45b7e476fa79d branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T11:35:47-08:00 summary: bpo-34987: Fix a possible null pointer dereference in _pickle.c's save_reduce(). (GH-9886) (cherry picked from commit 25d389789c59a52a31770f7c50ce9e02a8909190) Co-authored-by: Zackery Spytz files: M Modules/_pickle.c diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 389bb6701c5f..8c8163560f11 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -3743,7 +3743,10 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) if (obj != NULL) { obj_class = get_class(obj); - p = obj_class != cls; /* true iff a problem */ + if (obj_class == NULL) { + return -1; + } + p = obj_class != cls; Py_DECREF(obj_class); if (p) { PyErr_SetString(st->PicklingError, "args[0] from " From webhook-mailer at python.org Wed Dec 5 14:42:50 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 19:42:50 -0000 Subject: [Python-checkins] Move __missing__ after __delitem__ in Data model. (GH-10923) Message-ID: https://github.com/python/cpython/commit/1ce853f37783575e2b3aaa159ddcebc8660830ef commit: 1ce853f37783575e2b3aaa159ddcebc8660830ef branch: master author: Andre Delfino committer: Serhiy Storchaka date: 2018-12-05T21:42:44+02:00 summary: Move __missing__ after __delitem__ in Data model. (GH-10923) files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 7e2d2e66cac8..b20b708e9575 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2146,6 +2146,8 @@ through the container; for mappings, :meth:`__iter__` should be the same as .. versionadded:: 3.4 +.. index:: object: slice + .. note:: Slicing is done exclusively with the following three methods. A call like :: @@ -2161,8 +2163,6 @@ through the container; for mappings, :meth:`__iter__` should be the same as .. method:: object.__getitem__(self, key) - .. index:: object: slice - Called to implement evaluation of ``self[key]``. For sequence types, the accepted keys should be integers and slice objects. Note that the special interpretation of negative indexes (if the class wishes to emulate a sequence @@ -2178,12 +2178,6 @@ through the container; for mappings, :meth:`__iter__` should be the same as indexes to allow proper detection of the end of the sequence. -.. method:: object.__missing__(self, key) - - Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses - when key is not in the dictionary. - - .. method:: object.__setitem__(self, key, value) Called to implement assignment to ``self[key]``. Same note as for @@ -2202,6 +2196,12 @@ through the container; for mappings, :meth:`__iter__` should be the same as values as for the :meth:`__getitem__` method. +.. method:: object.__missing__(self, key) + + Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses + when key is not in the dictionary. + + .. method:: object.__iter__(self) This method is called when an iterator is required for a container. This method From webhook-mailer at python.org Wed Dec 5 14:45:34 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 19:45:34 -0000 Subject: [Python-checkins] Correct a couple of unbalanced parenthesis. (GH-10779) Message-ID: https://github.com/python/cpython/commit/55f41e45b4318cbe19209f5144641344d0049fb8 commit: 55f41e45b4318cbe19209f5144641344d0049fb8 branch: master author: Andre Delfino committer: Serhiy Storchaka date: 2018-12-05T21:45:30+02:00 summary: Correct a couple of unbalanced parenthesis. (GH-10779) files: M Doc/c-api/buffer.rst M Doc/faq/extending.rst M Doc/library/crypt.rst M Doc/library/email.compat32-message.rst M Doc/library/email.message.rst M Doc/library/email.parser.rst M Doc/library/exceptions.rst M Doc/library/stdtypes.rst M Doc/library/sysconfig.rst M Doc/reference/expressions.rst M Doc/using/windows.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 5a9a46fc67e8..33abb5bb94d9 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -198,7 +198,7 @@ a buffer, see :c:func:`PyObject_GetBuffer`. indicates that no de-referencing should occur (striding in a contiguous memory block). - If all suboffsets are negative (i.e. no de-referencing is needed, then + If all suboffsets are negative (i.e. no de-referencing is needed), then this field must be NULL (the default value). This type of array representation is used by the Python Imaging Library diff --git a/Doc/faq/extending.rst b/Doc/faq/extending.rst index b611bb48012a..74e1af6ef24d 100644 --- a/Doc/faq/extending.rst +++ b/Doc/faq/extending.rst @@ -277,7 +277,7 @@ However sometimes you have to run the embedded Python interpreter in the same thread as your rest application and you can't allow the :c:func:`PyRun_InteractiveLoop` to stop while waiting for user input. The one solution then is to call :c:func:`PyParser_ParseString` and test for ``e.error`` -equal to ``E_EOF``, which means the input is incomplete). Here's a sample code +equal to ``E_EOF``, which means the input is incomplete. Here's a sample code fragment, untested, inspired by code from Alex Farber:: #include diff --git a/Doc/library/crypt.rst b/Doc/library/crypt.rst index dd62cb32b9d1..43d4b5b749e4 100644 --- a/Doc/library/crypt.rst +++ b/Doc/library/crypt.rst @@ -91,7 +91,7 @@ The :mod:`crypt` module defines the following functions: may be available on all platforms), or a full encrypted password including salt, as returned by this function. If *salt* is not provided, the strongest method will be used (as returned by - :func:`methods`. + :func:`methods`). Checking a password is usually done by passing the plain-text password as *word* and the full results of a previous :func:`crypt` call, diff --git a/Doc/library/email.compat32-message.rst b/Doc/library/email.compat32-message.rst index d88495089482..f02b35b910a0 100644 --- a/Doc/library/email.compat32-message.rst +++ b/Doc/library/email.compat32-message.rst @@ -152,7 +152,7 @@ Here are the methods of the :class:`Message` class: Return ``True`` if the message's payload is a list of sub-\ :class:`Message` objects, otherwise return ``False``. When :meth:`is_multipart` returns ``False``, the payload should be a string - object (which might be a CTE encoded binary payload. (Note that + object (which might be a CTE encoded binary payload). (Note that :meth:`is_multipart` returning ``True`` does not necessarily mean that "msg.get_content_maintype() == 'multipart'" will return the ``True``. For example, ``is_multipart`` will return ``True`` when the diff --git a/Doc/library/email.message.rst b/Doc/library/email.message.rst index ff5404553e96..77b8099a7d4c 100644 --- a/Doc/library/email.message.rst +++ b/Doc/library/email.message.rst @@ -92,7 +92,7 @@ message objects. .. method:: __str__() - Equivalent to `as_string(policy=self.policy.clone(utf8=True)`. Allows + Equivalent to ``as_string(policy=self.policy.clone(utf8=True))``. Allows ``str(msg)`` to produce a string containing the serialized message in a readable format. @@ -379,7 +379,7 @@ message objects. Note that existing parameter values of headers may be accessed through the :attr:`~email.headerregistry.BaseHeader.params` attribute of the - header value (for example, ``msg['Content-Type'].params['charset']``. + header value (for example, ``msg['Content-Type'].params['charset']``). .. versionchanged:: 3.4 ``replace`` keyword was added. @@ -679,7 +679,7 @@ message objects. specified by the current :mod:`~email.policy`. If the added part has no :mailheader:`Content-Disposition` header, add one with the value ``attachment``. This method can be used both for explicit attachments - (:mailheader:`Content-Disposition: attachment` and ``inline`` attachments + (:mailheader:`Content-Disposition: attachment`) and ``inline`` attachments (:mailheader:`Content-Disposition: inline`), by passing appropriate options to the ``content_manager``. diff --git a/Doc/library/email.parser.rst b/Doc/library/email.parser.rst index 49b4e992708a..d9a61616bbbd 100644 --- a/Doc/library/email.parser.rst +++ b/Doc/library/email.parser.rst @@ -164,7 +164,7 @@ message body, instead setting the payload to the raw body. envelope header. The header block is terminated either by the end of the data or by a blank line. Following the header block is the body of the message (which may contain MIME-encoded subparts, including subparts - with a :mailheader:`Content-Transfer-Encoding` of ``8bit``. + with a :mailheader:`Content-Transfer-Encoding` of ``8bit``). Optional *headersonly* is a flag specifying whether to stop parsing after reading the headers or not. The default is ``False``, meaning it parses diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index e33b88673f75..57ed29145816 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -52,7 +52,7 @@ will be set as :attr:`__cause__` on the raised exception. Setting :attr:`__cause__` also implicitly sets the :attr:`__suppress_context__` attribute to ``True``, so that using ``raise new_exc from None`` effectively replaces the old exception with the new one for display -purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`, while +purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`), while leaving the old exception available in :attr:`__context__` for introspection when debugging. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 3375f64d94d2..6ddf41a74a4c 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -443,7 +443,7 @@ Notes: (4) Performing these calculations with at least one extra sign extension bit in a finite two's complement representation (a working bit-width of - ``1 + max(x.bit_length(), y.bit_length()`` or more) is sufficient to get the + ``1 + max(x.bit_length(), y.bit_length())`` or more) is sufficient to get the same result as if there were an infinite number of sign bits. @@ -3222,7 +3222,7 @@ place, and instead produce new objects. Return a copy of the sequence left filled with ASCII ``b'0'`` digits to make a sequence of length *width*. A leading sign prefix (``b'+'``/ - ``b'-'`` is handled by inserting the padding *after* the sign character + ``b'-'``) is handled by inserting the padding *after* the sign character rather than before. For :class:`bytes` objects, the original sequence is returned if *width* is less than or equal to ``len(seq)``. diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index 9f549fbc5130..b5a1da80c686 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -185,7 +185,7 @@ Other functions Windows will return one of: - - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + - win-amd64 (64bit Windows on AMD64, aka x86_64, Intel64, and EM64T) - win32 (all others - specifically, sys.platform is returned) Mac OS X can return: diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index fb27857a6f42..571e6fa191c8 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1639,7 +1639,7 @@ returned; otherwise, *y* is evaluated and the resulting value is returned. The expression ``x or y`` first evaluates *x*; if *x* is true, its value is returned; otherwise, *y* is evaluated and the resulting value is returned. -(Note that neither :keyword:`and` nor :keyword:`or` restrict the value and type +Note that neither :keyword:`and` nor :keyword:`or` restrict the value and type they return to ``False`` and ``True``, but rather return the last evaluated argument. This is sometimes useful, e.g., if ``s`` is a string that should be replaced by a default value if it is empty, the expression ``s or 'foo'`` yields diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index e93fa0a78d1f..296d51b5a587 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -613,11 +613,11 @@ user's "application data" directory (i.e. the directory returned by calling the Windows function ``SHGetFolderPath`` with ``CSIDL_LOCAL_APPDATA``) and ``py.ini`` in the same directory as the launcher. The same .ini files are used for both the 'console' version of the launcher (i.e. py.exe) and for the 'windows' version -(i.e. pyw.exe) +(i.e. pyw.exe). Customization specified in the "application directory" will have precedence over the one next to the executable, so a user, who may not have write access to the -.ini file next to the launcher, can override commands in that global .ini file) +.ini file next to the launcher, can override commands in that global .ini file. Customizing default Python versions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From webhook-mailer at python.org Wed Dec 5 14:46:29 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 19:46:29 -0000 Subject: [Python-checkins] bpo-34738: Add directory entries in ZIP files created by distutils. (GH-9419) Message-ID: https://github.com/python/cpython/commit/67a93b3a0b3814e97ef9d077b21325fc8ce351b2 commit: 67a93b3a0b3814e97ef9d077b21325fc8ce351b2 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-05T21:46:25+02:00 summary: bpo-34738: Add directory entries in ZIP files created by distutils. (GH-9419) files: A Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst M Lib/distutils/archive_util.py M Lib/distutils/tests/test_archive_util.py M Lib/distutils/tests/test_bdist_dumb.py M Lib/distutils/tests/test_sdist.py diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py index 78ae5757c36d..b002dc3b8456 100644 --- a/Lib/distutils/archive_util.py +++ b/Lib/distutils/archive_util.py @@ -166,7 +166,15 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED) + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) + zip.write(path, path) + log.info("adding '%s'", path) for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) + zip.write(path, path) + log.info("adding '%s'", path) for name in filenames: path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): diff --git a/Lib/distutils/tests/test_archive_util.py b/Lib/distutils/tests/test_archive_util.py index 14ba4ca34b4a..e9aad0e40fd1 100644 --- a/Lib/distutils/tests/test_archive_util.py +++ b/Lib/distutils/tests/test_archive_util.py @@ -122,12 +122,13 @@ def _tarinfo(self, path): try: names = tar.getnames() names.sort() - return tuple(names) + return names finally: tar.close() - _created_files = ('dist', 'dist/file1', 'dist/file2', - 'dist/sub', 'dist/sub/file3', 'dist/sub2') + _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', + 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] + _created_files = [p.rstrip('/') for p in _zip_created_files] def _create_files(self): # creating something to tar @@ -244,8 +245,7 @@ def test_make_zipfile(self): tarball = base_name + '.zip' self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), - ['dist/file1', 'dist/file2', 'dist/sub/file3']) + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile_no_zlib(self): @@ -271,8 +271,7 @@ def fake_zipfile(*a, **kw): [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), - ['dist/file1', 'dist/file2', 'dist/sub/file3']) + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) def test_check_archive_formats(self): self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), diff --git a/Lib/distutils/tests/test_bdist_dumb.py b/Lib/distutils/tests/test_bdist_dumb.py index c8ccdc2383de..01a233bce37f 100644 --- a/Lib/distutils/tests/test_bdist_dumb.py +++ b/Lib/distutils/tests/test_bdist_dumb.py @@ -84,7 +84,7 @@ def test_simple_built(self): finally: fp.close() - contents = sorted(os.path.basename(fn) for fn in contents) + contents = sorted(filter(None, map(os.path.basename, contents))) wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] if not sys.dont_write_bytecode: wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) diff --git a/Lib/distutils/tests/test_sdist.py b/Lib/distutils/tests/test_sdist.py index 5444b815a8b2..23db1269591d 100644 --- a/Lib/distutils/tests/test_sdist.py +++ b/Lib/distutils/tests/test_sdist.py @@ -128,7 +128,9 @@ def test_prune_file_list(self): zip_file.close() # making sure everything has been pruned correctly - self.assertEqual(len(content), 4) + expected = ['', 'PKG-INFO', 'README', 'setup.py', + 'somecode/', 'somecode/__init__.py'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') @unittest.skipIf(find_executable('tar') is None, @@ -226,7 +228,13 @@ def test_add_defaults(self): zip_file.close() # making sure everything was added - self.assertEqual(len(content), 12) + expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', + 'data/', 'data/data.dt', 'inroot.txt', + 'scripts/', 'scripts/script.py', 'setup.py', + 'some/', 'some/file.txt', 'some/other_file.txt', + 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', + 'somecode/doc.txt'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) # checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) diff --git a/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst b/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst new file mode 100644 index 000000000000..c3f402d39ae0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst @@ -0,0 +1,2 @@ +ZIP files created by :mod:`distutils` will now include entries for +directories. From webhook-mailer at python.org Wed Dec 5 14:54:47 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 19:54:47 -0000 Subject: [Python-checkins] bpo-34185: Fix test module collision in test_bdb when ran as script. (GH-8537) Message-ID: https://github.com/python/cpython/commit/c7c4e938b98068e8e4e5fe56d441db696d47de78 commit: c7c4e938b98068e8e4e5fe56d441db696d47de78 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T11:54:42-08:00 summary: bpo-34185: Fix test module collision in test_bdb when ran as script. (GH-8537) When running test_bdb.py as a script, `import test_module` would be importing the existing Lib/test/test_modules.py instead of the tempcwd/test_module.py module which was dynamically created by test_bdb.py itself. (cherry picked from commit 54fd45505b3a365e6d53441e6dd7e0d1ec13b46f) Co-authored-by: Alex H <1884912+lajarre at users.noreply.github.com> files: M Lib/test/test_bdb.py diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py index 616c3a864984..55f42e2535b7 100644 --- a/Lib/test/test_bdb.py +++ b/Lib/test/test_bdb.py @@ -549,11 +549,11 @@ def create_modules(modules): def break_in_func(funcname, fname=__file__, temporary=False, cond=None): return 'break', (fname, None, temporary, cond, funcname) -TEST_MODULE = 'test_module' +TEST_MODULE = 'test_module_for_bdb' TEST_MODULE_FNAME = TEST_MODULE + '.py' def tfunc_import(): - import test_module - test_module.main() + import test_module_for_bdb + test_module_for_bdb.main() def tfunc_main(): lno = 2 @@ -971,9 +971,9 @@ def main(): ('return', 3, 'main'), ('step', ), ('return', 1, ''), ('quit', ), ] - import test_module + import test_module_for_bdb with TracerRun(self) as tracer: - tracer.runeval('test_module.main()', globals(), locals()) + tracer.runeval('test_module_for_bdb.main()', globals(), locals()) class IssuesTestCase(BaseTestCase): """Test fixed bdb issues.""" @@ -983,7 +983,7 @@ def test_step_at_return_with_no_trace_in_caller(self): # Check that the tracer does step into the caller frame when the # trace function is not set in that frame. code_1 = """ - from test_module_2 import func + from test_module_for_bdb_2 import func def main(): func() lno = 5 @@ -994,12 +994,12 @@ def func(): """ modules = { TEST_MODULE: code_1, - 'test_module_2': code_2, + 'test_module_for_bdb_2': code_2, } with create_modules(modules): self.expect_set = [ ('line', 2, 'tfunc_import'), - break_in_func('func', 'test_module_2.py'), + break_in_func('func', 'test_module_for_bdb_2.py'), ('None', 2, 'tfunc_import'), ('continue', ), ('line', 3, 'func', ({1:1}, [])), ('step', ), ('return', 3, 'func'), ('step', ), From webhook-mailer at python.org Wed Dec 5 15:05:01 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 20:05:01 -0000 Subject: [Python-checkins] bpo-34185: Fix test module collision in test_bdb when ran as script. (GH-8537) Message-ID: https://github.com/python/cpython/commit/bacc272afc165df21c607aae4ff7bfa21ae1979d commit: bacc272afc165df21c607aae4ff7bfa21ae1979d branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T12:04:57-08:00 summary: bpo-34185: Fix test module collision in test_bdb when ran as script. (GH-8537) When running test_bdb.py as a script, `import test_module` would be importing the existing Lib/test/test_modules.py instead of the tempcwd/test_module.py module which was dynamically created by test_bdb.py itself. (cherry picked from commit 54fd45505b3a365e6d53441e6dd7e0d1ec13b46f) Co-authored-by: Alex H <1884912+lajarre at users.noreply.github.com> files: M Lib/test/test_bdb.py diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py index d3ff8fc76860..3ad70e21c878 100644 --- a/Lib/test/test_bdb.py +++ b/Lib/test/test_bdb.py @@ -569,11 +569,11 @@ def create_modules(modules): def break_in_func(funcname, fname=__file__, temporary=False, cond=None): return 'break', (fname, None, temporary, cond, funcname) -TEST_MODULE = 'test_module' +TEST_MODULE = 'test_module_for_bdb' TEST_MODULE_FNAME = TEST_MODULE + '.py' def tfunc_import(): - import test_module - test_module.main() + import test_module_for_bdb + test_module_for_bdb.main() def tfunc_main(): lno = 2 @@ -985,9 +985,9 @@ def main(): ('return', 3, 'main'), ('step', ), ('return', 1, ''), ('quit', ), ] - import test_module + import test_module_for_bdb with TracerRun(self) as tracer: - tracer.runeval('test_module.main()', globals(), locals()) + tracer.runeval('test_module_for_bdb.main()', globals(), locals()) class IssuesTestCase(BaseTestCase): """Test fixed bdb issues.""" @@ -997,7 +997,7 @@ def test_step_at_return_with_no_trace_in_caller(self): # Check that the tracer does step into the caller frame when the # trace function is not set in that frame. code_1 = """ - from test_module_2 import func + from test_module_for_bdb_2 import func def main(): func() lno = 5 @@ -1008,12 +1008,12 @@ def func(): """ modules = { TEST_MODULE: code_1, - 'test_module_2': code_2, + 'test_module_for_bdb_2': code_2, } with create_modules(modules): self.expect_set = [ ('line', 2, 'tfunc_import'), - break_in_func('func', 'test_module_2.py'), + break_in_func('func', 'test_module_for_bdb_2.py'), ('None', 2, 'tfunc_import'), ('continue', ), ('line', 3, 'func', ({1:1}, [])), ('step', ), ('return', 3, 'func'), ('step', ), From webhook-mailer at python.org Wed Dec 5 15:11:19 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 20:11:19 -0000 Subject: [Python-checkins] Move __missing__ after __delitem__ in Data model. (GH-10923) Message-ID: https://github.com/python/cpython/commit/d2c7c1f7681bee77b221fb645a15a548bb3dd745 commit: d2c7c1f7681bee77b221fb645a15a548bb3dd745 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T12:11:16-08:00 summary: Move __missing__ after __delitem__ in Data model. (GH-10923) (cherry picked from commit 1ce853f37783575e2b3aaa159ddcebc8660830ef) Co-authored-by: Andre Delfino files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 02319509ca37..e6fa8a0d2a6f 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2147,6 +2147,8 @@ through the container; for mappings, :meth:`__iter__` should be the same as .. versionadded:: 3.4 +.. index:: object: slice + .. note:: Slicing is done exclusively with the following three methods. A call like :: @@ -2162,8 +2164,6 @@ through the container; for mappings, :meth:`__iter__` should be the same as .. method:: object.__getitem__(self, key) - .. index:: object: slice - Called to implement evaluation of ``self[key]``. For sequence types, the accepted keys should be integers and slice objects. Note that the special interpretation of negative indexes (if the class wishes to emulate a sequence @@ -2179,12 +2179,6 @@ through the container; for mappings, :meth:`__iter__` should be the same as indexes to allow proper detection of the end of the sequence. -.. method:: object.__missing__(self, key) - - Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses - when key is not in the dictionary. - - .. method:: object.__setitem__(self, key, value) Called to implement assignment to ``self[key]``. Same note as for @@ -2203,6 +2197,12 @@ through the container; for mappings, :meth:`__iter__` should be the same as values as for the :meth:`__getitem__` method. +.. method:: object.__missing__(self, key) + + Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses + when key is not in the dictionary. + + .. method:: object.__iter__(self) This method is called when an iterator is required for a container. This method From webhook-mailer at python.org Wed Dec 5 15:13:37 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 20:13:37 -0000 Subject: [Python-checkins] Move __missing__ after __delitem__ in Data model. (GH-10923) Message-ID: https://github.com/python/cpython/commit/9b3d6a1f9772e810afaa0dca106bd0e4d5f4be9f commit: 9b3d6a1f9772e810afaa0dca106bd0e4d5f4be9f branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T12:13:34-08:00 summary: Move __missing__ after __delitem__ in Data model. (GH-10923) (cherry picked from commit 1ce853f37783575e2b3aaa159ddcebc8660830ef) Co-authored-by: Andre Delfino files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index abf06357e7f9..143376bc5ce0 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2045,6 +2045,8 @@ through the container; for mappings, :meth:`__iter__` should be the same as .. versionadded:: 3.4 +.. index:: object: slice + .. note:: Slicing is done exclusively with the following three methods. A call like :: @@ -2060,8 +2062,6 @@ through the container; for mappings, :meth:`__iter__` should be the same as .. method:: object.__getitem__(self, key) - .. index:: object: slice - Called to implement evaluation of ``self[key]``. For sequence types, the accepted keys should be integers and slice objects. Note that the special interpretation of negative indexes (if the class wishes to emulate a sequence @@ -2077,12 +2077,6 @@ through the container; for mappings, :meth:`__iter__` should be the same as indexes to allow proper detection of the end of the sequence. -.. method:: object.__missing__(self, key) - - Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses - when key is not in the dictionary. - - .. method:: object.__setitem__(self, key, value) Called to implement assignment to ``self[key]``. Same note as for @@ -2101,6 +2095,12 @@ through the container; for mappings, :meth:`__iter__` should be the same as values as for the :meth:`__getitem__` method. +.. method:: object.__missing__(self, key) + + Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses + when key is not in the dictionary. + + .. method:: object.__iter__(self) This method is called when an iterator is required for a container. This method From webhook-mailer at python.org Wed Dec 5 15:15:57 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 20:15:57 -0000 Subject: [Python-checkins] Correct a couple of unbalanced parenthesis. (GH-10779) Message-ID: https://github.com/python/cpython/commit/349d9910b225f2425391b754f36579b6988e2882 commit: 349d9910b225f2425391b754f36579b6988e2882 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T12:15:54-08:00 summary: Correct a couple of unbalanced parenthesis. (GH-10779) (cherry picked from commit 55f41e45b4318cbe19209f5144641344d0049fb8) Co-authored-by: Andre Delfino files: M Doc/c-api/buffer.rst M Doc/faq/extending.rst M Doc/library/crypt.rst M Doc/library/email.compat32-message.rst M Doc/library/email.message.rst M Doc/library/email.parser.rst M Doc/library/exceptions.rst M Doc/library/stdtypes.rst M Doc/library/sysconfig.rst M Doc/reference/expressions.rst M Doc/using/windows.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 5a9a46fc67e8..33abb5bb94d9 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -198,7 +198,7 @@ a buffer, see :c:func:`PyObject_GetBuffer`. indicates that no de-referencing should occur (striding in a contiguous memory block). - If all suboffsets are negative (i.e. no de-referencing is needed, then + If all suboffsets are negative (i.e. no de-referencing is needed), then this field must be NULL (the default value). This type of array representation is used by the Python Imaging Library diff --git a/Doc/faq/extending.rst b/Doc/faq/extending.rst index b611bb48012a..74e1af6ef24d 100644 --- a/Doc/faq/extending.rst +++ b/Doc/faq/extending.rst @@ -277,7 +277,7 @@ However sometimes you have to run the embedded Python interpreter in the same thread as your rest application and you can't allow the :c:func:`PyRun_InteractiveLoop` to stop while waiting for user input. The one solution then is to call :c:func:`PyParser_ParseString` and test for ``e.error`` -equal to ``E_EOF``, which means the input is incomplete). Here's a sample code +equal to ``E_EOF``, which means the input is incomplete. Here's a sample code fragment, untested, inspired by code from Alex Farber:: #include diff --git a/Doc/library/crypt.rst b/Doc/library/crypt.rst index dd62cb32b9d1..43d4b5b749e4 100644 --- a/Doc/library/crypt.rst +++ b/Doc/library/crypt.rst @@ -91,7 +91,7 @@ The :mod:`crypt` module defines the following functions: may be available on all platforms), or a full encrypted password including salt, as returned by this function. If *salt* is not provided, the strongest method will be used (as returned by - :func:`methods`. + :func:`methods`). Checking a password is usually done by passing the plain-text password as *word* and the full results of a previous :func:`crypt` call, diff --git a/Doc/library/email.compat32-message.rst b/Doc/library/email.compat32-message.rst index d88495089482..f02b35b910a0 100644 --- a/Doc/library/email.compat32-message.rst +++ b/Doc/library/email.compat32-message.rst @@ -152,7 +152,7 @@ Here are the methods of the :class:`Message` class: Return ``True`` if the message's payload is a list of sub-\ :class:`Message` objects, otherwise return ``False``. When :meth:`is_multipart` returns ``False``, the payload should be a string - object (which might be a CTE encoded binary payload. (Note that + object (which might be a CTE encoded binary payload). (Note that :meth:`is_multipart` returning ``True`` does not necessarily mean that "msg.get_content_maintype() == 'multipart'" will return the ``True``. For example, ``is_multipart`` will return ``True`` when the diff --git a/Doc/library/email.message.rst b/Doc/library/email.message.rst index ff5404553e96..77b8099a7d4c 100644 --- a/Doc/library/email.message.rst +++ b/Doc/library/email.message.rst @@ -92,7 +92,7 @@ message objects. .. method:: __str__() - Equivalent to `as_string(policy=self.policy.clone(utf8=True)`. Allows + Equivalent to ``as_string(policy=self.policy.clone(utf8=True))``. Allows ``str(msg)`` to produce a string containing the serialized message in a readable format. @@ -379,7 +379,7 @@ message objects. Note that existing parameter values of headers may be accessed through the :attr:`~email.headerregistry.BaseHeader.params` attribute of the - header value (for example, ``msg['Content-Type'].params['charset']``. + header value (for example, ``msg['Content-Type'].params['charset']``). .. versionchanged:: 3.4 ``replace`` keyword was added. @@ -679,7 +679,7 @@ message objects. specified by the current :mod:`~email.policy`. If the added part has no :mailheader:`Content-Disposition` header, add one with the value ``attachment``. This method can be used both for explicit attachments - (:mailheader:`Content-Disposition: attachment` and ``inline`` attachments + (:mailheader:`Content-Disposition: attachment`) and ``inline`` attachments (:mailheader:`Content-Disposition: inline`), by passing appropriate options to the ``content_manager``. diff --git a/Doc/library/email.parser.rst b/Doc/library/email.parser.rst index 49b4e992708a..d9a61616bbbd 100644 --- a/Doc/library/email.parser.rst +++ b/Doc/library/email.parser.rst @@ -164,7 +164,7 @@ message body, instead setting the payload to the raw body. envelope header. The header block is terminated either by the end of the data or by a blank line. Following the header block is the body of the message (which may contain MIME-encoded subparts, including subparts - with a :mailheader:`Content-Transfer-Encoding` of ``8bit``. + with a :mailheader:`Content-Transfer-Encoding` of ``8bit``). Optional *headersonly* is a flag specifying whether to stop parsing after reading the headers or not. The default is ``False``, meaning it parses diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index e33b88673f75..57ed29145816 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -52,7 +52,7 @@ will be set as :attr:`__cause__` on the raised exception. Setting :attr:`__cause__` also implicitly sets the :attr:`__suppress_context__` attribute to ``True``, so that using ``raise new_exc from None`` effectively replaces the old exception with the new one for display -purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`, while +purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`), while leaving the old exception available in :attr:`__context__` for introspection when debugging. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 885e765673d1..416790b796f5 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -445,7 +445,7 @@ Notes: (4) Performing these calculations with at least one extra sign extension bit in a finite two's complement representation (a working bit-width of - ``1 + max(x.bit_length(), y.bit_length()`` or more) is sufficient to get the + ``1 + max(x.bit_length(), y.bit_length())`` or more) is sufficient to get the same result as if there were an infinite number of sign bits. @@ -3203,7 +3203,7 @@ place, and instead produce new objects. Return a copy of the sequence left filled with ASCII ``b'0'`` digits to make a sequence of length *width*. A leading sign prefix (``b'+'``/ - ``b'-'`` is handled by inserting the padding *after* the sign character + ``b'-'``) is handled by inserting the padding *after* the sign character rather than before. For :class:`bytes` objects, the original sequence is returned if *width* is less than or equal to ``len(seq)``. diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index 9f549fbc5130..b5a1da80c686 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -185,7 +185,7 @@ Other functions Windows will return one of: - - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + - win-amd64 (64bit Windows on AMD64, aka x86_64, Intel64, and EM64T) - win32 (all others - specifically, sys.platform is returned) Mac OS X can return: diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 3b50efe5e277..3e7db901b849 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1644,7 +1644,7 @@ returned; otherwise, *y* is evaluated and the resulting value is returned. The expression ``x or y`` first evaluates *x*; if *x* is true, its value is returned; otherwise, *y* is evaluated and the resulting value is returned. -(Note that neither :keyword:`and` nor :keyword:`or` restrict the value and type +Note that neither :keyword:`and` nor :keyword:`or` restrict the value and type they return to ``False`` and ``True``, but rather return the last evaluated argument. This is sometimes useful, e.g., if ``s`` is a string that should be replaced by a default value if it is empty, the expression ``s or 'foo'`` yields diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 3ed39e64ad9b..087ab6f4877a 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -613,11 +613,11 @@ user's "application data" directory (i.e. the directory returned by calling the Windows function ``SHGetFolderPath`` with ``CSIDL_LOCAL_APPDATA``) and ``py.ini`` in the same directory as the launcher. The same .ini files are used for both the 'console' version of the launcher (i.e. py.exe) and for the 'windows' version -(i.e. pyw.exe) +(i.e. pyw.exe). Customization specified in the "application directory" will have precedence over the one next to the executable, so a user, who may not have write access to the -.ini file next to the launcher, can override commands in that global .ini file) +.ini file next to the launcher, can override commands in that global .ini file. Customizing default Python versions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From webhook-mailer at python.org Wed Dec 5 15:20:06 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 20:20:06 -0000 Subject: [Python-checkins] bpo-34185: Fix test module collision in test_bdb when ran as script. (GH-8537) (GH-10936) Message-ID: https://github.com/python/cpython/commit/49d995fd6f2a703d19d93baf06fc9f911cb2ce06 commit: 49d995fd6f2a703d19d93baf06fc9f911cb2ce06 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Serhiy Storchaka date: 2018-12-05T22:20:03+02:00 summary: bpo-34185: Fix test module collision in test_bdb when ran as script. (GH-8537) (GH-10936) When running test_bdb.py as a script, `import test_module` would be importing the existing Lib/test/test_modules.py instead of the tempcwd/test_module.py module which was dynamically created by test_bdb.py itself. (cherry picked from commit 54fd45505b3a365e6d53441e6dd7e0d1ec13b46f) Co-authored-by: Alex H <1884912+lajarre at users.noreply.github.com> files: M Lib/test/test_bdb.py diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py index 616c3a864984..55f42e2535b7 100644 --- a/Lib/test/test_bdb.py +++ b/Lib/test/test_bdb.py @@ -549,11 +549,11 @@ def create_modules(modules): def break_in_func(funcname, fname=__file__, temporary=False, cond=None): return 'break', (fname, None, temporary, cond, funcname) -TEST_MODULE = 'test_module' +TEST_MODULE = 'test_module_for_bdb' TEST_MODULE_FNAME = TEST_MODULE + '.py' def tfunc_import(): - import test_module - test_module.main() + import test_module_for_bdb + test_module_for_bdb.main() def tfunc_main(): lno = 2 @@ -971,9 +971,9 @@ def main(): ('return', 3, 'main'), ('step', ), ('return', 1, ''), ('quit', ), ] - import test_module + import test_module_for_bdb with TracerRun(self) as tracer: - tracer.runeval('test_module.main()', globals(), locals()) + tracer.runeval('test_module_for_bdb.main()', globals(), locals()) class IssuesTestCase(BaseTestCase): """Test fixed bdb issues.""" @@ -983,7 +983,7 @@ def test_step_at_return_with_no_trace_in_caller(self): # Check that the tracer does step into the caller frame when the # trace function is not set in that frame. code_1 = """ - from test_module_2 import func + from test_module_for_bdb_2 import func def main(): func() lno = 5 @@ -994,12 +994,12 @@ def func(): """ modules = { TEST_MODULE: code_1, - 'test_module_2': code_2, + 'test_module_for_bdb_2': code_2, } with create_modules(modules): self.expect_set = [ ('line', 2, 'tfunc_import'), - break_in_func('func', 'test_module_2.py'), + break_in_func('func', 'test_module_for_bdb_2.py'), ('None', 2, 'tfunc_import'), ('continue', ), ('line', 3, 'func', ({1:1}, [])), ('step', ), ('return', 3, 'func'), ('step', ), From webhook-mailer at python.org Wed Dec 5 15:29:34 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 20:29:34 -0000 Subject: [Python-checkins] bpo-34738: Add directory entries in ZIP files created by distutils. (GH-9419) Message-ID: https://github.com/python/cpython/commit/53bed18d93233b030bb5b2637daf1b5e87548ef1 commit: 53bed18d93233b030bb5b2637daf1b5e87548ef1 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T12:29:31-08:00 summary: bpo-34738: Add directory entries in ZIP files created by distutils. (GH-9419) (cherry picked from commit 67a93b3a0b3814e97ef9d077b21325fc8ce351b2) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst M Lib/distutils/archive_util.py M Lib/distutils/tests/test_archive_util.py M Lib/distutils/tests/test_bdist_dumb.py M Lib/distutils/tests/test_sdist.py diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py index 78ae5757c36d..b002dc3b8456 100644 --- a/Lib/distutils/archive_util.py +++ b/Lib/distutils/archive_util.py @@ -166,7 +166,15 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED) + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) + zip.write(path, path) + log.info("adding '%s'", path) for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) + zip.write(path, path) + log.info("adding '%s'", path) for name in filenames: path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): diff --git a/Lib/distutils/tests/test_archive_util.py b/Lib/distutils/tests/test_archive_util.py index 14ba4ca34b4a..e9aad0e40fd1 100644 --- a/Lib/distutils/tests/test_archive_util.py +++ b/Lib/distutils/tests/test_archive_util.py @@ -122,12 +122,13 @@ def _tarinfo(self, path): try: names = tar.getnames() names.sort() - return tuple(names) + return names finally: tar.close() - _created_files = ('dist', 'dist/file1', 'dist/file2', - 'dist/sub', 'dist/sub/file3', 'dist/sub2') + _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', + 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] + _created_files = [p.rstrip('/') for p in _zip_created_files] def _create_files(self): # creating something to tar @@ -244,8 +245,7 @@ def test_make_zipfile(self): tarball = base_name + '.zip' self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), - ['dist/file1', 'dist/file2', 'dist/sub/file3']) + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile_no_zlib(self): @@ -271,8 +271,7 @@ def fake_zipfile(*a, **kw): [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), - ['dist/file1', 'dist/file2', 'dist/sub/file3']) + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) def test_check_archive_formats(self): self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), diff --git a/Lib/distutils/tests/test_bdist_dumb.py b/Lib/distutils/tests/test_bdist_dumb.py index c8ccdc2383de..01a233bce37f 100644 --- a/Lib/distutils/tests/test_bdist_dumb.py +++ b/Lib/distutils/tests/test_bdist_dumb.py @@ -84,7 +84,7 @@ def test_simple_built(self): finally: fp.close() - contents = sorted(os.path.basename(fn) for fn in contents) + contents = sorted(filter(None, map(os.path.basename, contents))) wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] if not sys.dont_write_bytecode: wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) diff --git a/Lib/distutils/tests/test_sdist.py b/Lib/distutils/tests/test_sdist.py index 5444b815a8b2..23db1269591d 100644 --- a/Lib/distutils/tests/test_sdist.py +++ b/Lib/distutils/tests/test_sdist.py @@ -128,7 +128,9 @@ def test_prune_file_list(self): zip_file.close() # making sure everything has been pruned correctly - self.assertEqual(len(content), 4) + expected = ['', 'PKG-INFO', 'README', 'setup.py', + 'somecode/', 'somecode/__init__.py'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') @unittest.skipIf(find_executable('tar') is None, @@ -226,7 +228,13 @@ def test_add_defaults(self): zip_file.close() # making sure everything was added - self.assertEqual(len(content), 12) + expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', + 'data/', 'data/data.dt', 'inroot.txt', + 'scripts/', 'scripts/script.py', 'setup.py', + 'some/', 'some/file.txt', 'some/other_file.txt', + 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', + 'somecode/doc.txt'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) # checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) diff --git a/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst b/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst new file mode 100644 index 000000000000..c3f402d39ae0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst @@ -0,0 +1,2 @@ +ZIP files created by :mod:`distutils` will now include entries for +directories. From webhook-mailer at python.org Wed Dec 5 15:36:06 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 20:36:06 -0000 Subject: [Python-checkins] bpo-35310: Clear select() lists before returning upon EINTR (GH-10877) Message-ID: https://github.com/python/cpython/commit/7f52415a6d4841d77d3b7853e83b25a22e0048dc commit: 7f52415a6d4841d77d3b7853e83b25a22e0048dc branch: master author: Oran Avraham <252748+oranav at users.noreply.github.com> committer: Victor Stinner date: 2018-12-05T21:36:03+01:00 summary: bpo-35310: Clear select() lists before returning upon EINTR (GH-10877) select() calls are retried on EINTR (per PEP 475). However, if a timeout was provided and the deadline has passed after running the signal handlers, rlist, wlist and xlist should be cleared since select(2) left them unmodified. files: A Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst M Modules/selectmodule.c diff --git a/Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst b/Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst new file mode 100644 index 000000000000..1ab2e168c86a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst @@ -0,0 +1,4 @@ +Fix a bug in :func:`select.select` where, in some cases, the file descriptor +sequences were returned unmodified after a signal interruption, even though the +file descriptors might not be ready yet. :func:`select.select` will now always +return empty lists if a timeout has occurred. Patch by Oran Avraham. diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 5a6b13466c80..fe69cd58dc6a 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -335,6 +335,10 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist, if (tvp) { timeout = deadline - _PyTime_GetMonotonicClock(); if (timeout < 0) { + /* bpo-35310: lists were unmodified -- clear them explicitly */ + FD_ZERO(&ifdset); + FD_ZERO(&ofdset); + FD_ZERO(&efdset); n = 0; break; } From webhook-mailer at python.org Wed Dec 5 15:44:44 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 20:44:44 -0000 Subject: [Python-checkins] bpo-34738: Add directory entries in ZIP files created by distutils. (GH-9419) (GH-10942) Message-ID: https://github.com/python/cpython/commit/e0c2046d82f6db45a7f3d3ee5f555c3e2487a74d commit: e0c2046d82f6db45a7f3d3ee5f555c3e2487a74d branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Serhiy Storchaka date: 2018-12-05T22:44:38+02:00 summary: bpo-34738: Add directory entries in ZIP files created by distutils. (GH-9419) (GH-10942) (cherry picked from commit 67a93b3a0b3814e97ef9d077b21325fc8ce351b2) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst M Lib/distutils/archive_util.py M Lib/distutils/tests/test_archive_util.py M Lib/distutils/tests/test_bdist_dumb.py M Lib/distutils/tests/test_sdist.py diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py index 78ae5757c36d..b002dc3b8456 100644 --- a/Lib/distutils/archive_util.py +++ b/Lib/distutils/archive_util.py @@ -166,7 +166,15 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED) + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) + zip.write(path, path) + log.info("adding '%s'", path) for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) + zip.write(path, path) + log.info("adding '%s'", path) for name in filenames: path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): diff --git a/Lib/distutils/tests/test_archive_util.py b/Lib/distutils/tests/test_archive_util.py index 02fa1e27a47f..18a0313eb606 100644 --- a/Lib/distutils/tests/test_archive_util.py +++ b/Lib/distutils/tests/test_archive_util.py @@ -122,12 +122,13 @@ def _tarinfo(self, path): try: names = tar.getnames() names.sort() - return tuple(names) + return names finally: tar.close() - _created_files = ('dist', 'dist/file1', 'dist/file2', - 'dist/sub', 'dist/sub/file3', 'dist/sub2') + _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', + 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] + _created_files = [p.rstrip('/') for p in _zip_created_files] def _create_files(self): # creating something to tar @@ -244,8 +245,7 @@ def test_make_zipfile(self): tarball = base_name + '.zip' self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), - ['dist/file1', 'dist/file2', 'dist/sub/file3']) + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile_no_zlib(self): @@ -271,8 +271,7 @@ def fake_zipfile(*a, **kw): [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), - ['dist/file1', 'dist/file2', 'dist/sub/file3']) + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) def test_check_archive_formats(self): self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), diff --git a/Lib/distutils/tests/test_bdist_dumb.py b/Lib/distutils/tests/test_bdist_dumb.py index c8ccdc2383de..01a233bce37f 100644 --- a/Lib/distutils/tests/test_bdist_dumb.py +++ b/Lib/distutils/tests/test_bdist_dumb.py @@ -84,7 +84,7 @@ def test_simple_built(self): finally: fp.close() - contents = sorted(os.path.basename(fn) for fn in contents) + contents = sorted(filter(None, map(os.path.basename, contents))) wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] if not sys.dont_write_bytecode: wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) diff --git a/Lib/distutils/tests/test_sdist.py b/Lib/distutils/tests/test_sdist.py index 5444b815a8b2..23db1269591d 100644 --- a/Lib/distutils/tests/test_sdist.py +++ b/Lib/distutils/tests/test_sdist.py @@ -128,7 +128,9 @@ def test_prune_file_list(self): zip_file.close() # making sure everything has been pruned correctly - self.assertEqual(len(content), 4) + expected = ['', 'PKG-INFO', 'README', 'setup.py', + 'somecode/', 'somecode/__init__.py'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') @unittest.skipIf(find_executable('tar') is None, @@ -226,7 +228,13 @@ def test_add_defaults(self): zip_file.close() # making sure everything was added - self.assertEqual(len(content), 12) + expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', + 'data/', 'data/data.dt', 'inroot.txt', + 'scripts/', 'scripts/script.py', 'setup.py', + 'some/', 'some/file.txt', 'some/other_file.txt', + 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', + 'somecode/doc.txt'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) # checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) diff --git a/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst b/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst new file mode 100644 index 000000000000..c3f402d39ae0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst @@ -0,0 +1,2 @@ +ZIP files created by :mod:`distutils` will now include entries for +directories. From webhook-mailer at python.org Wed Dec 5 15:56:29 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 20:56:29 -0000 Subject: [Python-checkins] bpo-10496: posixpath.expanduser() catchs pwd.getpwuid() error (GH-10919) (GH-10930) Message-ID: https://github.com/python/cpython/commit/b50b33b4ac62c8798199682e511b2028f2d3ec42 commit: b50b33b4ac62c8798199682e511b2028f2d3ec42 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-12-05T21:56:24+01:00 summary: bpo-10496: posixpath.expanduser() catchs pwd.getpwuid() error (GH-10919) (GH-10930) * posixpath.expanduser() now returns the input path unchanged if the HOME environment variable is not set and pwd.getpwuid() raises KeyError (the current user identifier doesn't exist in the password database). * Add test_no_home_directory() to test_site. (cherry picked from commit f2f4555d8287ad217a1dba7bbd93103ad4daf3a8) files: A Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst M Lib/posixpath.py M Lib/test/test_posixpath.py M Lib/test/test_site.py diff --git a/Lib/posixpath.py b/Lib/posixpath.py index f5c2260f1e5f..bbc2369ce7be 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -259,7 +259,12 @@ def expanduser(path): if i == 1: if 'HOME' not in os.environ: import pwd - userhome = pwd.getpwuid(os.getuid()).pw_dir + try: + userhome = pwd.getpwuid(os.getuid()).pw_dir + except KeyError: + # bpo-10496: if the current user identifier doesn't exist in the + # password database, return the path unchanged + return path else: userhome = os.environ['HOME'] else: @@ -267,6 +272,8 @@ def expanduser(path): try: pwent = pwd.getpwnam(path[1:i]) except KeyError: + # bpo-10496: if the user name from the path doesn't exist in the + # password database, return the path unchanged return path userhome = pwent.pw_dir userhome = userhome.rstrip('/') diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index deaa5772835b..0663a21ff043 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -262,34 +262,56 @@ def fake_lstat(path): def test_expanduser(self): self.assertEqual(posixpath.expanduser("foo"), "foo") - with test_support.EnvironmentVarGuard() as env: + + def test_expanduser_home_envvar(self): + with support.EnvironmentVarGuard() as env: + env['HOME'] = '/home/victor' + self.assertEqual(posixpath.expanduser("~"), "/home/victor") + + # expanduser() strips trailing slash + env['HOME'] = '/home/victor/' + self.assertEqual(posixpath.expanduser("~"), "/home/victor") + for home in '/', '', '//', '///': env['HOME'] = home self.assertEqual(posixpath.expanduser("~"), "/") self.assertEqual(posixpath.expanduser("~/"), "/") self.assertEqual(posixpath.expanduser("~/foo"), "/foo") - try: - import pwd - except ImportError: - pass - else: - self.assertIsInstance(posixpath.expanduser("~/"), basestring) - # if home directory == root directory, this test makes no sense - if posixpath.expanduser("~") != '/': - self.assertEqual( - posixpath.expanduser("~") + "/", - posixpath.expanduser("~/") - ) - self.assertIsInstance(posixpath.expanduser("~root/"), basestring) - self.assertIsInstance(posixpath.expanduser("~foo/"), basestring) - - with test_support.EnvironmentVarGuard() as env: - # expanduser should fall back to using the password database - del env['HOME'] - home = pwd.getpwuid(os.getuid()).pw_dir - # $HOME can end with a trailing /, so strip it (see #17809) - home = home.rstrip("/") or '/' - self.assertEqual(posixpath.expanduser("~"), home) + + def test_expanduser_pwd(self): + pwd = support.import_module('pwd') + + self.assertIsInstance(posixpath.expanduser("~/"), str) + + # if home directory == root directory, this test makes no sense + if posixpath.expanduser("~") != '/': + self.assertEqual( + posixpath.expanduser("~") + "/", + posixpath.expanduser("~/") + ) + self.assertIsInstance(posixpath.expanduser("~root/"), str) + self.assertIsInstance(posixpath.expanduser("~foo/"), str) + + with support.EnvironmentVarGuard() as env: + # expanduser should fall back to using the password database + del env['HOME'] + + home = pwd.getpwuid(os.getuid()).pw_dir + # $HOME can end with a trailing /, so strip it (see #17809) + home = home.rstrip("/") or '/' + self.assertEqual(posixpath.expanduser("~"), home) + + # bpo-10496: If the HOME environment variable is not set and the + # user (current identifier or name in the path) doesn't exist in + # the password database (pwd.getuid() or pwd.getpwnam() fail), + # expanduser() must return the path unchanged. + def raise_keyerror(*args): + raise KeyError + + with support.swap_attr(pwd, 'getpwuid', raise_keyerror), \ + support.swap_attr(pwd, 'getpwnam', raise_keyerror): + for path in ('~', '~/.local', '~vstinner/'): + self.assertEqual(posixpath.expanduser(path), path) def test_normpath(self): self.assertEqual(posixpath.normpath(""), ".") diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 9569135c2ca2..b4384ee2cf83 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -7,6 +7,7 @@ import unittest from test.test_support import run_unittest, TESTFN, EnvironmentVarGuard from test.test_support import captured_output +from test import support import __builtin__ import errno import os @@ -241,6 +242,7 @@ def test_getusersitepackages(self): # the call sets USER_BASE *and* USER_SITE self.assertEqual(site.USER_SITE, user_site) self.assertTrue(user_site.startswith(site.USER_BASE), user_site) + self.assertEqual(site.USER_BASE, site.getuserbase()) def test_getsitepackages(self): site.PREFIXES = ['xoxo'] @@ -265,6 +267,48 @@ def test_getsitepackages(self): wanted = os.path.join('xoxo', 'lib', 'site-packages') self.assertEqual(dirs[1], wanted) + def test_no_home_directory(self): + # bpo-10496: getuserbase() and getusersitepackages() must not fail if + # the current user has no home directory (if expanduser() returns the + # path unchanged). + site.USER_SITE = None + site.USER_BASE = None + sysconfig._CONFIG_VARS = None + + with EnvironmentVarGuard() as environ, \ + support.swap_attr(os.path, 'expanduser', lambda path: path): + + del environ['PYTHONUSERBASE'] + del environ['APPDATA'] + + user_base = site.getuserbase() + self.assertTrue(user_base.startswith('~' + os.sep), + user_base) + + user_site = site.getusersitepackages() + self.assertTrue(user_site.startswith(user_base), user_site) + + def fake_isdir(path): + fake_isdir.arg = path + return False + fake_isdir.arg = None + + def must_not_be_called(*args): + raise AssertionError + + with support.swap_attr(os.path, 'isdir', fake_isdir), \ + support.swap_attr(site, 'addsitedir', must_not_be_called), \ + support.swap_attr(site, 'ENABLE_USER_SITE', True): + + # addusersitepackages() must not add user_site to sys.path + # if it is not an existing directory + known_paths = set() + site.addusersitepackages(known_paths) + + self.assertEqual(fake_isdir.arg, user_site) + self.assertFalse(known_paths) + + class PthFile(object): """Helper class for handling testing of .pth files""" diff --git a/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst b/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst new file mode 100644 index 000000000000..232fcc6503b0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-05-13-37-39.bpo-10496.VH-1Lp.rst @@ -0,0 +1,5 @@ +:func:`posixpath.expanduser` now returns the input *path* unchanged if the +``HOME`` environment variable is not set and the current user has no home +directory (if the current user identifier doesn't exist in the password +database). This change fix the :mod:`site` module if the current user doesn't +exist in the password database (if the user has no home directory). From webhook-mailer at python.org Wed Dec 5 16:08:54 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 21:08:54 -0000 Subject: [Python-checkins] [3.6] Correct a couple of unbalanced parenthesis. (GH-10779). (GH-10945) Message-ID: https://github.com/python/cpython/commit/62af9e6583b0b5f5069232c2a6bfd0ceca7fbf95 commit: 62af9e6583b0b5f5069232c2a6bfd0ceca7fbf95 branch: 3.6 author: Andre Delfino committer: Serhiy Storchaka date: 2018-12-05T23:08:50+02:00 summary: [3.6] Correct a couple of unbalanced parenthesis. (GH-10779). (GH-10945) (cherry picked from commit 55f41e45b4318cbe19209f5144641344d0049fb8) Co-authored-by: Andre Delfino files: M Doc/c-api/buffer.rst M Doc/faq/extending.rst M Doc/library/crypt.rst M Doc/library/email.compat32-message.rst M Doc/library/email.message.rst M Doc/library/email.parser.rst M Doc/library/exceptions.rst M Doc/library/stdtypes.rst M Doc/library/sysconfig.rst M Doc/reference/expressions.rst M Doc/using/windows.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 5a9a46fc67e8..33abb5bb94d9 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -198,7 +198,7 @@ a buffer, see :c:func:`PyObject_GetBuffer`. indicates that no de-referencing should occur (striding in a contiguous memory block). - If all suboffsets are negative (i.e. no de-referencing is needed, then + If all suboffsets are negative (i.e. no de-referencing is needed), then this field must be NULL (the default value). This type of array representation is used by the Python Imaging Library diff --git a/Doc/faq/extending.rst b/Doc/faq/extending.rst index fd04a83df33c..a0828172d48e 100644 --- a/Doc/faq/extending.rst +++ b/Doc/faq/extending.rst @@ -277,7 +277,7 @@ However sometimes you have to run the embedded Python interpreter in the same thread as your rest application and you can't allow the :c:func:`PyRun_InteractiveLoop` to stop while waiting for user input. The one solution then is to call :c:func:`PyParser_ParseString` and test for ``e.error`` -equal to ``E_EOF``, which means the input is incomplete). Here's a sample code +equal to ``E_EOF``, which means the input is incomplete. Here's a sample code fragment, untested, inspired by code from Alex Farber:: #include diff --git a/Doc/library/crypt.rst b/Doc/library/crypt.rst index dbd427403847..bccc76b82621 100644 --- a/Doc/library/crypt.rst +++ b/Doc/library/crypt.rst @@ -84,7 +84,7 @@ The :mod:`crypt` module defines the following functions: may be available on all platforms), or a full encrypted password including salt, as returned by this function. If *salt* is not provided, the strongest method will be used (as returned by - :func:`methods`. + :func:`methods`). Checking a password is usually done by passing the plain-text password as *word* and the full results of a previous :func:`crypt` call, diff --git a/Doc/library/email.compat32-message.rst b/Doc/library/email.compat32-message.rst index 9f85c5921c8f..ed380151769a 100644 --- a/Doc/library/email.compat32-message.rst +++ b/Doc/library/email.compat32-message.rst @@ -152,7 +152,7 @@ Here are the methods of the :class:`Message` class: Return ``True`` if the message's payload is a list of sub-\ :class:`Message` objects, otherwise return ``False``. When :meth:`is_multipart` returns ``False``, the payload should be a string - object (which might be a CTE encoded binary payload. (Note that + object (which might be a CTE encoded binary payload). (Note that :meth:`is_multipart` returning ``True`` does not necessarily mean that "msg.get_content_maintype() == 'multipart'" will return the ``True``. For example, ``is_multipart`` will return ``True`` when the diff --git a/Doc/library/email.message.rst b/Doc/library/email.message.rst index ff5404553e96..77b8099a7d4c 100644 --- a/Doc/library/email.message.rst +++ b/Doc/library/email.message.rst @@ -92,7 +92,7 @@ message objects. .. method:: __str__() - Equivalent to `as_string(policy=self.policy.clone(utf8=True)`. Allows + Equivalent to ``as_string(policy=self.policy.clone(utf8=True))``. Allows ``str(msg)`` to produce a string containing the serialized message in a readable format. @@ -379,7 +379,7 @@ message objects. Note that existing parameter values of headers may be accessed through the :attr:`~email.headerregistry.BaseHeader.params` attribute of the - header value (for example, ``msg['Content-Type'].params['charset']``. + header value (for example, ``msg['Content-Type'].params['charset']``). .. versionchanged:: 3.4 ``replace`` keyword was added. @@ -679,7 +679,7 @@ message objects. specified by the current :mod:`~email.policy`. If the added part has no :mailheader:`Content-Disposition` header, add one with the value ``attachment``. This method can be used both for explicit attachments - (:mailheader:`Content-Disposition: attachment` and ``inline`` attachments + (:mailheader:`Content-Disposition: attachment`) and ``inline`` attachments (:mailheader:`Content-Disposition: inline`), by passing appropriate options to the ``content_manager``. diff --git a/Doc/library/email.parser.rst b/Doc/library/email.parser.rst index 49b4e992708a..d9a61616bbbd 100644 --- a/Doc/library/email.parser.rst +++ b/Doc/library/email.parser.rst @@ -164,7 +164,7 @@ message body, instead setting the payload to the raw body. envelope header. The header block is terminated either by the end of the data or by a blank line. Following the header block is the body of the message (which may contain MIME-encoded subparts, including subparts - with a :mailheader:`Content-Transfer-Encoding` of ``8bit``. + with a :mailheader:`Content-Transfer-Encoding` of ``8bit``). Optional *headersonly* is a flag specifying whether to stop parsing after reading the headers or not. The default is ``False``, meaning it parses diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index cfa1649d4d1f..5c0cac8997c6 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -52,7 +52,7 @@ will be set as :attr:`__cause__` on the raised exception. Setting :attr:`__cause__` also implicitly sets the :attr:`__suppress_context__` attribute to ``True``, so that using ``raise new_exc from None`` effectively replaces the old exception with the new one for display -purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`, while +purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`), while leaving the old exception available in :attr:`__context__` for introspection when debugging. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index a5fca9d46900..eed37454bc35 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -445,7 +445,7 @@ Notes: (4) Performing these calculations with at least one extra sign extension bit in a finite two's complement representation (a working bit-width of - ``1 + max(x.bit_length(), y.bit_length()`` or more) is sufficient to get the + ``1 + max(x.bit_length(), y.bit_length())`` or more) is sufficient to get the same result as if there were an infinite number of sign bits. @@ -3175,7 +3175,7 @@ place, and instead produce new objects. Return a copy of the sequence left filled with ASCII ``b'0'`` digits to make a sequence of length *width*. A leading sign prefix (``b'+'``/ - ``b'-'`` is handled by inserting the padding *after* the sign character + ``b'-'``) is handled by inserting the padding *after* the sign character rather than before. For :class:`bytes` objects, the original sequence is returned if *width* is less than or equal to ``len(seq)``. diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index f066a765d00e..d29396e01f25 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -188,7 +188,7 @@ Other functions Windows will return one of: - - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + - win-amd64 (64bit Windows on AMD64, aka x86_64, Intel64, and EM64T) - win-ia64 (64bit Windows on Itanium) - win32 (all others - specifically, sys.platform is returned) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 05f62cd38ba9..e13072feebc1 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1603,7 +1603,7 @@ returned; otherwise, *y* is evaluated and the resulting value is returned. The expression ``x or y`` first evaluates *x*; if *x* is true, its value is returned; otherwise, *y* is evaluated and the resulting value is returned. -(Note that neither :keyword:`and` nor :keyword:`or` restrict the value and type +Note that neither :keyword:`and` nor :keyword:`or` restrict the value and type they return to ``False`` and ``True``, but rather return the last evaluated argument. This is sometimes useful, e.g., if ``s`` is a string that should be replaced by a default value if it is empty, the expression ``s or 'foo'`` yields diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index a4294686fafe..0ee66561d2cf 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -613,11 +613,11 @@ user's "application data" directory (i.e. the directory returned by calling the Windows function ``SHGetFolderPath`` with ``CSIDL_LOCAL_APPDATA``) and ``py.ini`` in the same directory as the launcher. The same .ini files are used for both the 'console' version of the launcher (i.e. py.exe) and for the 'windows' version -(i.e. pyw.exe) +(i.e. pyw.exe). Customization specified in the "application directory" will have precedence over the one next to the executable, so a user, who may not have write access to the -.ini file next to the launcher, can override commands in that global .ini file) +.ini file next to the launcher, can override commands in that global .ini file. Customizing default Python versions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From webhook-mailer at python.org Wed Dec 5 16:09:28 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 21:09:28 -0000 Subject: [Python-checkins] [2.7] Move __missing__ after __delitem__ in Data model. (GH-10923). (GH-10944) Message-ID: https://github.com/python/cpython/commit/c6639cda512d32b016dce713ac2c737a9056eac0 commit: c6639cda512d32b016dce713ac2c737a9056eac0 branch: 2.7 author: Andre Delfino committer: Serhiy Storchaka date: 2018-12-05T23:09:25+02:00 summary: [2.7] Move __missing__ after __delitem__ in Data model. (GH-10923). (GH-10944) (cherry picked from commit 1ce853f37783575e2b3aaa159ddcebc8660830ef) Co-authored-by: Andre Delfino files: M Doc/reference/datamodel.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 7bdb141ae55e..92f95c2db9b9 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1919,12 +1919,6 @@ sequences, it should iterate through the values. indexes to allow proper detection of the end of the sequence. -.. method:: object.__missing__(self, key) - - Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses - when key is not in the dictionary. - - .. method:: object.__setitem__(self, key, value) Called to implement assignment to ``self[key]``. Same note as for @@ -1943,6 +1937,12 @@ sequences, it should iterate through the values. values as for the :meth:`__getitem__` method. +.. method:: object.__missing__(self, key) + + Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses + when key is not in the dictionary. + + .. method:: object.__iter__(self) This method is called when an iterator is required for a container. This method From webhook-mailer at python.org Wed Dec 5 16:10:00 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 21:10:00 -0000 Subject: [Python-checkins] bpo-34052: Prevent SQLite functions from setting callbacks on exceptions. (GH-8113). (GH-10946) Message-ID: https://github.com/python/cpython/commit/1de91a0032fed500ddd3d8c4fb7a38c0b8719f67 commit: 1de91a0032fed500ddd3d8c4fb7a38c0b8719f67 branch: 3.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-05T23:09:56+02:00 summary: bpo-34052: Prevent SQLite functions from setting callbacks on exceptions. (GH-8113). (GH-10946) (cherry picked from commit 5b25f1d03100e2283c1b129d461ba68ac0169a14) Co-authored-by: Sergey Fedoseev files: A Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst M Lib/sqlite3/test/regression.py M Modules/_sqlite/connection.c diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 34cd233535dc..1c59a3cd31c6 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -256,24 +256,6 @@ def CheckPragmaAutocommit(self): cur.execute("pragma page_size") row = cur.fetchone() - def CheckSetDict(self): - """ - See http://bugs.python.org/issue7478 - - It was possible to successfully register callbacks that could not be - hashed. Return codes of PyDict_SetItem were not checked properly. - """ - class NotHashable: - def __call__(self, *args, **kw): - pass - def __hash__(self): - raise TypeError() - var = NotHashable() - self.assertRaises(TypeError, self.con.create_function, var) - self.assertRaises(TypeError, self.con.create_aggregate, var) - self.assertRaises(TypeError, self.con.set_authorizer, var) - self.assertRaises(TypeError, self.con.set_progress_handler, var) - def CheckConnectionCall(self): """ Call a connection with a non-string SQL request: check error handling @@ -398,9 +380,72 @@ def callback(*args): support.gc_collect() +class UnhashableFunc: + __hash__ = 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,)) + return unittest.TestSuite(( + regression_suite, + unittest.makeSuite(UnhashableCallbacksTestCase), + )) def test(): runner = unittest.TextTestRunner() diff --git a/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst b/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst new file mode 100644 index 000000000000..5aa3cc9a81d7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst @@ -0,0 +1,7 @@ +:meth:`sqlite3.Connection.create_aggregate`, +:meth:`sqlite3.Connection.create_function`, +:meth:`sqlite3.Connection.set_authorizer`, +:meth:`sqlite3.Connection.set_progress_handler` methods raises TypeError +when unhashable objects are passed as callable. These methods now don't pass +such objects to SQLite API. Previous behavior could lead to segfaults. Patch +by Sergey Fedoseev. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 3d14865315eb..351317e78db4 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -827,18 +827,17 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec return NULL; } + if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) { + return NULL; + } rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _pysqlite_func_callback, NULL, NULL); if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating function"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) - return NULL; - - Py_RETURN_NONE; } + Py_RETURN_NONE; } PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) @@ -859,17 +858,16 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje 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); if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) - return NULL; - - Py_RETURN_NONE; } + Py_RETURN_NONE; } static int _authorizer_callback(void* user_arg, int action, const char* arg1, const char* arg2 , const char* dbname, const char* access_attempt_source) @@ -982,17 +980,15 @@ 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"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) - return NULL; - - Py_RETURN_NONE; } + Py_RETURN_NONE; } static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) @@ -1015,9 +1011,9 @@ static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* s /* None clears the progress handler previously set */ sqlite3_progress_handler(self->db, 0, 0, (void*)0); } else { - sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); if (PyDict_SetItem(self->function_pinboard, progress_handler, Py_None) == -1) return NULL; + sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); } Py_RETURN_NONE; From webhook-mailer at python.org Wed Dec 5 16:10:39 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 21:10:39 -0000 Subject: [Python-checkins] [2.7] bpo-35250: Correct argument name "num" -> "btn" in turtle docs. (GH-10565). (GH-10943) Message-ID: https://github.com/python/cpython/commit/abe74feb912292aa4df4e70d39e85d0bcd425436 commit: abe74feb912292aa4df4e70d39e85d0bcd425436 branch: 2.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-05T23:10:36+02:00 summary: [2.7] bpo-35250: Correct argument name "num" -> "btn" in turtle docs. (GH-10565). (GH-10943) (cherry picked from commit 4edeaeac4c194ba5d09187640b5cfca5e03be617) Co-authored-by: Srinivas Thatiparthy (?????????? ?????????) files: M Doc/library/turtle.rst M Lib/lib-tk/turtle.py diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index 1c8a9675b70c..63c0bee6adba 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -1229,7 +1229,7 @@ Using events :param fun: a function with two arguments which will be called with the coordinates of the clicked point on the canvas - :param num: number of the mouse-button, defaults to 1 (left mouse button) + :param btn: number of the mouse-button, defaults to 1 (left mouse button) :param add: ``True`` or ``False`` -- if ``True``, a new binding will be added, otherwise it will replace a former binding @@ -1250,7 +1250,7 @@ Using events :param fun: a function with two arguments which will be called with the coordinates of the clicked point on the canvas - :param num: number of the mouse-button, defaults to 1 (left mouse button) + :param btn: number of the mouse-button, defaults to 1 (left mouse button) :param add: ``True`` or ``False`` -- if ``True``, a new binding will be added, otherwise it will replace a former binding @@ -1274,7 +1274,7 @@ Using events :param fun: a function with two arguments which will be called with the coordinates of the clicked point on the canvas - :param num: number of the mouse-button, defaults to 1 (left mouse button) + :param btn: number of the mouse-button, defaults to 1 (left mouse button) :param add: ``True`` or ``False`` -- if ``True``, a new binding will be added, otherwise it will replace a former binding @@ -1656,7 +1656,7 @@ Using screen events :param fun: a function with two arguments which will be called with the coordinates of the clicked point on the canvas - :param num: number of the mouse-button, defaults to 1 (left mouse button) + :param btn: number of the mouse-button, defaults to 1 (left mouse button) :param add: ``True`` or ``False`` -- if ``True``, a new binding will be added, otherwise it will replace a former binding diff --git a/Lib/lib-tk/turtle.py b/Lib/lib-tk/turtle.py index 52e669b4824c..ae921ce2e5a5 100644 --- a/Lib/lib-tk/turtle.py +++ b/Lib/lib-tk/turtle.py @@ -1300,7 +1300,7 @@ def onclick(self, fun, btn=1, add=None): Arguments: fun -- a function with two arguments, the coordinates of the clicked point on the canvas. - num -- the number of the mouse-button, defaults to 1 + btn -- the number of the mouse-button, defaults to 1 Example (for a TurtleScreen instance named screen and a Turtle instance named turtle): @@ -3418,7 +3418,7 @@ def onclick(self, fun, btn=1, add=None): Arguments: fun -- a function with two arguments, to which will be assigned the coordinates of the clicked point on the canvas. - num -- number of the mouse-button defaults to 1 (left mouse button). + btn -- number of the mouse-button defaults to 1 (left mouse button). add -- True or False. If True, new binding will be added, otherwise it will replace a former binding. @@ -3439,7 +3439,7 @@ def onrelease(self, fun, btn=1, add=None): Arguments: fun -- a function with two arguments, to which will be assigned the coordinates of the clicked point on the canvas. - num -- number of the mouse-button defaults to 1 (left mouse button). + btn -- number of the mouse-button defaults to 1 (left mouse button). Example (for a MyTurtle instance named joe): >>> class MyTurtle(Turtle): @@ -3464,7 +3464,7 @@ def ondrag(self, fun, btn=1, add=None): Arguments: fun -- a function with two arguments, to which will be assigned the coordinates of the clicked point on the canvas. - num -- number of the mouse-button defaults to 1 (left mouse button). + btn -- number of the mouse-button defaults to 1 (left mouse button). Every sequence of mouse-move-events on a turtle is preceded by a mouse-click event on that turtle. From webhook-mailer at python.org Wed Dec 5 16:23:09 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 21:23:09 -0000 Subject: [Python-checkins] bpo-34604: Use %R because of invisible characters or trailing whitespaces. (GH-9165). (GH-10947) Message-ID: https://github.com/python/cpython/commit/ac8b47c8b4edd59aaee857717d434df52ec49e6c commit: ac8b47c8b4edd59aaee857717d434df52ec49e6c branch: 3.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-05T23:23:06+02:00 summary: bpo-34604: Use %R because of invisible characters or trailing whitespaces. (GH-9165). (GH-10947) (cherry picked from commit 34c7f0c04e2b4e715b2c3df1875af8939fbe7d0b) Co-authored-by: William Grzybowski files: A Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst M Modules/grpmodule.c M Modules/pwdmodule.c diff --git a/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst b/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst new file mode 100644 index 000000000000..958b74fd0da6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst @@ -0,0 +1,3 @@ +Fix possible mojibake in the error message of `pwd.getpwnam` and +`grp.getgrnam` using string representation because of invisible characters +or trailing whitespaces. Patch by William Grzybowski. diff --git a/Modules/grpmodule.c b/Modules/grpmodule.c index 43e45ef7aad5..8a724b6b438f 100644 --- a/Modules/grpmodule.c +++ b/Modules/grpmodule.c @@ -156,7 +156,7 @@ grp_getgrnam_impl(PyObject *module, PyObject *name) goto out; if ((p = getgrnam(name_chars)) == NULL) { - PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %S", name); + PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %R", name); goto out; } retval = mkgrent(p); diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index 21c2b546f6dd..810427a229b7 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -163,7 +163,7 @@ pwd_getpwnam_impl(PyObject *module, PyObject *arg) goto out; if ((p = getpwnam(name)) == NULL) { PyErr_Format(PyExc_KeyError, - "getpwnam(): name not found: %S", arg); + "getpwnam(): name not found: %R", arg); goto out; } retval = mkpwent(p); From webhook-mailer at python.org Wed Dec 5 16:29:11 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 21:29:11 -0000 Subject: [Python-checkins] bpo-35310: Clear select() lists before returning upon EINTR (GH-10877) Message-ID: https://github.com/python/cpython/commit/b2e0649dd9a36d54478d0edb623a18d7379e6f19 commit: b2e0649dd9a36d54478d0edb623a18d7379e6f19 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T13:29:08-08:00 summary: bpo-35310: Clear select() lists before returning upon EINTR (GH-10877) select() calls are retried on EINTR (per PEP 475). However, if a timeout was provided and the deadline has passed after running the signal handlers, rlist, wlist and xlist should be cleared since select(2) left them unmodified. (cherry picked from commit 7f52415a6d4841d77d3b7853e83b25a22e0048dc) Co-authored-by: Oran Avraham <252748+oranav at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst M Modules/selectmodule.c diff --git a/Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst b/Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst new file mode 100644 index 000000000000..1ab2e168c86a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst @@ -0,0 +1,4 @@ +Fix a bug in :func:`select.select` where, in some cases, the file descriptor +sequences were returned unmodified after a signal interruption, even though the +file descriptors might not be ready yet. :func:`select.select` will now always +return empty lists if a timeout has occurred. Patch by Oran Avraham. diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index f02f5ae4e10c..4b9965724915 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -279,6 +279,10 @@ select_select(PyObject *self, PyObject *args) if (tvp) { timeout = deadline - _PyTime_GetMonotonicClock(); if (timeout < 0) { + /* bpo-35310: lists were unmodified -- clear them explicitly */ + FD_ZERO(&ifdset); + FD_ZERO(&ofdset); + FD_ZERO(&efdset); n = 0; break; } From webhook-mailer at python.org Wed Dec 5 16:31:12 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 21:31:12 -0000 Subject: [Python-checkins] bpo-35310: Clear select() lists before returning upon EINTR (GH-10877) Message-ID: https://github.com/python/cpython/commit/34510781901b75c9aeca79db41ce0fa92c67878f commit: 34510781901b75c9aeca79db41ce0fa92c67878f branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T13:31:07-08:00 summary: bpo-35310: Clear select() lists before returning upon EINTR (GH-10877) select() calls are retried on EINTR (per PEP 475). However, if a timeout was provided and the deadline has passed after running the signal handlers, rlist, wlist and xlist should be cleared since select(2) left them unmodified. (cherry picked from commit 7f52415a6d4841d77d3b7853e83b25a22e0048dc) Co-authored-by: Oran Avraham <252748+oranav at users.noreply.github.com> files: A Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst M Modules/selectmodule.c diff --git a/Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst b/Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst new file mode 100644 index 000000000000..1ab2e168c86a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-03-19-45-00.bpo-35310.9k28gR.rst @@ -0,0 +1,4 @@ +Fix a bug in :func:`select.select` where, in some cases, the file descriptor +sequences were returned unmodified after a signal interruption, even though the +file descriptors might not be ready yet. :func:`select.select` will now always +return empty lists if a timeout has occurred. Patch by Oran Avraham. diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 63266af4dce9..88679e814495 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -279,6 +279,10 @@ select_select(PyObject *self, PyObject *args) if (tvp) { timeout = deadline - _PyTime_GetMonotonicClock(); if (timeout < 0) { + /* bpo-35310: lists were unmodified -- clear them explicitly */ + FD_ZERO(&ifdset); + FD_ZERO(&ofdset); + FD_ZERO(&efdset); n = 0; break; } From webhook-mailer at python.org Wed Dec 5 16:41:25 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 21:41:25 -0000 Subject: [Python-checkins] Fix typos in concurrent.Futures documentation (GH-10920) Message-ID: https://github.com/python/cpython/commit/40a61da40d252626f8b9ff524d76c1f0ccb3a4f7 commit: 40a61da40d252626f8b9ff524d76c1f0ccb3a4f7 branch: master author: Matt Wheeler committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-05T13:41:20-08:00 summary: Fix typos in concurrent.Futures documentation (GH-10920) Add a missing word `as` in `as well as an`. Linkify `threading.Thread`. files: M Doc/library/concurrent.futures.rst diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index 3890e49f597b..8d6b1e8d71ff 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -141,7 +141,7 @@ And:: each worker thread; *initargs* is a tuple of arguments passed to the initializer. Should *initializer* raise an exception, all currently pending jobs will raise a :exc:`~concurrent.futures.thread.BrokenThreadPool`, - as well any attempt to submit more jobs to the pool. + as well as any attempt to submit more jobs to the pool. .. versionchanged:: 3.5 If *max_workers* is ``None`` or @@ -153,7 +153,7 @@ And:: .. versionadded:: 3.6 The *thread_name_prefix* argument was added to allow users to - control the threading.Thread names for worker threads created by + control the :class:`threading.Thread` names for worker threads created by the pool for easier debugging. .. versionchanged:: 3.7 From webhook-mailer at python.org Wed Dec 5 16:41:55 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 21:41:55 -0000 Subject: [Python-checkins] bpo-35344: platform.platform() uses mac_ver() on macOS (GH-10780) Message-ID: https://github.com/python/cpython/commit/ea0ca218b0c28b2af2b1f6a5d3383569de7fc2c1 commit: ea0ca218b0c28b2af2b1f6a5d3383569de7fc2c1 branch: master author: Victor Stinner committer: GitHub date: 2018-12-05T22:41:52+01:00 summary: bpo-35344: platform.platform() uses mac_ver() on macOS (GH-10780) On macOS, platform.platform() now uses mac_ver(), if it returns a non-empty release string, to get the macOS version rather than darwin version. files: A Misc/NEWS.d/next/Library/2018-11-29-00-23-25.bpo-35344.4QOPJQ.rst M Doc/library/platform.rst M Lib/platform.py M Lib/test/test_platform.py diff --git a/Doc/library/platform.rst b/Doc/library/platform.rst index 7ac4b027418e..60c6089ad3cc 100644 --- a/Doc/library/platform.rst +++ b/Doc/library/platform.rst @@ -79,6 +79,11 @@ Cross Platform Setting *terse* to true causes the function to return only the absolute minimum information needed to identify the platform. + .. versionchanged:: 3.8 + On macOS, the function now uses :func:`mac_ver`, if it returns a + non-empty release string, to get the macOS version rather than the darwin + version. + .. function:: processor() diff --git a/Lib/platform.py b/Lib/platform.py index f089a463ef9f..d8455256bb9a 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -1182,6 +1182,14 @@ def platform(aliased=0, terse=0): if aliased: system, release, version = system_alias(system, release, version) + if system == 'Darwin': + # macOS (darwin kernel) + macos_release = mac_ver()[0] + if macos_release: + # note: 'macOS' is different than 'MacOS' used below + system = 'macOS' + release = macos_release + if system == 'Windows': # MS platforms rel, vers, csd, ptype = win32_ver(version) diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 978d2f76ab68..c1a7e3407934 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -10,6 +10,11 @@ from test import support class PlatformTest(unittest.TestCase): + def clear_caches(self): + platform._platform_cache.clear() + platform._sys_version_cache.clear() + platform._uname_cache = None + def test_architecture(self): res = platform.architecture() @@ -344,5 +349,33 @@ def test__comparable_version(self): self.assertLess(V('0.960923'), V('2.2beta29')) + def test_macos(self): + self.addCleanup(self.clear_caches) + + uname = ('Darwin', 'hostname', '17.7.0', + ('Darwin Kernel Version 17.7.0: ' + 'Thu Jun 21 22:53:14 PDT 2018; ' + 'root:xnu-4570.71.2~1/RELEASE_X86_64'), + 'x86_64', 'i386') + arch = ('64bit', '') + with mock.patch.object(platform, 'uname', return_value=uname), \ + mock.patch.object(platform, 'architecture', return_value=arch): + for mac_ver, expected_terse, expected in [ + # darwin: mac_ver() returns empty strings + (('', '', ''), + 'Darwin-17.7.0', + 'Darwin-17.7.0-x86_64-i386-64bit'), + # macOS: mac_ver() returns macOS version + (('10.13.6', ('', '', ''), 'x86_64'), + 'macOS-10.13.6', + 'macOS-10.13.6-x86_64-i386-64bit'), + ]: + with mock.patch.object(platform, 'mac_ver', + return_value=mac_ver): + self.clear_caches() + self.assertEqual(platform.platform(terse=1), expected_terse) + self.assertEqual(platform.platform(), expected) + + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2018-11-29-00-23-25.bpo-35344.4QOPJQ.rst b/Misc/NEWS.d/next/Library/2018-11-29-00-23-25.bpo-35344.4QOPJQ.rst new file mode 100644 index 000000000000..a999cbe2a72e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-11-29-00-23-25.bpo-35344.4QOPJQ.rst @@ -0,0 +1,3 @@ +On macOS, :func:`platform.platform` now uses :func:`platform.mac_ver`, if it +returns a non-empty release string, to get the macOS version rather than the +darwin version. From webhook-mailer at python.org Wed Dec 5 17:02:16 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 22:02:16 -0000 Subject: [Python-checkins] [2.7] bpo-34738: Add directory entries in ZIP files created by distutils. (GH-9419). (GH-10950) Message-ID: https://github.com/python/cpython/commit/b2742ba5f9ce8a6108202e0645662f2b58da423b commit: b2742ba5f9ce8a6108202e0645662f2b58da423b branch: 2.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-06T00:02:10+02:00 summary: [2.7] bpo-34738: Add directory entries in ZIP files created by distutils. (GH-9419). (GH-10950) (cherry picked from commit 67a93b3a0b3814e97ef9d077b21325fc8ce351b2) files: A Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst M Lib/distutils/archive_util.py M Lib/distutils/tests/test_archive_util.py M Lib/distutils/tests/test_bdist_dumb.py M Lib/distutils/tests/test_sdist.py diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py index 834b722ed3f1..19a3bc466894 100644 --- a/Lib/distutils/archive_util.py +++ b/Lib/distutils/archive_util.py @@ -162,7 +162,15 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) + zip.write(path, path) + log.info("adding '%s'", path) for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) + zip.write(path, path) + log.info("adding '%s'", path) for name in filenames: path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): diff --git a/Lib/distutils/tests/test_archive_util.py b/Lib/distutils/tests/test_archive_util.py index ed7c2cea69cb..137100cca83d 100644 --- a/Lib/distutils/tests/test_archive_util.py +++ b/Lib/distutils/tests/test_archive_util.py @@ -98,7 +98,7 @@ def _tarinfo(self, path): try: names = tar.getnames() names.sort() - return tuple(names) + return names finally: tar.close() diff --git a/Lib/distutils/tests/test_bdist_dumb.py b/Lib/distutils/tests/test_bdist_dumb.py index 5db3a850f8e4..ef9e68131b1a 100644 --- a/Lib/distutils/tests/test_bdist_dumb.py +++ b/Lib/distutils/tests/test_bdist_dumb.py @@ -86,7 +86,7 @@ def test_simple_built(self): finally: fp.close() - contents = sorted(os.path.basename(fn) for fn in contents) + contents = sorted(filter(None, map(os.path.basename, contents))) wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] if not sys.dont_write_bytecode: wanted.append('foo.pyc') diff --git a/Lib/distutils/tests/test_sdist.py b/Lib/distutils/tests/test_sdist.py index 02c1d12e20cc..c503bd62b7a4 100644 --- a/Lib/distutils/tests/test_sdist.py +++ b/Lib/distutils/tests/test_sdist.py @@ -130,7 +130,9 @@ def test_prune_file_list(self): zip_file.close() # making sure everything has been pruned correctly - self.assertEqual(len(content), 4) + expected = ['', 'PKG-INFO', 'README', 'setup.py', + 'somecode/', 'somecode/__init__.py'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) @unittest.skipUnless(zlib, "requires zlib") def test_make_distribution(self): @@ -246,7 +248,13 @@ def test_add_defaults(self): zip_file.close() # making sure everything was added - self.assertEqual(len(content), 12) + expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', + 'data/', 'data/data.dt', 'inroot.txt', + 'scripts/', 'scripts/script.py', 'setup.py', + 'some/', 'some/file.txt', 'some/other_file.txt', + 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', + 'somecode/doc.txt'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) # checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) diff --git a/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst b/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst new file mode 100644 index 000000000000..c3f402d39ae0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-19-16-51-04.bpo-34738.Pr3-iG.rst @@ -0,0 +1,2 @@ +ZIP files created by :mod:`distutils` will now include entries for +directories. From webhook-mailer at python.org Wed Dec 5 17:03:18 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 22:03:18 -0000 Subject: [Python-checkins] bpo-34052: Prevent SQLite functions from setting callbacks on exceptions. (GH-8113). (GH-10946) (GH-10952) Message-ID: https://github.com/python/cpython/commit/fdf505000f135df3bdae08697b2a324d8f046768 commit: fdf505000f135df3bdae08697b2a324d8f046768 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Serhiy Storchaka date: 2018-12-06T00:03:13+02:00 summary: bpo-34052: Prevent SQLite functions from setting callbacks on exceptions. (GH-8113). (GH-10946) (GH-10952) (cherry picked from commit 5b25f1d03100e2283c1b129d461ba68ac0169a14) (cherry picked from commit 1de91a0032fed500ddd3d8c4fb7a38c0b8719f67) Co-authored-by: Sergey Fedoseev files: A Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst M Lib/sqlite3/test/regression.py M Modules/_sqlite/connection.c diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 34cd233535dc..1c59a3cd31c6 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -256,24 +256,6 @@ def CheckPragmaAutocommit(self): cur.execute("pragma page_size") row = cur.fetchone() - def CheckSetDict(self): - """ - See http://bugs.python.org/issue7478 - - It was possible to successfully register callbacks that could not be - hashed. Return codes of PyDict_SetItem were not checked properly. - """ - class NotHashable: - def __call__(self, *args, **kw): - pass - def __hash__(self): - raise TypeError() - var = NotHashable() - self.assertRaises(TypeError, self.con.create_function, var) - self.assertRaises(TypeError, self.con.create_aggregate, var) - self.assertRaises(TypeError, self.con.set_authorizer, var) - self.assertRaises(TypeError, self.con.set_progress_handler, var) - def CheckConnectionCall(self): """ Call a connection with a non-string SQL request: check error handling @@ -398,9 +380,72 @@ def callback(*args): support.gc_collect() +class UnhashableFunc: + __hash__ = 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,)) + return unittest.TestSuite(( + regression_suite, + unittest.makeSuite(UnhashableCallbacksTestCase), + )) def test(): runner = unittest.TextTestRunner() diff --git a/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst b/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst new file mode 100644 index 000000000000..5aa3cc9a81d7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst @@ -0,0 +1,7 @@ +:meth:`sqlite3.Connection.create_aggregate`, +:meth:`sqlite3.Connection.create_function`, +:meth:`sqlite3.Connection.set_authorizer`, +:meth:`sqlite3.Connection.set_progress_handler` methods raises TypeError +when unhashable objects are passed as callable. These methods now don't pass +such objects to SQLite API. Previous behavior could lead to segfaults. Patch +by Sergey Fedoseev. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index d86caef7ab60..326d268a7a4e 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -836,18 +836,17 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec return NULL; } + if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) { + return NULL; + } rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _pysqlite_func_callback, NULL, NULL); if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating function"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) - return NULL; - - Py_RETURN_NONE; } + Py_RETURN_NONE; } PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) @@ -868,17 +867,16 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje 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); if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) - return NULL; - - Py_RETURN_NONE; } + Py_RETURN_NONE; } static int _authorizer_callback(void* user_arg, int action, const char* arg1, const char* arg2 , const char* dbname, const char* access_attempt_source) @@ -1003,17 +1001,15 @@ 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"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) - return NULL; - - Py_RETURN_NONE; } + Py_RETURN_NONE; } static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) @@ -1036,9 +1032,9 @@ static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* s /* None clears the progress handler previously set */ sqlite3_progress_handler(self->db, 0, 0, (void*)0); } else { - sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); if (PyDict_SetItem(self->function_pinboard, progress_handler, Py_None) == -1) return NULL; + sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); } Py_RETURN_NONE; From webhook-mailer at python.org Wed Dec 5 17:04:24 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 22:04:24 -0000 Subject: [Python-checkins] bpo-34604: Use %R because of invisible characters or trailing whitespaces. (GH-9165). (GH-10947) (GH-10954) Message-ID: https://github.com/python/cpython/commit/3a9b3346b03796d8573c063ab4c2407043609459 commit: 3a9b3346b03796d8573c063ab4c2407043609459 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Serhiy Storchaka date: 2018-12-06T00:04:19+02:00 summary: bpo-34604: Use %R because of invisible characters or trailing whitespaces. (GH-9165). (GH-10947) (GH-10954) (cherry picked from commit 34c7f0c04e2b4e715b2c3df1875af8939fbe7d0b) (cherry picked from commit ac8b47c8b4edd59aaee857717d434df52ec49e6c) Co-authored-by: William Grzybowski files: A Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst M Modules/grpmodule.c M Modules/pwdmodule.c diff --git a/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst b/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst new file mode 100644 index 000000000000..958b74fd0da6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-07-10-16-34.bpo-34604.xL7-kG.rst @@ -0,0 +1,3 @@ +Fix possible mojibake in the error message of `pwd.getpwnam` and +`grp.getgrnam` using string representation because of invisible characters +or trailing whitespaces. Patch by William Grzybowski. diff --git a/Modules/grpmodule.c b/Modules/grpmodule.c index 43e45ef7aad5..8a724b6b438f 100644 --- a/Modules/grpmodule.c +++ b/Modules/grpmodule.c @@ -156,7 +156,7 @@ grp_getgrnam_impl(PyObject *module, PyObject *name) goto out; if ((p = getgrnam(name_chars)) == NULL) { - PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %S", name); + PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %R", name); goto out; } retval = mkgrent(p); diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index 21c2b546f6dd..810427a229b7 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -163,7 +163,7 @@ pwd_getpwnam_impl(PyObject *module, PyObject *arg) goto out; if ((p = getpwnam(name)) == NULL) { PyErr_Format(PyExc_KeyError, - "getpwnam(): name not found: %S", arg); + "getpwnam(): name not found: %R", arg); goto out; } retval = mkpwent(p); From webhook-mailer at python.org Wed Dec 5 17:15:47 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 22:15:47 -0000 Subject: [Python-checkins] Fix typo in xml.dom.minidom documentation (GH-10956) Message-ID: https://github.com/python/cpython/commit/2d8f976cde4794d174b44ff7d5fd970aa89c65e8 commit: 2d8f976cde4794d174b44ff7d5fd970aa89c65e8 branch: master author: E Kawashima committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-05T14:15:42-08:00 summary: Fix typo in xml.dom.minidom documentation (GH-10956) Escape the \t and \n. Follow up from https://github.com/python/cpython/pull/10814. files: M Doc/library/xml.dom.minidom.rst diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index 42340802f193..fb6fe6e66143 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -163,7 +163,7 @@ module documentation. This section lists the differences between the API and The :meth:`toxml` method now preserves the attribute order specified by the user. -.. method:: Node.toprettyxml(indent="\t", newl="\n", encoding=None) +.. method:: Node.toprettyxml(indent="\\t", newl="\\n", encoding=None) Return a pretty-printed version of the document. *indent* specifies the indentation string and defaults to a tabulator; *newl* specifies the string From webhook-mailer at python.org Wed Dec 5 17:17:31 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 22:17:31 -0000 Subject: [Python-checkins] Fix typos in concurrent.Futures documentation (GH-10920) Message-ID: https://github.com/python/cpython/commit/022d7bc7ccad5b3b3c62450731142e250f1e547d commit: 022d7bc7ccad5b3b3c62450731142e250f1e547d branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T14:17:28-08:00 summary: Fix typos in concurrent.Futures documentation (GH-10920) Add a missing word `as` in `as well as an`. Linkify `threading.Thread`. (cherry picked from commit 40a61da40d252626f8b9ff524d76c1f0ccb3a4f7) Co-authored-by: Matt Wheeler files: M Doc/library/concurrent.futures.rst diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index b1b086b442de..a57491543bf4 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -141,7 +141,7 @@ And:: each worker thread; *initargs* is a tuple of arguments passed to the initializer. Should *initializer* raise an exception, all currently pending jobs will raise a :exc:`~concurrent.futures.thread.BrokenThreadPool`, - as well any attempt to submit more jobs to the pool. + as well as any attempt to submit more jobs to the pool. .. versionchanged:: 3.5 If *max_workers* is ``None`` or @@ -153,7 +153,7 @@ And:: .. versionadded:: 3.6 The *thread_name_prefix* argument was added to allow users to - control the threading.Thread names for worker threads created by + control the :class:`threading.Thread` names for worker threads created by the pool for easier debugging. .. versionchanged:: 3.7 From webhook-mailer at python.org Wed Dec 5 17:19:21 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 22:19:21 -0000 Subject: [Python-checkins] [3.6] Fix typos in concurrent.Futures documentation (GH-10920) (GH-10958) Message-ID: https://github.com/python/cpython/commit/2997fa42bbd7a079a316b9acb134c5d9d12f05ee commit: 2997fa42bbd7a079a316b9acb134c5d9d12f05ee branch: 3.6 author: Mariatta committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-05T14:19:17-08:00 summary: [3.6] Fix typos in concurrent.Futures documentation (GH-10920) (GH-10958) Add a missing word `as` in `as well as an`. Linkify `threading.Thread`.. (cherry picked from commit 40a61da40d252626f8b9ff524d76c1f0ccb3a4f7) Co-authored-by: Matt Wheeler files: M Doc/library/concurrent.futures.rst diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index 9794e735d422..69dfd0f47c1c 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -137,6 +137,12 @@ And:: An :class:`Executor` subclass that uses a pool of at most *max_workers* threads to execute calls asynchronously. + *initializer* is an optional callable that is called at the start of + each worker thread; *initargs* is a tuple of arguments passed to the + initializer. Should *initializer* raise an exception, all currently + pending jobs will raise a :exc:`~concurrent.futures.thread.BrokenThreadPool`, + as well any attempt to submit more jobs to the pool. + .. versionchanged:: 3.5 If *max_workers* is ``None`` or not given, it will default to the number of processors on the machine, @@ -147,7 +153,7 @@ And:: .. versionadded:: 3.6 The *thread_name_prefix* argument was added to allow users to - control the threading.Thread names for worker threads created by + control the :class:`threading.Thread` names for worker threads created by the pool for easier debugging. .. _threadpoolexecutor-example: From webhook-mailer at python.org Wed Dec 5 17:21:44 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 05 Dec 2018 22:21:44 -0000 Subject: [Python-checkins] [2.7] bpo-34052: Prevent SQLite functions from setting callbacks on exceptions. (GH-8113). (GH-10946) (GH-10955) Message-ID: https://github.com/python/cpython/commit/fff8fab1ce4af208cd9c6cd84a8be626a1b744d8 commit: fff8fab1ce4af208cd9c6cd84a8be626a1b744d8 branch: 2.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-06T00:21:40+02:00 summary: [2.7] bpo-34052: Prevent SQLite functions from setting callbacks on exceptions. (GH-8113). (GH-10946) (GH-10955) (cherry picked from commit 5b25f1d03100e2283c1b129d461ba68ac0169a14) (cherry picked from commit 1de91a0032fed500ddd3d8c4fb7a38c0b8719f67) Co-authored-by: Sergey Fedoseev . files: A Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst M Lib/sqlite3/test/regression.py M Modules/_sqlite/connection.c diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 7eeac324d272..271afb0a7f03 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -246,24 +246,6 @@ def CheckPragmaAutocommit(self): cur.execute("pragma page_size") row = cur.fetchone() - def CheckSetDict(self): - """ - See http://bugs.python.org/issue7478 - - It was possible to successfully register callbacks that could not be - hashed. Return codes of PyDict_SetItem were not checked properly. - """ - class NotHashable: - def __call__(self, *args, **kw): - pass - def __hash__(self): - raise TypeError() - var = NotHashable() - self.assertRaises(TypeError, self.con.create_function, var) - self.assertRaises(TypeError, self.con.create_aggregate, var) - self.assertRaises(TypeError, self.con.set_authorizer, var) - self.assertRaises(TypeError, self.con.set_progress_handler, var) - def CheckConnectionCall(self): """ Call a connection with a non-string SQL request: check error handling @@ -380,9 +362,73 @@ def callback(*args): support.gc_collect() +class UnhashableFunc: + def __hash__(self): + raise TypeError('unhashable type') + + 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.assertRaisesRegexp(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.assertRaisesRegexp(TypeError, 'unhashable type'): + self.con.create_function(func_name, 0, f) + msg = 'no such function: %s' % func_name + with self.assertRaisesRegexp(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.assertRaisesRegexp(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.assertRaisesRegexp(TypeError, 'unhashable type'): + self.con.create_aggregate(aggr_name, 0, UnhashableType('Aggr', (), {})) + msg = 'no such function: %s' % aggr_name + with self.assertRaisesRegexp(sqlite.OperationalError, msg): + self.con.execute('SELECT %s()' % aggr_name) + + def suite(): regression_suite = unittest.makeSuite(RegressionTests, "Check") - return unittest.TestSuite((regression_suite,)) + return unittest.TestSuite(( + regression_suite, + unittest.makeSuite(UnhashableCallbacksTestCase), + )) def test(): runner = unittest.TextTestRunner() diff --git a/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst b/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst new file mode 100644 index 000000000000..5aa3cc9a81d7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-07-24-16-37-40.bpo-34052.VbbFAE.rst @@ -0,0 +1,7 @@ +:meth:`sqlite3.Connection.create_aggregate`, +:meth:`sqlite3.Connection.create_function`, +:meth:`sqlite3.Connection.set_authorizer`, +:meth:`sqlite3.Connection.set_progress_handler` methods raises TypeError +when unhashable objects are passed as callable. These methods now don't pass +such objects to SQLite API. Previous behavior could lead to segfaults. Patch +by Sergey Fedoseev. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index c3f39fd78a2b..585453a28207 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -877,19 +877,17 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec return NULL; } + if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) { + return NULL; + } rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _pysqlite_func_callback, NULL, NULL); if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating function"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) - return NULL; - - Py_INCREF(Py_None); - return Py_None; } + Py_RETURN_NONE; } PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) @@ -910,18 +908,16 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje 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); if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) - return NULL; - - Py_INCREF(Py_None); - return Py_None; } + Py_RETURN_NONE; } static int _authorizer_callback(void* user_arg, int action, const char* arg1, const char* arg2 , const char* dbname, const char* access_attempt_source) @@ -1007,18 +1003,15 @@ 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"); return NULL; - } else { - if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) - return NULL; - - Py_INCREF(Py_None); - return Py_None; } + Py_RETURN_NONE; } static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) @@ -1041,9 +1034,9 @@ static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* s /* None clears the progress handler previously set */ sqlite3_progress_handler(self->db, 0, 0, (void*)0); } else { - sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler); 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(Py_None); From webhook-mailer at python.org Wed Dec 5 17:21:57 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 22:21:57 -0000 Subject: [Python-checkins] bpo-35389: test.pythoninfo logs platform.libc_ver (GH-10951) Message-ID: https://github.com/python/cpython/commit/848acf7249b5669d73d70a7cb6e5ab60689cf825 commit: 848acf7249b5669d73d70a7cb6e5ab60689cf825 branch: master author: Victor Stinner committer: GitHub date: 2018-12-05T23:21:54+01:00 summary: bpo-35389: test.pythoninfo logs platform.libc_ver (GH-10951) files: M Lib/test/pythoninfo.py diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 9befd12e4095..81fca10eaeb1 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -144,6 +144,10 @@ def collect_platform(info_add): info_add('platform.platform', platform.platform(aliased=True)) + libc_ver = ('%s %s' % platform.libc_ver()).strip() + if libc_ver: + info_add('platform.libc_ver', libc_ver) + def collect_locale(info_add): import locale From webhook-mailer at python.org Wed Dec 5 17:31:15 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 22:31:15 -0000 Subject: [Python-checkins] Fix typo in xml.dom.minidom documentation (GH-10956) Message-ID: https://github.com/python/cpython/commit/40ef5b73c240ad0a73b734919b9e4f7062ae5b1f commit: 40ef5b73c240ad0a73b734919b9e4f7062ae5b1f branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T14:31:11-08:00 summary: Fix typo in xml.dom.minidom documentation (GH-10956) Escape the \t and \n. Follow up from https://github.com/python/cpython/pull/10814. (cherry picked from commit 2d8f976cde4794d174b44ff7d5fd970aa89c65e8) Co-authored-by: E Kawashima files: M Doc/library/xml.dom.minidom.rst diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index 2f4022428db0..2df5bdd5cf3b 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -169,7 +169,7 @@ module documentation. This section lists the differences between the API and the *encoding* argument was introduced; see :meth:`writexml`. -.. method:: Node.toprettyxml(indent="\t", newl="\n", encoding=None) +.. method:: Node.toprettyxml(indent="\\t", newl="\\n", encoding=None) Return a pretty-printed version of the document. *indent* specifies the indentation string and defaults to a tabulator; *newl* specifies the string From webhook-mailer at python.org Wed Dec 5 18:18:36 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 05 Dec 2018 23:18:36 -0000 Subject: [Python-checkins] bpo-35363: test_eintr runs eintr_tester.py in verbose mode (GH-10965) Message-ID: https://github.com/python/cpython/commit/aa8ae904ad2f576f8e7b38a9a6542d3e9a569be9 commit: aa8ae904ad2f576f8e7b38a9a6542d3e9a569be9 branch: master author: Victor Stinner committer: GitHub date: 2018-12-06T00:18:30+01:00 summary: bpo-35363: test_eintr runs eintr_tester.py in verbose mode (GH-10965) Moreover, "python3 -m test test_eintr -v" now avoids redirecting stdout/stderr to a pipe, the child process inherits stdout/stderr from the parent. files: M Lib/test/test_eintr.py diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index 25f86d31de35..c2e8deadbab7 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -1,5 +1,7 @@ import os import signal +import subprocess +import sys import unittest from test import support @@ -15,7 +17,19 @@ def test_all(self): # thread (for reliable signal delivery). tester = support.findfile("eintr_tester.py", subdir="eintrdata") # use -u to try to get the full output if the test hangs or crash - script_helper.assert_python_ok("-u", tester) + args = ["-u", tester, "-v"] + if support.verbose: + print() + print("--- run eintr_tester.py ---") + # In verbose mode, the child process inherit stdout and stdout, + # to see output in realtime and reduce the risk of loosing output. + args = [sys.executable, "-E", "-X", "faulthandler", *args] + proc = subprocess.run(args) + print(f"--- eintr_tester.py completed: exit code {proc.returncode} ---") + if proc.returncode: + self.fail("eintr_tester.py failed") + else: + script_helper.assert_python_ok("-u", tester, "-v") if __name__ == "__main__": From webhook-mailer at python.org Wed Dec 5 18:35:47 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 23:35:47 -0000 Subject: [Python-checkins] bpo-35363: test_eintr runs eintr_tester.py in verbose mode (GH-10965) Message-ID: https://github.com/python/cpython/commit/0fc3b2fe010e42a8c146fb84924e9fd33c6f4e29 commit: 0fc3b2fe010e42a8c146fb84924e9fd33c6f4e29 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T15:35:43-08:00 summary: bpo-35363: test_eintr runs eintr_tester.py in verbose mode (GH-10965) Moreover, "python3 -m test test_eintr -v" now avoids redirecting stdout/stderr to a pipe, the child process inherits stdout/stderr from the parent. (cherry picked from commit aa8ae904ad2f576f8e7b38a9a6542d3e9a569be9) Co-authored-by: Victor Stinner files: M Lib/test/test_eintr.py diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index 25f86d31de35..c2e8deadbab7 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -1,5 +1,7 @@ import os import signal +import subprocess +import sys import unittest from test import support @@ -15,7 +17,19 @@ def test_all(self): # thread (for reliable signal delivery). tester = support.findfile("eintr_tester.py", subdir="eintrdata") # use -u to try to get the full output if the test hangs or crash - script_helper.assert_python_ok("-u", tester) + args = ["-u", tester, "-v"] + if support.verbose: + print() + print("--- run eintr_tester.py ---") + # In verbose mode, the child process inherit stdout and stdout, + # to see output in realtime and reduce the risk of loosing output. + args = [sys.executable, "-E", "-X", "faulthandler", *args] + proc = subprocess.run(args) + print(f"--- eintr_tester.py completed: exit code {proc.returncode} ---") + if proc.returncode: + self.fail("eintr_tester.py failed") + else: + script_helper.assert_python_ok("-u", tester, "-v") if __name__ == "__main__": From webhook-mailer at python.org Wed Dec 5 18:43:42 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 23:43:42 -0000 Subject: [Python-checkins] bpo-35363: test_eintr runs eintr_tester.py in verbose mode (GH-10965) Message-ID: https://github.com/python/cpython/commit/4699f2aa26b2f8befa77852e0c6fba0b474a2748 commit: 4699f2aa26b2f8befa77852e0c6fba0b474a2748 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T15:43:38-08:00 summary: bpo-35363: test_eintr runs eintr_tester.py in verbose mode (GH-10965) Moreover, "python3 -m test test_eintr -v" now avoids redirecting stdout/stderr to a pipe, the child process inherits stdout/stderr from the parent. (cherry picked from commit aa8ae904ad2f576f8e7b38a9a6542d3e9a569be9) Co-authored-by: Victor Stinner files: M Lib/test/test_eintr.py diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index 25f86d31de35..c2e8deadbab7 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -1,5 +1,7 @@ import os import signal +import subprocess +import sys import unittest from test import support @@ -15,7 +17,19 @@ def test_all(self): # thread (for reliable signal delivery). tester = support.findfile("eintr_tester.py", subdir="eintrdata") # use -u to try to get the full output if the test hangs or crash - script_helper.assert_python_ok("-u", tester) + args = ["-u", tester, "-v"] + if support.verbose: + print() + print("--- run eintr_tester.py ---") + # In verbose mode, the child process inherit stdout and stdout, + # to see output in realtime and reduce the risk of loosing output. + args = [sys.executable, "-E", "-X", "faulthandler", *args] + proc = subprocess.run(args) + print(f"--- eintr_tester.py completed: exit code {proc.returncode} ---") + if proc.returncode: + self.fail("eintr_tester.py failed") + else: + script_helper.assert_python_ok("-u", tester, "-v") if __name__ == "__main__": From webhook-mailer at python.org Wed Dec 5 18:53:24 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 05 Dec 2018 23:53:24 -0000 Subject: [Python-checkins] [3.6] Fix typo in xml.dom.minidom documentation (GH-10956) (GH-10962) Message-ID: https://github.com/python/cpython/commit/f7fe18a9254cb1a868801968f315467547d33996 commit: f7fe18a9254cb1a868801968f315467547d33996 branch: 3.6 author: Mariatta committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-05T15:53:21-08:00 summary: [3.6] Fix typo in xml.dom.minidom documentation (GH-10956) (GH-10962) Escape the \t and \n. Follow up from https://github.com/python/cpython/pull/10814.. (cherry picked from commit 2d8f976cde4794d174b44ff7d5fd970aa89c65e8) Co-authored-by: E Kawashima files: M Doc/library/xml.dom.minidom.rst diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index d5d7b20efe60..15b1cb0cbf78 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -156,7 +156,7 @@ module documentation. This section lists the differences between the API and encoding. Encoding this string in an encoding other than UTF-8 is likely incorrect, since UTF-8 is the default encoding of XML. -.. method:: Node.toprettyxml(indent="\t", newl="\n", encoding=None) +.. method:: Node.toprettyxml(indent="\\t", newl="\\n", encoding=None) Return a pretty-printed version of the document. *indent* specifies the indentation string and defaults to a tabulator; *newl* specifies the string From webhook-mailer at python.org Wed Dec 5 19:24:48 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 00:24:48 -0000 Subject: [Python-checkins] [3.7] Fix typo in xml.dom.minidom documentation (GH-10956) (GH-10961) Message-ID: https://github.com/python/cpython/commit/c28317e4c2520f6df7a281f1664d63a6447d1ebb commit: c28317e4c2520f6df7a281f1664d63a6447d1ebb branch: 3.7 author: Mariatta committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-05T16:24:44-08:00 summary: [3.7] Fix typo in xml.dom.minidom documentation (GH-10956) (GH-10961) Escape the \t and \n. Follow up from https://github.com/python/cpython/pull/10814.. (cherry picked from commit 2d8f976cde4794d174b44ff7d5fd970aa89c65e8) Co-authored-by: E Kawashima files: M Doc/library/xml.dom.minidom.rst diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index d5d7b20efe60..15b1cb0cbf78 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -156,7 +156,7 @@ module documentation. This section lists the differences between the API and encoding. Encoding this string in an encoding other than UTF-8 is likely incorrect, since UTF-8 is the default encoding of XML. -.. method:: Node.toprettyxml(indent="\t", newl="\n", encoding=None) +.. method:: Node.toprettyxml(indent="\\t", newl="\\n", encoding=None) Return a pretty-printed version of the document. *indent* specifies the indentation string and defaults to a tabulator; *newl* specifies the string From webhook-mailer at python.org Wed Dec 5 19:49:08 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 06 Dec 2018 00:49:08 -0000 Subject: [Python-checkins] [3.7] Revert "bpo-34172: multiprocessing.Pool leaks resources after being deleted (GH-8450) (GH-9676)" (#10968) Message-ID: https://github.com/python/cpython/commit/3c6b0d967eb4c95e06c4f1beddfca4f6300d92ce commit: 3c6b0d967eb4c95e06c4f1beddfca4f6300d92ce branch: 3.7 author: Victor Stinner committer: GitHub date: 2018-12-06T01:49:05+01:00 summary: [3.7] Revert "bpo-34172: multiprocessing.Pool leaks resources after being deleted (GH-8450) (GH-9676)" (#10968) This reverts commit 97f998a4dfd6db6d867f446daa62445d0782bf39. files: A Misc/NEWS.d/next/Library/2018-12-06-00-29-28.bpo-34172.l7CIYt.rst M Lib/multiprocessing/pool.py M Lib/test/_test_multiprocessing.py diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py index 574b5db5afb6..3e9a0d6b4867 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -149,9 +149,8 @@ class Pool(object): ''' _wrap_exception = True - @staticmethod - def Process(ctx, *args, **kwds): - return ctx.Process(*args, **kwds) + def Process(self, *args, **kwds): + return self._ctx.Process(*args, **kwds) def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): @@ -178,15 +177,13 @@ def __init__(self, processes=None, initializer=None, initargs=(), self._worker_handler = threading.Thread( target=Pool._handle_workers, - args=(self._cache, self._taskqueue, self._ctx, self.Process, - self._processes, self._pool, self._inqueue, self._outqueue, - self._initializer, self._initargs, self._maxtasksperchild, - self._wrap_exception) + args=(self, ) ) self._worker_handler.daemon = True self._worker_handler._state = RUN self._worker_handler.start() + self._task_handler = threading.Thread( target=Pool._handle_tasks, args=(self._taskqueue, self._quick_put, self._outqueue, @@ -212,62 +209,43 @@ def __init__(self, processes=None, initializer=None, initargs=(), exitpriority=15 ) - @staticmethod - def _join_exited_workers(pool): + def _join_exited_workers(self): """Cleanup after any worker processes which have exited due to reaching their specified lifetime. Returns True if any workers were cleaned up. """ cleaned = False - for i in reversed(range(len(pool))): - worker = pool[i] + for i in reversed(range(len(self._pool))): + worker = self._pool[i] if worker.exitcode is not None: # worker exited util.debug('cleaning up worker %d' % i) worker.join() cleaned = True - del pool[i] + del self._pool[i] return cleaned def _repopulate_pool(self): - return self._repopulate_pool_static(self._ctx, self.Process, - self._processes, - self._pool, self._inqueue, - self._outqueue, self._initializer, - self._initargs, - self._maxtasksperchild, - self._wrap_exception) - - @staticmethod - def _repopulate_pool_static(ctx, Process, processes, pool, inqueue, - outqueue, initializer, initargs, - maxtasksperchild, wrap_exception): """Bring the number of pool processes up to the specified number, for use after reaping workers which have exited. """ - for i in range(processes - len(pool)): - w = Process(ctx, target=worker, - args=(inqueue, outqueue, - initializer, - initargs, maxtasksperchild, - wrap_exception) - ) - pool.append(w) + for i in range(self._processes - len(self._pool)): + w = self.Process(target=worker, + args=(self._inqueue, self._outqueue, + self._initializer, + self._initargs, self._maxtasksperchild, + self._wrap_exception) + ) + self._pool.append(w) w.name = w.name.replace('Process', 'PoolWorker') w.daemon = True w.start() util.debug('added worker') - @staticmethod - def _maintain_pool(ctx, Process, processes, pool, inqueue, outqueue, - initializer, initargs, maxtasksperchild, - wrap_exception): + def _maintain_pool(self): """Clean up any exited workers and start replacements for them. """ - if Pool._join_exited_workers(pool): - Pool._repopulate_pool_static(ctx, Process, processes, pool, - inqueue, outqueue, initializer, - initargs, maxtasksperchild, - wrap_exception) + if self._join_exited_workers(): + self._repopulate_pool() def _setup_queues(self): self._inqueue = self._ctx.SimpleQueue() @@ -425,20 +403,16 @@ def _map_async(self, func, iterable, mapper, chunksize=None, callback=None, return result @staticmethod - def _handle_workers(cache, taskqueue, ctx, Process, processes, pool, - inqueue, outqueue, initializer, initargs, - maxtasksperchild, wrap_exception): + def _handle_workers(pool): thread = threading.current_thread() # Keep maintaining workers until the cache gets drained, unless the pool # is terminated. - while thread._state == RUN or (cache and thread._state != TERMINATE): - Pool._maintain_pool(ctx, Process, processes, pool, inqueue, - outqueue, initializer, initargs, - maxtasksperchild, wrap_exception) + while thread._state == RUN or (pool._cache and thread._state != TERMINATE): + pool._maintain_pool() time.sleep(0.1) # send sentinel to stop workers - taskqueue.put(None) + pool._taskqueue.put(None) util.debug('worker handler exiting') @staticmethod @@ -820,7 +794,7 @@ class ThreadPool(Pool): _wrap_exception = False @staticmethod - def Process(ctx, *args, **kwds): + def Process(*args, **kwds): from .dummy import Process return Process(*args, **kwds) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 7a657d9d120c..a0daa43a55a1 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2552,13 +2552,6 @@ def test_release_task_refs(self): # they were released too. self.assertEqual(CountedObject.n_instances, 0) - @support.reap_threads - def test_del_pool(self): - p = self.Pool(1) - wr = weakref.ref(p) - del p - gc.collect() - self.assertIsNone(wr()) def raising(): raise KeyError("key") diff --git a/Misc/NEWS.d/next/Library/2018-12-06-00-29-28.bpo-34172.l7CIYt.rst b/Misc/NEWS.d/next/Library/2018-12-06-00-29-28.bpo-34172.l7CIYt.rst new file mode 100644 index 000000000000..e467cc967825 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-06-00-29-28.bpo-34172.l7CIYt.rst @@ -0,0 +1,3 @@ +REVERT: Fix a reference issue inside multiprocessing.Pool that caused the +pool to remain alive if it was deleted without being closed or terminated +explicitly. From webhook-mailer at python.org Wed Dec 5 19:49:38 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 06 Dec 2018 00:49:38 -0000 Subject: [Python-checkins] [3.6] Revert "bpo-34172: multiprocessing.Pool leaks resources after being deleted (GH-8450) (GH-9677)" (GH-10969) Message-ID: https://github.com/python/cpython/commit/eb38ee052e2273568d0041e969aa851ee44e43ce commit: eb38ee052e2273568d0041e969aa851ee44e43ce branch: 3.6 author: Victor Stinner committer: GitHub date: 2018-12-06T01:49:34+01:00 summary: [3.6] Revert "bpo-34172: multiprocessing.Pool leaks resources after being deleted (GH-8450) (GH-9677)" (GH-10969) This reverts commit 07b96a95db78eff3557d1bfed1df9ebecc40815b. files: A Misc/NEWS.d/next/Library/2018-12-06-00-31-25.bpo-34172.l7CIYt.rst M Lib/multiprocessing/pool.py M Lib/test/_test_multiprocessing.py diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py index 32254d8ea6cf..a545f3c1a189 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -147,9 +147,8 @@ class Pool(object): ''' _wrap_exception = True - @staticmethod - def Process(ctx, *args, **kwds): - return ctx.Process(*args, **kwds) + def Process(self, *args, **kwds): + return self._ctx.Process(*args, **kwds) def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): @@ -176,15 +175,13 @@ def __init__(self, processes=None, initializer=None, initargs=(), self._worker_handler = threading.Thread( target=Pool._handle_workers, - args=(self._cache, self._taskqueue, self._ctx, self.Process, - self._processes, self._pool, self._inqueue, self._outqueue, - self._initializer, self._initargs, self._maxtasksperchild, - self._wrap_exception) + args=(self, ) ) self._worker_handler.daemon = True self._worker_handler._state = RUN self._worker_handler.start() + self._task_handler = threading.Thread( target=Pool._handle_tasks, args=(self._taskqueue, self._quick_put, self._outqueue, @@ -210,62 +207,43 @@ def __init__(self, processes=None, initializer=None, initargs=(), exitpriority=15 ) - @staticmethod - def _join_exited_workers(pool): + def _join_exited_workers(self): """Cleanup after any worker processes which have exited due to reaching their specified lifetime. Returns True if any workers were cleaned up. """ cleaned = False - for i in reversed(range(len(pool))): - worker = pool[i] + for i in reversed(range(len(self._pool))): + worker = self._pool[i] if worker.exitcode is not None: # worker exited util.debug('cleaning up worker %d' % i) worker.join() cleaned = True - del pool[i] + del self._pool[i] return cleaned def _repopulate_pool(self): - return self._repopulate_pool_static(self._ctx, self.Process, - self._processes, - self._pool, self._inqueue, - self._outqueue, self._initializer, - self._initargs, - self._maxtasksperchild, - self._wrap_exception) - - @staticmethod - def _repopulate_pool_static(ctx, Process, processes, pool, inqueue, - outqueue, initializer, initargs, - maxtasksperchild, wrap_exception): """Bring the number of pool processes up to the specified number, for use after reaping workers which have exited. """ - for i in range(processes - len(pool)): - w = Process(ctx, target=worker, - args=(inqueue, outqueue, - initializer, - initargs, maxtasksperchild, - wrap_exception) - ) - pool.append(w) + for i in range(self._processes - len(self._pool)): + w = self.Process(target=worker, + args=(self._inqueue, self._outqueue, + self._initializer, + self._initargs, self._maxtasksperchild, + self._wrap_exception) + ) + self._pool.append(w) w.name = w.name.replace('Process', 'PoolWorker') w.daemon = True w.start() util.debug('added worker') - @staticmethod - def _maintain_pool(ctx, Process, processes, pool, inqueue, outqueue, - initializer, initargs, maxtasksperchild, - wrap_exception): + def _maintain_pool(self): """Clean up any exited workers and start replacements for them. """ - if Pool._join_exited_workers(pool): - Pool._repopulate_pool_static(ctx, Process, processes, pool, - inqueue, outqueue, initializer, - initargs, maxtasksperchild, - wrap_exception) + if self._join_exited_workers(): + self._repopulate_pool() def _setup_queues(self): self._inqueue = self._ctx.SimpleQueue() @@ -418,20 +396,16 @@ def _map_async(self, func, iterable, mapper, chunksize=None, callback=None, return result @staticmethod - def _handle_workers(cache, taskqueue, ctx, Process, processes, pool, - inqueue, outqueue, initializer, initargs, - maxtasksperchild, wrap_exception): + def _handle_workers(pool): thread = threading.current_thread() # Keep maintaining workers until the cache gets drained, unless the pool # is terminated. - while thread._state == RUN or (cache and thread._state != TERMINATE): - Pool._maintain_pool(ctx, Process, processes, pool, inqueue, - outqueue, initializer, initargs, - maxtasksperchild, wrap_exception) + while thread._state == RUN or (pool._cache and thread._state != TERMINATE): + pool._maintain_pool() time.sleep(0.1) # send sentinel to stop workers - taskqueue.put(None) + pool._taskqueue.put(None) util.debug('worker handler exiting') @staticmethod @@ -807,7 +781,7 @@ class ThreadPool(Pool): _wrap_exception = False @staticmethod - def Process(ctx, *args, **kwds): + def Process(*args, **kwds): from .dummy import Process return Process(*args, **kwds) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 59f9a2e1e2eb..6cafc2e9cbc0 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2286,13 +2286,6 @@ def test_release_task_refs(self): # they were released too. self.assertEqual(CountedObject.n_instances, 0) - @support.reap_threads - def test_del_pool(self): - p = self.Pool(1) - wr = weakref.ref(p) - del p - gc.collect() - self.assertIsNone(wr()) def raising(): raise KeyError("key") diff --git a/Misc/NEWS.d/next/Library/2018-12-06-00-31-25.bpo-34172.l7CIYt.rst b/Misc/NEWS.d/next/Library/2018-12-06-00-31-25.bpo-34172.l7CIYt.rst new file mode 100644 index 000000000000..e467cc967825 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-06-00-31-25.bpo-34172.l7CIYt.rst @@ -0,0 +1,3 @@ +REVERT: Fix a reference issue inside multiprocessing.Pool that caused the +pool to remain alive if it was deleted without being closed or terminated +explicitly. From webhook-mailer at python.org Wed Dec 5 19:49:44 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 06 Dec 2018 00:49:44 -0000 Subject: [Python-checkins] Revert "[2.7] bpo-34172: multiprocessing.Pool leaks resources after being deleted (GH-9686)" (GH-10970) Message-ID: https://github.com/python/cpython/commit/358fc87f53cf97a1768d5b1ded08f2a564f9fd85 commit: 358fc87f53cf97a1768d5b1ded08f2a564f9fd85 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-12-06T01:49:41+01:00 summary: Revert "[2.7] bpo-34172: multiprocessing.Pool leaks resources after being deleted (GH-9686)" (GH-10970) This reverts commit 4a7dd30f5810e8861a3834159a222ab32d5c97d0. files: D Misc/NEWS.d/next/Library/2018-07-26-10-31-52.bpo-34172.8ovLNi.rst M Lib/multiprocessing/pool.py M Lib/test/test_multiprocessing.py diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py index 489c7d67cf34..a47cd0f58a05 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -162,9 +162,7 @@ def __init__(self, processes=None, initializer=None, initargs=(), self._worker_handler = threading.Thread( target=Pool._handle_workers, - args=(self._cache, self._processes, self._pool, self.Process, - self._inqueue, self._outqueue, self._initializer, - self._initargs, self._maxtasksperchild, self._taskqueue) + args=(self, ) ) self._worker_handler.daemon = True self._worker_handler._state = RUN @@ -196,56 +194,42 @@ def __init__(self, processes=None, initializer=None, initargs=(), exitpriority=15 ) - @staticmethod - def _join_exited_workers(pool): + def _join_exited_workers(self): """Cleanup after any worker processes which have exited due to reaching their specified lifetime. Returns True if any workers were cleaned up. """ cleaned = False - for i in reversed(range(len(pool))): - worker = pool[i] + for i in reversed(range(len(self._pool))): + worker = self._pool[i] if worker.exitcode is not None: # worker exited debug('cleaning up worker %d' % i) worker.join() cleaned = True - del pool[i] + del self._pool[i] return cleaned def _repopulate_pool(self): - return self._repopulate_pool_static(self._processes, self._pool, - self.Process, self._inqueue, - self._outqueue, self._initializer, - self._initargs, - self._maxtasksperchild) - - @staticmethod - def _repopulate_pool_static(processes, pool, Process, inqueue, outqueue, - initializer, initargs, maxtasksperchild): """Bring the number of pool processes up to the specified number, for use after reaping workers which have exited. """ - for i in range(processes - len(pool)): - w = Process(target=worker, - args=(inqueue, outqueue, - initializer, - initargs, maxtasksperchild) - ) - pool.append(w) + for i in range(self._processes - len(self._pool)): + w = self.Process(target=worker, + args=(self._inqueue, self._outqueue, + self._initializer, + self._initargs, self._maxtasksperchild) + ) + self._pool.append(w) w.name = w.name.replace('Process', 'PoolWorker') w.daemon = True w.start() debug('added worker') - @staticmethod - def _maintain_pool(processes, pool, Process, inqueue, outqueue, - initializer, initargs, maxtasksperchild): + def _maintain_pool(self): """Clean up any exited workers and start replacements for them. """ - if Pool._join_exited_workers(pool): - Pool._repopulate_pool_static(processes, pool, Process, inqueue, - outqueue, initializer, initargs, - maxtasksperchild) + if self._join_exited_workers(): + self._repopulate_pool() def _setup_queues(self): from .queues import SimpleQueue @@ -335,18 +319,16 @@ def map_async(self, func, iterable, chunksize=None, callback=None): return result @staticmethod - def _handle_workers(cache, processes, pool, Process, inqueue, outqueue, - initializer, initargs, maxtasksperchild, taskqueue): + def _handle_workers(pool): thread = threading.current_thread() # Keep maintaining workers until the cache gets drained, unless the pool # is terminated. - while thread._state == RUN or (cache and thread._state != TERMINATE): - Pool._maintain_pool(processes, pool, Process, inqueue, outqueue, - initializer, initargs, maxtasksperchild) + while thread._state == RUN or (pool._cache and thread._state != TERMINATE): + pool._maintain_pool() time.sleep(0.1) # send sentinel to stop workers - taskqueue.put(None) + pool._taskqueue.put(None) debug('worker handler exiting') @staticmethod diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py index d3192181e5ad..ff299feed894 100644 --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -1359,13 +1359,6 @@ def test_release_task_refs(self): # they were released too. self.assertEqual(CountedObject.n_instances, 0) - def test_del_pool(self): - p = self.Pool(1) - wr = weakref.ref(p) - del p - gc.collect() - self.assertIsNone(wr()) - def unpickleable_result(): return lambda: 42 diff --git a/Misc/NEWS.d/next/Library/2018-07-26-10-31-52.bpo-34172.8ovLNi.rst b/Misc/NEWS.d/next/Library/2018-07-26-10-31-52.bpo-34172.8ovLNi.rst deleted file mode 100644 index d1c5a7721019..000000000000 --- a/Misc/NEWS.d/next/Library/2018-07-26-10-31-52.bpo-34172.8ovLNi.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a reference issue inside multiprocessing.Pool that caused the pool to remain alive if it was deleted without being closed or terminated explicitly. From webhook-mailer at python.org Thu Dec 6 00:46:26 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 06 Dec 2018 05:46:26 -0000 Subject: [Python-checkins] bpo-34162: Update idlelib NEWS to 2018-12-05. (GH-10964) Message-ID: https://github.com/python/cpython/commit/6ea9d54dea9f2f8be7fe6d284064c579331388a9 commit: 6ea9d54dea9f2f8be7fe6d284064c579331388a9 branch: master author: Terry Jan Reedy committer: GitHub date: 2018-12-06T00:46:22-05:00 summary: bpo-34162: Update idlelib NEWS to 2018-12-05. (GH-10964) files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index f59095cda261..9a16ece3bbe3 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,38 @@ Released on 2019-10-20? ====================================== +bpo-35213: Where appropriate, use 'macOS' in idlelib. + +bpo-34864: Document two IDLE on MacOS issues. The System Preferences +Dock "prefer tabs always" setting disables some IDLE features. +Menus are a bit different than as described for Windows and Linux. + +bpo-35202: Remove unused imports in idlelib. + +bpo-33000: Document that IDLE's shell has no line limit. +A program that runs indefinitely can overfill memory. + +bpo-23220: Explain how IDLE's Shell displays output. +Add new subsection "User output in Shell". + +bpo-35099: Improve the doc about IDLE running user code. +"IDLE -- console differences" is renamed "Running user code". +It mostly covers the implications of using custom sys.stdxxx objects. + +bpo-35097: Add IDLE doc subsection explaining editor windows. +Topics include opening, title and status bars, .py* extension, and running. + +Issue 35093: Document the IDLE document viewer in the IDLE doc. +Add a paragraph in "Help and preferences", "Help sources" subsection. + +bpo-1529353: Explain Shell text squeezing in the IDLE doc. + +bpo-35088: Update idlelib.help.copy_string docstring. +We now use git and backporting instead of hg and forward merging. + +bpo-35087: Update idlelib help files for the current doc build. +The main change is the elimination of chapter-section numbers. + bpo-1529353: 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 From webhook-mailer at python.org Thu Dec 6 02:04:39 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 06 Dec 2018 07:04:39 -0000 Subject: [Python-checkins] bpo-33709: test_ntpath and test_posixpath fail in Windows with ACP!=1252. (GH-7278) Message-ID: https://github.com/python/cpython/commit/8752dfbd1f0c96ca09cdacabaf0d0f8c3895b6ce commit: 8752dfbd1f0c96ca09cdacabaf0d0f8c3895b6ce branch: master author: native-api committer: Serhiy Storchaka date: 2018-12-06T09:04:35+02:00 summary: bpo-33709: test_ntpath and test_posixpath fail in Windows with ACP!=1252. (GH-7278) files: M Lib/test/support/__init__.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index f90212cd7ecf..05e8593f9825 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -871,7 +871,11 @@ def dec(*args, **kwargs): '\u20AC', ): try: - os.fsdecode(os.fsencode(character)) + # If Python is set up to use the legacy 'mbcs' in Windows, + # 'replace' error mode is used, and encode() returns b'?' + # for characters missing in the ANSI codepage + if os.fsdecode(os.fsencode(character)) != character: + raise UnicodeError except UnicodeError: pass else: From webhook-mailer at python.org Thu Dec 6 02:22:21 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 07:22:21 -0000 Subject: [Python-checkins] bpo-33709: test_ntpath and test_posixpath fail in Windows with ACP!=1252. (GH-7278) Message-ID: https://github.com/python/cpython/commit/b1438c0d376e1d438a11927e2698e3317da0d854 commit: b1438c0d376e1d438a11927e2698e3317da0d854 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T23:22:17-08:00 summary: bpo-33709: test_ntpath and test_posixpath fail in Windows with ACP!=1252. (GH-7278) (cherry picked from commit 8752dfbd1f0c96ca09cdacabaf0d0f8c3895b6ce) Co-authored-by: native-api files: M Lib/test/support/__init__.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 2768e1147946..512e354fabc8 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -852,7 +852,11 @@ def dec(*args, **kwargs): '\u20AC', ): try: - os.fsdecode(os.fsencode(character)) + # If Python is set up to use the legacy 'mbcs' in Windows, + # 'replace' error mode is used, and encode() returns b'?' + # for characters missing in the ANSI codepage + if os.fsdecode(os.fsencode(character)) != character: + raise UnicodeError except UnicodeError: pass else: From webhook-mailer at python.org Thu Dec 6 02:26:54 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 07:26:54 -0000 Subject: [Python-checkins] bpo-33709: test_ntpath and test_posixpath fail in Windows with ACP!=1252. (GH-7278) Message-ID: https://github.com/python/cpython/commit/af31228650d30f02a283d291ba106e84275a04c1 commit: af31228650d30f02a283d291ba106e84275a04c1 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-05T23:26:51-08:00 summary: bpo-33709: test_ntpath and test_posixpath fail in Windows with ACP!=1252. (GH-7278) (cherry picked from commit 8752dfbd1f0c96ca09cdacabaf0d0f8c3895b6ce) Co-authored-by: native-api files: M Lib/test/support/__init__.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index c0627dc14ef0..66c0fed8411c 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -857,7 +857,11 @@ def dec(*args, **kwargs): '\u20AC', ): try: - os.fsdecode(os.fsencode(character)) + # If Python is set up to use the legacy 'mbcs' in Windows, + # 'replace' error mode is used, and encode() returns b'?' + # for characters missing in the ANSI codepage + if os.fsdecode(os.fsencode(character)) != character: + raise UnicodeError except UnicodeError: pass else: From webhook-mailer at python.org Thu Dec 6 02:51:54 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 06 Dec 2018 07:51:54 -0000 Subject: [Python-checkins] Revert "bpo-34172: multiprocessing.Pool leaks resources after being deleted (GH-8450)" (GH-10971) Message-ID: https://github.com/python/cpython/commit/9dfc754d61c55a526304e10a328bad448efa9ee9 commit: 9dfc754d61c55a526304e10a328bad448efa9ee9 branch: master author: Victor Stinner committer: GitHub date: 2018-12-06T08:51:47+01:00 summary: Revert "bpo-34172: multiprocessing.Pool leaks resources after being deleted (GH-8450)" (GH-10971) This reverts commit 97bfe8d3ebb0a54c8798f57555cb4152f9b2e1d0. files: D Misc/NEWS.d/next/Library/2018-07-26-10-31-52.bpo-34172.8ovLNi.rst M Lib/multiprocessing/pool.py M Lib/test/_test_multiprocessing.py diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py index 7a6d01490146..2b3cc59a9ff8 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -149,9 +149,8 @@ class Pool(object): ''' _wrap_exception = True - @staticmethod - def Process(ctx, *args, **kwds): - return ctx.Process(*args, **kwds) + def Process(self, *args, **kwds): + return self._ctx.Process(*args, **kwds) def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): @@ -186,15 +185,13 @@ def __init__(self, processes=None, initializer=None, initargs=(), self._worker_handler = threading.Thread( target=Pool._handle_workers, - args=(self._cache, self._taskqueue, self._ctx, self.Process, - self._processes, self._pool, self._inqueue, self._outqueue, - self._initializer, self._initargs, self._maxtasksperchild, - self._wrap_exception) + args=(self, ) ) self._worker_handler.daemon = True self._worker_handler._state = RUN self._worker_handler.start() + self._task_handler = threading.Thread( target=Pool._handle_tasks, args=(self._taskqueue, self._quick_put, self._outqueue, @@ -220,62 +217,43 @@ def __init__(self, processes=None, initializer=None, initargs=(), exitpriority=15 ) - @staticmethod - def _join_exited_workers(pool): + def _join_exited_workers(self): """Cleanup after any worker processes which have exited due to reaching their specified lifetime. Returns True if any workers were cleaned up. """ cleaned = False - for i in reversed(range(len(pool))): - worker = pool[i] + for i in reversed(range(len(self._pool))): + worker = self._pool[i] if worker.exitcode is not None: # worker exited util.debug('cleaning up worker %d' % i) worker.join() cleaned = True - del pool[i] + del self._pool[i] return cleaned def _repopulate_pool(self): - return self._repopulate_pool_static(self._ctx, self.Process, - self._processes, - self._pool, self._inqueue, - self._outqueue, self._initializer, - self._initargs, - self._maxtasksperchild, - self._wrap_exception) - - @staticmethod - def _repopulate_pool_static(ctx, Process, processes, pool, inqueue, - outqueue, initializer, initargs, - maxtasksperchild, wrap_exception): """Bring the number of pool processes up to the specified number, for use after reaping workers which have exited. """ - for i in range(processes - len(pool)): - w = Process(ctx, target=worker, - args=(inqueue, outqueue, - initializer, - initargs, maxtasksperchild, - wrap_exception) - ) + for i in range(self._processes - len(self._pool)): + w = self.Process(target=worker, + args=(self._inqueue, self._outqueue, + self._initializer, + self._initargs, self._maxtasksperchild, + self._wrap_exception) + ) w.name = w.name.replace('Process', 'PoolWorker') w.daemon = True w.start() - pool.append(w) + self._pool.append(w) util.debug('added worker') - @staticmethod - def _maintain_pool(ctx, Process, processes, pool, inqueue, outqueue, - initializer, initargs, maxtasksperchild, - wrap_exception): + def _maintain_pool(self): """Clean up any exited workers and start replacements for them. """ - if Pool._join_exited_workers(pool): - Pool._repopulate_pool_static(ctx, Process, processes, pool, - inqueue, outqueue, initializer, - initargs, maxtasksperchild, - wrap_exception) + if self._join_exited_workers(): + self._repopulate_pool() def _setup_queues(self): self._inqueue = self._ctx.SimpleQueue() @@ -433,20 +411,16 @@ def _map_async(self, func, iterable, mapper, chunksize=None, callback=None, return result @staticmethod - def _handle_workers(cache, taskqueue, ctx, Process, processes, pool, - inqueue, outqueue, initializer, initargs, - maxtasksperchild, wrap_exception): + def _handle_workers(pool): thread = threading.current_thread() # Keep maintaining workers until the cache gets drained, unless the pool # is terminated. - while thread._state == RUN or (cache and thread._state != TERMINATE): - Pool._maintain_pool(ctx, Process, processes, pool, inqueue, - outqueue, initializer, initargs, - maxtasksperchild, wrap_exception) + while thread._state == RUN or (pool._cache and thread._state != TERMINATE): + pool._maintain_pool() time.sleep(0.1) # send sentinel to stop workers - taskqueue.put(None) + pool._taskqueue.put(None) util.debug('worker handler exiting') @staticmethod @@ -828,7 +802,7 @@ class ThreadPool(Pool): _wrap_exception = False @staticmethod - def Process(ctx, *args, **kwds): + def Process(*args, **kwds): from .dummy import Process return Process(*args, **kwds) diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index b62c119e9ae0..163419c30eba 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2558,13 +2558,6 @@ def test_release_task_refs(self): # they were released too. self.assertEqual(CountedObject.n_instances, 0) - @support.reap_threads - def test_del_pool(self): - p = self.Pool(1) - wr = weakref.ref(p) - del p - gc.collect() - self.assertIsNone(wr()) def raising(): raise KeyError("key") diff --git a/Misc/NEWS.d/next/Library/2018-07-26-10-31-52.bpo-34172.8ovLNi.rst b/Misc/NEWS.d/next/Library/2018-07-26-10-31-52.bpo-34172.8ovLNi.rst deleted file mode 100644 index d1c5a7721019..000000000000 --- a/Misc/NEWS.d/next/Library/2018-07-26-10-31-52.bpo-34172.8ovLNi.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a reference issue inside multiprocessing.Pool that caused the pool to remain alive if it was deleted without being closed or terminated explicitly. From webhook-mailer at python.org Thu Dec 6 03:25:43 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 06 Dec 2018 08:25:43 -0000 Subject: [Python-checkins] [3.7] bpo-34162: Update idlelib NEWS to 2018-12-05 (GH-10964) (GH-10980) Message-ID: https://github.com/python/cpython/commit/de8037db8c203ca0a1bf549f690230d5e7b8429e commit: de8037db8c203ca0a1bf549f690230d5e7b8429e branch: 3.7 author: Terry Jan Reedy committer: GitHub date: 2018-12-06T03:25:40-05:00 summary: [3.7] bpo-34162: Update idlelib NEWS to 2018-12-05 (GH-10964) (GH-10980) Cherry-picked from 6ea9d54. files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 133807c447b9..e34b439319dd 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,8 +1,45 @@ -What's New in IDLE 3.7.1 +What's New in IDLE 3.7.2 Released on 2018-07-31? ====================================== +bpo-35213: Where appropriate, use 'macOS' in idlelib. + +bpo-34864: Document two IDLE on MacOS issues. The System Preferences +Dock "prefer tabs always" setting disables some IDLE features. +Menus are a bit different than as described for Windows and Linux. + +bpo-35202: Remove unused imports in idlelib. + +bpo-33000: Document that IDLE's shell has no line limit. +A program that runs indefinitely can overfill memory. + +bpo-23220: Explain how IDLE's Shell displays output. +Add new subsection "User output in Shell". + +bpo-35099: Improve the doc about IDLE running user code. +"IDLE -- console differences" is renamed "Running user code". +It mostly covers the implications of using custom sys.stdxxx objects. + +bpo-35097: Add IDLE doc subsection explaining editor windows. +Topics include opening, title and status bars, .py* extension, and running. + +Issue 35093: Document the IDLE document viewer in the IDLE doc. +Add a paragraph in "Help and preferences", "Help sources" subsection. + +bpo-1529353: Explain Shell text squeezing in the IDLE doc. + +bpo-35088: Update idlelib.help.copy_string docstring. +We now use git and backporting instead of hg and forward merging. + +bpo-35087: Update idlelib help files for the current doc build. +The main change is the elimination of chapter-section numbers. + + +What's New in IDLE 3.7.1 +Released on 2018-07-31? +====================================== + bpo-1529353: 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 From webhook-mailer at python.org Thu Dec 6 03:26:00 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 06 Dec 2018 08:26:00 -0000 Subject: [Python-checkins] [3.6] bpo-34162: Update idlelib NEWS to 2018-12-05 (GH-10964) (GH-10979) Message-ID: https://github.com/python/cpython/commit/af1f977575331623547d53247d99be8953a13b9f commit: af1f977575331623547d53247d99be8953a13b9f branch: 3.6 author: Terry Jan Reedy committer: GitHub date: 2018-12-06T03:25:57-05:00 summary: [3.6] bpo-34162: Update idlelib NEWS to 2018-12-05 (GH-10964) (GH-10979) Cherry-picked from 6ea9d54. files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 8ecd85a9ceb6..0868b1f1a3c3 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,8 +1,46 @@ -What's New in IDLE 3.6.7 -Released on 2018-09-24? +What's New in IDLE 3.6.8 +Released on 2018-12-20? +**This is the final 3.6 bugfix maintenance release** ====================================== +bpo-35213: Where appropriate, use 'macOS' in idlelib. + +bpo-34864: Document two IDLE on MacOS issues. The System Preferences +Dock "prefer tabs always" setting disables some IDLE features. +Menus are a bit different than as described for Windows and Linux. + +bpo-35202: Remove unused imports in idlelib. + +bpo-33000: Document that IDLE's shell has no line limit. +A program that runs indefinitely can overfill memory. + +bpo-23220: Explain how IDLE's Shell displays output. +Add new subsection "User output in Shell". + +bpo-35099: Improve the doc about IDLE running user code. +"IDLE -- console differences" is renamed "Running user code". +It mostly covers the implications of using custom sys.stdxxx objects. + +bpo-35097: Add IDLE doc subsection explaining editor windows. +Topics include opening, title and status bars, .py* extension, and running. + +Issue 35093: Document the IDLE document viewer in the IDLE doc. +Add a paragraph in "Help and preferences", "Help sources" subsection. + +bpo-1529353: Explain Shell text squeezing in the IDLE doc. + +bpo-35088: Update idlelib.help.copy_string docstring. +We now use git and backporting instead of hg and forward merging. + +bpo-35087: Update idlelib help files for the current doc build. +The main change is the elimination of chapter-section numbers. + + +What's New in IDLE 3.6.7 +Released on 2018-10-20 +====================================== + bpo-1529353: 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 From solipsis at pitrou.net Thu Dec 6 04:06:12 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 06 Dec 2018 09:06:12 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=4 Message-ID: <20181206090612.1.5DBE175CAA9E65D3@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [0, 7, -7] memory blocks, sum=0 test_functools leaked [0, 3, 1] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogO_YxoN', '--timeout', '7200'] From webhook-mailer at python.org Thu Dec 6 04:16:30 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 06 Dec 2018 09:16:30 -0000 Subject: [Python-checkins] bpo-35384: The repr of ctypes.CArgObject no longer fails for non-ascii character. (GH-10863) Message-ID: https://github.com/python/cpython/commit/3ffa8b9ba190101f674a0e524e482a83ed09cccd commit: 3ffa8b9ba190101f674a0e524e482a83ed09cccd branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-06T11:16:24+02:00 summary: bpo-35384: The repr of ctypes.CArgObject no longer fails for non-ascii character. (GH-10863) files: M Lib/ctypes/test/test_bytes.py M Modules/_ctypes/callproc.c diff --git a/Lib/ctypes/test/test_bytes.py b/Lib/ctypes/test/test_bytes.py index 20fa05650340..092ec5af0524 100644 --- a/Lib/ctypes/test/test_bytes.py +++ b/Lib/ctypes/test/test_bytes.py @@ -12,6 +12,7 @@ def test_c_char(self): x.value = "y" c_char.from_param(b"x") self.assertRaises(TypeError, c_char.from_param, "x") + self.assertIn('xbd', repr(c_char.from_param(b"\xbd"))) (c_char * 3)(b"a", b"b", b"c") self.assertRaises(TypeError, c_char * 3, "a", "b", "c") diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 1185c9156ae9..a7965c19b70c 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -455,6 +455,12 @@ PyCArg_dealloc(PyCArgObject *self) PyObject_Del(self); } +static int +is_literal_char(unsigned char c) +{ + return c < 128 && _PyUnicode_IsPrintable(c) && c != '\\' && c != '\''; +} + static PyObject * PyCArg_repr(PyCArgObject *self) { @@ -501,8 +507,14 @@ PyCArg_repr(PyCArgObject *self) break; case 'c': - sprintf(buffer, "", - self->tag, self->value.c); + if (is_literal_char((unsigned char)self->value.c)) { + sprintf(buffer, "", + self->tag, self->value.c); + } + else { + sprintf(buffer, "", + self->tag, (unsigned char)self->value.c); + } break; /* Hm, are these 'z' and 'Z' codes useful at all? @@ -517,8 +529,14 @@ PyCArg_repr(PyCArgObject *self) break; default: - sprintf(buffer, "", - self->tag, self); + if (is_literal_char((unsigned char)self->tag)) { + sprintf(buffer, "", + (unsigned char)self->tag, self); + } + else { + sprintf(buffer, "", + (unsigned char)self->tag, self); + } break; } return PyUnicode_FromString(buffer); From webhook-mailer at python.org Thu Dec 6 04:19:28 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 06 Dec 2018 09:19:28 -0000 Subject: [Python-checkins] [2.7] Correct a couple of unbalanced parenthesis. (GH-10779). (GH-10963) Message-ID: https://github.com/python/cpython/commit/46aa472a8f2dd9e47ba6fbe3cc416ec7c62f11f4 commit: 46aa472a8f2dd9e47ba6fbe3cc416ec7c62f11f4 branch: 2.7 author: Andre Delfino committer: Serhiy Storchaka date: 2018-12-06T11:19:23+02:00 summary: [2.7] Correct a couple of unbalanced parenthesis. (GH-10779). (GH-10963) (cherry picked from commit 55f41e45b4318cbe19209f5144641344d0049fb8) files: M Doc/c-api/buffer.rst M Doc/faq/extending.rst M Doc/library/sysconfig.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 4e5a04397a35..77940acaf600 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -98,7 +98,7 @@ The new-style Py_buffer struct suboffset value that it negative indicates that no de-referencing should occur (striding in a contiguous memory block). - If all suboffsets are negative (i.e. no de-referencing is needed, then + If all suboffsets are negative (i.e. no de-referencing is needed), then this field must be NULL (the default value). Here is a function that returns a pointer to the element in an N-D array diff --git a/Doc/faq/extending.rst b/Doc/faq/extending.rst index 4be58d69714c..0a256b56eed2 100644 --- a/Doc/faq/extending.rst +++ b/Doc/faq/extending.rst @@ -286,7 +286,7 @@ However sometimes you have to run the embedded Python interpreter in the same thread as your rest application and you can't allow the :c:func:`PyRun_InteractiveLoop` to stop while waiting for user input. The one solution then is to call :c:func:`PyParser_ParseString` and test for ``e.error`` -equal to ``E_EOF``, which means the input is incomplete). Here's a sample code +equal to ``E_EOF``, which means the input is incomplete. Here's a sample code fragment, untested, inspired by code from Alex Farber:: #include diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index 3b58266d228a..7655c60c4041 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -188,7 +188,7 @@ Other functions Windows will return one of: - - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + - win-amd64 (64bit Windows on AMD64, aka x86_64, Intel64, and EM64T) - win-ia64 (64bit Windows on Itanium) - win32 (all others - specifically, sys.platform is returned) From webhook-mailer at python.org Thu Dec 6 04:41:19 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 09:41:19 -0000 Subject: [Python-checkins] bpo-35384: The repr of ctypes.CArgObject no longer fails for non-ascii character. (GH-10863) Message-ID: https://github.com/python/cpython/commit/f9d8b686285926c985cfe88a8392a9a497c0a916 commit: f9d8b686285926c985cfe88a8392a9a497c0a916 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T01:41:16-08:00 summary: bpo-35384: The repr of ctypes.CArgObject no longer fails for non-ascii character. (GH-10863) (cherry picked from commit 3ffa8b9ba190101f674a0e524e482a83ed09cccd) Co-authored-by: Serhiy Storchaka files: M Lib/ctypes/test/test_bytes.py M Modules/_ctypes/callproc.c diff --git a/Lib/ctypes/test/test_bytes.py b/Lib/ctypes/test/test_bytes.py index 20fa05650340..092ec5af0524 100644 --- a/Lib/ctypes/test/test_bytes.py +++ b/Lib/ctypes/test/test_bytes.py @@ -12,6 +12,7 @@ def test_c_char(self): x.value = "y" c_char.from_param(b"x") self.assertRaises(TypeError, c_char.from_param, "x") + self.assertIn('xbd', repr(c_char.from_param(b"\xbd"))) (c_char * 3)(b"a", b"b", b"c") self.assertRaises(TypeError, c_char * 3, "a", "b", "c") diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 04fbc010ca7d..d1c190f35910 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -452,6 +452,12 @@ PyCArg_dealloc(PyCArgObject *self) PyObject_Del(self); } +static int +is_literal_char(unsigned char c) +{ + return c < 128 && _PyUnicode_IsPrintable(c) && c != '\\' && c != '\''; +} + static PyObject * PyCArg_repr(PyCArgObject *self) { @@ -498,8 +504,14 @@ PyCArg_repr(PyCArgObject *self) break; case 'c': - sprintf(buffer, "", - self->tag, self->value.c); + if (is_literal_char((unsigned char)self->value.c)) { + sprintf(buffer, "", + self->tag, self->value.c); + } + else { + sprintf(buffer, "", + self->tag, (unsigned char)self->value.c); + } break; /* Hm, are these 'z' and 'Z' codes useful at all? @@ -514,8 +526,14 @@ PyCArg_repr(PyCArgObject *self) break; default: - sprintf(buffer, "", - self->tag, self); + if (is_literal_char((unsigned char)self->tag)) { + sprintf(buffer, "", + (unsigned char)self->tag, self); + } + else { + sprintf(buffer, "", + (unsigned char)self->tag, self); + } break; } return PyUnicode_FromString(buffer); From webhook-mailer at python.org Thu Dec 6 04:43:40 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 06 Dec 2018 09:43:40 -0000 Subject: [Python-checkins] [2.7] bpo-33709: test_ntpath and test_posixpath fail in Windows with ACP!=1252. (GH-7278) (GH-7279) Message-ID: https://github.com/python/cpython/commit/29a4cbff92862207eb9df9a970b3636b8b06ff5d commit: 29a4cbff92862207eb9df9a970b3636b8b06ff5d branch: 2.7 author: native-api committer: Serhiy Storchaka date: 2018-12-06T11:43:37+02:00 summary: [2.7] bpo-33709: test_ntpath and test_posixpath fail in Windows with ACP!=1252. (GH-7278) (GH-7279) files: M Lib/test/support/__init__.py M Lib/test/test_posixpath.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 23b7065174ee..aaf028632a59 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -657,8 +657,12 @@ def u(s): unichr(0x20AC), ): try: - character.encode(sys.getfilesystemencoding())\ - .decode(sys.getfilesystemencoding()) + # In Windows, 'mbcs' is used, and encode() returns '?' + # for characters missing in the ANSI codepage + if character.encode(sys.getfilesystemencoding())\ + .decode(sys.getfilesystemencoding())\ + != character: + raise UnicodeError except UnicodeError: pass else: diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 0663a21ff043..18ea2e42eade 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -496,12 +496,10 @@ def test_relpath(self): finally: os.getcwd = real_getcwd - @test_support.requires_unicode + @unittest.skipUnless(test_support.FS_NONASCII, 'need test_support.FS_NONASCII') def test_expandvars_nonascii_word(self): encoding = sys.getfilesystemencoding() - # Non-ASCII word characters - letters = test_support.u(r'\xe6\u0130\u0141\u03c6\u041a\u05d0\u062a\u0e01') - uwnonascii = letters.encode(encoding, 'ignore').decode(encoding)[:3] + uwnonascii = test_support.FS_NONASCII swnonascii = uwnonascii.encode(encoding) if not swnonascii: self.skipTest('Needs non-ASCII word characters') From webhook-mailer at python.org Thu Dec 6 04:58:31 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 09:58:31 -0000 Subject: [Python-checkins] bpo-35384: The repr of ctypes.CArgObject no longer fails for non-ascii character. (GH-10863) Message-ID: https://github.com/python/cpython/commit/f740818f3d92497c564d515a661039dc8434fc6c commit: f740818f3d92497c564d515a661039dc8434fc6c branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T01:58:24-08:00 summary: bpo-35384: The repr of ctypes.CArgObject no longer fails for non-ascii character. (GH-10863) (cherry picked from commit 3ffa8b9ba190101f674a0e524e482a83ed09cccd) Co-authored-by: Serhiy Storchaka files: M Lib/ctypes/test/test_bytes.py M Modules/_ctypes/callproc.c diff --git a/Lib/ctypes/test/test_bytes.py b/Lib/ctypes/test/test_bytes.py index 20fa05650340..092ec5af0524 100644 --- a/Lib/ctypes/test/test_bytes.py +++ b/Lib/ctypes/test/test_bytes.py @@ -12,6 +12,7 @@ def test_c_char(self): x.value = "y" c_char.from_param(b"x") self.assertRaises(TypeError, c_char.from_param, "x") + self.assertIn('xbd', repr(c_char.from_param(b"\xbd"))) (c_char * 3)(b"a", b"b", b"c") self.assertRaises(TypeError, c_char * 3, "a", "b", "c") diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index ad40ca1c5247..ec596b4de31d 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -452,6 +452,12 @@ PyCArg_dealloc(PyCArgObject *self) PyObject_Del(self); } +static int +is_literal_char(unsigned char c) +{ + return c < 128 && _PyUnicode_IsPrintable(c) && c != '\\' && c != '\''; +} + static PyObject * PyCArg_repr(PyCArgObject *self) { @@ -498,8 +504,14 @@ PyCArg_repr(PyCArgObject *self) break; case 'c': - sprintf(buffer, "", - self->tag, self->value.c); + if (is_literal_char((unsigned char)self->value.c)) { + sprintf(buffer, "", + self->tag, self->value.c); + } + else { + sprintf(buffer, "", + self->tag, (unsigned char)self->value.c); + } break; /* Hm, are these 'z' and 'Z' codes useful at all? @@ -514,8 +526,14 @@ PyCArg_repr(PyCArgObject *self) break; default: - sprintf(buffer, "", - self->tag, self); + if (is_literal_char((unsigned char)self->tag)) { + sprintf(buffer, "", + (unsigned char)self->tag, self); + } + else { + sprintf(buffer, "", + (unsigned char)self->tag, self); + } break; } return PyUnicode_FromString(buffer); From webhook-mailer at python.org Thu Dec 6 05:56:06 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 06 Dec 2018 10:56:06 -0000 Subject: [Python-checkins] bpo-35359: Add _CRT_SECURE_NO_WARNINGS to pythoncore project (GH-10819) Message-ID: https://github.com/python/cpython/commit/49cedc51a68b4cd2525c14ab02bd1a483d8be389 commit: 49cedc51a68b4cd2525c14ab02bd1a483d8be389 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-12-06T11:56:00+01:00 summary: bpo-35359: Add _CRT_SECURE_NO_WARNINGS to pythoncore project (GH-10819) Define _CRT_SECURE_NO_WARNINGS in the Visual Studio pythoncore project to make quiet security warnings when building zlib C files (Modules\zlib\ subdirectory). files: M PCbuild/pythoncore.vcxproj diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 99291ea5a95d..7cae0a54802b 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -70,7 +70,7 @@ /Zm200 %(AdditionalOptions) $(PySourcePath)Python;$(PySourcePath)Modules\zlib;%(AdditionalIncludeDirectories) - _USRDLL;Py_BUILD_CORE;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions) + _USRDLL;Py_BUILD_CORE;Py_ENABLE_SHARED;_CRT_SECURE_NO_WARNINGS;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions) ws2_32.lib;%(AdditionalDependencies) From webhook-mailer at python.org Thu Dec 6 05:56:55 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 06 Dec 2018 10:56:55 -0000 Subject: [Python-checkins] bpo-35424: test_multiprocessing: join 3 pools (GH-10986) Message-ID: https://github.com/python/cpython/commit/388c8c208d9d09bd28289c1e4776b947d4d0f0f0 commit: 388c8c208d9d09bd28289c1e4776b947d4d0f0f0 branch: master author: Victor Stinner committer: GitHub date: 2018-12-06T11:56:52+01:00 summary: bpo-35424: test_multiprocessing: join 3 pools (GH-10986) Join 3 pools in these tests: * test.test_multiprocessing_spawn.WithProcessesTestPool.test_context * test.test_multiprocessing_spawn.WithProcessesTestPool.test_traceback files: M Lib/test/_test_multiprocessing.py diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 163419c30eba..0b0fe7c9b298 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2471,6 +2471,7 @@ def test_context(self): with self.Pool(2) as p: r = p.map_async(sqr, L) self.assertEqual(r.get(), expected) + p.join() self.assertRaises(ValueError, p.map_async, sqr, L) @classmethod @@ -2488,6 +2489,7 @@ def test_traceback(self): exc = e else: self.fail('expected RuntimeError') + p.join() self.assertIs(type(exc), RuntimeError) self.assertEqual(exc.args, (123,)) cause = exc.__cause__ @@ -2512,6 +2514,7 @@ def test_traceback(self): self.fail('expected SayWhenError') self.assertIs(type(exc), SayWhenError) self.assertIs(exc.__cause__, None) + p.join() @classmethod def _test_wrapped_exception(cls): From webhook-mailer at python.org Thu Dec 6 06:20:55 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 11:20:55 -0000 Subject: [Python-checkins] bpo-35424: test_multiprocessing: join 3 pools (GH-10986) Message-ID: https://github.com/python/cpython/commit/b7c67c4d510a7a72a35983cc168dbb2ce796cb8c commit: b7c67c4d510a7a72a35983cc168dbb2ce796cb8c branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T03:20:50-08:00 summary: bpo-35424: test_multiprocessing: join 3 pools (GH-10986) Join 3 pools in these tests: * test.test_multiprocessing_spawn.WithProcessesTestPool.test_context * test.test_multiprocessing_spawn.WithProcessesTestPool.test_traceback (cherry picked from commit 388c8c208d9d09bd28289c1e4776b947d4d0f0f0) Co-authored-by: Victor Stinner files: M Lib/test/_test_multiprocessing.py diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index a0daa43a55a1..abcb5d45e7ab 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2465,6 +2465,7 @@ def test_context(self): with self.Pool(2) as p: r = p.map_async(sqr, L) self.assertEqual(r.get(), expected) + p.join() self.assertRaises(ValueError, p.map_async, sqr, L) @classmethod @@ -2482,6 +2483,7 @@ def test_traceback(self): exc = e else: self.fail('expected RuntimeError') + p.join() self.assertIs(type(exc), RuntimeError) self.assertEqual(exc.args, (123,)) cause = exc.__cause__ @@ -2506,6 +2508,7 @@ def test_traceback(self): self.fail('expected SayWhenError') self.assertIs(type(exc), SayWhenError) self.assertIs(exc.__cause__, None) + p.join() @classmethod def _test_wrapped_exception(cls): From webhook-mailer at python.org Thu Dec 6 06:23:23 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 11:23:23 -0000 Subject: [Python-checkins] bpo-35424: test_multiprocessing: join 3 pools (GH-10986) Message-ID: https://github.com/python/cpython/commit/e44b5b2afa6fe2966d8caff45e36c0980413bb86 commit: e44b5b2afa6fe2966d8caff45e36c0980413bb86 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T03:23:20-08:00 summary: bpo-35424: test_multiprocessing: join 3 pools (GH-10986) Join 3 pools in these tests: * test.test_multiprocessing_spawn.WithProcessesTestPool.test_context * test.test_multiprocessing_spawn.WithProcessesTestPool.test_traceback (cherry picked from commit 388c8c208d9d09bd28289c1e4776b947d4d0f0f0) Co-authored-by: Victor Stinner files: M Lib/test/_test_multiprocessing.py diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 6cafc2e9cbc0..d5c1ec128482 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2199,6 +2199,7 @@ def test_context(self): with self.Pool(2) as p: r = p.map_async(sqr, L) self.assertEqual(r.get(), expected) + p.join() self.assertRaises(ValueError, p.map_async, sqr, L) @classmethod @@ -2216,6 +2217,7 @@ def test_traceback(self): exc = e else: self.fail('expected RuntimeError') + p.join() self.assertIs(type(exc), RuntimeError) self.assertEqual(exc.args, (123,)) cause = exc.__cause__ @@ -2240,6 +2242,7 @@ def test_traceback(self): self.fail('expected SayWhenError') self.assertIs(type(exc), SayWhenError) self.assertIs(exc.__cause__, None) + p.join() @classmethod def _test_wrapped_exception(cls): From webhook-mailer at python.org Thu Dec 6 08:16:26 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 06 Dec 2018 13:16:26 -0000 Subject: [Python-checkins] bpo-35363: test_eintr uses print(flush=True) (GH-10990) Message-ID: https://github.com/python/cpython/commit/0644b33821b70efbf0ac1ec1fb8729b05796564a commit: 0644b33821b70efbf0ac1ec1fb8729b05796564a branch: master author: Victor Stinner committer: GitHub date: 2018-12-06T14:16:21+01:00 summary: bpo-35363: test_eintr uses print(flush=True) (GH-10990) files: M Lib/test/test_eintr.py diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index c2e8deadbab7..f61efa3c648e 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -20,12 +20,13 @@ def test_all(self): args = ["-u", tester, "-v"] if support.verbose: print() - print("--- run eintr_tester.py ---") + 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. args = [sys.executable, "-E", "-X", "faulthandler", *args] proc = subprocess.run(args) - print(f"--- eintr_tester.py completed: exit code {proc.returncode} ---") + print(f"--- eintr_tester.py completed: " + f"exit code {proc.returncode} ---", flush=True) if proc.returncode: self.fail("eintr_tester.py failed") else: From webhook-mailer at python.org Thu Dec 6 08:35:04 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 13:35:04 -0000 Subject: [Python-checkins] bpo-35363: test_eintr uses print(flush=True) (GH-10990) Message-ID: https://github.com/python/cpython/commit/560fa4db17983ce37c1453c057901c627b2c3abc commit: 560fa4db17983ce37c1453c057901c627b2c3abc branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T05:34:59-08:00 summary: bpo-35363: test_eintr uses print(flush=True) (GH-10990) (cherry picked from commit 0644b33821b70efbf0ac1ec1fb8729b05796564a) Co-authored-by: Victor Stinner files: M Lib/test/test_eintr.py diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index c2e8deadbab7..f61efa3c648e 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -20,12 +20,13 @@ def test_all(self): args = ["-u", tester, "-v"] if support.verbose: print() - print("--- run eintr_tester.py ---") + 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. args = [sys.executable, "-E", "-X", "faulthandler", *args] proc = subprocess.run(args) - print(f"--- eintr_tester.py completed: exit code {proc.returncode} ---") + print(f"--- eintr_tester.py completed: " + f"exit code {proc.returncode} ---", flush=True) if proc.returncode: self.fail("eintr_tester.py failed") else: From webhook-mailer at python.org Thu Dec 6 08:40:35 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 13:40:35 -0000 Subject: [Python-checkins] bpo-35363: test_eintr uses print(flush=True) (GH-10990) Message-ID: https://github.com/python/cpython/commit/3f0e8e225e2275d22c4bd2e8f8f212b6a8b849aa commit: 3f0e8e225e2275d22c4bd2e8f8f212b6a8b849aa branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T05:40:31-08:00 summary: bpo-35363: test_eintr uses print(flush=True) (GH-10990) (cherry picked from commit 0644b33821b70efbf0ac1ec1fb8729b05796564a) Co-authored-by: Victor Stinner files: M Lib/test/test_eintr.py diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index c2e8deadbab7..f61efa3c648e 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -20,12 +20,13 @@ def test_all(self): args = ["-u", tester, "-v"] if support.verbose: print() - print("--- run eintr_tester.py ---") + 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. args = [sys.executable, "-E", "-X", "faulthandler", *args] proc = subprocess.run(args) - print(f"--- eintr_tester.py completed: exit code {proc.returncode} ---") + print(f"--- eintr_tester.py completed: " + f"exit code {proc.returncode} ---", flush=True) if proc.returncode: self.fail("eintr_tester.py failed") else: From webhook-mailer at python.org Thu Dec 6 12:06:00 2018 From: webhook-mailer at python.org (Pablo Galindo) Date: Thu, 06 Dec 2018 17:06:00 -0000 Subject: [Python-checkins] Remove unused function in `testmock/support.py` (GH-10975) Message-ID: https://github.com/python/cpython/commit/20428527a7c188d988d20b267cfef58da10b0fc9 commit: 20428527a7c188d988d20b267cfef58da10b0fc9 branch: master author: Mario Corchero committer: Pablo Galindo date: 2018-12-06T17:05:46Z summary: Remove unused function in `testmock/support.py` (GH-10975) The function is never imported and the implementation is actually buggy. As `warnings.catch_warnings` is not imported here. files: M Lib/unittest/test/testmock/support.py diff --git a/Lib/unittest/test/testmock/support.py b/Lib/unittest/test/testmock/support.py index 205431adcacc..c7ad20b80665 100644 --- a/Lib/unittest/test/testmock/support.py +++ b/Lib/unittest/test/testmock/support.py @@ -12,10 +12,3 @@ def wibble(self): class X(object): pass - - -def examine_warnings(func): - def wrapper(): - with catch_warnings(record=True) as ws: - func(ws) - return wrapper From webhook-mailer at python.org Thu Dec 6 15:37:04 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 06 Dec 2018 20:37:04 -0000 Subject: [Python-checkins] bpo-33023: Fix NotImplemented to NotImplementedError. (GH-10934) Message-ID: https://github.com/python/cpython/commit/42b1d6127bd8595522a78a75166ebb9fba74a6a2 commit: 42b1d6127bd8595522a78a75166ebb9fba74a6a2 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-06T22:36:55+02:00 summary: bpo-33023: Fix NotImplemented to NotImplementedError. (GH-10934) files: M Lib/idlelib/debugger_r.py M Lib/ssl.py M Lib/test/test_ssl.py diff --git a/Lib/idlelib/debugger_r.py b/Lib/idlelib/debugger_r.py index 01a3bd25998f..0e6dcfbd12c2 100644 --- a/Lib/idlelib/debugger_r.py +++ b/Lib/idlelib/debugger_r.py @@ -157,7 +157,7 @@ def code_filename(self, cid): #----------called by a DictProxy---------- def dict_keys(self, did): - raise NotImplemented("dict_keys not public or pickleable") + raise NotImplementedError("dict_keys not public or pickleable") ## dict = dicttable[did] ## return dict.keys() diff --git a/Lib/ssl.py b/Lib/ssl.py index 8f6d402209b1..e6e3a6d0fa8d 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -884,8 +884,8 @@ def session_reused(self): return self._sslobj.session_reused def dup(self): - raise NotImplemented("Can't dup() %s instances" % - self.__class__.__name__) + raise NotImplementedError("Can't dup() %s instances" % + self.__class__.__name__) def _checkClosed(self, msg=None): # raise an exception here if you wish to check for spurious closes diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 74a91f6c2396..7f6b93148f45 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -463,8 +463,12 @@ def test_wrapped_unconnected(self): self.assertRaises(OSError, ss.recvfrom_into, bytearray(b'x'), 1) self.assertRaises(OSError, ss.send, b'x') self.assertRaises(OSError, ss.sendto, b'x', ('0.0.0.0', 0)) + self.assertRaises(NotImplementedError, ss.dup) self.assertRaises(NotImplementedError, ss.sendmsg, [b'x'], (), 0, ('0.0.0.0', 0)) + self.assertRaises(NotImplementedError, ss.recvmsg, 100) + self.assertRaises(NotImplementedError, ss.recvmsg_into, + [bytearray(100)]) def test_timeout(self): # Issue #8524: when creating an SSL socket, the timeout of the @@ -3382,10 +3386,11 @@ def _recvfrom_into(): # Make sure sendmsg et al are disallowed to avoid # inadvertent disclosure of data and/or corruption # of the encrypted data stream + self.assertRaises(NotImplementedError, s.dup) self.assertRaises(NotImplementedError, s.sendmsg, [b"data"]) self.assertRaises(NotImplementedError, s.recvmsg, 100) self.assertRaises(NotImplementedError, - s.recvmsg_into, bytearray(100)) + s.recvmsg_into, [bytearray(100)]) s.write(b"over\n") self.assertRaises(ValueError, s.recv, -1) From webhook-mailer at python.org Thu Dec 6 15:52:48 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 20:52:48 -0000 Subject: [Python-checkins] bpo-33023: Fix NotImplemented to NotImplementedError. (GH-10934) Message-ID: https://github.com/python/cpython/commit/6485aa6eb1024672f08afdd577e2b5792eb6b03c commit: 6485aa6eb1024672f08afdd577e2b5792eb6b03c branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T12:52:43-08:00 summary: bpo-33023: Fix NotImplemented to NotImplementedError. (GH-10934) (cherry picked from commit 42b1d6127bd8595522a78a75166ebb9fba74a6a2) Co-authored-by: Serhiy Storchaka files: M Lib/idlelib/debugger_r.py M Lib/ssl.py M Lib/test/test_ssl.py diff --git a/Lib/idlelib/debugger_r.py b/Lib/idlelib/debugger_r.py index 01a3bd25998f..0e6dcfbd12c2 100644 --- a/Lib/idlelib/debugger_r.py +++ b/Lib/idlelib/debugger_r.py @@ -157,7 +157,7 @@ def code_filename(self, cid): #----------called by a DictProxy---------- def dict_keys(self, did): - raise NotImplemented("dict_keys not public or pickleable") + raise NotImplementedError("dict_keys not public or pickleable") ## dict = dicttable[did] ## return dict.keys() diff --git a/Lib/ssl.py b/Lib/ssl.py index 38aa38907e15..d1d986682035 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -884,8 +884,8 @@ def session_reused(self): return self._sslobj.session_reused def dup(self): - raise NotImplemented("Can't dup() %s instances" % - self.__class__.__name__) + raise NotImplementedError("Can't dup() %s instances" % + self.__class__.__name__) def _checkClosed(self, msg=None): # raise an exception here if you wish to check for spurious closes diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index d4132c5043c7..f1b9565c8d91 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -480,8 +480,12 @@ def test_wrapped_unconnected(self): self.assertRaises(OSError, ss.recvfrom_into, bytearray(b'x'), 1) self.assertRaises(OSError, ss.send, b'x') self.assertRaises(OSError, ss.sendto, b'x', ('0.0.0.0', 0)) + self.assertRaises(NotImplementedError, ss.dup) self.assertRaises(NotImplementedError, ss.sendmsg, [b'x'], (), 0, ('0.0.0.0', 0)) + self.assertRaises(NotImplementedError, ss.recvmsg, 100) + self.assertRaises(NotImplementedError, ss.recvmsg_into, + [bytearray(100)]) def test_timeout(self): # Issue #8524: when creating an SSL socket, the timeout of the @@ -3410,10 +3414,11 @@ def _recvfrom_into(): # Make sure sendmsg et al are disallowed to avoid # inadvertent disclosure of data and/or corruption # of the encrypted data stream + self.assertRaises(NotImplementedError, s.dup) self.assertRaises(NotImplementedError, s.sendmsg, [b"data"]) self.assertRaises(NotImplementedError, s.recvmsg, 100) self.assertRaises(NotImplementedError, - s.recvmsg_into, bytearray(100)) + s.recvmsg_into, [bytearray(100)]) s.write(b"over\n") self.assertRaises(ValueError, s.recv, -1) From webhook-mailer at python.org Thu Dec 6 15:56:28 2018 From: webhook-mailer at python.org (Gregory P. Smith) Date: Thu, 06 Dec 2018 20:56:28 -0000 Subject: [Python-checkins] Clarify expectedFailure in the unittest docs. (#10953) Message-ID: https://github.com/python/cpython/commit/91f259b478ae8bfb4c73e5b5a767e4bf0ee9257f commit: 91f259b478ae8bfb4c73e5b5a767e4bf0ee9257f branch: master author: Gregory P. Smith committer: GitHub date: 2018-12-06T12:56:24-08:00 summary: Clarify expectedFailure in the unittest docs. (#10953) files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 8afbee642620..acf9b49548b3 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -585,8 +585,8 @@ The following decorators implement test skipping and expected failures: .. decorator:: expectedFailure - Mark the test as an expected failure. If the test fails when run, the test - is not counted as a failure. + Mark the test as an expected failure. If the test fails it will be + considered a success. If the test passes, it will be considered a failure. .. exception:: SkipTest(reason) From webhook-mailer at python.org Thu Dec 6 16:00:42 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 06 Dec 2018 21:00:42 -0000 Subject: [Python-checkins] bpo-33023: Fix NotImplemented to NotImplementedError. (GH-10934). (GH-11001) Message-ID: https://github.com/python/cpython/commit/7a2cf1e7d3bf300e98c702589d405734f4a8fcf8 commit: 7a2cf1e7d3bf300e98c702589d405734f4a8fcf8 branch: 3.6 author: Serhiy Storchaka committer: GitHub date: 2018-12-06T23:00:39+02:00 summary: bpo-33023: Fix NotImplemented to NotImplementedError. (GH-10934). (GH-11001) (cherry picked from commit 42b1d6127bd8595522a78a75166ebb9fba74a6a2) files: M Lib/idlelib/debugger_r.py M Lib/ssl.py M Lib/test/test_ssl.py diff --git a/Lib/idlelib/debugger_r.py b/Lib/idlelib/debugger_r.py index 01a3bd25998f..0e6dcfbd12c2 100644 --- a/Lib/idlelib/debugger_r.py +++ b/Lib/idlelib/debugger_r.py @@ -157,7 +157,7 @@ def code_filename(self, cid): #----------called by a DictProxy---------- def dict_keys(self, did): - raise NotImplemented("dict_keys not public or pickleable") + raise NotImplementedError("dict_keys not public or pickleable") ## dict = dicttable[did] ## return dict.keys() diff --git a/Lib/ssl.py b/Lib/ssl.py index cd216a15cda8..58d3e939226b 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -848,8 +848,8 @@ def session_reused(self): return self._sslobj.session_reused def dup(self): - raise NotImplemented("Can't dup() %s instances" % - self.__class__.__name__) + raise NotImplementedError("Can't dup() %s instances" % + self.__class__.__name__) def _checkClosed(self, msg=None): # raise an exception here if you wish to check for spurious closes diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 2f0b6a75e96f..705f1d3245b6 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -408,6 +408,12 @@ def test_wrapped_unconnected(self): self.assertRaises(OSError, ss.recvfrom_into, bytearray(b'x'), 1) self.assertRaises(OSError, ss.send, b'x') self.assertRaises(OSError, ss.sendto, b'x', ('0.0.0.0', 0)) + self.assertRaises(NotImplementedError, ss.dup) + self.assertRaises(NotImplementedError, ss.sendmsg, + [b'x'], (), 0, ('0.0.0.0', 0)) + self.assertRaises(NotImplementedError, ss.recvmsg, 100) + self.assertRaises(NotImplementedError, ss.recvmsg_into, + [bytearray(100)]) def test_timeout(self): # Issue #8524: when creating an SSL socket, the timeout of the @@ -2942,11 +2948,11 @@ def _recvfrom_into(): # Make sure sendmsg et al are disallowed to avoid # inadvertent disclosure of data and/or corruption # of the encrypted data stream + self.assertRaises(NotImplementedError, s.dup) self.assertRaises(NotImplementedError, s.sendmsg, [b"data"]) self.assertRaises(NotImplementedError, s.recvmsg, 100) self.assertRaises(NotImplementedError, - s.recvmsg_into, bytearray(100)) - + s.recvmsg_into, [bytearray(100)]) s.write(b"over\n") self.assertRaises(ValueError, s.recv, -1) From webhook-mailer at python.org Thu Dec 6 16:06:59 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 21:06:59 -0000 Subject: [Python-checkins] Add missing period in distutils.dep_util.newer_group doc (GH-11003) Message-ID: https://github.com/python/cpython/commit/c9566b8c454120e3d0ddb5ab970f262a6cd80077 commit: c9566b8c454120e3d0ddb5ab970f262a6cd80077 branch: master author: Andre Delfino committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-06T13:06:55-08:00 summary: Add missing period in distutils.dep_util.newer_group doc (GH-11003) files: M Doc/distutils/apiref.rst diff --git a/Doc/distutils/apiref.rst b/Doc/distutils/apiref.rst index b10b39ae22ac..a825efc1a672 100644 --- a/Doc/distutils/apiref.rst +++ b/Doc/distutils/apiref.rst @@ -941,7 +941,7 @@ timestamp dependency analysis. .. function:: newer_group(sources, target[, missing='error']) Return true if *target* is out-of-date with respect to any file listed in - *sources* In other words, if *target* exists and is newer than every file in + *sources*. In other words, if *target* exists and is newer than every file in *sources*, return false; otherwise return true. *missing* controls what we do when a source file is missing; the default (``'error'``) is to blow up with an :exc:`OSError` from inside :func:`os.stat`; if it is ``'ignore'``, we silently From webhook-mailer at python.org Thu Dec 6 16:16:08 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 21:16:08 -0000 Subject: [Python-checkins] Clarify expectedFailure in the unittest docs. (GH-10953) Message-ID: https://github.com/python/cpython/commit/f913d44eb75f930c37dc6bcc5d6579be5740ae73 commit: f913d44eb75f930c37dc6bcc5d6579be5740ae73 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T13:16:03-08:00 summary: Clarify expectedFailure in the unittest docs. (GH-10953) (cherry picked from commit 91f259b478ae8bfb4c73e5b5a767e4bf0ee9257f) Co-authored-by: Gregory P. Smith files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index fb5bd2b7648e..774b0875bbf1 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -585,8 +585,8 @@ The following decorators implement test skipping and expected failures: .. decorator:: expectedFailure - Mark the test as an expected failure. If the test fails when run, the test - is not counted as a failure. + Mark the test as an expected failure. If the test fails it will be + considered a success. If the test passes, it will be considered a failure. .. exception:: SkipTest(reason) From webhook-mailer at python.org Thu Dec 6 16:30:17 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 21:30:17 -0000 Subject: [Python-checkins] Add missing period in distutils.dep_util.newer_group doc (GH-11003) Message-ID: https://github.com/python/cpython/commit/72c71956cade606bd5500cf76d4d7c1d50a7ccae commit: 72c71956cade606bd5500cf76d4d7c1d50a7ccae branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T13:30:13-08:00 summary: Add missing period in distutils.dep_util.newer_group doc (GH-11003) (cherry picked from commit c9566b8c454120e3d0ddb5ab970f262a6cd80077) Co-authored-by: Andre Delfino files: M Doc/distutils/apiref.rst diff --git a/Doc/distutils/apiref.rst b/Doc/distutils/apiref.rst index dccd7ce236cb..8efeffb6376e 100644 --- a/Doc/distutils/apiref.rst +++ b/Doc/distutils/apiref.rst @@ -941,7 +941,7 @@ timestamp dependency analysis. .. function:: newer_group(sources, target[, missing='error']) Return true if *target* is out-of-date with respect to any file listed in - *sources* In other words, if *target* exists and is newer than every file in + *sources*. In other words, if *target* exists and is newer than every file in *sources*, return false; otherwise return true. *missing* controls what we do when a source file is missing; the default (``'error'``) is to blow up with an :exc:`OSError` from inside :func:`os.stat`; if it is ``'ignore'``, we silently From webhook-mailer at python.org Thu Dec 6 16:32:39 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 21:32:39 -0000 Subject: [Python-checkins] Add missing period in distutils.dep_util.newer_group doc (GH-11003) Message-ID: https://github.com/python/cpython/commit/a51a5ca77eae079b34f911975a77e713b0c237f1 commit: a51a5ca77eae079b34f911975a77e713b0c237f1 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T13:32:31-08:00 summary: Add missing period in distutils.dep_util.newer_group doc (GH-11003) (cherry picked from commit c9566b8c454120e3d0ddb5ab970f262a6cd80077) Co-authored-by: Andre Delfino files: M Doc/distutils/apiref.rst diff --git a/Doc/distutils/apiref.rst b/Doc/distutils/apiref.rst index f6163258df2e..207f43864c93 100644 --- a/Doc/distutils/apiref.rst +++ b/Doc/distutils/apiref.rst @@ -937,7 +937,7 @@ timestamp dependency analysis. .. function:: newer_group(sources, target[, missing='error']) Return true if *target* is out-of-date with respect to any file listed in - *sources* In other words, if *target* exists and is newer than every file in + *sources*. In other words, if *target* exists and is newer than every file in *sources*, return false; otherwise return true. *missing* controls what we do when a source file is missing; the default (``'error'``) is to blow up with an :exc:`OSError` from inside :func:`os.stat`; if it is ``'ignore'``, we silently From webhook-mailer at python.org Thu Dec 6 16:34:19 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 06 Dec 2018 21:34:19 -0000 Subject: [Python-checkins] Add missing period in distutils.dep_util.newer_group doc (GH-11003) Message-ID: https://github.com/python/cpython/commit/107b27eee013f0747ba133886f87aacc7f451030 commit: 107b27eee013f0747ba133886f87aacc7f451030 branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T13:34:15-08:00 summary: Add missing period in distutils.dep_util.newer_group doc (GH-11003) (cherry picked from commit c9566b8c454120e3d0ddb5ab970f262a6cd80077) Co-authored-by: Andre Delfino files: M Doc/distutils/apiref.rst diff --git a/Doc/distutils/apiref.rst b/Doc/distutils/apiref.rst index 34a3cb34af32..eade90542c0a 100644 --- a/Doc/distutils/apiref.rst +++ b/Doc/distutils/apiref.rst @@ -934,7 +934,7 @@ timestamp dependency analysis. .. function:: newer_group(sources, target[, missing='error']) Return true if *target* is out-of-date with respect to any file listed in - *sources* In other words, if *target* exists and is newer than every file in + *sources*. In other words, if *target* exists and is newer than every file in *sources*, return false; otherwise return true. *missing* controls what we do when a source file is missing; the default (``'error'``) is to blow up with an :exc:`OSError` from inside :func:`os.stat`; if it is ``'ignore'``, we silently From webhook-mailer at python.org Fri Dec 7 00:09:26 2018 From: webhook-mailer at python.org (Steve Dower) Date: Fri, 07 Dec 2018 05:09:26 -0000 Subject: [Python-checkins] bpo-34977: Add Windows App Store package (GH-10245) Message-ID: https://github.com/python/cpython/commit/468a15aaf9206448a744fc5eab3fc21f51966aad commit: 468a15aaf9206448a744fc5eab3fc21f51966aad branch: master author: Steve Dower committer: GitHub date: 2018-12-06T21:09:20-08:00 summary: bpo-34977: Add Windows App Store package (GH-10245) files: A .azure-pipelines/windows-appx-test.yml A Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst A PC/classicAppCompat.can.xml A PC/classicAppCompat.cat A PC/classicAppCompat.sccd A PC/icons/pythonwx150.png A PC/icons/pythonwx44.png A PC/icons/pythonx150.png A PC/icons/pythonx44.png A PC/icons/pythonx50.png A PC/layout/__init__.py A PC/layout/__main__.py A PC/layout/main.py A PC/layout/support/__init__.py A PC/layout/support/appxmanifest.py A PC/layout/support/catalog.py A PC/layout/support/constants.py A PC/layout/support/distutils.command.bdist_wininst.py A PC/layout/support/filesets.py A PC/layout/support/logging.py A PC/layout/support/options.py A PC/layout/support/pip.py A PC/layout/support/props.py A PC/layout/support/python.props A PC/python_uwp.cpp A PC/store_info.txt A PCbuild/python_uwp.vcxproj A PCbuild/pythonw_uwp.vcxproj A PCbuild/venvlauncher.vcxproj A PCbuild/venvwlauncher.vcxproj A Tools/msi/make_appx.ps1 A Tools/msi/make_cat.ps1 A Tools/msi/sdktools.psm1 A Tools/msi/sign_build.ps1 D Tools/msi/make_zip.py D Tools/nuget/python.props M .gitattributes M Doc/make.bat M Lib/test/test_pathlib.py M Lib/test/test_venv.py M Lib/venv/__init__.py M PC/getpathp.c M PC/launcher.c M PC/pylauncher.rc M PCbuild/_tkinter.vcxproj M PCbuild/find_msbuild.bat M PCbuild/pcbuild.proj M PCbuild/pcbuild.sln M PCbuild/python.props M PCbuild/pythoncore.vcxproj M Tools/msi/buildrelease.bat M Tools/msi/make_zip.proj M Tools/nuget/make_pkg.proj diff --git a/.azure-pipelines/windows-appx-test.yml b/.azure-pipelines/windows-appx-test.yml new file mode 100644 index 000000000000..9840c0a1221f --- /dev/null +++ b/.azure-pipelines/windows-appx-test.yml @@ -0,0 +1,65 @@ +jobs: +- job: Prebuild + displayName: Pre-build checks + + pool: + vmImage: ubuntu-16.04 + + steps: + - template: ./prebuild-checks.yml + + +- job: Windows_Appx_Tests + displayName: Windows Appx Tests + dependsOn: Prebuild + condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) + + pool: + vmImage: vs2017-win2016 + + strategy: + matrix: + win64: + arch: amd64 + buildOpt: '-p x64' + testRunTitle: '$(Build.SourceBranchName)-win64-appx' + testRunPlatform: win64 + maxParallel: 2 + + steps: + - checkout: self + clean: true + fetchDepth: 5 + + - powershell: | + # Relocate build outputs outside of source directory to make cleaning faster + Write-Host '##vso[task.setvariable variable=Py_IntDir]$(Build.BinariesDirectory)\obj' + # UNDONE: Do not build to a different directory because of broken tests + Write-Host '##vso[task.setvariable variable=Py_OutDir]$(Build.SourcesDirectory)\PCbuild' + Write-Host '##vso[task.setvariable variable=EXTERNAL_DIR]$(Build.BinariesDirectory)\externals' + displayName: Update build locations + + - script: PCbuild\build.bat -e $(buildOpt) + displayName: 'Build CPython' + + - script: python.bat PC\layout -vv -s "$(Build.SourcesDirectory)" -b "$(Py_OutDir)\$(arch)" -t "$(Py_IntDir)\layout-tmp-$(arch)" --copy "$(Py_IntDir)\layout-$(arch)" --precompile --preset-appx --include-tests + displayName: 'Create APPX layout' + + - script: .\python.exe -m test.pythoninfo + workingDirectory: $(Py_IntDir)\layout-$(arch) + displayName: 'Display build info' + + - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml" --tempdir "$(Py_IntDir)\tmp-$(arch)" + workingDirectory: $(Py_IntDir)\layout-$(arch) + displayName: 'Tests' + env: + PREFIX: $(Py_IntDir)\layout-$(arch) + + - task: PublishTestResults at 2 + displayName: 'Publish Test Results' + inputs: + testResultsFiles: '$(Build.BinariesDirectory)\test-results.xml' + mergeTestResults: true + testRunTitle: $(testRunTitle) + platform: $(testRunPlatform) + condition: succeededOrFailed() diff --git a/.gitattributes b/.gitattributes index 4a487c3c2a14..16237bb2b3ac 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,6 +19,7 @@ # Specific binary files Lib/test/sndhdrdata/sndhdr.* binary +PC/classicAppCompat.* binary # Text files that should not be subject to eol conversion Lib/test/cjkencodings/* -text diff --git a/Doc/make.bat b/Doc/make.bat index d28dae78e86d..a8b32375810d 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -117,10 +117,12 @@ if not exist "%BUILDDIR%" mkdir "%BUILDDIR%" if exist ..\Misc\NEWS ( echo.Copying Misc\NEWS to build\NEWS + if not exist build mkdir build copy ..\Misc\NEWS build\NEWS > nul ) else if exist ..\Misc\NEWS.D ( if defined BLURB ( echo.Merging Misc/NEWS with %BLURB% + if not exist build mkdir build %BLURB% merge -f build\NEWS ) else ( echo.No Misc/NEWS file and Blurb is not available. diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 876eecccfd5f..d3fd4bd9e6b7 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1521,7 +1521,7 @@ def test_resolve_common(self): # resolves to 'dirB/..' first before resolving to parent of dirB. self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) # Now create absolute symlinks - d = support._longpath(tempfile.mkdtemp(suffix='-dirD')) + d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) self.addCleanup(support.rmtree, d) os.symlink(os.path.join(d), join('dirA', 'linkX')) os.symlink(join('dirB'), os.path.join(d, 'linkY')) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 461fe7afd213..22a3b78852f8 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -243,6 +243,7 @@ def test_isolation(self): self.assertIn('include-system-site-packages = %s\n' % s, data) @unittest.skipUnless(can_symlink(), 'Needs symlinks') + @unittest.skipIf(os.name == 'nt', 'Symlinks are never used on Windows') def test_symlinking(self): """ Test symlinking works as expected diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index 043420897e47..5438b0d4e508 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -64,10 +64,11 @@ def create(self, env_dir): self.system_site_packages = False self.create_configuration(context) self.setup_python(context) + if not self.upgrade: + self.setup_scripts(context) if self.with_pip: self._setup_pip(context) if not self.upgrade: - self.setup_scripts(context) self.post_setup(context) if true_system_site_packages: # We had set it to False before, now @@ -158,14 +159,6 @@ def create_configuration(self, context): f.write('include-system-site-packages = %s\n' % incl) f.write('version = %d.%d.%d\n' % sys.version_info[:3]) - if os.name == 'nt': - def include_binary(self, f): - if f.endswith(('.pyd', '.dll')): - result = True - else: - result = f.startswith('python') and f.endswith('.exe') - return result - def symlink_or_copy(self, src, dst, relative_symlinks_ok=False): """ Try symlinking a file, and if that fails, fall back to copying. @@ -195,9 +188,9 @@ def setup_python(self, context): binpath = context.bin_path path = context.env_exe copier = self.symlink_or_copy - copier(context.executable, path) dirname = context.python_dir if os.name != 'nt': + copier(context.executable, path) if not os.path.islink(path): os.chmod(path, 0o755) for suffix in ('python', 'python3'): @@ -209,26 +202,22 @@ def setup_python(self, context): if not os.path.islink(path): os.chmod(path, 0o755) else: - # See bpo-34011. When using a proper install, we should only need to - # copy the top-level of DLLs. - include = self.include_binary - files = [f for f in os.listdir(dirname) if include(f)] - for f in files: - src = os.path.join(dirname, f) - dst = os.path.join(binpath, f) - if dst != context.env_exe: # already done, above - copier(src, dst) - - # When creating from a build directory, we continue to copy all files. + # For normal cases, the venvlauncher will be copied from + # our scripts folder. For builds, we need to copy it + # manually. if sysconfig.is_python_build(True): - subdir = 'DLLs' - dirname = os.path.join(dirname, subdir) - if os.path.isdir(dirname): - files = [f for f in os.listdir(dirname) if include(f)] - for f in files: - src = os.path.join(dirname, f) - dst = os.path.join(binpath, f) - copier(src, dst) + suffix = '.exe' + if context.python_exe.lower().endswith('_d.exe'): + suffix = '_d.exe' + + src = os.path.join(dirname, "venvlauncher" + suffix) + dst = os.path.join(binpath, context.python_exe) + copier(src, dst) + + src = os.path.join(dirname, "venvwlauncher" + suffix) + dst = os.path.join(binpath, "pythonw" + suffix) + copier(src, dst) + # copy init.tcl over for root, dirs, files in os.walk(context.python_dir): if 'init.tcl' in files: @@ -326,7 +315,7 @@ def install_scripts(self, context, path): dstfile = os.path.join(dstdir, f) with open(srcfile, 'rb') as f: data = f.read() - if not srcfile.endswith('.exe'): + if not srcfile.endswith(('.exe', '.pdb')): try: data = data.decode('utf-8') data = self.replace_variables(data, context) diff --git a/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst new file mode 100644 index 000000000000..8e1a4ba84880 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst @@ -0,0 +1 @@ +Adds support for building a Windows App Store package diff --git a/PC/classicAppCompat.can.xml b/PC/classicAppCompat.can.xml new file mode 100644 index 000000000000..f00475c8da31 --- /dev/null +++ b/PC/classicAppCompat.can.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/PC/classicAppCompat.cat b/PC/classicAppCompat.cat new file mode 100644 index 000000000000..3d213596accf Binary files /dev/null and b/PC/classicAppCompat.cat differ diff --git a/PC/classicAppCompat.sccd b/PC/classicAppCompat.sccd new file mode 100644 index 000000000000..97648985a2cc --- /dev/null +++ b/PC/classicAppCompat.sccd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + MIIq5AYJKoZIhvcNAQcCoIIq1TCCKtECAQExDzANBglghkgBZQMEAgEFADCCARAGCSsGAQQBgjcKAaCCAQEwgf4wDAYKKwYBBAGCNwwBAQQQaM+L42jwBUGvBczrtolMmhcNMTgxMTMwMDA1OTAzWjAOBgorBgEEAYI3DAEDBQAwgbwwKgQUWKcU3R38DGPlKK33XGIwKtVL1r4xEjAQBgorBgEEAYI3DAIDMQKCADCBjQQg3K+KBOQX7HfxjRNZC9cx8gIPkEhPRO1nJFRdWQrVEJ4xaTAQBgorBgEEAYI3DAIDMQKCADBVBgorBgEEAYI3AgEEMUcwRTAQBgorBgEEAYI3AgEZogKAADAxMA0GCWCGSAFlAwQCAQUABCDcr4oE5Bfsd/GNE1kL1zHyAg+QSE9E7WckVF1ZCtUQnqCCFFAwggZSMIIEOqADAgECAhMzAAMu49KhfNamygpWAAIAAy7jMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQDEx5NaWNyb3NvZnQgTWFya2V0cGxhY2UgQ0EgRyAwMTMwHhcNMTgxMTMwMDA1NTA1WhcNMTgxMjAzMDA1NTA1WjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwpcimfAx3HEpba1GLL/gDaRVddHE5PXTRmwlgaz8kt6/rq5rlrPFnCnbIc5818v0xJIznastbmrq26xyCEHyMLBKnyneTKE36I7+TGjcY0D7ow+o2vY7LDKMCTGlh31fx1Tvrl+5xTbWX5jdLU/3MB5faeOGh+0Knzwx1KDoXWgPtfXnD8I5jxJieoWoCwCjKTJgBOklLy9nbOalxf0h+xQRy2p5fj+PxAwQPgHWft36AF7/IMbt9FcXMtg4xdpnTYz4OV3dFOPz4m3M8HwVgNMv89W/1Ozc7uOyZt0Ij1baT6r2L3IjYg5ftzpGqaDOFcWlyDFSdhMR6BIKW8xEpAgMBAAGjggHCMIIBvjAYBgNVHSUBAf8EDjAMBgorBgEEAYI3TBwBMB0GA1UdDgQWBBRdpGYiCytx83FYzPSl+o97YzpxGzAPBgNVHREECDAGggRNT1BSMB8GA1UdIwQYMBaAFEnYB1RFhpclHtZZcRLDcpt0OE3oMGIGA1UdHwRbMFkwV6BVoFOGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyME1hcmtldHBsYWNlJTIwQ0ElMjBHJTIwMDEzKDIpLmNybDBvBggrBgEFBQcBAQRjMGEwXwYIKwYBBQUHMAKGU2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwTWFya2V0cGxhY2UlMjBDQSUyMEclMjAwMTMoMikuY3J0MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXgMDwGCSsGAQQBgjcVBwQvMC0GJSsGAQQBgjcVCIOS9kTqrxCDkY0wgqzgLIKinDE0g+6NOIaE7wACAWQCARYwIAYJKwYBBAGCNxUKAQH/BBAwDjAMBgorBgEEAYI3TBwBMA0GCSqGSIb3DQEBCwUAA4ICAQB3Dk3rXH52CDq/z1fwqn9xI5WGjGmu6oAE4HSc3sNdFrSVMMGm4gTlYGWSZ0wJUUf16mVr/rdXhxuR3MZn+m4Bhdl8KQqYjYbIvCUVj0o9nZ+yT6foeY8bKnB+K5h6rol+mjDj5IfcutC4x2Kx5RrtDtRTSoKA63iZ74DYngPpBGBBgaS2c/QzgqPRAMMRqy2KBDP0miCnpR3F4YlzHGyOZwyHhESjYd9kwF47+msuHS04JZpnGHIvBppKN9XQzH3WezNnnX3lz4AyAUMsMFuARqEnacUhrAHL9n5zMv9CzxDYN1r1/aDh/788RuGuZM+E3NtmbxJJ7j6T5/VtXNBRgKtIq8d2+11j6qvKLigOTxSC25/A70BZBEvllLFnvc1vA2LrC9drwt1KpSmWie1nvpilw7o+gHMOG9utUxGha2VuVizuVNGCywTRRjvmGS1QqTfaun1URVrLfnDINXuTgN1Vwp0J5IGpJ3D8yj01NDQ/RworE+3W/R531NBYova9QRhU/igEw/Aa/q8wjZ4Pzxr9oBIo0Ta3Tv6qIggaWXw0U9+F0J7SCqIhn0d0ATO+E1Qs/SxZIAICLwmqzoLYUAh8q153esBs4uesueqgt5ueyHK8V3WjMS4wxEyVN5ZMET3hFtEshsZC31tLDdjq750U4SgQVmoYSm3F3ZOKQDCCBtcwggS/oAMCAQICCmESRKIAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDMyODIxMDkzOVoXDTMxMDMyODIxMTkzOVowfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAubUaSwGYVsE3MAnPfvmozUhAB3qxBABgJRW1vDp4+tVinXxD32f7k1K89JQ6zDOgS/iDgULC+yFK1K/1Qjac/0M7P6c8v5LSjnWGlERLa/qY32j46S7SLQcit3g2jgoTTO03eUG+9yHZUTGV/FJdRYB8uXhrznJBa+Y+yGwiQKF+m6XFeBH/KORoKFx+dmMoy9EWJ/m/o9IiUj2kzm9C691+vZ/I2w0Bj93W9SPPkV2PCNHlzgfIAoeajWpHmi38Wi3xZHonkzAVBHxPsCBppOoNsWvmAfUM7eBthkSPvFruekyDCPNEYhfGqgqtqLkoBebXLZCOVybF7wTQaLvse60//3P003icRcCoQYgY4NAqrF7j80o5U7DkeXxcB0xvengsaKgiAaV1DKkRbpe98wCqr1AASvm5rAJUYMU+mXmOieV2EelY2jGrenWe9FQpNXYV1NoWBh0WKoFxttoWYAnF705bIWtSZsz08ZfK6WLX4GXNLcPBlgCzfTm1sdKYASWdBbH2haaNhPapFhQQBJHKwnVW2iXErImhuPi45W3MVTZ5D9ASshZx69cLYY6xAdIa+89Kf/uRrsGOVZfahDuDw+NI183iAyzC8z/QRt2P32LYxP0xrCdqVh+DJo2i4NoE8Uk1usCdbVRuBMBQl/AwpOTq7IMvHGElf65CqzUCAwEAAaOCAUswggFHMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBQPU8s/FmEl/mCJHdO5fOiQrbOU0TAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCjuZmM8ZVNDgp9wHsL4RY8KJ8nLinvxFTphNGCrxaLknkYG5pmMhVlX+UB/tSiW8W13W60nggz9u5xwMx7v/1t/Tgm6g2brVyOKI5A7u6/2SIJwkJKFw953K0YIKVT28w9zl8dSJnmRnyR0G86ncWbF6CLQ6A6lBQ9o2mTGVqDr4m35WKAnc6YxUUM1y74mbzFFZr63VHsCcOp3pXWnUqAY1rb6Q6NX1b3clncKqLFm0EjKHcQ56grTbwuuB7pMdh/IFCJR01MQzQbDtpEisbOeZUi43YVAAHKqI1EO9bRwg3frCjwAbml9MmI4utMW94gWFgvrMxIX+n42RBDIjf3Ot3jkT6gt3XeTTmO9bptgblZimhERdkFRUFpVtkocJeLoGuuzP93uH/Yp032wzRH+XmMgujfZv+vnfllJqxdowoQLx55FxLLeTeYfwi/xMSjZO2gNven3U/3KeSCd1kUOFS3AOrwZ0UNOXJeW5JQC6Vfd1BavFZ6FAta1fMLu3WFvNB+FqeHUaU3ya7rmtxJnzk29DeSqXgGNmVSywBS4NajI5jJIKAA6UhNJlsg8CHYwUOKf5ej8OoQCkbadUxXygAfxCfW2YBbujtI+PoyejRFxWUjYFWO5LeTI62UMyqfOEiqugoYjNxmQZla2s4YHVuqIC34R85FQlg9pKQBsDCCBxswggUDoAMCAQICEzMAAABCs21EHGjyqKYAAAAAAEIwDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMB4XDTE4MDQyMDE2NDI0NFoXDTIxMDQyMDE2NDI0NFowgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOZ2KM9Pq1YCOiqWOivmHjUtkMgznTMP/Mr2YfzZeIIJySg1F4WxFZc4jagGHHNof9NRT+GGnktWsXkZuH1DzQEG4Ps1ln8+4vhbDglqu5ymDnd6RmsyoD+8xfc8bBIvE5o6R+ES4/GVD5TqNsOrWbwETaIZVbmTulJLoTS1WSsSjowmbc+sHqZiY8BNJNThUEmXSjuHqkQKKshuiFWYEqOTitp71mBLyH1wN7/jThRzGpolOeFusRNJdb8sEqvNzEN9Qh+Kp6ndzrnjE+t8ixXW3lShyyOOZqQMwsQn9q9T0v7Q69GuojBTFBOHKwigcCHr4xahuN+ZYMk0xGg+sm3Uj7I9mrWTSTiIRMZNIWq3sFg4+rFg48NYfRlXUpONmL7vXq6v1pIU99d2MXQ6uUrnUr1/n5ZiHGCeFcvWwqO8BYHdcTlrSOkayfFp7W9oCk9QO4Xy0h9cQRedRo2kvdTHxIuJS70Hdv6oePPF2ZFaLucUzzwsR4/XMAVKY8Vsm950omsSSOImsMtzavUdQM+wZFxvHTRqVDkF3quPdME0bCZOWB4hQJmd+o2clw+1mpwPu0/M92nA9FJg7MGPxkFaYW7g26jSqUJZ9AcX+Xa5TSIeqMZt3cRVjMTx0T/v73Sv8TpalqIQ5Fde1+hFK07sOAm3TwgzvlVJnbYgp0/rAgMBAAGjggGCMIIBfjASBgkrBgEEAYI3FQEEBQIDAgACMCMGCSsGAQQBgjcVAgQWBBSbJnDhuc3nQXuKuACsPflEbwjbozAdBgNVHQ4EFgQUSdgHVEWGlyUe1llxEsNym3Q4TegwEQYDVR0gBAowCDAGBgRVHSAAMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFA9Tyz8WYSX+YIkd07l86JCts5TRMFcGA1UdHwRQME4wTKBKoEiGRmh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcmwwWwYIKwYBBQUHAQEETzBNMEsGCCsGAQUFBzAChj9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAIa2oa6kvuIHCNfz7anlL0W9tOCt8gQNkxOGRK3yliQIelNQahDJojyEFlHQ2BcHL5oZit3WeSDoYddhojx6YzJIWwfGwtVqgc0JFDKJJ2ZXRYMRsuy01Hn25xob+zRMS6VmV1axQn6uwOSMcgYmzoroh6edjPKu7qXcpt6LmhF2qFvLySA7wBCwfI/rR5/PX6I7a07Av7PpbY6/+2ujd8m1H3hwMrb4Hq3z6gcq62zJ3nDXUbC0Bp6Jt2kV9f0rEFpDK9oxE2qrGBUf8c3O2XirHOgAjRyWjWWtVms+MP8qBIA1NSLrBmToEWVP3sEkQZWMkoZWo4rYEJZpX7UIgdDc9zYNakgTCJqPhqn8AE1sgSSnpqAdMkkP41rTlFCv2ig2QVzDerjGfEv+uPDnlAT0kucbBJxHHvUC4aqUxaTSa0sy2bZ6NWFx8/u0gW8JahzxYvvvZL8SfwaA9P4ETb8pH1jw+6N/LfM2zJrNKhf5hjKa0VDOXUpkYq60OqVVnWJ6oJaSIWNkZKfzPnl/UHA8Bh4qfVrhc9H5PExPhhB9WVTsjf4r+OOVuolJldThcWQqljiPjk5rultr63G5xLyFpxNi4BCrcNQBJFB5wKgOWOyjQTVWTmh2ESaeqZ2aWBjftFHlxJ/qYc7WOGJV0+cHGkB/dvFxmKnv6tuWexiMMYIVUTCCFU0CAQEwgaQwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMwITMwADLuPSoXzWpsoKVgACAAMu4zANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKwYBBAGCNwoBMC8GCSqGSIb3DQEJBDEiBCAS0d3bw2YOODvKFr0S4e3BDnaDcZXUKeBO77yvkWzVojBIBgorBgEEAYI3AgEMMTowOKAegBwATQBpAGMAcgBvAHMAbwBmAHQAIABDAG8AcgBwoRaAFGh0dHA6Ly9NaWNyb3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIBABoap3Y+2k+zFz2cCmkc8xxHnpIygLsUSRMXeXdjPVcYx3o5cPLIixnL6p8+LIrlIagPg23mzTEmnjZaO4aaexk+3XojlHj22w/bEigEDnKyWt5bHeS0UNHJbxEFYRfd84IP1+mSH4c4+GuU9p3LsAMh6wN03MYrGmczUOnlP6YlxHNQbQxnV0sl14yOE5ni9oT4y+l+SllvbV3/Jhwpov68aoP/2MazqxR4QyGfSxhCPJ4UuDHU7IrpnTxGBTL1/oUU8ED0FxyDoH/Sc5OhTLInFqbZaVzm5Mpr12wYUBL4nE5h0Kf6BCKdgM8a+Ti3wMUsBoC79ff3jE9U/xwSneOhghLlMIIS4QYKKwYBBAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQghPy22lwuCYESw8jYhb4F9ZDPJ1LPgSSZgJDkyXYzVt4CBlv98KtAoBgTMjAxODExMzAwMTA1MTkuMTM4WjAEgAIB9KCB0KSBzTCByjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RDA4Mi00QkZELUVFQkExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAOIYOHtm6erB2AAAAAAA4jANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0xODA4MjMyMDI3MDNaFw0xOTExMjMyMDI3MDNaMIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEMDgyLTRCRkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgc2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKirA72FF3NCLW5mfLO/D0EZ5Ycs00oiMSissXLB6WF9GNdP78QzFwAypxW/+qZSczqaHbDH8hlbxkzf3DiYgAdpQjnGkLujwKtWSaP29/lVf7jFqHy9v6eH+LdOi0LvtrPRW34MyCvpxZyOW4H1h3PkxCBL5Ra21sDqgcVL1me0osw8QTURXmI4LyeLdTH3CcI2AgNDXTjsFBf3QsO+JYyAOYWrTcLnywVN6DrigmgrDJk5w+wR4VrHfl2T9PRZbZ+UDt13wwyB9d6IURuzV8lHsAVfF8t9S0aGVPmkQ3c2waOhHpsp6VEM+T5D2Ph8xJX1r82z67WRlmGcOP2NWC0CAwEAAaOCARswggEXMB0GA1UdDgQWBBSJPpD6BsP2p+crDJL232voEtLxezAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQARQHu7ISeBuJSHKuDRI04704cH0B7BYzeEIrD15awviMRcYIfIOHpvGzZOWQgP2Hm0Rr7kvTUu1VrSSaQ7i1gPWdhqMmw5WBnSS5bxeMhhx9UsASeE84vUu82NeZapGSjH38YAb4WT+TtiTkcoI59rA+CTCq108ttIxVfZcr3id76OETIH0HvhlnxOOWjwGy4ul6Za5RoTLG/oo2rrGmVi3FwrNWGezYLBODuEsjzG36lCRtBKC2ZAHfbOz5wtkUHbqh79mUKocjP4r3qxf5TN87yf6g1uTx+J8pdnAi5iHt+ZtangWqnVTE8PoIREWhBVlGFfQdkELUx2Or90aAqWMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQwODItNEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBzZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQByQCUheEOevaI9Zc/3QGrkX42iC6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA36ppYDAiGA8yMDE4MTEyOTIxMzQyNFoYDzIwMTgxMTMwMjEzNDI0WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDfqmlgAgEAMAoCAQACAitfAgH/MAcCAQACAhGtMAoCBQDfq7rgAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbAXXPR9wy4NA0892GGqetaZF+pNClpGcfEpSuHABaZ4Gzr1nY1nmrhexTtr/U6omHALRWzkQwthk0cy+mnEHXyOZGmoEEpgrLgK3AAP5NbK/XbtHQRyZJQyhZScFbOyQycoE8QQalSVOhWxk/bbBMQaQiYVMIexNd/T0KgaDDUMxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAOIYOHtm6erB2AAAAAAA4jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCr9IiSbx6s8MLdxldRG49+4h6CbicW8hWXAicI3jNmhDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIN8BpJSmQCGubWwVa4tW+aMveoHMX/nDnVN8fiDOMsrLMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADiGDh7ZunqwdgAAAAAAOIwIgQgTkOfRvGEZNbr5/hgWclsL4/Q7SOZihE/U0lz2wEMIGcwDQYJKoZIhvcNAQELBQAEggEATlxnCfTzFfTMDvK085zlYPVCroKYW6gKFYnbAhNmrNzcxqALKmIYXpFU7B6HH/vYzkUfCyXpf5tsyEWu0oTySOjyAZ9+2vdaG8nEgjOp0L737lcitgusIjpWtta3Ik0b+mzffnvyjrgTSuKDDni3mxGfvJU77k1Ctempma4H2FJso6Bur0PRH99vIYDu4lHigOSLbeyjR5CiDciBwEVUSA0FxhoFNX1yfpxz3sukOvkaoTduREIjH5LxUjNI1ZTMK/ZkeETI8IPRpWVzAc8q7CujErHKo4sdKej/O2cfUTUHplFLVCGGExpJUCg5FH5jVUUFt75ad8503sdGplggVQ== diff --git a/PC/getpathp.c b/PC/getpathp.c index 25f371fc9f9d..452501a9a884 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -536,10 +536,16 @@ static _PyInitError get_program_full_path(const _PyCoreConfig *core_config, PyCalculatePath *calculate, _PyPathConfig *config) { + const wchar_t *pyvenv_launcher; wchar_t program_full_path[MAXPATHLEN+1]; memset(program_full_path, 0, sizeof(program_full_path)); - if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { + /* The launcher may need to force the executable path to a + * different environment, so override it here. */ + pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__"); + if (pyvenv_launcher && pyvenv_launcher[0]) { + wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher); + } else if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { /* GetModuleFileName should never fail when passed NULL */ return _Py_INIT_ERR("Cannot determine program path"); } diff --git a/PC/icons/pythonwx150.png b/PC/icons/pythonwx150.png new file mode 100644 index 000000000000..4c3eb316739c Binary files /dev/null and b/PC/icons/pythonwx150.png differ diff --git a/PC/icons/pythonwx44.png b/PC/icons/pythonwx44.png new file mode 100644 index 000000000000..e3b32a871f90 Binary files /dev/null and b/PC/icons/pythonwx44.png differ diff --git a/PC/icons/pythonx150.png b/PC/icons/pythonx150.png new file mode 100644 index 000000000000..5f8d30418386 Binary files /dev/null and b/PC/icons/pythonx150.png differ diff --git a/PC/icons/pythonx44.png b/PC/icons/pythonx44.png new file mode 100644 index 000000000000..3881daaef233 Binary files /dev/null and b/PC/icons/pythonx44.png differ diff --git a/PC/icons/pythonx50.png b/PC/icons/pythonx50.png new file mode 100644 index 000000000000..7cc3aecd0242 Binary files /dev/null and b/PC/icons/pythonx50.png differ diff --git a/PC/launcher.c b/PC/launcher.c index 2c2da76f6146..0242f2639119 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -28,7 +28,7 @@ #define RC_NO_PYTHON 103 #define RC_NO_MEMORY 104 /* - * SCRIPT_WRAPPER is used to choose between two variants of an executable built + * SCRIPT_WRAPPER is used to choose one of the variants of an executable built * from this source file. If not defined, the PEP 397 Python launcher is built; * if defined, a script launcher of the type used by setuptools is built, which * looks for a script name related to the executable name and runs that script @@ -40,6 +40,15 @@ #if defined(SCRIPT_WRAPPER) #define RC_NO_SCRIPT 105 #endif +/* + * VENV_REDIRECT is used to choose the variant that looks for an adjacent or + * one-level-higher pyvenv.cfg, and uses its "home" property to locate and + * launch the original python.exe. + */ +#if defined(VENV_REDIRECT) +#define RC_NO_VENV_CFG 106 +#define RC_BAD_VENV_CFG 107 +#endif /* Just for now - static definition */ @@ -97,7 +106,7 @@ error(int rc, wchar_t * format, ... ) #if !defined(_WINDOWS) fwprintf(stderr, L"%ls\n", message); #else - MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), + MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...", MB_OK); #endif exit(rc); @@ -131,6 +140,17 @@ static wchar_t * get_env(wchar_t * key) return buf; } +#if defined(_DEBUG) +#if defined(_WINDOWS) + +#define PYTHON_EXECUTABLE L"pythonw_d.exe" + +#else + +#define PYTHON_EXECUTABLE L"python_d.exe" + +#endif +#else #if defined(_WINDOWS) #define PYTHON_EXECUTABLE L"pythonw.exe" @@ -139,6 +159,7 @@ static wchar_t * get_env(wchar_t * key) #define PYTHON_EXECUTABLE L"python.exe" +#endif #endif #define MAX_VERSION_SIZE 4 @@ -1457,6 +1478,87 @@ show_python_list(wchar_t ** argv) return FALSE; /* If this has been called we cannot continue */ } +#if defined(VENV_REDIRECT) + +static int +find_home_value(const char *buffer, const char **start, DWORD *length) +{ + for (const char *s = strstr(buffer, "home"); s; s = strstr(s + 1, "\nhome")) { + if (*s == '\n') { + ++s; + } + for (int i = 4; i > 0 && *s; --i, ++s); + + while (*s && iswspace(*s)) { + ++s; + } + if (*s != L'=') { + continue; + } + + do { + ++s; + } while (*s && iswspace(*s)); + + *start = s; + char *nl = strchr(s, '\n'); + if (nl) { + *length = (DWORD)((ptrdiff_t)nl - (ptrdiff_t)s); + } else { + *length = (DWORD)strlen(s); + } + return 1; + } + return 0; +} +#endif + +static wchar_t * +wcsdup_pad(const wchar_t *s, int padding, int *newlen) +{ + size_t len = wcslen(s); + len += 1 + padding; + wchar_t *r = (wchar_t *)malloc(len * sizeof(wchar_t)); + if (!r) { + return NULL; + } + if (wcscpy_s(r, len, s)) { + free(r); + return NULL; + } + *newlen = len < MAXINT ? (int)len : MAXINT; + return r; +} + +static wchar_t * +get_process_name() +{ + DWORD bufferLen = MAX_PATH; + DWORD len = bufferLen; + wchar_t *r = NULL; + + while (!r) { + r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); + if (!r) { + error(RC_NO_MEMORY, L"out of memory"); + return NULL; + } + len = GetModuleFileNameW(NULL, r, bufferLen); + if (len == 0) { + free(r); + error(0, L"Failed to get module name"); + return NULL; + } else if (len == bufferLen && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(r); + r = NULL; + bufferLen *= 2; + } + } + + return r; +} + static int process(int argc, wchar_t ** argv) { @@ -1464,21 +1566,27 @@ process(int argc, wchar_t ** argv) wchar_t * command; wchar_t * executable; wchar_t * p; + wchar_t * argv0; int rc = 0; - size_t plen; INSTALLED_PYTHON * ip; BOOL valid; DWORD size, attrs; - HRESULT hr; wchar_t message[MSGSIZE]; void * version_data; VS_FIXEDFILEINFO * file_info; UINT block_size; - int index; -#if defined(SCRIPT_WRAPPER) +#if defined(VENV_REDIRECT) + wchar_t * venv_cfg_path; int newlen; +#elif defined(SCRIPT_WRAPPER) wchar_t * newcommand; wchar_t * av[2]; + int newlen; + HRESULT hr; + int index; +#else + HRESULT hr; + int index; #endif setvbuf(stderr, (char *)NULL, _IONBF, 0); @@ -1496,6 +1604,7 @@ process(int argc, wchar_t ** argv) #else debug(L"launcher executable: Console\n"); #endif +#if !defined(VENV_REDIRECT) /* Get the local appdata folder (non-roaming) */ hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appdata_ini_path); @@ -1504,9 +1613,7 @@ process(int argc, wchar_t ** argv) appdata_ini_path[0] = L'\0'; } else { - plen = wcslen(appdata_ini_path); - p = &appdata_ini_path[plen]; - wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE); + wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE); attrs = GetFileAttributesW(appdata_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", appdata_ini_path); @@ -1515,8 +1622,9 @@ process(int argc, wchar_t ** argv) debug(L"Using local configuration file '%ls'\n", appdata_ini_path); } } - plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH); - size = GetFileVersionInfoSizeW(launcher_ini_path, &size); +#endif + argv0 = get_process_name(); + size = GetFileVersionInfoSizeW(argv0, &size); if (size == 0) { winerror(GetLastError(), message, MSGSIZE); debug(L"GetFileVersionInfoSize failed: %ls\n", message); @@ -1524,7 +1632,7 @@ process(int argc, wchar_t ** argv) else { version_data = malloc(size); if (version_data) { - valid = GetFileVersionInfoW(launcher_ini_path, 0, size, + valid = GetFileVersionInfoW(argv0, 0, size, version_data); if (!valid) debug(L"GetFileVersionInfo failed: %X\n", GetLastError()); @@ -1541,15 +1649,51 @@ process(int argc, wchar_t ** argv) free(version_data); } } + +#if defined(VENV_REDIRECT) + /* Allocate some extra space for new filenames */ + venv_cfg_path = wcsdup_pad(argv0, 32, &newlen); + if (!venv_cfg_path) { + error(RC_NO_MEMORY, L"Failed to copy module name"); + } + p = wcsrchr(venv_cfg_path, L'\\'); + + if (p == NULL) { + error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); + } + p[0] = L'\0'; + wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); + attrs = GetFileAttributesW(venv_cfg_path); + if (attrs == INVALID_FILE_ATTRIBUTES) { + debug(L"File '%ls' non-existent\n", venv_cfg_path); + p[0] = '\0'; + p = wcsrchr(venv_cfg_path, L'\\'); + if (p != NULL) { + p[0] = '\0'; + wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); + attrs = GetFileAttributesW(venv_cfg_path); + if (attrs == INVALID_FILE_ATTRIBUTES) { + debug(L"File '%ls' non-existent\n", venv_cfg_path); + error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); + } + } + } + debug(L"Using venv configuration file '%ls'\n", venv_cfg_path); +#else + /* Allocate some extra space for new filenames */ + if (wcscpy_s(launcher_ini_path, MAX_PATH, argv0)) { + error(RC_NO_MEMORY, L"Failed to copy module name"); + } p = wcsrchr(launcher_ini_path, L'\\'); + if (p == NULL) { debug(L"GetModuleFileNameW returned value has no backslash: %ls\n", launcher_ini_path); launcher_ini_path[0] = L'\0'; } else { - wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini", - _TRUNCATE); + p[0] = L'\0'; + wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini"); attrs = GetFileAttributesW(launcher_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", launcher_ini_path); @@ -1558,6 +1702,7 @@ process(int argc, wchar_t ** argv) debug(L"Using global configuration file '%ls'\n", launcher_ini_path); } } +#endif command = skip_me(GetCommandLineW()); debug(L"Called with command line: %ls\n", command); @@ -1593,6 +1738,52 @@ process(int argc, wchar_t ** argv) command = newcommand; valid = FALSE; } +#elif defined(VENV_REDIRECT) + { + FILE *f; + char buffer[4096]; /* 4KB should be enough for anybody */ + char *start; + DWORD len, cch, cch_actual; + size_t cb; + if (_wfopen_s(&f, venv_cfg_path, L"r")) { + error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path); + } + cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]), + sizeof(buffer) / sizeof(buffer[0]), f); + fclose(f); + + if (!find_home_value(buffer, &start, &len)) { + error(RC_BAD_VENV_CFG, L"Cannot find home in '%ls'", + venv_cfg_path); + } + + cch = MultiByteToWideChar(CP_UTF8, 0, start, len, NULL, 0); + if (!cch) { + error(0, L"Cannot determine memory for home path"); + } + cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 1 + 1; /* include sep and null */ + executable = (wchar_t *)malloc(cch * sizeof(wchar_t)); + cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, executable, cch); + if (!cch_actual) { + error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'", + venv_cfg_path); + } + if (executable[cch_actual - 1] != L'\\') { + executable[cch_actual++] = L'\\'; + executable[cch_actual] = L'\0'; + } + if (wcscat_s(executable, cch, PYTHON_EXECUTABLE)) { + error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'", + venv_cfg_path); + } + if (GetFileAttributesW(executable) == INVALID_FILE_ATTRIBUTES) { + error(RC_NO_PYTHON, L"No Python at '%ls'", executable); + } + if (!SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", argv0)) { + error(0, L"Failed to set launcher environment"); + } + valid = 1; + } #else if (argc <= 1) { valid = FALSE; @@ -1600,7 +1791,6 @@ process(int argc, wchar_t ** argv) } else { p = argv[1]; - plen = wcslen(p); if ((argc == 2) && // list version args (!wcsncmp(p, L"-0", wcslen(L"-0")) || !wcsncmp(p, L"--list", wcslen(L"--list")))) diff --git a/PC/layout/__init__.py b/PC/layout/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/PC/layout/__main__.py b/PC/layout/__main__.py new file mode 100644 index 000000000000..f7aa1e6d261f --- /dev/null +++ b/PC/layout/__main__.py @@ -0,0 +1,14 @@ +import sys + +try: + import layout +except ImportError: + # Failed to import our package, which likely means we were started directly + # Add the additional search path needed to locate our module. + from pathlib import Path + + sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +from layout.main import main + +sys.exit(int(main() or 0)) diff --git a/PC/layout/main.py b/PC/layout/main.py new file mode 100644 index 000000000000..82d0536ca920 --- /dev/null +++ b/PC/layout/main.py @@ -0,0 +1,612 @@ +""" +Generates a layout of Python for Windows from a build. + +See python make_layout.py --help for usage. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import argparse +import functools +import os +import re +import shutil +import subprocess +import sys +import tempfile +import zipfile + +from pathlib import Path + +if __name__ == "__main__": + # Started directly, so enable relative imports + __path__ = [str(Path(__file__).resolve().parent)] + +from .support.appxmanifest import * +from .support.catalog import * +from .support.constants import * +from .support.filesets import * +from .support.logging import * +from .support.options import * +from .support.pip import * +from .support.props import * + +BDIST_WININST_FILES_ONLY = FileNameSet("wininst-*", "bdist_wininst.py") +BDIST_WININST_STUB = "PC/layout/support/distutils.command.bdist_wininst.py" + +TEST_PYDS_ONLY = FileStemSet("xxlimited", "_ctypes_test", "_test*") +TEST_DIRS_ONLY = FileNameSet("test", "tests") + +IDLE_DIRS_ONLY = FileNameSet("idlelib") + +TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter") +TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo") +TCLTK_FILES_ONLY = FileNameSet("turtle.py") + +VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip") + +EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext") +EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle") +EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt") +EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*") +EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll") + +REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*") + +LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt") + +PY_FILES = FileSuffixSet(".py") +PYC_FILES = FileSuffixSet(".pyc") +CAT_FILES = FileSuffixSet(".cat") +CDF_FILES = FileSuffixSet(".cdf") + +DATA_DIRS = FileNameSet("data") + +TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser") +TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt") + + +def get_lib_layout(ns): + def _c(f): + if f in EXCLUDE_FROM_LIB: + return False + if f.is_dir(): + if f in TEST_DIRS_ONLY: + return ns.include_tests + if f in TCLTK_DIRS_ONLY: + return ns.include_tcltk + if f in IDLE_DIRS_ONLY: + return ns.include_idle + if f in VENV_DIRS_ONLY: + return ns.include_venv + else: + if f in TCLTK_FILES_ONLY: + return ns.include_tcltk + if f in BDIST_WININST_FILES_ONLY: + return ns.include_bdist_wininst + return True + + for dest, src in rglob(ns.source / "Lib", "**/*", _c): + yield dest, src + + if not ns.include_bdist_wininst: + src = ns.source / BDIST_WININST_STUB + yield Path("distutils/command/bdist_wininst.py"), src + + +def get_tcltk_lib(ns): + if not ns.include_tcltk: + return + + tcl_lib = os.getenv("TCL_LIBRARY") + if not tcl_lib or not os.path.isdir(tcl_lib): + try: + with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f: + tcl_lib = f.read().strip() + except FileNotFoundError: + pass + if not tcl_lib or not os.path.isdir(tcl_lib): + warn("Failed to find TCL_LIBRARY") + return + + for dest, src in rglob(Path(tcl_lib).parent, "**/*"): + yield "tcl/{}".format(dest), src + + +def get_layout(ns): + def in_build(f, dest="", new_name=None): + n, _, x = f.rpartition(".") + n = new_name or n + src = ns.build / f + if ns.debug and src not in REQUIRED_DLLS: + if not src.stem.endswith("_d"): + src = src.parent / (src.stem + "_d" + src.suffix) + if not n.endswith("_d"): + n += "_d" + f = n + "." + x + yield dest + n + "." + x, src + if ns.include_symbols: + pdb = src.with_suffix(".pdb") + if pdb.is_file(): + yield dest + n + ".pdb", pdb + if ns.include_dev: + lib = src.with_suffix(".lib") + if lib.is_file(): + yield "libs/" + n + ".lib", lib + + yield from in_build("python_uwp.exe", new_name="python") + yield from in_build("pythonw_uwp.exe", new_name="pythonw") + + yield from in_build(PYTHON_DLL_NAME) + + if ns.include_launchers: + if ns.include_pip: + yield from in_build("python_uwp.exe", new_name="pip") + if ns.include_idle: + yield from in_build("pythonw_uwp.exe", new_name="idle") + + if ns.include_stable: + yield from in_build(PYTHON_STABLE_DLL_NAME) + + for dest, src in rglob(ns.build, "vcruntime*.dll"): + yield dest, src + + for dest, src in rglob(ns.build, ("*.pyd", "*.dll")): + if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS: + continue + if src in EXCLUDE_FROM_PYDS: + continue + if src in TEST_PYDS_ONLY and not ns.include_tests: + continue + if src in TCLTK_PYDS_ONLY and not ns.include_tcltk: + continue + + yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/") + + if ns.zip_lib: + zip_name = PYTHON_ZIP_NAME + yield zip_name, ns.temp / zip_name + else: + for dest, src in get_lib_layout(ns): + yield "Lib/{}".format(dest), src + + if ns.include_venv: + yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python") + yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw") + + if ns.include_tools: + + def _c(d): + if d.is_dir(): + return d in TOOLS_DIRS + return d in TOOLS_FILES + + for dest, src in rglob(ns.source / "Tools", "**/*", _c): + yield "Tools/{}".format(dest), src + + if ns.include_underpth: + yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME + + if ns.include_dev: + + def _c(d): + if d.is_dir(): + return d.name != "internal" + return True + + for dest, src in rglob(ns.source / "Include", "**/*.h", _c): + yield "include/{}".format(dest), src + src = ns.source / "PC" / "pyconfig.h" + yield "include/pyconfig.h", src + + for dest, src in get_tcltk_lib(ns): + yield dest, src + + if ns.include_pip: + pip_dir = get_pip_dir(ns) + if not pip_dir.is_dir(): + log_warning("Failed to find {} - pip will not be included", pip_dir) + else: + pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}" + for dest, src in rglob(pip_dir, "**/*"): + if src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB: + continue + yield pkg_root.format(dest), src + + if ns.include_chm: + for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME): + yield "Doc/{}".format(dest), src + + if ns.include_html_doc: + for dest, src in rglob(ns.doc_build / "html", "**/*"): + yield "Doc/html/{}".format(dest), src + + if ns.include_props: + for dest, src in get_props_layout(ns): + yield dest, src + + for dest, src in get_appx_layout(ns): + yield dest, src + + if ns.include_cat: + if ns.flat_dlls: + yield ns.include_cat.name, ns.include_cat + else: + yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat + + +def _compile_one_py(src, dest, name, optimize): + import py_compile + + if dest is not None: + dest = str(dest) + + try: + return Path( + py_compile.compile( + str(src), + dest, + str(name), + doraise=True, + optimize=optimize, + invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, + ) + ) + except py_compile.PyCompileError: + log_warning("Failed to compile {}", src) + return None + + +def _py_temp_compile(src, ns, dest_dir=None): + 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") + return _compile_one_py(src, dest.with_suffix(".pyc"), dest, optimize=2) + + +def _write_to_zip(zf, dest, src, ns): + pyc = _py_temp_compile(src, ns) + if pyc: + try: + zf.write(str(pyc), dest.with_suffix(".pyc")) + finally: + try: + pyc.unlink() + except: + log_exception("Failed to delete {}", pyc) + return + + if src in LIB2TO3_GRAMMAR_FILES: + from lib2to3.pgen2.driver import load_grammar + + tmp = ns.temp / src.name + try: + shutil.copy(src, tmp) + load_grammar(str(tmp)) + for f in ns.temp.glob(src.stem + "*.pickle"): + zf.write(str(f), str(dest.parent / f.name)) + try: + f.unlink() + except: + log_exception("Failed to delete {}", f) + except: + log_exception("Failed to compile {}", src) + finally: + try: + tmp.unlink() + except: + log_exception("Failed to delete {}", tmp) + + zf.write(str(src), str(dest)) + + +def generate_source_files(ns): + if ns.zip_lib: + zip_name = PYTHON_ZIP_NAME + zip_path = ns.temp / zip_name + if zip_path.is_file(): + zip_path.unlink() + elif zip_path.is_dir(): + log_error( + "Cannot create zip file because a directory exists by the same name" + ) + return + log_info("Generating {} in {}", zip_name, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: + for dest, src in get_lib_layout(ns): + _write_to_zip(zf, dest, src, ns) + + if ns.include_underpth: + log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f: + if ns.zip_lib: + print(PYTHON_ZIP_NAME, file=f) + if ns.include_pip: + print("packages", file=f) + else: + print("Lib", file=f) + print("Lib/site-packages", file=f) + if not ns.flat_dlls: + print("DLLs", file=f) + print(".", file=f) + print(file=f) + print("# Uncomment to run site.main() automatically", file=f) + print("#import site", file=f) + + if ns.include_appxmanifest: + log_info("Generating AppxManifest.xml in {}", ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + + with open(ns.temp / "AppxManifest.xml", "wb") as f: + f.write(get_appxmanifest(ns)) + + with open(ns.temp / "_resources.xml", "wb") as f: + f.write(get_resources_xml(ns)) + + if ns.include_pip: + pip_dir = get_pip_dir(ns) + if not (pip_dir / "pip").is_dir(): + log_info("Extracting pip to {}", pip_dir) + pip_dir.mkdir(parents=True, exist_ok=True) + extract_pip_files(ns) + + if ns.include_props: + log_info("Generating {} in {}", PYTHON_PROPS_NAME, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / PYTHON_PROPS_NAME, "wb") as f: + f.write(get_props(ns)) + + +def _create_zip_file(ns): + if not ns.zip: + return None + + if ns.zip.is_file(): + try: + ns.zip.unlink() + except OSError: + log_exception("Unable to remove {}", ns.zip) + sys.exit(8) + elif ns.zip.is_dir(): + log_error("Cannot create ZIP file because {} is a directory", ns.zip) + sys.exit(8) + + ns.zip.parent.mkdir(parents=True, exist_ok=True) + return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED) + + +def copy_files(files, ns): + if ns.copy: + ns.copy.mkdir(parents=True, exist_ok=True) + + try: + total = len(files) + except TypeError: + total = None + count = 0 + + zip_file = _create_zip_file(ns) + try: + need_compile = [] + in_catalog = [] + + for dest, src in files: + count += 1 + if count % 10 == 0: + if total: + log_info("Processed {:>4} of {} files", count, total) + else: + log_info("Processed {} files", count) + log_debug("Processing {!s}", src) + + if ( + ns.precompile + and src in PY_FILES + and src not in EXCLUDE_FROM_COMPILE + and src.parent not in DATA_DIRS + and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib")) + ): + if ns.copy: + need_compile.append((dest, ns.copy / dest)) + else: + (ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(src, ns.temp / "Lib" / dest) + need_compile.append((dest, ns.temp / "Lib" / dest)) + + if src not in EXCLUDE_FROM_CATALOG: + in_catalog.append((src.name, src)) + + if ns.copy: + log_debug("Copy {} -> {}", src, ns.copy / dest) + (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True) + try: + shutil.copy2(src, ns.copy / dest) + except shutil.SameFileError: + pass + + if ns.zip: + log_debug("Zip {} into {}", src, ns.zip) + zip_file.write(src, str(dest)) + + if need_compile: + for dest, src in need_compile: + compiled = [ + _compile_one_py(src, None, dest, optimize=0), + _compile_one_py(src, None, dest, optimize=1), + _compile_one_py(src, None, dest, optimize=2), + ] + for c in compiled: + if not c: + continue + cdest = Path(dest).parent / Path(c).relative_to(src.parent) + if ns.zip: + log_debug("Zip {} into {}", c, ns.zip) + zip_file.write(c, str(cdest)) + in_catalog.append((cdest.name, cdest)) + + if ns.catalog: + # Just write out the CDF now. Compilation and signing is + # an extra step + log_info("Generating {}", ns.catalog) + ns.catalog.parent.mkdir(parents=True, exist_ok=True) + write_catalog(ns.catalog, in_catalog) + + finally: + if zip_file: + zip_file.close() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-v", help="Increase verbosity", action="count") + parser.add_argument( + "-s", + "--source", + metavar="dir", + help="The directory containing the repository root", + type=Path, + default=None, + ) + parser.add_argument( + "-b", "--build", metavar="dir", help="Specify the build directory", type=Path + ) + parser.add_argument( + "--doc-build", + metavar="dir", + help="Specify the docs build directory", + type=Path, + default=None, + ) + parser.add_argument( + "--copy", + metavar="directory", + help="The name of the directory to copy an extracted layout to", + type=Path, + default=None, + ) + parser.add_argument( + "--zip", + metavar="file", + help="The ZIP file to write all files to", + type=Path, + default=None, + ) + parser.add_argument( + "--catalog", + metavar="file", + help="The CDF file to write catalog entries to", + type=Path, + default=None, + ) + parser.add_argument( + "--log", + metavar="file", + help="Write all operations to the specified file", + type=Path, + default=None, + ) + parser.add_argument( + "-t", + "--temp", + metavar="file", + help="A temporary working directory", + type=Path, + default=None, + ) + parser.add_argument( + "-d", "--debug", help="Include debug build", action="store_true" + ) + parser.add_argument( + "-p", + "--precompile", + help="Include .pyc files instead of .py", + action="store_true", + ) + parser.add_argument( + "-z", "--zip-lib", help="Include library in a ZIP file", action="store_true" + ) + parser.add_argument( + "--flat-dlls", help="Does not create a DLLs directory", action="store_true" + ) + parser.add_argument( + "-a", + "--include-all", + help="Include all optional components", + action="store_true", + ) + parser.add_argument( + "--include-cat", + metavar="file", + help="Specify the catalog file to include", + type=Path, + default=None, + ) + for opt, help in get_argparse_options(): + parser.add_argument(opt, help=help, action="store_true") + + ns = parser.parse_args() + update_presets(ns) + + ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent) + ns.build = ns.build or Path(sys.executable).parent + ns.temp = ns.temp or Path(tempfile.mkdtemp()) + ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build") + if not ns.source.is_absolute(): + ns.source = (Path.cwd() / ns.source).resolve() + if not ns.build.is_absolute(): + ns.build = (Path.cwd() / ns.build).resolve() + if not ns.temp.is_absolute(): + ns.temp = (Path.cwd() / ns.temp).resolve() + if not ns.doc_build.is_absolute(): + ns.doc_build = (Path.cwd() / ns.doc_build).resolve() + if ns.include_cat and not ns.include_cat.is_absolute(): + ns.include_cat = (Path.cwd() / ns.include_cat).resolve() + + if ns.copy and not ns.copy.is_absolute(): + ns.copy = (Path.cwd() / ns.copy).resolve() + if ns.zip and not ns.zip.is_absolute(): + ns.zip = (Path.cwd() / ns.zip).resolve() + if ns.catalog and not ns.catalog.is_absolute(): + ns.catalog = (Path.cwd() / ns.catalog).resolve() + + configure_logger(ns) + + log_info( + """OPTIONS +Source: {ns.source} +Build: {ns.build} +Temp: {ns.temp} + +Copy to: {ns.copy} +Zip to: {ns.zip} +Catalog: {ns.catalog}""", + ns=ns, + ) + + if ns.include_idle and not ns.include_tcltk: + log_warning("Assuming --include-tcltk to support --include-idle") + ns.include_tcltk = True + + try: + generate_source_files(ns) + files = list(get_layout(ns)) + copy_files(files, ns) + except KeyboardInterrupt: + log_info("Interrupted by Ctrl+C") + return 3 + except SystemExit: + raise + except: + log_exception("Unhandled error") + + if error_was_logged(): + log_error("Errors occurred.") + return 1 + + +if __name__ == "__main__": + sys.exit(int(main() or 0)) diff --git a/PC/layout/support/__init__.py b/PC/layout/support/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py new file mode 100644 index 000000000000..c5dda70c7ef8 --- /dev/null +++ b/PC/layout/support/appxmanifest.py @@ -0,0 +1,487 @@ +""" +File generation for APPX/MSIX manifests. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import collections +import ctypes +import io +import os +import sys + +from pathlib import Path, PureWindowsPath +from xml.etree import ElementTree as ET + +from .constants import * + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + +APPX_DATA = dict( + Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT), + Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3), + Publisher=os.getenv( + "APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B" + ), + DisplayName="Python {}".format(VER_DOT), + Description="The Python {} runtime and console.".format(VER_DOT), + ProcessorArchitecture="x64" if IS_X64 else "x86", +) + +PYTHON_VE_DATA = dict( + DisplayName="Python {}".format(VER_DOT), + Description="Python interactive console", + Square150x150Logo="_resources/pythonx150.png", + Square44x44Logo="_resources/pythonx44.png", + BackgroundColor="transparent", +) + +PYTHONW_VE_DATA = dict( + DisplayName="Python {} (Windowed)".format(VER_DOT), + Description="Python windowed app launcher", + Square150x150Logo="_resources/pythonwx150.png", + Square44x44Logo="_resources/pythonwx44.png", + BackgroundColor="transparent", + AppListEntry="none", +) + +PIP_VE_DATA = dict( + DisplayName="pip (Python {})".format(VER_DOT), + Description="pip package manager for Python {}".format(VER_DOT), + Square150x150Logo="_resources/pythonx150.png", + Square44x44Logo="_resources/pythonx44.png", + BackgroundColor="transparent", + AppListEntry="none", +) + +IDLE_VE_DATA = dict( + DisplayName="IDLE (Python {})".format(VER_DOT), + Description="IDLE editor for Python {}".format(VER_DOT), + Square150x150Logo="_resources/pythonwx150.png", + Square44x44Logo="_resources/pythonwx44.png", + BackgroundColor="transparent", +) + +APPXMANIFEST_NS = { + "": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", + "m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", + "uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10", + "rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities", + "rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4", + "desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4", + "desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6", + "uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3", + "uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4", + "uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5", +} + +APPXMANIFEST_TEMPLATE = """ + + + + + Python Software Foundation + + _resources/pythonx50.png + + + + + + + + + + + + + + +""" + + +RESOURCES_XML_TEMPLATE = r""" + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + + +SCCD_FILENAME = "PC/classicAppCompat.sccd" + +REGISTRY = { + "HKCU\\Software\\Python\\PythonCore": { + VER_DOT: { + "DisplayName": APPX_DATA["DisplayName"], + "SupportUrl": "https://www.python.org/", + "SysArchitecture": "64bit" if IS_X64 else "32bit", + "SysVersion": VER_DOT, + "Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO), + "InstallPath": { + # I have no idea why the trailing spaces are needed, but they seem to be needed. + "": "[{AppVPackageRoot}][ ]", + "ExecutablePath": "[{AppVPackageRoot}]python.exe[ ]", + "WindowedExecutablePath": "[{AppVPackageRoot}]pythonw.exe[ ]", + }, + "Help": { + "Main Python Documentation": { + "_condition": lambda ns: ns.include_chm, + "": "[{{AppVPackageRoot}}]Doc\\{}[ ]".format( + PYTHON_CHM_NAME + ), + }, + "Local Python Documentation": { + "_condition": lambda ns: ns.include_html_doc, + "": "[{AppVPackageRoot}]Doc\\html\\index.html[ ]", + }, + "Online Python Documentation": { + "": "https://docs.python.org/{}".format(VER_DOT) + }, + }, + "Idle": { + "_condition": lambda ns: ns.include_idle, + "": "[{AppVPackageRoot}]Lib\\idlelib\\idle.pyw[ ]", + }, + } + } +} + + +def get_packagefamilyname(name, publisher_id): + class PACKAGE_ID(ctypes.Structure): + _fields_ = [ + ("reserved", ctypes.c_uint32), + ("processorArchitecture", ctypes.c_uint32), + ("version", ctypes.c_uint64), + ("name", ctypes.c_wchar_p), + ("publisher", ctypes.c_wchar_p), + ("resourceId", ctypes.c_wchar_p), + ("publisherId", ctypes.c_wchar_p), + ] + _pack_ = 4 + + pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None) + result = ctypes.create_unicode_buffer(256) + result_len = ctypes.c_uint32(256) + r = ctypes.windll.kernel32.PackageFamilyNameFromId( + pid, ctypes.byref(result_len), result + ) + if r: + raise OSError(r, "failed to get package family name") + return result.value[: result_len.value] + + +def _fixup_sccd(ns, sccd, new_hash=None): + if not new_hash: + return sccd + + NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd") + with open(sccd, "rb") as f: + xml = ET.parse(f) + + pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"]) + + ae = xml.find("s:AuthorizedEntities", NS) + ae.clear() + + e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity")) + e.set("AppPackageFamilyName", pfn) + e.set("CertificateSignatureHash", new_hash) + + for e in xml.findall("s:Catalog", NS): + e.text = "FFFF" + + sccd = ns.temp / sccd.name + sccd.parent.mkdir(parents=True, exist_ok=True) + with open(sccd, "wb") as f: + xml.write(f, encoding="utf-8") + + return sccd + + + at public +def get_appx_layout(ns): + if not ns.include_appxmanifest: + return + + yield "AppxManifest.xml", ns.temp / "AppxManifest.xml" + yield "_resources.xml", ns.temp / "_resources.xml" + icons = ns.source / "PC" / "icons" + yield "_resources/pythonx44.png", icons / "pythonx44.png" + yield "_resources/pythonx44$targetsize-44_altform-unplated.png", icons / "pythonx44.png" + yield "_resources/pythonx50.png", icons / "pythonx50.png" + yield "_resources/pythonx50$targetsize-50_altform-unplated.png", icons / "pythonx50.png" + yield "_resources/pythonx150.png", icons / "pythonx150.png" + yield "_resources/pythonx150$targetsize-150_altform-unplated.png", icons / "pythonx150.png" + yield "_resources/pythonwx44.png", icons / "pythonwx44.png" + yield "_resources/pythonwx44$targetsize-44_altform-unplated.png", icons / "pythonwx44.png" + yield "_resources/pythonwx150.png", icons / "pythonwx150.png" + yield "_resources/pythonwx150$targetsize-150_altform-unplated.png", icons / "pythonwx150.png" + sccd = ns.source / SCCD_FILENAME + if sccd.is_file(): + # This should only be set for side-loading purposes. + sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256")) + yield sccd.name, sccd + + +def find_or_add(xml, element, attr=None, always_add=False): + if always_add: + e = None + else: + q = element + if attr: + q += "[@{}='{}']".format(*attr) + e = xml.find(q, APPXMANIFEST_NS) + if e is None: + prefix, _, name = element.partition(":") + name = ET.QName(APPXMANIFEST_NS[prefix or ""], name) + e = ET.SubElement(xml, name) + if attr: + e.set(*attr) + return e + + +def _get_app(xml, appid): + if appid: + app = xml.find( + "m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS + ) + if app is None: + raise LookupError(appid) + else: + app = xml + return app + + +def add_visual(xml, appid, data): + app = _get_app(xml, appid) + e = find_or_add(app, "uap:VisualElements") + for i in data.items(): + e.set(*i) + return e + + +def add_alias(xml, appid, alias, subsystem="windows"): + app = _get_app(xml, appid) + e = find_or_add(app, "m:Extensions") + e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias")) + e = find_or_add(e, "uap5:AppExecutionAlias") + e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem) + e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias)) + + +def add_file_type(xml, appid, name, suffix, parameters='"%1"'): + app = _get_app(xml, appid) + e = find_or_add(app, "m:Extensions") + e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation")) + e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name)) + e.set("Parameters", parameters) + e = find_or_add(e, "uap:SupportedFileTypes") + if isinstance(suffix, str): + suffix = [suffix] + for s in suffix: + ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s + + +def add_application( + ns, xml, appid, executable, aliases, visual_element, subsystem, file_types +): + node = xml.find("m:Applications", APPXMANIFEST_NS) + suffix = "_d.exe" if ns.debug else ".exe" + app = ET.SubElement( + node, + ET.QName(APPXMANIFEST_NS[""], "Application"), + { + "Id": appid, + "Executable": executable + suffix, + "EntryPoint": "Windows.FullTrustApplication", + ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true", + }, + ) + if visual_element: + add_visual(app, None, visual_element) + for alias in aliases: + add_alias(app, None, alias + suffix, subsystem) + if file_types: + add_file_type(app, None, *file_types) + return app + + +def _get_registry_entries(ns, root="", d=None): + r = root if root else PureWindowsPath("") + if d is None: + d = REGISTRY + for key, value in d.items(): + if key == "_condition": + continue + elif isinstance(value, dict): + cond = value.get("_condition") + if cond and not cond(ns): + continue + fullkey = r + for part in PureWindowsPath(key).parts: + fullkey /= part + if len(fullkey.parts) > 1: + yield str(fullkey), None, None + yield from _get_registry_entries(ns, fullkey, value) + elif len(r.parts) > 1: + yield str(r), key, value + + +def add_registry_entries(ns, xml): + e = find_or_add(xml, "m:Extensions") + e = find_or_add(e, "rescap4:Extension") + e.set("Category", "windows.classicAppCompatKeys") + e.set("EntryPoint", "Windows.FullTrustApplication") + e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys")) + for name, valuename, value in _get_registry_entries(ns): + k = ET.SubElement( + e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey") + ) + k.set("Name", name) + if value: + k.set("ValueName", valuename) + k.set("Value", value) + k.set("ValueType", "REG_SZ") + + +def disable_registry_virtualization(xml): + e = find_or_add(xml, "m:Properties") + e = find_or_add(e, "desktop6:RegistryWriteVirtualization") + e.text = "disabled" + e = find_or_add(xml, "m:Capabilities") + e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources")) + + + at public +def get_appxmanifest(ns): + for k, v in APPXMANIFEST_NS.items(): + ET.register_namespace(k, v) + ET.register_namespace("", APPXMANIFEST_NS["m"]) + + xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE)) + NS = APPXMANIFEST_NS + QN = ET.QName + + node = xml.find("m:Identity", NS) + for k in node.keys(): + value = APPX_DATA.get(k) + if value: + node.set(k, value) + + for node in xml.find("m:Properties", NS): + value = APPX_DATA.get(node.tag.rpartition("}")[2]) + if value: + node.text = value + + winver = sys.getwindowsversion()[:3] + if winver < (10, 0, 17763): + winver = 10, 0, 17763 + find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set( + "MaxVersionTested", "{}.{}.{}.0".format(*winver) + ) + + if winver > (10, 0, 17763): + disable_registry_virtualization(xml) + + app = add_application( + ns, + xml, + "Python", + "python", + ["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)], + PYTHON_VE_DATA, + "console", + ("python.file", [".py"]), + ) + + add_application( + ns, + xml, + "PythonW", + "pythonw", + ["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)], + PYTHONW_VE_DATA, + "windows", + ("python.windowedfile", [".pyw"]), + ) + + if ns.include_pip and ns.include_launchers: + add_application( + ns, + xml, + "Pip", + "pip", + ["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)], + PIP_VE_DATA, + "console", + ("python.wheel", [".whl"], 'install "%1"'), + ) + + if ns.include_idle and ns.include_launchers: + add_application( + ns, + xml, + "Idle", + "idle", + ["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)], + IDLE_VE_DATA, + "windows", + None, + ) + + if (ns.source / SCCD_FILENAME).is_file(): + add_registry_entries(ns, xml) + node = xml.find("m:Capabilities", NS) + node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability")) + node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe") + + buffer = io.BytesIO() + xml.write(buffer, encoding="utf-8", xml_declaration=True) + return buffer.getbuffer() + + + at public +def get_resources_xml(ns): + return RESOURCES_XML_TEMPLATE.encode("utf-8") diff --git a/PC/layout/support/catalog.py b/PC/layout/support/catalog.py new file mode 100644 index 000000000000..43121187ed18 --- /dev/null +++ b/PC/layout/support/catalog.py @@ -0,0 +1,44 @@ +""" +File generation for catalog signing non-binary contents. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import sys + +__all__ = ["PYTHON_CAT_NAME", "PYTHON_CDF_NAME"] + + +def public(f): + __all__.append(f.__name__) + return f + + +PYTHON_CAT_NAME = "python.cat" +PYTHON_CDF_NAME = "python.cdf" + + +CATALOG_TEMPLATE = r"""[CatalogHeader] +Name={target.stem}.cat +ResultDir={target.parent} +PublicVersion=1 +CatalogVersion=2 +HashAlgorithms=SHA256 +PageHashes=false +EncodingType= + +[CatalogFiles] +""" + + +def can_sign(file): + return file.is_file() and file.stat().st_size + + + at public +def write_catalog(target, files): + with target.open("w", encoding="utf-8") as cat: + cat.write(CATALOG_TEMPLATE.format(target=target)) + cat.writelines("{}={}\n".format(n, f) for n, f in files if can_sign(f)) diff --git a/PC/layout/support/constants.py b/PC/layout/support/constants.py new file mode 100644 index 000000000000..88ea410b340e --- /dev/null +++ b/PC/layout/support/constants.py @@ -0,0 +1,28 @@ +""" +Constants for generating the layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import struct +import sys + +VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = struct.pack(">i", sys.hexversion) +VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4 +VER_NAME = {"alpha": "a", "beta": "b", "rc": "rc"}.get( + sys.version_info.releaselevel, "" +) +VER_SERIAL = sys.version_info.serial if VER_NAME else "" +VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR) + +PYTHON_DLL_NAME = "python{}{}.dll".format(VER_MAJOR, VER_MINOR) +PYTHON_STABLE_DLL_NAME = "python{}.dll".format(VER_MAJOR) +PYTHON_ZIP_NAME = "python{}{}.zip".format(VER_MAJOR, VER_MINOR) +PYTHON_PTH_NAME = "python{}{}._pth".format(VER_MAJOR, VER_MINOR) + +PYTHON_CHM_NAME = "python{}{}{}{}{}.chm".format( + VER_MAJOR, VER_MINOR, VER_MICRO, VER_NAME, VER_SERIAL +) + +IS_X64 = sys.maxsize > 2 ** 32 diff --git a/PC/layout/support/distutils.command.bdist_wininst.py b/PC/layout/support/distutils.command.bdist_wininst.py new file mode 100644 index 000000000000..6e9b49fe42df --- /dev/null +++ b/PC/layout/support/distutils.command.bdist_wininst.py @@ -0,0 +1,25 @@ +"""distutils.command.bdist_wininst + +Suppress the 'bdist_wininst' command, while still allowing +setuptools to import it without breaking.""" + +from distutils.core import Command +from distutils.errors import DistutilsPlatformError + + +class bdist_wininst(Command): + description = "create an executable installer for MS Windows" + + # Marker for tests that we have the unsupported bdist_wininst + _unsupported = True + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + raise DistutilsPlatformError( + "bdist_wininst is not supported in this Python distribution" + ) diff --git a/PC/layout/support/filesets.py b/PC/layout/support/filesets.py new file mode 100644 index 000000000000..47f727c05784 --- /dev/null +++ b/PC/layout/support/filesets.py @@ -0,0 +1,100 @@ +""" +File sets and globbing helper for make_layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import os + + +class FileStemSet: + def __init__(self, *patterns): + self._names = set() + self._prefixes = [] + self._suffixes = [] + for p in map(os.path.normcase, patterns): + if p.endswith("*"): + self._prefixes.append(p[:-1]) + elif p.startswith("*"): + self._suffixes.append(p[1:]) + else: + self._names.add(p) + + def _make_name(self, f): + return os.path.normcase(f.stem) + + def __contains__(self, f): + bn = self._make_name(f) + return ( + bn in self._names + or any(map(bn.startswith, self._prefixes)) + or any(map(bn.endswith, self._suffixes)) + ) + + +class FileNameSet(FileStemSet): + def _make_name(self, f): + return os.path.normcase(f.name) + + +class FileSuffixSet: + def __init__(self, *patterns): + self._names = set() + self._prefixes = [] + self._suffixes = [] + for p in map(os.path.normcase, patterns): + if p.startswith("*."): + self._names.add(p[1:]) + elif p.startswith("*"): + self._suffixes.append(p[1:]) + elif p.endswith("*"): + self._prefixes.append(p[:-1]) + elif p.startswith("."): + self._names.add(p) + else: + self._names.add("." + p) + + def _make_name(self, f): + return os.path.normcase(f.suffix) + + def __contains__(self, f): + bn = self._make_name(f) + return ( + bn in self._names + or any(map(bn.startswith, self._prefixes)) + or any(map(bn.endswith, self._suffixes)) + ) + + +def _rglob(root, pattern, condition): + dirs = [root] + recurse = pattern[:3] in {"**/", "**\\"} + if recurse: + pattern = pattern[3:] + + while dirs: + d = dirs.pop(0) + if recurse: + dirs.extend( + filter( + condition, (type(root)(f2) for f2 in os.scandir(d) if f2.is_dir()) + ) + ) + yield from ( + (f.relative_to(root), f) + for f in d.glob(pattern) + if f.is_file() and condition(f) + ) + + +def _return_true(f): + return True + + +def rglob(root, patterns, condition=None): + if isinstance(patterns, tuple): + for p in patterns: + yield from _rglob(root, p, condition or _return_true) + else: + yield from _rglob(root, patterns, condition or _return_true) diff --git a/PC/layout/support/logging.py b/PC/layout/support/logging.py new file mode 100644 index 000000000000..30869b949a1c --- /dev/null +++ b/PC/layout/support/logging.py @@ -0,0 +1,93 @@ +""" +Logging support for make_layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import logging +import sys + +__all__ = [] + +LOG = None +HAS_ERROR = False + + +def public(f): + __all__.append(f.__name__) + return f + + + at public +def configure_logger(ns): + global LOG + if LOG: + return + + LOG = logging.getLogger("make_layout") + LOG.level = logging.DEBUG + + if ns.v: + s_level = max(logging.ERROR - ns.v * 10, logging.DEBUG) + f_level = max(logging.WARNING - ns.v * 10, logging.DEBUG) + else: + s_level = logging.ERROR + f_level = logging.INFO + + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(logging.Formatter("{levelname:8s} {message}", style="{")) + handler.setLevel(s_level) + LOG.addHandler(handler) + + if ns.log: + handler = logging.FileHandler(ns.log, encoding="utf-8", delay=True) + handler.setFormatter( + logging.Formatter("[{asctime}]{levelname:8s}: {message}", style="{") + ) + handler.setLevel(f_level) + LOG.addHandler(handler) + + +class BraceMessage: + def __init__(self, fmt, *args, **kwargs): + self.fmt = fmt + self.args = args + self.kwargs = kwargs + + def __str__(self): + return self.fmt.format(*self.args, **self.kwargs) + + + at public +def log_debug(msg, *args, **kwargs): + return LOG.debug(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_info(msg, *args, **kwargs): + return LOG.info(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_warning(msg, *args, **kwargs): + return LOG.warning(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_error(msg, *args, **kwargs): + global HAS_ERROR + HAS_ERROR = True + return LOG.error(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_exception(msg, *args, **kwargs): + global HAS_ERROR + HAS_ERROR = True + return LOG.exception(BraceMessage(msg, *args, **kwargs)) + + + at public +def error_was_logged(): + return HAS_ERROR diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py new file mode 100644 index 000000000000..76d9e34e1f46 --- /dev/null +++ b/PC/layout/support/options.py @@ -0,0 +1,122 @@ +""" +List of optional components. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + +OPTIONS = { + "stable": {"help": "stable ABI stub"}, + "pip": {"help": "pip"}, + "distutils": {"help": "distutils"}, + "tcltk": {"help": "Tcl, Tk and tkinter"}, + "idle": {"help": "Idle"}, + "tests": {"help": "test suite"}, + "tools": {"help": "tools"}, + "venv": {"help": "venv"}, + "dev": {"help": "headers and libs"}, + "symbols": {"help": "symbols"}, + "bdist-wininst": {"help": "bdist_wininst support"}, + "underpth": {"help": "a python._pth file", "not-in-all": True}, + "launchers": {"help": "specific launchers"}, + "appxmanifest": {"help": "an appxmanifest"}, + "props": {"help": "a python.props file"}, + "chm": {"help": "the CHM documentation"}, + "html-doc": {"help": "the HTML documentation"}, +} + + +PRESETS = { + "appx": { + "help": "APPX package", + "options": [ + "stable", + "pip", + "distutils", + "tcltk", + "idle", + "venv", + "dev", + "launchers", + "appxmanifest", + # XXX: Disabled for now "precompile", + ], + }, + "nuget": { + "help": "nuget package", + "options": ["stable", "pip", "distutils", "dev", "props"], + }, + "default": { + "help": "development kit package", + "options": [ + "stable", + "pip", + "distutils", + "tcltk", + "idle", + "tests", + "tools", + "venv", + "dev", + "symbols", + "bdist-wininst", + "chm", + ], + }, + "embed": { + "help": "embeddable package", + "options": ["stable", "zip-lib", "flat-dlls", "underpth", "precompile"], + }, +} + + + at public +def get_argparse_options(): + for opt, info in OPTIONS.items(): + help = "When specified, includes {}".format(info["help"]) + if info.get("not-in-all"): + help = "{}. Not affected by --include-all".format(help) + + yield "--include-{}".format(opt), help + + for opt, info in PRESETS.items(): + help = "When specified, includes default options for {}".format(info["help"]) + yield "--preset-{}".format(opt), help + + +def ns_get(ns, key, default=False): + return getattr(ns, key.replace("-", "_"), default) + + +def ns_set(ns, key, value=True): + k1 = key.replace("-", "_") + k2 = "include_{}".format(k1) + if hasattr(ns, k2): + setattr(ns, k2, value) + elif hasattr(ns, k1): + setattr(ns, k1, value) + else: + raise AttributeError("no argument named '{}'".format(k1)) + + + at public +def update_presets(ns): + for preset, info in PRESETS.items(): + if ns_get(ns, "preset-{}".format(preset)): + for opt in info["options"]: + ns_set(ns, opt) + + if ns.include_all: + for opt in OPTIONS: + if OPTIONS[opt].get("not-in-all"): + continue + ns_set(ns, opt) diff --git a/PC/layout/support/pip.py b/PC/layout/support/pip.py new file mode 100644 index 000000000000..369a923ce139 --- /dev/null +++ b/PC/layout/support/pip.py @@ -0,0 +1,79 @@ +""" +Extraction and file list generation for pip. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import os +import shutil +import subprocess +import sys + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + + at public +def get_pip_dir(ns): + if ns.copy: + if ns.zip_lib: + return ns.copy / "packages" + return ns.copy / "Lib" / "site-packages" + else: + return ns.temp / "packages" + + + at public +def extract_pip_files(ns): + dest = get_pip_dir(ns) + dest.mkdir(parents=True, exist_ok=True) + + src = ns.source / "Lib" / "ensurepip" / "_bundled" + + ns.temp.mkdir(parents=True, exist_ok=True) + wheels = [shutil.copy(whl, ns.temp) for whl in src.glob("*.whl")] + search_path = os.pathsep.join(wheels) + if os.environ.get("PYTHONPATH"): + search_path += ";" + os.environ["PYTHONPATH"] + + env = os.environ.copy() + env["PYTHONPATH"] = search_path + + output = subprocess.check_output( + [ + sys.executable, + "-m", + "pip", + "--no-color", + "install", + "pip", + "setuptools", + "--upgrade", + "--target", + str(dest), + "--no-index", + "--no-cache-dir", + "-f", + str(src), + "--only-binary", + ":all:", + ], + env=env, + ) + + try: + shutil.rmtree(dest / "bin") + except OSError: + pass + + for file in wheels: + try: + os.remove(file) + except OSError: + pass diff --git a/PC/layout/support/props.py b/PC/layout/support/props.py new file mode 100644 index 000000000000..3a047d215058 --- /dev/null +++ b/PC/layout/support/props.py @@ -0,0 +1,110 @@ +""" +Provides .props file. +""" + +import os + +from .constants import * + +__all__ = ["PYTHON_PROPS_NAME"] + + +def public(f): + __all__.append(f.__name__) + return f + + +PYTHON_PROPS_NAME = "python.props" + +PROPS_DATA = { + "PYTHON_TAG": VER_DOT, + "PYTHON_VERSION": os.getenv("PYTHON_NUSPEC_VERSION"), + "PYTHON_PLATFORM": os.getenv("PYTHON_PROPS_PLATFORM"), + "PYTHON_TARGET": "", +} + +if not PROPS_DATA["PYTHON_VERSION"]: + if VER_NAME: + PROPS_DATA["PYTHON_VERSION"] = "{}.{}-{}{}".format( + VER_DOT, VER_MICRO, VER_NAME, VER_SERIAL + ) + else: + PROPS_DATA["PYTHON_VERSION"] = "{}.{}".format(VER_DOT, VER_MICRO) + +if not PROPS_DATA["PYTHON_PLATFORM"]: + PROPS_DATA["PYTHON_PLATFORM"] = "x64" if IS_X64 else "Win32" + +PROPS_DATA["PYTHON_TARGET"] = "_GetPythonRuntimeFilesDependsOn{}{}_{}".format( + VER_MAJOR, VER_MINOR, PROPS_DATA["PYTHON_PLATFORM"] +) + +PROPS_TEMPLATE = r""" + + + $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python_d.exe") + $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python.exe") + $(PythonHome)\include + $(PythonHome)\libs + {PYTHON_TAG} + {PYTHON_VERSION} + + true + false + false + false + + {PYTHON_TARGET};$(GetPythonRuntimeFilesDependsOn) + + + + + $(PythonInclude);%(AdditionalIncludeDirectories) + MultiThreadedDLL + + + $(PythonLibs);%(AdditionalLibraryDirectories) + + + + + + + + <_PythonRuntimeExe Include="$(PythonHome)\python*.dll" /> + <_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" /> + <_PythonRuntimeExe> + %(Filename)%(Extension) + + <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" /> + <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" /> + <_PythonRuntimeDlls> + DLLs\%(Filename)%(Extension) + + <_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" /> + <_PythonRuntimeLib> + Lib\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + +""" + + + at public +def get_props_layout(ns): + if ns.include_all or ns.include_props: + yield "python.props", ns.temp / "python.props" + + + at public +def get_props(ns): + # TODO: Filter contents of props file according to included/excluded items + props = PROPS_TEMPLATE.format_map(PROPS_DATA) + return props.encode("utf-8") diff --git a/Tools/nuget/python.props b/PC/layout/support/python.props similarity index 100% rename from Tools/nuget/python.props rename to PC/layout/support/python.props diff --git a/PC/pylauncher.rc b/PC/pylauncher.rc index 3da3445f5fc4..92987af7138d 100644 --- a/PC/pylauncher.rc +++ b/PC/pylauncher.rc @@ -7,6 +7,11 @@ #include 1 RT_MANIFEST "python.manifest" +#if defined(PY_ICON) +1 ICON DISCARDABLE "icons\python.ico" +#elif defined(PYW_ICON) +1 ICON DISCARDABLE "icons\pythonw.ico" +#else 1 ICON DISCARDABLE "icons\launcher.ico" 2 ICON DISCARDABLE "icons\py.ico" 3 ICON DISCARDABLE "icons\pyc.ico" @@ -14,6 +19,7 @@ 5 ICON DISCARDABLE "icons\python.ico" 6 ICON DISCARDABLE "icons\pythonw.ico" 7 ICON DISCARDABLE "icons\setup.ico" +#endif ///////////////////////////////////////////////////////////////////////////// // diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp new file mode 100644 index 000000000000..1658d05994bb --- /dev/null +++ b/PC/python_uwp.cpp @@ -0,0 +1,226 @@ +/* Main program when embedded in a UWP application on Windows */ + +#include "Python.h" +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#ifdef PYTHONW +#ifdef _DEBUG +const wchar_t *PROGNAME = L"pythonw_d.exe"; +#else +const wchar_t *PROGNAME = L"pythonw.exe"; +#endif +#else +#ifdef _DEBUG +const wchar_t *PROGNAME = L"python_d.exe"; +#else +const wchar_t *PROGNAME = L"python.exe"; +#endif +#endif + +static void +set_user_base() +{ + wchar_t envBuffer[2048]; + try { + const auto appData = winrt::Windows::Storage::ApplicationData::Current(); + if (appData) { + const auto localCache = appData.LocalCacheFolder(); + if (localCache) { + auto path = localCache.Path(); + if (!path.empty() && + !wcscpy_s(envBuffer, path.c_str()) && + !wcscat_s(envBuffer, L"\\local-packages") + ) { + _wputenv_s(L"PYTHONUSERBASE", envBuffer); + } + } + } + } catch (...) { + } +} + +static const wchar_t * +get_argv0(const wchar_t *argv0) +{ + winrt::hstring installPath; + const wchar_t *launcherPath; + wchar_t *buffer; + size_t len; + + launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__"); + if (launcherPath && launcherPath[0]) { + len = wcslen(launcherPath) + 1; + buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); + if (!buffer) { + Py_FatalError("out of memory"); + return NULL; + } + if (wcscpy_s(buffer, len, launcherPath)) { + Py_FatalError("failed to copy to buffer"); + return NULL; + } + return buffer; + } + + try { + const auto package = winrt::Windows::ApplicationModel::Package::Current(); + if (package) { + const auto install = package.InstalledLocation(); + if (install) { + installPath = install.Path(); + } + } + } + catch (...) { + } + + if (!installPath.empty()) { + len = installPath.size() + wcslen(PROGNAME) + 2; + } else { + len = wcslen(argv0) + wcslen(PROGNAME) + 1; + } + + buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); + if (!buffer) { + Py_FatalError("out of memory"); + return NULL; + } + + if (!installPath.empty()) { + if (wcscpy_s(buffer, len, installPath.c_str())) { + Py_FatalError("failed to copy to buffer"); + return NULL; + } + if (wcscat_s(buffer, len, L"\\")) { + Py_FatalError("failed to concatenate backslash"); + return NULL; + } + } else { + if (wcscpy_s(buffer, len, argv0)) { + Py_FatalError("failed to copy argv[0]"); + return NULL; + } + + wchar_t *name = wcsrchr(buffer, L'\\'); + if (name) { + name[1] = L'\0'; + } else { + buffer[0] = L'\0'; + } + } + + if (wcscat_s(buffer, len, PROGNAME)) { + Py_FatalError("failed to concatenate program name"); + return NULL; + } + + return buffer; +} + +static wchar_t * +get_process_name() +{ + DWORD bufferLen = MAX_PATH; + DWORD len = bufferLen; + wchar_t *r = NULL; + + while (!r) { + r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); + if (!r) { + Py_FatalError("out of memory"); + return NULL; + } + len = GetModuleFileNameW(NULL, r, bufferLen); + if (len == 0) { + free((void *)r); + return NULL; + } else if (len == bufferLen && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(r); + r = NULL; + bufferLen *= 2; + } + } + + return r; +} + +int +wmain(int argc, wchar_t **argv) +{ + const wchar_t **new_argv; + int new_argc; + const wchar_t *exeName; + + new_argc = argc; + new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2)); + if (new_argv == NULL) { + Py_FatalError("out of memory"); + return -1; + } + + exeName = get_process_name(); + + new_argv[0] = get_argv0(exeName ? exeName : argv[0]); + for (int i = 1; i < argc; ++i) { + new_argv[i] = argv[i]; + } + + set_user_base(); + + if (exeName) { + const wchar_t *p = wcsrchr(exeName, L'\\'); + if (p) { + const wchar_t *moduleName = NULL; + if (*p++ == L'\\') { + if (wcsnicmp(p, L"pip", 3) == 0) { + moduleName = L"pip"; + _wputenv_s(L"PIP_USER", L"true"); + } + else if (wcsnicmp(p, L"idle", 4) == 0) { + moduleName = L"idlelib"; + } + } + + if (moduleName) { + new_argc += 2; + for (int i = argc; i >= 1; --i) { + new_argv[i + 2] = new_argv[i]; + } + new_argv[1] = L"-m"; + new_argv[2] = moduleName; + } + } + } + + /* Override program_full_path from here so that + sys.executable is set correctly. */ + _Py_SetProgramFullPath(new_argv[0]); + + int result = Py_Main(new_argc, (wchar_t **)new_argv); + + free((void *)exeName); + free((void *)new_argv); + + return result; +} + +#ifdef PYTHONW + +int WINAPI wWinMain( + HINSTANCE hInstance, /* handle to current instance */ + HINSTANCE hPrevInstance, /* handle to previous instance */ + LPWSTR lpCmdLine, /* pointer to command line */ + int nCmdShow /* show state of window */ +) +{ + return wmain(__argc, __wargv); +} + +#endif diff --git a/PC/store_info.txt b/PC/store_info.txt new file mode 100644 index 000000000000..ed40a918e2e7 --- /dev/null +++ b/PC/store_info.txt @@ -0,0 +1,146 @@ +# Overview + +NOTE: This file requires more content. + +Since Python 3.8.2, releases have been made through the Microsoft Store +to allow easy installation on Windows 10.0.17763.0 and later. + +# Building + +To build the store package, the PC/layout script should be used. +Execute the directory with the build of Python to package, and pass +"-h" for full command-line options. + +To sideload test builds, you will need a local certificate. +Instructions are available at +https://docs.microsoft.com/windows/uwp/packaging/create-certificate-package-signing. + +After exporting your certificate, you will need the subject name and +SHA256 hash. The `certutil -dump ` command will display this +information. + +To build for sideloading, use these commands in PowerShell: + +``` +$env:APPX_DATA_PUBLISHER= +$env:APPX_DATA_SHA256= +$env:SigningCertificateFile= + +python PC/layout --copy --include-appxmanifest +Tools/msi/make_appx.ps1 python.msix -sign + +Add-AppxPackage python.msix +``` + +(Note that only the last command requires PowerShell, and the others +can be used from Command Prompt. You can also double-click to install +the final package.) + +To build for publishing to the Store, use these commands: + +``` +$env:APPX_DATA_PUBLISHER = $null +$env:APPX_DATA_SHA256 = $null + +python PC/layout --copy --preset-appxmanifest --precompile +Tools/msi/make_appx.ps1 python.msix +``` + +Note that this package cannot be installed locally. It may only be +added to a submission for the store. + + +# Submission Metadata + +This file contains the text that we use to fill out the store listing +for the Microsoft Store. It needs to be entered manually when creating +a new submission via the dashboard at +https://partner.microsoft.com/dashboard. + +We keep it here for convenience and to allow it to be updated via pull +requests. + +## Title + +Python 3.8 + +## Short Title + +Python + +## Description + +Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python?s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms. + +The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python Web site, https://www.python.org/, and may be freely distributed. The same site also contains distributions of and pointers to many free third party Python modules, programs and tools, and additional documentation. + +The Python interpreter is easily extended with new functions and data types implemented in C or C++ (or other languages callable from C). Python is also suitable as an extension language for customizable applications. + +## ShortDescription + +The Python 3.8 interpreter and runtime. + +## Copyright Trademark Information + +(c) Python Software Foundation + +## Additional License Terms + +Visit https://docs.python.org/3.8/license.html for latest license terms. + +PSF LICENSE AGREEMENT FOR PYTHON 3.8 + +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.8 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.8 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright ? 2001-2018 Python Software Foundation; All Rights + Reserved" are retained in Python 3.8 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.8 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.8. + +4. PSF is making Python 3.8 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.8 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.8 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.8, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.8, Licensee agrees + to be bound by the terms and conditions of this License Agreement. + +## Features + +* Easy to install Python runtime +* Supported by core CPython team +* Find Python, Pip and Idle on PATH + +## Search Terms + +* Python +* Scripting +* Interpreter + diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj index 95e3cd50eca5..bd61c0d4f689 100644 --- a/PCbuild/_tkinter.vcxproj +++ b/PCbuild/_tkinter.vcxproj @@ -95,4 +95,10 @@ + + + + + + \ No newline at end of file diff --git a/PCbuild/find_msbuild.bat b/PCbuild/find_msbuild.bat index 57512a01927e..a2810f09c45e 100644 --- a/PCbuild/find_msbuild.bat +++ b/PCbuild/find_msbuild.bat @@ -29,6 +29,16 @@ @where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc" @if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found + at rem VS 2017 and later provide vswhere.exe, which can be used + at if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere + at set _Py_MSBuild_Root= + at for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest') DO @(set _Py_MSBuild_Root=%%i\MSBuild) + at if not defined _Py_MSBuild_Root goto :skip_vswhere + at for %%j in (Current 15.0) DO @if exist "%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe" (set MSBUILD="%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe") + at set _Py_MSBuild_Root= + at if defined MSBUILD @if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio installation) & goto :found +:skip_vswhere + @rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there. @reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul @if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @( diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index 9e103e12103f..6bf1667e39f8 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -52,6 +52,8 @@ + + @@ -70,6 +72,7 @@ + diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index 59b3861ed406..c212d9f8f32c 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -93,6 +93,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_uwp", "python_uwp.vcxproj", "{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvlauncher", "venvlauncher.vcxproj", "{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvwlauncher", "venvwlauncher.vcxproj", "{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythonw_uwp", "pythonw_uwp.vcxproj", "{AB603547-1E2A-45B3-9E09-B04596006393}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -693,6 +701,70 @@ Global {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.Build.0 = Debug|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.ActiveCfg = Debug|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.Build.0 = Debug|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.ActiveCfg = Release|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.Build.0 = Release|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.ActiveCfg = Release|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.Build.0 = Release|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.ActiveCfg = Debug|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.Build.0 = Debug|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.ActiveCfg = Debug|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.Build.0 = Debug|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.ActiveCfg = Release|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.Build.0 = Release|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.ActiveCfg = Release|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.Build.0 = Release|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.ActiveCfg = Debug|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.Build.0 = Debug|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.ActiveCfg = Debug|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.Build.0 = Debug|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.ActiveCfg = Release|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.Build.0 = Release|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.ActiveCfg = Release|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.Build.0 = Release|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.ActiveCfg = Debug|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.Build.0 = Debug|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.ActiveCfg = Debug|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.Build.0 = Debug|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.ActiveCfg = Release|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.Build.0 = Release|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.ActiveCfg = Release|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PCbuild/python.props b/PCbuild/python.props index 09f11d3bba8c..6dbb503b3243 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -77,7 +77,8 @@ --> <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) - 10.0.17134.0 + 10.0.17763.0 + 10.0.17134.0 10.0.16299.0 10.0.15063.0 10.0.14393.0 diff --git a/PCbuild/python_uwp.vcxproj b/PCbuild/python_uwp.vcxproj new file mode 100644 index 000000000000..af187dd4df30 --- /dev/null +++ b/PCbuild/python_uwp.vcxproj @@ -0,0 +1,86 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF} + + + + + Application + false + Unicode + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + %(PreprocessorDefinitions) + /EHsc /std:c++17 %(AdditionalOptions) + + + windowsapp.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} + false + + + + + + diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 4ae2d692eee1..78ec9a16efa7 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -479,4 +479,19 @@ + + + $(VCInstallDir)\Redist\MSVC\$(VCToolsRedistVersion)\ + $(VCRedistDir)x86\ + $(VCRedistDir)$(Platform)\ + + + + + + + + + + diff --git a/PCbuild/pythonw_uwp.vcxproj b/PCbuild/pythonw_uwp.vcxproj new file mode 100644 index 000000000000..79e105877fbe --- /dev/null +++ b/PCbuild/pythonw_uwp.vcxproj @@ -0,0 +1,86 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {AB603547-1E2A-45B3-9E09-B04596006393} + + + + + Application + false + Unicode + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + PYTHONW;%(PreprocessorDefinitions) + /EHsc /std:c++17 %(AdditionalOptions) + + + windowsapp.lib;%(AdditionalDependencies) + Windows + + + + + + + + + + + + + + {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} + false + + + + + + diff --git a/PCbuild/venvlauncher.vcxproj b/PCbuild/venvlauncher.vcxproj new file mode 100644 index 000000000000..295b36304733 --- /dev/null +++ b/PCbuild/venvlauncher.vcxproj @@ -0,0 +1,85 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D} + venvlauncher + venvlauncher + false + + + + + Application + MultiByte + + + + + + ClCompile + + + + + + + + + _CONSOLE;VENV_REDIRECT;%(PreprocessorDefinitions) + MultiThreaded + + + PY_ICON;%(PreprocessorDefinitions) + + + version.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + diff --git a/PCbuild/venvwlauncher.vcxproj b/PCbuild/venvwlauncher.vcxproj new file mode 100644 index 000000000000..e7ba25da41eb --- /dev/null +++ b/PCbuild/venvwlauncher.vcxproj @@ -0,0 +1,85 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D} + venvwlauncher + venvwlauncher + false + + + + + Application + MultiByte + + + + + + ClCompile + + + + + + + + + _WINDOWS;VENV_REDIRECT;%(PreprocessorDefinitions) + MultiThreaded + + + PYW_ICON;%(PreprocessorDefinitions) + + + version.lib;%(AdditionalDependencies) + Windows + + + + + + + + + + + + + + + diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat index 4178981195ee..45e189b537f6 100644 --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -37,6 +37,7 @@ set BUILDX64= set TARGET=Rebuild set TESTTARGETDIR= set PGO=-m test -q --pgo +set BUILDMSI=1 set BUILDNUGET=1 set BUILDZIP=1 @@ -61,6 +62,7 @@ if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts +if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 @@ -174,10 +176,12 @@ if "%OUTDIR_PLAT%" EQU "win32" ( ) set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI% -%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true -if errorlevel 1 exit /B -%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false -if errorlevel 1 exit /B +if defined BUILDMSI ( + %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true + if errorlevel 1 exit /B + %MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false + if errorlevel 1 exit /B +) if defined BUILDZIP ( %MSBUILD% "%D%make_zip.proj" /t:Build %BUILDOPTS% %CERTOPTS% /p:OutputPath="%BUILD%en-us" @@ -214,6 +218,7 @@ echo --skip-build (-B) Do not build Python (just do the installers) echo --skip-doc (-D) Do not build documentation echo --pgo Specify PGO command for x64 installers echo --skip-pgo Build x64 installers without using PGO +echo --skip-msi Do not build executable/MSI packages echo --skip-nuget Do not build Nuget packages echo --skip-zip Do not build embeddable package echo --download Specify the full download URL for MSIs diff --git a/Tools/msi/make_appx.ps1 b/Tools/msi/make_appx.ps1 new file mode 100644 index 000000000000..b3f190e07db8 --- /dev/null +++ b/Tools/msi/make_appx.ps1 @@ -0,0 +1,71 @@ +<# +.Synopsis + Compiles and signs an APPX package +.Description + Given the file listing, ensures all the contents are signed + and builds and signs the final package. +.Parameter mapfile + The location on disk of the text mapping file. +.Parameter msix + The path and name to store the APPX/MSIX. +.Parameter sign + When set, signs the APPX/MSIX. Packages to be published to + the store should not be signed. +.Parameter description + Description to embed in the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$layout, + [Parameter(Mandatory=$true)][string]$msix, + [switch]$sign, + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +Set-Alias makeappx (Find-Tool "makeappx.exe") -Scope Script +Set-Alias makepri (Find-Tool "makepri.exe") -Scope Script + +$msixdir = Split-Path $msix -Parent +if ($msixdir) { + $msixdir = (mkdir -Force $msixdir).FullName +} else { + $msixdir = Get-Location +} +$msix = Join-Path $msixdir (Split-Path $msix -Leaf) + +pushd $layout +try { + if (Test-Path resources.pri) { + del resources.pri + } + $name = ([xml](gc AppxManifest.xml)).Package.Identity.Name + makepri new /pr . /mn AppxManifest.xml /in $name /cf _resources.xml /of _resources.pri /mf appx /o + if (-not $? -or -not (Test-Path _resources.map.txt)) { + throw "makepri step failed" + } + $lines = gc _resources.map.txt + $lines | ?{ -not ($_ -match '"_resources[\w\.]+?"') } | Out-File _resources.map.txt -Encoding utf8 + makeappx pack /f _resources.map.txt /m AppxManifest.xml /o /p $msix + if (-not $?) { + throw "makeappx step failed" + } +} finally { + popd +} + +if ($sign) { + Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files $msix + + if (-not $?) { + throw "Package signing failed" + } +} diff --git a/Tools/msi/make_cat.ps1 b/Tools/msi/make_cat.ps1 new file mode 100644 index 000000000000..70741439869a --- /dev/null +++ b/Tools/msi/make_cat.ps1 @@ -0,0 +1,34 @@ +<# +.Synopsis + Compiles and signs a catalog file. +.Description + Given the CDF definition file, builds and signs a catalog. +.Parameter catalog + The path to the catalog definition file to compile and + sign. It is assumed that the .cat file will be the same + name with a new extension. +.Parameter description + The description to add to the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$catalog, + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +Set-Alias MakeCat (Find-Tool "makecat.exe") -Scope Script + +MakeCat $catalog +if (-not $?) { + throw "Catalog compilation failed" +} +Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files @($catalog -replace 'cdf$', 'cat') diff --git a/Tools/msi/make_zip.proj b/Tools/msi/make_zip.proj index 214111734219..125a434e51f4 100644 --- a/Tools/msi/make_zip.proj +++ b/Tools/msi/make_zip.proj @@ -15,11 +15,12 @@ .zip $(OutputPath)\$(TargetName)$(TargetExt) rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)" - "$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py" - $(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -b "$(BuildPath.TrimEnd(`\`))" - set DOC_FILENAME=python$(PythonVersion).chm + "$(PythonExe)" "$(PySourcePath)PC\layout" + $(Arguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" + $(Arguments) -t "$(IntermediateOutputPath)\zip_$(ArchName)" + $(Arguments) --zip "$(TargetPath)" + $(Arguments) --precompile --zip-lib --include-underpth --include-stable --flat-dlls $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) diff --git a/Tools/msi/make_zip.py b/Tools/msi/make_zip.py deleted file mode 100644 index 58f3b15ef852..000000000000 --- a/Tools/msi/make_zip.py +++ /dev/null @@ -1,250 +0,0 @@ -import argparse -import py_compile -import re -import sys -import shutil -import stat -import os -import tempfile - -from itertools import chain -from pathlib import Path -from zipfile import ZipFile, ZIP_DEFLATED - - -TKTCL_RE = re.compile(r'^(_?tk|tcl).+\.(pyd|dll)', re.IGNORECASE) -DEBUG_RE = re.compile(r'_d\.(pyd|dll|exe|pdb|lib)$', re.IGNORECASE) -PYTHON_DLL_RE = re.compile(r'python\d\d?\.dll$', re.IGNORECASE) - -DEBUG_FILES = { - '_ctypes_test', - '_testbuffer', - '_testcapi', - '_testconsole', - '_testimportmultiple', - '_testmultiphase', - 'xxlimited', - 'python3_dstub', -} - -EXCLUDE_FROM_LIBRARY = { - '__pycache__', - 'idlelib', - 'pydoc_data', - 'site-packages', - 'tkinter', - 'turtledemo', -} - -EXCLUDE_FROM_EMBEDDABLE_LIBRARY = { - 'ensurepip', - 'venv', -} - -EXCLUDE_FILE_FROM_LIBRARY = { - 'bdist_wininst.py', -} - -EXCLUDE_FILE_FROM_LIBS = { - 'liblzma', - 'python3stub', -} - -EXCLUDED_FILES = { - 'pyshellext', -} - -def is_not_debug(p): - if DEBUG_RE.search(p.name): - return False - - if TKTCL_RE.search(p.name): - return False - - return p.stem.lower() not in DEBUG_FILES and p.stem.lower() not in EXCLUDED_FILES - -def is_not_debug_or_python(p): - return is_not_debug(p) and not PYTHON_DLL_RE.search(p.name) - -def include_in_lib(p): - name = p.name.lower() - if p.is_dir(): - if name in EXCLUDE_FROM_LIBRARY: - return False - if name == 'test' and p.parts[-2].lower() == 'lib': - return False - if name in {'test', 'tests'} and p.parts[-3].lower() == 'lib': - return False - return True - - if name in EXCLUDE_FILE_FROM_LIBRARY: - return False - - suffix = p.suffix.lower() - return suffix not in {'.pyc', '.pyo', '.exe'} - -def include_in_embeddable_lib(p): - if p.is_dir() and p.name.lower() in EXCLUDE_FROM_EMBEDDABLE_LIBRARY: - return False - - return include_in_lib(p) - -def include_in_libs(p): - if not is_not_debug(p): - return False - - return p.stem.lower() not in EXCLUDE_FILE_FROM_LIBS - -def include_in_tools(p): - if p.is_dir() and p.name.lower() in {'scripts', 'i18n', 'pynche', 'demo', 'parser'}: - return True - - return p.suffix.lower() in {'.py', '.pyw', '.txt'} - -BASE_NAME = 'python{0.major}{0.minor}'.format(sys.version_info) - -FULL_LAYOUT = [ - ('/', '$build', 'python.exe', is_not_debug), - ('/', '$build', 'pythonw.exe', is_not_debug), - ('/', '$build', 'python{}.dll'.format(sys.version_info.major), is_not_debug), - ('/', '$build', '{}.dll'.format(BASE_NAME), is_not_debug), - ('DLLs/', '$build', '*.pyd', is_not_debug), - ('DLLs/', '$build', '*.dll', is_not_debug_or_python), - ('include/', 'include', '*.h', None), - ('include/', 'PC', 'pyconfig.h', None), - ('Lib/', 'Lib', '**/*', include_in_lib), - ('libs/', '$build', '*.lib', include_in_libs), - ('Tools/', 'Tools', '**/*', include_in_tools), -] - -EMBED_LAYOUT = [ - ('/', '$build', 'python*.exe', is_not_debug), - ('/', '$build', '*.pyd', is_not_debug), - ('/', '$build', '*.dll', is_not_debug), - ('{}.zip'.format(BASE_NAME), 'Lib', '**/*', include_in_embeddable_lib), -] - -if os.getenv('DOC_FILENAME'): - FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None)) -if os.getenv('VCREDIST_PATH'): - FULL_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) - EMBED_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) - -def copy_to_layout(target, rel_sources): - count = 0 - - if target.suffix.lower() == '.zip': - if target.exists(): - target.unlink() - - with ZipFile(str(target), 'w', ZIP_DEFLATED) as f: - with tempfile.TemporaryDirectory() as tmpdir: - for s, rel in rel_sources: - if rel.suffix.lower() == '.py': - pyc = Path(tmpdir) / rel.with_suffix('.pyc').name - try: - py_compile.compile(str(s), str(pyc), str(rel), doraise=True, optimize=2) - except py_compile.PyCompileError: - f.write(str(s), str(rel)) - else: - f.write(str(pyc), str(rel.with_suffix('.pyc'))) - else: - f.write(str(s), str(rel)) - count += 1 - - else: - for s, rel in rel_sources: - dest = target / rel - try: - dest.parent.mkdir(parents=True) - except FileExistsError: - pass - if dest.is_file(): - dest.chmod(stat.S_IWRITE) - shutil.copy(str(s), str(dest)) - if dest.is_file(): - dest.chmod(stat.S_IWRITE) - count += 1 - - return count - -def rglob(root, pattern, condition): - dirs = [root] - recurse = pattern[:3] in {'**/', '**\\'} - while dirs: - d = dirs.pop(0) - for f in d.glob(pattern[3:] if recurse else pattern): - if recurse and f.is_dir() and (not condition or condition(f)): - dirs.append(f) - elif f.is_file() and (not condition or condition(f)): - yield f, f.relative_to(root) - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('-s', '--source', metavar='dir', help='The directory containing the repository root', type=Path) - parser.add_argument('-o', '--out', metavar='file', help='The name of the output archive', type=Path, default=None) - parser.add_argument('-t', '--temp', metavar='dir', help='A directory to temporarily extract files into', type=Path, default=None) - parser.add_argument('-e', '--embed', help='Create an embedding layout', action='store_true', default=False) - parser.add_argument('-b', '--build', help='Specify the build directory', type=Path, default=None) - ns = parser.parse_args() - - source = ns.source or (Path(__file__).resolve().parent.parent.parent) - out = ns.out - build = ns.build or Path(sys.exec_prefix) - assert isinstance(source, Path) - assert not out or isinstance(out, Path) - assert isinstance(build, Path) - - if ns.temp: - temp = ns.temp - delete_temp = False - else: - temp = Path(tempfile.mkdtemp()) - delete_temp = True - - if out: - try: - out.parent.mkdir(parents=True) - except FileExistsError: - pass - try: - temp.mkdir(parents=True) - except FileExistsError: - pass - - layout = EMBED_LAYOUT if ns.embed else FULL_LAYOUT - - try: - for t, s, p, c in layout: - if s == '$build': - fs = build - else: - fs = source / s - files = rglob(fs, p, c) - extra_files = [] - if s == 'Lib' and p == '**/*': - extra_files.append(( - source / 'tools' / 'msi' / 'distutils.command.bdist_wininst.py', - Path('distutils') / 'command' / 'bdist_wininst.py' - )) - copied = copy_to_layout(temp / t.rstrip('/'), chain(files, extra_files)) - print('Copied {} files'.format(copied)) - - if ns.embed: - with open(str(temp / (BASE_NAME + '._pth')), 'w') as f: - print(BASE_NAME + '.zip', file=f) - print('.', file=f) - print('', file=f) - print('# Uncomment to run site.main() automatically', file=f) - print('#import site', file=f) - - if out: - total = copy_to_layout(out, rglob(temp, '**/*', None)) - print('Wrote {} files to {}'.format(total, out)) - finally: - if delete_temp: - shutil.rmtree(temp, True) - - -if __name__ == "__main__": - sys.exit(int(main() or 0)) diff --git a/Tools/msi/sdktools.psm1 b/Tools/msi/sdktools.psm1 new file mode 100644 index 000000000000..81a74d3679d7 --- /dev/null +++ b/Tools/msi/sdktools.psm1 @@ -0,0 +1,43 @@ +function Find-Tool { + param([string]$toolname) + + $kitroot = (gp 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots\').KitsRoot10 + $tool = (gci -r "$kitroot\Bin\*\x64\$toolname" | sort FullName -Desc | select -First 1) + if (-not $tool) { + throw "$toolname is not available" + } + Write-Host "Found $toolname at $($tool.FullName)" + return $tool.FullName +} + +Set-Alias SignTool (Find-Tool "signtool.exe") -Scope Script + +function Sign-File { + param([string]$certname, [string]$certsha1, [string]$certfile, [string]$description, [string[]]$files) + + if (-not $description) { + $description = $env:SigningDescription; + if (-not $description) { + $description = "Python"; + } + } + if (-not $certname) { + $certname = $env:SigningCertificate; + } + if (-not $certfile) { + $certfile = $env:SigningCertificateFile; + } + + foreach ($a in $files) { + if ($certsha1) { + SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } elseif ($certname) { + SignTool sign /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } elseif ($certfile) { + SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } else { + SignTool sign /a /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } + } +} + diff --git a/Tools/msi/sign_build.ps1 b/Tools/msi/sign_build.ps1 new file mode 100644 index 000000000000..6668eb33a2d1 --- /dev/null +++ b/Tools/msi/sign_build.ps1 @@ -0,0 +1,34 @@ +<# +.Synopsis + Recursively signs the contents of a directory. +.Description + Given the file patterns, code signs the contents. +.Parameter root + The root directory to sign. +.Parameter patterns + The file patterns to sign +.Parameter description + The description to add to the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$root, + [string[]]$patterns=@("*.exe", "*.dll", "*.pyd"), + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +pushd $root +try { + Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files (gci -r $patterns) +} finally { + popd +} \ No newline at end of file diff --git a/Tools/nuget/make_pkg.proj b/Tools/nuget/make_pkg.proj index 9843bc97ccdc..e093a6d0bd76 100644 --- a/Tools/nuget/make_pkg.proj +++ b/Tools/nuget/make_pkg.proj @@ -20,25 +20,28 @@ false $(OutputName).$(NuspecVersion) .nupkg - $(IntermediateOutputPath)\nuget_$(ArchName) + $(IntermediateOutputPath)\nuget_$(ArchName)\ - rmdir /q/s "$(IntermediateOutputPath)" + rmdir /q/s "$(IntermediateOutputPath.TrimEnd(`\`))" - "$(PythonExe)" "$(MSBuildThisFileDirectory)\..\msi\make_zip.py" - $(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))" + "$(PythonExe)" "$(PySourcePath)PC\layout" + $(PythonArguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" + $(PythonArguments) -t "$(IntermediateOutputPath)obj" + $(PythonArguments) --copy "$(IntermediateOutputPath)pkg" + $(PythonArguments) --include-dev --include-tools --include-pip --include-stable --include-launcher --include-props - "$(IntermediateOutputPath)\python.exe" -B -c "import sys; sys.path.append(r'$(PySourcePath)\Lib'); import ensurepip; ensurepip._main()" - "$(IntermediateOutputPath)\python.exe" -B -m pip install -U $(Packages) + "$(IntermediateOutputPath)pkg\pip.exe" -B -m pip install -U $(Packages) - "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)" + "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)pkg" "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))" $(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))" $(NugetArguments) -Version "$(NuspecVersion)" $(NugetArguments) -NoPackageAnalysis -NonInteractive - set DOC_FILENAME=python$(PythonVersion).chm $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) + $(Environment)%0D%0Aset PYTHON_NUSPEC_VERSION=$(NuspecVersion) + $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=$(Platform) + $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=Win32 $(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" >nul 2>nul @@ -48,22 +51,7 @@ - - - - - - <_PropsContents>$([System.IO.File]::ReadAllText('python.props')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TAG$$', '$(MajorVersionNumber).$(MinorVersionNumber)')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_VERSION$$', '$(NuspecVersion)')) - <_PropsContents Condition="$(Platform) == 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', 'Win32')) - <_PropsContents Condition="$(Platform) != 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', '$(Platform)')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TARGET$$', '_GetPythonRuntimeFilesDependsOn$(MajorVersionNumber)$(MinorVersionNumber)_$(Platform)')) - <_ExistingContents Condition="Exists('$(IntermediateOutputPath)\python.props')">$([System.IO.File]::ReadAllText('$(IntermediateOutputPath)\python.props')) - - + From webhook-mailer at python.org Fri Dec 7 00:09:58 2018 From: webhook-mailer at python.org (Steve Dower) Date: Fri, 07 Dec 2018 05:09:58 -0000 Subject: [Python-checkins] [3.7] bpo-34977: Add Windows App Store package (GH-10245) Message-ID: https://github.com/python/cpython/commit/253209149389e6793a052034e1f2d97691086f18 commit: 253209149389e6793a052034e1f2d97691086f18 branch: 3.7 author: Steve Dower committer: GitHub date: 2018-12-06T21:09:53-08:00 summary: [3.7] bpo-34977: Add Windows App Store package (GH-10245) files: A .azure-pipelines/windows-appx-test.yml A Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst A PC/classicAppCompat.can.xml A PC/classicAppCompat.cat A PC/classicAppCompat.sccd A PC/icons/pythonwx150.png A PC/icons/pythonwx44.png A PC/icons/pythonx150.png A PC/icons/pythonx44.png A PC/icons/pythonx50.png A PC/layout/__init__.py A PC/layout/__main__.py A PC/layout/main.py A PC/layout/support/__init__.py A PC/layout/support/appxmanifest.py A PC/layout/support/catalog.py A PC/layout/support/constants.py A PC/layout/support/distutils.command.bdist_wininst.py A PC/layout/support/filesets.py A PC/layout/support/logging.py A PC/layout/support/options.py A PC/layout/support/pip.py A PC/layout/support/props.py A PC/layout/support/python.props A PC/python_uwp.cpp A PC/store_info.txt A PCbuild/python_uwp.vcxproj A PCbuild/pythonw_uwp.vcxproj A PCbuild/venvlauncher.vcxproj A PCbuild/venvwlauncher.vcxproj A Tools/msi/make_appx.ps1 A Tools/msi/make_cat.ps1 A Tools/msi/sdktools.psm1 A Tools/msi/sign_build.ps1 D Tools/msi/make_zip.py D Tools/nuget/python.props M .gitattributes M Lib/test/test_pathlib.py M Lib/test/test_venv.py M Lib/venv/__init__.py M PC/getpathp.c M PC/launcher.c M PC/pylauncher.rc M PCbuild/_tkinter.vcxproj M PCbuild/find_msbuild.bat M PCbuild/pcbuild.proj M PCbuild/pcbuild.sln M PCbuild/python.props M PCbuild/pythoncore.vcxproj M Tools/msi/buildrelease.bat M Tools/msi/make_zip.proj M Tools/nuget/make_pkg.proj diff --git a/.azure-pipelines/windows-appx-test.yml b/.azure-pipelines/windows-appx-test.yml new file mode 100644 index 000000000000..9840c0a1221f --- /dev/null +++ b/.azure-pipelines/windows-appx-test.yml @@ -0,0 +1,65 @@ +jobs: +- job: Prebuild + displayName: Pre-build checks + + pool: + vmImage: ubuntu-16.04 + + steps: + - template: ./prebuild-checks.yml + + +- job: Windows_Appx_Tests + displayName: Windows Appx Tests + dependsOn: Prebuild + condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) + + pool: + vmImage: vs2017-win2016 + + strategy: + matrix: + win64: + arch: amd64 + buildOpt: '-p x64' + testRunTitle: '$(Build.SourceBranchName)-win64-appx' + testRunPlatform: win64 + maxParallel: 2 + + steps: + - checkout: self + clean: true + fetchDepth: 5 + + - powershell: | + # Relocate build outputs outside of source directory to make cleaning faster + Write-Host '##vso[task.setvariable variable=Py_IntDir]$(Build.BinariesDirectory)\obj' + # UNDONE: Do not build to a different directory because of broken tests + Write-Host '##vso[task.setvariable variable=Py_OutDir]$(Build.SourcesDirectory)\PCbuild' + Write-Host '##vso[task.setvariable variable=EXTERNAL_DIR]$(Build.BinariesDirectory)\externals' + displayName: Update build locations + + - script: PCbuild\build.bat -e $(buildOpt) + displayName: 'Build CPython' + + - script: python.bat PC\layout -vv -s "$(Build.SourcesDirectory)" -b "$(Py_OutDir)\$(arch)" -t "$(Py_IntDir)\layout-tmp-$(arch)" --copy "$(Py_IntDir)\layout-$(arch)" --precompile --preset-appx --include-tests + displayName: 'Create APPX layout' + + - script: .\python.exe -m test.pythoninfo + workingDirectory: $(Py_IntDir)\layout-$(arch) + displayName: 'Display build info' + + - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml" --tempdir "$(Py_IntDir)\tmp-$(arch)" + workingDirectory: $(Py_IntDir)\layout-$(arch) + displayName: 'Tests' + env: + PREFIX: $(Py_IntDir)\layout-$(arch) + + - task: PublishTestResults at 2 + displayName: 'Publish Test Results' + inputs: + testResultsFiles: '$(Build.BinariesDirectory)\test-results.xml' + mergeTestResults: true + testRunTitle: $(testRunTitle) + platform: $(testRunPlatform) + condition: succeededOrFailed() diff --git a/.gitattributes b/.gitattributes index 4a487c3c2a14..16237bb2b3ac 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,6 +19,7 @@ # Specific binary files Lib/test/sndhdrdata/sndhdr.* binary +PC/classicAppCompat.* binary # Text files that should not be subject to eol conversion Lib/test/cjkencodings/* -text diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index e436db995ce4..056507ef6fe8 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1519,7 +1519,7 @@ def test_resolve_common(self): # resolves to 'dirB/..' first before resolving to parent of dirB. self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) # Now create absolute symlinks - d = support._longpath(tempfile.mkdtemp(suffix='-dirD')) + d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) self.addCleanup(support.rmtree, d) os.symlink(os.path.join(d), join('dirA', 'linkX')) os.symlink(join('dirB'), os.path.join(d, 'linkY')) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 461fe7afd213..22a3b78852f8 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -243,6 +243,7 @@ def test_isolation(self): self.assertIn('include-system-site-packages = %s\n' % s, data) @unittest.skipUnless(can_symlink(), 'Needs symlinks') + @unittest.skipIf(os.name == 'nt', 'Symlinks are never used on Windows') def test_symlinking(self): """ Test symlinking works as expected diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index 716129d13987..5438b0d4e508 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -9,6 +9,7 @@ import shutil import subprocess import sys +import sysconfig import types logger = logging.getLogger(__name__) @@ -63,10 +64,11 @@ def create(self, env_dir): self.system_site_packages = False self.create_configuration(context) self.setup_python(context) + if not self.upgrade: + self.setup_scripts(context) if self.with_pip: self._setup_pip(context) if not self.upgrade: - self.setup_scripts(context) self.post_setup(context) if true_system_site_packages: # We had set it to False before, now @@ -157,14 +159,6 @@ def create_configuration(self, context): f.write('include-system-site-packages = %s\n' % incl) f.write('version = %d.%d.%d\n' % sys.version_info[:3]) - if os.name == 'nt': - def include_binary(self, f): - if f.endswith(('.pyd', '.dll')): - result = True - else: - result = f.startswith('python') and f.endswith('.exe') - return result - def symlink_or_copy(self, src, dst, relative_symlinks_ok=False): """ Try symlinking a file, and if that fails, fall back to copying. @@ -194,9 +188,9 @@ def setup_python(self, context): binpath = context.bin_path path = context.env_exe copier = self.symlink_or_copy - copier(context.executable, path) dirname = context.python_dir if os.name != 'nt': + copier(context.executable, path) if not os.path.islink(path): os.chmod(path, 0o755) for suffix in ('python', 'python3'): @@ -208,32 +202,33 @@ def setup_python(self, context): if not os.path.islink(path): os.chmod(path, 0o755) else: - subdir = 'DLLs' - include = self.include_binary - files = [f for f in os.listdir(dirname) if include(f)] - for f in files: - src = os.path.join(dirname, f) - dst = os.path.join(binpath, f) - if dst != context.env_exe: # already done, above - copier(src, dst) - dirname = os.path.join(dirname, subdir) - if os.path.isdir(dirname): - files = [f for f in os.listdir(dirname) if include(f)] - for f in files: - src = os.path.join(dirname, f) - dst = os.path.join(binpath, f) - copier(src, dst) - # copy init.tcl over - for root, dirs, files in os.walk(context.python_dir): - if 'init.tcl' in files: - tcldir = os.path.basename(root) - tcldir = os.path.join(context.env_dir, 'Lib', tcldir) - if not os.path.exists(tcldir): - os.makedirs(tcldir) - src = os.path.join(root, 'init.tcl') - dst = os.path.join(tcldir, 'init.tcl') - shutil.copyfile(src, dst) - break + # For normal cases, the venvlauncher will be copied from + # our scripts folder. For builds, we need to copy it + # manually. + if sysconfig.is_python_build(True): + suffix = '.exe' + if context.python_exe.lower().endswith('_d.exe'): + suffix = '_d.exe' + + src = os.path.join(dirname, "venvlauncher" + suffix) + dst = os.path.join(binpath, context.python_exe) + copier(src, dst) + + src = os.path.join(dirname, "venvwlauncher" + suffix) + dst = os.path.join(binpath, "pythonw" + suffix) + copier(src, dst) + + # copy init.tcl over + for root, dirs, files in os.walk(context.python_dir): + if 'init.tcl' in files: + tcldir = os.path.basename(root) + tcldir = os.path.join(context.env_dir, 'Lib', tcldir) + if not os.path.exists(tcldir): + os.makedirs(tcldir) + src = os.path.join(root, 'init.tcl') + dst = os.path.join(tcldir, 'init.tcl') + shutil.copyfile(src, dst) + break def _setup_pip(self, context): """Installs or upgrades pip in a virtual environment""" @@ -320,7 +315,7 @@ def install_scripts(self, context, path): dstfile = os.path.join(dstdir, f) with open(srcfile, 'rb') as f: data = f.read() - if not srcfile.endswith('.exe'): + if not srcfile.endswith(('.exe', '.pdb')): try: data = data.decode('utf-8') data = self.replace_variables(data, context) diff --git a/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst new file mode 100644 index 000000000000..8e1a4ba84880 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst @@ -0,0 +1 @@ +Adds support for building a Windows App Store package diff --git a/PC/classicAppCompat.can.xml b/PC/classicAppCompat.can.xml new file mode 100644 index 000000000000..f00475c8da31 --- /dev/null +++ b/PC/classicAppCompat.can.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/PC/classicAppCompat.cat b/PC/classicAppCompat.cat new file mode 100644 index 000000000000..3d213596accf Binary files /dev/null and b/PC/classicAppCompat.cat differ diff --git a/PC/classicAppCompat.sccd b/PC/classicAppCompat.sccd new file mode 100644 index 000000000000..97648985a2cc --- /dev/null +++ b/PC/classicAppCompat.sccd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + MIIq5AYJKoZIhvcNAQcCoIIq1TCCKtECAQExDzANBglghkgBZQMEAgEFADCCARAGCSsGAQQBgjcKAaCCAQEwgf4wDAYKKwYBBAGCNwwBAQQQaM+L42jwBUGvBczrtolMmhcNMTgxMTMwMDA1OTAzWjAOBgorBgEEAYI3DAEDBQAwgbwwKgQUWKcU3R38DGPlKK33XGIwKtVL1r4xEjAQBgorBgEEAYI3DAIDMQKCADCBjQQg3K+KBOQX7HfxjRNZC9cx8gIPkEhPRO1nJFRdWQrVEJ4xaTAQBgorBgEEAYI3DAIDMQKCADBVBgorBgEEAYI3AgEEMUcwRTAQBgorBgEEAYI3AgEZogKAADAxMA0GCWCGSAFlAwQCAQUABCDcr4oE5Bfsd/GNE1kL1zHyAg+QSE9E7WckVF1ZCtUQnqCCFFAwggZSMIIEOqADAgECAhMzAAMu49KhfNamygpWAAIAAy7jMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQDEx5NaWNyb3NvZnQgTWFya2V0cGxhY2UgQ0EgRyAwMTMwHhcNMTgxMTMwMDA1NTA1WhcNMTgxMjAzMDA1NTA1WjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwpcimfAx3HEpba1GLL/gDaRVddHE5PXTRmwlgaz8kt6/rq5rlrPFnCnbIc5818v0xJIznastbmrq26xyCEHyMLBKnyneTKE36I7+TGjcY0D7ow+o2vY7LDKMCTGlh31fx1Tvrl+5xTbWX5jdLU/3MB5faeOGh+0Knzwx1KDoXWgPtfXnD8I5jxJieoWoCwCjKTJgBOklLy9nbOalxf0h+xQRy2p5fj+PxAwQPgHWft36AF7/IMbt9FcXMtg4xdpnTYz4OV3dFOPz4m3M8HwVgNMv89W/1Ozc7uOyZt0Ij1baT6r2L3IjYg5ftzpGqaDOFcWlyDFSdhMR6BIKW8xEpAgMBAAGjggHCMIIBvjAYBgNVHSUBAf8EDjAMBgorBgEEAYI3TBwBMB0GA1UdDgQWBBRdpGYiCytx83FYzPSl+o97YzpxGzAPBgNVHREECDAGggRNT1BSMB8GA1UdIwQYMBaAFEnYB1RFhpclHtZZcRLDcpt0OE3oMGIGA1UdHwRbMFkwV6BVoFOGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyME1hcmtldHBsYWNlJTIwQ0ElMjBHJTIwMDEzKDIpLmNybDBvBggrBgEFBQcBAQRjMGEwXwYIKwYBBQUHMAKGU2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwTWFya2V0cGxhY2UlMjBDQSUyMEclMjAwMTMoMikuY3J0MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXgMDwGCSsGAQQBgjcVBwQvMC0GJSsGAQQBgjcVCIOS9kTqrxCDkY0wgqzgLIKinDE0g+6NOIaE7wACAWQCARYwIAYJKwYBBAGCNxUKAQH/BBAwDjAMBgorBgEEAYI3TBwBMA0GCSqGSIb3DQEBCwUAA4ICAQB3Dk3rXH52CDq/z1fwqn9xI5WGjGmu6oAE4HSc3sNdFrSVMMGm4gTlYGWSZ0wJUUf16mVr/rdXhxuR3MZn+m4Bhdl8KQqYjYbIvCUVj0o9nZ+yT6foeY8bKnB+K5h6rol+mjDj5IfcutC4x2Kx5RrtDtRTSoKA63iZ74DYngPpBGBBgaS2c/QzgqPRAMMRqy2KBDP0miCnpR3F4YlzHGyOZwyHhESjYd9kwF47+msuHS04JZpnGHIvBppKN9XQzH3WezNnnX3lz4AyAUMsMFuARqEnacUhrAHL9n5zMv9CzxDYN1r1/aDh/788RuGuZM+E3NtmbxJJ7j6T5/VtXNBRgKtIq8d2+11j6qvKLigOTxSC25/A70BZBEvllLFnvc1vA2LrC9drwt1KpSmWie1nvpilw7o+gHMOG9utUxGha2VuVizuVNGCywTRRjvmGS1QqTfaun1URVrLfnDINXuTgN1Vwp0J5IGpJ3D8yj01NDQ/RworE+3W/R531NBYova9QRhU/igEw/Aa/q8wjZ4Pzxr9oBIo0Ta3Tv6qIggaWXw0U9+F0J7SCqIhn0d0ATO+E1Qs/SxZIAICLwmqzoLYUAh8q153esBs4uesueqgt5ueyHK8V3WjMS4wxEyVN5ZMET3hFtEshsZC31tLDdjq750U4SgQVmoYSm3F3ZOKQDCCBtcwggS/oAMCAQICCmESRKIAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDMyODIxMDkzOVoXDTMxMDMyODIxMTkzOVowfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAubUaSwGYVsE3MAnPfvmozUhAB3qxBABgJRW1vDp4+tVinXxD32f7k1K89JQ6zDOgS/iDgULC+yFK1K/1Qjac/0M7P6c8v5LSjnWGlERLa/qY32j46S7SLQcit3g2jgoTTO03eUG+9yHZUTGV/FJdRYB8uXhrznJBa+Y+yGwiQKF+m6XFeBH/KORoKFx+dmMoy9EWJ/m/o9IiUj2kzm9C691+vZ/I2w0Bj93W9SPPkV2PCNHlzgfIAoeajWpHmi38Wi3xZHonkzAVBHxPsCBppOoNsWvmAfUM7eBthkSPvFruekyDCPNEYhfGqgqtqLkoBebXLZCOVybF7wTQaLvse60//3P003icRcCoQYgY4NAqrF7j80o5U7DkeXxcB0xvengsaKgiAaV1DKkRbpe98wCqr1AASvm5rAJUYMU+mXmOieV2EelY2jGrenWe9FQpNXYV1NoWBh0WKoFxttoWYAnF705bIWtSZsz08ZfK6WLX4GXNLcPBlgCzfTm1sdKYASWdBbH2haaNhPapFhQQBJHKwnVW2iXErImhuPi45W3MVTZ5D9ASshZx69cLYY6xAdIa+89Kf/uRrsGOVZfahDuDw+NI183iAyzC8z/QRt2P32LYxP0xrCdqVh+DJo2i4NoE8Uk1usCdbVRuBMBQl/AwpOTq7IMvHGElf65CqzUCAwEAAaOCAUswggFHMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBQPU8s/FmEl/mCJHdO5fOiQrbOU0TAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCjuZmM8ZVNDgp9wHsL4RY8KJ8nLinvxFTphNGCrxaLknkYG5pmMhVlX+UB/tSiW8W13W60nggz9u5xwMx7v/1t/Tgm6g2brVyOKI5A7u6/2SIJwkJKFw953K0YIKVT28w9zl8dSJnmRnyR0G86ncWbF6CLQ6A6lBQ9o2mTGVqDr4m35WKAnc6YxUUM1y74mbzFFZr63VHsCcOp3pXWnUqAY1rb6Q6NX1b3clncKqLFm0EjKHcQ56grTbwuuB7pMdh/IFCJR01MQzQbDtpEisbOeZUi43YVAAHKqI1EO9bRwg3frCjwAbml9MmI4utMW94gWFgvrMxIX+n42RBDIjf3Ot3jkT6gt3XeTTmO9bptgblZimhERdkFRUFpVtkocJeLoGuuzP93uH/Yp032wzRH+XmMgujfZv+vnfllJqxdowoQLx55FxLLeTeYfwi/xMSjZO2gNven3U/3KeSCd1kUOFS3AOrwZ0UNOXJeW5JQC6Vfd1BavFZ6FAta1fMLu3WFvNB+FqeHUaU3ya7rmtxJnzk29DeSqXgGNmVSywBS4NajI5jJIKAA6UhNJlsg8CHYwUOKf5ej8OoQCkbadUxXygAfxCfW2YBbujtI+PoyejRFxWUjYFWO5LeTI62UMyqfOEiqugoYjNxmQZla2s4YHVuqIC34R85FQlg9pKQBsDCCBxswggUDoAMCAQICEzMAAABCs21EHGjyqKYAAAAAAEIwDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMB4XDTE4MDQyMDE2NDI0NFoXDTIxMDQyMDE2NDI0NFowgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOZ2KM9Pq1YCOiqWOivmHjUtkMgznTMP/Mr2YfzZeIIJySg1F4WxFZc4jagGHHNof9NRT+GGnktWsXkZuH1DzQEG4Ps1ln8+4vhbDglqu5ymDnd6RmsyoD+8xfc8bBIvE5o6R+ES4/GVD5TqNsOrWbwETaIZVbmTulJLoTS1WSsSjowmbc+sHqZiY8BNJNThUEmXSjuHqkQKKshuiFWYEqOTitp71mBLyH1wN7/jThRzGpolOeFusRNJdb8sEqvNzEN9Qh+Kp6ndzrnjE+t8ixXW3lShyyOOZqQMwsQn9q9T0v7Q69GuojBTFBOHKwigcCHr4xahuN+ZYMk0xGg+sm3Uj7I9mrWTSTiIRMZNIWq3sFg4+rFg48NYfRlXUpONmL7vXq6v1pIU99d2MXQ6uUrnUr1/n5ZiHGCeFcvWwqO8BYHdcTlrSOkayfFp7W9oCk9QO4Xy0h9cQRedRo2kvdTHxIuJS70Hdv6oePPF2ZFaLucUzzwsR4/XMAVKY8Vsm950omsSSOImsMtzavUdQM+wZFxvHTRqVDkF3quPdME0bCZOWB4hQJmd+o2clw+1mpwPu0/M92nA9FJg7MGPxkFaYW7g26jSqUJZ9AcX+Xa5TSIeqMZt3cRVjMTx0T/v73Sv8TpalqIQ5Fde1+hFK07sOAm3TwgzvlVJnbYgp0/rAgMBAAGjggGCMIIBfjASBgkrBgEEAYI3FQEEBQIDAgACMCMGCSsGAQQBgjcVAgQWBBSbJnDhuc3nQXuKuACsPflEbwjbozAdBgNVHQ4EFgQUSdgHVEWGlyUe1llxEsNym3Q4TegwEQYDVR0gBAowCDAGBgRVHSAAMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFA9Tyz8WYSX+YIkd07l86JCts5TRMFcGA1UdHwRQME4wTKBKoEiGRmh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcmwwWwYIKwYBBQUHAQEETzBNMEsGCCsGAQUFBzAChj9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAIa2oa6kvuIHCNfz7anlL0W9tOCt8gQNkxOGRK3yliQIelNQahDJojyEFlHQ2BcHL5oZit3WeSDoYddhojx6YzJIWwfGwtVqgc0JFDKJJ2ZXRYMRsuy01Hn25xob+zRMS6VmV1axQn6uwOSMcgYmzoroh6edjPKu7qXcpt6LmhF2qFvLySA7wBCwfI/rR5/PX6I7a07Av7PpbY6/+2ujd8m1H3hwMrb4Hq3z6gcq62zJ3nDXUbC0Bp6Jt2kV9f0rEFpDK9oxE2qrGBUf8c3O2XirHOgAjRyWjWWtVms+MP8qBIA1NSLrBmToEWVP3sEkQZWMkoZWo4rYEJZpX7UIgdDc9zYNakgTCJqPhqn8AE1sgSSnpqAdMkkP41rTlFCv2ig2QVzDerjGfEv+uPDnlAT0kucbBJxHHvUC4aqUxaTSa0sy2bZ6NWFx8/u0gW8JahzxYvvvZL8SfwaA9P4ETb8pH1jw+6N/LfM2zJrNKhf5hjKa0VDOXUpkYq60OqVVnWJ6oJaSIWNkZKfzPnl/UHA8Bh4qfVrhc9H5PExPhhB9WVTsjf4r+OOVuolJldThcWQqljiPjk5rultr63G5xLyFpxNi4BCrcNQBJFB5wKgOWOyjQTVWTmh2ESaeqZ2aWBjftFHlxJ/qYc7WOGJV0+cHGkB/dvFxmKnv6tuWexiMMYIVUTCCFU0CAQEwgaQwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMwITMwADLuPSoXzWpsoKVgACAAMu4zANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKwYBBAGCNwoBMC8GCSqGSIb3DQEJBDEiBCAS0d3bw2YOODvKFr0S4e3BDnaDcZXUKeBO77yvkWzVojBIBgorBgEEAYI3AgEMMTowOKAegBwATQBpAGMAcgBvAHMAbwBmAHQAIABDAG8AcgBwoRaAFGh0dHA6Ly9NaWNyb3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIBABoap3Y+2k+zFz2cCmkc8xxHnpIygLsUSRMXeXdjPVcYx3o5cPLIixnL6p8+LIrlIagPg23mzTEmnjZaO4aaexk+3XojlHj22w/bEigEDnKyWt5bHeS0UNHJbxEFYRfd84IP1+mSH4c4+GuU9p3LsAMh6wN03MYrGmczUOnlP6YlxHNQbQxnV0sl14yOE5ni9oT4y+l+SllvbV3/Jhwpov68aoP/2MazqxR4QyGfSxhCPJ4UuDHU7IrpnTxGBTL1/oUU8ED0FxyDoH/Sc5OhTLInFqbZaVzm5Mpr12wYUBL4nE5h0Kf6BCKdgM8a+Ti3wMUsBoC79ff3jE9U/xwSneOhghLlMIIS4QYKKwYBBAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQghPy22lwuCYESw8jYhb4F9ZDPJ1LPgSSZgJDkyXYzVt4CBlv98KtAoBgTMjAxODExMzAwMTA1MTkuMTM4WjAEgAIB9KCB0KSBzTCByjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RDA4Mi00QkZELUVFQkExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAOIYOHtm6erB2AAAAAAA4jANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0xODA4MjMyMDI3MDNaFw0xOTExMjMyMDI3MDNaMIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEMDgyLTRCRkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgc2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKirA72FF3NCLW5mfLO/D0EZ5Ycs00oiMSissXLB6WF9GNdP78QzFwAypxW/+qZSczqaHbDH8hlbxkzf3DiYgAdpQjnGkLujwKtWSaP29/lVf7jFqHy9v6eH+LdOi0LvtrPRW34MyCvpxZyOW4H1h3PkxCBL5Ra21sDqgcVL1me0osw8QTURXmI4LyeLdTH3CcI2AgNDXTjsFBf3QsO+JYyAOYWrTcLnywVN6DrigmgrDJk5w+wR4VrHfl2T9PRZbZ+UDt13wwyB9d6IURuzV8lHsAVfF8t9S0aGVPmkQ3c2waOhHpsp6VEM+T5D2Ph8xJX1r82z67WRlmGcOP2NWC0CAwEAAaOCARswggEXMB0GA1UdDgQWBBSJPpD6BsP2p+crDJL232voEtLxezAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQARQHu7ISeBuJSHKuDRI04704cH0B7BYzeEIrD15awviMRcYIfIOHpvGzZOWQgP2Hm0Rr7kvTUu1VrSSaQ7i1gPWdhqMmw5WBnSS5bxeMhhx9UsASeE84vUu82NeZapGSjH38YAb4WT+TtiTkcoI59rA+CTCq108ttIxVfZcr3id76OETIH0HvhlnxOOWjwGy4ul6Za5RoTLG/oo2rrGmVi3FwrNWGezYLBODuEsjzG36lCRtBKC2ZAHfbOz5wtkUHbqh79mUKocjP4r3qxf5TN87yf6g1uTx+J8pdnAi5iHt+ZtangWqnVTE8PoIREWhBVlGFfQdkELUx2Or90aAqWMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQwODItNEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBzZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQByQCUheEOevaI9Zc/3QGrkX42iC6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA36ppYDAiGA8yMDE4MTEyOTIxMzQyNFoYDzIwMTgxMTMwMjEzNDI0WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDfqmlgAgEAMAoCAQACAitfAgH/MAcCAQACAhGtMAoCBQDfq7rgAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbAXXPR9wy4NA0892GGqetaZF+pNClpGcfEpSuHABaZ4Gzr1nY1nmrhexTtr/U6omHALRWzkQwthk0cy+mnEHXyOZGmoEEpgrLgK3AAP5NbK/XbtHQRyZJQyhZScFbOyQycoE8QQalSVOhWxk/bbBMQaQiYVMIexNd/T0KgaDDUMxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAOIYOHtm6erB2AAAAAAA4jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCr9IiSbx6s8MLdxldRG49+4h6CbicW8hWXAicI3jNmhDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIN8BpJSmQCGubWwVa4tW+aMveoHMX/nDnVN8fiDOMsrLMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADiGDh7ZunqwdgAAAAAAOIwIgQgTkOfRvGEZNbr5/hgWclsL4/Q7SOZihE/U0lz2wEMIGcwDQYJKoZIhvcNAQELBQAEggEATlxnCfTzFfTMDvK085zlYPVCroKYW6gKFYnbAhNmrNzcxqALKmIYXpFU7B6HH/vYzkUfCyXpf5tsyEWu0oTySOjyAZ9+2vdaG8nEgjOp0L737lcitgusIjpWtta3Ik0b+mzffnvyjrgTSuKDDni3mxGfvJU77k1Ctempma4H2FJso6Bur0PRH99vIYDu4lHigOSLbeyjR5CiDciBwEVUSA0FxhoFNX1yfpxz3sukOvkaoTduREIjH5LxUjNI1ZTMK/ZkeETI8IPRpWVzAc8q7CujErHKo4sdKej/O2cfUTUHplFLVCGGExpJUCg5FH5jVUUFt75ad8503sdGplggVQ== diff --git a/PC/getpathp.c b/PC/getpathp.c index bc85b58abff1..4075463f2260 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -536,10 +536,16 @@ static _PyInitError get_program_full_path(const _PyCoreConfig *core_config, PyCalculatePath *calculate, _PyPathConfig *config) { + const wchar_t *pyvenv_launcher; wchar_t program_full_path[MAXPATHLEN+1]; memset(program_full_path, 0, sizeof(program_full_path)); - if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { + /* The launcher may need to force the executable path to a + * different environment, so override it here. */ + pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__"); + if (pyvenv_launcher && pyvenv_launcher[0]) { + wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher); + } else if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { /* GetModuleFileName should never fail when passed NULL */ return _Py_INIT_ERR("Cannot determine program path"); } diff --git a/PC/icons/pythonwx150.png b/PC/icons/pythonwx150.png new file mode 100644 index 000000000000..4c3eb316739c Binary files /dev/null and b/PC/icons/pythonwx150.png differ diff --git a/PC/icons/pythonwx44.png b/PC/icons/pythonwx44.png new file mode 100644 index 000000000000..e3b32a871f90 Binary files /dev/null and b/PC/icons/pythonwx44.png differ diff --git a/PC/icons/pythonx150.png b/PC/icons/pythonx150.png new file mode 100644 index 000000000000..5f8d30418386 Binary files /dev/null and b/PC/icons/pythonx150.png differ diff --git a/PC/icons/pythonx44.png b/PC/icons/pythonx44.png new file mode 100644 index 000000000000..3881daaef233 Binary files /dev/null and b/PC/icons/pythonx44.png differ diff --git a/PC/icons/pythonx50.png b/PC/icons/pythonx50.png new file mode 100644 index 000000000000..7cc3aecd0242 Binary files /dev/null and b/PC/icons/pythonx50.png differ diff --git a/PC/launcher.c b/PC/launcher.c index 7d666aae4ab1..0242f2639119 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -28,7 +28,7 @@ #define RC_NO_PYTHON 103 #define RC_NO_MEMORY 104 /* - * SCRIPT_WRAPPER is used to choose between two variants of an executable built + * SCRIPT_WRAPPER is used to choose one of the variants of an executable built * from this source file. If not defined, the PEP 397 Python launcher is built; * if defined, a script launcher of the type used by setuptools is built, which * looks for a script name related to the executable name and runs that script @@ -40,6 +40,15 @@ #if defined(SCRIPT_WRAPPER) #define RC_NO_SCRIPT 105 #endif +/* + * VENV_REDIRECT is used to choose the variant that looks for an adjacent or + * one-level-higher pyvenv.cfg, and uses its "home" property to locate and + * launch the original python.exe. + */ +#if defined(VENV_REDIRECT) +#define RC_NO_VENV_CFG 106 +#define RC_BAD_VENV_CFG 107 +#endif /* Just for now - static definition */ @@ -97,7 +106,7 @@ error(int rc, wchar_t * format, ... ) #if !defined(_WINDOWS) fwprintf(stderr, L"%ls\n", message); #else - MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), + MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...", MB_OK); #endif exit(rc); @@ -131,6 +140,17 @@ static wchar_t * get_env(wchar_t * key) return buf; } +#if defined(_DEBUG) +#if defined(_WINDOWS) + +#define PYTHON_EXECUTABLE L"pythonw_d.exe" + +#else + +#define PYTHON_EXECUTABLE L"python_d.exe" + +#endif +#else #if defined(_WINDOWS) #define PYTHON_EXECUTABLE L"pythonw.exe" @@ -139,6 +159,7 @@ static wchar_t * get_env(wchar_t * key) #define PYTHON_EXECUTABLE L"python.exe" +#endif #endif #define MAX_VERSION_SIZE 4 @@ -1118,6 +1139,7 @@ static PYC_MAGIC magic_values[] = { { 3320, 3351, L"3.5" }, { 3360, 3379, L"3.6" }, { 3390, 3399, L"3.7" }, + { 3400, 3409, L"3.8" }, { 0 } }; @@ -1456,6 +1478,87 @@ show_python_list(wchar_t ** argv) return FALSE; /* If this has been called we cannot continue */ } +#if defined(VENV_REDIRECT) + +static int +find_home_value(const char *buffer, const char **start, DWORD *length) +{ + for (const char *s = strstr(buffer, "home"); s; s = strstr(s + 1, "\nhome")) { + if (*s == '\n') { + ++s; + } + for (int i = 4; i > 0 && *s; --i, ++s); + + while (*s && iswspace(*s)) { + ++s; + } + if (*s != L'=') { + continue; + } + + do { + ++s; + } while (*s && iswspace(*s)); + + *start = s; + char *nl = strchr(s, '\n'); + if (nl) { + *length = (DWORD)((ptrdiff_t)nl - (ptrdiff_t)s); + } else { + *length = (DWORD)strlen(s); + } + return 1; + } + return 0; +} +#endif + +static wchar_t * +wcsdup_pad(const wchar_t *s, int padding, int *newlen) +{ + size_t len = wcslen(s); + len += 1 + padding; + wchar_t *r = (wchar_t *)malloc(len * sizeof(wchar_t)); + if (!r) { + return NULL; + } + if (wcscpy_s(r, len, s)) { + free(r); + return NULL; + } + *newlen = len < MAXINT ? (int)len : MAXINT; + return r; +} + +static wchar_t * +get_process_name() +{ + DWORD bufferLen = MAX_PATH; + DWORD len = bufferLen; + wchar_t *r = NULL; + + while (!r) { + r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); + if (!r) { + error(RC_NO_MEMORY, L"out of memory"); + return NULL; + } + len = GetModuleFileNameW(NULL, r, bufferLen); + if (len == 0) { + free(r); + error(0, L"Failed to get module name"); + return NULL; + } else if (len == bufferLen && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(r); + r = NULL; + bufferLen *= 2; + } + } + + return r; +} + static int process(int argc, wchar_t ** argv) { @@ -1463,21 +1566,27 @@ process(int argc, wchar_t ** argv) wchar_t * command; wchar_t * executable; wchar_t * p; + wchar_t * argv0; int rc = 0; - size_t plen; INSTALLED_PYTHON * ip; BOOL valid; DWORD size, attrs; - HRESULT hr; wchar_t message[MSGSIZE]; void * version_data; VS_FIXEDFILEINFO * file_info; UINT block_size; - int index; -#if defined(SCRIPT_WRAPPER) +#if defined(VENV_REDIRECT) + wchar_t * venv_cfg_path; int newlen; +#elif defined(SCRIPT_WRAPPER) wchar_t * newcommand; wchar_t * av[2]; + int newlen; + HRESULT hr; + int index; +#else + HRESULT hr; + int index; #endif setvbuf(stderr, (char *)NULL, _IONBF, 0); @@ -1495,6 +1604,7 @@ process(int argc, wchar_t ** argv) #else debug(L"launcher executable: Console\n"); #endif +#if !defined(VENV_REDIRECT) /* Get the local appdata folder (non-roaming) */ hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appdata_ini_path); @@ -1503,9 +1613,7 @@ process(int argc, wchar_t ** argv) appdata_ini_path[0] = L'\0'; } else { - plen = wcslen(appdata_ini_path); - p = &appdata_ini_path[plen]; - wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE); + wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE); attrs = GetFileAttributesW(appdata_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", appdata_ini_path); @@ -1514,8 +1622,9 @@ process(int argc, wchar_t ** argv) debug(L"Using local configuration file '%ls'\n", appdata_ini_path); } } - plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH); - size = GetFileVersionInfoSizeW(launcher_ini_path, &size); +#endif + argv0 = get_process_name(); + size = GetFileVersionInfoSizeW(argv0, &size); if (size == 0) { winerror(GetLastError(), message, MSGSIZE); debug(L"GetFileVersionInfoSize failed: %ls\n", message); @@ -1523,7 +1632,7 @@ process(int argc, wchar_t ** argv) else { version_data = malloc(size); if (version_data) { - valid = GetFileVersionInfoW(launcher_ini_path, 0, size, + valid = GetFileVersionInfoW(argv0, 0, size, version_data); if (!valid) debug(L"GetFileVersionInfo failed: %X\n", GetLastError()); @@ -1540,15 +1649,51 @@ process(int argc, wchar_t ** argv) free(version_data); } } + +#if defined(VENV_REDIRECT) + /* Allocate some extra space for new filenames */ + venv_cfg_path = wcsdup_pad(argv0, 32, &newlen); + if (!venv_cfg_path) { + error(RC_NO_MEMORY, L"Failed to copy module name"); + } + p = wcsrchr(venv_cfg_path, L'\\'); + + if (p == NULL) { + error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); + } + p[0] = L'\0'; + wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); + attrs = GetFileAttributesW(venv_cfg_path); + if (attrs == INVALID_FILE_ATTRIBUTES) { + debug(L"File '%ls' non-existent\n", venv_cfg_path); + p[0] = '\0'; + p = wcsrchr(venv_cfg_path, L'\\'); + if (p != NULL) { + p[0] = '\0'; + wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); + attrs = GetFileAttributesW(venv_cfg_path); + if (attrs == INVALID_FILE_ATTRIBUTES) { + debug(L"File '%ls' non-existent\n", venv_cfg_path); + error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); + } + } + } + debug(L"Using venv configuration file '%ls'\n", venv_cfg_path); +#else + /* Allocate some extra space for new filenames */ + if (wcscpy_s(launcher_ini_path, MAX_PATH, argv0)) { + error(RC_NO_MEMORY, L"Failed to copy module name"); + } p = wcsrchr(launcher_ini_path, L'\\'); + if (p == NULL) { debug(L"GetModuleFileNameW returned value has no backslash: %ls\n", launcher_ini_path); launcher_ini_path[0] = L'\0'; } else { - wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini", - _TRUNCATE); + p[0] = L'\0'; + wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini"); attrs = GetFileAttributesW(launcher_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", launcher_ini_path); @@ -1557,6 +1702,7 @@ process(int argc, wchar_t ** argv) debug(L"Using global configuration file '%ls'\n", launcher_ini_path); } } +#endif command = skip_me(GetCommandLineW()); debug(L"Called with command line: %ls\n", command); @@ -1592,6 +1738,52 @@ process(int argc, wchar_t ** argv) command = newcommand; valid = FALSE; } +#elif defined(VENV_REDIRECT) + { + FILE *f; + char buffer[4096]; /* 4KB should be enough for anybody */ + char *start; + DWORD len, cch, cch_actual; + size_t cb; + if (_wfopen_s(&f, venv_cfg_path, L"r")) { + error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path); + } + cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]), + sizeof(buffer) / sizeof(buffer[0]), f); + fclose(f); + + if (!find_home_value(buffer, &start, &len)) { + error(RC_BAD_VENV_CFG, L"Cannot find home in '%ls'", + venv_cfg_path); + } + + cch = MultiByteToWideChar(CP_UTF8, 0, start, len, NULL, 0); + if (!cch) { + error(0, L"Cannot determine memory for home path"); + } + cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 1 + 1; /* include sep and null */ + executable = (wchar_t *)malloc(cch * sizeof(wchar_t)); + cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, executable, cch); + if (!cch_actual) { + error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'", + venv_cfg_path); + } + if (executable[cch_actual - 1] != L'\\') { + executable[cch_actual++] = L'\\'; + executable[cch_actual] = L'\0'; + } + if (wcscat_s(executable, cch, PYTHON_EXECUTABLE)) { + error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'", + venv_cfg_path); + } + if (GetFileAttributesW(executable) == INVALID_FILE_ATTRIBUTES) { + error(RC_NO_PYTHON, L"No Python at '%ls'", executable); + } + if (!SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", argv0)) { + error(0, L"Failed to set launcher environment"); + } + valid = 1; + } #else if (argc <= 1) { valid = FALSE; @@ -1599,7 +1791,6 @@ process(int argc, wchar_t ** argv) } else { p = argv[1]; - plen = wcslen(p); if ((argc == 2) && // list version args (!wcsncmp(p, L"-0", wcslen(L"-0")) || !wcsncmp(p, L"--list", wcslen(L"--list")))) diff --git a/PC/layout/__init__.py b/PC/layout/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/PC/layout/__main__.py b/PC/layout/__main__.py new file mode 100644 index 000000000000..f7aa1e6d261f --- /dev/null +++ b/PC/layout/__main__.py @@ -0,0 +1,14 @@ +import sys + +try: + import layout +except ImportError: + # Failed to import our package, which likely means we were started directly + # Add the additional search path needed to locate our module. + from pathlib import Path + + sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +from layout.main import main + +sys.exit(int(main() or 0)) diff --git a/PC/layout/main.py b/PC/layout/main.py new file mode 100644 index 000000000000..82d0536ca920 --- /dev/null +++ b/PC/layout/main.py @@ -0,0 +1,612 @@ +""" +Generates a layout of Python for Windows from a build. + +See python make_layout.py --help for usage. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import argparse +import functools +import os +import re +import shutil +import subprocess +import sys +import tempfile +import zipfile + +from pathlib import Path + +if __name__ == "__main__": + # Started directly, so enable relative imports + __path__ = [str(Path(__file__).resolve().parent)] + +from .support.appxmanifest import * +from .support.catalog import * +from .support.constants import * +from .support.filesets import * +from .support.logging import * +from .support.options import * +from .support.pip import * +from .support.props import * + +BDIST_WININST_FILES_ONLY = FileNameSet("wininst-*", "bdist_wininst.py") +BDIST_WININST_STUB = "PC/layout/support/distutils.command.bdist_wininst.py" + +TEST_PYDS_ONLY = FileStemSet("xxlimited", "_ctypes_test", "_test*") +TEST_DIRS_ONLY = FileNameSet("test", "tests") + +IDLE_DIRS_ONLY = FileNameSet("idlelib") + +TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter") +TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo") +TCLTK_FILES_ONLY = FileNameSet("turtle.py") + +VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip") + +EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext") +EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle") +EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt") +EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*") +EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll") + +REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*") + +LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt") + +PY_FILES = FileSuffixSet(".py") +PYC_FILES = FileSuffixSet(".pyc") +CAT_FILES = FileSuffixSet(".cat") +CDF_FILES = FileSuffixSet(".cdf") + +DATA_DIRS = FileNameSet("data") + +TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser") +TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt") + + +def get_lib_layout(ns): + def _c(f): + if f in EXCLUDE_FROM_LIB: + return False + if f.is_dir(): + if f in TEST_DIRS_ONLY: + return ns.include_tests + if f in TCLTK_DIRS_ONLY: + return ns.include_tcltk + if f in IDLE_DIRS_ONLY: + return ns.include_idle + if f in VENV_DIRS_ONLY: + return ns.include_venv + else: + if f in TCLTK_FILES_ONLY: + return ns.include_tcltk + if f in BDIST_WININST_FILES_ONLY: + return ns.include_bdist_wininst + return True + + for dest, src in rglob(ns.source / "Lib", "**/*", _c): + yield dest, src + + if not ns.include_bdist_wininst: + src = ns.source / BDIST_WININST_STUB + yield Path("distutils/command/bdist_wininst.py"), src + + +def get_tcltk_lib(ns): + if not ns.include_tcltk: + return + + tcl_lib = os.getenv("TCL_LIBRARY") + if not tcl_lib or not os.path.isdir(tcl_lib): + try: + with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f: + tcl_lib = f.read().strip() + except FileNotFoundError: + pass + if not tcl_lib or not os.path.isdir(tcl_lib): + warn("Failed to find TCL_LIBRARY") + return + + for dest, src in rglob(Path(tcl_lib).parent, "**/*"): + yield "tcl/{}".format(dest), src + + +def get_layout(ns): + def in_build(f, dest="", new_name=None): + n, _, x = f.rpartition(".") + n = new_name or n + src = ns.build / f + if ns.debug and src not in REQUIRED_DLLS: + if not src.stem.endswith("_d"): + src = src.parent / (src.stem + "_d" + src.suffix) + if not n.endswith("_d"): + n += "_d" + f = n + "." + x + yield dest + n + "." + x, src + if ns.include_symbols: + pdb = src.with_suffix(".pdb") + if pdb.is_file(): + yield dest + n + ".pdb", pdb + if ns.include_dev: + lib = src.with_suffix(".lib") + if lib.is_file(): + yield "libs/" + n + ".lib", lib + + yield from in_build("python_uwp.exe", new_name="python") + yield from in_build("pythonw_uwp.exe", new_name="pythonw") + + yield from in_build(PYTHON_DLL_NAME) + + if ns.include_launchers: + if ns.include_pip: + yield from in_build("python_uwp.exe", new_name="pip") + if ns.include_idle: + yield from in_build("pythonw_uwp.exe", new_name="idle") + + if ns.include_stable: + yield from in_build(PYTHON_STABLE_DLL_NAME) + + for dest, src in rglob(ns.build, "vcruntime*.dll"): + yield dest, src + + for dest, src in rglob(ns.build, ("*.pyd", "*.dll")): + if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS: + continue + if src in EXCLUDE_FROM_PYDS: + continue + if src in TEST_PYDS_ONLY and not ns.include_tests: + continue + if src in TCLTK_PYDS_ONLY and not ns.include_tcltk: + continue + + yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/") + + if ns.zip_lib: + zip_name = PYTHON_ZIP_NAME + yield zip_name, ns.temp / zip_name + else: + for dest, src in get_lib_layout(ns): + yield "Lib/{}".format(dest), src + + if ns.include_venv: + yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python") + yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw") + + if ns.include_tools: + + def _c(d): + if d.is_dir(): + return d in TOOLS_DIRS + return d in TOOLS_FILES + + for dest, src in rglob(ns.source / "Tools", "**/*", _c): + yield "Tools/{}".format(dest), src + + if ns.include_underpth: + yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME + + if ns.include_dev: + + def _c(d): + if d.is_dir(): + return d.name != "internal" + return True + + for dest, src in rglob(ns.source / "Include", "**/*.h", _c): + yield "include/{}".format(dest), src + src = ns.source / "PC" / "pyconfig.h" + yield "include/pyconfig.h", src + + for dest, src in get_tcltk_lib(ns): + yield dest, src + + if ns.include_pip: + pip_dir = get_pip_dir(ns) + if not pip_dir.is_dir(): + log_warning("Failed to find {} - pip will not be included", pip_dir) + else: + pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}" + for dest, src in rglob(pip_dir, "**/*"): + if src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB: + continue + yield pkg_root.format(dest), src + + if ns.include_chm: + for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME): + yield "Doc/{}".format(dest), src + + if ns.include_html_doc: + for dest, src in rglob(ns.doc_build / "html", "**/*"): + yield "Doc/html/{}".format(dest), src + + if ns.include_props: + for dest, src in get_props_layout(ns): + yield dest, src + + for dest, src in get_appx_layout(ns): + yield dest, src + + if ns.include_cat: + if ns.flat_dlls: + yield ns.include_cat.name, ns.include_cat + else: + yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat + + +def _compile_one_py(src, dest, name, optimize): + import py_compile + + if dest is not None: + dest = str(dest) + + try: + return Path( + py_compile.compile( + str(src), + dest, + str(name), + doraise=True, + optimize=optimize, + invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, + ) + ) + except py_compile.PyCompileError: + log_warning("Failed to compile {}", src) + return None + + +def _py_temp_compile(src, ns, dest_dir=None): + 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") + return _compile_one_py(src, dest.with_suffix(".pyc"), dest, optimize=2) + + +def _write_to_zip(zf, dest, src, ns): + pyc = _py_temp_compile(src, ns) + if pyc: + try: + zf.write(str(pyc), dest.with_suffix(".pyc")) + finally: + try: + pyc.unlink() + except: + log_exception("Failed to delete {}", pyc) + return + + if src in LIB2TO3_GRAMMAR_FILES: + from lib2to3.pgen2.driver import load_grammar + + tmp = ns.temp / src.name + try: + shutil.copy(src, tmp) + load_grammar(str(tmp)) + for f in ns.temp.glob(src.stem + "*.pickle"): + zf.write(str(f), str(dest.parent / f.name)) + try: + f.unlink() + except: + log_exception("Failed to delete {}", f) + except: + log_exception("Failed to compile {}", src) + finally: + try: + tmp.unlink() + except: + log_exception("Failed to delete {}", tmp) + + zf.write(str(src), str(dest)) + + +def generate_source_files(ns): + if ns.zip_lib: + zip_name = PYTHON_ZIP_NAME + zip_path = ns.temp / zip_name + if zip_path.is_file(): + zip_path.unlink() + elif zip_path.is_dir(): + log_error( + "Cannot create zip file because a directory exists by the same name" + ) + return + log_info("Generating {} in {}", zip_name, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: + for dest, src in get_lib_layout(ns): + _write_to_zip(zf, dest, src, ns) + + if ns.include_underpth: + log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f: + if ns.zip_lib: + print(PYTHON_ZIP_NAME, file=f) + if ns.include_pip: + print("packages", file=f) + else: + print("Lib", file=f) + print("Lib/site-packages", file=f) + if not ns.flat_dlls: + print("DLLs", file=f) + print(".", file=f) + print(file=f) + print("# Uncomment to run site.main() automatically", file=f) + print("#import site", file=f) + + if ns.include_appxmanifest: + log_info("Generating AppxManifest.xml in {}", ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + + with open(ns.temp / "AppxManifest.xml", "wb") as f: + f.write(get_appxmanifest(ns)) + + with open(ns.temp / "_resources.xml", "wb") as f: + f.write(get_resources_xml(ns)) + + if ns.include_pip: + pip_dir = get_pip_dir(ns) + if not (pip_dir / "pip").is_dir(): + log_info("Extracting pip to {}", pip_dir) + pip_dir.mkdir(parents=True, exist_ok=True) + extract_pip_files(ns) + + if ns.include_props: + log_info("Generating {} in {}", PYTHON_PROPS_NAME, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / PYTHON_PROPS_NAME, "wb") as f: + f.write(get_props(ns)) + + +def _create_zip_file(ns): + if not ns.zip: + return None + + if ns.zip.is_file(): + try: + ns.zip.unlink() + except OSError: + log_exception("Unable to remove {}", ns.zip) + sys.exit(8) + elif ns.zip.is_dir(): + log_error("Cannot create ZIP file because {} is a directory", ns.zip) + sys.exit(8) + + ns.zip.parent.mkdir(parents=True, exist_ok=True) + return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED) + + +def copy_files(files, ns): + if ns.copy: + ns.copy.mkdir(parents=True, exist_ok=True) + + try: + total = len(files) + except TypeError: + total = None + count = 0 + + zip_file = _create_zip_file(ns) + try: + need_compile = [] + in_catalog = [] + + for dest, src in files: + count += 1 + if count % 10 == 0: + if total: + log_info("Processed {:>4} of {} files", count, total) + else: + log_info("Processed {} files", count) + log_debug("Processing {!s}", src) + + if ( + ns.precompile + and src in PY_FILES + and src not in EXCLUDE_FROM_COMPILE + and src.parent not in DATA_DIRS + and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib")) + ): + if ns.copy: + need_compile.append((dest, ns.copy / dest)) + else: + (ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(src, ns.temp / "Lib" / dest) + need_compile.append((dest, ns.temp / "Lib" / dest)) + + if src not in EXCLUDE_FROM_CATALOG: + in_catalog.append((src.name, src)) + + if ns.copy: + log_debug("Copy {} -> {}", src, ns.copy / dest) + (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True) + try: + shutil.copy2(src, ns.copy / dest) + except shutil.SameFileError: + pass + + if ns.zip: + log_debug("Zip {} into {}", src, ns.zip) + zip_file.write(src, str(dest)) + + if need_compile: + for dest, src in need_compile: + compiled = [ + _compile_one_py(src, None, dest, optimize=0), + _compile_one_py(src, None, dest, optimize=1), + _compile_one_py(src, None, dest, optimize=2), + ] + for c in compiled: + if not c: + continue + cdest = Path(dest).parent / Path(c).relative_to(src.parent) + if ns.zip: + log_debug("Zip {} into {}", c, ns.zip) + zip_file.write(c, str(cdest)) + in_catalog.append((cdest.name, cdest)) + + if ns.catalog: + # Just write out the CDF now. Compilation and signing is + # an extra step + log_info("Generating {}", ns.catalog) + ns.catalog.parent.mkdir(parents=True, exist_ok=True) + write_catalog(ns.catalog, in_catalog) + + finally: + if zip_file: + zip_file.close() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-v", help="Increase verbosity", action="count") + parser.add_argument( + "-s", + "--source", + metavar="dir", + help="The directory containing the repository root", + type=Path, + default=None, + ) + parser.add_argument( + "-b", "--build", metavar="dir", help="Specify the build directory", type=Path + ) + parser.add_argument( + "--doc-build", + metavar="dir", + help="Specify the docs build directory", + type=Path, + default=None, + ) + parser.add_argument( + "--copy", + metavar="directory", + help="The name of the directory to copy an extracted layout to", + type=Path, + default=None, + ) + parser.add_argument( + "--zip", + metavar="file", + help="The ZIP file to write all files to", + type=Path, + default=None, + ) + parser.add_argument( + "--catalog", + metavar="file", + help="The CDF file to write catalog entries to", + type=Path, + default=None, + ) + parser.add_argument( + "--log", + metavar="file", + help="Write all operations to the specified file", + type=Path, + default=None, + ) + parser.add_argument( + "-t", + "--temp", + metavar="file", + help="A temporary working directory", + type=Path, + default=None, + ) + parser.add_argument( + "-d", "--debug", help="Include debug build", action="store_true" + ) + parser.add_argument( + "-p", + "--precompile", + help="Include .pyc files instead of .py", + action="store_true", + ) + parser.add_argument( + "-z", "--zip-lib", help="Include library in a ZIP file", action="store_true" + ) + parser.add_argument( + "--flat-dlls", help="Does not create a DLLs directory", action="store_true" + ) + parser.add_argument( + "-a", + "--include-all", + help="Include all optional components", + action="store_true", + ) + parser.add_argument( + "--include-cat", + metavar="file", + help="Specify the catalog file to include", + type=Path, + default=None, + ) + for opt, help in get_argparse_options(): + parser.add_argument(opt, help=help, action="store_true") + + ns = parser.parse_args() + update_presets(ns) + + ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent) + ns.build = ns.build or Path(sys.executable).parent + ns.temp = ns.temp or Path(tempfile.mkdtemp()) + ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build") + if not ns.source.is_absolute(): + ns.source = (Path.cwd() / ns.source).resolve() + if not ns.build.is_absolute(): + ns.build = (Path.cwd() / ns.build).resolve() + if not ns.temp.is_absolute(): + ns.temp = (Path.cwd() / ns.temp).resolve() + if not ns.doc_build.is_absolute(): + ns.doc_build = (Path.cwd() / ns.doc_build).resolve() + if ns.include_cat and not ns.include_cat.is_absolute(): + ns.include_cat = (Path.cwd() / ns.include_cat).resolve() + + if ns.copy and not ns.copy.is_absolute(): + ns.copy = (Path.cwd() / ns.copy).resolve() + if ns.zip and not ns.zip.is_absolute(): + ns.zip = (Path.cwd() / ns.zip).resolve() + if ns.catalog and not ns.catalog.is_absolute(): + ns.catalog = (Path.cwd() / ns.catalog).resolve() + + configure_logger(ns) + + log_info( + """OPTIONS +Source: {ns.source} +Build: {ns.build} +Temp: {ns.temp} + +Copy to: {ns.copy} +Zip to: {ns.zip} +Catalog: {ns.catalog}""", + ns=ns, + ) + + if ns.include_idle and not ns.include_tcltk: + log_warning("Assuming --include-tcltk to support --include-idle") + ns.include_tcltk = True + + try: + generate_source_files(ns) + files = list(get_layout(ns)) + copy_files(files, ns) + except KeyboardInterrupt: + log_info("Interrupted by Ctrl+C") + return 3 + except SystemExit: + raise + except: + log_exception("Unhandled error") + + if error_was_logged(): + log_error("Errors occurred.") + return 1 + + +if __name__ == "__main__": + sys.exit(int(main() or 0)) diff --git a/PC/layout/support/__init__.py b/PC/layout/support/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py new file mode 100644 index 000000000000..c5dda70c7ef8 --- /dev/null +++ b/PC/layout/support/appxmanifest.py @@ -0,0 +1,487 @@ +""" +File generation for APPX/MSIX manifests. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import collections +import ctypes +import io +import os +import sys + +from pathlib import Path, PureWindowsPath +from xml.etree import ElementTree as ET + +from .constants import * + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + +APPX_DATA = dict( + Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT), + Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3), + Publisher=os.getenv( + "APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B" + ), + DisplayName="Python {}".format(VER_DOT), + Description="The Python {} runtime and console.".format(VER_DOT), + ProcessorArchitecture="x64" if IS_X64 else "x86", +) + +PYTHON_VE_DATA = dict( + DisplayName="Python {}".format(VER_DOT), + Description="Python interactive console", + Square150x150Logo="_resources/pythonx150.png", + Square44x44Logo="_resources/pythonx44.png", + BackgroundColor="transparent", +) + +PYTHONW_VE_DATA = dict( + DisplayName="Python {} (Windowed)".format(VER_DOT), + Description="Python windowed app launcher", + Square150x150Logo="_resources/pythonwx150.png", + Square44x44Logo="_resources/pythonwx44.png", + BackgroundColor="transparent", + AppListEntry="none", +) + +PIP_VE_DATA = dict( + DisplayName="pip (Python {})".format(VER_DOT), + Description="pip package manager for Python {}".format(VER_DOT), + Square150x150Logo="_resources/pythonx150.png", + Square44x44Logo="_resources/pythonx44.png", + BackgroundColor="transparent", + AppListEntry="none", +) + +IDLE_VE_DATA = dict( + DisplayName="IDLE (Python {})".format(VER_DOT), + Description="IDLE editor for Python {}".format(VER_DOT), + Square150x150Logo="_resources/pythonwx150.png", + Square44x44Logo="_resources/pythonwx44.png", + BackgroundColor="transparent", +) + +APPXMANIFEST_NS = { + "": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", + "m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", + "uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10", + "rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities", + "rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4", + "desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4", + "desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6", + "uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3", + "uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4", + "uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5", +} + +APPXMANIFEST_TEMPLATE = """ + + + + + Python Software Foundation + + _resources/pythonx50.png + + + + + + + + + + + + + + +""" + + +RESOURCES_XML_TEMPLATE = r""" + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + + +SCCD_FILENAME = "PC/classicAppCompat.sccd" + +REGISTRY = { + "HKCU\\Software\\Python\\PythonCore": { + VER_DOT: { + "DisplayName": APPX_DATA["DisplayName"], + "SupportUrl": "https://www.python.org/", + "SysArchitecture": "64bit" if IS_X64 else "32bit", + "SysVersion": VER_DOT, + "Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO), + "InstallPath": { + # I have no idea why the trailing spaces are needed, but they seem to be needed. + "": "[{AppVPackageRoot}][ ]", + "ExecutablePath": "[{AppVPackageRoot}]python.exe[ ]", + "WindowedExecutablePath": "[{AppVPackageRoot}]pythonw.exe[ ]", + }, + "Help": { + "Main Python Documentation": { + "_condition": lambda ns: ns.include_chm, + "": "[{{AppVPackageRoot}}]Doc\\{}[ ]".format( + PYTHON_CHM_NAME + ), + }, + "Local Python Documentation": { + "_condition": lambda ns: ns.include_html_doc, + "": "[{AppVPackageRoot}]Doc\\html\\index.html[ ]", + }, + "Online Python Documentation": { + "": "https://docs.python.org/{}".format(VER_DOT) + }, + }, + "Idle": { + "_condition": lambda ns: ns.include_idle, + "": "[{AppVPackageRoot}]Lib\\idlelib\\idle.pyw[ ]", + }, + } + } +} + + +def get_packagefamilyname(name, publisher_id): + class PACKAGE_ID(ctypes.Structure): + _fields_ = [ + ("reserved", ctypes.c_uint32), + ("processorArchitecture", ctypes.c_uint32), + ("version", ctypes.c_uint64), + ("name", ctypes.c_wchar_p), + ("publisher", ctypes.c_wchar_p), + ("resourceId", ctypes.c_wchar_p), + ("publisherId", ctypes.c_wchar_p), + ] + _pack_ = 4 + + pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None) + result = ctypes.create_unicode_buffer(256) + result_len = ctypes.c_uint32(256) + r = ctypes.windll.kernel32.PackageFamilyNameFromId( + pid, ctypes.byref(result_len), result + ) + if r: + raise OSError(r, "failed to get package family name") + return result.value[: result_len.value] + + +def _fixup_sccd(ns, sccd, new_hash=None): + if not new_hash: + return sccd + + NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd") + with open(sccd, "rb") as f: + xml = ET.parse(f) + + pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"]) + + ae = xml.find("s:AuthorizedEntities", NS) + ae.clear() + + e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity")) + e.set("AppPackageFamilyName", pfn) + e.set("CertificateSignatureHash", new_hash) + + for e in xml.findall("s:Catalog", NS): + e.text = "FFFF" + + sccd = ns.temp / sccd.name + sccd.parent.mkdir(parents=True, exist_ok=True) + with open(sccd, "wb") as f: + xml.write(f, encoding="utf-8") + + return sccd + + + at public +def get_appx_layout(ns): + if not ns.include_appxmanifest: + return + + yield "AppxManifest.xml", ns.temp / "AppxManifest.xml" + yield "_resources.xml", ns.temp / "_resources.xml" + icons = ns.source / "PC" / "icons" + yield "_resources/pythonx44.png", icons / "pythonx44.png" + yield "_resources/pythonx44$targetsize-44_altform-unplated.png", icons / "pythonx44.png" + yield "_resources/pythonx50.png", icons / "pythonx50.png" + yield "_resources/pythonx50$targetsize-50_altform-unplated.png", icons / "pythonx50.png" + yield "_resources/pythonx150.png", icons / "pythonx150.png" + yield "_resources/pythonx150$targetsize-150_altform-unplated.png", icons / "pythonx150.png" + yield "_resources/pythonwx44.png", icons / "pythonwx44.png" + yield "_resources/pythonwx44$targetsize-44_altform-unplated.png", icons / "pythonwx44.png" + yield "_resources/pythonwx150.png", icons / "pythonwx150.png" + yield "_resources/pythonwx150$targetsize-150_altform-unplated.png", icons / "pythonwx150.png" + sccd = ns.source / SCCD_FILENAME + if sccd.is_file(): + # This should only be set for side-loading purposes. + sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256")) + yield sccd.name, sccd + + +def find_or_add(xml, element, attr=None, always_add=False): + if always_add: + e = None + else: + q = element + if attr: + q += "[@{}='{}']".format(*attr) + e = xml.find(q, APPXMANIFEST_NS) + if e is None: + prefix, _, name = element.partition(":") + name = ET.QName(APPXMANIFEST_NS[prefix or ""], name) + e = ET.SubElement(xml, name) + if attr: + e.set(*attr) + return e + + +def _get_app(xml, appid): + if appid: + app = xml.find( + "m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS + ) + if app is None: + raise LookupError(appid) + else: + app = xml + return app + + +def add_visual(xml, appid, data): + app = _get_app(xml, appid) + e = find_or_add(app, "uap:VisualElements") + for i in data.items(): + e.set(*i) + return e + + +def add_alias(xml, appid, alias, subsystem="windows"): + app = _get_app(xml, appid) + e = find_or_add(app, "m:Extensions") + e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias")) + e = find_or_add(e, "uap5:AppExecutionAlias") + e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem) + e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias)) + + +def add_file_type(xml, appid, name, suffix, parameters='"%1"'): + app = _get_app(xml, appid) + e = find_or_add(app, "m:Extensions") + e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation")) + e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name)) + e.set("Parameters", parameters) + e = find_or_add(e, "uap:SupportedFileTypes") + if isinstance(suffix, str): + suffix = [suffix] + for s in suffix: + ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s + + +def add_application( + ns, xml, appid, executable, aliases, visual_element, subsystem, file_types +): + node = xml.find("m:Applications", APPXMANIFEST_NS) + suffix = "_d.exe" if ns.debug else ".exe" + app = ET.SubElement( + node, + ET.QName(APPXMANIFEST_NS[""], "Application"), + { + "Id": appid, + "Executable": executable + suffix, + "EntryPoint": "Windows.FullTrustApplication", + ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true", + }, + ) + if visual_element: + add_visual(app, None, visual_element) + for alias in aliases: + add_alias(app, None, alias + suffix, subsystem) + if file_types: + add_file_type(app, None, *file_types) + return app + + +def _get_registry_entries(ns, root="", d=None): + r = root if root else PureWindowsPath("") + if d is None: + d = REGISTRY + for key, value in d.items(): + if key == "_condition": + continue + elif isinstance(value, dict): + cond = value.get("_condition") + if cond and not cond(ns): + continue + fullkey = r + for part in PureWindowsPath(key).parts: + fullkey /= part + if len(fullkey.parts) > 1: + yield str(fullkey), None, None + yield from _get_registry_entries(ns, fullkey, value) + elif len(r.parts) > 1: + yield str(r), key, value + + +def add_registry_entries(ns, xml): + e = find_or_add(xml, "m:Extensions") + e = find_or_add(e, "rescap4:Extension") + e.set("Category", "windows.classicAppCompatKeys") + e.set("EntryPoint", "Windows.FullTrustApplication") + e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys")) + for name, valuename, value in _get_registry_entries(ns): + k = ET.SubElement( + e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey") + ) + k.set("Name", name) + if value: + k.set("ValueName", valuename) + k.set("Value", value) + k.set("ValueType", "REG_SZ") + + +def disable_registry_virtualization(xml): + e = find_or_add(xml, "m:Properties") + e = find_or_add(e, "desktop6:RegistryWriteVirtualization") + e.text = "disabled" + e = find_or_add(xml, "m:Capabilities") + e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources")) + + + at public +def get_appxmanifest(ns): + for k, v in APPXMANIFEST_NS.items(): + ET.register_namespace(k, v) + ET.register_namespace("", APPXMANIFEST_NS["m"]) + + xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE)) + NS = APPXMANIFEST_NS + QN = ET.QName + + node = xml.find("m:Identity", NS) + for k in node.keys(): + value = APPX_DATA.get(k) + if value: + node.set(k, value) + + for node in xml.find("m:Properties", NS): + value = APPX_DATA.get(node.tag.rpartition("}")[2]) + if value: + node.text = value + + winver = sys.getwindowsversion()[:3] + if winver < (10, 0, 17763): + winver = 10, 0, 17763 + find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set( + "MaxVersionTested", "{}.{}.{}.0".format(*winver) + ) + + if winver > (10, 0, 17763): + disable_registry_virtualization(xml) + + app = add_application( + ns, + xml, + "Python", + "python", + ["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)], + PYTHON_VE_DATA, + "console", + ("python.file", [".py"]), + ) + + add_application( + ns, + xml, + "PythonW", + "pythonw", + ["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)], + PYTHONW_VE_DATA, + "windows", + ("python.windowedfile", [".pyw"]), + ) + + if ns.include_pip and ns.include_launchers: + add_application( + ns, + xml, + "Pip", + "pip", + ["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)], + PIP_VE_DATA, + "console", + ("python.wheel", [".whl"], 'install "%1"'), + ) + + if ns.include_idle and ns.include_launchers: + add_application( + ns, + xml, + "Idle", + "idle", + ["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)], + IDLE_VE_DATA, + "windows", + None, + ) + + if (ns.source / SCCD_FILENAME).is_file(): + add_registry_entries(ns, xml) + node = xml.find("m:Capabilities", NS) + node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability")) + node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe") + + buffer = io.BytesIO() + xml.write(buffer, encoding="utf-8", xml_declaration=True) + return buffer.getbuffer() + + + at public +def get_resources_xml(ns): + return RESOURCES_XML_TEMPLATE.encode("utf-8") diff --git a/PC/layout/support/catalog.py b/PC/layout/support/catalog.py new file mode 100644 index 000000000000..43121187ed18 --- /dev/null +++ b/PC/layout/support/catalog.py @@ -0,0 +1,44 @@ +""" +File generation for catalog signing non-binary contents. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import sys + +__all__ = ["PYTHON_CAT_NAME", "PYTHON_CDF_NAME"] + + +def public(f): + __all__.append(f.__name__) + return f + + +PYTHON_CAT_NAME = "python.cat" +PYTHON_CDF_NAME = "python.cdf" + + +CATALOG_TEMPLATE = r"""[CatalogHeader] +Name={target.stem}.cat +ResultDir={target.parent} +PublicVersion=1 +CatalogVersion=2 +HashAlgorithms=SHA256 +PageHashes=false +EncodingType= + +[CatalogFiles] +""" + + +def can_sign(file): + return file.is_file() and file.stat().st_size + + + at public +def write_catalog(target, files): + with target.open("w", encoding="utf-8") as cat: + cat.write(CATALOG_TEMPLATE.format(target=target)) + cat.writelines("{}={}\n".format(n, f) for n, f in files if can_sign(f)) diff --git a/PC/layout/support/constants.py b/PC/layout/support/constants.py new file mode 100644 index 000000000000..88ea410b340e --- /dev/null +++ b/PC/layout/support/constants.py @@ -0,0 +1,28 @@ +""" +Constants for generating the layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import struct +import sys + +VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = struct.pack(">i", sys.hexversion) +VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4 +VER_NAME = {"alpha": "a", "beta": "b", "rc": "rc"}.get( + sys.version_info.releaselevel, "" +) +VER_SERIAL = sys.version_info.serial if VER_NAME else "" +VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR) + +PYTHON_DLL_NAME = "python{}{}.dll".format(VER_MAJOR, VER_MINOR) +PYTHON_STABLE_DLL_NAME = "python{}.dll".format(VER_MAJOR) +PYTHON_ZIP_NAME = "python{}{}.zip".format(VER_MAJOR, VER_MINOR) +PYTHON_PTH_NAME = "python{}{}._pth".format(VER_MAJOR, VER_MINOR) + +PYTHON_CHM_NAME = "python{}{}{}{}{}.chm".format( + VER_MAJOR, VER_MINOR, VER_MICRO, VER_NAME, VER_SERIAL +) + +IS_X64 = sys.maxsize > 2 ** 32 diff --git a/PC/layout/support/distutils.command.bdist_wininst.py b/PC/layout/support/distutils.command.bdist_wininst.py new file mode 100644 index 000000000000..6e9b49fe42df --- /dev/null +++ b/PC/layout/support/distutils.command.bdist_wininst.py @@ -0,0 +1,25 @@ +"""distutils.command.bdist_wininst + +Suppress the 'bdist_wininst' command, while still allowing +setuptools to import it without breaking.""" + +from distutils.core import Command +from distutils.errors import DistutilsPlatformError + + +class bdist_wininst(Command): + description = "create an executable installer for MS Windows" + + # Marker for tests that we have the unsupported bdist_wininst + _unsupported = True + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + raise DistutilsPlatformError( + "bdist_wininst is not supported in this Python distribution" + ) diff --git a/PC/layout/support/filesets.py b/PC/layout/support/filesets.py new file mode 100644 index 000000000000..47f727c05784 --- /dev/null +++ b/PC/layout/support/filesets.py @@ -0,0 +1,100 @@ +""" +File sets and globbing helper for make_layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import os + + +class FileStemSet: + def __init__(self, *patterns): + self._names = set() + self._prefixes = [] + self._suffixes = [] + for p in map(os.path.normcase, patterns): + if p.endswith("*"): + self._prefixes.append(p[:-1]) + elif p.startswith("*"): + self._suffixes.append(p[1:]) + else: + self._names.add(p) + + def _make_name(self, f): + return os.path.normcase(f.stem) + + def __contains__(self, f): + bn = self._make_name(f) + return ( + bn in self._names + or any(map(bn.startswith, self._prefixes)) + or any(map(bn.endswith, self._suffixes)) + ) + + +class FileNameSet(FileStemSet): + def _make_name(self, f): + return os.path.normcase(f.name) + + +class FileSuffixSet: + def __init__(self, *patterns): + self._names = set() + self._prefixes = [] + self._suffixes = [] + for p in map(os.path.normcase, patterns): + if p.startswith("*."): + self._names.add(p[1:]) + elif p.startswith("*"): + self._suffixes.append(p[1:]) + elif p.endswith("*"): + self._prefixes.append(p[:-1]) + elif p.startswith("."): + self._names.add(p) + else: + self._names.add("." + p) + + def _make_name(self, f): + return os.path.normcase(f.suffix) + + def __contains__(self, f): + bn = self._make_name(f) + return ( + bn in self._names + or any(map(bn.startswith, self._prefixes)) + or any(map(bn.endswith, self._suffixes)) + ) + + +def _rglob(root, pattern, condition): + dirs = [root] + recurse = pattern[:3] in {"**/", "**\\"} + if recurse: + pattern = pattern[3:] + + while dirs: + d = dirs.pop(0) + if recurse: + dirs.extend( + filter( + condition, (type(root)(f2) for f2 in os.scandir(d) if f2.is_dir()) + ) + ) + yield from ( + (f.relative_to(root), f) + for f in d.glob(pattern) + if f.is_file() and condition(f) + ) + + +def _return_true(f): + return True + + +def rglob(root, patterns, condition=None): + if isinstance(patterns, tuple): + for p in patterns: + yield from _rglob(root, p, condition or _return_true) + else: + yield from _rglob(root, patterns, condition or _return_true) diff --git a/PC/layout/support/logging.py b/PC/layout/support/logging.py new file mode 100644 index 000000000000..30869b949a1c --- /dev/null +++ b/PC/layout/support/logging.py @@ -0,0 +1,93 @@ +""" +Logging support for make_layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import logging +import sys + +__all__ = [] + +LOG = None +HAS_ERROR = False + + +def public(f): + __all__.append(f.__name__) + return f + + + at public +def configure_logger(ns): + global LOG + if LOG: + return + + LOG = logging.getLogger("make_layout") + LOG.level = logging.DEBUG + + if ns.v: + s_level = max(logging.ERROR - ns.v * 10, logging.DEBUG) + f_level = max(logging.WARNING - ns.v * 10, logging.DEBUG) + else: + s_level = logging.ERROR + f_level = logging.INFO + + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(logging.Formatter("{levelname:8s} {message}", style="{")) + handler.setLevel(s_level) + LOG.addHandler(handler) + + if ns.log: + handler = logging.FileHandler(ns.log, encoding="utf-8", delay=True) + handler.setFormatter( + logging.Formatter("[{asctime}]{levelname:8s}: {message}", style="{") + ) + handler.setLevel(f_level) + LOG.addHandler(handler) + + +class BraceMessage: + def __init__(self, fmt, *args, **kwargs): + self.fmt = fmt + self.args = args + self.kwargs = kwargs + + def __str__(self): + return self.fmt.format(*self.args, **self.kwargs) + + + at public +def log_debug(msg, *args, **kwargs): + return LOG.debug(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_info(msg, *args, **kwargs): + return LOG.info(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_warning(msg, *args, **kwargs): + return LOG.warning(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_error(msg, *args, **kwargs): + global HAS_ERROR + HAS_ERROR = True + return LOG.error(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_exception(msg, *args, **kwargs): + global HAS_ERROR + HAS_ERROR = True + return LOG.exception(BraceMessage(msg, *args, **kwargs)) + + + at public +def error_was_logged(): + return HAS_ERROR diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py new file mode 100644 index 000000000000..76d9e34e1f46 --- /dev/null +++ b/PC/layout/support/options.py @@ -0,0 +1,122 @@ +""" +List of optional components. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + +OPTIONS = { + "stable": {"help": "stable ABI stub"}, + "pip": {"help": "pip"}, + "distutils": {"help": "distutils"}, + "tcltk": {"help": "Tcl, Tk and tkinter"}, + "idle": {"help": "Idle"}, + "tests": {"help": "test suite"}, + "tools": {"help": "tools"}, + "venv": {"help": "venv"}, + "dev": {"help": "headers and libs"}, + "symbols": {"help": "symbols"}, + "bdist-wininst": {"help": "bdist_wininst support"}, + "underpth": {"help": "a python._pth file", "not-in-all": True}, + "launchers": {"help": "specific launchers"}, + "appxmanifest": {"help": "an appxmanifest"}, + "props": {"help": "a python.props file"}, + "chm": {"help": "the CHM documentation"}, + "html-doc": {"help": "the HTML documentation"}, +} + + +PRESETS = { + "appx": { + "help": "APPX package", + "options": [ + "stable", + "pip", + "distutils", + "tcltk", + "idle", + "venv", + "dev", + "launchers", + "appxmanifest", + # XXX: Disabled for now "precompile", + ], + }, + "nuget": { + "help": "nuget package", + "options": ["stable", "pip", "distutils", "dev", "props"], + }, + "default": { + "help": "development kit package", + "options": [ + "stable", + "pip", + "distutils", + "tcltk", + "idle", + "tests", + "tools", + "venv", + "dev", + "symbols", + "bdist-wininst", + "chm", + ], + }, + "embed": { + "help": "embeddable package", + "options": ["stable", "zip-lib", "flat-dlls", "underpth", "precompile"], + }, +} + + + at public +def get_argparse_options(): + for opt, info in OPTIONS.items(): + help = "When specified, includes {}".format(info["help"]) + if info.get("not-in-all"): + help = "{}. Not affected by --include-all".format(help) + + yield "--include-{}".format(opt), help + + for opt, info in PRESETS.items(): + help = "When specified, includes default options for {}".format(info["help"]) + yield "--preset-{}".format(opt), help + + +def ns_get(ns, key, default=False): + return getattr(ns, key.replace("-", "_"), default) + + +def ns_set(ns, key, value=True): + k1 = key.replace("-", "_") + k2 = "include_{}".format(k1) + if hasattr(ns, k2): + setattr(ns, k2, value) + elif hasattr(ns, k1): + setattr(ns, k1, value) + else: + raise AttributeError("no argument named '{}'".format(k1)) + + + at public +def update_presets(ns): + for preset, info in PRESETS.items(): + if ns_get(ns, "preset-{}".format(preset)): + for opt in info["options"]: + ns_set(ns, opt) + + if ns.include_all: + for opt in OPTIONS: + if OPTIONS[opt].get("not-in-all"): + continue + ns_set(ns, opt) diff --git a/PC/layout/support/pip.py b/PC/layout/support/pip.py new file mode 100644 index 000000000000..369a923ce139 --- /dev/null +++ b/PC/layout/support/pip.py @@ -0,0 +1,79 @@ +""" +Extraction and file list generation for pip. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import os +import shutil +import subprocess +import sys + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + + at public +def get_pip_dir(ns): + if ns.copy: + if ns.zip_lib: + return ns.copy / "packages" + return ns.copy / "Lib" / "site-packages" + else: + return ns.temp / "packages" + + + at public +def extract_pip_files(ns): + dest = get_pip_dir(ns) + dest.mkdir(parents=True, exist_ok=True) + + src = ns.source / "Lib" / "ensurepip" / "_bundled" + + ns.temp.mkdir(parents=True, exist_ok=True) + wheels = [shutil.copy(whl, ns.temp) for whl in src.glob("*.whl")] + search_path = os.pathsep.join(wheels) + if os.environ.get("PYTHONPATH"): + search_path += ";" + os.environ["PYTHONPATH"] + + env = os.environ.copy() + env["PYTHONPATH"] = search_path + + output = subprocess.check_output( + [ + sys.executable, + "-m", + "pip", + "--no-color", + "install", + "pip", + "setuptools", + "--upgrade", + "--target", + str(dest), + "--no-index", + "--no-cache-dir", + "-f", + str(src), + "--only-binary", + ":all:", + ], + env=env, + ) + + try: + shutil.rmtree(dest / "bin") + except OSError: + pass + + for file in wheels: + try: + os.remove(file) + except OSError: + pass diff --git a/PC/layout/support/props.py b/PC/layout/support/props.py new file mode 100644 index 000000000000..3a047d215058 --- /dev/null +++ b/PC/layout/support/props.py @@ -0,0 +1,110 @@ +""" +Provides .props file. +""" + +import os + +from .constants import * + +__all__ = ["PYTHON_PROPS_NAME"] + + +def public(f): + __all__.append(f.__name__) + return f + + +PYTHON_PROPS_NAME = "python.props" + +PROPS_DATA = { + "PYTHON_TAG": VER_DOT, + "PYTHON_VERSION": os.getenv("PYTHON_NUSPEC_VERSION"), + "PYTHON_PLATFORM": os.getenv("PYTHON_PROPS_PLATFORM"), + "PYTHON_TARGET": "", +} + +if not PROPS_DATA["PYTHON_VERSION"]: + if VER_NAME: + PROPS_DATA["PYTHON_VERSION"] = "{}.{}-{}{}".format( + VER_DOT, VER_MICRO, VER_NAME, VER_SERIAL + ) + else: + PROPS_DATA["PYTHON_VERSION"] = "{}.{}".format(VER_DOT, VER_MICRO) + +if not PROPS_DATA["PYTHON_PLATFORM"]: + PROPS_DATA["PYTHON_PLATFORM"] = "x64" if IS_X64 else "Win32" + +PROPS_DATA["PYTHON_TARGET"] = "_GetPythonRuntimeFilesDependsOn{}{}_{}".format( + VER_MAJOR, VER_MINOR, PROPS_DATA["PYTHON_PLATFORM"] +) + +PROPS_TEMPLATE = r""" + + + $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python_d.exe") + $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python.exe") + $(PythonHome)\include + $(PythonHome)\libs + {PYTHON_TAG} + {PYTHON_VERSION} + + true + false + false + false + + {PYTHON_TARGET};$(GetPythonRuntimeFilesDependsOn) + + + + + $(PythonInclude);%(AdditionalIncludeDirectories) + MultiThreadedDLL + + + $(PythonLibs);%(AdditionalLibraryDirectories) + + + + + + + + <_PythonRuntimeExe Include="$(PythonHome)\python*.dll" /> + <_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" /> + <_PythonRuntimeExe> + %(Filename)%(Extension) + + <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" /> + <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" /> + <_PythonRuntimeDlls> + DLLs\%(Filename)%(Extension) + + <_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" /> + <_PythonRuntimeLib> + Lib\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + +""" + + + at public +def get_props_layout(ns): + if ns.include_all or ns.include_props: + yield "python.props", ns.temp / "python.props" + + + at public +def get_props(ns): + # TODO: Filter contents of props file according to included/excluded items + props = PROPS_TEMPLATE.format_map(PROPS_DATA) + return props.encode("utf-8") diff --git a/Tools/nuget/python.props b/PC/layout/support/python.props similarity index 100% rename from Tools/nuget/python.props rename to PC/layout/support/python.props diff --git a/PC/pylauncher.rc b/PC/pylauncher.rc index 3da3445f5fc4..92987af7138d 100644 --- a/PC/pylauncher.rc +++ b/PC/pylauncher.rc @@ -7,6 +7,11 @@ #include 1 RT_MANIFEST "python.manifest" +#if defined(PY_ICON) +1 ICON DISCARDABLE "icons\python.ico" +#elif defined(PYW_ICON) +1 ICON DISCARDABLE "icons\pythonw.ico" +#else 1 ICON DISCARDABLE "icons\launcher.ico" 2 ICON DISCARDABLE "icons\py.ico" 3 ICON DISCARDABLE "icons\pyc.ico" @@ -14,6 +19,7 @@ 5 ICON DISCARDABLE "icons\python.ico" 6 ICON DISCARDABLE "icons\pythonw.ico" 7 ICON DISCARDABLE "icons\setup.ico" +#endif ///////////////////////////////////////////////////////////////////////////// // diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp new file mode 100644 index 000000000000..1658d05994bb --- /dev/null +++ b/PC/python_uwp.cpp @@ -0,0 +1,226 @@ +/* Main program when embedded in a UWP application on Windows */ + +#include "Python.h" +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#ifdef PYTHONW +#ifdef _DEBUG +const wchar_t *PROGNAME = L"pythonw_d.exe"; +#else +const wchar_t *PROGNAME = L"pythonw.exe"; +#endif +#else +#ifdef _DEBUG +const wchar_t *PROGNAME = L"python_d.exe"; +#else +const wchar_t *PROGNAME = L"python.exe"; +#endif +#endif + +static void +set_user_base() +{ + wchar_t envBuffer[2048]; + try { + const auto appData = winrt::Windows::Storage::ApplicationData::Current(); + if (appData) { + const auto localCache = appData.LocalCacheFolder(); + if (localCache) { + auto path = localCache.Path(); + if (!path.empty() && + !wcscpy_s(envBuffer, path.c_str()) && + !wcscat_s(envBuffer, L"\\local-packages") + ) { + _wputenv_s(L"PYTHONUSERBASE", envBuffer); + } + } + } + } catch (...) { + } +} + +static const wchar_t * +get_argv0(const wchar_t *argv0) +{ + winrt::hstring installPath; + const wchar_t *launcherPath; + wchar_t *buffer; + size_t len; + + launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__"); + if (launcherPath && launcherPath[0]) { + len = wcslen(launcherPath) + 1; + buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); + if (!buffer) { + Py_FatalError("out of memory"); + return NULL; + } + if (wcscpy_s(buffer, len, launcherPath)) { + Py_FatalError("failed to copy to buffer"); + return NULL; + } + return buffer; + } + + try { + const auto package = winrt::Windows::ApplicationModel::Package::Current(); + if (package) { + const auto install = package.InstalledLocation(); + if (install) { + installPath = install.Path(); + } + } + } + catch (...) { + } + + if (!installPath.empty()) { + len = installPath.size() + wcslen(PROGNAME) + 2; + } else { + len = wcslen(argv0) + wcslen(PROGNAME) + 1; + } + + buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); + if (!buffer) { + Py_FatalError("out of memory"); + return NULL; + } + + if (!installPath.empty()) { + if (wcscpy_s(buffer, len, installPath.c_str())) { + Py_FatalError("failed to copy to buffer"); + return NULL; + } + if (wcscat_s(buffer, len, L"\\")) { + Py_FatalError("failed to concatenate backslash"); + return NULL; + } + } else { + if (wcscpy_s(buffer, len, argv0)) { + Py_FatalError("failed to copy argv[0]"); + return NULL; + } + + wchar_t *name = wcsrchr(buffer, L'\\'); + if (name) { + name[1] = L'\0'; + } else { + buffer[0] = L'\0'; + } + } + + if (wcscat_s(buffer, len, PROGNAME)) { + Py_FatalError("failed to concatenate program name"); + return NULL; + } + + return buffer; +} + +static wchar_t * +get_process_name() +{ + DWORD bufferLen = MAX_PATH; + DWORD len = bufferLen; + wchar_t *r = NULL; + + while (!r) { + r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); + if (!r) { + Py_FatalError("out of memory"); + return NULL; + } + len = GetModuleFileNameW(NULL, r, bufferLen); + if (len == 0) { + free((void *)r); + return NULL; + } else if (len == bufferLen && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(r); + r = NULL; + bufferLen *= 2; + } + } + + return r; +} + +int +wmain(int argc, wchar_t **argv) +{ + const wchar_t **new_argv; + int new_argc; + const wchar_t *exeName; + + new_argc = argc; + new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2)); + if (new_argv == NULL) { + Py_FatalError("out of memory"); + return -1; + } + + exeName = get_process_name(); + + new_argv[0] = get_argv0(exeName ? exeName : argv[0]); + for (int i = 1; i < argc; ++i) { + new_argv[i] = argv[i]; + } + + set_user_base(); + + if (exeName) { + const wchar_t *p = wcsrchr(exeName, L'\\'); + if (p) { + const wchar_t *moduleName = NULL; + if (*p++ == L'\\') { + if (wcsnicmp(p, L"pip", 3) == 0) { + moduleName = L"pip"; + _wputenv_s(L"PIP_USER", L"true"); + } + else if (wcsnicmp(p, L"idle", 4) == 0) { + moduleName = L"idlelib"; + } + } + + if (moduleName) { + new_argc += 2; + for (int i = argc; i >= 1; --i) { + new_argv[i + 2] = new_argv[i]; + } + new_argv[1] = L"-m"; + new_argv[2] = moduleName; + } + } + } + + /* Override program_full_path from here so that + sys.executable is set correctly. */ + _Py_SetProgramFullPath(new_argv[0]); + + int result = Py_Main(new_argc, (wchar_t **)new_argv); + + free((void *)exeName); + free((void *)new_argv); + + return result; +} + +#ifdef PYTHONW + +int WINAPI wWinMain( + HINSTANCE hInstance, /* handle to current instance */ + HINSTANCE hPrevInstance, /* handle to previous instance */ + LPWSTR lpCmdLine, /* pointer to command line */ + int nCmdShow /* show state of window */ +) +{ + return wmain(__argc, __wargv); +} + +#endif diff --git a/PC/store_info.txt b/PC/store_info.txt new file mode 100644 index 000000000000..e252692b73cc --- /dev/null +++ b/PC/store_info.txt @@ -0,0 +1,146 @@ +# Overview + +NOTE: This file requires more content. + +Since Python 3.7.2, releases have been made through the Microsoft Store +to allow easy installation on Windows 10.0.17763.0 and later. + +# Building + +To build the store package, the PC/layout script should be used. +Execute the directory with the build of Python to package, and pass +"-h" for full command-line options. + +To sideload test builds, you will need a local certificate. +Instructions are available at +https://docs.microsoft.com/windows/uwp/packaging/create-certificate-package-signing. + +After exporting your certificate, you will need the subject name and +SHA256 hash. The `certutil -dump ` command will display this +information. + +To build for sideloading, use these commands in PowerShell: + +``` +$env:APPX_DATA_PUBLISHER= +$env:APPX_DATA_SHA256= +$env:SigningCertificateFile= + +python PC/layout --copy --include-appxmanifest +Tools/msi/make_appx.ps1 python.msix -sign + +Add-AppxPackage python.msix +``` + +(Note that only the last command requires PowerShell, and the others +can be used from Command Prompt. You can also double-click to install +the final package.) + +To build for publishing to the Store, use these commands: + +``` +$env:APPX_DATA_PUBLISHER = $null +$env:APPX_DATA_SHA256 = $null + +python PC/layout --copy --preset-appxmanifest --precompile +Tools/msi/make_appx.ps1 python.msix +``` + +Note that this package cannot be installed locally. It may only be +added to a submission for the store. + + +# Submission Metadata + +This file contains the text that we use to fill out the store listing +for the Microsoft Store. It needs to be entered manually when creating +a new submission via the dashboard at +https://partner.microsoft.com/dashboard. + +We keep it here for convenience and to allow it to be updated via pull +requests. + +## Title + +Python 3.7 + +## Short Title + +Python + +## Description + +Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python?s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms. + +The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python Web site, https://www.python.org/, and may be freely distributed. The same site also contains distributions of and pointers to many free third party Python modules, programs and tools, and additional documentation. + +The Python interpreter is easily extended with new functions and data types implemented in C or C++ (or other languages callable from C). Python is also suitable as an extension language for customizable applications. + +## ShortDescription + +The Python 3.7 interpreter and runtime. + +## Copyright Trademark Information + +(c) Python Software Foundation + +## Additional License Terms + +Visit https://docs.python.org/3.7/license.html for latest license terms. + +PSF LICENSE AGREEMENT FOR PYTHON 3.7 + +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.7 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.7 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright ? 2001-2018 Python Software Foundation; All Rights + Reserved" are retained in Python 3.7 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.7 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.7. + +4. PSF is making Python 3.7 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.7 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.7, Licensee agrees + to be bound by the terms and conditions of this License Agreement. + +## Features + +* Easy to install Python runtime +* Supported by core CPython team +* Find Python, Pip and Idle on PATH + +## Search Terms + +* Python +* Scripting +* Interpreter + diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj index 95e3cd50eca5..bd61c0d4f689 100644 --- a/PCbuild/_tkinter.vcxproj +++ b/PCbuild/_tkinter.vcxproj @@ -95,4 +95,10 @@ + + + + + + \ No newline at end of file diff --git a/PCbuild/find_msbuild.bat b/PCbuild/find_msbuild.bat index 57512a01927e..a2810f09c45e 100644 --- a/PCbuild/find_msbuild.bat +++ b/PCbuild/find_msbuild.bat @@ -29,6 +29,16 @@ @where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc" @if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found + at rem VS 2017 and later provide vswhere.exe, which can be used + at if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere + at set _Py_MSBuild_Root= + at for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest') DO @(set _Py_MSBuild_Root=%%i\MSBuild) + at if not defined _Py_MSBuild_Root goto :skip_vswhere + at for %%j in (Current 15.0) DO @if exist "%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe" (set MSBUILD="%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe") + at set _Py_MSBuild_Root= + at if defined MSBUILD @if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio installation) & goto :found +:skip_vswhere + @rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there. @reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul @if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @( diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index 9e103e12103f..6bf1667e39f8 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -52,6 +52,8 @@ + + @@ -70,6 +72,7 @@ + diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index 59b3861ed406..c212d9f8f32c 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -93,6 +93,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_uwp", "python_uwp.vcxproj", "{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvlauncher", "venvlauncher.vcxproj", "{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvwlauncher", "venvwlauncher.vcxproj", "{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythonw_uwp", "pythonw_uwp.vcxproj", "{AB603547-1E2A-45B3-9E09-B04596006393}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -693,6 +701,70 @@ Global {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.Build.0 = Debug|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.ActiveCfg = Debug|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.Build.0 = Debug|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.ActiveCfg = Release|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.Build.0 = Release|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.ActiveCfg = Release|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.Build.0 = Release|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.ActiveCfg = Debug|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.Build.0 = Debug|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.ActiveCfg = Debug|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.Build.0 = Debug|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.ActiveCfg = Release|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.Build.0 = Release|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.ActiveCfg = Release|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.Build.0 = Release|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.ActiveCfg = Debug|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.Build.0 = Debug|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.ActiveCfg = Debug|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.Build.0 = Debug|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.ActiveCfg = Release|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.Build.0 = Release|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.ActiveCfg = Release|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.Build.0 = Release|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.ActiveCfg = Debug|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.Build.0 = Debug|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.ActiveCfg = Debug|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.Build.0 = Debug|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.ActiveCfg = Release|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.Build.0 = Release|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.ActiveCfg = Release|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PCbuild/python.props b/PCbuild/python.props index 09f11d3bba8c..6dbb503b3243 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -77,7 +77,8 @@ --> <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) - 10.0.17134.0 + 10.0.17763.0 + 10.0.17134.0 10.0.16299.0 10.0.15063.0 10.0.14393.0 diff --git a/PCbuild/python_uwp.vcxproj b/PCbuild/python_uwp.vcxproj new file mode 100644 index 000000000000..af187dd4df30 --- /dev/null +++ b/PCbuild/python_uwp.vcxproj @@ -0,0 +1,86 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF} + + + + + Application + false + Unicode + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + %(PreprocessorDefinitions) + /EHsc /std:c++17 %(AdditionalOptions) + + + windowsapp.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} + false + + + + + + diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index d19b5f5acf89..2ee88225b62c 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -460,4 +460,19 @@ + + + $(VCInstallDir)\Redist\MSVC\$(VCToolsRedistVersion)\ + $(VCRedistDir)x86\ + $(VCRedistDir)$(Platform)\ + + + + + + + + + + diff --git a/PCbuild/pythonw_uwp.vcxproj b/PCbuild/pythonw_uwp.vcxproj new file mode 100644 index 000000000000..79e105877fbe --- /dev/null +++ b/PCbuild/pythonw_uwp.vcxproj @@ -0,0 +1,86 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {AB603547-1E2A-45B3-9E09-B04596006393} + + + + + Application + false + Unicode + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + PYTHONW;%(PreprocessorDefinitions) + /EHsc /std:c++17 %(AdditionalOptions) + + + windowsapp.lib;%(AdditionalDependencies) + Windows + + + + + + + + + + + + + + {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} + false + + + + + + diff --git a/PCbuild/venvlauncher.vcxproj b/PCbuild/venvlauncher.vcxproj new file mode 100644 index 000000000000..295b36304733 --- /dev/null +++ b/PCbuild/venvlauncher.vcxproj @@ -0,0 +1,85 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D} + venvlauncher + venvlauncher + false + + + + + Application + MultiByte + + + + + + ClCompile + + + + + + + + + _CONSOLE;VENV_REDIRECT;%(PreprocessorDefinitions) + MultiThreaded + + + PY_ICON;%(PreprocessorDefinitions) + + + version.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + diff --git a/PCbuild/venvwlauncher.vcxproj b/PCbuild/venvwlauncher.vcxproj new file mode 100644 index 000000000000..e7ba25da41eb --- /dev/null +++ b/PCbuild/venvwlauncher.vcxproj @@ -0,0 +1,85 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D} + venvwlauncher + venvwlauncher + false + + + + + Application + MultiByte + + + + + + ClCompile + + + + + + + + + _WINDOWS;VENV_REDIRECT;%(PreprocessorDefinitions) + MultiThreaded + + + PYW_ICON;%(PreprocessorDefinitions) + + + version.lib;%(AdditionalDependencies) + Windows + + + + + + + + + + + + + + + diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat index 4178981195ee..45e189b537f6 100644 --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -37,6 +37,7 @@ set BUILDX64= set TARGET=Rebuild set TESTTARGETDIR= set PGO=-m test -q --pgo +set BUILDMSI=1 set BUILDNUGET=1 set BUILDZIP=1 @@ -61,6 +62,7 @@ if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts +if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 @@ -174,10 +176,12 @@ if "%OUTDIR_PLAT%" EQU "win32" ( ) set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI% -%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true -if errorlevel 1 exit /B -%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false -if errorlevel 1 exit /B +if defined BUILDMSI ( + %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true + if errorlevel 1 exit /B + %MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false + if errorlevel 1 exit /B +) if defined BUILDZIP ( %MSBUILD% "%D%make_zip.proj" /t:Build %BUILDOPTS% %CERTOPTS% /p:OutputPath="%BUILD%en-us" @@ -214,6 +218,7 @@ echo --skip-build (-B) Do not build Python (just do the installers) echo --skip-doc (-D) Do not build documentation echo --pgo Specify PGO command for x64 installers echo --skip-pgo Build x64 installers without using PGO +echo --skip-msi Do not build executable/MSI packages echo --skip-nuget Do not build Nuget packages echo --skip-zip Do not build embeddable package echo --download Specify the full download URL for MSIs diff --git a/Tools/msi/make_appx.ps1 b/Tools/msi/make_appx.ps1 new file mode 100644 index 000000000000..b3f190e07db8 --- /dev/null +++ b/Tools/msi/make_appx.ps1 @@ -0,0 +1,71 @@ +<# +.Synopsis + Compiles and signs an APPX package +.Description + Given the file listing, ensures all the contents are signed + and builds and signs the final package. +.Parameter mapfile + The location on disk of the text mapping file. +.Parameter msix + The path and name to store the APPX/MSIX. +.Parameter sign + When set, signs the APPX/MSIX. Packages to be published to + the store should not be signed. +.Parameter description + Description to embed in the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$layout, + [Parameter(Mandatory=$true)][string]$msix, + [switch]$sign, + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +Set-Alias makeappx (Find-Tool "makeappx.exe") -Scope Script +Set-Alias makepri (Find-Tool "makepri.exe") -Scope Script + +$msixdir = Split-Path $msix -Parent +if ($msixdir) { + $msixdir = (mkdir -Force $msixdir).FullName +} else { + $msixdir = Get-Location +} +$msix = Join-Path $msixdir (Split-Path $msix -Leaf) + +pushd $layout +try { + if (Test-Path resources.pri) { + del resources.pri + } + $name = ([xml](gc AppxManifest.xml)).Package.Identity.Name + makepri new /pr . /mn AppxManifest.xml /in $name /cf _resources.xml /of _resources.pri /mf appx /o + if (-not $? -or -not (Test-Path _resources.map.txt)) { + throw "makepri step failed" + } + $lines = gc _resources.map.txt + $lines | ?{ -not ($_ -match '"_resources[\w\.]+?"') } | Out-File _resources.map.txt -Encoding utf8 + makeappx pack /f _resources.map.txt /m AppxManifest.xml /o /p $msix + if (-not $?) { + throw "makeappx step failed" + } +} finally { + popd +} + +if ($sign) { + Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files $msix + + if (-not $?) { + throw "Package signing failed" + } +} diff --git a/Tools/msi/make_cat.ps1 b/Tools/msi/make_cat.ps1 new file mode 100644 index 000000000000..70741439869a --- /dev/null +++ b/Tools/msi/make_cat.ps1 @@ -0,0 +1,34 @@ +<# +.Synopsis + Compiles and signs a catalog file. +.Description + Given the CDF definition file, builds and signs a catalog. +.Parameter catalog + The path to the catalog definition file to compile and + sign. It is assumed that the .cat file will be the same + name with a new extension. +.Parameter description + The description to add to the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$catalog, + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +Set-Alias MakeCat (Find-Tool "makecat.exe") -Scope Script + +MakeCat $catalog +if (-not $?) { + throw "Catalog compilation failed" +} +Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files @($catalog -replace 'cdf$', 'cat') diff --git a/Tools/msi/make_zip.proj b/Tools/msi/make_zip.proj index 214111734219..125a434e51f4 100644 --- a/Tools/msi/make_zip.proj +++ b/Tools/msi/make_zip.proj @@ -15,11 +15,12 @@ .zip $(OutputPath)\$(TargetName)$(TargetExt) rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)" - "$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py" - $(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -b "$(BuildPath.TrimEnd(`\`))" - set DOC_FILENAME=python$(PythonVersion).chm + "$(PythonExe)" "$(PySourcePath)PC\layout" + $(Arguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" + $(Arguments) -t "$(IntermediateOutputPath)\zip_$(ArchName)" + $(Arguments) --zip "$(TargetPath)" + $(Arguments) --precompile --zip-lib --include-underpth --include-stable --flat-dlls $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) diff --git a/Tools/msi/make_zip.py b/Tools/msi/make_zip.py deleted file mode 100644 index 58f3b15ef852..000000000000 --- a/Tools/msi/make_zip.py +++ /dev/null @@ -1,250 +0,0 @@ -import argparse -import py_compile -import re -import sys -import shutil -import stat -import os -import tempfile - -from itertools import chain -from pathlib import Path -from zipfile import ZipFile, ZIP_DEFLATED - - -TKTCL_RE = re.compile(r'^(_?tk|tcl).+\.(pyd|dll)', re.IGNORECASE) -DEBUG_RE = re.compile(r'_d\.(pyd|dll|exe|pdb|lib)$', re.IGNORECASE) -PYTHON_DLL_RE = re.compile(r'python\d\d?\.dll$', re.IGNORECASE) - -DEBUG_FILES = { - '_ctypes_test', - '_testbuffer', - '_testcapi', - '_testconsole', - '_testimportmultiple', - '_testmultiphase', - 'xxlimited', - 'python3_dstub', -} - -EXCLUDE_FROM_LIBRARY = { - '__pycache__', - 'idlelib', - 'pydoc_data', - 'site-packages', - 'tkinter', - 'turtledemo', -} - -EXCLUDE_FROM_EMBEDDABLE_LIBRARY = { - 'ensurepip', - 'venv', -} - -EXCLUDE_FILE_FROM_LIBRARY = { - 'bdist_wininst.py', -} - -EXCLUDE_FILE_FROM_LIBS = { - 'liblzma', - 'python3stub', -} - -EXCLUDED_FILES = { - 'pyshellext', -} - -def is_not_debug(p): - if DEBUG_RE.search(p.name): - return False - - if TKTCL_RE.search(p.name): - return False - - return p.stem.lower() not in DEBUG_FILES and p.stem.lower() not in EXCLUDED_FILES - -def is_not_debug_or_python(p): - return is_not_debug(p) and not PYTHON_DLL_RE.search(p.name) - -def include_in_lib(p): - name = p.name.lower() - if p.is_dir(): - if name in EXCLUDE_FROM_LIBRARY: - return False - if name == 'test' and p.parts[-2].lower() == 'lib': - return False - if name in {'test', 'tests'} and p.parts[-3].lower() == 'lib': - return False - return True - - if name in EXCLUDE_FILE_FROM_LIBRARY: - return False - - suffix = p.suffix.lower() - return suffix not in {'.pyc', '.pyo', '.exe'} - -def include_in_embeddable_lib(p): - if p.is_dir() and p.name.lower() in EXCLUDE_FROM_EMBEDDABLE_LIBRARY: - return False - - return include_in_lib(p) - -def include_in_libs(p): - if not is_not_debug(p): - return False - - return p.stem.lower() not in EXCLUDE_FILE_FROM_LIBS - -def include_in_tools(p): - if p.is_dir() and p.name.lower() in {'scripts', 'i18n', 'pynche', 'demo', 'parser'}: - return True - - return p.suffix.lower() in {'.py', '.pyw', '.txt'} - -BASE_NAME = 'python{0.major}{0.minor}'.format(sys.version_info) - -FULL_LAYOUT = [ - ('/', '$build', 'python.exe', is_not_debug), - ('/', '$build', 'pythonw.exe', is_not_debug), - ('/', '$build', 'python{}.dll'.format(sys.version_info.major), is_not_debug), - ('/', '$build', '{}.dll'.format(BASE_NAME), is_not_debug), - ('DLLs/', '$build', '*.pyd', is_not_debug), - ('DLLs/', '$build', '*.dll', is_not_debug_or_python), - ('include/', 'include', '*.h', None), - ('include/', 'PC', 'pyconfig.h', None), - ('Lib/', 'Lib', '**/*', include_in_lib), - ('libs/', '$build', '*.lib', include_in_libs), - ('Tools/', 'Tools', '**/*', include_in_tools), -] - -EMBED_LAYOUT = [ - ('/', '$build', 'python*.exe', is_not_debug), - ('/', '$build', '*.pyd', is_not_debug), - ('/', '$build', '*.dll', is_not_debug), - ('{}.zip'.format(BASE_NAME), 'Lib', '**/*', include_in_embeddable_lib), -] - -if os.getenv('DOC_FILENAME'): - FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None)) -if os.getenv('VCREDIST_PATH'): - FULL_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) - EMBED_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) - -def copy_to_layout(target, rel_sources): - count = 0 - - if target.suffix.lower() == '.zip': - if target.exists(): - target.unlink() - - with ZipFile(str(target), 'w', ZIP_DEFLATED) as f: - with tempfile.TemporaryDirectory() as tmpdir: - for s, rel in rel_sources: - if rel.suffix.lower() == '.py': - pyc = Path(tmpdir) / rel.with_suffix('.pyc').name - try: - py_compile.compile(str(s), str(pyc), str(rel), doraise=True, optimize=2) - except py_compile.PyCompileError: - f.write(str(s), str(rel)) - else: - f.write(str(pyc), str(rel.with_suffix('.pyc'))) - else: - f.write(str(s), str(rel)) - count += 1 - - else: - for s, rel in rel_sources: - dest = target / rel - try: - dest.parent.mkdir(parents=True) - except FileExistsError: - pass - if dest.is_file(): - dest.chmod(stat.S_IWRITE) - shutil.copy(str(s), str(dest)) - if dest.is_file(): - dest.chmod(stat.S_IWRITE) - count += 1 - - return count - -def rglob(root, pattern, condition): - dirs = [root] - recurse = pattern[:3] in {'**/', '**\\'} - while dirs: - d = dirs.pop(0) - for f in d.glob(pattern[3:] if recurse else pattern): - if recurse and f.is_dir() and (not condition or condition(f)): - dirs.append(f) - elif f.is_file() and (not condition or condition(f)): - yield f, f.relative_to(root) - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('-s', '--source', metavar='dir', help='The directory containing the repository root', type=Path) - parser.add_argument('-o', '--out', metavar='file', help='The name of the output archive', type=Path, default=None) - parser.add_argument('-t', '--temp', metavar='dir', help='A directory to temporarily extract files into', type=Path, default=None) - parser.add_argument('-e', '--embed', help='Create an embedding layout', action='store_true', default=False) - parser.add_argument('-b', '--build', help='Specify the build directory', type=Path, default=None) - ns = parser.parse_args() - - source = ns.source or (Path(__file__).resolve().parent.parent.parent) - out = ns.out - build = ns.build or Path(sys.exec_prefix) - assert isinstance(source, Path) - assert not out or isinstance(out, Path) - assert isinstance(build, Path) - - if ns.temp: - temp = ns.temp - delete_temp = False - else: - temp = Path(tempfile.mkdtemp()) - delete_temp = True - - if out: - try: - out.parent.mkdir(parents=True) - except FileExistsError: - pass - try: - temp.mkdir(parents=True) - except FileExistsError: - pass - - layout = EMBED_LAYOUT if ns.embed else FULL_LAYOUT - - try: - for t, s, p, c in layout: - if s == '$build': - fs = build - else: - fs = source / s - files = rglob(fs, p, c) - extra_files = [] - if s == 'Lib' and p == '**/*': - extra_files.append(( - source / 'tools' / 'msi' / 'distutils.command.bdist_wininst.py', - Path('distutils') / 'command' / 'bdist_wininst.py' - )) - copied = copy_to_layout(temp / t.rstrip('/'), chain(files, extra_files)) - print('Copied {} files'.format(copied)) - - if ns.embed: - with open(str(temp / (BASE_NAME + '._pth')), 'w') as f: - print(BASE_NAME + '.zip', file=f) - print('.', file=f) - print('', file=f) - print('# Uncomment to run site.main() automatically', file=f) - print('#import site', file=f) - - if out: - total = copy_to_layout(out, rglob(temp, '**/*', None)) - print('Wrote {} files to {}'.format(total, out)) - finally: - if delete_temp: - shutil.rmtree(temp, True) - - -if __name__ == "__main__": - sys.exit(int(main() or 0)) diff --git a/Tools/msi/sdktools.psm1 b/Tools/msi/sdktools.psm1 new file mode 100644 index 000000000000..81a74d3679d7 --- /dev/null +++ b/Tools/msi/sdktools.psm1 @@ -0,0 +1,43 @@ +function Find-Tool { + param([string]$toolname) + + $kitroot = (gp 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots\').KitsRoot10 + $tool = (gci -r "$kitroot\Bin\*\x64\$toolname" | sort FullName -Desc | select -First 1) + if (-not $tool) { + throw "$toolname is not available" + } + Write-Host "Found $toolname at $($tool.FullName)" + return $tool.FullName +} + +Set-Alias SignTool (Find-Tool "signtool.exe") -Scope Script + +function Sign-File { + param([string]$certname, [string]$certsha1, [string]$certfile, [string]$description, [string[]]$files) + + if (-not $description) { + $description = $env:SigningDescription; + if (-not $description) { + $description = "Python"; + } + } + if (-not $certname) { + $certname = $env:SigningCertificate; + } + if (-not $certfile) { + $certfile = $env:SigningCertificateFile; + } + + foreach ($a in $files) { + if ($certsha1) { + SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } elseif ($certname) { + SignTool sign /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } elseif ($certfile) { + SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } else { + SignTool sign /a /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } + } +} + diff --git a/Tools/msi/sign_build.ps1 b/Tools/msi/sign_build.ps1 new file mode 100644 index 000000000000..6668eb33a2d1 --- /dev/null +++ b/Tools/msi/sign_build.ps1 @@ -0,0 +1,34 @@ +<# +.Synopsis + Recursively signs the contents of a directory. +.Description + Given the file patterns, code signs the contents. +.Parameter root + The root directory to sign. +.Parameter patterns + The file patterns to sign +.Parameter description + The description to add to the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$root, + [string[]]$patterns=@("*.exe", "*.dll", "*.pyd"), + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +pushd $root +try { + Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files (gci -r $patterns) +} finally { + popd +} \ No newline at end of file diff --git a/Tools/nuget/make_pkg.proj b/Tools/nuget/make_pkg.proj index 9843bc97ccdc..e093a6d0bd76 100644 --- a/Tools/nuget/make_pkg.proj +++ b/Tools/nuget/make_pkg.proj @@ -20,25 +20,28 @@ false $(OutputName).$(NuspecVersion) .nupkg - $(IntermediateOutputPath)\nuget_$(ArchName) + $(IntermediateOutputPath)\nuget_$(ArchName)\ - rmdir /q/s "$(IntermediateOutputPath)" + rmdir /q/s "$(IntermediateOutputPath.TrimEnd(`\`))" - "$(PythonExe)" "$(MSBuildThisFileDirectory)\..\msi\make_zip.py" - $(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))" + "$(PythonExe)" "$(PySourcePath)PC\layout" + $(PythonArguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" + $(PythonArguments) -t "$(IntermediateOutputPath)obj" + $(PythonArguments) --copy "$(IntermediateOutputPath)pkg" + $(PythonArguments) --include-dev --include-tools --include-pip --include-stable --include-launcher --include-props - "$(IntermediateOutputPath)\python.exe" -B -c "import sys; sys.path.append(r'$(PySourcePath)\Lib'); import ensurepip; ensurepip._main()" - "$(IntermediateOutputPath)\python.exe" -B -m pip install -U $(Packages) + "$(IntermediateOutputPath)pkg\pip.exe" -B -m pip install -U $(Packages) - "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)" + "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)pkg" "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))" $(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))" $(NugetArguments) -Version "$(NuspecVersion)" $(NugetArguments) -NoPackageAnalysis -NonInteractive - set DOC_FILENAME=python$(PythonVersion).chm $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) + $(Environment)%0D%0Aset PYTHON_NUSPEC_VERSION=$(NuspecVersion) + $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=$(Platform) + $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=Win32 $(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" >nul 2>nul @@ -48,22 +51,7 @@ - - - - - - <_PropsContents>$([System.IO.File]::ReadAllText('python.props')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TAG$$', '$(MajorVersionNumber).$(MinorVersionNumber)')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_VERSION$$', '$(NuspecVersion)')) - <_PropsContents Condition="$(Platform) == 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', 'Win32')) - <_PropsContents Condition="$(Platform) != 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', '$(Platform)')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TARGET$$', '_GetPythonRuntimeFilesDependsOn$(MajorVersionNumber)$(MinorVersionNumber)_$(Platform)')) - <_ExistingContents Condition="Exists('$(IntermediateOutputPath)\python.props')">$([System.IO.File]::ReadAllText('$(IntermediateOutputPath)\python.props')) - - + From webhook-mailer at python.org Fri Dec 7 00:59:46 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 07 Dec 2018 05:59:46 -0000 Subject: [Python-checkins] bpo-35434 Fix wrong issue number in what's new in 3.8 (GH-11012) Message-ID: https://github.com/python/cpython/commit/16501b70826695991b3a151dfc538f010be5c765 commit: 16501b70826695991b3a151dfc538f010be5c765 branch: master author: Mariatta committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-06T21:59:42-08:00 summary: bpo-35434 Fix wrong issue number in what's new in 3.8 (GH-11012) https://bugs.python.org/issue35434 files: M Doc/whatsnew/3.8.rst diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 5492a90aacec..0d0d1ca70e6c 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -275,7 +275,7 @@ Optimizations +26% on Linux, +50% on macOS and +40% on Windows. Also, much less CPU cycles are consumed. See :ref:`shutil-platform-dependent-efficient-copy-operations` section. - (Contributed by Giampaolo Rodola' in :issue:`25427`.) + (Contributed by Giampaolo Rodola' in :issue:`33671`.) * :func:`shutil.copytree` uses :func:`os.scandir` function and all copy functions depending from it use cached :func:`os.stat` values. The speedup From webhook-mailer at python.org Fri Dec 7 01:02:37 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 07 Dec 2018 06:02:37 -0000 Subject: [Python-checkins] [2.7] bpo-33023: Fix NotImplemented to NotImplementedError. (GH-10934). (GH-11001) (GH-11008) Message-ID: https://github.com/python/cpython/commit/324e1790094708538acf2e7795f9c44e3732aaf7 commit: 324e1790094708538acf2e7795f9c44e3732aaf7 branch: 2.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-07T08:02:33+02:00 summary: [2.7] bpo-33023: Fix NotImplemented to NotImplementedError. (GH-10934). (GH-11001) (GH-11008) (cherry picked from commit 42b1d6127bd8595522a78a75166ebb9fba74a6a2) (cherry picked from commit 7a2cf1e7d3bf300e98c702589d405734f4a8fcf8) files: M Lib/ssl.py M Lib/test/test_ssl.py diff --git a/Lib/ssl.py b/Lib/ssl.py index 22d478b56871..087faf95ad99 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -630,8 +630,8 @@ def context(self, ctx): self._sslobj.context = ctx def dup(self): - raise NotImplemented("Can't dup() %s instances" % - self.__class__.__name__) + raise NotImplementedError("Can't dup() %s instances" % + self.__class__.__name__) def _checkClosed(self, msg=None): # raise an exception here if you wish to check for spurious closes diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index dc14e22ad121..e47603170253 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -341,6 +341,7 @@ def test_wrapped_unconnected(self): self.assertRaises(socket.error, ss.recvfrom_into, bytearray(b'x'), 1) self.assertRaises(socket.error, ss.send, b'x') self.assertRaises(socket.error, ss.sendto, b'x', ('0.0.0.0', 0)) + self.assertRaises(NotImplementedError, ss.dup) def test_timeout(self): # Issue #8524: when creating an SSL socket, the timeout of the @@ -2645,6 +2646,7 @@ def _recvfrom_into(): self.assertEqual(s.read(-1, buffer), len(data)) self.assertEqual(buffer, data) + self.assertRaises(NotImplementedError, s.dup) s.write(b"over\n") self.assertRaises(ValueError, s.recv, -1) From webhook-mailer at python.org Fri Dec 7 01:32:25 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Fri, 07 Dec 2018 06:32:25 -0000 Subject: [Python-checkins] bpo-34864: warn if "Prefer tabs when opening documents" set to "Always" (#10464) Message-ID: https://github.com/python/cpython/commit/9ebe8794f003dadfff578a066ea503a3e37ffe1d commit: 9ebe8794f003dadfff578a066ea503a3e37ffe1d branch: master author: Tal Einat committer: Terry Jan Reedy date: 2018-12-07T01:32:21-05:00 summary: bpo-34864: warn if "Prefer tabs when opening documents" set to "Always" (#10464) * bpo-34864: warn if "Prefer tabs when opening documents" set to "Always" * add NEWS entry * address code review comments * address second code review comments * Add entry for idlelib/NEWS.txt. files: A Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst M Lib/idlelib/NEWS.txt M Lib/idlelib/macosx.py M Lib/idlelib/outwin.py M Lib/idlelib/pyshell.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 9a16ece3bbe3..6e11cab58600 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,12 @@ Released on 2019-10-20? ====================================== +bpo-34864: When starting IDLE on MacOS, warn if the system setting +"Prefer tabs when opening documents" is "Always". As previous +documented for this issue, running IDLE with this setting causes +problems. If the setting is changed while IDLE is running, +there will be no warning until IDLE is restarted. + bpo-35213: Where appropriate, use 'macOS' in idlelib. bpo-34864: Document two IDLE on MacOS issues. The System Preferences diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index 8f8484a37015..9be4ed2ec411 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -1,6 +1,8 @@ """ A number of functions that enhance IDLE on macOS. """ +from os.path import expanduser +import plistlib from sys import platform # Used in _init_tk_type, changed by test. import tkinter @@ -79,14 +81,47 @@ def tkVersionWarning(root): patchlevel = root.tk.call('info', 'patchlevel') if patchlevel not in ('8.5.7', '8.5.9'): return False - return (r"WARNING: The version of Tcl/Tk ({0}) in use may" - r" be unstable.\n" - r"Visit http://www.python.org/download/mac/tcltk/" - r" for current information.".format(patchlevel)) + return ("WARNING: The version of Tcl/Tk ({0}) in use may" + " be unstable.\n" + "Visit http://www.python.org/download/mac/tcltk/" + " for current information.".format(patchlevel)) else: return False +def readSystemPreferences(): + """ + Fetch the macOS system preferences. + """ + if platform != 'darwin': + return None + + plist_path = expanduser('~/Library/Preferences/.GlobalPreferences.plist') + try: + with open(plist_path, 'rb') as plist_file: + return plistlib.load(plist_file) + except OSError: + return None + + +def preferTabsPreferenceWarning(): + """ + Warn if "Prefer tabs when opening documents" is set to "Always". + """ + if platform != 'darwin': + return None + + prefs = readSystemPreferences() + if prefs and prefs.get('AppleWindowTabbingMode') == 'always': + return ( + 'WARNING: The system preference "Prefer tabs when opening' + ' documents" is set to "Always". This will cause various problems' + ' with IDLE. For the best experience, change this setting when' + ' running IDLE (via System Preferences -> Dock).' + ) + return None + + ## Fix the menu and related functions. def addOpenEventSupport(root, flist): diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index 4af9f1afaed5..e962142498df 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -109,7 +109,7 @@ def write(self, s, tags=(), mark="insert"): Return: Length of text inserted. """ - if isinstance(s, (bytes, bytes)): + if isinstance(s, bytes): s = s.decode(iomenu.encoding, "replace") self.text.insert(mark, s, tags) self.text.see(mark) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 47eef4399ce6..81a97ef6d6bc 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -38,6 +38,7 @@ import re import socket import subprocess +from textwrap import TextWrapper import threading import time import tokenize @@ -1273,6 +1274,14 @@ def showprompt(self): self.set_line_and_column() self.io.reset_undo() + def show_warning(self, msg): + width = self.interp.tkconsole.width + wrapper = TextWrapper(width=width, tabsize=8, expand_tabs=True) + wrapped_msg = '\n'.join(wrapper.wrap(msg)) + if not wrapped_msg.endswith('\n'): + wrapped_msg += '\n' + self.per.bottom.insert("iomark linestart", wrapped_msg, "stderr") + def resetoutput(self): source = self.text.get("iomark", "end-1c") if self.history: @@ -1541,12 +1550,20 @@ def main(): shell.interp.execfile(script) elif shell: # If there is a shell window and no cmd or script in progress, - # check for problematic OS X Tk versions and print a warning - # message in the IDLE shell window; this is less intrusive - # than always opening a separate window. + # check for problematic issues and print warning message(s) in + # the IDLE shell window; this is less intrusive than always + # opening a separate window. + + # Warn if using a problematic OS X Tk version. tkversionwarning = macosx.tkVersionWarning(root) if tkversionwarning: - shell.interp.runcommand("print('%s')" % tkversionwarning) + shell.show_warning(tkversionwarning) + + # Warn if the "Prefer tabs when opening documents" system + # preference is set to "Always". + prefer_tabs_preference_warning = macosx.preferTabsPreferenceWarning() + if prefer_tabs_preference_warning: + shell.show_warning(prefer_tabs_preference_warning) while flist.inversedict: # keep IDLE running while files are open. root.mainloop() diff --git a/Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst b/Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst new file mode 100644 index 000000000000..8d2b61d599ff --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst @@ -0,0 +1,2 @@ +On macOS, warn if the system preference "Prefer tabs when opening documents" +is set to "Always". \ No newline at end of file From webhook-mailer at python.org Fri Dec 7 01:51:13 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 07 Dec 2018 06:51:13 -0000 Subject: [Python-checkins] bpo-34864: warn if "Prefer tabs when opening documents" set to "Always" (GH-10464) Message-ID: https://github.com/python/cpython/commit/2db190bb356d00422087e1286637887efb8d97c5 commit: 2db190bb356d00422087e1286637887efb8d97c5 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T22:51:10-08:00 summary: bpo-34864: warn if "Prefer tabs when opening documents" set to "Always" (GH-10464) * bpo-34864: warn if "Prefer tabs when opening documents" set to "Always" * add NEWS entry * address code review comments * address second code review comments * Add entry for idlelib/NEWS.txt. (cherry picked from commit 9ebe8794f003dadfff578a066ea503a3e37ffe1d) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst M Lib/idlelib/NEWS.txt M Lib/idlelib/macosx.py M Lib/idlelib/outwin.py M Lib/idlelib/pyshell.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index e34b439319dd..b93769e73bbb 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,12 @@ Released on 2018-07-31? ====================================== +bpo-34864: When starting IDLE on MacOS, warn if the system setting +"Prefer tabs when opening documents" is "Always". As previous +documented for this issue, running IDLE with this setting causes +problems. If the setting is changed while IDLE is running, +there will be no warning until IDLE is restarted. + bpo-35213: Where appropriate, use 'macOS' in idlelib. bpo-34864: Document two IDLE on MacOS issues. The System Preferences diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index 8f8484a37015..9be4ed2ec411 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -1,6 +1,8 @@ """ A number of functions that enhance IDLE on macOS. """ +from os.path import expanduser +import plistlib from sys import platform # Used in _init_tk_type, changed by test. import tkinter @@ -79,14 +81,47 @@ def tkVersionWarning(root): patchlevel = root.tk.call('info', 'patchlevel') if patchlevel not in ('8.5.7', '8.5.9'): return False - return (r"WARNING: The version of Tcl/Tk ({0}) in use may" - r" be unstable.\n" - r"Visit http://www.python.org/download/mac/tcltk/" - r" for current information.".format(patchlevel)) + return ("WARNING: The version of Tcl/Tk ({0}) in use may" + " be unstable.\n" + "Visit http://www.python.org/download/mac/tcltk/" + " for current information.".format(patchlevel)) else: return False +def readSystemPreferences(): + """ + Fetch the macOS system preferences. + """ + if platform != 'darwin': + return None + + plist_path = expanduser('~/Library/Preferences/.GlobalPreferences.plist') + try: + with open(plist_path, 'rb') as plist_file: + return plistlib.load(plist_file) + except OSError: + return None + + +def preferTabsPreferenceWarning(): + """ + Warn if "Prefer tabs when opening documents" is set to "Always". + """ + if platform != 'darwin': + return None + + prefs = readSystemPreferences() + if prefs and prefs.get('AppleWindowTabbingMode') == 'always': + return ( + 'WARNING: The system preference "Prefer tabs when opening' + ' documents" is set to "Always". This will cause various problems' + ' with IDLE. For the best experience, change this setting when' + ' running IDLE (via System Preferences -> Dock).' + ) + return None + + ## Fix the menu and related functions. def addOpenEventSupport(root, flist): diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index 4af9f1afaed5..e962142498df 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -109,7 +109,7 @@ def write(self, s, tags=(), mark="insert"): Return: Length of text inserted. """ - if isinstance(s, (bytes, bytes)): + if isinstance(s, bytes): s = s.decode(iomenu.encoding, "replace") self.text.insert(mark, s, tags) self.text.see(mark) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 47eef4399ce6..81a97ef6d6bc 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -38,6 +38,7 @@ import re import socket import subprocess +from textwrap import TextWrapper import threading import time import tokenize @@ -1273,6 +1274,14 @@ def showprompt(self): self.set_line_and_column() self.io.reset_undo() + def show_warning(self, msg): + width = self.interp.tkconsole.width + wrapper = TextWrapper(width=width, tabsize=8, expand_tabs=True) + wrapped_msg = '\n'.join(wrapper.wrap(msg)) + if not wrapped_msg.endswith('\n'): + wrapped_msg += '\n' + self.per.bottom.insert("iomark linestart", wrapped_msg, "stderr") + def resetoutput(self): source = self.text.get("iomark", "end-1c") if self.history: @@ -1541,12 +1550,20 @@ def main(): shell.interp.execfile(script) elif shell: # If there is a shell window and no cmd or script in progress, - # check for problematic OS X Tk versions and print a warning - # message in the IDLE shell window; this is less intrusive - # than always opening a separate window. + # check for problematic issues and print warning message(s) in + # the IDLE shell window; this is less intrusive than always + # opening a separate window. + + # Warn if using a problematic OS X Tk version. tkversionwarning = macosx.tkVersionWarning(root) if tkversionwarning: - shell.interp.runcommand("print('%s')" % tkversionwarning) + shell.show_warning(tkversionwarning) + + # Warn if the "Prefer tabs when opening documents" system + # preference is set to "Always". + prefer_tabs_preference_warning = macosx.preferTabsPreferenceWarning() + if prefer_tabs_preference_warning: + shell.show_warning(prefer_tabs_preference_warning) while flist.inversedict: # keep IDLE running while files are open. root.mainloop() diff --git a/Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst b/Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst new file mode 100644 index 000000000000..8d2b61d599ff --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst @@ -0,0 +1,2 @@ +On macOS, warn if the system preference "Prefer tabs when opening documents" +is set to "Always". \ No newline at end of file From webhook-mailer at python.org Fri Dec 7 02:03:36 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 07 Dec 2018 07:03:36 -0000 Subject: [Python-checkins] bpo-34864: warn if "Prefer tabs when opening documents" set to "Always" (GH-10464) Message-ID: https://github.com/python/cpython/commit/10665544a97b6616898faafc12ac9d06505d0690 commit: 10665544a97b6616898faafc12ac9d06505d0690 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-06T23:03:32-08:00 summary: bpo-34864: warn if "Prefer tabs when opening documents" set to "Always" (GH-10464) * bpo-34864: warn if "Prefer tabs when opening documents" set to "Always" * add NEWS entry * address code review comments * address second code review comments * Add entry for idlelib/NEWS.txt. (cherry picked from commit 9ebe8794f003dadfff578a066ea503a3e37ffe1d) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst M Lib/idlelib/NEWS.txt M Lib/idlelib/macosx.py M Lib/idlelib/outwin.py M Lib/idlelib/pyshell.py diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 0868b1f1a3c3..e2951b0e6d7b 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -4,6 +4,12 @@ Released on 2018-12-20? ====================================== +bpo-34864: When starting IDLE on MacOS, warn if the system setting +"Prefer tabs when opening documents" is "Always". As previous +documented for this issue, running IDLE with this setting causes +problems. If the setting is changed while IDLE is running, +there will be no warning until IDLE is restarted. + bpo-35213: Where appropriate, use 'macOS' in idlelib. bpo-34864: Document two IDLE on MacOS issues. The System Preferences diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index 8f8484a37015..9be4ed2ec411 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -1,6 +1,8 @@ """ A number of functions that enhance IDLE on macOS. """ +from os.path import expanduser +import plistlib from sys import platform # Used in _init_tk_type, changed by test. import tkinter @@ -79,14 +81,47 @@ def tkVersionWarning(root): patchlevel = root.tk.call('info', 'patchlevel') if patchlevel not in ('8.5.7', '8.5.9'): return False - return (r"WARNING: The version of Tcl/Tk ({0}) in use may" - r" be unstable.\n" - r"Visit http://www.python.org/download/mac/tcltk/" - r" for current information.".format(patchlevel)) + return ("WARNING: The version of Tcl/Tk ({0}) in use may" + " be unstable.\n" + "Visit http://www.python.org/download/mac/tcltk/" + " for current information.".format(patchlevel)) else: return False +def readSystemPreferences(): + """ + Fetch the macOS system preferences. + """ + if platform != 'darwin': + return None + + plist_path = expanduser('~/Library/Preferences/.GlobalPreferences.plist') + try: + with open(plist_path, 'rb') as plist_file: + return plistlib.load(plist_file) + except OSError: + return None + + +def preferTabsPreferenceWarning(): + """ + Warn if "Prefer tabs when opening documents" is set to "Always". + """ + if platform != 'darwin': + return None + + prefs = readSystemPreferences() + if prefs and prefs.get('AppleWindowTabbingMode') == 'always': + return ( + 'WARNING: The system preference "Prefer tabs when opening' + ' documents" is set to "Always". This will cause various problems' + ' with IDLE. For the best experience, change this setting when' + ' running IDLE (via System Preferences -> Dock).' + ) + return None + + ## Fix the menu and related functions. def addOpenEventSupport(root, flist): diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index 4af9f1afaed5..e962142498df 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -109,7 +109,7 @@ def write(self, s, tags=(), mark="insert"): Return: Length of text inserted. """ - if isinstance(s, (bytes, bytes)): + if isinstance(s, bytes): s = s.decode(iomenu.encoding, "replace") self.text.insert(mark, s, tags) self.text.see(mark) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 47eef4399ce6..81a97ef6d6bc 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -38,6 +38,7 @@ import re import socket import subprocess +from textwrap import TextWrapper import threading import time import tokenize @@ -1273,6 +1274,14 @@ def showprompt(self): self.set_line_and_column() self.io.reset_undo() + def show_warning(self, msg): + width = self.interp.tkconsole.width + wrapper = TextWrapper(width=width, tabsize=8, expand_tabs=True) + wrapped_msg = '\n'.join(wrapper.wrap(msg)) + if not wrapped_msg.endswith('\n'): + wrapped_msg += '\n' + self.per.bottom.insert("iomark linestart", wrapped_msg, "stderr") + def resetoutput(self): source = self.text.get("iomark", "end-1c") if self.history: @@ -1541,12 +1550,20 @@ def main(): shell.interp.execfile(script) elif shell: # If there is a shell window and no cmd or script in progress, - # check for problematic OS X Tk versions and print a warning - # message in the IDLE shell window; this is less intrusive - # than always opening a separate window. + # check for problematic issues and print warning message(s) in + # the IDLE shell window; this is less intrusive than always + # opening a separate window. + + # Warn if using a problematic OS X Tk version. tkversionwarning = macosx.tkVersionWarning(root) if tkversionwarning: - shell.interp.runcommand("print('%s')" % tkversionwarning) + shell.show_warning(tkversionwarning) + + # Warn if the "Prefer tabs when opening documents" system + # preference is set to "Always". + prefer_tabs_preference_warning = macosx.preferTabsPreferenceWarning() + if prefer_tabs_preference_warning: + shell.show_warning(prefer_tabs_preference_warning) while flist.inversedict: # keep IDLE running while files are open. root.mainloop() diff --git a/Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst b/Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst new file mode 100644 index 000000000000..8d2b61d599ff --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-11-11-17-13-50.bpo-34864.cw0PvO.rst @@ -0,0 +1,2 @@ +On macOS, warn if the system preference "Prefer tabs when opening documents" +is set to "Always". \ No newline at end of file From solipsis at pitrou.net Fri Dec 7 04:08:31 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 07 Dec 2018 09:08:31 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=70 Message-ID: <20181207090831.1.C55D4D6245939479@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_fork leaked [0, 0, 44] references, sum=44 test_multiprocessing_fork leaked [0, -1, 20] memory blocks, sum=19 test_multiprocessing_fork leaked [0, 0, 2] file descriptors, sum=2 test_multiprocessing_forkserver leaked [2, 0, -1] memory blocks, sum=1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogxLTjZf', '--timeout', '7200'] From webhook-mailer at python.org Fri Dec 7 05:10:40 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 07 Dec 2018 10:10:40 -0000 Subject: [Python-checkins] bpo-35346, platform: replace os.popen() with subprocess (GH-10786) Message-ID: https://github.com/python/cpython/commit/3a521f0b6167628f975c773b56c7daf8d33d6f40 commit: 3a521f0b6167628f975c773b56c7daf8d33d6f40 branch: master author: Victor Stinner committer: GitHub date: 2018-12-07T11:10:33+01:00 summary: bpo-35346, platform: replace os.popen() with subprocess (GH-10786) Replace os.popen() with subprocess.check_output() in the platform module: * platform.uname() (its _syscmd_ver() helper function) now redirects stderr to DEVNULL. * Remove platform.DEV_NULL. * _syscmd_uname() and _syscmd_file() no longer catch AttributeError. The "except AttributeError:" was only needed in Python 2, when os.popen() was not always available. In Python 3, subprocess.check_output() is always available. files: A Misc/NEWS.d/next/Library/2018-11-29-12-42-13.bpo-35346.OmTY5c.rst M Lib/platform.py M Lib/test/test_platform.py diff --git a/Lib/platform.py b/Lib/platform.py index d8455256bb9a..5f9491819169 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -119,19 +119,6 @@ ### Globals & Constants -# Determine the platform's /dev/null device -try: - DEV_NULL = os.devnull -except AttributeError: - # os.devnull was added in Python 2.4, so emulate it for earlier - # Python versions - if sys.platform in ('dos', 'win32', 'win16'): - # Use the old CP/M NUL as device name - DEV_NULL = 'NUL' - else: - # Standard Unix uses /dev/null - DEV_NULL = '/dev/null' - # Helper for comparing two version number strings. # Based on the description of the PHP's version_compare(): # http://php.net/manual/en/function.version-compare.php @@ -288,16 +275,15 @@ def _syscmd_ver(system='', release='', version='', return system, release, version # Try some common cmd strings + import subprocess for cmd in ('ver', 'command /c ver', 'cmd /c ver'): try: - pipe = os.popen(cmd) - info = pipe.read() - if pipe.close(): - raise OSError('command failed') - # XXX How can I suppress shell errors from being written - # to stderr ? - except OSError as why: - #print 'Command %s failed: %s' % (cmd, why) + info = subprocess.check_output(cmd, + stderr=subprocess.DEVNULL, + text=True, + shell=True) + except (OSError, subprocess.CalledProcessError) as why: + #print('Command %s failed: %s' % (cmd, why)) continue else: break @@ -602,16 +588,15 @@ def _syscmd_uname(option, default=''): if sys.platform in ('dos', 'win32', 'win16'): # XXX Others too ? return default + + import subprocess try: - f = os.popen('uname %s 2> %s' % (option, DEV_NULL)) - except (AttributeError, OSError): - return default - output = f.read().strip() - rc = f.close() - if not output or rc: + output = subprocess.check_output(('uname', option), + stderr=subprocess.DEVNULL, + text=True) + except (OSError, subprocess.CalledProcessError): return default - else: - return output + return (output.strip() or default) def _syscmd_file(target, default=''): @@ -629,17 +614,12 @@ def _syscmd_file(target, default=''): import subprocess target = _follow_symlinks(target) try: - proc = subprocess.Popen(['file', target], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - except (AttributeError, OSError): + output = subprocess.check_output(['file', target], + stderr=subprocess.DEVNULL, + encoding='latin-1') + except (OSError, subprocess.CalledProcessError): return default - output = proc.communicate()[0].decode('latin-1') - rc = proc.wait() - if not output or rc: - return default - else: - return output + return (output or default) ### Information about the used architecture diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index c1a7e3407934..9cf17726d92e 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -222,16 +222,16 @@ def test_mac_ver(self): res = platform.mac_ver() if platform.uname().system == 'Darwin': - # We're on a MacOSX system, check that - # the right version information is returned - fd = os.popen('sw_vers', 'r') - real_ver = None - for ln in fd: - if ln.startswith('ProductVersion:'): - real_ver = ln.strip().split()[-1] + # We are on a macOS system, check that the right version + # information is returned + output = subprocess.check_output(['sw_vers'], text=True) + for line in output.splitlines(): + if line.startswith('ProductVersion:'): + real_ver = line.strip().split()[-1] break - fd.close() - self.assertFalse(real_ver is None) + else: + self.fail(f"failed to parse sw_vers output: {output!r}") + result_list = res[0].split('.') expect_list = real_ver.split('.') len_diff = len(result_list) - len(expect_list) diff --git a/Misc/NEWS.d/next/Library/2018-11-29-12-42-13.bpo-35346.OmTY5c.rst b/Misc/NEWS.d/next/Library/2018-11-29-12-42-13.bpo-35346.OmTY5c.rst new file mode 100644 index 000000000000..f6d28feab78c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-11-29-12-42-13.bpo-35346.OmTY5c.rst @@ -0,0 +1,2 @@ +:func:`platform.uname` now redirects ``stderr`` to :data:`os.devnull` when +running external programs like ``cmd /c ver``. From webhook-mailer at python.org Fri Dec 7 05:11:34 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 07 Dec 2018 10:11:34 -0000 Subject: [Python-checkins] bpo-35436: Add missing PyErr_NoMemory() calls and other minor bug fixes. (GH-11015) Message-ID: https://github.com/python/cpython/commit/4c49da0cb7434c676d70b9ccf38aca82ac0d64a9 commit: 4c49da0cb7434c676d70b9ccf38aca82ac0d64a9 branch: master author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-07T12:11:30+02:00 summary: bpo-35436: Add missing PyErr_NoMemory() calls and other minor bug fixes. (GH-11015) Set MemoryError when appropriate, add missing failure checks, and fix some potential leaks. files: A Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst M Modules/_abc.c M Modules/_ctypes/_ctypes.c M Modules/_ctypes/callbacks.c M Modules/_io/winconsoleio.c M Modules/_multiprocessing/semaphore.c M Modules/_ssl.c M Modules/mathmodule.c M Modules/posixmodule.c M Objects/capsule.c M PC/getpathp.c M PC/launcher.c M Parser/myreadline.c M Parser/tokenizer.c M Python/ast.c M Python/marshal.c M Python/pystrtod.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst new file mode 100644 index 000000000000..542fe93a00eb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst @@ -0,0 +1,2 @@ +Fix various issues with memory allocation error handling. Patch by Zackery +Spytz. diff --git a/Modules/_abc.c b/Modules/_abc.c index 9de199fa143f..36c1757b5fd3 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -728,6 +728,10 @@ subclasscheck_check_registry(_abc_data *impl, PyObject *subclass, // Weakref callback may remove entry from set. // So we take snapshot of registry first. PyObject **copy = PyMem_Malloc(sizeof(PyObject*) * registry_size); + if (copy == NULL) { + PyErr_NoMemory(); + return -1; + } PyObject *key; Py_ssize_t pos = 0; Py_hash_t hash; diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 3debe3ace695..163b3e3b6c78 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -305,8 +305,10 @@ _ctypes_alloc_format_string_for_type(char code, int big_endian) } result = PyMem_Malloc(3); - if (result == NULL) + if (result == NULL) { + PyErr_NoMemory(); return NULL; + } result[0] = big_endian ? '>' : '<'; result[1] = pep_code; @@ -366,8 +368,10 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, if (prefix) prefix_len += strlen(prefix); new_prefix = PyMem_Malloc(prefix_len); - if (new_prefix == NULL) + if (new_prefix == NULL) { + PyErr_NoMemory(); return NULL; + } new_prefix[0] = '\0'; if (prefix) strcpy(new_prefix, prefix); @@ -1899,6 +1903,10 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject #else suffix = PyUnicode_InternFromString("_be"); #endif + if (suffix == NULL) { + Py_DECREF(swapped_args); + return NULL; + } newname = PyUnicode_Concat(name, suffix); if (newname == NULL) { diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 871bc4f49458..c1e9b723aac6 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -310,7 +310,6 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nArgs) p = PyObject_GC_NewVar(CThunkObject, &PyCThunk_Type, nArgs); if (p == NULL) { - PyErr_NoMemory(); return NULL; } diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 824690ff58d6..dd0997a10580 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -816,11 +816,13 @@ _io__WindowsConsoleIO_readall_impl(winconsoleio *self) } bufsize = newsize; - buf = PyMem_Realloc(buf, (bufsize + 1) * sizeof(wchar_t)); - if (!buf) { + wchar_t *tmp = PyMem_Realloc(buf, + (bufsize + 1) * sizeof(wchar_t)); + if (tmp == NULL) { PyMem_Free(buf); return NULL; } + buf = tmp; } subbuf = read_console_w(self->handle, bufsize - len, &n); diff --git a/Modules/_multiprocessing/semaphore.c b/Modules/_multiprocessing/semaphore.c index e15adfbd6b19..bb7219e13070 100644 --- a/Modules/_multiprocessing/semaphore.c +++ b/Modules/_multiprocessing/semaphore.c @@ -449,8 +449,9 @@ semlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!unlink) { name_copy = PyMem_Malloc(strlen(name) + 1); - if (name_copy == NULL) - goto failure; + if (name_copy == NULL) { + return PyErr_NoMemory(); + } strcpy(name_copy, name); } @@ -473,7 +474,9 @@ semlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (handle != SEM_FAILED) SEM_CLOSE(handle); PyMem_Free(name_copy); - _PyMp_SetError(NULL, MP_STANDARD_ERROR); + if (!PyErr_Occurred()) { + _PyMp_SetError(NULL, MP_STANDARD_ERROR); + } return NULL; } diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 85819f5b0509..269f003e0df9 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -911,6 +911,11 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, PySSL_BEGIN_ALLOW_THREADS self->ssl = SSL_new(ctx); PySSL_END_ALLOW_THREADS + if (self->ssl == NULL) { + Py_DECREF(self); + _setSSLError(NULL, 0, __FILE__, __LINE__); + return NULL; + } SSL_set_app_data(self->ssl, self); if (sock) { SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int)); @@ -1240,6 +1245,10 @@ _get_peer_alt_names (X509 *certificate) { /* get a memory buffer */ biobuf = BIO_new(BIO_s_mem()); + if (biobuf == NULL) { + PyErr_SetString(PySSLErrorObject, "failed to allocate BIO"); + return NULL; + } names = (GENERAL_NAMES *)X509_get_ext_d2i( certificate, NID_subject_alt_name, NULL, NULL); @@ -1592,6 +1601,10 @@ _decode_certificate(X509 *certificate) { /* get a memory buffer */ biobuf = BIO_new(BIO_s_mem()); + if (biobuf == NULL) { + PyErr_SetString(PySSLErrorObject, "failed to allocate BIO"); + goto fail0; + } (void) BIO_reset(biobuf); serialNumber = X509_get_serialNumber(certificate); diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index b83befbd3194..d56c91cedc26 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2142,7 +2142,7 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) if (n > NUM_STACK_ELEMS) { diffs = (double *) PyObject_Malloc(n * sizeof(double)); if (diffs == NULL) { - return NULL; + return PyErr_NoMemory(); } } for (i=0 ; i NUM_STACK_ELEMS) { coordinates = (double *) PyObject_Malloc(n * sizeof(double)); - if (coordinates == NULL) - return NULL; + if (coordinates == NULL) { + return PyErr_NoMemory(); + } } for (i=0 ; i bufsiz) { bufsiz += MAXPATHLEN; - buf = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) * sizeof(wchar_t)); - if (!buf) { + wchar_t *tmp = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) * + sizeof(wchar_t)); + if (tmp == NULL) { PyMem_RawFree(wline); goto error; } + buf = tmp; } if (usedsiz) { diff --git a/PC/launcher.c b/PC/launcher.c index 0242f2639119..4c620dab7c09 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -1763,6 +1763,9 @@ process(int argc, wchar_t ** argv) } cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 1 + 1; /* include sep and null */ executable = (wchar_t *)malloc(cch * sizeof(wchar_t)); + if (executable == NULL) { + error(RC_NO_MEMORY, L"A memory allocation failed"); + } cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, executable, cch); if (!cch_actual) { error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'", diff --git a/Parser/myreadline.c b/Parser/myreadline.c index acb4d01fdf92..43e5583b8bcc 100644 --- a/Parser/myreadline.c +++ b/Parser/myreadline.c @@ -153,20 +153,37 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn) wbuf = (wchar_t*)PyMem_RawMalloc(wbuflen * sizeof(wchar_t)); if (wbuf) wcscpy_s(wbuf, wbuflen, wbuf_local); + else { + PyErr_NoMemory(); + goto exit; + } + } + else { + wchar_t *tmp = PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t)); + if (tmp == NULL) { + PyErr_NoMemory(); + goto exit; + } + wbuf = tmp; } - else - wbuf = (wchar_t*)PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t)); } if (wbuf[0] == '\x1a') { buf = PyMem_RawMalloc(1); if (buf) buf[0] = '\0'; + else { + PyErr_NoMemory(); + } goto exit; } u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, NULL, 0, NULL, NULL); buf = PyMem_RawMalloc(u8len + 1); + if (buf == NULL) { + PyErr_NoMemory(); + goto exit; + } u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, buf, u8len, NULL, NULL); buf[u8len] = '\0'; @@ -211,8 +228,12 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) int wlen; wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1, NULL, 0); - if (wlen && - (wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t)))) { + if (wlen) { + wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t)); + if (wbuf == NULL) { + PyErr_NoMemory(); + return NULL; + } wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1, wbuf, wlen); if (wlen) { @@ -236,8 +257,10 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) n = 100; p = (char *)PyMem_RawMalloc(n); - if (p == NULL) + if (p == NULL) { + PyErr_NoMemory(); return NULL; + } fflush(sys_stdout); if (prompt) @@ -314,6 +337,10 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) if (_PyOS_ReadlineLock == NULL) { _PyOS_ReadlineLock = PyThread_allocate_lock(); + if (_PyOS_ReadlineLock == NULL) { + PyErr_SetString(PyExc_MemoryError, "can't allocate lock"); + return NULL; + } } _PyOS_ReadlineTState = _PyThreadState_GET(); @@ -341,8 +368,12 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) len = strlen(rv) + 1; res = PyMem_Malloc(len); - if (res != NULL) + if (res != NULL) { memcpy(res, rv, len); + } + else { + PyErr_NoMemory(); + } PyMem_RawFree(rv); return res; diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index fc75bae53766..d319a4c90a9e 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -953,6 +953,11 @@ tok_nextc(struct tok_state *tok) buflen = PyBytes_GET_SIZE(u); buf = PyBytes_AS_STRING(u); newtok = PyMem_MALLOC(buflen+1); + if (newtok == NULL) { + Py_DECREF(u); + tok->done = E_NOMEM; + return EOF; + } strcpy(newtok, buf); Py_DECREF(u); } diff --git a/Python/ast.c b/Python/ast.c index 24d5843aabd2..8a305a80ffac 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -4097,6 +4097,9 @@ parsenumber(struct compiling *c, const char *s) } /* Create a duplicate without underscores. */ dup = PyMem_Malloc(strlen(s) + 1); + if (dup == NULL) { + return PyErr_NoMemory(); + } end = dup; for (; *s; s++) { if (*s != '_') { @@ -4325,8 +4328,10 @@ fstring_compile_expr(const char *expr_start, const char *expr_end, len = expr_end - expr_start; /* Allocate 3 extra bytes: open paren, close paren, null byte. */ str = PyMem_RawMalloc(len + 3); - if (str == NULL) + if (str == NULL) { + PyErr_NoMemory(); return NULL; + } str[0] = '('; memcpy(str+1, expr_start, len); diff --git a/Python/marshal.c b/Python/marshal.c index 21cdd60c7e13..52932af225a4 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -670,11 +670,12 @@ r_string(Py_ssize_t n, RFILE *p) p->buf_size = n; } else if (p->buf_size < n) { - p->buf = PyMem_REALLOC(p->buf, n); - if (p->buf == NULL) { + char *tmp = PyMem_REALLOC(p->buf, n); + if (tmp == NULL) { PyErr_NoMemory(); return NULL; } + p->buf = tmp; p->buf_size = n; } diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 98aa9ba48bda..02a3fb57805c 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -398,6 +398,9 @@ _Py_string_to_number_with_underscores( } dup = PyMem_Malloc(orig_len + 1); + if (dup == NULL) { + return PyErr_NoMemory(); + } end = dup; prev = '\0'; last = s + orig_len; From webhook-mailer at python.org Fri Dec 7 06:42:15 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 07 Dec 2018 11:42:15 -0000 Subject: [Python-checkins] bpo-22005: Fixed unpickling instances of datetime classes pickled by Python 2. (GH-11017) Message-ID: https://github.com/python/cpython/commit/8452ca15f41061c8a6297d7956df22ab476d4df4 commit: 8452ca15f41061c8a6297d7956df22ab476d4df4 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-07T13:42:10+02:00 summary: bpo-22005: Fixed unpickling instances of datetime classes pickled by Python 2. (GH-11017) encoding='latin1' should be used for successful decoding. files: A Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst M Doc/library/pickle.rst M Lib/datetime.py M Lib/test/datetimetester.py M Modules/_datetimemodule.c diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 4f9d3596b649..5fe49a013bc4 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -243,6 +243,9 @@ process more convenient: *errors* tell pickle how to decode 8-bit string instances pickled by Python 2; these default to 'ASCII' and 'strict', respectively. The *encoding* can be 'bytes' to read these 8-bit string instances as bytes objects. + Using ``encoding='latin1'`` is required for unpickling NumPy arrays and + instances of :class:`~datetime.datetime`, :class:`~datetime.date` and + :class:`~datetime.time` pickled by Python 2. .. function:: loads(bytes_object, \*, fix_imports=True, encoding="ASCII", errors="strict") @@ -260,6 +263,9 @@ process more convenient: *errors* tell pickle how to decode 8-bit string instances pickled by Python 2; these default to 'ASCII' and 'strict', respectively. The *encoding* can be 'bytes' to read these 8-bit string instances as bytes objects. + Using ``encoding='latin1'`` is required for unpickling NumPy arrays and + instances of :class:`~datetime.datetime`, :class:`~datetime.date` and + :class:`~datetime.time` pickled by Python 2. The :mod:`pickle` module defines three exceptions: diff --git a/Lib/datetime.py b/Lib/datetime.py index 292919fd7988..4780b6df8f9b 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -808,9 +808,19 @@ def __new__(cls, year, month=None, day=None): year, month, day (required, base 1) """ - if month is None and isinstance(year, bytes) and len(year) == 4 and \ - 1 <= year[2] <= 12: + if (month is None and + isinstance(year, (bytes, str)) and len(year) == 4 and + 1 <= ord(year[2:3]) <= 12): # Pickle support + if isinstance(year, str): + try: + year = year.encode('latin1') + except UnicodeEncodeError: + # More informative error message. + raise ValueError( + "Failed to encode latin1 string when unpickling " + "a date object. " + "pickle.load(data, encoding='latin1') is assumed.") self = object.__new__(cls) self.__setstate(year) self._hashcode = -1 @@ -1184,8 +1194,18 @@ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold tzinfo (default to None) fold (keyword only, default to zero) """ - if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24: + if (isinstance(hour, (bytes, str)) and len(hour) == 6 and + ord(hour[0:1])&0x7F < 24): # Pickle support + if isinstance(hour, str): + try: + hour = hour.encode('latin1') + except UnicodeEncodeError: + # More informative error message. + raise ValueError( + "Failed to encode latin1 string when unpickling " + "a time object. " + "pickle.load(data, encoding='latin1') is assumed.") self = object.__new__(cls) self.__setstate(hour, minute or None) self._hashcode = -1 @@ -1496,8 +1516,18 @@ class datetime(date): def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0): - if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12: + if (isinstance(year, (bytes, str)) and len(year) == 10 and + 1 <= ord(year[2:3])&0x7F <= 12): # Pickle support + if isinstance(year, str): + try: + year = bytes(year, 'latin1') + except UnicodeEncodeError: + # More informative error message. + raise ValueError( + "Failed to encode latin1 string when unpickling " + "a datetime object. " + "pickle.load(data, encoding='latin1') is assumed.") self = object.__new__(cls) self.__setstate(year, month) self._hashcode = -1 diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 78b123f5b118..2f838c445555 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -38,6 +38,7 @@ import _strptime # +pickle_loads = {pickle.loads, pickle._loads} pickle_choices = [(pickle, pickle, proto) for proto in range(pickle.HIGHEST_PROTOCOL + 1)] @@ -1434,6 +1435,19 @@ def test_pickling(self): self.assertEqual(orig, derived) self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) + def test_compat_unpickle(self): + tests = [ + b"cdatetime\ndate\n(S'\\x07\\xdf\\x0b\\x1b'\ntR.", + b'cdatetime\ndate\n(U\x04\x07\xdf\x0b\x1btR.', + b'\x80\x02cdatetime\ndate\nU\x04\x07\xdf\x0b\x1b\x85R.', + ] + args = 2015, 11, 27 + expected = self.theclass(*args) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + def test_compare(self): t1 = self.theclass(2, 3, 4) t2 = self.theclass(2, 3, 4) @@ -2098,6 +2112,24 @@ def test_pickling_subclass_datetime(self): derived = unpickler.loads(green) self.assertEqual(orig, derived) + def test_compat_unpickle(self): + tests = [ + b'cdatetime\ndatetime\n(' + b"S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x00\\x10\\x00'\ntR.", + + b'cdatetime\ndatetime\n(' + b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00tR.', + + b'\x80\x02cdatetime\ndatetime\n' + b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00\x85R.', + ] + args = 2015, 11, 27, 20, 59, 1, 64**2 + expected = self.theclass(*args) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + def test_more_compare(self): # The test_compare() inherited from TestDate covers the error cases. # We just want to test lexicographic ordering on the members datetime @@ -3069,6 +3101,19 @@ def test_pickling_subclass_time(self): derived = unpickler.loads(green) self.assertEqual(orig, derived) + def test_compat_unpickle(self): + tests = [ + b"cdatetime\ntime\n(S'\\x14;\\x10\\x00\\x10\\x00'\ntR.", + b'cdatetime\ntime\n(U\x06\x14;\x10\x00\x10\x00tR.', + b'\x80\x02cdatetime\ntime\nU\x06\x14;\x10\x00\x10\x00\x85R.', + ] + args = 20, 59, 16, 64**2 + expected = self.theclass(*args) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + def test_bool(self): # time is always True. cls = self.theclass @@ -3441,6 +3486,40 @@ def test_pickling(self): self.assertEqual(derived.tzname(), 'cookie') self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) + def test_compat_unpickle(self): + tests = [ + b"cdatetime\ntime\n(S'\\x05\\x06\\x07\\x01\\xe2@'\n" + b"ctest.datetimetester\nPicklableFixedOffset\n(tR" + b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n" + b"(I-1\nI68400\nI0\ntRs" + b"S'_FixedOffset__dstoffset'\nNs" + b"S'_FixedOffset__name'\nS'cookie'\nsbtR.", + + b'cdatetime\ntime\n(U\x06\x05\x06\x07\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieubtR.', + + b'\x80\x02cdatetime\ntime\nU\x06\x05\x06\x07\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieub\x86R.', + ] + + tinfo = PicklableFixedOffset(-300, 'cookie') + expected = self.theclass(5, 6, 7, 123456, tzinfo=tinfo) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected, repr(data)) + self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) + self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) + self.assertEqual(derived.tzname(), 'cookie') + def test_more_bool(self): # time is always True. cls = self.theclass @@ -3789,6 +3868,43 @@ def test_pickling(self): self.assertEqual(derived.tzname(), 'cookie') self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) + def test_compat_unpickle(self): + tests = [ + b'cdatetime\ndatetime\n' + b"(S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x01\\xe2@'\n" + b'ctest.datetimetester\nPicklableFixedOffset\n(tR' + b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n" + b'(I-1\nI68400\nI0\ntRs' + b"S'_FixedOffset__dstoffset'\nNs" + b"S'_FixedOffset__name'\nS'cookie'\nsbtR.", + + b'cdatetime\ndatetime\n' + b'(U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieubtR.', + + b'\x80\x02cdatetime\ndatetime\n' + b'U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieub\x86R.', + ] + args = 2015, 11, 27, 20, 59, 1, 123456 + tinfo = PicklableFixedOffset(-300, 'cookie') + expected = self.theclass(*args, **{'tzinfo': tinfo}) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) + self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) + self.assertEqual(derived.tzname(), 'cookie') + def test_extreme_hashes(self): # If an attempt is made to hash these via subtracting the offset # then hashing a datetime object, OverflowError results. The diff --git a/Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst b/Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst new file mode 100644 index 000000000000..951098d0a7a3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst @@ -0,0 +1,3 @@ +Implemented unpickling instances of :class:`~datetime.datetime`, +:class:`~datetime.date` and :class:`~datetime.time` pickled by Python 2. +``encoding='latin1'`` should be used for successful decoding. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index eb9c35d02d4e..87a88be6090c 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -2788,31 +2788,60 @@ static PyGetSetDef date_getset[] = { static char *date_kws[] = {"year", "month", "day", NULL}; +static PyObject * +date_from_pickle(PyTypeObject *type, PyObject *state) +{ + PyDateTime_Date *me; + + me = (PyDateTime_Date *) (type->tp_alloc(type, 0)); + if (me != NULL) { + const char *pdata = PyBytes_AS_STRING(state); + memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE); + me->hashcode = -1; + } + return (PyObject *)me; +} + static PyObject * date_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *state; int year; int month; int day; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) == 1) { - state = PyTuple_GET_ITEM(args, 0); - if (PyBytes_Check(state) && - PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE && - MONTH_IS_SANE(PyBytes_AS_STRING(state)[2])) - { - PyDateTime_Date *me; - - me = (PyDateTime_Date *) (type->tp_alloc(type, 0)); - if (me != NULL) { - char *pdata = PyBytes_AS_STRING(state); - memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE); - me->hashcode = -1; + if (PyTuple_GET_SIZE(args) >= 1) { + PyObject *state = PyTuple_GET_ITEM(args, 0); + if (PyBytes_Check(state)) { + if (PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE && + MONTH_IS_SANE(PyBytes_AS_STRING(state)[2])) + { + return date_from_pickle(type, state); + } + } + else if (PyUnicode_Check(state)) { + if (PyUnicode_READY(state)) { + return NULL; + } + if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATE_DATASIZE && + MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2))) + { + state = PyUnicode_AsLatin1String(state); + if (state == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* More informative error message. */ + PyErr_SetString(PyExc_ValueError, + "Failed to encode latin1 string when unpickling " + "a date object. " + "pickle.load(data, encoding='latin1') is assumed."); + } + return NULL; + } + self = date_from_pickle(type, state); + Py_DECREF(state); + return self; } - return (PyObject *)me; } } @@ -3901,11 +3930,43 @@ static PyGetSetDef time_getset[] = { static char *time_kws[] = {"hour", "minute", "second", "microsecond", "tzinfo", "fold", NULL}; +static PyObject * +time_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) +{ + PyDateTime_Time *me; + char aware = (char)(tzinfo != Py_None); + + if (aware && check_tzinfo_subclass(tzinfo) < 0) { + PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); + return NULL; + } + + me = (PyDateTime_Time *) (type->tp_alloc(type, aware)); + if (me != NULL) { + const char *pdata = PyBytes_AS_STRING(state); + + memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE); + me->hashcode = -1; + me->hastzinfo = aware; + if (aware) { + Py_INCREF(tzinfo); + me->tzinfo = tzinfo; + } + if (pdata[0] & (1 << 7)) { + me->data[0] -= 128; + me->fold = 1; + } + else { + me->fold = 0; + } + } + return (PyObject *)me; +} + static PyObject * time_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *state; int hour = 0; int minute = 0; int second = 0; @@ -3914,47 +3975,42 @@ time_new(PyTypeObject *type, PyObject *args, PyObject *kw) int fold = 0; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) >= 1 && - PyTuple_GET_SIZE(args) <= 2) - { - state = PyTuple_GET_ITEM(args, 0); - if (PyBytes_Check(state) && - PyBytes_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE && - (0x7F & ((unsigned char) (PyBytes_AS_STRING(state)[0]))) < 24) - { - PyDateTime_Time *me; - char aware; - - if (PyTuple_GET_SIZE(args) == 2) { - tzinfo = PyTuple_GET_ITEM(args, 1); - if (check_tzinfo_subclass(tzinfo) < 0) { - PyErr_SetString(PyExc_TypeError, "bad " - "tzinfo state arg"); - return NULL; - } + if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) { + PyObject *state = PyTuple_GET_ITEM(args, 0); + if (PyTuple_GET_SIZE(args) == 2) { + tzinfo = PyTuple_GET_ITEM(args, 1); + } + if (PyBytes_Check(state)) { + if (PyBytes_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE && + (0x7F & ((unsigned char) (PyBytes_AS_STRING(state)[0]))) < 24) + { + return time_from_pickle(type, state, tzinfo); } - aware = (char)(tzinfo != Py_None); - me = (PyDateTime_Time *) (type->tp_alloc(type, aware)); - if (me != NULL) { - char *pdata = PyBytes_AS_STRING(state); - - memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE); - me->hashcode = -1; - me->hastzinfo = aware; - if (aware) { - Py_INCREF(tzinfo); - me->tzinfo = tzinfo; - } - if (pdata[0] & (1 << 7)) { - me->data[0] -= 128; - me->fold = 1; - } - else { - me->fold = 0; + } + else if (PyUnicode_Check(state)) { + if (PyUnicode_READY(state)) { + return NULL; + } + if (PyUnicode_GET_LENGTH(state) == _PyDateTime_TIME_DATASIZE && + (0x7F & PyUnicode_READ_CHAR(state, 2)) < 24) + { + state = PyUnicode_AsLatin1String(state); + if (state == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* More informative error message. */ + PyErr_SetString(PyExc_ValueError, + "Failed to encode latin1 string when unpickling " + "a time object. " + "pickle.load(data, encoding='latin1') is assumed."); + } + return NULL; } + self = time_from_pickle(type, state, tzinfo); + Py_DECREF(state); + return self; } - return (PyObject *)me; } + tzinfo = Py_None; } if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO$i", time_kws, @@ -4540,11 +4596,43 @@ static char *datetime_kws[] = { "microsecond", "tzinfo", "fold", NULL }; +static PyObject * +datetime_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) +{ + PyDateTime_DateTime *me; + char aware = (char)(tzinfo != Py_None); + + if (aware && check_tzinfo_subclass(tzinfo) < 0) { + PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); + return NULL; + } + + me = (PyDateTime_DateTime *) (type->tp_alloc(type , aware)); + if (me != NULL) { + const char *pdata = PyBytes_AS_STRING(state); + + memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE); + me->hashcode = -1; + me->hastzinfo = aware; + if (aware) { + Py_INCREF(tzinfo); + me->tzinfo = tzinfo; + } + if (pdata[2] & (1 << 7)) { + me->data[2] -= 128; + me->fold = 1; + } + else { + me->fold = 0; + } + } + return (PyObject *)me; +} + static PyObject * datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *state; int year; int month; int day; @@ -4556,47 +4644,42 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) PyObject *tzinfo = Py_None; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) >= 1 && - PyTuple_GET_SIZE(args) <= 2) - { - state = PyTuple_GET_ITEM(args, 0); - if (PyBytes_Check(state) && - PyBytes_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE && - MONTH_IS_SANE(PyBytes_AS_STRING(state)[2] & 0x7F)) - { - PyDateTime_DateTime *me; - char aware; - - if (PyTuple_GET_SIZE(args) == 2) { - tzinfo = PyTuple_GET_ITEM(args, 1); - if (check_tzinfo_subclass(tzinfo) < 0) { - PyErr_SetString(PyExc_TypeError, "bad " - "tzinfo state arg"); - return NULL; - } + if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) { + PyObject *state = PyTuple_GET_ITEM(args, 0); + if (PyTuple_GET_SIZE(args) == 2) { + tzinfo = PyTuple_GET_ITEM(args, 1); + } + if (PyBytes_Check(state)) { + if (PyBytes_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE && + MONTH_IS_SANE(PyBytes_AS_STRING(state)[2] & 0x7F)) + { + return datetime_from_pickle(type, state, tzinfo); } - aware = (char)(tzinfo != Py_None); - me = (PyDateTime_DateTime *) (type->tp_alloc(type , aware)); - if (me != NULL) { - char *pdata = PyBytes_AS_STRING(state); - - memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE); - me->hashcode = -1; - me->hastzinfo = aware; - if (aware) { - Py_INCREF(tzinfo); - me->tzinfo = tzinfo; - } - if (pdata[2] & (1 << 7)) { - me->data[2] -= 128; - me->fold = 1; - } - else { - me->fold = 0; + } + else if (PyUnicode_Check(state)) { + if (PyUnicode_READY(state)) { + return NULL; + } + if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATETIME_DATASIZE && + MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2) & 0x7F)) + { + state = PyUnicode_AsLatin1String(state); + if (state == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* More informative error message. */ + PyErr_SetString(PyExc_ValueError, + "Failed to encode latin1 string when unpickling " + "a datetime object. " + "pickle.load(data, encoding='latin1') is assumed."); + } + return NULL; } + self = datetime_from_pickle(type, state, tzinfo); + Py_DECREF(state); + return self; } - return (PyObject *)me; } + tzinfo = Py_None; } if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO$i", datetime_kws, From webhook-mailer at python.org Fri Dec 7 06:57:47 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 07 Dec 2018 11:57:47 -0000 Subject: [Python-checkins] Revert "bpo-34977: Add Windows App Store package (GH-10245)" (GH-11019) Message-ID: https://github.com/python/cpython/commit/cb0b78a070ea3b704416e74f64046178ae0dff3e commit: cb0b78a070ea3b704416e74f64046178ae0dff3e branch: master author: Victor Stinner committer: GitHub date: 2018-12-07T12:57:43+01:00 summary: Revert "bpo-34977: Add Windows App Store package (GH-10245)" (GH-11019) This reverts commit 468a15aaf9206448a744fc5eab3fc21f51966aad. files: A Tools/msi/make_zip.py A Tools/nuget/python.props D .azure-pipelines/windows-appx-test.yml D Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst D PC/classicAppCompat.can.xml D PC/classicAppCompat.cat D PC/classicAppCompat.sccd D PC/icons/pythonwx150.png D PC/icons/pythonwx44.png D PC/icons/pythonx150.png D PC/icons/pythonx44.png D PC/icons/pythonx50.png D PC/layout/__init__.py D PC/layout/__main__.py D PC/layout/main.py D PC/layout/support/__init__.py D PC/layout/support/appxmanifest.py D PC/layout/support/catalog.py D PC/layout/support/constants.py D PC/layout/support/distutils.command.bdist_wininst.py D PC/layout/support/filesets.py D PC/layout/support/logging.py D PC/layout/support/options.py D PC/layout/support/pip.py D PC/layout/support/props.py D PC/layout/support/python.props D PC/python_uwp.cpp D PC/store_info.txt D PCbuild/python_uwp.vcxproj D PCbuild/pythonw_uwp.vcxproj D PCbuild/venvlauncher.vcxproj D PCbuild/venvwlauncher.vcxproj D Tools/msi/make_appx.ps1 D Tools/msi/make_cat.ps1 D Tools/msi/sdktools.psm1 D Tools/msi/sign_build.ps1 M .gitattributes M Doc/make.bat M Lib/test/test_pathlib.py M Lib/test/test_venv.py M Lib/venv/__init__.py M PC/getpathp.c M PC/launcher.c M PC/pylauncher.rc M PCbuild/_tkinter.vcxproj M PCbuild/find_msbuild.bat M PCbuild/pcbuild.proj M PCbuild/pcbuild.sln M PCbuild/python.props M PCbuild/pythoncore.vcxproj M Tools/msi/buildrelease.bat M Tools/msi/make_zip.proj M Tools/nuget/make_pkg.proj diff --git a/.azure-pipelines/windows-appx-test.yml b/.azure-pipelines/windows-appx-test.yml deleted file mode 100644 index 9840c0a1221f..000000000000 --- a/.azure-pipelines/windows-appx-test.yml +++ /dev/null @@ -1,65 +0,0 @@ -jobs: -- job: Prebuild - displayName: Pre-build checks - - pool: - vmImage: ubuntu-16.04 - - steps: - - template: ./prebuild-checks.yml - - -- job: Windows_Appx_Tests - displayName: Windows Appx Tests - dependsOn: Prebuild - condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) - - pool: - vmImage: vs2017-win2016 - - strategy: - matrix: - win64: - arch: amd64 - buildOpt: '-p x64' - testRunTitle: '$(Build.SourceBranchName)-win64-appx' - testRunPlatform: win64 - maxParallel: 2 - - steps: - - checkout: self - clean: true - fetchDepth: 5 - - - powershell: | - # Relocate build outputs outside of source directory to make cleaning faster - Write-Host '##vso[task.setvariable variable=Py_IntDir]$(Build.BinariesDirectory)\obj' - # UNDONE: Do not build to a different directory because of broken tests - Write-Host '##vso[task.setvariable variable=Py_OutDir]$(Build.SourcesDirectory)\PCbuild' - Write-Host '##vso[task.setvariable variable=EXTERNAL_DIR]$(Build.BinariesDirectory)\externals' - displayName: Update build locations - - - script: PCbuild\build.bat -e $(buildOpt) - displayName: 'Build CPython' - - - script: python.bat PC\layout -vv -s "$(Build.SourcesDirectory)" -b "$(Py_OutDir)\$(arch)" -t "$(Py_IntDir)\layout-tmp-$(arch)" --copy "$(Py_IntDir)\layout-$(arch)" --precompile --preset-appx --include-tests - displayName: 'Create APPX layout' - - - script: .\python.exe -m test.pythoninfo - workingDirectory: $(Py_IntDir)\layout-$(arch) - displayName: 'Display build info' - - - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml" --tempdir "$(Py_IntDir)\tmp-$(arch)" - workingDirectory: $(Py_IntDir)\layout-$(arch) - displayName: 'Tests' - env: - PREFIX: $(Py_IntDir)\layout-$(arch) - - - task: PublishTestResults at 2 - displayName: 'Publish Test Results' - inputs: - testResultsFiles: '$(Build.BinariesDirectory)\test-results.xml' - mergeTestResults: true - testRunTitle: $(testRunTitle) - platform: $(testRunPlatform) - condition: succeededOrFailed() diff --git a/.gitattributes b/.gitattributes index 16237bb2b3ac..4a487c3c2a14 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,7 +19,6 @@ # Specific binary files Lib/test/sndhdrdata/sndhdr.* binary -PC/classicAppCompat.* binary # Text files that should not be subject to eol conversion Lib/test/cjkencodings/* -text diff --git a/Doc/make.bat b/Doc/make.bat index a8b32375810d..d28dae78e86d 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -117,12 +117,10 @@ if not exist "%BUILDDIR%" mkdir "%BUILDDIR%" if exist ..\Misc\NEWS ( echo.Copying Misc\NEWS to build\NEWS - if not exist build mkdir build copy ..\Misc\NEWS build\NEWS > nul ) else if exist ..\Misc\NEWS.D ( if defined BLURB ( echo.Merging Misc/NEWS with %BLURB% - if not exist build mkdir build %BLURB% merge -f build\NEWS ) else ( echo.No Misc/NEWS file and Blurb is not available. diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index d3fd4bd9e6b7..876eecccfd5f 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1521,7 +1521,7 @@ def test_resolve_common(self): # resolves to 'dirB/..' first before resolving to parent of dirB. self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) # Now create absolute symlinks - d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) + d = support._longpath(tempfile.mkdtemp(suffix='-dirD')) self.addCleanup(support.rmtree, d) os.symlink(os.path.join(d), join('dirA', 'linkX')) os.symlink(join('dirB'), os.path.join(d, 'linkY')) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 22a3b78852f8..461fe7afd213 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -243,7 +243,6 @@ def test_isolation(self): self.assertIn('include-system-site-packages = %s\n' % s, data) @unittest.skipUnless(can_symlink(), 'Needs symlinks') - @unittest.skipIf(os.name == 'nt', 'Symlinks are never used on Windows') def test_symlinking(self): """ Test symlinking works as expected diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index 5438b0d4e508..043420897e47 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -64,11 +64,10 @@ def create(self, env_dir): self.system_site_packages = False self.create_configuration(context) self.setup_python(context) - if not self.upgrade: - self.setup_scripts(context) if self.with_pip: self._setup_pip(context) if not self.upgrade: + self.setup_scripts(context) self.post_setup(context) if true_system_site_packages: # We had set it to False before, now @@ -159,6 +158,14 @@ def create_configuration(self, context): f.write('include-system-site-packages = %s\n' % incl) f.write('version = %d.%d.%d\n' % sys.version_info[:3]) + if os.name == 'nt': + def include_binary(self, f): + if f.endswith(('.pyd', '.dll')): + result = True + else: + result = f.startswith('python') and f.endswith('.exe') + return result + def symlink_or_copy(self, src, dst, relative_symlinks_ok=False): """ Try symlinking a file, and if that fails, fall back to copying. @@ -188,9 +195,9 @@ def setup_python(self, context): binpath = context.bin_path path = context.env_exe copier = self.symlink_or_copy + copier(context.executable, path) dirname = context.python_dir if os.name != 'nt': - copier(context.executable, path) if not os.path.islink(path): os.chmod(path, 0o755) for suffix in ('python', 'python3'): @@ -202,22 +209,26 @@ def setup_python(self, context): if not os.path.islink(path): os.chmod(path, 0o755) else: - # For normal cases, the venvlauncher will be copied from - # our scripts folder. For builds, we need to copy it - # manually. - if sysconfig.is_python_build(True): - suffix = '.exe' - if context.python_exe.lower().endswith('_d.exe'): - suffix = '_d.exe' - - src = os.path.join(dirname, "venvlauncher" + suffix) - dst = os.path.join(binpath, context.python_exe) - copier(src, dst) - - src = os.path.join(dirname, "venvwlauncher" + suffix) - dst = os.path.join(binpath, "pythonw" + suffix) - copier(src, dst) + # See bpo-34011. When using a proper install, we should only need to + # copy the top-level of DLLs. + include = self.include_binary + files = [f for f in os.listdir(dirname) if include(f)] + for f in files: + src = os.path.join(dirname, f) + dst = os.path.join(binpath, f) + if dst != context.env_exe: # already done, above + copier(src, dst) + # When creating from a build directory, we continue to copy all files. + if sysconfig.is_python_build(True): + subdir = 'DLLs' + dirname = os.path.join(dirname, subdir) + if os.path.isdir(dirname): + files = [f for f in os.listdir(dirname) if include(f)] + for f in files: + src = os.path.join(dirname, f) + dst = os.path.join(binpath, f) + copier(src, dst) # copy init.tcl over for root, dirs, files in os.walk(context.python_dir): if 'init.tcl' in files: @@ -315,7 +326,7 @@ def install_scripts(self, context, path): dstfile = os.path.join(dstdir, f) with open(srcfile, 'rb') as f: data = f.read() - if not srcfile.endswith(('.exe', '.pdb')): + if not srcfile.endswith('.exe'): try: data = data.decode('utf-8') data = self.replace_variables(data, context) diff --git a/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst deleted file mode 100644 index 8e1a4ba84880..000000000000 --- a/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst +++ /dev/null @@ -1 +0,0 @@ -Adds support for building a Windows App Store package diff --git a/PC/classicAppCompat.can.xml b/PC/classicAppCompat.can.xml deleted file mode 100644 index f00475c8da31..000000000000 --- a/PC/classicAppCompat.can.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/PC/classicAppCompat.cat b/PC/classicAppCompat.cat deleted file mode 100644 index 3d213596accf..000000000000 Binary files a/PC/classicAppCompat.cat and /dev/null differ diff --git a/PC/classicAppCompat.sccd b/PC/classicAppCompat.sccd deleted file mode 100644 index 97648985a2cc..000000000000 --- a/PC/classicAppCompat.sccd +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - MIIq5AYJKoZIhvcNAQcCoIIq1TCCKtECAQExDzANBglghkgBZQMEAgEFADCCARAGCSsGAQQBgjcKAaCCAQEwgf4wDAYKKwYBBAGCNwwBAQQQaM+L42jwBUGvBczrtolMmhcNMTgxMTMwMDA1OTAzWjAOBgorBgEEAYI3DAEDBQAwgbwwKgQUWKcU3R38DGPlKK33XGIwKtVL1r4xEjAQBgorBgEEAYI3DAIDMQKCADCBjQQg3K+KBOQX7HfxjRNZC9cx8gIPkEhPRO1nJFRdWQrVEJ4xaTAQBgorBgEEAYI3DAIDMQKCADBVBgorBgEEAYI3AgEEMUcwRTAQBgorBgEEAYI3AgEZogKAADAxMA0GCWCGSAFlAwQCAQUABCDcr4oE5Bfsd/GNE1kL1zHyAg+QSE9E7WckVF1ZCtUQnqCCFFAwggZSMIIEOqADAgECAhMzAAMu49KhfNamygpWAAIAAy7jMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQDEx5NaWNyb3NvZnQgTWFya2V0cGxhY2UgQ0EgRyAwMTMwHhcNMTgxMTMwMDA1NTA1WhcNMTgxMjAzMDA1NTA1WjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwpcimfAx3HEpba1GLL/gDaRVddHE5PXTRmwlgaz8kt6/rq5rlrPFnCnbIc5818v0xJIznastbmrq26xyCEHyMLBKnyneTKE36I7+TGjcY0D7ow+o2vY7LDKMCTGlh31fx1Tvrl+5xTbWX5jdLU/3MB5faeOGh+0Knzwx1KDoXWgPtfXnD8I5jxJieoWoCwCjKTJgBOklLy9nbOalxf0h+xQRy2p5fj+PxAwQPgHWft36AF7/IMbt9FcXMtg4xdpnTYz4OV3dFOPz4m3M8HwVgNMv89W/1Ozc7uOyZt0Ij1baT6r2L3IjYg5ftzpGqaDOFcWlyDFSdhMR6BIKW8xEpAgMBAAGjggHCMIIBvjAYBgNVHSUBAf8EDjAMBgorBgEEAYI3TBwBMB0GA1UdDgQWBBRdpGYiCytx83FYzPSl+o97YzpxGzAPBgNVHREECDAGggRNT1BSMB8GA1UdIwQYMBaAFEnYB1RFhpclHtZZcRLDcpt0OE3oMGIGA1UdHwRbMFkwV6BVoFOGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyME1hcmtldHBsYWNlJTIwQ0ElMjBHJTIwMDEzKDIpLmNybDBvBggrBgEFBQcBAQRjMGEwXwYIKwYBBQUHMAKGU2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwTWFya2V0cGxhY2UlMjBDQSUyMEclMjAwMTMoMikuY3J0MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXgMDwGCSsGAQQBgjcVBwQvMC0GJSsGAQQBgjcVCIOS9kTqrxCDkY0wgqzgLIKinDE0g+6NOIaE7wACAWQCARYwIAYJKwYBBAGCNxUKAQH/BBAwDjAMBgorBgEEAYI3TBwBMA0GCSqGSIb3DQEBCwUAA4ICAQB3Dk3rXH52CDq/z1fwqn9xI5WGjGmu6oAE4HSc3sNdFrSVMMGm4gTlYGWSZ0wJUUf16mVr/rdXhxuR3MZn+m4Bhdl8KQqYjYbIvCUVj0o9nZ+yT6foeY8bKnB+K5h6rol+mjDj5IfcutC4x2Kx5RrtDtRTSoKA63iZ74DYngPpBGBBgaS2c/QzgqPRAMMRqy2KBDP0miCnpR3F4YlzHGyOZwyHhESjYd9kwF47+msuHS04JZpnGHIvBppKN9XQzH3WezNnnX3lz4AyAUMsMFuARqEnacUhrAHL9n5zMv9CzxDYN1r1/aDh/788RuGuZM+E3NtmbxJJ7j6T5/VtXNBRgKtIq8d2+11j6qvKLigOTxSC25/A70BZBEvllLFnvc1vA2LrC9drwt1KpSmWie1nvpilw7o+gHMOG9utUxGha2VuVizuVNGCywTRRjvmGS1QqTfaun1URVrLfnDINXuTgN1Vwp0J5IGpJ3D8yj01NDQ/RworE+3W/R531NBYova9QRhU/igEw/Aa/q8wjZ4Pzxr9oBIo0Ta3Tv6qIggaWXw0U9+F0J7SCqIhn0d0ATO+E1Qs/SxZIAICLwmqzoLYUAh8q153esBs4uesueqgt5ueyHK8V3WjMS4wxEyVN5ZMET3hFtEshsZC31tLDdjq750U4SgQVmoYSm3F3ZOKQDCCBtcwggS/oAMCAQICCmESRKIAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDMyODIxMDkzOVoXDTMxMDMyODIxMTkzOVowfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAubUaSwGYVsE3MAnPfvmozUhAB3qxBABgJRW1vDp4+tVinXxD32f7k1K89JQ6zDOgS/iDgULC+yFK1K/1Qjac/0M7P6c8v5LSjnWGlERLa/qY32j46S7SLQcit3g2jgoTTO03eUG+9yHZUTGV/FJdRYB8uXhrznJBa+Y+yGwiQKF+m6XFeBH/KORoKFx+dmMoy9EWJ/m/o9IiUj2kzm9C691+vZ/I2w0Bj93W9SPPkV2PCNHlzgfIAoeajWpHmi38Wi3xZHonkzAVBHxPsCBppOoNsWvmAfUM7eBthkSPvFruekyDCPNEYhfGqgqtqLkoBebXLZCOVybF7wTQaLvse60//3P003icRcCoQYgY4NAqrF7j80o5U7DkeXxcB0xvengsaKgiAaV1DKkRbpe98wCqr1AASvm5rAJUYMU+mXmOieV2EelY2jGrenWe9FQpNXYV1NoWBh0WKoFxttoWYAnF705bIWtSZsz08ZfK6WLX4GXNLcPBlgCzfTm1sdKYASWdBbH2haaNhPapFhQQBJHKwnVW2iXErImhuPi45W3MVTZ5D9ASshZx69cLYY6xAdIa+89Kf/uRrsGOVZfahDuDw+NI183iAyzC8z/QRt2P32LYxP0xrCdqVh+DJo2i4NoE8Uk1usCdbVRuBMBQl/AwpOTq7IMvHGElf65CqzUCAwEAAaOCAUswggFHMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBQPU8s/FmEl/mCJHdO5fOiQrbOU0TAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCjuZmM8ZVNDgp9wHsL4RY8KJ8nLinvxFTphNGCrxaLknkYG5pmMhVlX+UB/tSiW8W13W60nggz9u5xwMx7v/1t/Tgm6g2brVyOKI5A7u6/2SIJwkJKFw953K0YIKVT28w9zl8dSJnmRnyR0G86ncWbF6CLQ6A6lBQ9o2mTGVqDr4m35WKAnc6YxUUM1y74mbzFFZr63VHsCcOp3pXWnUqAY1rb6Q6NX1b3clncKqLFm0EjKHcQ56grTbwuuB7pMdh/IFCJR01MQzQbDtpEisbOeZUi43YVAAHKqI1EO9bRwg3frCjwAbml9MmI4utMW94gWFgvrMxIX+n42RBDIjf3Ot3jkT6gt3XeTTmO9bptgblZimhERdkFRUFpVtkocJeLoGuuzP93uH/Yp032wzRH+XmMgujfZv+vnfllJqxdowoQLx55FxLLeTeYfwi/xMSjZO2gNven3U/3KeSCd1kUOFS3AOrwZ0UNOXJeW5JQC6Vfd1BavFZ6FAta1fMLu3WFvNB+FqeHUaU3ya7rmtxJnzk29DeSqXgGNmVSywBS4NajI5jJIKAA6UhNJlsg8CHYwUOKf5ej8OoQCkbadUxXygAfxCfW2YBbujtI+PoyejRFxWUjYFWO5LeTI62UMyqfOEiqugoYjNxmQZla2s4YHVuqIC34R85FQlg9pKQBsDCCBxswggUDoAMCAQICEzMAAABCs21EHGjyqKYAAAAAAEIwDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMB4XDTE4MDQyMDE2NDI0NFoXDTIxMDQyMDE2NDI0NFowgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOZ2KM9Pq1YCOiqWOivmHjUtkMgznTMP/Mr2YfzZeIIJySg1F4WxFZc4jagGHHNof9NRT+GGnktWsXkZuH1DzQEG4Ps1ln8+4vhbDglqu5ymDnd6RmsyoD+8xfc8bBIvE5o6R+ES4/GVD5TqNsOrWbwETaIZVbmTulJLoTS1WSsSjowmbc+sHqZiY8BNJNThUEmXSjuHqkQKKshuiFWYEqOTitp71mBLyH1wN7/jThRzGpolOeFusRNJdb8sEqvNzEN9Qh+Kp6ndzrnjE+t8ixXW3lShyyOOZqQMwsQn9q9T0v7Q69GuojBTFBOHKwigcCHr4xahuN+ZYMk0xGg+sm3Uj7I9mrWTSTiIRMZNIWq3sFg4+rFg48NYfRlXUpONmL7vXq6v1pIU99d2MXQ6uUrnUr1/n5ZiHGCeFcvWwqO8BYHdcTlrSOkayfFp7W9oCk9QO4Xy0h9cQRedRo2kvdTHxIuJS70Hdv6oePPF2ZFaLucUzzwsR4/XMAVKY8Vsm950omsSSOImsMtzavUdQM+wZFxvHTRqVDkF3quPdME0bCZOWB4hQJmd+o2clw+1mpwPu0/M92nA9FJg7MGPxkFaYW7g26jSqUJZ9AcX+Xa5TSIeqMZt3cRVjMTx0T/v73Sv8TpalqIQ5Fde1+hFK07sOAm3TwgzvlVJnbYgp0/rAgMBAAGjggGCMIIBfjASBgkrBgEEAYI3FQEEBQIDAgACMCMGCSsGAQQBgjcVAgQWBBSbJnDhuc3nQXuKuACsPflEbwjbozAdBgNVHQ4EFgQUSdgHVEWGlyUe1llxEsNym3Q4TegwEQYDVR0gBAowCDAGBgRVHSAAMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFA9Tyz8WYSX+YIkd07l86JCts5TRMFcGA1UdHwRQME4wTKBKoEiGRmh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcmwwWwYIKwYBBQUHAQEETzBNMEsGCCsGAQUFBzAChj9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAIa2oa6kvuIHCNfz7anlL0W9tOCt8gQNkxOGRK3yliQIelNQahDJojyEFlHQ2BcHL5oZit3WeSDoYddhojx6YzJIWwfGwtVqgc0JFDKJJ2ZXRYMRsuy01Hn25xob+zRMS6VmV1axQn6uwOSMcgYmzoroh6edjPKu7qXcpt6LmhF2qFvLySA7wBCwfI/rR5/PX6I7a07Av7PpbY6/+2ujd8m1H3hwMrb4Hq3z6gcq62zJ3nDXUbC0Bp6Jt2kV9f0rEFpDK9oxE2qrGBUf8c3O2XirHOgAjRyWjWWtVms+MP8qBIA1NSLrBmToEWVP3sEkQZWMkoZWo4rYEJZpX7UIgdDc9zYNakgTCJqPhqn8AE1sgSSnpqAdMkkP41rTlFCv2ig2QVzDerjGfEv+uPDnlAT0kucbBJxHHvUC4aqUxaTSa0sy2bZ6NWFx8/u0gW8JahzxYvvvZL8SfwaA9P4ETb8pH1jw+6N/LfM2zJrNKhf5hjKa0VDOXUpkYq60OqVVnWJ6oJaSIWNkZKfzPnl/UHA8Bh4qfVrhc9H5PExPhhB9WVTsjf4r+OOVuolJldThcWQqljiPjk5rultr63G5xLyFpxNi4BCrcNQBJFB5wKgOWOyjQTVWTmh2ESaeqZ2aWBjftFHlxJ/qYc7WOGJV0+cHGkB/dvFxmKnv6tuWexiMMYIVUTCCFU0CAQEwgaQwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMwITMwADLuPSoXzWpsoKVgACAAMu4zANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKwYBBAGCNwoBMC8GCSqGSIb3DQEJBDEiBCAS0d3bw2YOODvKFr0S4e3BDnaDcZXUKeBO77yvkWzVojBIBgorBgEEAYI3AgEMMTowOKAegBwATQBpAGMAcgBvAHMAbwBmAHQAIABDAG8AcgBwoRaAFGh0dHA6Ly9NaWNyb3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIBABoap3Y+2k+zFz2cCmkc8xxHnpIygLsUSRMXeXdjPVcYx3o5cPLIixnL6p8+LIrlIagPg23mzTEmnjZaO4aaexk+3XojlHj22w/bEigEDnKyWt5bHeS0UNHJbxEFYRfd84IP1+mSH4c4+GuU9p3LsAMh6wN03MYrGmczUOnlP6YlxHNQbQxnV0sl14yOE5ni9oT4y+l+SllvbV3/Jhwpov68aoP/2MazqxR4QyGfSxhCPJ4UuDHU7IrpnTxGBTL1/oUU8ED0FxyDoH/Sc5OhTLInFqbZaVzm5Mpr12wYUBL4nE5h0Kf6BCKdgM8a+Ti3wMUsBoC79ff3jE9U/xwSneOhghLlMIIS4QYKKwYBBAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQghPy22lwuCYESw8jYhb4F9ZDPJ1LPgSSZgJDkyXYzVt4CBlv98KtAoBgTMjAxODExMzAwMTA1MTkuMTM4WjAEgAIB9KCB0KSBzTCByjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RDA4Mi00QkZELUVFQkExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAOIYOHtm6erB2AAAAAAA4jANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0xODA4MjMyMDI3MDNaFw0xOTExMjMyMDI3MDNaMIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEMDgyLTRCRkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgc2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKirA72FF3NCLW5mfLO/D0EZ5Ycs00oiMSissXLB6WF9GNdP78QzFwAypxW/+qZSczqaHbDH8hlbxkzf3DiYgAdpQjnGkLujwKtWSaP29/lVf7jFqHy9v6eH+LdOi0LvtrPRW34MyCvpxZyOW4H1h3PkxCBL5Ra21sDqgcVL1me0osw8QTURXmI4LyeLdTH3CcI2AgNDXTjsFBf3QsO+JYyAOYWrTcLnywVN6DrigmgrDJk5w+wR4VrHfl2T9PRZbZ+UDt13wwyB9d6IURuzV8lHsAVfF8t9S0aGVPmkQ3c2waOhHpsp6VEM+T5D2Ph8xJX1r82z67WRlmGcOP2NWC0CAwEAAaOCARswggEXMB0GA1UdDgQWBBSJPpD6BsP2p+crDJL232voEtLxezAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQARQHu7ISeBuJSHKuDRI04704cH0B7BYzeEIrD15awviMRcYIfIOHpvGzZOWQgP2Hm0Rr7kvTUu1VrSSaQ7i1gPWdhqMmw5WBnSS5bxeMhhx9UsASeE84vUu82NeZapGSjH38YAb4WT+TtiTkcoI59rA+CTCq108ttIxVfZcr3id76OETIH0HvhlnxOOWjwGy4ul6Za5RoTLG/oo2rrGmVi3FwrNWGezYLBODuEsjzG36lCRtBKC2ZAHfbOz5wtkUHbqh79mUKocjP4r3qxf5TN87yf6g1uTx+J8pdnAi5iHt+ZtangWqnVTE8PoIREWhBVlGFfQdkELUx2Or90aAqWMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQwODItNEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBzZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQByQCUheEOevaI9Zc/3QGrkX42iC6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA36ppYDAiGA8yMDE4MTEyOTIxMzQyNFoYDzIwMTgxMTMwMjEzNDI0WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDfqmlgAgEAMAoCAQACAitfAgH/MAcCAQACAhGtMAoCBQDfq7rgAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbAXXPR9wy4NA0892GGqetaZF+pNClpGcfEpSuHABaZ4Gzr1nY1nmrhexTtr/U6omHALRWzkQwthk0cy+mnEHXyOZGmoEEpgrLgK3AAP5NbK/XbtHQRyZJQyhZScFbOyQycoE8QQalSVOhWxk/bbBMQaQiYVMIexNd/T0KgaDDUMxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAOIYOHtm6erB2AAAAAAA4jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCr9IiSbx6s8MLdxldRG49+4h6CbicW8hWXAicI3jNmhDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIN8BpJSmQCGubWwVa4tW+aMveoHMX/nDnVN8fiDOMsrLMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADiGDh7ZunqwdgAAAAAAOIwIgQgTkOfRvGEZNbr5/hgWclsL4/Q7SOZihE/U0lz2wEMIGcwDQYJKoZIhvcNAQELBQAEggEATlxnCfTzFfTMDvK085zlYPVCroKYW6gKFYnbAhNmrNzcxqALKmIYXpFU7B6HH/vYzkUfCyXpf5tsyEWu0oTySOjyAZ9+2vdaG8nEgjOp0L737lcitgusIjpWtta3Ik0b+mzffnvyjrgTSuKDDni3mxGfvJU77k1Ctempma4H2FJso6Bur0PRH99vIYDu4lHigOSLbeyjR5CiDciBwEVUSA0FxhoFNX1yfpxz3sukOvkaoTduREIjH5LxUjNI1ZTMK/ZkeETI8IPRpWVzAc8q7CujErHKo4sdKej/O2cfUTUHplFLVCGGExpJUCg5FH5jVUUFt75ad8503sdGplggVQ== diff --git a/PC/getpathp.c b/PC/getpathp.c index 76d3a081b363..35c31bf29bdf 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -536,16 +536,10 @@ static _PyInitError get_program_full_path(const _PyCoreConfig *core_config, PyCalculatePath *calculate, _PyPathConfig *config) { - const wchar_t *pyvenv_launcher; wchar_t program_full_path[MAXPATHLEN+1]; memset(program_full_path, 0, sizeof(program_full_path)); - /* The launcher may need to force the executable path to a - * different environment, so override it here. */ - pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__"); - if (pyvenv_launcher && pyvenv_launcher[0]) { - wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher); - } else if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { + if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { /* GetModuleFileName should never fail when passed NULL */ return _Py_INIT_ERR("Cannot determine program path"); } diff --git a/PC/icons/pythonwx150.png b/PC/icons/pythonwx150.png deleted file mode 100644 index 4c3eb316739c..000000000000 Binary files a/PC/icons/pythonwx150.png and /dev/null differ diff --git a/PC/icons/pythonwx44.png b/PC/icons/pythonwx44.png deleted file mode 100644 index e3b32a871f90..000000000000 Binary files a/PC/icons/pythonwx44.png and /dev/null differ diff --git a/PC/icons/pythonx150.png b/PC/icons/pythonx150.png deleted file mode 100644 index 5f8d30418386..000000000000 Binary files a/PC/icons/pythonx150.png and /dev/null differ diff --git a/PC/icons/pythonx44.png b/PC/icons/pythonx44.png deleted file mode 100644 index 3881daaef233..000000000000 Binary files a/PC/icons/pythonx44.png and /dev/null differ diff --git a/PC/icons/pythonx50.png b/PC/icons/pythonx50.png deleted file mode 100644 index 7cc3aecd0242..000000000000 Binary files a/PC/icons/pythonx50.png and /dev/null differ diff --git a/PC/launcher.c b/PC/launcher.c index 4c620dab7c09..2c2da76f6146 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -28,7 +28,7 @@ #define RC_NO_PYTHON 103 #define RC_NO_MEMORY 104 /* - * SCRIPT_WRAPPER is used to choose one of the variants of an executable built + * SCRIPT_WRAPPER is used to choose between two variants of an executable built * from this source file. If not defined, the PEP 397 Python launcher is built; * if defined, a script launcher of the type used by setuptools is built, which * looks for a script name related to the executable name and runs that script @@ -40,15 +40,6 @@ #if defined(SCRIPT_WRAPPER) #define RC_NO_SCRIPT 105 #endif -/* - * VENV_REDIRECT is used to choose the variant that looks for an adjacent or - * one-level-higher pyvenv.cfg, and uses its "home" property to locate and - * launch the original python.exe. - */ -#if defined(VENV_REDIRECT) -#define RC_NO_VENV_CFG 106 -#define RC_BAD_VENV_CFG 107 -#endif /* Just for now - static definition */ @@ -106,7 +97,7 @@ error(int rc, wchar_t * format, ... ) #if !defined(_WINDOWS) fwprintf(stderr, L"%ls\n", message); #else - MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...", + MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), MB_OK); #endif exit(rc); @@ -140,17 +131,6 @@ static wchar_t * get_env(wchar_t * key) return buf; } -#if defined(_DEBUG) -#if defined(_WINDOWS) - -#define PYTHON_EXECUTABLE L"pythonw_d.exe" - -#else - -#define PYTHON_EXECUTABLE L"python_d.exe" - -#endif -#else #if defined(_WINDOWS) #define PYTHON_EXECUTABLE L"pythonw.exe" @@ -159,7 +139,6 @@ static wchar_t * get_env(wchar_t * key) #define PYTHON_EXECUTABLE L"python.exe" -#endif #endif #define MAX_VERSION_SIZE 4 @@ -1478,87 +1457,6 @@ show_python_list(wchar_t ** argv) return FALSE; /* If this has been called we cannot continue */ } -#if defined(VENV_REDIRECT) - -static int -find_home_value(const char *buffer, const char **start, DWORD *length) -{ - for (const char *s = strstr(buffer, "home"); s; s = strstr(s + 1, "\nhome")) { - if (*s == '\n') { - ++s; - } - for (int i = 4; i > 0 && *s; --i, ++s); - - while (*s && iswspace(*s)) { - ++s; - } - if (*s != L'=') { - continue; - } - - do { - ++s; - } while (*s && iswspace(*s)); - - *start = s; - char *nl = strchr(s, '\n'); - if (nl) { - *length = (DWORD)((ptrdiff_t)nl - (ptrdiff_t)s); - } else { - *length = (DWORD)strlen(s); - } - return 1; - } - return 0; -} -#endif - -static wchar_t * -wcsdup_pad(const wchar_t *s, int padding, int *newlen) -{ - size_t len = wcslen(s); - len += 1 + padding; - wchar_t *r = (wchar_t *)malloc(len * sizeof(wchar_t)); - if (!r) { - return NULL; - } - if (wcscpy_s(r, len, s)) { - free(r); - return NULL; - } - *newlen = len < MAXINT ? (int)len : MAXINT; - return r; -} - -static wchar_t * -get_process_name() -{ - DWORD bufferLen = MAX_PATH; - DWORD len = bufferLen; - wchar_t *r = NULL; - - while (!r) { - r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); - if (!r) { - error(RC_NO_MEMORY, L"out of memory"); - return NULL; - } - len = GetModuleFileNameW(NULL, r, bufferLen); - if (len == 0) { - free(r); - error(0, L"Failed to get module name"); - return NULL; - } else if (len == bufferLen && - GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - free(r); - r = NULL; - bufferLen *= 2; - } - } - - return r; -} - static int process(int argc, wchar_t ** argv) { @@ -1566,27 +1464,21 @@ process(int argc, wchar_t ** argv) wchar_t * command; wchar_t * executable; wchar_t * p; - wchar_t * argv0; int rc = 0; + size_t plen; INSTALLED_PYTHON * ip; BOOL valid; DWORD size, attrs; + HRESULT hr; wchar_t message[MSGSIZE]; void * version_data; VS_FIXEDFILEINFO * file_info; UINT block_size; -#if defined(VENV_REDIRECT) - wchar_t * venv_cfg_path; + int index; +#if defined(SCRIPT_WRAPPER) int newlen; -#elif defined(SCRIPT_WRAPPER) wchar_t * newcommand; wchar_t * av[2]; - int newlen; - HRESULT hr; - int index; -#else - HRESULT hr; - int index; #endif setvbuf(stderr, (char *)NULL, _IONBF, 0); @@ -1604,7 +1496,6 @@ process(int argc, wchar_t ** argv) #else debug(L"launcher executable: Console\n"); #endif -#if !defined(VENV_REDIRECT) /* Get the local appdata folder (non-roaming) */ hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appdata_ini_path); @@ -1613,7 +1504,9 @@ process(int argc, wchar_t ** argv) appdata_ini_path[0] = L'\0'; } else { - wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE); + plen = wcslen(appdata_ini_path); + p = &appdata_ini_path[plen]; + wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE); attrs = GetFileAttributesW(appdata_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", appdata_ini_path); @@ -1622,9 +1515,8 @@ process(int argc, wchar_t ** argv) debug(L"Using local configuration file '%ls'\n", appdata_ini_path); } } -#endif - argv0 = get_process_name(); - size = GetFileVersionInfoSizeW(argv0, &size); + plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH); + size = GetFileVersionInfoSizeW(launcher_ini_path, &size); if (size == 0) { winerror(GetLastError(), message, MSGSIZE); debug(L"GetFileVersionInfoSize failed: %ls\n", message); @@ -1632,7 +1524,7 @@ process(int argc, wchar_t ** argv) else { version_data = malloc(size); if (version_data) { - valid = GetFileVersionInfoW(argv0, 0, size, + valid = GetFileVersionInfoW(launcher_ini_path, 0, size, version_data); if (!valid) debug(L"GetFileVersionInfo failed: %X\n", GetLastError()); @@ -1649,51 +1541,15 @@ process(int argc, wchar_t ** argv) free(version_data); } } - -#if defined(VENV_REDIRECT) - /* Allocate some extra space for new filenames */ - venv_cfg_path = wcsdup_pad(argv0, 32, &newlen); - if (!venv_cfg_path) { - error(RC_NO_MEMORY, L"Failed to copy module name"); - } - p = wcsrchr(venv_cfg_path, L'\\'); - - if (p == NULL) { - error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); - } - p[0] = L'\0'; - wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); - attrs = GetFileAttributesW(venv_cfg_path); - if (attrs == INVALID_FILE_ATTRIBUTES) { - debug(L"File '%ls' non-existent\n", venv_cfg_path); - p[0] = '\0'; - p = wcsrchr(venv_cfg_path, L'\\'); - if (p != NULL) { - p[0] = '\0'; - wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); - attrs = GetFileAttributesW(venv_cfg_path); - if (attrs == INVALID_FILE_ATTRIBUTES) { - debug(L"File '%ls' non-existent\n", venv_cfg_path); - error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); - } - } - } - debug(L"Using venv configuration file '%ls'\n", venv_cfg_path); -#else - /* Allocate some extra space for new filenames */ - if (wcscpy_s(launcher_ini_path, MAX_PATH, argv0)) { - error(RC_NO_MEMORY, L"Failed to copy module name"); - } p = wcsrchr(launcher_ini_path, L'\\'); - if (p == NULL) { debug(L"GetModuleFileNameW returned value has no backslash: %ls\n", launcher_ini_path); launcher_ini_path[0] = L'\0'; } else { - p[0] = L'\0'; - wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini"); + wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini", + _TRUNCATE); attrs = GetFileAttributesW(launcher_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", launcher_ini_path); @@ -1702,7 +1558,6 @@ process(int argc, wchar_t ** argv) debug(L"Using global configuration file '%ls'\n", launcher_ini_path); } } -#endif command = skip_me(GetCommandLineW()); debug(L"Called with command line: %ls\n", command); @@ -1738,55 +1593,6 @@ process(int argc, wchar_t ** argv) command = newcommand; valid = FALSE; } -#elif defined(VENV_REDIRECT) - { - FILE *f; - char buffer[4096]; /* 4KB should be enough for anybody */ - char *start; - DWORD len, cch, cch_actual; - size_t cb; - if (_wfopen_s(&f, venv_cfg_path, L"r")) { - error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path); - } - cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]), - sizeof(buffer) / sizeof(buffer[0]), f); - fclose(f); - - if (!find_home_value(buffer, &start, &len)) { - error(RC_BAD_VENV_CFG, L"Cannot find home in '%ls'", - venv_cfg_path); - } - - cch = MultiByteToWideChar(CP_UTF8, 0, start, len, NULL, 0); - if (!cch) { - error(0, L"Cannot determine memory for home path"); - } - cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 1 + 1; /* include sep and null */ - executable = (wchar_t *)malloc(cch * sizeof(wchar_t)); - if (executable == NULL) { - error(RC_NO_MEMORY, L"A memory allocation failed"); - } - cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, executable, cch); - if (!cch_actual) { - error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'", - venv_cfg_path); - } - if (executable[cch_actual - 1] != L'\\') { - executable[cch_actual++] = L'\\'; - executable[cch_actual] = L'\0'; - } - if (wcscat_s(executable, cch, PYTHON_EXECUTABLE)) { - error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'", - venv_cfg_path); - } - if (GetFileAttributesW(executable) == INVALID_FILE_ATTRIBUTES) { - error(RC_NO_PYTHON, L"No Python at '%ls'", executable); - } - if (!SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", argv0)) { - error(0, L"Failed to set launcher environment"); - } - valid = 1; - } #else if (argc <= 1) { valid = FALSE; @@ -1794,6 +1600,7 @@ process(int argc, wchar_t ** argv) } else { p = argv[1]; + plen = wcslen(p); if ((argc == 2) && // list version args (!wcsncmp(p, L"-0", wcslen(L"-0")) || !wcsncmp(p, L"--list", wcslen(L"--list")))) diff --git a/PC/layout/__init__.py b/PC/layout/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/PC/layout/__main__.py b/PC/layout/__main__.py deleted file mode 100644 index f7aa1e6d261f..000000000000 --- a/PC/layout/__main__.py +++ /dev/null @@ -1,14 +0,0 @@ -import sys - -try: - import layout -except ImportError: - # Failed to import our package, which likely means we were started directly - # Add the additional search path needed to locate our module. - from pathlib import Path - - sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) - -from layout.main import main - -sys.exit(int(main() or 0)) diff --git a/PC/layout/main.py b/PC/layout/main.py deleted file mode 100644 index 82d0536ca920..000000000000 --- a/PC/layout/main.py +++ /dev/null @@ -1,612 +0,0 @@ -""" -Generates a layout of Python for Windows from a build. - -See python make_layout.py --help for usage. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - -import argparse -import functools -import os -import re -import shutil -import subprocess -import sys -import tempfile -import zipfile - -from pathlib import Path - -if __name__ == "__main__": - # Started directly, so enable relative imports - __path__ = [str(Path(__file__).resolve().parent)] - -from .support.appxmanifest import * -from .support.catalog import * -from .support.constants import * -from .support.filesets import * -from .support.logging import * -from .support.options import * -from .support.pip import * -from .support.props import * - -BDIST_WININST_FILES_ONLY = FileNameSet("wininst-*", "bdist_wininst.py") -BDIST_WININST_STUB = "PC/layout/support/distutils.command.bdist_wininst.py" - -TEST_PYDS_ONLY = FileStemSet("xxlimited", "_ctypes_test", "_test*") -TEST_DIRS_ONLY = FileNameSet("test", "tests") - -IDLE_DIRS_ONLY = FileNameSet("idlelib") - -TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter") -TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo") -TCLTK_FILES_ONLY = FileNameSet("turtle.py") - -VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip") - -EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext") -EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle") -EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt") -EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*") -EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll") - -REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*") - -LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt") - -PY_FILES = FileSuffixSet(".py") -PYC_FILES = FileSuffixSet(".pyc") -CAT_FILES = FileSuffixSet(".cat") -CDF_FILES = FileSuffixSet(".cdf") - -DATA_DIRS = FileNameSet("data") - -TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser") -TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt") - - -def get_lib_layout(ns): - def _c(f): - if f in EXCLUDE_FROM_LIB: - return False - if f.is_dir(): - if f in TEST_DIRS_ONLY: - return ns.include_tests - if f in TCLTK_DIRS_ONLY: - return ns.include_tcltk - if f in IDLE_DIRS_ONLY: - return ns.include_idle - if f in VENV_DIRS_ONLY: - return ns.include_venv - else: - if f in TCLTK_FILES_ONLY: - return ns.include_tcltk - if f in BDIST_WININST_FILES_ONLY: - return ns.include_bdist_wininst - return True - - for dest, src in rglob(ns.source / "Lib", "**/*", _c): - yield dest, src - - if not ns.include_bdist_wininst: - src = ns.source / BDIST_WININST_STUB - yield Path("distutils/command/bdist_wininst.py"), src - - -def get_tcltk_lib(ns): - if not ns.include_tcltk: - return - - tcl_lib = os.getenv("TCL_LIBRARY") - if not tcl_lib or not os.path.isdir(tcl_lib): - try: - with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f: - tcl_lib = f.read().strip() - except FileNotFoundError: - pass - if not tcl_lib or not os.path.isdir(tcl_lib): - warn("Failed to find TCL_LIBRARY") - return - - for dest, src in rglob(Path(tcl_lib).parent, "**/*"): - yield "tcl/{}".format(dest), src - - -def get_layout(ns): - def in_build(f, dest="", new_name=None): - n, _, x = f.rpartition(".") - n = new_name or n - src = ns.build / f - if ns.debug and src not in REQUIRED_DLLS: - if not src.stem.endswith("_d"): - src = src.parent / (src.stem + "_d" + src.suffix) - if not n.endswith("_d"): - n += "_d" - f = n + "." + x - yield dest + n + "." + x, src - if ns.include_symbols: - pdb = src.with_suffix(".pdb") - if pdb.is_file(): - yield dest + n + ".pdb", pdb - if ns.include_dev: - lib = src.with_suffix(".lib") - if lib.is_file(): - yield "libs/" + n + ".lib", lib - - yield from in_build("python_uwp.exe", new_name="python") - yield from in_build("pythonw_uwp.exe", new_name="pythonw") - - yield from in_build(PYTHON_DLL_NAME) - - if ns.include_launchers: - if ns.include_pip: - yield from in_build("python_uwp.exe", new_name="pip") - if ns.include_idle: - yield from in_build("pythonw_uwp.exe", new_name="idle") - - if ns.include_stable: - yield from in_build(PYTHON_STABLE_DLL_NAME) - - for dest, src in rglob(ns.build, "vcruntime*.dll"): - yield dest, src - - for dest, src in rglob(ns.build, ("*.pyd", "*.dll")): - if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS: - continue - if src in EXCLUDE_FROM_PYDS: - continue - if src in TEST_PYDS_ONLY and not ns.include_tests: - continue - if src in TCLTK_PYDS_ONLY and not ns.include_tcltk: - continue - - yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/") - - if ns.zip_lib: - zip_name = PYTHON_ZIP_NAME - yield zip_name, ns.temp / zip_name - else: - for dest, src in get_lib_layout(ns): - yield "Lib/{}".format(dest), src - - if ns.include_venv: - yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python") - yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw") - - if ns.include_tools: - - def _c(d): - if d.is_dir(): - return d in TOOLS_DIRS - return d in TOOLS_FILES - - for dest, src in rglob(ns.source / "Tools", "**/*", _c): - yield "Tools/{}".format(dest), src - - if ns.include_underpth: - yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME - - if ns.include_dev: - - def _c(d): - if d.is_dir(): - return d.name != "internal" - return True - - for dest, src in rglob(ns.source / "Include", "**/*.h", _c): - yield "include/{}".format(dest), src - src = ns.source / "PC" / "pyconfig.h" - yield "include/pyconfig.h", src - - for dest, src in get_tcltk_lib(ns): - yield dest, src - - if ns.include_pip: - pip_dir = get_pip_dir(ns) - if not pip_dir.is_dir(): - log_warning("Failed to find {} - pip will not be included", pip_dir) - else: - pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}" - for dest, src in rglob(pip_dir, "**/*"): - if src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB: - continue - yield pkg_root.format(dest), src - - if ns.include_chm: - for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME): - yield "Doc/{}".format(dest), src - - if ns.include_html_doc: - for dest, src in rglob(ns.doc_build / "html", "**/*"): - yield "Doc/html/{}".format(dest), src - - if ns.include_props: - for dest, src in get_props_layout(ns): - yield dest, src - - for dest, src in get_appx_layout(ns): - yield dest, src - - if ns.include_cat: - if ns.flat_dlls: - yield ns.include_cat.name, ns.include_cat - else: - yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat - - -def _compile_one_py(src, dest, name, optimize): - import py_compile - - if dest is not None: - dest = str(dest) - - try: - return Path( - py_compile.compile( - str(src), - dest, - str(name), - doraise=True, - optimize=optimize, - invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, - ) - ) - except py_compile.PyCompileError: - log_warning("Failed to compile {}", src) - return None - - -def _py_temp_compile(src, ns, dest_dir=None): - 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") - return _compile_one_py(src, dest.with_suffix(".pyc"), dest, optimize=2) - - -def _write_to_zip(zf, dest, src, ns): - pyc = _py_temp_compile(src, ns) - if pyc: - try: - zf.write(str(pyc), dest.with_suffix(".pyc")) - finally: - try: - pyc.unlink() - except: - log_exception("Failed to delete {}", pyc) - return - - if src in LIB2TO3_GRAMMAR_FILES: - from lib2to3.pgen2.driver import load_grammar - - tmp = ns.temp / src.name - try: - shutil.copy(src, tmp) - load_grammar(str(tmp)) - for f in ns.temp.glob(src.stem + "*.pickle"): - zf.write(str(f), str(dest.parent / f.name)) - try: - f.unlink() - except: - log_exception("Failed to delete {}", f) - except: - log_exception("Failed to compile {}", src) - finally: - try: - tmp.unlink() - except: - log_exception("Failed to delete {}", tmp) - - zf.write(str(src), str(dest)) - - -def generate_source_files(ns): - if ns.zip_lib: - zip_name = PYTHON_ZIP_NAME - zip_path = ns.temp / zip_name - if zip_path.is_file(): - zip_path.unlink() - elif zip_path.is_dir(): - log_error( - "Cannot create zip file because a directory exists by the same name" - ) - return - log_info("Generating {} in {}", zip_name, ns.temp) - ns.temp.mkdir(parents=True, exist_ok=True) - with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: - for dest, src in get_lib_layout(ns): - _write_to_zip(zf, dest, src, ns) - - if ns.include_underpth: - log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp) - ns.temp.mkdir(parents=True, exist_ok=True) - with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f: - if ns.zip_lib: - print(PYTHON_ZIP_NAME, file=f) - if ns.include_pip: - print("packages", file=f) - else: - print("Lib", file=f) - print("Lib/site-packages", file=f) - if not ns.flat_dlls: - print("DLLs", file=f) - print(".", file=f) - print(file=f) - print("# Uncomment to run site.main() automatically", file=f) - print("#import site", file=f) - - if ns.include_appxmanifest: - log_info("Generating AppxManifest.xml in {}", ns.temp) - ns.temp.mkdir(parents=True, exist_ok=True) - - with open(ns.temp / "AppxManifest.xml", "wb") as f: - f.write(get_appxmanifest(ns)) - - with open(ns.temp / "_resources.xml", "wb") as f: - f.write(get_resources_xml(ns)) - - if ns.include_pip: - pip_dir = get_pip_dir(ns) - if not (pip_dir / "pip").is_dir(): - log_info("Extracting pip to {}", pip_dir) - pip_dir.mkdir(parents=True, exist_ok=True) - extract_pip_files(ns) - - if ns.include_props: - log_info("Generating {} in {}", PYTHON_PROPS_NAME, ns.temp) - ns.temp.mkdir(parents=True, exist_ok=True) - with open(ns.temp / PYTHON_PROPS_NAME, "wb") as f: - f.write(get_props(ns)) - - -def _create_zip_file(ns): - if not ns.zip: - return None - - if ns.zip.is_file(): - try: - ns.zip.unlink() - except OSError: - log_exception("Unable to remove {}", ns.zip) - sys.exit(8) - elif ns.zip.is_dir(): - log_error("Cannot create ZIP file because {} is a directory", ns.zip) - sys.exit(8) - - ns.zip.parent.mkdir(parents=True, exist_ok=True) - return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED) - - -def copy_files(files, ns): - if ns.copy: - ns.copy.mkdir(parents=True, exist_ok=True) - - try: - total = len(files) - except TypeError: - total = None - count = 0 - - zip_file = _create_zip_file(ns) - try: - need_compile = [] - in_catalog = [] - - for dest, src in files: - count += 1 - if count % 10 == 0: - if total: - log_info("Processed {:>4} of {} files", count, total) - else: - log_info("Processed {} files", count) - log_debug("Processing {!s}", src) - - if ( - ns.precompile - and src in PY_FILES - and src not in EXCLUDE_FROM_COMPILE - and src.parent not in DATA_DIRS - and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib")) - ): - if ns.copy: - need_compile.append((dest, ns.copy / dest)) - else: - (ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(src, ns.temp / "Lib" / dest) - need_compile.append((dest, ns.temp / "Lib" / dest)) - - if src not in EXCLUDE_FROM_CATALOG: - in_catalog.append((src.name, src)) - - if ns.copy: - log_debug("Copy {} -> {}", src, ns.copy / dest) - (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True) - try: - shutil.copy2(src, ns.copy / dest) - except shutil.SameFileError: - pass - - if ns.zip: - log_debug("Zip {} into {}", src, ns.zip) - zip_file.write(src, str(dest)) - - if need_compile: - for dest, src in need_compile: - compiled = [ - _compile_one_py(src, None, dest, optimize=0), - _compile_one_py(src, None, dest, optimize=1), - _compile_one_py(src, None, dest, optimize=2), - ] - for c in compiled: - if not c: - continue - cdest = Path(dest).parent / Path(c).relative_to(src.parent) - if ns.zip: - log_debug("Zip {} into {}", c, ns.zip) - zip_file.write(c, str(cdest)) - in_catalog.append((cdest.name, cdest)) - - if ns.catalog: - # Just write out the CDF now. Compilation and signing is - # an extra step - log_info("Generating {}", ns.catalog) - ns.catalog.parent.mkdir(parents=True, exist_ok=True) - write_catalog(ns.catalog, in_catalog) - - finally: - if zip_file: - zip_file.close() - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("-v", help="Increase verbosity", action="count") - parser.add_argument( - "-s", - "--source", - metavar="dir", - help="The directory containing the repository root", - type=Path, - default=None, - ) - parser.add_argument( - "-b", "--build", metavar="dir", help="Specify the build directory", type=Path - ) - parser.add_argument( - "--doc-build", - metavar="dir", - help="Specify the docs build directory", - type=Path, - default=None, - ) - parser.add_argument( - "--copy", - metavar="directory", - help="The name of the directory to copy an extracted layout to", - type=Path, - default=None, - ) - parser.add_argument( - "--zip", - metavar="file", - help="The ZIP file to write all files to", - type=Path, - default=None, - ) - parser.add_argument( - "--catalog", - metavar="file", - help="The CDF file to write catalog entries to", - type=Path, - default=None, - ) - parser.add_argument( - "--log", - metavar="file", - help="Write all operations to the specified file", - type=Path, - default=None, - ) - parser.add_argument( - "-t", - "--temp", - metavar="file", - help="A temporary working directory", - type=Path, - default=None, - ) - parser.add_argument( - "-d", "--debug", help="Include debug build", action="store_true" - ) - parser.add_argument( - "-p", - "--precompile", - help="Include .pyc files instead of .py", - action="store_true", - ) - parser.add_argument( - "-z", "--zip-lib", help="Include library in a ZIP file", action="store_true" - ) - parser.add_argument( - "--flat-dlls", help="Does not create a DLLs directory", action="store_true" - ) - parser.add_argument( - "-a", - "--include-all", - help="Include all optional components", - action="store_true", - ) - parser.add_argument( - "--include-cat", - metavar="file", - help="Specify the catalog file to include", - type=Path, - default=None, - ) - for opt, help in get_argparse_options(): - parser.add_argument(opt, help=help, action="store_true") - - ns = parser.parse_args() - update_presets(ns) - - ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent) - ns.build = ns.build or Path(sys.executable).parent - ns.temp = ns.temp or Path(tempfile.mkdtemp()) - ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build") - if not ns.source.is_absolute(): - ns.source = (Path.cwd() / ns.source).resolve() - if not ns.build.is_absolute(): - ns.build = (Path.cwd() / ns.build).resolve() - if not ns.temp.is_absolute(): - ns.temp = (Path.cwd() / ns.temp).resolve() - if not ns.doc_build.is_absolute(): - ns.doc_build = (Path.cwd() / ns.doc_build).resolve() - if ns.include_cat and not ns.include_cat.is_absolute(): - ns.include_cat = (Path.cwd() / ns.include_cat).resolve() - - if ns.copy and not ns.copy.is_absolute(): - ns.copy = (Path.cwd() / ns.copy).resolve() - if ns.zip and not ns.zip.is_absolute(): - ns.zip = (Path.cwd() / ns.zip).resolve() - if ns.catalog and not ns.catalog.is_absolute(): - ns.catalog = (Path.cwd() / ns.catalog).resolve() - - configure_logger(ns) - - log_info( - """OPTIONS -Source: {ns.source} -Build: {ns.build} -Temp: {ns.temp} - -Copy to: {ns.copy} -Zip to: {ns.zip} -Catalog: {ns.catalog}""", - ns=ns, - ) - - if ns.include_idle and not ns.include_tcltk: - log_warning("Assuming --include-tcltk to support --include-idle") - ns.include_tcltk = True - - try: - generate_source_files(ns) - files = list(get_layout(ns)) - copy_files(files, ns) - except KeyboardInterrupt: - log_info("Interrupted by Ctrl+C") - return 3 - except SystemExit: - raise - except: - log_exception("Unhandled error") - - if error_was_logged(): - log_error("Errors occurred.") - return 1 - - -if __name__ == "__main__": - sys.exit(int(main() or 0)) diff --git a/PC/layout/support/__init__.py b/PC/layout/support/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py deleted file mode 100644 index c5dda70c7ef8..000000000000 --- a/PC/layout/support/appxmanifest.py +++ /dev/null @@ -1,487 +0,0 @@ -""" -File generation for APPX/MSIX manifests. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - - -import collections -import ctypes -import io -import os -import sys - -from pathlib import Path, PureWindowsPath -from xml.etree import ElementTree as ET - -from .constants import * - -__all__ = [] - - -def public(f): - __all__.append(f.__name__) - return f - - -APPX_DATA = dict( - Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT), - Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3), - Publisher=os.getenv( - "APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B" - ), - DisplayName="Python {}".format(VER_DOT), - Description="The Python {} runtime and console.".format(VER_DOT), - ProcessorArchitecture="x64" if IS_X64 else "x86", -) - -PYTHON_VE_DATA = dict( - DisplayName="Python {}".format(VER_DOT), - Description="Python interactive console", - Square150x150Logo="_resources/pythonx150.png", - Square44x44Logo="_resources/pythonx44.png", - BackgroundColor="transparent", -) - -PYTHONW_VE_DATA = dict( - DisplayName="Python {} (Windowed)".format(VER_DOT), - Description="Python windowed app launcher", - Square150x150Logo="_resources/pythonwx150.png", - Square44x44Logo="_resources/pythonwx44.png", - BackgroundColor="transparent", - AppListEntry="none", -) - -PIP_VE_DATA = dict( - DisplayName="pip (Python {})".format(VER_DOT), - Description="pip package manager for Python {}".format(VER_DOT), - Square150x150Logo="_resources/pythonx150.png", - Square44x44Logo="_resources/pythonx44.png", - BackgroundColor="transparent", - AppListEntry="none", -) - -IDLE_VE_DATA = dict( - DisplayName="IDLE (Python {})".format(VER_DOT), - Description="IDLE editor for Python {}".format(VER_DOT), - Square150x150Logo="_resources/pythonwx150.png", - Square44x44Logo="_resources/pythonwx44.png", - BackgroundColor="transparent", -) - -APPXMANIFEST_NS = { - "": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", - "m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", - "uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10", - "rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities", - "rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4", - "desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4", - "desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6", - "uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3", - "uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4", - "uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5", -} - -APPXMANIFEST_TEMPLATE = """ - - - - - Python Software Foundation - - _resources/pythonx50.png - - - - - - - - - - - - - - -""" - - -RESOURCES_XML_TEMPLATE = r""" - - - - - - - - - - - - - - - - - - - - - - - - - - - -""" - - -SCCD_FILENAME = "PC/classicAppCompat.sccd" - -REGISTRY = { - "HKCU\\Software\\Python\\PythonCore": { - VER_DOT: { - "DisplayName": APPX_DATA["DisplayName"], - "SupportUrl": "https://www.python.org/", - "SysArchitecture": "64bit" if IS_X64 else "32bit", - "SysVersion": VER_DOT, - "Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO), - "InstallPath": { - # I have no idea why the trailing spaces are needed, but they seem to be needed. - "": "[{AppVPackageRoot}][ ]", - "ExecutablePath": "[{AppVPackageRoot}]python.exe[ ]", - "WindowedExecutablePath": "[{AppVPackageRoot}]pythonw.exe[ ]", - }, - "Help": { - "Main Python Documentation": { - "_condition": lambda ns: ns.include_chm, - "": "[{{AppVPackageRoot}}]Doc\\{}[ ]".format( - PYTHON_CHM_NAME - ), - }, - "Local Python Documentation": { - "_condition": lambda ns: ns.include_html_doc, - "": "[{AppVPackageRoot}]Doc\\html\\index.html[ ]", - }, - "Online Python Documentation": { - "": "https://docs.python.org/{}".format(VER_DOT) - }, - }, - "Idle": { - "_condition": lambda ns: ns.include_idle, - "": "[{AppVPackageRoot}]Lib\\idlelib\\idle.pyw[ ]", - }, - } - } -} - - -def get_packagefamilyname(name, publisher_id): - class PACKAGE_ID(ctypes.Structure): - _fields_ = [ - ("reserved", ctypes.c_uint32), - ("processorArchitecture", ctypes.c_uint32), - ("version", ctypes.c_uint64), - ("name", ctypes.c_wchar_p), - ("publisher", ctypes.c_wchar_p), - ("resourceId", ctypes.c_wchar_p), - ("publisherId", ctypes.c_wchar_p), - ] - _pack_ = 4 - - pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None) - result = ctypes.create_unicode_buffer(256) - result_len = ctypes.c_uint32(256) - r = ctypes.windll.kernel32.PackageFamilyNameFromId( - pid, ctypes.byref(result_len), result - ) - if r: - raise OSError(r, "failed to get package family name") - return result.value[: result_len.value] - - -def _fixup_sccd(ns, sccd, new_hash=None): - if not new_hash: - return sccd - - NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd") - with open(sccd, "rb") as f: - xml = ET.parse(f) - - pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"]) - - ae = xml.find("s:AuthorizedEntities", NS) - ae.clear() - - e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity")) - e.set("AppPackageFamilyName", pfn) - e.set("CertificateSignatureHash", new_hash) - - for e in xml.findall("s:Catalog", NS): - e.text = "FFFF" - - sccd = ns.temp / sccd.name - sccd.parent.mkdir(parents=True, exist_ok=True) - with open(sccd, "wb") as f: - xml.write(f, encoding="utf-8") - - return sccd - - - at public -def get_appx_layout(ns): - if not ns.include_appxmanifest: - return - - yield "AppxManifest.xml", ns.temp / "AppxManifest.xml" - yield "_resources.xml", ns.temp / "_resources.xml" - icons = ns.source / "PC" / "icons" - yield "_resources/pythonx44.png", icons / "pythonx44.png" - yield "_resources/pythonx44$targetsize-44_altform-unplated.png", icons / "pythonx44.png" - yield "_resources/pythonx50.png", icons / "pythonx50.png" - yield "_resources/pythonx50$targetsize-50_altform-unplated.png", icons / "pythonx50.png" - yield "_resources/pythonx150.png", icons / "pythonx150.png" - yield "_resources/pythonx150$targetsize-150_altform-unplated.png", icons / "pythonx150.png" - yield "_resources/pythonwx44.png", icons / "pythonwx44.png" - yield "_resources/pythonwx44$targetsize-44_altform-unplated.png", icons / "pythonwx44.png" - yield "_resources/pythonwx150.png", icons / "pythonwx150.png" - yield "_resources/pythonwx150$targetsize-150_altform-unplated.png", icons / "pythonwx150.png" - sccd = ns.source / SCCD_FILENAME - if sccd.is_file(): - # This should only be set for side-loading purposes. - sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256")) - yield sccd.name, sccd - - -def find_or_add(xml, element, attr=None, always_add=False): - if always_add: - e = None - else: - q = element - if attr: - q += "[@{}='{}']".format(*attr) - e = xml.find(q, APPXMANIFEST_NS) - if e is None: - prefix, _, name = element.partition(":") - name = ET.QName(APPXMANIFEST_NS[prefix or ""], name) - e = ET.SubElement(xml, name) - if attr: - e.set(*attr) - return e - - -def _get_app(xml, appid): - if appid: - app = xml.find( - "m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS - ) - if app is None: - raise LookupError(appid) - else: - app = xml - return app - - -def add_visual(xml, appid, data): - app = _get_app(xml, appid) - e = find_or_add(app, "uap:VisualElements") - for i in data.items(): - e.set(*i) - return e - - -def add_alias(xml, appid, alias, subsystem="windows"): - app = _get_app(xml, appid) - e = find_or_add(app, "m:Extensions") - e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias")) - e = find_or_add(e, "uap5:AppExecutionAlias") - e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem) - e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias)) - - -def add_file_type(xml, appid, name, suffix, parameters='"%1"'): - app = _get_app(xml, appid) - e = find_or_add(app, "m:Extensions") - e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation")) - e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name)) - e.set("Parameters", parameters) - e = find_or_add(e, "uap:SupportedFileTypes") - if isinstance(suffix, str): - suffix = [suffix] - for s in suffix: - ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s - - -def add_application( - ns, xml, appid, executable, aliases, visual_element, subsystem, file_types -): - node = xml.find("m:Applications", APPXMANIFEST_NS) - suffix = "_d.exe" if ns.debug else ".exe" - app = ET.SubElement( - node, - ET.QName(APPXMANIFEST_NS[""], "Application"), - { - "Id": appid, - "Executable": executable + suffix, - "EntryPoint": "Windows.FullTrustApplication", - ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true", - }, - ) - if visual_element: - add_visual(app, None, visual_element) - for alias in aliases: - add_alias(app, None, alias + suffix, subsystem) - if file_types: - add_file_type(app, None, *file_types) - return app - - -def _get_registry_entries(ns, root="", d=None): - r = root if root else PureWindowsPath("") - if d is None: - d = REGISTRY - for key, value in d.items(): - if key == "_condition": - continue - elif isinstance(value, dict): - cond = value.get("_condition") - if cond and not cond(ns): - continue - fullkey = r - for part in PureWindowsPath(key).parts: - fullkey /= part - if len(fullkey.parts) > 1: - yield str(fullkey), None, None - yield from _get_registry_entries(ns, fullkey, value) - elif len(r.parts) > 1: - yield str(r), key, value - - -def add_registry_entries(ns, xml): - e = find_or_add(xml, "m:Extensions") - e = find_or_add(e, "rescap4:Extension") - e.set("Category", "windows.classicAppCompatKeys") - e.set("EntryPoint", "Windows.FullTrustApplication") - e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys")) - for name, valuename, value in _get_registry_entries(ns): - k = ET.SubElement( - e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey") - ) - k.set("Name", name) - if value: - k.set("ValueName", valuename) - k.set("Value", value) - k.set("ValueType", "REG_SZ") - - -def disable_registry_virtualization(xml): - e = find_or_add(xml, "m:Properties") - e = find_or_add(e, "desktop6:RegistryWriteVirtualization") - e.text = "disabled" - e = find_or_add(xml, "m:Capabilities") - e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources")) - - - at public -def get_appxmanifest(ns): - for k, v in APPXMANIFEST_NS.items(): - ET.register_namespace(k, v) - ET.register_namespace("", APPXMANIFEST_NS["m"]) - - xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE)) - NS = APPXMANIFEST_NS - QN = ET.QName - - node = xml.find("m:Identity", NS) - for k in node.keys(): - value = APPX_DATA.get(k) - if value: - node.set(k, value) - - for node in xml.find("m:Properties", NS): - value = APPX_DATA.get(node.tag.rpartition("}")[2]) - if value: - node.text = value - - winver = sys.getwindowsversion()[:3] - if winver < (10, 0, 17763): - winver = 10, 0, 17763 - find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set( - "MaxVersionTested", "{}.{}.{}.0".format(*winver) - ) - - if winver > (10, 0, 17763): - disable_registry_virtualization(xml) - - app = add_application( - ns, - xml, - "Python", - "python", - ["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)], - PYTHON_VE_DATA, - "console", - ("python.file", [".py"]), - ) - - add_application( - ns, - xml, - "PythonW", - "pythonw", - ["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)], - PYTHONW_VE_DATA, - "windows", - ("python.windowedfile", [".pyw"]), - ) - - if ns.include_pip and ns.include_launchers: - add_application( - ns, - xml, - "Pip", - "pip", - ["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)], - PIP_VE_DATA, - "console", - ("python.wheel", [".whl"], 'install "%1"'), - ) - - if ns.include_idle and ns.include_launchers: - add_application( - ns, - xml, - "Idle", - "idle", - ["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)], - IDLE_VE_DATA, - "windows", - None, - ) - - if (ns.source / SCCD_FILENAME).is_file(): - add_registry_entries(ns, xml) - node = xml.find("m:Capabilities", NS) - node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability")) - node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe") - - buffer = io.BytesIO() - xml.write(buffer, encoding="utf-8", xml_declaration=True) - return buffer.getbuffer() - - - at public -def get_resources_xml(ns): - return RESOURCES_XML_TEMPLATE.encode("utf-8") diff --git a/PC/layout/support/catalog.py b/PC/layout/support/catalog.py deleted file mode 100644 index 43121187ed18..000000000000 --- a/PC/layout/support/catalog.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -File generation for catalog signing non-binary contents. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - - -import sys - -__all__ = ["PYTHON_CAT_NAME", "PYTHON_CDF_NAME"] - - -def public(f): - __all__.append(f.__name__) - return f - - -PYTHON_CAT_NAME = "python.cat" -PYTHON_CDF_NAME = "python.cdf" - - -CATALOG_TEMPLATE = r"""[CatalogHeader] -Name={target.stem}.cat -ResultDir={target.parent} -PublicVersion=1 -CatalogVersion=2 -HashAlgorithms=SHA256 -PageHashes=false -EncodingType= - -[CatalogFiles] -""" - - -def can_sign(file): - return file.is_file() and file.stat().st_size - - - at public -def write_catalog(target, files): - with target.open("w", encoding="utf-8") as cat: - cat.write(CATALOG_TEMPLATE.format(target=target)) - cat.writelines("{}={}\n".format(n, f) for n, f in files if can_sign(f)) diff --git a/PC/layout/support/constants.py b/PC/layout/support/constants.py deleted file mode 100644 index 88ea410b340e..000000000000 --- a/PC/layout/support/constants.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Constants for generating the layout. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - -import struct -import sys - -VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = struct.pack(">i", sys.hexversion) -VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4 -VER_NAME = {"alpha": "a", "beta": "b", "rc": "rc"}.get( - sys.version_info.releaselevel, "" -) -VER_SERIAL = sys.version_info.serial if VER_NAME else "" -VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR) - -PYTHON_DLL_NAME = "python{}{}.dll".format(VER_MAJOR, VER_MINOR) -PYTHON_STABLE_DLL_NAME = "python{}.dll".format(VER_MAJOR) -PYTHON_ZIP_NAME = "python{}{}.zip".format(VER_MAJOR, VER_MINOR) -PYTHON_PTH_NAME = "python{}{}._pth".format(VER_MAJOR, VER_MINOR) - -PYTHON_CHM_NAME = "python{}{}{}{}{}.chm".format( - VER_MAJOR, VER_MINOR, VER_MICRO, VER_NAME, VER_SERIAL -) - -IS_X64 = sys.maxsize > 2 ** 32 diff --git a/PC/layout/support/distutils.command.bdist_wininst.py b/PC/layout/support/distutils.command.bdist_wininst.py deleted file mode 100644 index 6e9b49fe42df..000000000000 --- a/PC/layout/support/distutils.command.bdist_wininst.py +++ /dev/null @@ -1,25 +0,0 @@ -"""distutils.command.bdist_wininst - -Suppress the 'bdist_wininst' command, while still allowing -setuptools to import it without breaking.""" - -from distutils.core import Command -from distutils.errors import DistutilsPlatformError - - -class bdist_wininst(Command): - description = "create an executable installer for MS Windows" - - # Marker for tests that we have the unsupported bdist_wininst - _unsupported = True - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - raise DistutilsPlatformError( - "bdist_wininst is not supported in this Python distribution" - ) diff --git a/PC/layout/support/filesets.py b/PC/layout/support/filesets.py deleted file mode 100644 index 47f727c05784..000000000000 --- a/PC/layout/support/filesets.py +++ /dev/null @@ -1,100 +0,0 @@ -""" -File sets and globbing helper for make_layout. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - -import os - - -class FileStemSet: - def __init__(self, *patterns): - self._names = set() - self._prefixes = [] - self._suffixes = [] - for p in map(os.path.normcase, patterns): - if p.endswith("*"): - self._prefixes.append(p[:-1]) - elif p.startswith("*"): - self._suffixes.append(p[1:]) - else: - self._names.add(p) - - def _make_name(self, f): - return os.path.normcase(f.stem) - - def __contains__(self, f): - bn = self._make_name(f) - return ( - bn in self._names - or any(map(bn.startswith, self._prefixes)) - or any(map(bn.endswith, self._suffixes)) - ) - - -class FileNameSet(FileStemSet): - def _make_name(self, f): - return os.path.normcase(f.name) - - -class FileSuffixSet: - def __init__(self, *patterns): - self._names = set() - self._prefixes = [] - self._suffixes = [] - for p in map(os.path.normcase, patterns): - if p.startswith("*."): - self._names.add(p[1:]) - elif p.startswith("*"): - self._suffixes.append(p[1:]) - elif p.endswith("*"): - self._prefixes.append(p[:-1]) - elif p.startswith("."): - self._names.add(p) - else: - self._names.add("." + p) - - def _make_name(self, f): - return os.path.normcase(f.suffix) - - def __contains__(self, f): - bn = self._make_name(f) - return ( - bn in self._names - or any(map(bn.startswith, self._prefixes)) - or any(map(bn.endswith, self._suffixes)) - ) - - -def _rglob(root, pattern, condition): - dirs = [root] - recurse = pattern[:3] in {"**/", "**\\"} - if recurse: - pattern = pattern[3:] - - while dirs: - d = dirs.pop(0) - if recurse: - dirs.extend( - filter( - condition, (type(root)(f2) for f2 in os.scandir(d) if f2.is_dir()) - ) - ) - yield from ( - (f.relative_to(root), f) - for f in d.glob(pattern) - if f.is_file() and condition(f) - ) - - -def _return_true(f): - return True - - -def rglob(root, patterns, condition=None): - if isinstance(patterns, tuple): - for p in patterns: - yield from _rglob(root, p, condition or _return_true) - else: - yield from _rglob(root, patterns, condition or _return_true) diff --git a/PC/layout/support/logging.py b/PC/layout/support/logging.py deleted file mode 100644 index 30869b949a1c..000000000000 --- a/PC/layout/support/logging.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -Logging support for make_layout. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - -import logging -import sys - -__all__ = [] - -LOG = None -HAS_ERROR = False - - -def public(f): - __all__.append(f.__name__) - return f - - - at public -def configure_logger(ns): - global LOG - if LOG: - return - - LOG = logging.getLogger("make_layout") - LOG.level = logging.DEBUG - - if ns.v: - s_level = max(logging.ERROR - ns.v * 10, logging.DEBUG) - f_level = max(logging.WARNING - ns.v * 10, logging.DEBUG) - else: - s_level = logging.ERROR - f_level = logging.INFO - - handler = logging.StreamHandler(sys.stdout) - handler.setFormatter(logging.Formatter("{levelname:8s} {message}", style="{")) - handler.setLevel(s_level) - LOG.addHandler(handler) - - if ns.log: - handler = logging.FileHandler(ns.log, encoding="utf-8", delay=True) - handler.setFormatter( - logging.Formatter("[{asctime}]{levelname:8s}: {message}", style="{") - ) - handler.setLevel(f_level) - LOG.addHandler(handler) - - -class BraceMessage: - def __init__(self, fmt, *args, **kwargs): - self.fmt = fmt - self.args = args - self.kwargs = kwargs - - def __str__(self): - return self.fmt.format(*self.args, **self.kwargs) - - - at public -def log_debug(msg, *args, **kwargs): - return LOG.debug(BraceMessage(msg, *args, **kwargs)) - - - at public -def log_info(msg, *args, **kwargs): - return LOG.info(BraceMessage(msg, *args, **kwargs)) - - - at public -def log_warning(msg, *args, **kwargs): - return LOG.warning(BraceMessage(msg, *args, **kwargs)) - - - at public -def log_error(msg, *args, **kwargs): - global HAS_ERROR - HAS_ERROR = True - return LOG.error(BraceMessage(msg, *args, **kwargs)) - - - at public -def log_exception(msg, *args, **kwargs): - global HAS_ERROR - HAS_ERROR = True - return LOG.exception(BraceMessage(msg, *args, **kwargs)) - - - at public -def error_was_logged(): - return HAS_ERROR diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py deleted file mode 100644 index 76d9e34e1f46..000000000000 --- a/PC/layout/support/options.py +++ /dev/null @@ -1,122 +0,0 @@ -""" -List of optional components. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - - -__all__ = [] - - -def public(f): - __all__.append(f.__name__) - return f - - -OPTIONS = { - "stable": {"help": "stable ABI stub"}, - "pip": {"help": "pip"}, - "distutils": {"help": "distutils"}, - "tcltk": {"help": "Tcl, Tk and tkinter"}, - "idle": {"help": "Idle"}, - "tests": {"help": "test suite"}, - "tools": {"help": "tools"}, - "venv": {"help": "venv"}, - "dev": {"help": "headers and libs"}, - "symbols": {"help": "symbols"}, - "bdist-wininst": {"help": "bdist_wininst support"}, - "underpth": {"help": "a python._pth file", "not-in-all": True}, - "launchers": {"help": "specific launchers"}, - "appxmanifest": {"help": "an appxmanifest"}, - "props": {"help": "a python.props file"}, - "chm": {"help": "the CHM documentation"}, - "html-doc": {"help": "the HTML documentation"}, -} - - -PRESETS = { - "appx": { - "help": "APPX package", - "options": [ - "stable", - "pip", - "distutils", - "tcltk", - "idle", - "venv", - "dev", - "launchers", - "appxmanifest", - # XXX: Disabled for now "precompile", - ], - }, - "nuget": { - "help": "nuget package", - "options": ["stable", "pip", "distutils", "dev", "props"], - }, - "default": { - "help": "development kit package", - "options": [ - "stable", - "pip", - "distutils", - "tcltk", - "idle", - "tests", - "tools", - "venv", - "dev", - "symbols", - "bdist-wininst", - "chm", - ], - }, - "embed": { - "help": "embeddable package", - "options": ["stable", "zip-lib", "flat-dlls", "underpth", "precompile"], - }, -} - - - at public -def get_argparse_options(): - for opt, info in OPTIONS.items(): - help = "When specified, includes {}".format(info["help"]) - if info.get("not-in-all"): - help = "{}. Not affected by --include-all".format(help) - - yield "--include-{}".format(opt), help - - for opt, info in PRESETS.items(): - help = "When specified, includes default options for {}".format(info["help"]) - yield "--preset-{}".format(opt), help - - -def ns_get(ns, key, default=False): - return getattr(ns, key.replace("-", "_"), default) - - -def ns_set(ns, key, value=True): - k1 = key.replace("-", "_") - k2 = "include_{}".format(k1) - if hasattr(ns, k2): - setattr(ns, k2, value) - elif hasattr(ns, k1): - setattr(ns, k1, value) - else: - raise AttributeError("no argument named '{}'".format(k1)) - - - at public -def update_presets(ns): - for preset, info in PRESETS.items(): - if ns_get(ns, "preset-{}".format(preset)): - for opt in info["options"]: - ns_set(ns, opt) - - if ns.include_all: - for opt in OPTIONS: - if OPTIONS[opt].get("not-in-all"): - continue - ns_set(ns, opt) diff --git a/PC/layout/support/pip.py b/PC/layout/support/pip.py deleted file mode 100644 index 369a923ce139..000000000000 --- a/PC/layout/support/pip.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Extraction and file list generation for pip. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - - -import os -import shutil -import subprocess -import sys - -__all__ = [] - - -def public(f): - __all__.append(f.__name__) - return f - - - at public -def get_pip_dir(ns): - if ns.copy: - if ns.zip_lib: - return ns.copy / "packages" - return ns.copy / "Lib" / "site-packages" - else: - return ns.temp / "packages" - - - at public -def extract_pip_files(ns): - dest = get_pip_dir(ns) - dest.mkdir(parents=True, exist_ok=True) - - src = ns.source / "Lib" / "ensurepip" / "_bundled" - - ns.temp.mkdir(parents=True, exist_ok=True) - wheels = [shutil.copy(whl, ns.temp) for whl in src.glob("*.whl")] - search_path = os.pathsep.join(wheels) - if os.environ.get("PYTHONPATH"): - search_path += ";" + os.environ["PYTHONPATH"] - - env = os.environ.copy() - env["PYTHONPATH"] = search_path - - output = subprocess.check_output( - [ - sys.executable, - "-m", - "pip", - "--no-color", - "install", - "pip", - "setuptools", - "--upgrade", - "--target", - str(dest), - "--no-index", - "--no-cache-dir", - "-f", - str(src), - "--only-binary", - ":all:", - ], - env=env, - ) - - try: - shutil.rmtree(dest / "bin") - except OSError: - pass - - for file in wheels: - try: - os.remove(file) - except OSError: - pass diff --git a/PC/layout/support/props.py b/PC/layout/support/props.py deleted file mode 100644 index 3a047d215058..000000000000 --- a/PC/layout/support/props.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -Provides .props file. -""" - -import os - -from .constants import * - -__all__ = ["PYTHON_PROPS_NAME"] - - -def public(f): - __all__.append(f.__name__) - return f - - -PYTHON_PROPS_NAME = "python.props" - -PROPS_DATA = { - "PYTHON_TAG": VER_DOT, - "PYTHON_VERSION": os.getenv("PYTHON_NUSPEC_VERSION"), - "PYTHON_PLATFORM": os.getenv("PYTHON_PROPS_PLATFORM"), - "PYTHON_TARGET": "", -} - -if not PROPS_DATA["PYTHON_VERSION"]: - if VER_NAME: - PROPS_DATA["PYTHON_VERSION"] = "{}.{}-{}{}".format( - VER_DOT, VER_MICRO, VER_NAME, VER_SERIAL - ) - else: - PROPS_DATA["PYTHON_VERSION"] = "{}.{}".format(VER_DOT, VER_MICRO) - -if not PROPS_DATA["PYTHON_PLATFORM"]: - PROPS_DATA["PYTHON_PLATFORM"] = "x64" if IS_X64 else "Win32" - -PROPS_DATA["PYTHON_TARGET"] = "_GetPythonRuntimeFilesDependsOn{}{}_{}".format( - VER_MAJOR, VER_MINOR, PROPS_DATA["PYTHON_PLATFORM"] -) - -PROPS_TEMPLATE = r""" - - - $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python_d.exe") - $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python.exe") - $(PythonHome)\include - $(PythonHome)\libs - {PYTHON_TAG} - {PYTHON_VERSION} - - true - false - false - false - - {PYTHON_TARGET};$(GetPythonRuntimeFilesDependsOn) - - - - - $(PythonInclude);%(AdditionalIncludeDirectories) - MultiThreadedDLL - - - $(PythonLibs);%(AdditionalLibraryDirectories) - - - - - - - - <_PythonRuntimeExe Include="$(PythonHome)\python*.dll" /> - <_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" /> - <_PythonRuntimeExe> - %(Filename)%(Extension) - - <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" /> - <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" /> - <_PythonRuntimeDlls> - DLLs\%(Filename)%(Extension) - - <_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" /> - <_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" /> - <_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" /> - <_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" /> - <_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" /> - <_PythonRuntimeLib> - Lib\%(RecursiveDir)%(Filename)%(Extension) - - - - - - - -""" - - - at public -def get_props_layout(ns): - if ns.include_all or ns.include_props: - yield "python.props", ns.temp / "python.props" - - - at public -def get_props(ns): - # TODO: Filter contents of props file according to included/excluded items - props = PROPS_TEMPLATE.format_map(PROPS_DATA) - return props.encode("utf-8") diff --git a/PC/pylauncher.rc b/PC/pylauncher.rc index 92987af7138d..3da3445f5fc4 100644 --- a/PC/pylauncher.rc +++ b/PC/pylauncher.rc @@ -7,11 +7,6 @@ #include 1 RT_MANIFEST "python.manifest" -#if defined(PY_ICON) -1 ICON DISCARDABLE "icons\python.ico" -#elif defined(PYW_ICON) -1 ICON DISCARDABLE "icons\pythonw.ico" -#else 1 ICON DISCARDABLE "icons\launcher.ico" 2 ICON DISCARDABLE "icons\py.ico" 3 ICON DISCARDABLE "icons\pyc.ico" @@ -19,7 +14,6 @@ 5 ICON DISCARDABLE "icons\python.ico" 6 ICON DISCARDABLE "icons\pythonw.ico" 7 ICON DISCARDABLE "icons\setup.ico" -#endif ///////////////////////////////////////////////////////////////////////////// // diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp deleted file mode 100644 index 1658d05994bb..000000000000 --- a/PC/python_uwp.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* Main program when embedded in a UWP application on Windows */ - -#include "Python.h" -#include - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include - -#ifdef PYTHONW -#ifdef _DEBUG -const wchar_t *PROGNAME = L"pythonw_d.exe"; -#else -const wchar_t *PROGNAME = L"pythonw.exe"; -#endif -#else -#ifdef _DEBUG -const wchar_t *PROGNAME = L"python_d.exe"; -#else -const wchar_t *PROGNAME = L"python.exe"; -#endif -#endif - -static void -set_user_base() -{ - wchar_t envBuffer[2048]; - try { - const auto appData = winrt::Windows::Storage::ApplicationData::Current(); - if (appData) { - const auto localCache = appData.LocalCacheFolder(); - if (localCache) { - auto path = localCache.Path(); - if (!path.empty() && - !wcscpy_s(envBuffer, path.c_str()) && - !wcscat_s(envBuffer, L"\\local-packages") - ) { - _wputenv_s(L"PYTHONUSERBASE", envBuffer); - } - } - } - } catch (...) { - } -} - -static const wchar_t * -get_argv0(const wchar_t *argv0) -{ - winrt::hstring installPath; - const wchar_t *launcherPath; - wchar_t *buffer; - size_t len; - - launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__"); - if (launcherPath && launcherPath[0]) { - len = wcslen(launcherPath) + 1; - buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); - if (!buffer) { - Py_FatalError("out of memory"); - return NULL; - } - if (wcscpy_s(buffer, len, launcherPath)) { - Py_FatalError("failed to copy to buffer"); - return NULL; - } - return buffer; - } - - try { - const auto package = winrt::Windows::ApplicationModel::Package::Current(); - if (package) { - const auto install = package.InstalledLocation(); - if (install) { - installPath = install.Path(); - } - } - } - catch (...) { - } - - if (!installPath.empty()) { - len = installPath.size() + wcslen(PROGNAME) + 2; - } else { - len = wcslen(argv0) + wcslen(PROGNAME) + 1; - } - - buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); - if (!buffer) { - Py_FatalError("out of memory"); - return NULL; - } - - if (!installPath.empty()) { - if (wcscpy_s(buffer, len, installPath.c_str())) { - Py_FatalError("failed to copy to buffer"); - return NULL; - } - if (wcscat_s(buffer, len, L"\\")) { - Py_FatalError("failed to concatenate backslash"); - return NULL; - } - } else { - if (wcscpy_s(buffer, len, argv0)) { - Py_FatalError("failed to copy argv[0]"); - return NULL; - } - - wchar_t *name = wcsrchr(buffer, L'\\'); - if (name) { - name[1] = L'\0'; - } else { - buffer[0] = L'\0'; - } - } - - if (wcscat_s(buffer, len, PROGNAME)) { - Py_FatalError("failed to concatenate program name"); - return NULL; - } - - return buffer; -} - -static wchar_t * -get_process_name() -{ - DWORD bufferLen = MAX_PATH; - DWORD len = bufferLen; - wchar_t *r = NULL; - - while (!r) { - r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); - if (!r) { - Py_FatalError("out of memory"); - return NULL; - } - len = GetModuleFileNameW(NULL, r, bufferLen); - if (len == 0) { - free((void *)r); - return NULL; - } else if (len == bufferLen && - GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - free(r); - r = NULL; - bufferLen *= 2; - } - } - - return r; -} - -int -wmain(int argc, wchar_t **argv) -{ - const wchar_t **new_argv; - int new_argc; - const wchar_t *exeName; - - new_argc = argc; - new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2)); - if (new_argv == NULL) { - Py_FatalError("out of memory"); - return -1; - } - - exeName = get_process_name(); - - new_argv[0] = get_argv0(exeName ? exeName : argv[0]); - for (int i = 1; i < argc; ++i) { - new_argv[i] = argv[i]; - } - - set_user_base(); - - if (exeName) { - const wchar_t *p = wcsrchr(exeName, L'\\'); - if (p) { - const wchar_t *moduleName = NULL; - if (*p++ == L'\\') { - if (wcsnicmp(p, L"pip", 3) == 0) { - moduleName = L"pip"; - _wputenv_s(L"PIP_USER", L"true"); - } - else if (wcsnicmp(p, L"idle", 4) == 0) { - moduleName = L"idlelib"; - } - } - - if (moduleName) { - new_argc += 2; - for (int i = argc; i >= 1; --i) { - new_argv[i + 2] = new_argv[i]; - } - new_argv[1] = L"-m"; - new_argv[2] = moduleName; - } - } - } - - /* Override program_full_path from here so that - sys.executable is set correctly. */ - _Py_SetProgramFullPath(new_argv[0]); - - int result = Py_Main(new_argc, (wchar_t **)new_argv); - - free((void *)exeName); - free((void *)new_argv); - - return result; -} - -#ifdef PYTHONW - -int WINAPI wWinMain( - HINSTANCE hInstance, /* handle to current instance */ - HINSTANCE hPrevInstance, /* handle to previous instance */ - LPWSTR lpCmdLine, /* pointer to command line */ - int nCmdShow /* show state of window */ -) -{ - return wmain(__argc, __wargv); -} - -#endif diff --git a/PC/store_info.txt b/PC/store_info.txt deleted file mode 100644 index ed40a918e2e7..000000000000 --- a/PC/store_info.txt +++ /dev/null @@ -1,146 +0,0 @@ -# Overview - -NOTE: This file requires more content. - -Since Python 3.8.2, releases have been made through the Microsoft Store -to allow easy installation on Windows 10.0.17763.0 and later. - -# Building - -To build the store package, the PC/layout script should be used. -Execute the directory with the build of Python to package, and pass -"-h" for full command-line options. - -To sideload test builds, you will need a local certificate. -Instructions are available at -https://docs.microsoft.com/windows/uwp/packaging/create-certificate-package-signing. - -After exporting your certificate, you will need the subject name and -SHA256 hash. The `certutil -dump ` command will display this -information. - -To build for sideloading, use these commands in PowerShell: - -``` -$env:APPX_DATA_PUBLISHER= -$env:APPX_DATA_SHA256= -$env:SigningCertificateFile= - -python PC/layout --copy --include-appxmanifest -Tools/msi/make_appx.ps1 python.msix -sign - -Add-AppxPackage python.msix -``` - -(Note that only the last command requires PowerShell, and the others -can be used from Command Prompt. You can also double-click to install -the final package.) - -To build for publishing to the Store, use these commands: - -``` -$env:APPX_DATA_PUBLISHER = $null -$env:APPX_DATA_SHA256 = $null - -python PC/layout --copy --preset-appxmanifest --precompile -Tools/msi/make_appx.ps1 python.msix -``` - -Note that this package cannot be installed locally. It may only be -added to a submission for the store. - - -# Submission Metadata - -This file contains the text that we use to fill out the store listing -for the Microsoft Store. It needs to be entered manually when creating -a new submission via the dashboard at -https://partner.microsoft.com/dashboard. - -We keep it here for convenience and to allow it to be updated via pull -requests. - -## Title - -Python 3.8 - -## Short Title - -Python - -## Description - -Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python?s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms. - -The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python Web site, https://www.python.org/, and may be freely distributed. The same site also contains distributions of and pointers to many free third party Python modules, programs and tools, and additional documentation. - -The Python interpreter is easily extended with new functions and data types implemented in C or C++ (or other languages callable from C). Python is also suitable as an extension language for customizable applications. - -## ShortDescription - -The Python 3.8 interpreter and runtime. - -## Copyright Trademark Information - -(c) Python Software Foundation - -## Additional License Terms - -Visit https://docs.python.org/3.8/license.html for latest license terms. - -PSF LICENSE AGREEMENT FOR PYTHON 3.8 - -1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and - the Individual or Organization ("Licensee") accessing and otherwise using Python - 3.8 software in source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby - grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, - analyze, test, perform and/or display publicly, prepare derivative works, - distribute, and otherwise use Python 3.8 alone or in any derivative - version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright ? 2001-2018 Python Software Foundation; All Rights - Reserved" are retained in Python 3.8 alone or in any derivative version - prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on or - incorporates Python 3.8 or any part thereof, and wants to make the - derivative work available to others as provided herein, then Licensee hereby - agrees to include in any such work a brief summary of the changes made to Python - 3.8. - -4. PSF is making Python 3.8 available to Licensee on an "AS IS" basis. - PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF - EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR - WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE - USE OF PYTHON 3.8 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.8 - FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF - MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.8, OR ANY DERIVATIVE - THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material breach of - its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any relationship - of agency, partnership, or joint venture between PSF and Licensee. This License - Agreement does not grant permission to use PSF trademarks or trade name in a - trademark sense to endorse or promote products or services of Licensee, or any - third party. - -8. By copying, installing or otherwise using Python 3.8, Licensee agrees - to be bound by the terms and conditions of this License Agreement. - -## Features - -* Easy to install Python runtime -* Supported by core CPython team -* Find Python, Pip and Idle on PATH - -## Search Terms - -* Python -* Scripting -* Interpreter - diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj index bd61c0d4f689..95e3cd50eca5 100644 --- a/PCbuild/_tkinter.vcxproj +++ b/PCbuild/_tkinter.vcxproj @@ -95,10 +95,4 @@ - - - - - - \ No newline at end of file diff --git a/PCbuild/find_msbuild.bat b/PCbuild/find_msbuild.bat index a2810f09c45e..57512a01927e 100644 --- a/PCbuild/find_msbuild.bat +++ b/PCbuild/find_msbuild.bat @@ -29,16 +29,6 @@ @where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc" @if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found - at rem VS 2017 and later provide vswhere.exe, which can be used - at if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere - at set _Py_MSBuild_Root= - at for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest') DO @(set _Py_MSBuild_Root=%%i\MSBuild) - at if not defined _Py_MSBuild_Root goto :skip_vswhere - at for %%j in (Current 15.0) DO @if exist "%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe" (set MSBUILD="%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe") - at set _Py_MSBuild_Root= - at if defined MSBUILD @if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio installation) & goto :found -:skip_vswhere - @rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there. @reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul @if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @( diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index 6bf1667e39f8..9e103e12103f 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -52,8 +52,6 @@ - - @@ -72,7 +70,6 @@ - diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index c212d9f8f32c..59b3861ed406 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -93,14 +93,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_uwp", "python_uwp.vcxproj", "{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvlauncher", "venvlauncher.vcxproj", "{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvwlauncher", "venvwlauncher.vcxproj", "{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythonw_uwp", "pythonw_uwp.vcxproj", "{AB603547-1E2A-45B3-9E09-B04596006393}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -701,70 +693,6 @@ Global {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.ActiveCfg = Debug|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.Build.0 = Debug|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.ActiveCfg = Debug|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.Build.0 = Debug|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.Build.0 = PGInstrument|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.Build.0 = PGUpdate|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.ActiveCfg = Release|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.Build.0 = Release|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.ActiveCfg = Release|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.Build.0 = Release|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.ActiveCfg = Debug|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.Build.0 = Debug|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.ActiveCfg = Debug|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.Build.0 = Debug|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.Build.0 = PGInstrument|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.Build.0 = PGUpdate|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.ActiveCfg = Release|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.Build.0 = Release|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.ActiveCfg = Release|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.Build.0 = Release|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.ActiveCfg = Debug|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.Build.0 = Debug|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.ActiveCfg = Debug|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.Build.0 = Debug|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.Build.0 = PGInstrument|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.Build.0 = PGUpdate|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.ActiveCfg = Release|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.Build.0 = Release|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.ActiveCfg = Release|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.Build.0 = Release|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.ActiveCfg = Debug|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.Build.0 = Debug|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.ActiveCfg = Debug|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.Build.0 = Debug|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.Build.0 = PGInstrument|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.Build.0 = PGUpdate|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.ActiveCfg = Release|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.Build.0 = Release|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.ActiveCfg = Release|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PCbuild/python.props b/PCbuild/python.props index 6dbb503b3243..09f11d3bba8c 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -77,8 +77,7 @@ --> <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) - 10.0.17763.0 - 10.0.17134.0 + 10.0.17134.0 10.0.16299.0 10.0.15063.0 10.0.14393.0 diff --git a/PCbuild/python_uwp.vcxproj b/PCbuild/python_uwp.vcxproj deleted file mode 100644 index af187dd4df30..000000000000 --- a/PCbuild/python_uwp.vcxproj +++ /dev/null @@ -1,86 +0,0 @@ -? - - - - Debug - Win32 - - - Debug - x64 - - - PGInstrument - Win32 - - - PGInstrument - x64 - - - PGUpdate - Win32 - - - PGUpdate - x64 - - - Release - Win32 - - - Release - x64 - - - - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF} - - - - - Application - false - Unicode - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - - - - %(PreprocessorDefinitions) - /EHsc /std:c++17 %(AdditionalOptions) - - - windowsapp.lib;%(AdditionalDependencies) - Console - - - - - - - - - - - - - - {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} - false - - - - - - diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 78ec9a16efa7..4ae2d692eee1 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -479,19 +479,4 @@ - - - $(VCInstallDir)\Redist\MSVC\$(VCToolsRedistVersion)\ - $(VCRedistDir)x86\ - $(VCRedistDir)$(Platform)\ - - - - - - - - - - diff --git a/PCbuild/pythonw_uwp.vcxproj b/PCbuild/pythonw_uwp.vcxproj deleted file mode 100644 index 79e105877fbe..000000000000 --- a/PCbuild/pythonw_uwp.vcxproj +++ /dev/null @@ -1,86 +0,0 @@ -? - - - - Debug - Win32 - - - Debug - x64 - - - PGInstrument - Win32 - - - PGInstrument - x64 - - - PGUpdate - Win32 - - - PGUpdate - x64 - - - Release - Win32 - - - Release - x64 - - - - {AB603547-1E2A-45B3-9E09-B04596006393} - - - - - Application - false - Unicode - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - - - - PYTHONW;%(PreprocessorDefinitions) - /EHsc /std:c++17 %(AdditionalOptions) - - - windowsapp.lib;%(AdditionalDependencies) - Windows - - - - - - - - - - - - - - {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} - false - - - - - - diff --git a/PCbuild/venvlauncher.vcxproj b/PCbuild/venvlauncher.vcxproj deleted file mode 100644 index 295b36304733..000000000000 --- a/PCbuild/venvlauncher.vcxproj +++ /dev/null @@ -1,85 +0,0 @@ -? - - - - Debug - Win32 - - - Debug - x64 - - - PGInstrument - Win32 - - - PGInstrument - x64 - - - PGUpdate - Win32 - - - PGUpdate - x64 - - - Release - Win32 - - - Release - x64 - - - - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D} - venvlauncher - venvlauncher - false - - - - - Application - MultiByte - - - - - - ClCompile - - - - - - - - - _CONSOLE;VENV_REDIRECT;%(PreprocessorDefinitions) - MultiThreaded - - - PY_ICON;%(PreprocessorDefinitions) - - - version.lib;%(AdditionalDependencies) - Console - - - - - - - - - - - - - - - diff --git a/PCbuild/venvwlauncher.vcxproj b/PCbuild/venvwlauncher.vcxproj deleted file mode 100644 index e7ba25da41eb..000000000000 --- a/PCbuild/venvwlauncher.vcxproj +++ /dev/null @@ -1,85 +0,0 @@ -? - - - - Debug - Win32 - - - Debug - x64 - - - PGInstrument - Win32 - - - PGInstrument - x64 - - - PGUpdate - Win32 - - - PGUpdate - x64 - - - Release - Win32 - - - Release - x64 - - - - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D} - venvwlauncher - venvwlauncher - false - - - - - Application - MultiByte - - - - - - ClCompile - - - - - - - - - _WINDOWS;VENV_REDIRECT;%(PreprocessorDefinitions) - MultiThreaded - - - PYW_ICON;%(PreprocessorDefinitions) - - - version.lib;%(AdditionalDependencies) - Windows - - - - - - - - - - - - - - - diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat index 45e189b537f6..4178981195ee 100644 --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -37,7 +37,6 @@ set BUILDX64= set TARGET=Rebuild set TESTTARGETDIR= set PGO=-m test -q --pgo -set BUILDMSI=1 set BUILDNUGET=1 set BUILDZIP=1 @@ -62,7 +61,6 @@ if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts -if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 @@ -176,12 +174,10 @@ if "%OUTDIR_PLAT%" EQU "win32" ( ) set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI% -if defined BUILDMSI ( - %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true - if errorlevel 1 exit /B - %MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false - if errorlevel 1 exit /B -) +%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true +if errorlevel 1 exit /B +%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false +if errorlevel 1 exit /B if defined BUILDZIP ( %MSBUILD% "%D%make_zip.proj" /t:Build %BUILDOPTS% %CERTOPTS% /p:OutputPath="%BUILD%en-us" @@ -218,7 +214,6 @@ echo --skip-build (-B) Do not build Python (just do the installers) echo --skip-doc (-D) Do not build documentation echo --pgo Specify PGO command for x64 installers echo --skip-pgo Build x64 installers without using PGO -echo --skip-msi Do not build executable/MSI packages echo --skip-nuget Do not build Nuget packages echo --skip-zip Do not build embeddable package echo --download Specify the full download URL for MSIs diff --git a/Tools/msi/make_appx.ps1 b/Tools/msi/make_appx.ps1 deleted file mode 100644 index b3f190e07db8..000000000000 --- a/Tools/msi/make_appx.ps1 +++ /dev/null @@ -1,71 +0,0 @@ -<# -.Synopsis - Compiles and signs an APPX package -.Description - Given the file listing, ensures all the contents are signed - and builds and signs the final package. -.Parameter mapfile - The location on disk of the text mapping file. -.Parameter msix - The path and name to store the APPX/MSIX. -.Parameter sign - When set, signs the APPX/MSIX. Packages to be published to - the store should not be signed. -.Parameter description - Description to embed in the signature (optional). -.Parameter certname - The name of the certificate to sign with (optional). -.Parameter certsha1 - The SHA1 hash of the certificate to sign with (optional). -#> -param( - [Parameter(Mandatory=$true)][string]$layout, - [Parameter(Mandatory=$true)][string]$msix, - [switch]$sign, - [string]$description, - [string]$certname, - [string]$certsha1, - [string]$certfile -) - -$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; -Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force - -Set-Alias makeappx (Find-Tool "makeappx.exe") -Scope Script -Set-Alias makepri (Find-Tool "makepri.exe") -Scope Script - -$msixdir = Split-Path $msix -Parent -if ($msixdir) { - $msixdir = (mkdir -Force $msixdir).FullName -} else { - $msixdir = Get-Location -} -$msix = Join-Path $msixdir (Split-Path $msix -Leaf) - -pushd $layout -try { - if (Test-Path resources.pri) { - del resources.pri - } - $name = ([xml](gc AppxManifest.xml)).Package.Identity.Name - makepri new /pr . /mn AppxManifest.xml /in $name /cf _resources.xml /of _resources.pri /mf appx /o - if (-not $? -or -not (Test-Path _resources.map.txt)) { - throw "makepri step failed" - } - $lines = gc _resources.map.txt - $lines | ?{ -not ($_ -match '"_resources[\w\.]+?"') } | Out-File _resources.map.txt -Encoding utf8 - makeappx pack /f _resources.map.txt /m AppxManifest.xml /o /p $msix - if (-not $?) { - throw "makeappx step failed" - } -} finally { - popd -} - -if ($sign) { - Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files $msix - - if (-not $?) { - throw "Package signing failed" - } -} diff --git a/Tools/msi/make_cat.ps1 b/Tools/msi/make_cat.ps1 deleted file mode 100644 index 70741439869a..000000000000 --- a/Tools/msi/make_cat.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -<# -.Synopsis - Compiles and signs a catalog file. -.Description - Given the CDF definition file, builds and signs a catalog. -.Parameter catalog - The path to the catalog definition file to compile and - sign. It is assumed that the .cat file will be the same - name with a new extension. -.Parameter description - The description to add to the signature (optional). -.Parameter certname - The name of the certificate to sign with (optional). -.Parameter certsha1 - The SHA1 hash of the certificate to sign with (optional). -#> -param( - [Parameter(Mandatory=$true)][string]$catalog, - [string]$description, - [string]$certname, - [string]$certsha1, - [string]$certfile -) - -$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; -Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force - -Set-Alias MakeCat (Find-Tool "makecat.exe") -Scope Script - -MakeCat $catalog -if (-not $?) { - throw "Catalog compilation failed" -} -Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files @($catalog -replace 'cdf$', 'cat') diff --git a/Tools/msi/make_zip.proj b/Tools/msi/make_zip.proj index 125a434e51f4..214111734219 100644 --- a/Tools/msi/make_zip.proj +++ b/Tools/msi/make_zip.proj @@ -15,12 +15,11 @@ .zip $(OutputPath)\$(TargetName)$(TargetExt) rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)" - "$(PythonExe)" "$(PySourcePath)PC\layout" - $(Arguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" - $(Arguments) -t "$(IntermediateOutputPath)\zip_$(ArchName)" - $(Arguments) --zip "$(TargetPath)" - $(Arguments) --precompile --zip-lib --include-underpth --include-stable --flat-dlls + "$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py" + $(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -b "$(BuildPath.TrimEnd(`\`))" + set DOC_FILENAME=python$(PythonVersion).chm $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib + $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) diff --git a/Tools/msi/make_zip.py b/Tools/msi/make_zip.py new file mode 100644 index 000000000000..58f3b15ef852 --- /dev/null +++ b/Tools/msi/make_zip.py @@ -0,0 +1,250 @@ +import argparse +import py_compile +import re +import sys +import shutil +import stat +import os +import tempfile + +from itertools import chain +from pathlib import Path +from zipfile import ZipFile, ZIP_DEFLATED + + +TKTCL_RE = re.compile(r'^(_?tk|tcl).+\.(pyd|dll)', re.IGNORECASE) +DEBUG_RE = re.compile(r'_d\.(pyd|dll|exe|pdb|lib)$', re.IGNORECASE) +PYTHON_DLL_RE = re.compile(r'python\d\d?\.dll$', re.IGNORECASE) + +DEBUG_FILES = { + '_ctypes_test', + '_testbuffer', + '_testcapi', + '_testconsole', + '_testimportmultiple', + '_testmultiphase', + 'xxlimited', + 'python3_dstub', +} + +EXCLUDE_FROM_LIBRARY = { + '__pycache__', + 'idlelib', + 'pydoc_data', + 'site-packages', + 'tkinter', + 'turtledemo', +} + +EXCLUDE_FROM_EMBEDDABLE_LIBRARY = { + 'ensurepip', + 'venv', +} + +EXCLUDE_FILE_FROM_LIBRARY = { + 'bdist_wininst.py', +} + +EXCLUDE_FILE_FROM_LIBS = { + 'liblzma', + 'python3stub', +} + +EXCLUDED_FILES = { + 'pyshellext', +} + +def is_not_debug(p): + if DEBUG_RE.search(p.name): + return False + + if TKTCL_RE.search(p.name): + return False + + return p.stem.lower() not in DEBUG_FILES and p.stem.lower() not in EXCLUDED_FILES + +def is_not_debug_or_python(p): + return is_not_debug(p) and not PYTHON_DLL_RE.search(p.name) + +def include_in_lib(p): + name = p.name.lower() + if p.is_dir(): + if name in EXCLUDE_FROM_LIBRARY: + return False + if name == 'test' and p.parts[-2].lower() == 'lib': + return False + if name in {'test', 'tests'} and p.parts[-3].lower() == 'lib': + return False + return True + + if name in EXCLUDE_FILE_FROM_LIBRARY: + return False + + suffix = p.suffix.lower() + return suffix not in {'.pyc', '.pyo', '.exe'} + +def include_in_embeddable_lib(p): + if p.is_dir() and p.name.lower() in EXCLUDE_FROM_EMBEDDABLE_LIBRARY: + return False + + return include_in_lib(p) + +def include_in_libs(p): + if not is_not_debug(p): + return False + + return p.stem.lower() not in EXCLUDE_FILE_FROM_LIBS + +def include_in_tools(p): + if p.is_dir() and p.name.lower() in {'scripts', 'i18n', 'pynche', 'demo', 'parser'}: + return True + + return p.suffix.lower() in {'.py', '.pyw', '.txt'} + +BASE_NAME = 'python{0.major}{0.minor}'.format(sys.version_info) + +FULL_LAYOUT = [ + ('/', '$build', 'python.exe', is_not_debug), + ('/', '$build', 'pythonw.exe', is_not_debug), + ('/', '$build', 'python{}.dll'.format(sys.version_info.major), is_not_debug), + ('/', '$build', '{}.dll'.format(BASE_NAME), is_not_debug), + ('DLLs/', '$build', '*.pyd', is_not_debug), + ('DLLs/', '$build', '*.dll', is_not_debug_or_python), + ('include/', 'include', '*.h', None), + ('include/', 'PC', 'pyconfig.h', None), + ('Lib/', 'Lib', '**/*', include_in_lib), + ('libs/', '$build', '*.lib', include_in_libs), + ('Tools/', 'Tools', '**/*', include_in_tools), +] + +EMBED_LAYOUT = [ + ('/', '$build', 'python*.exe', is_not_debug), + ('/', '$build', '*.pyd', is_not_debug), + ('/', '$build', '*.dll', is_not_debug), + ('{}.zip'.format(BASE_NAME), 'Lib', '**/*', include_in_embeddable_lib), +] + +if os.getenv('DOC_FILENAME'): + FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None)) +if os.getenv('VCREDIST_PATH'): + FULL_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) + EMBED_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) + +def copy_to_layout(target, rel_sources): + count = 0 + + if target.suffix.lower() == '.zip': + if target.exists(): + target.unlink() + + with ZipFile(str(target), 'w', ZIP_DEFLATED) as f: + with tempfile.TemporaryDirectory() as tmpdir: + for s, rel in rel_sources: + if rel.suffix.lower() == '.py': + pyc = Path(tmpdir) / rel.with_suffix('.pyc').name + try: + py_compile.compile(str(s), str(pyc), str(rel), doraise=True, optimize=2) + except py_compile.PyCompileError: + f.write(str(s), str(rel)) + else: + f.write(str(pyc), str(rel.with_suffix('.pyc'))) + else: + f.write(str(s), str(rel)) + count += 1 + + else: + for s, rel in rel_sources: + dest = target / rel + try: + dest.parent.mkdir(parents=True) + except FileExistsError: + pass + if dest.is_file(): + dest.chmod(stat.S_IWRITE) + shutil.copy(str(s), str(dest)) + if dest.is_file(): + dest.chmod(stat.S_IWRITE) + count += 1 + + return count + +def rglob(root, pattern, condition): + dirs = [root] + recurse = pattern[:3] in {'**/', '**\\'} + while dirs: + d = dirs.pop(0) + for f in d.glob(pattern[3:] if recurse else pattern): + if recurse and f.is_dir() and (not condition or condition(f)): + dirs.append(f) + elif f.is_file() and (not condition or condition(f)): + yield f, f.relative_to(root) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-s', '--source', metavar='dir', help='The directory containing the repository root', type=Path) + parser.add_argument('-o', '--out', metavar='file', help='The name of the output archive', type=Path, default=None) + parser.add_argument('-t', '--temp', metavar='dir', help='A directory to temporarily extract files into', type=Path, default=None) + parser.add_argument('-e', '--embed', help='Create an embedding layout', action='store_true', default=False) + parser.add_argument('-b', '--build', help='Specify the build directory', type=Path, default=None) + ns = parser.parse_args() + + source = ns.source or (Path(__file__).resolve().parent.parent.parent) + out = ns.out + build = ns.build or Path(sys.exec_prefix) + assert isinstance(source, Path) + assert not out or isinstance(out, Path) + assert isinstance(build, Path) + + if ns.temp: + temp = ns.temp + delete_temp = False + else: + temp = Path(tempfile.mkdtemp()) + delete_temp = True + + if out: + try: + out.parent.mkdir(parents=True) + except FileExistsError: + pass + try: + temp.mkdir(parents=True) + except FileExistsError: + pass + + layout = EMBED_LAYOUT if ns.embed else FULL_LAYOUT + + try: + for t, s, p, c in layout: + if s == '$build': + fs = build + else: + fs = source / s + files = rglob(fs, p, c) + extra_files = [] + if s == 'Lib' and p == '**/*': + extra_files.append(( + source / 'tools' / 'msi' / 'distutils.command.bdist_wininst.py', + Path('distutils') / 'command' / 'bdist_wininst.py' + )) + copied = copy_to_layout(temp / t.rstrip('/'), chain(files, extra_files)) + print('Copied {} files'.format(copied)) + + if ns.embed: + with open(str(temp / (BASE_NAME + '._pth')), 'w') as f: + print(BASE_NAME + '.zip', file=f) + print('.', file=f) + print('', file=f) + print('# Uncomment to run site.main() automatically', file=f) + print('#import site', file=f) + + if out: + total = copy_to_layout(out, rglob(temp, '**/*', None)) + print('Wrote {} files to {}'.format(total, out)) + finally: + if delete_temp: + shutil.rmtree(temp, True) + + +if __name__ == "__main__": + sys.exit(int(main() or 0)) diff --git a/Tools/msi/sdktools.psm1 b/Tools/msi/sdktools.psm1 deleted file mode 100644 index 81a74d3679d7..000000000000 --- a/Tools/msi/sdktools.psm1 +++ /dev/null @@ -1,43 +0,0 @@ -function Find-Tool { - param([string]$toolname) - - $kitroot = (gp 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots\').KitsRoot10 - $tool = (gci -r "$kitroot\Bin\*\x64\$toolname" | sort FullName -Desc | select -First 1) - if (-not $tool) { - throw "$toolname is not available" - } - Write-Host "Found $toolname at $($tool.FullName)" - return $tool.FullName -} - -Set-Alias SignTool (Find-Tool "signtool.exe") -Scope Script - -function Sign-File { - param([string]$certname, [string]$certsha1, [string]$certfile, [string]$description, [string[]]$files) - - if (-not $description) { - $description = $env:SigningDescription; - if (-not $description) { - $description = "Python"; - } - } - if (-not $certname) { - $certname = $env:SigningCertificate; - } - if (-not $certfile) { - $certfile = $env:SigningCertificateFile; - } - - foreach ($a in $files) { - if ($certsha1) { - SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a - } elseif ($certname) { - SignTool sign /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a - } elseif ($certfile) { - SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a - } else { - SignTool sign /a /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a - } - } -} - diff --git a/Tools/msi/sign_build.ps1 b/Tools/msi/sign_build.ps1 deleted file mode 100644 index 6668eb33a2d1..000000000000 --- a/Tools/msi/sign_build.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -<# -.Synopsis - Recursively signs the contents of a directory. -.Description - Given the file patterns, code signs the contents. -.Parameter root - The root directory to sign. -.Parameter patterns - The file patterns to sign -.Parameter description - The description to add to the signature (optional). -.Parameter certname - The name of the certificate to sign with (optional). -.Parameter certsha1 - The SHA1 hash of the certificate to sign with (optional). -#> -param( - [Parameter(Mandatory=$true)][string]$root, - [string[]]$patterns=@("*.exe", "*.dll", "*.pyd"), - [string]$description, - [string]$certname, - [string]$certsha1, - [string]$certfile -) - -$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; -Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force - -pushd $root -try { - Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files (gci -r $patterns) -} finally { - popd -} \ No newline at end of file diff --git a/Tools/nuget/make_pkg.proj b/Tools/nuget/make_pkg.proj index e093a6d0bd76..9843bc97ccdc 100644 --- a/Tools/nuget/make_pkg.proj +++ b/Tools/nuget/make_pkg.proj @@ -20,28 +20,25 @@ false $(OutputName).$(NuspecVersion) .nupkg - $(IntermediateOutputPath)\nuget_$(ArchName)\ + $(IntermediateOutputPath)\nuget_$(ArchName) - rmdir /q/s "$(IntermediateOutputPath.TrimEnd(`\`))" + rmdir /q/s "$(IntermediateOutputPath)" - "$(PythonExe)" "$(PySourcePath)PC\layout" - $(PythonArguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" - $(PythonArguments) -t "$(IntermediateOutputPath)obj" - $(PythonArguments) --copy "$(IntermediateOutputPath)pkg" - $(PythonArguments) --include-dev --include-tools --include-pip --include-stable --include-launcher --include-props + "$(PythonExe)" "$(MSBuildThisFileDirectory)\..\msi\make_zip.py" + $(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))" - "$(IntermediateOutputPath)pkg\pip.exe" -B -m pip install -U $(Packages) + "$(IntermediateOutputPath)\python.exe" -B -c "import sys; sys.path.append(r'$(PySourcePath)\Lib'); import ensurepip; ensurepip._main()" + "$(IntermediateOutputPath)\python.exe" -B -m pip install -U $(Packages) - "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)pkg" + "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)" "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))" $(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))" $(NugetArguments) -Version "$(NuspecVersion)" $(NugetArguments) -NoPackageAnalysis -NonInteractive + set DOC_FILENAME=python$(PythonVersion).chm $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset PYTHON_NUSPEC_VERSION=$(NuspecVersion) - $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=$(Platform) - $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=Win32 + $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) $(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" >nul 2>nul @@ -51,7 +48,22 @@ - + + + + + + <_PropsContents>$([System.IO.File]::ReadAllText('python.props')) + <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TAG$$', '$(MajorVersionNumber).$(MinorVersionNumber)')) + <_PropsContents>$(_PropsContents.Replace('$$PYTHON_VERSION$$', '$(NuspecVersion)')) + <_PropsContents Condition="$(Platform) == 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', 'Win32')) + <_PropsContents Condition="$(Platform) != 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', '$(Platform)')) + <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TARGET$$', '_GetPythonRuntimeFilesDependsOn$(MajorVersionNumber)$(MinorVersionNumber)_$(Platform)')) + <_ExistingContents Condition="Exists('$(IntermediateOutputPath)\python.props')">$([System.IO.File]::ReadAllText('$(IntermediateOutputPath)\python.props')) + + diff --git a/PC/layout/support/python.props b/Tools/nuget/python.props similarity index 100% rename from PC/layout/support/python.props rename to Tools/nuget/python.props From webhook-mailer at python.org Fri Dec 7 07:17:47 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 07 Dec 2018 12:17:47 -0000 Subject: [Python-checkins] bpo-35436: Add missing PyErr_NoMemory() calls and other minor bug fixes. (GH-11015) (GH-11020) Message-ID: https://github.com/python/cpython/commit/602d307ac5e8a2da38a193dca3bdfef5994dfe67 commit: 602d307ac5e8a2da38a193dca3bdfef5994dfe67 branch: 3.7 author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-07T14:17:43+02:00 summary: bpo-35436: Add missing PyErr_NoMemory() calls and other minor bug fixes. (GH-11015) (GH-11020) (cherry picked from commit 4c49da0cb7434c676d70b9ccf38aca82ac0d64a9) files: A Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst M Modules/_abc.c M Modules/_ctypes/_ctypes.c M Modules/_ctypes/callbacks.c M Modules/_io/winconsoleio.c M Modules/_multiprocessing/semaphore.c M Modules/_ssl.c M Modules/posixmodule.c M Objects/capsule.c M PC/getpathp.c M PC/launcher.c M Parser/myreadline.c M Parser/tokenizer.c M Python/ast.c M Python/marshal.c M Python/pystrtod.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst new file mode 100644 index 000000000000..542fe93a00eb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst @@ -0,0 +1,2 @@ +Fix various issues with memory allocation error handling. Patch by Zackery +Spytz. diff --git a/Modules/_abc.c b/Modules/_abc.c index 9de199fa143f..36c1757b5fd3 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -728,6 +728,10 @@ subclasscheck_check_registry(_abc_data *impl, PyObject *subclass, // Weakref callback may remove entry from set. // So we take snapshot of registry first. PyObject **copy = PyMem_Malloc(sizeof(PyObject*) * registry_size); + if (copy == NULL) { + PyErr_NoMemory(); + return -1; + } PyObject *key; Py_ssize_t pos = 0; Py_hash_t hash; diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 3bb96774b0c8..c5fc811ad98e 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -305,8 +305,10 @@ _ctypes_alloc_format_string_for_type(char code, int big_endian) } result = PyMem_Malloc(3); - if (result == NULL) + if (result == NULL) { + PyErr_NoMemory(); return NULL; + } result[0] = big_endian ? '>' : '<'; result[1] = pep_code; @@ -366,8 +368,10 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, if (prefix) prefix_len += strlen(prefix); new_prefix = PyMem_Malloc(prefix_len); - if (new_prefix == NULL) + if (new_prefix == NULL) { + PyErr_NoMemory(); return NULL; + } new_prefix[0] = '\0'; if (prefix) strcpy(new_prefix, prefix); @@ -1851,6 +1855,10 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject #else suffix = PyUnicode_InternFromString("_be"); #endif + if (suffix == NULL) { + Py_DECREF(swapped_args); + return NULL; + } newname = PyUnicode_Concat(name, suffix); if (newname == NULL) { diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index d579291b62fe..ec9f44336e0e 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -305,7 +305,6 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nArgs) p = PyObject_GC_NewVar(CThunkObject, &PyCThunk_Type, nArgs); if (p == NULL) { - PyErr_NoMemory(); return NULL; } diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 4d3d695e4210..c11c1e09f4c3 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -815,11 +815,13 @@ _io__WindowsConsoleIO_readall_impl(winconsoleio *self) } bufsize = newsize; - buf = PyMem_Realloc(buf, (bufsize + 1) * sizeof(wchar_t)); - if (!buf) { + wchar_t *tmp = PyMem_Realloc(buf, + (bufsize + 1) * sizeof(wchar_t)); + if (tmp == NULL) { PyMem_Free(buf); return NULL; } + buf = tmp; } subbuf = read_console_w(self->handle, bufsize - len, &n); diff --git a/Modules/_multiprocessing/semaphore.c b/Modules/_multiprocessing/semaphore.c index 0092b1393464..0cc46b53222e 100644 --- a/Modules/_multiprocessing/semaphore.c +++ b/Modules/_multiprocessing/semaphore.c @@ -449,8 +449,9 @@ semlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!unlink) { name_copy = PyMem_Malloc(strlen(name) + 1); - if (name_copy == NULL) - goto failure; + if (name_copy == NULL) { + return PyErr_NoMemory(); + } strcpy(name_copy, name); } @@ -473,7 +474,9 @@ semlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (handle != SEM_FAILED) SEM_CLOSE(handle); PyMem_Free(name_copy); - _PyMp_SetError(NULL, MP_STANDARD_ERROR); + if (!PyErr_Occurred()) { + _PyMp_SetError(NULL, MP_STANDARD_ERROR); + } return NULL; } diff --git a/Modules/_ssl.c b/Modules/_ssl.c index f9d1b8c30877..310b38bf11f4 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -912,6 +912,11 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, PySSL_BEGIN_ALLOW_THREADS self->ssl = SSL_new(ctx); PySSL_END_ALLOW_THREADS + if (self->ssl == NULL) { + Py_DECREF(self); + _setSSLError(NULL, 0, __FILE__, __LINE__); + return NULL; + } SSL_set_app_data(self->ssl, self); if (sock) { SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int)); @@ -1241,6 +1246,10 @@ _get_peer_alt_names (X509 *certificate) { /* get a memory buffer */ biobuf = BIO_new(BIO_s_mem()); + if (biobuf == NULL) { + PyErr_SetString(PySSLErrorObject, "failed to allocate BIO"); + return NULL; + } names = (GENERAL_NAMES *)X509_get_ext_d2i( certificate, NID_subject_alt_name, NULL, NULL); @@ -1593,6 +1602,10 @@ _decode_certificate(X509 *certificate) { /* get a memory buffer */ biobuf = BIO_new(BIO_s_mem()); + if (biobuf == NULL) { + PyErr_SetString(PySSLErrorObject, "failed to allocate BIO"); + goto fail0; + } (void) BIO_reset(biobuf); serialNumber = X509_get_serialNumber(certificate); diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index cab30c21025c..5403660ba45e 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6229,8 +6229,7 @@ os_getgroups_impl(PyObject *module) } else { alt_grouplist = PyMem_New(gid_t, n); if (alt_grouplist == NULL) { - errno = EINVAL; - return posix_error(); + return PyErr_NoMemory(); } } @@ -6255,8 +6254,7 @@ os_getgroups_impl(PyObject *module) } else { alt_grouplist = PyMem_New(gid_t, n); if (alt_grouplist == NULL) { - errno = EINVAL; - return posix_error(); + return PyErr_NoMemory(); } n = getgroups(n, alt_grouplist); if (n == -1) { diff --git a/Objects/capsule.c b/Objects/capsule.c index acd3de637dd5..4e15b440b170 100644 --- a/Objects/capsule.c +++ b/Objects/capsule.c @@ -201,7 +201,7 @@ PyCapsule_Import(const char *name, int no_block) char *name_dup = (char *)PyMem_MALLOC(name_length); if (!name_dup) { - return NULL; + return PyErr_NoMemory(); } memcpy(name_dup, name, name_length); diff --git a/PC/getpathp.c b/PC/getpathp.c index 4075463f2260..1b553d53affa 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -576,6 +576,9 @@ read_pth_file(_PyPathConfig *config, wchar_t *prefix, const wchar_t *path, size_t prefixlen = wcslen(prefix); wchar_t *buf = (wchar_t*)PyMem_RawMalloc(bufsiz * sizeof(wchar_t)); + if (buf == NULL) { + goto error; + } buf[0] = '\0'; while (!feof(sp_file)) { @@ -603,17 +606,22 @@ read_pth_file(_PyPathConfig *config, wchar_t *prefix, const wchar_t *path, DWORD wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, NULL, 0); wchar_t *wline = (wchar_t*)PyMem_RawMalloc((wn + 1) * sizeof(wchar_t)); + if (wline == NULL) { + goto error; + } wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, wline, wn + 1); wline[wn] = '\0'; size_t usedsiz = wcslen(buf); while (usedsiz + wn + prefixlen + 4 > bufsiz) { bufsiz += MAXPATHLEN; - buf = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) * sizeof(wchar_t)); - if (!buf) { + wchar_t *tmp = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) * + sizeof(wchar_t)); + if (tmp == NULL) { PyMem_RawFree(wline); goto error; } + buf = tmp; } if (usedsiz) { diff --git a/PC/launcher.c b/PC/launcher.c index 0242f2639119..4c620dab7c09 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -1763,6 +1763,9 @@ process(int argc, wchar_t ** argv) } cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 1 + 1; /* include sep and null */ executable = (wchar_t *)malloc(cch * sizeof(wchar_t)); + if (executable == NULL) { + error(RC_NO_MEMORY, L"A memory allocation failed"); + } cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, executable, cch); if (!cch_actual) { error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'", diff --git a/Parser/myreadline.c b/Parser/myreadline.c index edb291a6691a..58dc0f75fe24 100644 --- a/Parser/myreadline.c +++ b/Parser/myreadline.c @@ -153,20 +153,37 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn) wbuf = (wchar_t*)PyMem_RawMalloc(wbuflen * sizeof(wchar_t)); if (wbuf) wcscpy_s(wbuf, wbuflen, wbuf_local); + else { + PyErr_NoMemory(); + goto exit; + } + } + else { + wchar_t *tmp = PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t)); + if (tmp == NULL) { + PyErr_NoMemory(); + goto exit; + } + wbuf = tmp; } - else - wbuf = (wchar_t*)PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t)); } if (wbuf[0] == '\x1a') { buf = PyMem_RawMalloc(1); if (buf) buf[0] = '\0'; + else { + PyErr_NoMemory(); + } goto exit; } u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, NULL, 0, NULL, NULL); buf = PyMem_RawMalloc(u8len + 1); + if (buf == NULL) { + PyErr_NoMemory(); + goto exit; + } u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, buf, u8len, NULL, NULL); buf[u8len] = '\0'; @@ -211,8 +228,12 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) int wlen; wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1, NULL, 0); - if (wlen && - (wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t)))) { + if (wlen) { + wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t)); + if (wbuf == NULL) { + PyErr_NoMemory(); + return NULL; + } wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1, wbuf, wlen); if (wlen) { @@ -236,8 +257,10 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) n = 100; p = (char *)PyMem_RawMalloc(n); - if (p == NULL) + if (p == NULL) { + PyErr_NoMemory(); return NULL; + } fflush(sys_stdout); if (prompt) @@ -314,6 +337,10 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) if (_PyOS_ReadlineLock == NULL) { _PyOS_ReadlineLock = PyThread_allocate_lock(); + if (_PyOS_ReadlineLock == NULL) { + PyErr_SetString(PyExc_MemoryError, "can't allocate lock"); + return NULL; + } } _PyOS_ReadlineTState = PyThreadState_GET(); @@ -341,8 +368,12 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) len = strlen(rv) + 1; res = PyMem_Malloc(len); - if (res != NULL) + if (res != NULL) { memcpy(res, rv, len); + } + else { + PyErr_NoMemory(); + } PyMem_RawFree(rv); return res; diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index fbc98880c9a5..1a36f1c3840a 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -953,6 +953,11 @@ tok_nextc(struct tok_state *tok) buflen = PyBytes_GET_SIZE(u); buf = PyBytes_AS_STRING(u); newtok = PyMem_MALLOC(buflen+1); + if (newtok == NULL) { + Py_DECREF(u); + tok->done = E_NOMEM; + return EOF; + } strcpy(newtok, buf); Py_DECREF(u); } diff --git a/Python/ast.c b/Python/ast.c index 07227c238ef4..1e182c7d782a 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -4092,6 +4092,9 @@ parsenumber(struct compiling *c, const char *s) } /* Create a duplicate without underscores. */ dup = PyMem_Malloc(strlen(s) + 1); + if (dup == NULL) { + return PyErr_NoMemory(); + } end = dup; for (; *s; s++) { if (*s != '_') { @@ -4326,8 +4329,10 @@ fstring_compile_expr(const char *expr_start, const char *expr_end, len = expr_end - expr_start; /* Allocate 3 extra bytes: open paren, close paren, null byte. */ str = PyMem_RawMalloc(len + 3); - if (str == NULL) + if (str == NULL) { + PyErr_NoMemory(); return NULL; + } str[0] = '('; memcpy(str+1, expr_start, len); diff --git a/Python/marshal.c b/Python/marshal.c index 6d06266c6a8e..7d60614e712a 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -689,11 +689,12 @@ r_string(Py_ssize_t n, RFILE *p) p->buf_size = n; } else if (p->buf_size < n) { - p->buf = PyMem_REALLOC(p->buf, n); - if (p->buf == NULL) { + char *tmp = PyMem_REALLOC(p->buf, n); + if (tmp == NULL) { PyErr_NoMemory(); return NULL; } + p->buf = tmp; p->buf_size = n; } diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 461e8dcb5e0c..fea7e45c3b62 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -398,6 +398,9 @@ _Py_string_to_number_with_underscores( } dup = PyMem_Malloc(orig_len + 1); + if (dup == NULL) { + return PyErr_NoMemory(); + } end = dup; prev = '\0'; last = s + orig_len; From webhook-mailer at python.org Fri Dec 7 07:56:05 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 07 Dec 2018 12:56:05 -0000 Subject: [Python-checkins] [3.7] bpo-22005: Fixed unpickling instances of datetime classes pickled by Python 2. (GH-11017) (GH-11022) Message-ID: https://github.com/python/cpython/commit/0d5730e6437b157f4aeaf5d2e67abca23448c29a commit: 0d5730e6437b157f4aeaf5d2e67abca23448c29a branch: 3.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-07T14:56:02+02:00 summary: [3.7] bpo-22005: Fixed unpickling instances of datetime classes pickled by Python 2. (GH-11017) (GH-11022) encoding='latin1' should be used for successful decoding. (cherry picked from commit 8452ca15f41061c8a6297d7956df22ab476d4df4) files: A Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst M Doc/library/pickle.rst M Lib/datetime.py M Lib/test/datetimetester.py M Modules/_datetimemodule.c diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 2b10ee2eb8ee..52cbb6241bc9 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -235,6 +235,9 @@ process more convenient: *errors* tell pickle how to decode 8-bit string instances pickled by Python 2; these default to 'ASCII' and 'strict', respectively. The *encoding* can be 'bytes' to read these 8-bit string instances as bytes objects. + Using ``encoding='latin1'`` is required for unpickling NumPy arrays and + instances of :class:`~datetime.datetime`, :class:`~datetime.date` and + :class:`~datetime.time` pickled by Python 2. .. function:: loads(bytes_object, \*, fix_imports=True, encoding="ASCII", errors="strict") @@ -252,6 +255,9 @@ process more convenient: *errors* tell pickle how to decode 8-bit string instances pickled by Python 2; these default to 'ASCII' and 'strict', respectively. The *encoding* can be 'bytes' to read these 8-bit string instances as bytes objects. + Using ``encoding='latin1'`` is required for unpickling NumPy arrays and + instances of :class:`~datetime.datetime`, :class:`~datetime.date` and + :class:`~datetime.time` pickled by Python 2. The :mod:`pickle` module defines three exceptions: diff --git a/Lib/datetime.py b/Lib/datetime.py index 8bffbef9ac32..a964b202e3c7 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -808,9 +808,19 @@ def __new__(cls, year, month=None, day=None): year, month, day (required, base 1) """ - if month is None and isinstance(year, bytes) and len(year) == 4 and \ - 1 <= year[2] <= 12: + if (month is None and + isinstance(year, (bytes, str)) and len(year) == 4 and + 1 <= ord(year[2:3]) <= 12): # Pickle support + if isinstance(year, str): + try: + year = year.encode('latin1') + except UnicodeEncodeError: + # More informative error message. + raise ValueError( + "Failed to encode latin1 string when unpickling " + "a date object. " + "pickle.load(data, encoding='latin1') is assumed.") self = object.__new__(cls) self.__setstate(year) self._hashcode = -1 @@ -1184,8 +1194,18 @@ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold tzinfo (default to None) fold (keyword only, default to zero) """ - if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24: + if (isinstance(hour, (bytes, str)) and len(hour) == 6 and + ord(hour[0:1])&0x7F < 24): # Pickle support + if isinstance(hour, str): + try: + hour = hour.encode('latin1') + except UnicodeEncodeError: + # More informative error message. + raise ValueError( + "Failed to encode latin1 string when unpickling " + "a time object. " + "pickle.load(data, encoding='latin1') is assumed.") self = object.__new__(cls) self.__setstate(hour, minute or None) self._hashcode = -1 @@ -1496,8 +1516,18 @@ class datetime(date): def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0): - if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12: + if (isinstance(year, (bytes, str)) and len(year) == 10 and + 1 <= ord(year[2:3])&0x7F <= 12): # Pickle support + if isinstance(year, str): + try: + year = bytes(year, 'latin1') + except UnicodeEncodeError: + # More informative error message. + raise ValueError( + "Failed to encode latin1 string when unpickling " + "a datetime object. " + "pickle.load(data, encoding='latin1') is assumed.") self = object.__new__(cls) self.__setstate(year, month) self._hashcode = -1 diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 78b123f5b118..2f838c445555 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -38,6 +38,7 @@ import _strptime # +pickle_loads = {pickle.loads, pickle._loads} pickle_choices = [(pickle, pickle, proto) for proto in range(pickle.HIGHEST_PROTOCOL + 1)] @@ -1434,6 +1435,19 @@ def test_pickling(self): self.assertEqual(orig, derived) self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) + def test_compat_unpickle(self): + tests = [ + b"cdatetime\ndate\n(S'\\x07\\xdf\\x0b\\x1b'\ntR.", + b'cdatetime\ndate\n(U\x04\x07\xdf\x0b\x1btR.', + b'\x80\x02cdatetime\ndate\nU\x04\x07\xdf\x0b\x1b\x85R.', + ] + args = 2015, 11, 27 + expected = self.theclass(*args) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + def test_compare(self): t1 = self.theclass(2, 3, 4) t2 = self.theclass(2, 3, 4) @@ -2098,6 +2112,24 @@ def test_pickling_subclass_datetime(self): derived = unpickler.loads(green) self.assertEqual(orig, derived) + def test_compat_unpickle(self): + tests = [ + b'cdatetime\ndatetime\n(' + b"S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x00\\x10\\x00'\ntR.", + + b'cdatetime\ndatetime\n(' + b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00tR.', + + b'\x80\x02cdatetime\ndatetime\n' + b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00\x85R.', + ] + args = 2015, 11, 27, 20, 59, 1, 64**2 + expected = self.theclass(*args) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + def test_more_compare(self): # The test_compare() inherited from TestDate covers the error cases. # We just want to test lexicographic ordering on the members datetime @@ -3069,6 +3101,19 @@ def test_pickling_subclass_time(self): derived = unpickler.loads(green) self.assertEqual(orig, derived) + def test_compat_unpickle(self): + tests = [ + b"cdatetime\ntime\n(S'\\x14;\\x10\\x00\\x10\\x00'\ntR.", + b'cdatetime\ntime\n(U\x06\x14;\x10\x00\x10\x00tR.', + b'\x80\x02cdatetime\ntime\nU\x06\x14;\x10\x00\x10\x00\x85R.', + ] + args = 20, 59, 16, 64**2 + expected = self.theclass(*args) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + def test_bool(self): # time is always True. cls = self.theclass @@ -3441,6 +3486,40 @@ def test_pickling(self): self.assertEqual(derived.tzname(), 'cookie') self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) + def test_compat_unpickle(self): + tests = [ + b"cdatetime\ntime\n(S'\\x05\\x06\\x07\\x01\\xe2@'\n" + b"ctest.datetimetester\nPicklableFixedOffset\n(tR" + b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n" + b"(I-1\nI68400\nI0\ntRs" + b"S'_FixedOffset__dstoffset'\nNs" + b"S'_FixedOffset__name'\nS'cookie'\nsbtR.", + + b'cdatetime\ntime\n(U\x06\x05\x06\x07\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieubtR.', + + b'\x80\x02cdatetime\ntime\nU\x06\x05\x06\x07\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieub\x86R.', + ] + + tinfo = PicklableFixedOffset(-300, 'cookie') + expected = self.theclass(5, 6, 7, 123456, tzinfo=tinfo) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected, repr(data)) + self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) + self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) + self.assertEqual(derived.tzname(), 'cookie') + def test_more_bool(self): # time is always True. cls = self.theclass @@ -3789,6 +3868,43 @@ def test_pickling(self): self.assertEqual(derived.tzname(), 'cookie') self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) + def test_compat_unpickle(self): + tests = [ + b'cdatetime\ndatetime\n' + b"(S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x01\\xe2@'\n" + b'ctest.datetimetester\nPicklableFixedOffset\n(tR' + b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n" + b'(I-1\nI68400\nI0\ntRs' + b"S'_FixedOffset__dstoffset'\nNs" + b"S'_FixedOffset__name'\nS'cookie'\nsbtR.", + + b'cdatetime\ndatetime\n' + b'(U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieubtR.', + + b'\x80\x02cdatetime\ndatetime\n' + b'U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieub\x86R.', + ] + args = 2015, 11, 27, 20, 59, 1, 123456 + tinfo = PicklableFixedOffset(-300, 'cookie') + expected = self.theclass(*args, **{'tzinfo': tinfo}) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) + self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) + self.assertEqual(derived.tzname(), 'cookie') + def test_extreme_hashes(self): # If an attempt is made to hash these via subtracting the offset # then hashing a datetime object, OverflowError results. The diff --git a/Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst b/Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst new file mode 100644 index 000000000000..951098d0a7a3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst @@ -0,0 +1,3 @@ +Implemented unpickling instances of :class:`~datetime.datetime`, +:class:`~datetime.date` and :class:`~datetime.time` pickled by Python 2. +``encoding='latin1'`` should be used for successful decoding. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 9477f776138c..5afeeea4881d 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -2774,30 +2774,61 @@ static PyGetSetDef date_getset[] = { static char *date_kws[] = {"year", "month", "day", NULL}; +static PyObject * +date_from_pickle(PyTypeObject *type, PyObject *state) +{ + PyDateTime_Date *me; + + me = (PyDateTime_Date *) (type->tp_alloc(type, 0)); + if (me != NULL) { + const char *pdata = PyBytes_AS_STRING(state); + memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE); + me->hashcode = -1; + } + return (PyObject *)me; +} + static PyObject * date_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *state; int year; int month; int day; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) == 1 && - PyBytes_Check(state = PyTuple_GET_ITEM(args, 0)) && - PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE && - MONTH_IS_SANE(PyBytes_AS_STRING(state)[2])) - { - PyDateTime_Date *me; - - me = (PyDateTime_Date *) (type->tp_alloc(type, 0)); - if (me != NULL) { - char *pdata = PyBytes_AS_STRING(state); - memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE); - me->hashcode = -1; + if (PyTuple_GET_SIZE(args) == 1) { + PyObject *state = PyTuple_GET_ITEM(args, 0); + if (PyBytes_Check(state)) { + if (PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE && + MONTH_IS_SANE(PyBytes_AS_STRING(state)[2])) + { + return date_from_pickle(type, state); + } + } + else if (PyUnicode_Check(state)) { + if (PyUnicode_READY(state)) { + return NULL; + } + if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATE_DATASIZE && + MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2))) + { + state = PyUnicode_AsLatin1String(state); + if (state == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* More informative error message. */ + PyErr_SetString(PyExc_ValueError, + "Failed to encode latin1 string when unpickling " + "a date object. " + "pickle.load(data, encoding='latin1') is assumed."); + } + return NULL; + } + self = date_from_pickle(type, state); + Py_DECREF(state); + return self; + } } - return (PyObject *)me; } if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws, @@ -3883,11 +3914,43 @@ static PyGetSetDef time_getset[] = { static char *time_kws[] = {"hour", "minute", "second", "microsecond", "tzinfo", "fold", NULL}; +static PyObject * +time_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) +{ + PyDateTime_Time *me; + char aware = (char)(tzinfo != Py_None); + + if (aware && check_tzinfo_subclass(tzinfo) < 0) { + PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); + return NULL; + } + + me = (PyDateTime_Time *) (type->tp_alloc(type, aware)); + if (me != NULL) { + const char *pdata = PyBytes_AS_STRING(state); + + memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE); + me->hashcode = -1; + me->hastzinfo = aware; + if (aware) { + Py_INCREF(tzinfo); + me->tzinfo = tzinfo; + } + if (pdata[0] & (1 << 7)) { + me->data[0] -= 128; + me->fold = 1; + } + else { + me->fold = 0; + } + } + return (PyObject *)me; +} + static PyObject * time_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *state; int hour = 0; int minute = 0; int second = 0; @@ -3896,44 +3959,42 @@ time_new(PyTypeObject *type, PyObject *args, PyObject *kw) int fold = 0; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) >= 1 && - PyTuple_GET_SIZE(args) <= 2 && - PyBytes_Check(state = PyTuple_GET_ITEM(args, 0)) && - PyBytes_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE && - (0x7F & ((unsigned char) (PyBytes_AS_STRING(state)[0]))) < 24) - { - PyDateTime_Time *me; - char aware; - + if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) { + PyObject *state = PyTuple_GET_ITEM(args, 0); if (PyTuple_GET_SIZE(args) == 2) { tzinfo = PyTuple_GET_ITEM(args, 1); - if (check_tzinfo_subclass(tzinfo) < 0) { - PyErr_SetString(PyExc_TypeError, "bad " - "tzinfo state arg"); - return NULL; - } } - aware = (char)(tzinfo != Py_None); - me = (PyDateTime_Time *) (type->tp_alloc(type, aware)); - if (me != NULL) { - char *pdata = PyBytes_AS_STRING(state); - - memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE); - me->hashcode = -1; - me->hastzinfo = aware; - if (aware) { - Py_INCREF(tzinfo); - me->tzinfo = tzinfo; + if (PyBytes_Check(state)) { + if (PyBytes_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE && + (0x7F & ((unsigned char) (PyBytes_AS_STRING(state)[0]))) < 24) + { + return time_from_pickle(type, state, tzinfo); } - if (pdata[0] & (1 << 7)) { - me->data[0] -= 128; - me->fold = 1; + } + else if (PyUnicode_Check(state)) { + if (PyUnicode_READY(state)) { + return NULL; } - else { - me->fold = 0; + if (PyUnicode_GET_LENGTH(state) == _PyDateTime_TIME_DATASIZE && + (0x7F & PyUnicode_READ_CHAR(state, 2)) < 24) + { + state = PyUnicode_AsLatin1String(state); + if (state == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* More informative error message. */ + PyErr_SetString(PyExc_ValueError, + "Failed to encode latin1 string when unpickling " + "a time object. " + "pickle.load(data, encoding='latin1') is assumed."); + } + return NULL; + } + self = time_from_pickle(type, state, tzinfo); + Py_DECREF(state); + return self; } } - return (PyObject *)me; + tzinfo = Py_None; } if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO$i", time_kws, @@ -4519,11 +4580,43 @@ static char *datetime_kws[] = { "microsecond", "tzinfo", "fold", NULL }; +static PyObject * +datetime_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) +{ + PyDateTime_DateTime *me; + char aware = (char)(tzinfo != Py_None); + + if (aware && check_tzinfo_subclass(tzinfo) < 0) { + PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); + return NULL; + } + + me = (PyDateTime_DateTime *) (type->tp_alloc(type , aware)); + if (me != NULL) { + const char *pdata = PyBytes_AS_STRING(state); + + memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE); + me->hashcode = -1; + me->hastzinfo = aware; + if (aware) { + Py_INCREF(tzinfo); + me->tzinfo = tzinfo; + } + if (pdata[2] & (1 << 7)) { + me->data[2] -= 128; + me->fold = 1; + } + else { + me->fold = 0; + } + } + return (PyObject *)me; +} + static PyObject * datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *state; int year; int month; int day; @@ -4535,44 +4628,42 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) PyObject *tzinfo = Py_None; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) >= 1 && - PyTuple_GET_SIZE(args) <= 2 && - PyBytes_Check(state = PyTuple_GET_ITEM(args, 0)) && - PyBytes_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE && - MONTH_IS_SANE(PyBytes_AS_STRING(state)[2] & 0x7F)) - { - PyDateTime_DateTime *me; - char aware; - + if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) { + PyObject *state = PyTuple_GET_ITEM(args, 0); if (PyTuple_GET_SIZE(args) == 2) { tzinfo = PyTuple_GET_ITEM(args, 1); - if (check_tzinfo_subclass(tzinfo) < 0) { - PyErr_SetString(PyExc_TypeError, "bad " - "tzinfo state arg"); - return NULL; - } } - aware = (char)(tzinfo != Py_None); - me = (PyDateTime_DateTime *) (type->tp_alloc(type , aware)); - if (me != NULL) { - char *pdata = PyBytes_AS_STRING(state); - - memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE); - me->hashcode = -1; - me->hastzinfo = aware; - if (aware) { - Py_INCREF(tzinfo); - me->tzinfo = tzinfo; + if (PyBytes_Check(state)) { + if (PyBytes_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE && + MONTH_IS_SANE(PyBytes_AS_STRING(state)[2] & 0x7F)) + { + return datetime_from_pickle(type, state, tzinfo); } - if (pdata[2] & (1 << 7)) { - me->data[2] -= 128; - me->fold = 1; + } + else if (PyUnicode_Check(state)) { + if (PyUnicode_READY(state)) { + return NULL; } - else { - me->fold = 0; + if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATETIME_DATASIZE && + MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2) & 0x7F)) + { + state = PyUnicode_AsLatin1String(state); + if (state == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* More informative error message. */ + PyErr_SetString(PyExc_ValueError, + "Failed to encode latin1 string when unpickling " + "a datetime object. " + "pickle.load(data, encoding='latin1') is assumed."); + } + return NULL; + } + self = datetime_from_pickle(type, state, tzinfo); + Py_DECREF(state); + return self; } } - return (PyObject *)me; + tzinfo = Py_None; } if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO$i", datetime_kws, From webhook-mailer at python.org Fri Dec 7 08:31:47 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 07 Dec 2018 13:31:47 -0000 Subject: [Python-checkins] Revert "[3.7] bpo-34977: Add Windows App Store package (GH-10245)" (GH-11021) Message-ID: https://github.com/python/cpython/commit/783b794a5e6ea3bbbaba45a18b9e03ac322b3bd4 commit: 783b794a5e6ea3bbbaba45a18b9e03ac322b3bd4 branch: 3.7 author: Victor Stinner committer: GitHub date: 2018-12-07T14:31:40+01:00 summary: Revert "[3.7] bpo-34977: Add Windows App Store package (GH-10245)" (GH-11021) This reverts commit 253209149389e6793a052034e1f2d97691086f18. files: A Tools/msi/make_zip.py A Tools/nuget/python.props D .azure-pipelines/windows-appx-test.yml D Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst D PC/classicAppCompat.can.xml D PC/classicAppCompat.cat D PC/classicAppCompat.sccd D PC/icons/pythonwx150.png D PC/icons/pythonwx44.png D PC/icons/pythonx150.png D PC/icons/pythonx44.png D PC/icons/pythonx50.png D PC/layout/__init__.py D PC/layout/__main__.py D PC/layout/main.py D PC/layout/support/__init__.py D PC/layout/support/appxmanifest.py D PC/layout/support/catalog.py D PC/layout/support/constants.py D PC/layout/support/distutils.command.bdist_wininst.py D PC/layout/support/filesets.py D PC/layout/support/logging.py D PC/layout/support/options.py D PC/layout/support/pip.py D PC/layout/support/props.py D PC/layout/support/python.props D PC/python_uwp.cpp D PC/store_info.txt D PCbuild/python_uwp.vcxproj D PCbuild/pythonw_uwp.vcxproj D PCbuild/venvlauncher.vcxproj D PCbuild/venvwlauncher.vcxproj D Tools/msi/make_appx.ps1 D Tools/msi/make_cat.ps1 D Tools/msi/sdktools.psm1 D Tools/msi/sign_build.ps1 M .gitattributes M Lib/test/test_pathlib.py M Lib/test/test_venv.py M Lib/venv/__init__.py M PC/getpathp.c M PC/launcher.c M PC/pylauncher.rc M PCbuild/_tkinter.vcxproj M PCbuild/find_msbuild.bat M PCbuild/pcbuild.proj M PCbuild/pcbuild.sln M PCbuild/python.props M PCbuild/pythoncore.vcxproj M Tools/msi/buildrelease.bat M Tools/msi/make_zip.proj M Tools/nuget/make_pkg.proj diff --git a/.azure-pipelines/windows-appx-test.yml b/.azure-pipelines/windows-appx-test.yml deleted file mode 100644 index 9840c0a1221f..000000000000 --- a/.azure-pipelines/windows-appx-test.yml +++ /dev/null @@ -1,65 +0,0 @@ -jobs: -- job: Prebuild - displayName: Pre-build checks - - pool: - vmImage: ubuntu-16.04 - - steps: - - template: ./prebuild-checks.yml - - -- job: Windows_Appx_Tests - displayName: Windows Appx Tests - dependsOn: Prebuild - condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) - - pool: - vmImage: vs2017-win2016 - - strategy: - matrix: - win64: - arch: amd64 - buildOpt: '-p x64' - testRunTitle: '$(Build.SourceBranchName)-win64-appx' - testRunPlatform: win64 - maxParallel: 2 - - steps: - - checkout: self - clean: true - fetchDepth: 5 - - - powershell: | - # Relocate build outputs outside of source directory to make cleaning faster - Write-Host '##vso[task.setvariable variable=Py_IntDir]$(Build.BinariesDirectory)\obj' - # UNDONE: Do not build to a different directory because of broken tests - Write-Host '##vso[task.setvariable variable=Py_OutDir]$(Build.SourcesDirectory)\PCbuild' - Write-Host '##vso[task.setvariable variable=EXTERNAL_DIR]$(Build.BinariesDirectory)\externals' - displayName: Update build locations - - - script: PCbuild\build.bat -e $(buildOpt) - displayName: 'Build CPython' - - - script: python.bat PC\layout -vv -s "$(Build.SourcesDirectory)" -b "$(Py_OutDir)\$(arch)" -t "$(Py_IntDir)\layout-tmp-$(arch)" --copy "$(Py_IntDir)\layout-$(arch)" --precompile --preset-appx --include-tests - displayName: 'Create APPX layout' - - - script: .\python.exe -m test.pythoninfo - workingDirectory: $(Py_IntDir)\layout-$(arch) - displayName: 'Display build info' - - - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml" --tempdir "$(Py_IntDir)\tmp-$(arch)" - workingDirectory: $(Py_IntDir)\layout-$(arch) - displayName: 'Tests' - env: - PREFIX: $(Py_IntDir)\layout-$(arch) - - - task: PublishTestResults at 2 - displayName: 'Publish Test Results' - inputs: - testResultsFiles: '$(Build.BinariesDirectory)\test-results.xml' - mergeTestResults: true - testRunTitle: $(testRunTitle) - platform: $(testRunPlatform) - condition: succeededOrFailed() diff --git a/.gitattributes b/.gitattributes index 16237bb2b3ac..4a487c3c2a14 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,7 +19,6 @@ # Specific binary files Lib/test/sndhdrdata/sndhdr.* binary -PC/classicAppCompat.* binary # Text files that should not be subject to eol conversion Lib/test/cjkencodings/* -text diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 056507ef6fe8..e436db995ce4 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1519,7 +1519,7 @@ def test_resolve_common(self): # resolves to 'dirB/..' first before resolving to parent of dirB. self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) # Now create absolute symlinks - d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) + d = support._longpath(tempfile.mkdtemp(suffix='-dirD')) self.addCleanup(support.rmtree, d) os.symlink(os.path.join(d), join('dirA', 'linkX')) os.symlink(join('dirB'), os.path.join(d, 'linkY')) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 22a3b78852f8..461fe7afd213 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -243,7 +243,6 @@ def test_isolation(self): self.assertIn('include-system-site-packages = %s\n' % s, data) @unittest.skipUnless(can_symlink(), 'Needs symlinks') - @unittest.skipIf(os.name == 'nt', 'Symlinks are never used on Windows') def test_symlinking(self): """ Test symlinking works as expected diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index 5438b0d4e508..716129d13987 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -9,7 +9,6 @@ import shutil import subprocess import sys -import sysconfig import types logger = logging.getLogger(__name__) @@ -64,11 +63,10 @@ def create(self, env_dir): self.system_site_packages = False self.create_configuration(context) self.setup_python(context) - if not self.upgrade: - self.setup_scripts(context) if self.with_pip: self._setup_pip(context) if not self.upgrade: + self.setup_scripts(context) self.post_setup(context) if true_system_site_packages: # We had set it to False before, now @@ -159,6 +157,14 @@ def create_configuration(self, context): f.write('include-system-site-packages = %s\n' % incl) f.write('version = %d.%d.%d\n' % sys.version_info[:3]) + if os.name == 'nt': + def include_binary(self, f): + if f.endswith(('.pyd', '.dll')): + result = True + else: + result = f.startswith('python') and f.endswith('.exe') + return result + def symlink_or_copy(self, src, dst, relative_symlinks_ok=False): """ Try symlinking a file, and if that fails, fall back to copying. @@ -188,9 +194,9 @@ def setup_python(self, context): binpath = context.bin_path path = context.env_exe copier = self.symlink_or_copy + copier(context.executable, path) dirname = context.python_dir if os.name != 'nt': - copier(context.executable, path) if not os.path.islink(path): os.chmod(path, 0o755) for suffix in ('python', 'python3'): @@ -202,33 +208,32 @@ def setup_python(self, context): if not os.path.islink(path): os.chmod(path, 0o755) else: - # For normal cases, the venvlauncher will be copied from - # our scripts folder. For builds, we need to copy it - # manually. - if sysconfig.is_python_build(True): - suffix = '.exe' - if context.python_exe.lower().endswith('_d.exe'): - suffix = '_d.exe' - - src = os.path.join(dirname, "venvlauncher" + suffix) - dst = os.path.join(binpath, context.python_exe) - copier(src, dst) - - src = os.path.join(dirname, "venvwlauncher" + suffix) - dst = os.path.join(binpath, "pythonw" + suffix) - copier(src, dst) - - # copy init.tcl over - for root, dirs, files in os.walk(context.python_dir): - if 'init.tcl' in files: - tcldir = os.path.basename(root) - tcldir = os.path.join(context.env_dir, 'Lib', tcldir) - if not os.path.exists(tcldir): - os.makedirs(tcldir) - src = os.path.join(root, 'init.tcl') - dst = os.path.join(tcldir, 'init.tcl') - shutil.copyfile(src, dst) - break + subdir = 'DLLs' + include = self.include_binary + files = [f for f in os.listdir(dirname) if include(f)] + for f in files: + src = os.path.join(dirname, f) + dst = os.path.join(binpath, f) + if dst != context.env_exe: # already done, above + copier(src, dst) + dirname = os.path.join(dirname, subdir) + if os.path.isdir(dirname): + files = [f for f in os.listdir(dirname) if include(f)] + for f in files: + src = os.path.join(dirname, f) + dst = os.path.join(binpath, f) + copier(src, dst) + # copy init.tcl over + for root, dirs, files in os.walk(context.python_dir): + if 'init.tcl' in files: + tcldir = os.path.basename(root) + tcldir = os.path.join(context.env_dir, 'Lib', tcldir) + if not os.path.exists(tcldir): + os.makedirs(tcldir) + src = os.path.join(root, 'init.tcl') + dst = os.path.join(tcldir, 'init.tcl') + shutil.copyfile(src, dst) + break def _setup_pip(self, context): """Installs or upgrades pip in a virtual environment""" @@ -315,7 +320,7 @@ def install_scripts(self, context, path): dstfile = os.path.join(dstdir, f) with open(srcfile, 'rb') as f: data = f.read() - if not srcfile.endswith(('.exe', '.pdb')): + if not srcfile.endswith('.exe'): try: data = data.decode('utf-8') data = self.replace_variables(data, context) diff --git a/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst deleted file mode 100644 index 8e1a4ba84880..000000000000 --- a/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst +++ /dev/null @@ -1 +0,0 @@ -Adds support for building a Windows App Store package diff --git a/PC/classicAppCompat.can.xml b/PC/classicAppCompat.can.xml deleted file mode 100644 index f00475c8da31..000000000000 --- a/PC/classicAppCompat.can.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/PC/classicAppCompat.cat b/PC/classicAppCompat.cat deleted file mode 100644 index 3d213596accf..000000000000 Binary files a/PC/classicAppCompat.cat and /dev/null differ diff --git a/PC/classicAppCompat.sccd b/PC/classicAppCompat.sccd deleted file mode 100644 index 97648985a2cc..000000000000 --- a/PC/classicAppCompat.sccd +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - MIIq5AYJKoZIhvcNAQcCoIIq1TCCKtECAQExDzANBglghkgBZQMEAgEFADCCARAGCSsGAQQBgjcKAaCCAQEwgf4wDAYKKwYBBAGCNwwBAQQQaM+L42jwBUGvBczrtolMmhcNMTgxMTMwMDA1OTAzWjAOBgorBgEEAYI3DAEDBQAwgbwwKgQUWKcU3R38DGPlKK33XGIwKtVL1r4xEjAQBgorBgEEAYI3DAIDMQKCADCBjQQg3K+KBOQX7HfxjRNZC9cx8gIPkEhPRO1nJFRdWQrVEJ4xaTAQBgorBgEEAYI3DAIDMQKCADBVBgorBgEEAYI3AgEEMUcwRTAQBgorBgEEAYI3AgEZogKAADAxMA0GCWCGSAFlAwQCAQUABCDcr4oE5Bfsd/GNE1kL1zHyAg+QSE9E7WckVF1ZCtUQnqCCFFAwggZSMIIEOqADAgECAhMzAAMu49KhfNamygpWAAIAAy7jMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQDEx5NaWNyb3NvZnQgTWFya2V0cGxhY2UgQ0EgRyAwMTMwHhcNMTgxMTMwMDA1NTA1WhcNMTgxMjAzMDA1NTA1WjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwpcimfAx3HEpba1GLL/gDaRVddHE5PXTRmwlgaz8kt6/rq5rlrPFnCnbIc5818v0xJIznastbmrq26xyCEHyMLBKnyneTKE36I7+TGjcY0D7ow+o2vY7LDKMCTGlh31fx1Tvrl+5xTbWX5jdLU/3MB5faeOGh+0Knzwx1KDoXWgPtfXnD8I5jxJieoWoCwCjKTJgBOklLy9nbOalxf0h+xQRy2p5fj+PxAwQPgHWft36AF7/IMbt9FcXMtg4xdpnTYz4OV3dFOPz4m3M8HwVgNMv89W/1Ozc7uOyZt0Ij1baT6r2L3IjYg5ftzpGqaDOFcWlyDFSdhMR6BIKW8xEpAgMBAAGjggHCMIIBvjAYBgNVHSUBAf8EDjAMBgorBgEEAYI3TBwBMB0GA1UdDgQWBBRdpGYiCytx83FYzPSl+o97YzpxGzAPBgNVHREECDAGggRNT1BSMB8GA1UdIwQYMBaAFEnYB1RFhpclHtZZcRLDcpt0OE3oMGIGA1UdHwRbMFkwV6BVoFOGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyME1hcmtldHBsYWNlJTIwQ0ElMjBHJTIwMDEzKDIpLmNybDBvBggrBgEFBQcBAQRjMGEwXwYIKwYBBQUHMAKGU2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwTWFya2V0cGxhY2UlMjBDQSUyMEclMjAwMTMoMikuY3J0MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXgMDwGCSsGAQQBgjcVBwQvMC0GJSsGAQQBgjcVCIOS9kTqrxCDkY0wgqzgLIKinDE0g+6NOIaE7wACAWQCARYwIAYJKwYBBAGCNxUKAQH/BBAwDjAMBgorBgEEAYI3TBwBMA0GCSqGSIb3DQEBCwUAA4ICAQB3Dk3rXH52CDq/z1fwqn9xI5WGjGmu6oAE4HSc3sNdFrSVMMGm4gTlYGWSZ0wJUUf16mVr/rdXhxuR3MZn+m4Bhdl8KQqYjYbIvCUVj0o9nZ+yT6foeY8bKnB+K5h6rol+mjDj5IfcutC4x2Kx5RrtDtRTSoKA63iZ74DYngPpBGBBgaS2c/QzgqPRAMMRqy2KBDP0miCnpR3F4YlzHGyOZwyHhESjYd9kwF47+msuHS04JZpnGHIvBppKN9XQzH3WezNnnX3lz4AyAUMsMFuARqEnacUhrAHL9n5zMv9CzxDYN1r1/aDh/788RuGuZM+E3NtmbxJJ7j6T5/VtXNBRgKtIq8d2+11j6qvKLigOTxSC25/A70BZBEvllLFnvc1vA2LrC9drwt1KpSmWie1nvpilw7o+gHMOG9utUxGha2VuVizuVNGCywTRRjvmGS1QqTfaun1URVrLfnDINXuTgN1Vwp0J5IGpJ3D8yj01NDQ/RworE+3W/R531NBYova9QRhU/igEw/Aa/q8wjZ4Pzxr9oBIo0Ta3Tv6qIggaWXw0U9+F0J7SCqIhn0d0ATO+E1Qs/SxZIAICLwmqzoLYUAh8q153esBs4uesueqgt5ueyHK8V3WjMS4wxEyVN5ZMET3hFtEshsZC31tLDdjq750U4SgQVmoYSm3F3ZOKQDCCBtcwggS/oAMCAQICCmESRKIAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDMyODIxMDkzOVoXDTMxMDMyODIxMTkzOVowfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAubUaSwGYVsE3MAnPfvmozUhAB3qxBABgJRW1vDp4+tVinXxD32f7k1K89JQ6zDOgS/iDgULC+yFK1K/1Qjac/0M7P6c8v5LSjnWGlERLa/qY32j46S7SLQcit3g2jgoTTO03eUG+9yHZUTGV/FJdRYB8uXhrznJBa+Y+yGwiQKF+m6XFeBH/KORoKFx+dmMoy9EWJ/m/o9IiUj2kzm9C691+vZ/I2w0Bj93W9SPPkV2PCNHlzgfIAoeajWpHmi38Wi3xZHonkzAVBHxPsCBppOoNsWvmAfUM7eBthkSPvFruekyDCPNEYhfGqgqtqLkoBebXLZCOVybF7wTQaLvse60//3P003icRcCoQYgY4NAqrF7j80o5U7DkeXxcB0xvengsaKgiAaV1DKkRbpe98wCqr1AASvm5rAJUYMU+mXmOieV2EelY2jGrenWe9FQpNXYV1NoWBh0WKoFxttoWYAnF705bIWtSZsz08ZfK6WLX4GXNLcPBlgCzfTm1sdKYASWdBbH2haaNhPapFhQQBJHKwnVW2iXErImhuPi45W3MVTZ5D9ASshZx69cLYY6xAdIa+89Kf/uRrsGOVZfahDuDw+NI183iAyzC8z/QRt2P32LYxP0xrCdqVh+DJo2i4NoE8Uk1usCdbVRuBMBQl/AwpOTq7IMvHGElf65CqzUCAwEAAaOCAUswggFHMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBQPU8s/FmEl/mCJHdO5fOiQrbOU0TAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCjuZmM8ZVNDgp9wHsL4RY8KJ8nLinvxFTphNGCrxaLknkYG5pmMhVlX+UB/tSiW8W13W60nggz9u5xwMx7v/1t/Tgm6g2brVyOKI5A7u6/2SIJwkJKFw953K0YIKVT28w9zl8dSJnmRnyR0G86ncWbF6CLQ6A6lBQ9o2mTGVqDr4m35WKAnc6YxUUM1y74mbzFFZr63VHsCcOp3pXWnUqAY1rb6Q6NX1b3clncKqLFm0EjKHcQ56grTbwuuB7pMdh/IFCJR01MQzQbDtpEisbOeZUi43YVAAHKqI1EO9bRwg3frCjwAbml9MmI4utMW94gWFgvrMxIX+n42RBDIjf3Ot3jkT6gt3XeTTmO9bptgblZimhERdkFRUFpVtkocJeLoGuuzP93uH/Yp032wzRH+XmMgujfZv+vnfllJqxdowoQLx55FxLLeTeYfwi/xMSjZO2gNven3U/3KeSCd1kUOFS3AOrwZ0UNOXJeW5JQC6Vfd1BavFZ6FAta1fMLu3WFvNB+FqeHUaU3ya7rmtxJnzk29DeSqXgGNmVSywBS4NajI5jJIKAA6UhNJlsg8CHYwUOKf5ej8OoQCkbadUxXygAfxCfW2YBbujtI+PoyejRFxWUjYFWO5LeTI62UMyqfOEiqugoYjNxmQZla2s4YHVuqIC34R85FQlg9pKQBsDCCBxswggUDoAMCAQICEzMAAABCs21EHGjyqKYAAAAAAEIwDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMB4XDTE4MDQyMDE2NDI0NFoXDTIxMDQyMDE2NDI0NFowgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOZ2KM9Pq1YCOiqWOivmHjUtkMgznTMP/Mr2YfzZeIIJySg1F4WxFZc4jagGHHNof9NRT+GGnktWsXkZuH1DzQEG4Ps1ln8+4vhbDglqu5ymDnd6RmsyoD+8xfc8bBIvE5o6R+ES4/GVD5TqNsOrWbwETaIZVbmTulJLoTS1WSsSjowmbc+sHqZiY8BNJNThUEmXSjuHqkQKKshuiFWYEqOTitp71mBLyH1wN7/jThRzGpolOeFusRNJdb8sEqvNzEN9Qh+Kp6ndzrnjE+t8ixXW3lShyyOOZqQMwsQn9q9T0v7Q69GuojBTFBOHKwigcCHr4xahuN+ZYMk0xGg+sm3Uj7I9mrWTSTiIRMZNIWq3sFg4+rFg48NYfRlXUpONmL7vXq6v1pIU99d2MXQ6uUrnUr1/n5ZiHGCeFcvWwqO8BYHdcTlrSOkayfFp7W9oCk9QO4Xy0h9cQRedRo2kvdTHxIuJS70Hdv6oePPF2ZFaLucUzzwsR4/XMAVKY8Vsm950omsSSOImsMtzavUdQM+wZFxvHTRqVDkF3quPdME0bCZOWB4hQJmd+o2clw+1mpwPu0/M92nA9FJg7MGPxkFaYW7g26jSqUJZ9AcX+Xa5TSIeqMZt3cRVjMTx0T/v73Sv8TpalqIQ5Fde1+hFK07sOAm3TwgzvlVJnbYgp0/rAgMBAAGjggGCMIIBfjASBgkrBgEEAYI3FQEEBQIDAgACMCMGCSsGAQQBgjcVAgQWBBSbJnDhuc3nQXuKuACsPflEbwjbozAdBgNVHQ4EFgQUSdgHVEWGlyUe1llxEsNym3Q4TegwEQYDVR0gBAowCDAGBgRVHSAAMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFA9Tyz8WYSX+YIkd07l86JCts5TRMFcGA1UdHwRQME4wTKBKoEiGRmh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcmwwWwYIKwYBBQUHAQEETzBNMEsGCCsGAQUFBzAChj9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAIa2oa6kvuIHCNfz7anlL0W9tOCt8gQNkxOGRK3yliQIelNQahDJojyEFlHQ2BcHL5oZit3WeSDoYddhojx6YzJIWwfGwtVqgc0JFDKJJ2ZXRYMRsuy01Hn25xob+zRMS6VmV1axQn6uwOSMcgYmzoroh6edjPKu7qXcpt6LmhF2qFvLySA7wBCwfI/rR5/PX6I7a07Av7PpbY6/+2ujd8m1H3hwMrb4Hq3z6gcq62zJ3nDXUbC0Bp6Jt2kV9f0rEFpDK9oxE2qrGBUf8c3O2XirHOgAjRyWjWWtVms+MP8qBIA1NSLrBmToEWVP3sEkQZWMkoZWo4rYEJZpX7UIgdDc9zYNakgTCJqPhqn8AE1sgSSnpqAdMkkP41rTlFCv2ig2QVzDerjGfEv+uPDnlAT0kucbBJxHHvUC4aqUxaTSa0sy2bZ6NWFx8/u0gW8JahzxYvvvZL8SfwaA9P4ETb8pH1jw+6N/LfM2zJrNKhf5hjKa0VDOXUpkYq60OqVVnWJ6oJaSIWNkZKfzPnl/UHA8Bh4qfVrhc9H5PExPhhB9WVTsjf4r+OOVuolJldThcWQqljiPjk5rultr63G5xLyFpxNi4BCrcNQBJFB5wKgOWOyjQTVWTmh2ESaeqZ2aWBjftFHlxJ/qYc7WOGJV0+cHGkB/dvFxmKnv6tuWexiMMYIVUTCCFU0CAQEwgaQwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMwITMwADLuPSoXzWpsoKVgACAAMu4zANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKwYBBAGCNwoBMC8GCSqGSIb3DQEJBDEiBCAS0d3bw2YOODvKFr0S4e3BDnaDcZXUKeBO77yvkWzVojBIBgorBgEEAYI3AgEMMTowOKAegBwATQBpAGMAcgBvAHMAbwBmAHQAIABDAG8AcgBwoRaAFGh0dHA6Ly9NaWNyb3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIBABoap3Y+2k+zFz2cCmkc8xxHnpIygLsUSRMXeXdjPVcYx3o5cPLIixnL6p8+LIrlIagPg23mzTEmnjZaO4aaexk+3XojlHj22w/bEigEDnKyWt5bHeS0UNHJbxEFYRfd84IP1+mSH4c4+GuU9p3LsAMh6wN03MYrGmczUOnlP6YlxHNQbQxnV0sl14yOE5ni9oT4y+l+SllvbV3/Jhwpov68aoP/2MazqxR4QyGfSxhCPJ4UuDHU7IrpnTxGBTL1/oUU8ED0FxyDoH/Sc5OhTLInFqbZaVzm5Mpr12wYUBL4nE5h0Kf6BCKdgM8a+Ti3wMUsBoC79ff3jE9U/xwSneOhghLlMIIS4QYKKwYBBAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQghPy22lwuCYESw8jYhb4F9ZDPJ1LPgSSZgJDkyXYzVt4CBlv98KtAoBgTMjAxODExMzAwMTA1MTkuMTM4WjAEgAIB9KCB0KSBzTCByjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RDA4Mi00QkZELUVFQkExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAOIYOHtm6erB2AAAAAAA4jANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0xODA4MjMyMDI3MDNaFw0xOTExMjMyMDI3MDNaMIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEMDgyLTRCRkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgc2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKirA72FF3NCLW5mfLO/D0EZ5Ycs00oiMSissXLB6WF9GNdP78QzFwAypxW/+qZSczqaHbDH8hlbxkzf3DiYgAdpQjnGkLujwKtWSaP29/lVf7jFqHy9v6eH+LdOi0LvtrPRW34MyCvpxZyOW4H1h3PkxCBL5Ra21sDqgcVL1me0osw8QTURXmI4LyeLdTH3CcI2AgNDXTjsFBf3QsO+JYyAOYWrTcLnywVN6DrigmgrDJk5w+wR4VrHfl2T9PRZbZ+UDt13wwyB9d6IURuzV8lHsAVfF8t9S0aGVPmkQ3c2waOhHpsp6VEM+T5D2Ph8xJX1r82z67WRlmGcOP2NWC0CAwEAAaOCARswggEXMB0GA1UdDgQWBBSJPpD6BsP2p+crDJL232voEtLxezAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQARQHu7ISeBuJSHKuDRI04704cH0B7BYzeEIrD15awviMRcYIfIOHpvGzZOWQgP2Hm0Rr7kvTUu1VrSSaQ7i1gPWdhqMmw5WBnSS5bxeMhhx9UsASeE84vUu82NeZapGSjH38YAb4WT+TtiTkcoI59rA+CTCq108ttIxVfZcr3id76OETIH0HvhlnxOOWjwGy4ul6Za5RoTLG/oo2rrGmVi3FwrNWGezYLBODuEsjzG36lCRtBKC2ZAHfbOz5wtkUHbqh79mUKocjP4r3qxf5TN87yf6g1uTx+J8pdnAi5iHt+ZtangWqnVTE8PoIREWhBVlGFfQdkELUx2Or90aAqWMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQwODItNEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBzZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQByQCUheEOevaI9Zc/3QGrkX42iC6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA36ppYDAiGA8yMDE4MTEyOTIxMzQyNFoYDzIwMTgxMTMwMjEzNDI0WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDfqmlgAgEAMAoCAQACAitfAgH/MAcCAQACAhGtMAoCBQDfq7rgAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbAXXPR9wy4NA0892GGqetaZF+pNClpGcfEpSuHABaZ4Gzr1nY1nmrhexTtr/U6omHALRWzkQwthk0cy+mnEHXyOZGmoEEpgrLgK3AAP5NbK/XbtHQRyZJQyhZScFbOyQycoE8QQalSVOhWxk/bbBMQaQiYVMIexNd/T0KgaDDUMxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAOIYOHtm6erB2AAAAAAA4jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCr9IiSbx6s8MLdxldRG49+4h6CbicW8hWXAicI3jNmhDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIN8BpJSmQCGubWwVa4tW+aMveoHMX/nDnVN8fiDOMsrLMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADiGDh7ZunqwdgAAAAAAOIwIgQgTkOfRvGEZNbr5/hgWclsL4/Q7SOZihE/U0lz2wEMIGcwDQYJKoZIhvcNAQELBQAEggEATlxnCfTzFfTMDvK085zlYPVCroKYW6gKFYnbAhNmrNzcxqALKmIYXpFU7B6HH/vYzkUfCyXpf5tsyEWu0oTySOjyAZ9+2vdaG8nEgjOp0L737lcitgusIjpWtta3Ik0b+mzffnvyjrgTSuKDDni3mxGfvJU77k1Ctempma4H2FJso6Bur0PRH99vIYDu4lHigOSLbeyjR5CiDciBwEVUSA0FxhoFNX1yfpxz3sukOvkaoTduREIjH5LxUjNI1ZTMK/ZkeETI8IPRpWVzAc8q7CujErHKo4sdKej/O2cfUTUHplFLVCGGExpJUCg5FH5jVUUFt75ad8503sdGplggVQ== diff --git a/PC/getpathp.c b/PC/getpathp.c index 1b553d53affa..599b41b1efc1 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -536,16 +536,10 @@ static _PyInitError get_program_full_path(const _PyCoreConfig *core_config, PyCalculatePath *calculate, _PyPathConfig *config) { - const wchar_t *pyvenv_launcher; wchar_t program_full_path[MAXPATHLEN+1]; memset(program_full_path, 0, sizeof(program_full_path)); - /* The launcher may need to force the executable path to a - * different environment, so override it here. */ - pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__"); - if (pyvenv_launcher && pyvenv_launcher[0]) { - wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher); - } else if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { + if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { /* GetModuleFileName should never fail when passed NULL */ return _Py_INIT_ERR("Cannot determine program path"); } diff --git a/PC/icons/pythonwx150.png b/PC/icons/pythonwx150.png deleted file mode 100644 index 4c3eb316739c..000000000000 Binary files a/PC/icons/pythonwx150.png and /dev/null differ diff --git a/PC/icons/pythonwx44.png b/PC/icons/pythonwx44.png deleted file mode 100644 index e3b32a871f90..000000000000 Binary files a/PC/icons/pythonwx44.png and /dev/null differ diff --git a/PC/icons/pythonx150.png b/PC/icons/pythonx150.png deleted file mode 100644 index 5f8d30418386..000000000000 Binary files a/PC/icons/pythonx150.png and /dev/null differ diff --git a/PC/icons/pythonx44.png b/PC/icons/pythonx44.png deleted file mode 100644 index 3881daaef233..000000000000 Binary files a/PC/icons/pythonx44.png and /dev/null differ diff --git a/PC/icons/pythonx50.png b/PC/icons/pythonx50.png deleted file mode 100644 index 7cc3aecd0242..000000000000 Binary files a/PC/icons/pythonx50.png and /dev/null differ diff --git a/PC/launcher.c b/PC/launcher.c index 4c620dab7c09..7d666aae4ab1 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -28,7 +28,7 @@ #define RC_NO_PYTHON 103 #define RC_NO_MEMORY 104 /* - * SCRIPT_WRAPPER is used to choose one of the variants of an executable built + * SCRIPT_WRAPPER is used to choose between two variants of an executable built * from this source file. If not defined, the PEP 397 Python launcher is built; * if defined, a script launcher of the type used by setuptools is built, which * looks for a script name related to the executable name and runs that script @@ -40,15 +40,6 @@ #if defined(SCRIPT_WRAPPER) #define RC_NO_SCRIPT 105 #endif -/* - * VENV_REDIRECT is used to choose the variant that looks for an adjacent or - * one-level-higher pyvenv.cfg, and uses its "home" property to locate and - * launch the original python.exe. - */ -#if defined(VENV_REDIRECT) -#define RC_NO_VENV_CFG 106 -#define RC_BAD_VENV_CFG 107 -#endif /* Just for now - static definition */ @@ -106,7 +97,7 @@ error(int rc, wchar_t * format, ... ) #if !defined(_WINDOWS) fwprintf(stderr, L"%ls\n", message); #else - MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...", + MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), MB_OK); #endif exit(rc); @@ -140,17 +131,6 @@ static wchar_t * get_env(wchar_t * key) return buf; } -#if defined(_DEBUG) -#if defined(_WINDOWS) - -#define PYTHON_EXECUTABLE L"pythonw_d.exe" - -#else - -#define PYTHON_EXECUTABLE L"python_d.exe" - -#endif -#else #if defined(_WINDOWS) #define PYTHON_EXECUTABLE L"pythonw.exe" @@ -159,7 +139,6 @@ static wchar_t * get_env(wchar_t * key) #define PYTHON_EXECUTABLE L"python.exe" -#endif #endif #define MAX_VERSION_SIZE 4 @@ -1139,7 +1118,6 @@ static PYC_MAGIC magic_values[] = { { 3320, 3351, L"3.5" }, { 3360, 3379, L"3.6" }, { 3390, 3399, L"3.7" }, - { 3400, 3409, L"3.8" }, { 0 } }; @@ -1478,87 +1456,6 @@ show_python_list(wchar_t ** argv) return FALSE; /* If this has been called we cannot continue */ } -#if defined(VENV_REDIRECT) - -static int -find_home_value(const char *buffer, const char **start, DWORD *length) -{ - for (const char *s = strstr(buffer, "home"); s; s = strstr(s + 1, "\nhome")) { - if (*s == '\n') { - ++s; - } - for (int i = 4; i > 0 && *s; --i, ++s); - - while (*s && iswspace(*s)) { - ++s; - } - if (*s != L'=') { - continue; - } - - do { - ++s; - } while (*s && iswspace(*s)); - - *start = s; - char *nl = strchr(s, '\n'); - if (nl) { - *length = (DWORD)((ptrdiff_t)nl - (ptrdiff_t)s); - } else { - *length = (DWORD)strlen(s); - } - return 1; - } - return 0; -} -#endif - -static wchar_t * -wcsdup_pad(const wchar_t *s, int padding, int *newlen) -{ - size_t len = wcslen(s); - len += 1 + padding; - wchar_t *r = (wchar_t *)malloc(len * sizeof(wchar_t)); - if (!r) { - return NULL; - } - if (wcscpy_s(r, len, s)) { - free(r); - return NULL; - } - *newlen = len < MAXINT ? (int)len : MAXINT; - return r; -} - -static wchar_t * -get_process_name() -{ - DWORD bufferLen = MAX_PATH; - DWORD len = bufferLen; - wchar_t *r = NULL; - - while (!r) { - r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); - if (!r) { - error(RC_NO_MEMORY, L"out of memory"); - return NULL; - } - len = GetModuleFileNameW(NULL, r, bufferLen); - if (len == 0) { - free(r); - error(0, L"Failed to get module name"); - return NULL; - } else if (len == bufferLen && - GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - free(r); - r = NULL; - bufferLen *= 2; - } - } - - return r; -} - static int process(int argc, wchar_t ** argv) { @@ -1566,27 +1463,21 @@ process(int argc, wchar_t ** argv) wchar_t * command; wchar_t * executable; wchar_t * p; - wchar_t * argv0; int rc = 0; + size_t plen; INSTALLED_PYTHON * ip; BOOL valid; DWORD size, attrs; + HRESULT hr; wchar_t message[MSGSIZE]; void * version_data; VS_FIXEDFILEINFO * file_info; UINT block_size; -#if defined(VENV_REDIRECT) - wchar_t * venv_cfg_path; + int index; +#if defined(SCRIPT_WRAPPER) int newlen; -#elif defined(SCRIPT_WRAPPER) wchar_t * newcommand; wchar_t * av[2]; - int newlen; - HRESULT hr; - int index; -#else - HRESULT hr; - int index; #endif setvbuf(stderr, (char *)NULL, _IONBF, 0); @@ -1604,7 +1495,6 @@ process(int argc, wchar_t ** argv) #else debug(L"launcher executable: Console\n"); #endif -#if !defined(VENV_REDIRECT) /* Get the local appdata folder (non-roaming) */ hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appdata_ini_path); @@ -1613,7 +1503,9 @@ process(int argc, wchar_t ** argv) appdata_ini_path[0] = L'\0'; } else { - wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE); + plen = wcslen(appdata_ini_path); + p = &appdata_ini_path[plen]; + wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE); attrs = GetFileAttributesW(appdata_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", appdata_ini_path); @@ -1622,9 +1514,8 @@ process(int argc, wchar_t ** argv) debug(L"Using local configuration file '%ls'\n", appdata_ini_path); } } -#endif - argv0 = get_process_name(); - size = GetFileVersionInfoSizeW(argv0, &size); + plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH); + size = GetFileVersionInfoSizeW(launcher_ini_path, &size); if (size == 0) { winerror(GetLastError(), message, MSGSIZE); debug(L"GetFileVersionInfoSize failed: %ls\n", message); @@ -1632,7 +1523,7 @@ process(int argc, wchar_t ** argv) else { version_data = malloc(size); if (version_data) { - valid = GetFileVersionInfoW(argv0, 0, size, + valid = GetFileVersionInfoW(launcher_ini_path, 0, size, version_data); if (!valid) debug(L"GetFileVersionInfo failed: %X\n", GetLastError()); @@ -1649,51 +1540,15 @@ process(int argc, wchar_t ** argv) free(version_data); } } - -#if defined(VENV_REDIRECT) - /* Allocate some extra space for new filenames */ - venv_cfg_path = wcsdup_pad(argv0, 32, &newlen); - if (!venv_cfg_path) { - error(RC_NO_MEMORY, L"Failed to copy module name"); - } - p = wcsrchr(venv_cfg_path, L'\\'); - - if (p == NULL) { - error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); - } - p[0] = L'\0'; - wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); - attrs = GetFileAttributesW(venv_cfg_path); - if (attrs == INVALID_FILE_ATTRIBUTES) { - debug(L"File '%ls' non-existent\n", venv_cfg_path); - p[0] = '\0'; - p = wcsrchr(venv_cfg_path, L'\\'); - if (p != NULL) { - p[0] = '\0'; - wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); - attrs = GetFileAttributesW(venv_cfg_path); - if (attrs == INVALID_FILE_ATTRIBUTES) { - debug(L"File '%ls' non-existent\n", venv_cfg_path); - error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); - } - } - } - debug(L"Using venv configuration file '%ls'\n", venv_cfg_path); -#else - /* Allocate some extra space for new filenames */ - if (wcscpy_s(launcher_ini_path, MAX_PATH, argv0)) { - error(RC_NO_MEMORY, L"Failed to copy module name"); - } p = wcsrchr(launcher_ini_path, L'\\'); - if (p == NULL) { debug(L"GetModuleFileNameW returned value has no backslash: %ls\n", launcher_ini_path); launcher_ini_path[0] = L'\0'; } else { - p[0] = L'\0'; - wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini"); + wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini", + _TRUNCATE); attrs = GetFileAttributesW(launcher_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", launcher_ini_path); @@ -1702,7 +1557,6 @@ process(int argc, wchar_t ** argv) debug(L"Using global configuration file '%ls'\n", launcher_ini_path); } } -#endif command = skip_me(GetCommandLineW()); debug(L"Called with command line: %ls\n", command); @@ -1738,55 +1592,6 @@ process(int argc, wchar_t ** argv) command = newcommand; valid = FALSE; } -#elif defined(VENV_REDIRECT) - { - FILE *f; - char buffer[4096]; /* 4KB should be enough for anybody */ - char *start; - DWORD len, cch, cch_actual; - size_t cb; - if (_wfopen_s(&f, venv_cfg_path, L"r")) { - error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path); - } - cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]), - sizeof(buffer) / sizeof(buffer[0]), f); - fclose(f); - - if (!find_home_value(buffer, &start, &len)) { - error(RC_BAD_VENV_CFG, L"Cannot find home in '%ls'", - venv_cfg_path); - } - - cch = MultiByteToWideChar(CP_UTF8, 0, start, len, NULL, 0); - if (!cch) { - error(0, L"Cannot determine memory for home path"); - } - cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 1 + 1; /* include sep and null */ - executable = (wchar_t *)malloc(cch * sizeof(wchar_t)); - if (executable == NULL) { - error(RC_NO_MEMORY, L"A memory allocation failed"); - } - cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, executable, cch); - if (!cch_actual) { - error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'", - venv_cfg_path); - } - if (executable[cch_actual - 1] != L'\\') { - executable[cch_actual++] = L'\\'; - executable[cch_actual] = L'\0'; - } - if (wcscat_s(executable, cch, PYTHON_EXECUTABLE)) { - error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'", - venv_cfg_path); - } - if (GetFileAttributesW(executable) == INVALID_FILE_ATTRIBUTES) { - error(RC_NO_PYTHON, L"No Python at '%ls'", executable); - } - if (!SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", argv0)) { - error(0, L"Failed to set launcher environment"); - } - valid = 1; - } #else if (argc <= 1) { valid = FALSE; @@ -1794,6 +1599,7 @@ process(int argc, wchar_t ** argv) } else { p = argv[1]; + plen = wcslen(p); if ((argc == 2) && // list version args (!wcsncmp(p, L"-0", wcslen(L"-0")) || !wcsncmp(p, L"--list", wcslen(L"--list")))) diff --git a/PC/layout/__init__.py b/PC/layout/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/PC/layout/__main__.py b/PC/layout/__main__.py deleted file mode 100644 index f7aa1e6d261f..000000000000 --- a/PC/layout/__main__.py +++ /dev/null @@ -1,14 +0,0 @@ -import sys - -try: - import layout -except ImportError: - # Failed to import our package, which likely means we were started directly - # Add the additional search path needed to locate our module. - from pathlib import Path - - sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) - -from layout.main import main - -sys.exit(int(main() or 0)) diff --git a/PC/layout/main.py b/PC/layout/main.py deleted file mode 100644 index 82d0536ca920..000000000000 --- a/PC/layout/main.py +++ /dev/null @@ -1,612 +0,0 @@ -""" -Generates a layout of Python for Windows from a build. - -See python make_layout.py --help for usage. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - -import argparse -import functools -import os -import re -import shutil -import subprocess -import sys -import tempfile -import zipfile - -from pathlib import Path - -if __name__ == "__main__": - # Started directly, so enable relative imports - __path__ = [str(Path(__file__).resolve().parent)] - -from .support.appxmanifest import * -from .support.catalog import * -from .support.constants import * -from .support.filesets import * -from .support.logging import * -from .support.options import * -from .support.pip import * -from .support.props import * - -BDIST_WININST_FILES_ONLY = FileNameSet("wininst-*", "bdist_wininst.py") -BDIST_WININST_STUB = "PC/layout/support/distutils.command.bdist_wininst.py" - -TEST_PYDS_ONLY = FileStemSet("xxlimited", "_ctypes_test", "_test*") -TEST_DIRS_ONLY = FileNameSet("test", "tests") - -IDLE_DIRS_ONLY = FileNameSet("idlelib") - -TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter") -TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo") -TCLTK_FILES_ONLY = FileNameSet("turtle.py") - -VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip") - -EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext") -EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle") -EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt") -EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*") -EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll") - -REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*") - -LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt") - -PY_FILES = FileSuffixSet(".py") -PYC_FILES = FileSuffixSet(".pyc") -CAT_FILES = FileSuffixSet(".cat") -CDF_FILES = FileSuffixSet(".cdf") - -DATA_DIRS = FileNameSet("data") - -TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser") -TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt") - - -def get_lib_layout(ns): - def _c(f): - if f in EXCLUDE_FROM_LIB: - return False - if f.is_dir(): - if f in TEST_DIRS_ONLY: - return ns.include_tests - if f in TCLTK_DIRS_ONLY: - return ns.include_tcltk - if f in IDLE_DIRS_ONLY: - return ns.include_idle - if f in VENV_DIRS_ONLY: - return ns.include_venv - else: - if f in TCLTK_FILES_ONLY: - return ns.include_tcltk - if f in BDIST_WININST_FILES_ONLY: - return ns.include_bdist_wininst - return True - - for dest, src in rglob(ns.source / "Lib", "**/*", _c): - yield dest, src - - if not ns.include_bdist_wininst: - src = ns.source / BDIST_WININST_STUB - yield Path("distutils/command/bdist_wininst.py"), src - - -def get_tcltk_lib(ns): - if not ns.include_tcltk: - return - - tcl_lib = os.getenv("TCL_LIBRARY") - if not tcl_lib or not os.path.isdir(tcl_lib): - try: - with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f: - tcl_lib = f.read().strip() - except FileNotFoundError: - pass - if not tcl_lib or not os.path.isdir(tcl_lib): - warn("Failed to find TCL_LIBRARY") - return - - for dest, src in rglob(Path(tcl_lib).parent, "**/*"): - yield "tcl/{}".format(dest), src - - -def get_layout(ns): - def in_build(f, dest="", new_name=None): - n, _, x = f.rpartition(".") - n = new_name or n - src = ns.build / f - if ns.debug and src not in REQUIRED_DLLS: - if not src.stem.endswith("_d"): - src = src.parent / (src.stem + "_d" + src.suffix) - if not n.endswith("_d"): - n += "_d" - f = n + "." + x - yield dest + n + "." + x, src - if ns.include_symbols: - pdb = src.with_suffix(".pdb") - if pdb.is_file(): - yield dest + n + ".pdb", pdb - if ns.include_dev: - lib = src.with_suffix(".lib") - if lib.is_file(): - yield "libs/" + n + ".lib", lib - - yield from in_build("python_uwp.exe", new_name="python") - yield from in_build("pythonw_uwp.exe", new_name="pythonw") - - yield from in_build(PYTHON_DLL_NAME) - - if ns.include_launchers: - if ns.include_pip: - yield from in_build("python_uwp.exe", new_name="pip") - if ns.include_idle: - yield from in_build("pythonw_uwp.exe", new_name="idle") - - if ns.include_stable: - yield from in_build(PYTHON_STABLE_DLL_NAME) - - for dest, src in rglob(ns.build, "vcruntime*.dll"): - yield dest, src - - for dest, src in rglob(ns.build, ("*.pyd", "*.dll")): - if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS: - continue - if src in EXCLUDE_FROM_PYDS: - continue - if src in TEST_PYDS_ONLY and not ns.include_tests: - continue - if src in TCLTK_PYDS_ONLY and not ns.include_tcltk: - continue - - yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/") - - if ns.zip_lib: - zip_name = PYTHON_ZIP_NAME - yield zip_name, ns.temp / zip_name - else: - for dest, src in get_lib_layout(ns): - yield "Lib/{}".format(dest), src - - if ns.include_venv: - yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python") - yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw") - - if ns.include_tools: - - def _c(d): - if d.is_dir(): - return d in TOOLS_DIRS - return d in TOOLS_FILES - - for dest, src in rglob(ns.source / "Tools", "**/*", _c): - yield "Tools/{}".format(dest), src - - if ns.include_underpth: - yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME - - if ns.include_dev: - - def _c(d): - if d.is_dir(): - return d.name != "internal" - return True - - for dest, src in rglob(ns.source / "Include", "**/*.h", _c): - yield "include/{}".format(dest), src - src = ns.source / "PC" / "pyconfig.h" - yield "include/pyconfig.h", src - - for dest, src in get_tcltk_lib(ns): - yield dest, src - - if ns.include_pip: - pip_dir = get_pip_dir(ns) - if not pip_dir.is_dir(): - log_warning("Failed to find {} - pip will not be included", pip_dir) - else: - pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}" - for dest, src in rglob(pip_dir, "**/*"): - if src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB: - continue - yield pkg_root.format(dest), src - - if ns.include_chm: - for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME): - yield "Doc/{}".format(dest), src - - if ns.include_html_doc: - for dest, src in rglob(ns.doc_build / "html", "**/*"): - yield "Doc/html/{}".format(dest), src - - if ns.include_props: - for dest, src in get_props_layout(ns): - yield dest, src - - for dest, src in get_appx_layout(ns): - yield dest, src - - if ns.include_cat: - if ns.flat_dlls: - yield ns.include_cat.name, ns.include_cat - else: - yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat - - -def _compile_one_py(src, dest, name, optimize): - import py_compile - - if dest is not None: - dest = str(dest) - - try: - return Path( - py_compile.compile( - str(src), - dest, - str(name), - doraise=True, - optimize=optimize, - invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, - ) - ) - except py_compile.PyCompileError: - log_warning("Failed to compile {}", src) - return None - - -def _py_temp_compile(src, ns, dest_dir=None): - 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") - return _compile_one_py(src, dest.with_suffix(".pyc"), dest, optimize=2) - - -def _write_to_zip(zf, dest, src, ns): - pyc = _py_temp_compile(src, ns) - if pyc: - try: - zf.write(str(pyc), dest.with_suffix(".pyc")) - finally: - try: - pyc.unlink() - except: - log_exception("Failed to delete {}", pyc) - return - - if src in LIB2TO3_GRAMMAR_FILES: - from lib2to3.pgen2.driver import load_grammar - - tmp = ns.temp / src.name - try: - shutil.copy(src, tmp) - load_grammar(str(tmp)) - for f in ns.temp.glob(src.stem + "*.pickle"): - zf.write(str(f), str(dest.parent / f.name)) - try: - f.unlink() - except: - log_exception("Failed to delete {}", f) - except: - log_exception("Failed to compile {}", src) - finally: - try: - tmp.unlink() - except: - log_exception("Failed to delete {}", tmp) - - zf.write(str(src), str(dest)) - - -def generate_source_files(ns): - if ns.zip_lib: - zip_name = PYTHON_ZIP_NAME - zip_path = ns.temp / zip_name - if zip_path.is_file(): - zip_path.unlink() - elif zip_path.is_dir(): - log_error( - "Cannot create zip file because a directory exists by the same name" - ) - return - log_info("Generating {} in {}", zip_name, ns.temp) - ns.temp.mkdir(parents=True, exist_ok=True) - with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: - for dest, src in get_lib_layout(ns): - _write_to_zip(zf, dest, src, ns) - - if ns.include_underpth: - log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp) - ns.temp.mkdir(parents=True, exist_ok=True) - with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f: - if ns.zip_lib: - print(PYTHON_ZIP_NAME, file=f) - if ns.include_pip: - print("packages", file=f) - else: - print("Lib", file=f) - print("Lib/site-packages", file=f) - if not ns.flat_dlls: - print("DLLs", file=f) - print(".", file=f) - print(file=f) - print("# Uncomment to run site.main() automatically", file=f) - print("#import site", file=f) - - if ns.include_appxmanifest: - log_info("Generating AppxManifest.xml in {}", ns.temp) - ns.temp.mkdir(parents=True, exist_ok=True) - - with open(ns.temp / "AppxManifest.xml", "wb") as f: - f.write(get_appxmanifest(ns)) - - with open(ns.temp / "_resources.xml", "wb") as f: - f.write(get_resources_xml(ns)) - - if ns.include_pip: - pip_dir = get_pip_dir(ns) - if not (pip_dir / "pip").is_dir(): - log_info("Extracting pip to {}", pip_dir) - pip_dir.mkdir(parents=True, exist_ok=True) - extract_pip_files(ns) - - if ns.include_props: - log_info("Generating {} in {}", PYTHON_PROPS_NAME, ns.temp) - ns.temp.mkdir(parents=True, exist_ok=True) - with open(ns.temp / PYTHON_PROPS_NAME, "wb") as f: - f.write(get_props(ns)) - - -def _create_zip_file(ns): - if not ns.zip: - return None - - if ns.zip.is_file(): - try: - ns.zip.unlink() - except OSError: - log_exception("Unable to remove {}", ns.zip) - sys.exit(8) - elif ns.zip.is_dir(): - log_error("Cannot create ZIP file because {} is a directory", ns.zip) - sys.exit(8) - - ns.zip.parent.mkdir(parents=True, exist_ok=True) - return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED) - - -def copy_files(files, ns): - if ns.copy: - ns.copy.mkdir(parents=True, exist_ok=True) - - try: - total = len(files) - except TypeError: - total = None - count = 0 - - zip_file = _create_zip_file(ns) - try: - need_compile = [] - in_catalog = [] - - for dest, src in files: - count += 1 - if count % 10 == 0: - if total: - log_info("Processed {:>4} of {} files", count, total) - else: - log_info("Processed {} files", count) - log_debug("Processing {!s}", src) - - if ( - ns.precompile - and src in PY_FILES - and src not in EXCLUDE_FROM_COMPILE - and src.parent not in DATA_DIRS - and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib")) - ): - if ns.copy: - need_compile.append((dest, ns.copy / dest)) - else: - (ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(src, ns.temp / "Lib" / dest) - need_compile.append((dest, ns.temp / "Lib" / dest)) - - if src not in EXCLUDE_FROM_CATALOG: - in_catalog.append((src.name, src)) - - if ns.copy: - log_debug("Copy {} -> {}", src, ns.copy / dest) - (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True) - try: - shutil.copy2(src, ns.copy / dest) - except shutil.SameFileError: - pass - - if ns.zip: - log_debug("Zip {} into {}", src, ns.zip) - zip_file.write(src, str(dest)) - - if need_compile: - for dest, src in need_compile: - compiled = [ - _compile_one_py(src, None, dest, optimize=0), - _compile_one_py(src, None, dest, optimize=1), - _compile_one_py(src, None, dest, optimize=2), - ] - for c in compiled: - if not c: - continue - cdest = Path(dest).parent / Path(c).relative_to(src.parent) - if ns.zip: - log_debug("Zip {} into {}", c, ns.zip) - zip_file.write(c, str(cdest)) - in_catalog.append((cdest.name, cdest)) - - if ns.catalog: - # Just write out the CDF now. Compilation and signing is - # an extra step - log_info("Generating {}", ns.catalog) - ns.catalog.parent.mkdir(parents=True, exist_ok=True) - write_catalog(ns.catalog, in_catalog) - - finally: - if zip_file: - zip_file.close() - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("-v", help="Increase verbosity", action="count") - parser.add_argument( - "-s", - "--source", - metavar="dir", - help="The directory containing the repository root", - type=Path, - default=None, - ) - parser.add_argument( - "-b", "--build", metavar="dir", help="Specify the build directory", type=Path - ) - parser.add_argument( - "--doc-build", - metavar="dir", - help="Specify the docs build directory", - type=Path, - default=None, - ) - parser.add_argument( - "--copy", - metavar="directory", - help="The name of the directory to copy an extracted layout to", - type=Path, - default=None, - ) - parser.add_argument( - "--zip", - metavar="file", - help="The ZIP file to write all files to", - type=Path, - default=None, - ) - parser.add_argument( - "--catalog", - metavar="file", - help="The CDF file to write catalog entries to", - type=Path, - default=None, - ) - parser.add_argument( - "--log", - metavar="file", - help="Write all operations to the specified file", - type=Path, - default=None, - ) - parser.add_argument( - "-t", - "--temp", - metavar="file", - help="A temporary working directory", - type=Path, - default=None, - ) - parser.add_argument( - "-d", "--debug", help="Include debug build", action="store_true" - ) - parser.add_argument( - "-p", - "--precompile", - help="Include .pyc files instead of .py", - action="store_true", - ) - parser.add_argument( - "-z", "--zip-lib", help="Include library in a ZIP file", action="store_true" - ) - parser.add_argument( - "--flat-dlls", help="Does not create a DLLs directory", action="store_true" - ) - parser.add_argument( - "-a", - "--include-all", - help="Include all optional components", - action="store_true", - ) - parser.add_argument( - "--include-cat", - metavar="file", - help="Specify the catalog file to include", - type=Path, - default=None, - ) - for opt, help in get_argparse_options(): - parser.add_argument(opt, help=help, action="store_true") - - ns = parser.parse_args() - update_presets(ns) - - ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent) - ns.build = ns.build or Path(sys.executable).parent - ns.temp = ns.temp or Path(tempfile.mkdtemp()) - ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build") - if not ns.source.is_absolute(): - ns.source = (Path.cwd() / ns.source).resolve() - if not ns.build.is_absolute(): - ns.build = (Path.cwd() / ns.build).resolve() - if not ns.temp.is_absolute(): - ns.temp = (Path.cwd() / ns.temp).resolve() - if not ns.doc_build.is_absolute(): - ns.doc_build = (Path.cwd() / ns.doc_build).resolve() - if ns.include_cat and not ns.include_cat.is_absolute(): - ns.include_cat = (Path.cwd() / ns.include_cat).resolve() - - if ns.copy and not ns.copy.is_absolute(): - ns.copy = (Path.cwd() / ns.copy).resolve() - if ns.zip and not ns.zip.is_absolute(): - ns.zip = (Path.cwd() / ns.zip).resolve() - if ns.catalog and not ns.catalog.is_absolute(): - ns.catalog = (Path.cwd() / ns.catalog).resolve() - - configure_logger(ns) - - log_info( - """OPTIONS -Source: {ns.source} -Build: {ns.build} -Temp: {ns.temp} - -Copy to: {ns.copy} -Zip to: {ns.zip} -Catalog: {ns.catalog}""", - ns=ns, - ) - - if ns.include_idle and not ns.include_tcltk: - log_warning("Assuming --include-tcltk to support --include-idle") - ns.include_tcltk = True - - try: - generate_source_files(ns) - files = list(get_layout(ns)) - copy_files(files, ns) - except KeyboardInterrupt: - log_info("Interrupted by Ctrl+C") - return 3 - except SystemExit: - raise - except: - log_exception("Unhandled error") - - if error_was_logged(): - log_error("Errors occurred.") - return 1 - - -if __name__ == "__main__": - sys.exit(int(main() or 0)) diff --git a/PC/layout/support/__init__.py b/PC/layout/support/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py deleted file mode 100644 index c5dda70c7ef8..000000000000 --- a/PC/layout/support/appxmanifest.py +++ /dev/null @@ -1,487 +0,0 @@ -""" -File generation for APPX/MSIX manifests. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - - -import collections -import ctypes -import io -import os -import sys - -from pathlib import Path, PureWindowsPath -from xml.etree import ElementTree as ET - -from .constants import * - -__all__ = [] - - -def public(f): - __all__.append(f.__name__) - return f - - -APPX_DATA = dict( - Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT), - Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3), - Publisher=os.getenv( - "APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B" - ), - DisplayName="Python {}".format(VER_DOT), - Description="The Python {} runtime and console.".format(VER_DOT), - ProcessorArchitecture="x64" if IS_X64 else "x86", -) - -PYTHON_VE_DATA = dict( - DisplayName="Python {}".format(VER_DOT), - Description="Python interactive console", - Square150x150Logo="_resources/pythonx150.png", - Square44x44Logo="_resources/pythonx44.png", - BackgroundColor="transparent", -) - -PYTHONW_VE_DATA = dict( - DisplayName="Python {} (Windowed)".format(VER_DOT), - Description="Python windowed app launcher", - Square150x150Logo="_resources/pythonwx150.png", - Square44x44Logo="_resources/pythonwx44.png", - BackgroundColor="transparent", - AppListEntry="none", -) - -PIP_VE_DATA = dict( - DisplayName="pip (Python {})".format(VER_DOT), - Description="pip package manager for Python {}".format(VER_DOT), - Square150x150Logo="_resources/pythonx150.png", - Square44x44Logo="_resources/pythonx44.png", - BackgroundColor="transparent", - AppListEntry="none", -) - -IDLE_VE_DATA = dict( - DisplayName="IDLE (Python {})".format(VER_DOT), - Description="IDLE editor for Python {}".format(VER_DOT), - Square150x150Logo="_resources/pythonwx150.png", - Square44x44Logo="_resources/pythonwx44.png", - BackgroundColor="transparent", -) - -APPXMANIFEST_NS = { - "": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", - "m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", - "uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10", - "rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities", - "rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4", - "desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4", - "desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6", - "uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3", - "uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4", - "uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5", -} - -APPXMANIFEST_TEMPLATE = """ - - - - - Python Software Foundation - - _resources/pythonx50.png - - - - - - - - - - - - - - -""" - - -RESOURCES_XML_TEMPLATE = r""" - - - - - - - - - - - - - - - - - - - - - - - - - - - -""" - - -SCCD_FILENAME = "PC/classicAppCompat.sccd" - -REGISTRY = { - "HKCU\\Software\\Python\\PythonCore": { - VER_DOT: { - "DisplayName": APPX_DATA["DisplayName"], - "SupportUrl": "https://www.python.org/", - "SysArchitecture": "64bit" if IS_X64 else "32bit", - "SysVersion": VER_DOT, - "Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO), - "InstallPath": { - # I have no idea why the trailing spaces are needed, but they seem to be needed. - "": "[{AppVPackageRoot}][ ]", - "ExecutablePath": "[{AppVPackageRoot}]python.exe[ ]", - "WindowedExecutablePath": "[{AppVPackageRoot}]pythonw.exe[ ]", - }, - "Help": { - "Main Python Documentation": { - "_condition": lambda ns: ns.include_chm, - "": "[{{AppVPackageRoot}}]Doc\\{}[ ]".format( - PYTHON_CHM_NAME - ), - }, - "Local Python Documentation": { - "_condition": lambda ns: ns.include_html_doc, - "": "[{AppVPackageRoot}]Doc\\html\\index.html[ ]", - }, - "Online Python Documentation": { - "": "https://docs.python.org/{}".format(VER_DOT) - }, - }, - "Idle": { - "_condition": lambda ns: ns.include_idle, - "": "[{AppVPackageRoot}]Lib\\idlelib\\idle.pyw[ ]", - }, - } - } -} - - -def get_packagefamilyname(name, publisher_id): - class PACKAGE_ID(ctypes.Structure): - _fields_ = [ - ("reserved", ctypes.c_uint32), - ("processorArchitecture", ctypes.c_uint32), - ("version", ctypes.c_uint64), - ("name", ctypes.c_wchar_p), - ("publisher", ctypes.c_wchar_p), - ("resourceId", ctypes.c_wchar_p), - ("publisherId", ctypes.c_wchar_p), - ] - _pack_ = 4 - - pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None) - result = ctypes.create_unicode_buffer(256) - result_len = ctypes.c_uint32(256) - r = ctypes.windll.kernel32.PackageFamilyNameFromId( - pid, ctypes.byref(result_len), result - ) - if r: - raise OSError(r, "failed to get package family name") - return result.value[: result_len.value] - - -def _fixup_sccd(ns, sccd, new_hash=None): - if not new_hash: - return sccd - - NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd") - with open(sccd, "rb") as f: - xml = ET.parse(f) - - pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"]) - - ae = xml.find("s:AuthorizedEntities", NS) - ae.clear() - - e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity")) - e.set("AppPackageFamilyName", pfn) - e.set("CertificateSignatureHash", new_hash) - - for e in xml.findall("s:Catalog", NS): - e.text = "FFFF" - - sccd = ns.temp / sccd.name - sccd.parent.mkdir(parents=True, exist_ok=True) - with open(sccd, "wb") as f: - xml.write(f, encoding="utf-8") - - return sccd - - - at public -def get_appx_layout(ns): - if not ns.include_appxmanifest: - return - - yield "AppxManifest.xml", ns.temp / "AppxManifest.xml" - yield "_resources.xml", ns.temp / "_resources.xml" - icons = ns.source / "PC" / "icons" - yield "_resources/pythonx44.png", icons / "pythonx44.png" - yield "_resources/pythonx44$targetsize-44_altform-unplated.png", icons / "pythonx44.png" - yield "_resources/pythonx50.png", icons / "pythonx50.png" - yield "_resources/pythonx50$targetsize-50_altform-unplated.png", icons / "pythonx50.png" - yield "_resources/pythonx150.png", icons / "pythonx150.png" - yield "_resources/pythonx150$targetsize-150_altform-unplated.png", icons / "pythonx150.png" - yield "_resources/pythonwx44.png", icons / "pythonwx44.png" - yield "_resources/pythonwx44$targetsize-44_altform-unplated.png", icons / "pythonwx44.png" - yield "_resources/pythonwx150.png", icons / "pythonwx150.png" - yield "_resources/pythonwx150$targetsize-150_altform-unplated.png", icons / "pythonwx150.png" - sccd = ns.source / SCCD_FILENAME - if sccd.is_file(): - # This should only be set for side-loading purposes. - sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256")) - yield sccd.name, sccd - - -def find_or_add(xml, element, attr=None, always_add=False): - if always_add: - e = None - else: - q = element - if attr: - q += "[@{}='{}']".format(*attr) - e = xml.find(q, APPXMANIFEST_NS) - if e is None: - prefix, _, name = element.partition(":") - name = ET.QName(APPXMANIFEST_NS[prefix or ""], name) - e = ET.SubElement(xml, name) - if attr: - e.set(*attr) - return e - - -def _get_app(xml, appid): - if appid: - app = xml.find( - "m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS - ) - if app is None: - raise LookupError(appid) - else: - app = xml - return app - - -def add_visual(xml, appid, data): - app = _get_app(xml, appid) - e = find_or_add(app, "uap:VisualElements") - for i in data.items(): - e.set(*i) - return e - - -def add_alias(xml, appid, alias, subsystem="windows"): - app = _get_app(xml, appid) - e = find_or_add(app, "m:Extensions") - e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias")) - e = find_or_add(e, "uap5:AppExecutionAlias") - e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem) - e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias)) - - -def add_file_type(xml, appid, name, suffix, parameters='"%1"'): - app = _get_app(xml, appid) - e = find_or_add(app, "m:Extensions") - e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation")) - e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name)) - e.set("Parameters", parameters) - e = find_or_add(e, "uap:SupportedFileTypes") - if isinstance(suffix, str): - suffix = [suffix] - for s in suffix: - ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s - - -def add_application( - ns, xml, appid, executable, aliases, visual_element, subsystem, file_types -): - node = xml.find("m:Applications", APPXMANIFEST_NS) - suffix = "_d.exe" if ns.debug else ".exe" - app = ET.SubElement( - node, - ET.QName(APPXMANIFEST_NS[""], "Application"), - { - "Id": appid, - "Executable": executable + suffix, - "EntryPoint": "Windows.FullTrustApplication", - ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true", - }, - ) - if visual_element: - add_visual(app, None, visual_element) - for alias in aliases: - add_alias(app, None, alias + suffix, subsystem) - if file_types: - add_file_type(app, None, *file_types) - return app - - -def _get_registry_entries(ns, root="", d=None): - r = root if root else PureWindowsPath("") - if d is None: - d = REGISTRY - for key, value in d.items(): - if key == "_condition": - continue - elif isinstance(value, dict): - cond = value.get("_condition") - if cond and not cond(ns): - continue - fullkey = r - for part in PureWindowsPath(key).parts: - fullkey /= part - if len(fullkey.parts) > 1: - yield str(fullkey), None, None - yield from _get_registry_entries(ns, fullkey, value) - elif len(r.parts) > 1: - yield str(r), key, value - - -def add_registry_entries(ns, xml): - e = find_or_add(xml, "m:Extensions") - e = find_or_add(e, "rescap4:Extension") - e.set("Category", "windows.classicAppCompatKeys") - e.set("EntryPoint", "Windows.FullTrustApplication") - e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys")) - for name, valuename, value in _get_registry_entries(ns): - k = ET.SubElement( - e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey") - ) - k.set("Name", name) - if value: - k.set("ValueName", valuename) - k.set("Value", value) - k.set("ValueType", "REG_SZ") - - -def disable_registry_virtualization(xml): - e = find_or_add(xml, "m:Properties") - e = find_or_add(e, "desktop6:RegistryWriteVirtualization") - e.text = "disabled" - e = find_or_add(xml, "m:Capabilities") - e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources")) - - - at public -def get_appxmanifest(ns): - for k, v in APPXMANIFEST_NS.items(): - ET.register_namespace(k, v) - ET.register_namespace("", APPXMANIFEST_NS["m"]) - - xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE)) - NS = APPXMANIFEST_NS - QN = ET.QName - - node = xml.find("m:Identity", NS) - for k in node.keys(): - value = APPX_DATA.get(k) - if value: - node.set(k, value) - - for node in xml.find("m:Properties", NS): - value = APPX_DATA.get(node.tag.rpartition("}")[2]) - if value: - node.text = value - - winver = sys.getwindowsversion()[:3] - if winver < (10, 0, 17763): - winver = 10, 0, 17763 - find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set( - "MaxVersionTested", "{}.{}.{}.0".format(*winver) - ) - - if winver > (10, 0, 17763): - disable_registry_virtualization(xml) - - app = add_application( - ns, - xml, - "Python", - "python", - ["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)], - PYTHON_VE_DATA, - "console", - ("python.file", [".py"]), - ) - - add_application( - ns, - xml, - "PythonW", - "pythonw", - ["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)], - PYTHONW_VE_DATA, - "windows", - ("python.windowedfile", [".pyw"]), - ) - - if ns.include_pip and ns.include_launchers: - add_application( - ns, - xml, - "Pip", - "pip", - ["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)], - PIP_VE_DATA, - "console", - ("python.wheel", [".whl"], 'install "%1"'), - ) - - if ns.include_idle and ns.include_launchers: - add_application( - ns, - xml, - "Idle", - "idle", - ["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)], - IDLE_VE_DATA, - "windows", - None, - ) - - if (ns.source / SCCD_FILENAME).is_file(): - add_registry_entries(ns, xml) - node = xml.find("m:Capabilities", NS) - node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability")) - node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe") - - buffer = io.BytesIO() - xml.write(buffer, encoding="utf-8", xml_declaration=True) - return buffer.getbuffer() - - - at public -def get_resources_xml(ns): - return RESOURCES_XML_TEMPLATE.encode("utf-8") diff --git a/PC/layout/support/catalog.py b/PC/layout/support/catalog.py deleted file mode 100644 index 43121187ed18..000000000000 --- a/PC/layout/support/catalog.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -File generation for catalog signing non-binary contents. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - - -import sys - -__all__ = ["PYTHON_CAT_NAME", "PYTHON_CDF_NAME"] - - -def public(f): - __all__.append(f.__name__) - return f - - -PYTHON_CAT_NAME = "python.cat" -PYTHON_CDF_NAME = "python.cdf" - - -CATALOG_TEMPLATE = r"""[CatalogHeader] -Name={target.stem}.cat -ResultDir={target.parent} -PublicVersion=1 -CatalogVersion=2 -HashAlgorithms=SHA256 -PageHashes=false -EncodingType= - -[CatalogFiles] -""" - - -def can_sign(file): - return file.is_file() and file.stat().st_size - - - at public -def write_catalog(target, files): - with target.open("w", encoding="utf-8") as cat: - cat.write(CATALOG_TEMPLATE.format(target=target)) - cat.writelines("{}={}\n".format(n, f) for n, f in files if can_sign(f)) diff --git a/PC/layout/support/constants.py b/PC/layout/support/constants.py deleted file mode 100644 index 88ea410b340e..000000000000 --- a/PC/layout/support/constants.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Constants for generating the layout. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - -import struct -import sys - -VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = struct.pack(">i", sys.hexversion) -VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4 -VER_NAME = {"alpha": "a", "beta": "b", "rc": "rc"}.get( - sys.version_info.releaselevel, "" -) -VER_SERIAL = sys.version_info.serial if VER_NAME else "" -VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR) - -PYTHON_DLL_NAME = "python{}{}.dll".format(VER_MAJOR, VER_MINOR) -PYTHON_STABLE_DLL_NAME = "python{}.dll".format(VER_MAJOR) -PYTHON_ZIP_NAME = "python{}{}.zip".format(VER_MAJOR, VER_MINOR) -PYTHON_PTH_NAME = "python{}{}._pth".format(VER_MAJOR, VER_MINOR) - -PYTHON_CHM_NAME = "python{}{}{}{}{}.chm".format( - VER_MAJOR, VER_MINOR, VER_MICRO, VER_NAME, VER_SERIAL -) - -IS_X64 = sys.maxsize > 2 ** 32 diff --git a/PC/layout/support/distutils.command.bdist_wininst.py b/PC/layout/support/distutils.command.bdist_wininst.py deleted file mode 100644 index 6e9b49fe42df..000000000000 --- a/PC/layout/support/distutils.command.bdist_wininst.py +++ /dev/null @@ -1,25 +0,0 @@ -"""distutils.command.bdist_wininst - -Suppress the 'bdist_wininst' command, while still allowing -setuptools to import it without breaking.""" - -from distutils.core import Command -from distutils.errors import DistutilsPlatformError - - -class bdist_wininst(Command): - description = "create an executable installer for MS Windows" - - # Marker for tests that we have the unsupported bdist_wininst - _unsupported = True - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - raise DistutilsPlatformError( - "bdist_wininst is not supported in this Python distribution" - ) diff --git a/PC/layout/support/filesets.py b/PC/layout/support/filesets.py deleted file mode 100644 index 47f727c05784..000000000000 --- a/PC/layout/support/filesets.py +++ /dev/null @@ -1,100 +0,0 @@ -""" -File sets and globbing helper for make_layout. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - -import os - - -class FileStemSet: - def __init__(self, *patterns): - self._names = set() - self._prefixes = [] - self._suffixes = [] - for p in map(os.path.normcase, patterns): - if p.endswith("*"): - self._prefixes.append(p[:-1]) - elif p.startswith("*"): - self._suffixes.append(p[1:]) - else: - self._names.add(p) - - def _make_name(self, f): - return os.path.normcase(f.stem) - - def __contains__(self, f): - bn = self._make_name(f) - return ( - bn in self._names - or any(map(bn.startswith, self._prefixes)) - or any(map(bn.endswith, self._suffixes)) - ) - - -class FileNameSet(FileStemSet): - def _make_name(self, f): - return os.path.normcase(f.name) - - -class FileSuffixSet: - def __init__(self, *patterns): - self._names = set() - self._prefixes = [] - self._suffixes = [] - for p in map(os.path.normcase, patterns): - if p.startswith("*."): - self._names.add(p[1:]) - elif p.startswith("*"): - self._suffixes.append(p[1:]) - elif p.endswith("*"): - self._prefixes.append(p[:-1]) - elif p.startswith("."): - self._names.add(p) - else: - self._names.add("." + p) - - def _make_name(self, f): - return os.path.normcase(f.suffix) - - def __contains__(self, f): - bn = self._make_name(f) - return ( - bn in self._names - or any(map(bn.startswith, self._prefixes)) - or any(map(bn.endswith, self._suffixes)) - ) - - -def _rglob(root, pattern, condition): - dirs = [root] - recurse = pattern[:3] in {"**/", "**\\"} - if recurse: - pattern = pattern[3:] - - while dirs: - d = dirs.pop(0) - if recurse: - dirs.extend( - filter( - condition, (type(root)(f2) for f2 in os.scandir(d) if f2.is_dir()) - ) - ) - yield from ( - (f.relative_to(root), f) - for f in d.glob(pattern) - if f.is_file() and condition(f) - ) - - -def _return_true(f): - return True - - -def rglob(root, patterns, condition=None): - if isinstance(patterns, tuple): - for p in patterns: - yield from _rglob(root, p, condition or _return_true) - else: - yield from _rglob(root, patterns, condition or _return_true) diff --git a/PC/layout/support/logging.py b/PC/layout/support/logging.py deleted file mode 100644 index 30869b949a1c..000000000000 --- a/PC/layout/support/logging.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -Logging support for make_layout. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - -import logging -import sys - -__all__ = [] - -LOG = None -HAS_ERROR = False - - -def public(f): - __all__.append(f.__name__) - return f - - - at public -def configure_logger(ns): - global LOG - if LOG: - return - - LOG = logging.getLogger("make_layout") - LOG.level = logging.DEBUG - - if ns.v: - s_level = max(logging.ERROR - ns.v * 10, logging.DEBUG) - f_level = max(logging.WARNING - ns.v * 10, logging.DEBUG) - else: - s_level = logging.ERROR - f_level = logging.INFO - - handler = logging.StreamHandler(sys.stdout) - handler.setFormatter(logging.Formatter("{levelname:8s} {message}", style="{")) - handler.setLevel(s_level) - LOG.addHandler(handler) - - if ns.log: - handler = logging.FileHandler(ns.log, encoding="utf-8", delay=True) - handler.setFormatter( - logging.Formatter("[{asctime}]{levelname:8s}: {message}", style="{") - ) - handler.setLevel(f_level) - LOG.addHandler(handler) - - -class BraceMessage: - def __init__(self, fmt, *args, **kwargs): - self.fmt = fmt - self.args = args - self.kwargs = kwargs - - def __str__(self): - return self.fmt.format(*self.args, **self.kwargs) - - - at public -def log_debug(msg, *args, **kwargs): - return LOG.debug(BraceMessage(msg, *args, **kwargs)) - - - at public -def log_info(msg, *args, **kwargs): - return LOG.info(BraceMessage(msg, *args, **kwargs)) - - - at public -def log_warning(msg, *args, **kwargs): - return LOG.warning(BraceMessage(msg, *args, **kwargs)) - - - at public -def log_error(msg, *args, **kwargs): - global HAS_ERROR - HAS_ERROR = True - return LOG.error(BraceMessage(msg, *args, **kwargs)) - - - at public -def log_exception(msg, *args, **kwargs): - global HAS_ERROR - HAS_ERROR = True - return LOG.exception(BraceMessage(msg, *args, **kwargs)) - - - at public -def error_was_logged(): - return HAS_ERROR diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py deleted file mode 100644 index 76d9e34e1f46..000000000000 --- a/PC/layout/support/options.py +++ /dev/null @@ -1,122 +0,0 @@ -""" -List of optional components. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - - -__all__ = [] - - -def public(f): - __all__.append(f.__name__) - return f - - -OPTIONS = { - "stable": {"help": "stable ABI stub"}, - "pip": {"help": "pip"}, - "distutils": {"help": "distutils"}, - "tcltk": {"help": "Tcl, Tk and tkinter"}, - "idle": {"help": "Idle"}, - "tests": {"help": "test suite"}, - "tools": {"help": "tools"}, - "venv": {"help": "venv"}, - "dev": {"help": "headers and libs"}, - "symbols": {"help": "symbols"}, - "bdist-wininst": {"help": "bdist_wininst support"}, - "underpth": {"help": "a python._pth file", "not-in-all": True}, - "launchers": {"help": "specific launchers"}, - "appxmanifest": {"help": "an appxmanifest"}, - "props": {"help": "a python.props file"}, - "chm": {"help": "the CHM documentation"}, - "html-doc": {"help": "the HTML documentation"}, -} - - -PRESETS = { - "appx": { - "help": "APPX package", - "options": [ - "stable", - "pip", - "distutils", - "tcltk", - "idle", - "venv", - "dev", - "launchers", - "appxmanifest", - # XXX: Disabled for now "precompile", - ], - }, - "nuget": { - "help": "nuget package", - "options": ["stable", "pip", "distutils", "dev", "props"], - }, - "default": { - "help": "development kit package", - "options": [ - "stable", - "pip", - "distutils", - "tcltk", - "idle", - "tests", - "tools", - "venv", - "dev", - "symbols", - "bdist-wininst", - "chm", - ], - }, - "embed": { - "help": "embeddable package", - "options": ["stable", "zip-lib", "flat-dlls", "underpth", "precompile"], - }, -} - - - at public -def get_argparse_options(): - for opt, info in OPTIONS.items(): - help = "When specified, includes {}".format(info["help"]) - if info.get("not-in-all"): - help = "{}. Not affected by --include-all".format(help) - - yield "--include-{}".format(opt), help - - for opt, info in PRESETS.items(): - help = "When specified, includes default options for {}".format(info["help"]) - yield "--preset-{}".format(opt), help - - -def ns_get(ns, key, default=False): - return getattr(ns, key.replace("-", "_"), default) - - -def ns_set(ns, key, value=True): - k1 = key.replace("-", "_") - k2 = "include_{}".format(k1) - if hasattr(ns, k2): - setattr(ns, k2, value) - elif hasattr(ns, k1): - setattr(ns, k1, value) - else: - raise AttributeError("no argument named '{}'".format(k1)) - - - at public -def update_presets(ns): - for preset, info in PRESETS.items(): - if ns_get(ns, "preset-{}".format(preset)): - for opt in info["options"]: - ns_set(ns, opt) - - if ns.include_all: - for opt in OPTIONS: - if OPTIONS[opt].get("not-in-all"): - continue - ns_set(ns, opt) diff --git a/PC/layout/support/pip.py b/PC/layout/support/pip.py deleted file mode 100644 index 369a923ce139..000000000000 --- a/PC/layout/support/pip.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Extraction and file list generation for pip. -""" - -__author__ = "Steve Dower " -__version__ = "3.8" - - -import os -import shutil -import subprocess -import sys - -__all__ = [] - - -def public(f): - __all__.append(f.__name__) - return f - - - at public -def get_pip_dir(ns): - if ns.copy: - if ns.zip_lib: - return ns.copy / "packages" - return ns.copy / "Lib" / "site-packages" - else: - return ns.temp / "packages" - - - at public -def extract_pip_files(ns): - dest = get_pip_dir(ns) - dest.mkdir(parents=True, exist_ok=True) - - src = ns.source / "Lib" / "ensurepip" / "_bundled" - - ns.temp.mkdir(parents=True, exist_ok=True) - wheels = [shutil.copy(whl, ns.temp) for whl in src.glob("*.whl")] - search_path = os.pathsep.join(wheels) - if os.environ.get("PYTHONPATH"): - search_path += ";" + os.environ["PYTHONPATH"] - - env = os.environ.copy() - env["PYTHONPATH"] = search_path - - output = subprocess.check_output( - [ - sys.executable, - "-m", - "pip", - "--no-color", - "install", - "pip", - "setuptools", - "--upgrade", - "--target", - str(dest), - "--no-index", - "--no-cache-dir", - "-f", - str(src), - "--only-binary", - ":all:", - ], - env=env, - ) - - try: - shutil.rmtree(dest / "bin") - except OSError: - pass - - for file in wheels: - try: - os.remove(file) - except OSError: - pass diff --git a/PC/layout/support/props.py b/PC/layout/support/props.py deleted file mode 100644 index 3a047d215058..000000000000 --- a/PC/layout/support/props.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -Provides .props file. -""" - -import os - -from .constants import * - -__all__ = ["PYTHON_PROPS_NAME"] - - -def public(f): - __all__.append(f.__name__) - return f - - -PYTHON_PROPS_NAME = "python.props" - -PROPS_DATA = { - "PYTHON_TAG": VER_DOT, - "PYTHON_VERSION": os.getenv("PYTHON_NUSPEC_VERSION"), - "PYTHON_PLATFORM": os.getenv("PYTHON_PROPS_PLATFORM"), - "PYTHON_TARGET": "", -} - -if not PROPS_DATA["PYTHON_VERSION"]: - if VER_NAME: - PROPS_DATA["PYTHON_VERSION"] = "{}.{}-{}{}".format( - VER_DOT, VER_MICRO, VER_NAME, VER_SERIAL - ) - else: - PROPS_DATA["PYTHON_VERSION"] = "{}.{}".format(VER_DOT, VER_MICRO) - -if not PROPS_DATA["PYTHON_PLATFORM"]: - PROPS_DATA["PYTHON_PLATFORM"] = "x64" if IS_X64 else "Win32" - -PROPS_DATA["PYTHON_TARGET"] = "_GetPythonRuntimeFilesDependsOn{}{}_{}".format( - VER_MAJOR, VER_MINOR, PROPS_DATA["PYTHON_PLATFORM"] -) - -PROPS_TEMPLATE = r""" - - - $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python_d.exe") - $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python.exe") - $(PythonHome)\include - $(PythonHome)\libs - {PYTHON_TAG} - {PYTHON_VERSION} - - true - false - false - false - - {PYTHON_TARGET};$(GetPythonRuntimeFilesDependsOn) - - - - - $(PythonInclude);%(AdditionalIncludeDirectories) - MultiThreadedDLL - - - $(PythonLibs);%(AdditionalLibraryDirectories) - - - - - - - - <_PythonRuntimeExe Include="$(PythonHome)\python*.dll" /> - <_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" /> - <_PythonRuntimeExe> - %(Filename)%(Extension) - - <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" /> - <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" /> - <_PythonRuntimeDlls> - DLLs\%(Filename)%(Extension) - - <_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" /> - <_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" /> - <_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" /> - <_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" /> - <_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" /> - <_PythonRuntimeLib> - Lib\%(RecursiveDir)%(Filename)%(Extension) - - - - - - - -""" - - - at public -def get_props_layout(ns): - if ns.include_all or ns.include_props: - yield "python.props", ns.temp / "python.props" - - - at public -def get_props(ns): - # TODO: Filter contents of props file according to included/excluded items - props = PROPS_TEMPLATE.format_map(PROPS_DATA) - return props.encode("utf-8") diff --git a/PC/pylauncher.rc b/PC/pylauncher.rc index 92987af7138d..3da3445f5fc4 100644 --- a/PC/pylauncher.rc +++ b/PC/pylauncher.rc @@ -7,11 +7,6 @@ #include 1 RT_MANIFEST "python.manifest" -#if defined(PY_ICON) -1 ICON DISCARDABLE "icons\python.ico" -#elif defined(PYW_ICON) -1 ICON DISCARDABLE "icons\pythonw.ico" -#else 1 ICON DISCARDABLE "icons\launcher.ico" 2 ICON DISCARDABLE "icons\py.ico" 3 ICON DISCARDABLE "icons\pyc.ico" @@ -19,7 +14,6 @@ 5 ICON DISCARDABLE "icons\python.ico" 6 ICON DISCARDABLE "icons\pythonw.ico" 7 ICON DISCARDABLE "icons\setup.ico" -#endif ///////////////////////////////////////////////////////////////////////////// // diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp deleted file mode 100644 index 1658d05994bb..000000000000 --- a/PC/python_uwp.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* Main program when embedded in a UWP application on Windows */ - -#include "Python.h" -#include - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include - -#ifdef PYTHONW -#ifdef _DEBUG -const wchar_t *PROGNAME = L"pythonw_d.exe"; -#else -const wchar_t *PROGNAME = L"pythonw.exe"; -#endif -#else -#ifdef _DEBUG -const wchar_t *PROGNAME = L"python_d.exe"; -#else -const wchar_t *PROGNAME = L"python.exe"; -#endif -#endif - -static void -set_user_base() -{ - wchar_t envBuffer[2048]; - try { - const auto appData = winrt::Windows::Storage::ApplicationData::Current(); - if (appData) { - const auto localCache = appData.LocalCacheFolder(); - if (localCache) { - auto path = localCache.Path(); - if (!path.empty() && - !wcscpy_s(envBuffer, path.c_str()) && - !wcscat_s(envBuffer, L"\\local-packages") - ) { - _wputenv_s(L"PYTHONUSERBASE", envBuffer); - } - } - } - } catch (...) { - } -} - -static const wchar_t * -get_argv0(const wchar_t *argv0) -{ - winrt::hstring installPath; - const wchar_t *launcherPath; - wchar_t *buffer; - size_t len; - - launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__"); - if (launcherPath && launcherPath[0]) { - len = wcslen(launcherPath) + 1; - buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); - if (!buffer) { - Py_FatalError("out of memory"); - return NULL; - } - if (wcscpy_s(buffer, len, launcherPath)) { - Py_FatalError("failed to copy to buffer"); - return NULL; - } - return buffer; - } - - try { - const auto package = winrt::Windows::ApplicationModel::Package::Current(); - if (package) { - const auto install = package.InstalledLocation(); - if (install) { - installPath = install.Path(); - } - } - } - catch (...) { - } - - if (!installPath.empty()) { - len = installPath.size() + wcslen(PROGNAME) + 2; - } else { - len = wcslen(argv0) + wcslen(PROGNAME) + 1; - } - - buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); - if (!buffer) { - Py_FatalError("out of memory"); - return NULL; - } - - if (!installPath.empty()) { - if (wcscpy_s(buffer, len, installPath.c_str())) { - Py_FatalError("failed to copy to buffer"); - return NULL; - } - if (wcscat_s(buffer, len, L"\\")) { - Py_FatalError("failed to concatenate backslash"); - return NULL; - } - } else { - if (wcscpy_s(buffer, len, argv0)) { - Py_FatalError("failed to copy argv[0]"); - return NULL; - } - - wchar_t *name = wcsrchr(buffer, L'\\'); - if (name) { - name[1] = L'\0'; - } else { - buffer[0] = L'\0'; - } - } - - if (wcscat_s(buffer, len, PROGNAME)) { - Py_FatalError("failed to concatenate program name"); - return NULL; - } - - return buffer; -} - -static wchar_t * -get_process_name() -{ - DWORD bufferLen = MAX_PATH; - DWORD len = bufferLen; - wchar_t *r = NULL; - - while (!r) { - r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); - if (!r) { - Py_FatalError("out of memory"); - return NULL; - } - len = GetModuleFileNameW(NULL, r, bufferLen); - if (len == 0) { - free((void *)r); - return NULL; - } else if (len == bufferLen && - GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - free(r); - r = NULL; - bufferLen *= 2; - } - } - - return r; -} - -int -wmain(int argc, wchar_t **argv) -{ - const wchar_t **new_argv; - int new_argc; - const wchar_t *exeName; - - new_argc = argc; - new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2)); - if (new_argv == NULL) { - Py_FatalError("out of memory"); - return -1; - } - - exeName = get_process_name(); - - new_argv[0] = get_argv0(exeName ? exeName : argv[0]); - for (int i = 1; i < argc; ++i) { - new_argv[i] = argv[i]; - } - - set_user_base(); - - if (exeName) { - const wchar_t *p = wcsrchr(exeName, L'\\'); - if (p) { - const wchar_t *moduleName = NULL; - if (*p++ == L'\\') { - if (wcsnicmp(p, L"pip", 3) == 0) { - moduleName = L"pip"; - _wputenv_s(L"PIP_USER", L"true"); - } - else if (wcsnicmp(p, L"idle", 4) == 0) { - moduleName = L"idlelib"; - } - } - - if (moduleName) { - new_argc += 2; - for (int i = argc; i >= 1; --i) { - new_argv[i + 2] = new_argv[i]; - } - new_argv[1] = L"-m"; - new_argv[2] = moduleName; - } - } - } - - /* Override program_full_path from here so that - sys.executable is set correctly. */ - _Py_SetProgramFullPath(new_argv[0]); - - int result = Py_Main(new_argc, (wchar_t **)new_argv); - - free((void *)exeName); - free((void *)new_argv); - - return result; -} - -#ifdef PYTHONW - -int WINAPI wWinMain( - HINSTANCE hInstance, /* handle to current instance */ - HINSTANCE hPrevInstance, /* handle to previous instance */ - LPWSTR lpCmdLine, /* pointer to command line */ - int nCmdShow /* show state of window */ -) -{ - return wmain(__argc, __wargv); -} - -#endif diff --git a/PC/store_info.txt b/PC/store_info.txt deleted file mode 100644 index e252692b73cc..000000000000 --- a/PC/store_info.txt +++ /dev/null @@ -1,146 +0,0 @@ -# Overview - -NOTE: This file requires more content. - -Since Python 3.7.2, releases have been made through the Microsoft Store -to allow easy installation on Windows 10.0.17763.0 and later. - -# Building - -To build the store package, the PC/layout script should be used. -Execute the directory with the build of Python to package, and pass -"-h" for full command-line options. - -To sideload test builds, you will need a local certificate. -Instructions are available at -https://docs.microsoft.com/windows/uwp/packaging/create-certificate-package-signing. - -After exporting your certificate, you will need the subject name and -SHA256 hash. The `certutil -dump ` command will display this -information. - -To build for sideloading, use these commands in PowerShell: - -``` -$env:APPX_DATA_PUBLISHER= -$env:APPX_DATA_SHA256= -$env:SigningCertificateFile= - -python PC/layout --copy --include-appxmanifest -Tools/msi/make_appx.ps1 python.msix -sign - -Add-AppxPackage python.msix -``` - -(Note that only the last command requires PowerShell, and the others -can be used from Command Prompt. You can also double-click to install -the final package.) - -To build for publishing to the Store, use these commands: - -``` -$env:APPX_DATA_PUBLISHER = $null -$env:APPX_DATA_SHA256 = $null - -python PC/layout --copy --preset-appxmanifest --precompile -Tools/msi/make_appx.ps1 python.msix -``` - -Note that this package cannot be installed locally. It may only be -added to a submission for the store. - - -# Submission Metadata - -This file contains the text that we use to fill out the store listing -for the Microsoft Store. It needs to be entered manually when creating -a new submission via the dashboard at -https://partner.microsoft.com/dashboard. - -We keep it here for convenience and to allow it to be updated via pull -requests. - -## Title - -Python 3.7 - -## Short Title - -Python - -## Description - -Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python?s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms. - -The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python Web site, https://www.python.org/, and may be freely distributed. The same site also contains distributions of and pointers to many free third party Python modules, programs and tools, and additional documentation. - -The Python interpreter is easily extended with new functions and data types implemented in C or C++ (or other languages callable from C). Python is also suitable as an extension language for customizable applications. - -## ShortDescription - -The Python 3.7 interpreter and runtime. - -## Copyright Trademark Information - -(c) Python Software Foundation - -## Additional License Terms - -Visit https://docs.python.org/3.7/license.html for latest license terms. - -PSF LICENSE AGREEMENT FOR PYTHON 3.7 - -1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and - the Individual or Organization ("Licensee") accessing and otherwise using Python - 3.7 software in source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby - grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, - analyze, test, perform and/or display publicly, prepare derivative works, - distribute, and otherwise use Python 3.7 alone or in any derivative - version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright ? 2001-2018 Python Software Foundation; All Rights - Reserved" are retained in Python 3.7 alone or in any derivative version - prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on or - incorporates Python 3.7 or any part thereof, and wants to make the - derivative work available to others as provided herein, then Licensee hereby - agrees to include in any such work a brief summary of the changes made to Python - 3.7. - -4. PSF is making Python 3.7 available to Licensee on an "AS IS" basis. - PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF - EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR - WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE - USE OF PYTHON 3.7 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7 - FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF - MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7, OR ANY DERIVATIVE - THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material breach of - its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any relationship - of agency, partnership, or joint venture between PSF and Licensee. This License - Agreement does not grant permission to use PSF trademarks or trade name in a - trademark sense to endorse or promote products or services of Licensee, or any - third party. - -8. By copying, installing or otherwise using Python 3.7, Licensee agrees - to be bound by the terms and conditions of this License Agreement. - -## Features - -* Easy to install Python runtime -* Supported by core CPython team -* Find Python, Pip and Idle on PATH - -## Search Terms - -* Python -* Scripting -* Interpreter - diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj index bd61c0d4f689..95e3cd50eca5 100644 --- a/PCbuild/_tkinter.vcxproj +++ b/PCbuild/_tkinter.vcxproj @@ -95,10 +95,4 @@ - - - - - - \ No newline at end of file diff --git a/PCbuild/find_msbuild.bat b/PCbuild/find_msbuild.bat index a2810f09c45e..57512a01927e 100644 --- a/PCbuild/find_msbuild.bat +++ b/PCbuild/find_msbuild.bat @@ -29,16 +29,6 @@ @where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc" @if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found - at rem VS 2017 and later provide vswhere.exe, which can be used - at if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere - at set _Py_MSBuild_Root= - at for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest') DO @(set _Py_MSBuild_Root=%%i\MSBuild) - at if not defined _Py_MSBuild_Root goto :skip_vswhere - at for %%j in (Current 15.0) DO @if exist "%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe" (set MSBUILD="%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe") - at set _Py_MSBuild_Root= - at if defined MSBUILD @if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio installation) & goto :found -:skip_vswhere - @rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there. @reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul @if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @( diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index 6bf1667e39f8..9e103e12103f 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -52,8 +52,6 @@ - - @@ -72,7 +70,6 @@ - diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index c212d9f8f32c..59b3861ed406 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -93,14 +93,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_uwp", "python_uwp.vcxproj", "{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvlauncher", "venvlauncher.vcxproj", "{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvwlauncher", "venvwlauncher.vcxproj", "{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythonw_uwp", "pythonw_uwp.vcxproj", "{AB603547-1E2A-45B3-9E09-B04596006393}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -701,70 +693,6 @@ Global {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.ActiveCfg = Debug|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.Build.0 = Debug|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.ActiveCfg = Debug|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.Build.0 = Debug|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.Build.0 = PGInstrument|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.Build.0 = PGUpdate|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.ActiveCfg = Release|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.Build.0 = Release|Win32 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.ActiveCfg = Release|x64 - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.Build.0 = Release|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.ActiveCfg = Debug|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.Build.0 = Debug|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.ActiveCfg = Debug|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.Build.0 = Debug|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.Build.0 = PGInstrument|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.Build.0 = PGUpdate|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.ActiveCfg = Release|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.Build.0 = Release|Win32 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.ActiveCfg = Release|x64 - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.Build.0 = Release|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.ActiveCfg = Debug|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.Build.0 = Debug|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.ActiveCfg = Debug|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.Build.0 = Debug|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.Build.0 = PGInstrument|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.Build.0 = PGUpdate|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.ActiveCfg = Release|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.Build.0 = Release|Win32 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.ActiveCfg = Release|x64 - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.Build.0 = Release|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.ActiveCfg = Debug|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.Build.0 = Debug|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.ActiveCfg = Debug|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.Build.0 = Debug|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.Build.0 = PGInstrument|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.Build.0 = PGUpdate|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.ActiveCfg = Release|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.Build.0 = Release|Win32 - {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.ActiveCfg = Release|x64 - {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PCbuild/python.props b/PCbuild/python.props index 6dbb503b3243..09f11d3bba8c 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -77,8 +77,7 @@ --> <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) - 10.0.17763.0 - 10.0.17134.0 + 10.0.17134.0 10.0.16299.0 10.0.15063.0 10.0.14393.0 diff --git a/PCbuild/python_uwp.vcxproj b/PCbuild/python_uwp.vcxproj deleted file mode 100644 index af187dd4df30..000000000000 --- a/PCbuild/python_uwp.vcxproj +++ /dev/null @@ -1,86 +0,0 @@ -? - - - - Debug - Win32 - - - Debug - x64 - - - PGInstrument - Win32 - - - PGInstrument - x64 - - - PGUpdate - Win32 - - - PGUpdate - x64 - - - Release - Win32 - - - Release - x64 - - - - {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF} - - - - - Application - false - Unicode - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - - - - %(PreprocessorDefinitions) - /EHsc /std:c++17 %(AdditionalOptions) - - - windowsapp.lib;%(AdditionalDependencies) - Console - - - - - - - - - - - - - - {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} - false - - - - - - diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 2ee88225b62c..d19b5f5acf89 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -460,19 +460,4 @@ - - - $(VCInstallDir)\Redist\MSVC\$(VCToolsRedistVersion)\ - $(VCRedistDir)x86\ - $(VCRedistDir)$(Platform)\ - - - - - - - - - - diff --git a/PCbuild/pythonw_uwp.vcxproj b/PCbuild/pythonw_uwp.vcxproj deleted file mode 100644 index 79e105877fbe..000000000000 --- a/PCbuild/pythonw_uwp.vcxproj +++ /dev/null @@ -1,86 +0,0 @@ -? - - - - Debug - Win32 - - - Debug - x64 - - - PGInstrument - Win32 - - - PGInstrument - x64 - - - PGUpdate - Win32 - - - PGUpdate - x64 - - - Release - Win32 - - - Release - x64 - - - - {AB603547-1E2A-45B3-9E09-B04596006393} - - - - - Application - false - Unicode - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - - - - PYTHONW;%(PreprocessorDefinitions) - /EHsc /std:c++17 %(AdditionalOptions) - - - windowsapp.lib;%(AdditionalDependencies) - Windows - - - - - - - - - - - - - - {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} - false - - - - - - diff --git a/PCbuild/venvlauncher.vcxproj b/PCbuild/venvlauncher.vcxproj deleted file mode 100644 index 295b36304733..000000000000 --- a/PCbuild/venvlauncher.vcxproj +++ /dev/null @@ -1,85 +0,0 @@ -? - - - - Debug - Win32 - - - Debug - x64 - - - PGInstrument - Win32 - - - PGInstrument - x64 - - - PGUpdate - Win32 - - - PGUpdate - x64 - - - Release - Win32 - - - Release - x64 - - - - {494BAC80-A60C-43A9-99E7-ACB691CE2C4D} - venvlauncher - venvlauncher - false - - - - - Application - MultiByte - - - - - - ClCompile - - - - - - - - - _CONSOLE;VENV_REDIRECT;%(PreprocessorDefinitions) - MultiThreaded - - - PY_ICON;%(PreprocessorDefinitions) - - - version.lib;%(AdditionalDependencies) - Console - - - - - - - - - - - - - - - diff --git a/PCbuild/venvwlauncher.vcxproj b/PCbuild/venvwlauncher.vcxproj deleted file mode 100644 index e7ba25da41eb..000000000000 --- a/PCbuild/venvwlauncher.vcxproj +++ /dev/null @@ -1,85 +0,0 @@ -? - - - - Debug - Win32 - - - Debug - x64 - - - PGInstrument - Win32 - - - PGInstrument - x64 - - - PGUpdate - Win32 - - - PGUpdate - x64 - - - Release - Win32 - - - Release - x64 - - - - {FDB84CBB-2FB6-47C8-A2D6-091E0833239D} - venvwlauncher - venvwlauncher - false - - - - - Application - MultiByte - - - - - - ClCompile - - - - - - - - - _WINDOWS;VENV_REDIRECT;%(PreprocessorDefinitions) - MultiThreaded - - - PYW_ICON;%(PreprocessorDefinitions) - - - version.lib;%(AdditionalDependencies) - Windows - - - - - - - - - - - - - - - diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat index 45e189b537f6..4178981195ee 100644 --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -37,7 +37,6 @@ set BUILDX64= set TARGET=Rebuild set TESTTARGETDIR= set PGO=-m test -q --pgo -set BUILDMSI=1 set BUILDNUGET=1 set BUILDZIP=1 @@ -62,7 +61,6 @@ if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts -if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 @@ -176,12 +174,10 @@ if "%OUTDIR_PLAT%" EQU "win32" ( ) set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI% -if defined BUILDMSI ( - %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true - if errorlevel 1 exit /B - %MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false - if errorlevel 1 exit /B -) +%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true +if errorlevel 1 exit /B +%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false +if errorlevel 1 exit /B if defined BUILDZIP ( %MSBUILD% "%D%make_zip.proj" /t:Build %BUILDOPTS% %CERTOPTS% /p:OutputPath="%BUILD%en-us" @@ -218,7 +214,6 @@ echo --skip-build (-B) Do not build Python (just do the installers) echo --skip-doc (-D) Do not build documentation echo --pgo Specify PGO command for x64 installers echo --skip-pgo Build x64 installers without using PGO -echo --skip-msi Do not build executable/MSI packages echo --skip-nuget Do not build Nuget packages echo --skip-zip Do not build embeddable package echo --download Specify the full download URL for MSIs diff --git a/Tools/msi/make_appx.ps1 b/Tools/msi/make_appx.ps1 deleted file mode 100644 index b3f190e07db8..000000000000 --- a/Tools/msi/make_appx.ps1 +++ /dev/null @@ -1,71 +0,0 @@ -<# -.Synopsis - Compiles and signs an APPX package -.Description - Given the file listing, ensures all the contents are signed - and builds and signs the final package. -.Parameter mapfile - The location on disk of the text mapping file. -.Parameter msix - The path and name to store the APPX/MSIX. -.Parameter sign - When set, signs the APPX/MSIX. Packages to be published to - the store should not be signed. -.Parameter description - Description to embed in the signature (optional). -.Parameter certname - The name of the certificate to sign with (optional). -.Parameter certsha1 - The SHA1 hash of the certificate to sign with (optional). -#> -param( - [Parameter(Mandatory=$true)][string]$layout, - [Parameter(Mandatory=$true)][string]$msix, - [switch]$sign, - [string]$description, - [string]$certname, - [string]$certsha1, - [string]$certfile -) - -$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; -Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force - -Set-Alias makeappx (Find-Tool "makeappx.exe") -Scope Script -Set-Alias makepri (Find-Tool "makepri.exe") -Scope Script - -$msixdir = Split-Path $msix -Parent -if ($msixdir) { - $msixdir = (mkdir -Force $msixdir).FullName -} else { - $msixdir = Get-Location -} -$msix = Join-Path $msixdir (Split-Path $msix -Leaf) - -pushd $layout -try { - if (Test-Path resources.pri) { - del resources.pri - } - $name = ([xml](gc AppxManifest.xml)).Package.Identity.Name - makepri new /pr . /mn AppxManifest.xml /in $name /cf _resources.xml /of _resources.pri /mf appx /o - if (-not $? -or -not (Test-Path _resources.map.txt)) { - throw "makepri step failed" - } - $lines = gc _resources.map.txt - $lines | ?{ -not ($_ -match '"_resources[\w\.]+?"') } | Out-File _resources.map.txt -Encoding utf8 - makeappx pack /f _resources.map.txt /m AppxManifest.xml /o /p $msix - if (-not $?) { - throw "makeappx step failed" - } -} finally { - popd -} - -if ($sign) { - Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files $msix - - if (-not $?) { - throw "Package signing failed" - } -} diff --git a/Tools/msi/make_cat.ps1 b/Tools/msi/make_cat.ps1 deleted file mode 100644 index 70741439869a..000000000000 --- a/Tools/msi/make_cat.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -<# -.Synopsis - Compiles and signs a catalog file. -.Description - Given the CDF definition file, builds and signs a catalog. -.Parameter catalog - The path to the catalog definition file to compile and - sign. It is assumed that the .cat file will be the same - name with a new extension. -.Parameter description - The description to add to the signature (optional). -.Parameter certname - The name of the certificate to sign with (optional). -.Parameter certsha1 - The SHA1 hash of the certificate to sign with (optional). -#> -param( - [Parameter(Mandatory=$true)][string]$catalog, - [string]$description, - [string]$certname, - [string]$certsha1, - [string]$certfile -) - -$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; -Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force - -Set-Alias MakeCat (Find-Tool "makecat.exe") -Scope Script - -MakeCat $catalog -if (-not $?) { - throw "Catalog compilation failed" -} -Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files @($catalog -replace 'cdf$', 'cat') diff --git a/Tools/msi/make_zip.proj b/Tools/msi/make_zip.proj index 125a434e51f4..214111734219 100644 --- a/Tools/msi/make_zip.proj +++ b/Tools/msi/make_zip.proj @@ -15,12 +15,11 @@ .zip $(OutputPath)\$(TargetName)$(TargetExt) rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)" - "$(PythonExe)" "$(PySourcePath)PC\layout" - $(Arguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" - $(Arguments) -t "$(IntermediateOutputPath)\zip_$(ArchName)" - $(Arguments) --zip "$(TargetPath)" - $(Arguments) --precompile --zip-lib --include-underpth --include-stable --flat-dlls + "$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py" + $(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -b "$(BuildPath.TrimEnd(`\`))" + set DOC_FILENAME=python$(PythonVersion).chm $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib + $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) diff --git a/Tools/msi/make_zip.py b/Tools/msi/make_zip.py new file mode 100644 index 000000000000..58f3b15ef852 --- /dev/null +++ b/Tools/msi/make_zip.py @@ -0,0 +1,250 @@ +import argparse +import py_compile +import re +import sys +import shutil +import stat +import os +import tempfile + +from itertools import chain +from pathlib import Path +from zipfile import ZipFile, ZIP_DEFLATED + + +TKTCL_RE = re.compile(r'^(_?tk|tcl).+\.(pyd|dll)', re.IGNORECASE) +DEBUG_RE = re.compile(r'_d\.(pyd|dll|exe|pdb|lib)$', re.IGNORECASE) +PYTHON_DLL_RE = re.compile(r'python\d\d?\.dll$', re.IGNORECASE) + +DEBUG_FILES = { + '_ctypes_test', + '_testbuffer', + '_testcapi', + '_testconsole', + '_testimportmultiple', + '_testmultiphase', + 'xxlimited', + 'python3_dstub', +} + +EXCLUDE_FROM_LIBRARY = { + '__pycache__', + 'idlelib', + 'pydoc_data', + 'site-packages', + 'tkinter', + 'turtledemo', +} + +EXCLUDE_FROM_EMBEDDABLE_LIBRARY = { + 'ensurepip', + 'venv', +} + +EXCLUDE_FILE_FROM_LIBRARY = { + 'bdist_wininst.py', +} + +EXCLUDE_FILE_FROM_LIBS = { + 'liblzma', + 'python3stub', +} + +EXCLUDED_FILES = { + 'pyshellext', +} + +def is_not_debug(p): + if DEBUG_RE.search(p.name): + return False + + if TKTCL_RE.search(p.name): + return False + + return p.stem.lower() not in DEBUG_FILES and p.stem.lower() not in EXCLUDED_FILES + +def is_not_debug_or_python(p): + return is_not_debug(p) and not PYTHON_DLL_RE.search(p.name) + +def include_in_lib(p): + name = p.name.lower() + if p.is_dir(): + if name in EXCLUDE_FROM_LIBRARY: + return False + if name == 'test' and p.parts[-2].lower() == 'lib': + return False + if name in {'test', 'tests'} and p.parts[-3].lower() == 'lib': + return False + return True + + if name in EXCLUDE_FILE_FROM_LIBRARY: + return False + + suffix = p.suffix.lower() + return suffix not in {'.pyc', '.pyo', '.exe'} + +def include_in_embeddable_lib(p): + if p.is_dir() and p.name.lower() in EXCLUDE_FROM_EMBEDDABLE_LIBRARY: + return False + + return include_in_lib(p) + +def include_in_libs(p): + if not is_not_debug(p): + return False + + return p.stem.lower() not in EXCLUDE_FILE_FROM_LIBS + +def include_in_tools(p): + if p.is_dir() and p.name.lower() in {'scripts', 'i18n', 'pynche', 'demo', 'parser'}: + return True + + return p.suffix.lower() in {'.py', '.pyw', '.txt'} + +BASE_NAME = 'python{0.major}{0.minor}'.format(sys.version_info) + +FULL_LAYOUT = [ + ('/', '$build', 'python.exe', is_not_debug), + ('/', '$build', 'pythonw.exe', is_not_debug), + ('/', '$build', 'python{}.dll'.format(sys.version_info.major), is_not_debug), + ('/', '$build', '{}.dll'.format(BASE_NAME), is_not_debug), + ('DLLs/', '$build', '*.pyd', is_not_debug), + ('DLLs/', '$build', '*.dll', is_not_debug_or_python), + ('include/', 'include', '*.h', None), + ('include/', 'PC', 'pyconfig.h', None), + ('Lib/', 'Lib', '**/*', include_in_lib), + ('libs/', '$build', '*.lib', include_in_libs), + ('Tools/', 'Tools', '**/*', include_in_tools), +] + +EMBED_LAYOUT = [ + ('/', '$build', 'python*.exe', is_not_debug), + ('/', '$build', '*.pyd', is_not_debug), + ('/', '$build', '*.dll', is_not_debug), + ('{}.zip'.format(BASE_NAME), 'Lib', '**/*', include_in_embeddable_lib), +] + +if os.getenv('DOC_FILENAME'): + FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None)) +if os.getenv('VCREDIST_PATH'): + FULL_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) + EMBED_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) + +def copy_to_layout(target, rel_sources): + count = 0 + + if target.suffix.lower() == '.zip': + if target.exists(): + target.unlink() + + with ZipFile(str(target), 'w', ZIP_DEFLATED) as f: + with tempfile.TemporaryDirectory() as tmpdir: + for s, rel in rel_sources: + if rel.suffix.lower() == '.py': + pyc = Path(tmpdir) / rel.with_suffix('.pyc').name + try: + py_compile.compile(str(s), str(pyc), str(rel), doraise=True, optimize=2) + except py_compile.PyCompileError: + f.write(str(s), str(rel)) + else: + f.write(str(pyc), str(rel.with_suffix('.pyc'))) + else: + f.write(str(s), str(rel)) + count += 1 + + else: + for s, rel in rel_sources: + dest = target / rel + try: + dest.parent.mkdir(parents=True) + except FileExistsError: + pass + if dest.is_file(): + dest.chmod(stat.S_IWRITE) + shutil.copy(str(s), str(dest)) + if dest.is_file(): + dest.chmod(stat.S_IWRITE) + count += 1 + + return count + +def rglob(root, pattern, condition): + dirs = [root] + recurse = pattern[:3] in {'**/', '**\\'} + while dirs: + d = dirs.pop(0) + for f in d.glob(pattern[3:] if recurse else pattern): + if recurse and f.is_dir() and (not condition or condition(f)): + dirs.append(f) + elif f.is_file() and (not condition or condition(f)): + yield f, f.relative_to(root) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-s', '--source', metavar='dir', help='The directory containing the repository root', type=Path) + parser.add_argument('-o', '--out', metavar='file', help='The name of the output archive', type=Path, default=None) + parser.add_argument('-t', '--temp', metavar='dir', help='A directory to temporarily extract files into', type=Path, default=None) + parser.add_argument('-e', '--embed', help='Create an embedding layout', action='store_true', default=False) + parser.add_argument('-b', '--build', help='Specify the build directory', type=Path, default=None) + ns = parser.parse_args() + + source = ns.source or (Path(__file__).resolve().parent.parent.parent) + out = ns.out + build = ns.build or Path(sys.exec_prefix) + assert isinstance(source, Path) + assert not out or isinstance(out, Path) + assert isinstance(build, Path) + + if ns.temp: + temp = ns.temp + delete_temp = False + else: + temp = Path(tempfile.mkdtemp()) + delete_temp = True + + if out: + try: + out.parent.mkdir(parents=True) + except FileExistsError: + pass + try: + temp.mkdir(parents=True) + except FileExistsError: + pass + + layout = EMBED_LAYOUT if ns.embed else FULL_LAYOUT + + try: + for t, s, p, c in layout: + if s == '$build': + fs = build + else: + fs = source / s + files = rglob(fs, p, c) + extra_files = [] + if s == 'Lib' and p == '**/*': + extra_files.append(( + source / 'tools' / 'msi' / 'distutils.command.bdist_wininst.py', + Path('distutils') / 'command' / 'bdist_wininst.py' + )) + copied = copy_to_layout(temp / t.rstrip('/'), chain(files, extra_files)) + print('Copied {} files'.format(copied)) + + if ns.embed: + with open(str(temp / (BASE_NAME + '._pth')), 'w') as f: + print(BASE_NAME + '.zip', file=f) + print('.', file=f) + print('', file=f) + print('# Uncomment to run site.main() automatically', file=f) + print('#import site', file=f) + + if out: + total = copy_to_layout(out, rglob(temp, '**/*', None)) + print('Wrote {} files to {}'.format(total, out)) + finally: + if delete_temp: + shutil.rmtree(temp, True) + + +if __name__ == "__main__": + sys.exit(int(main() or 0)) diff --git a/Tools/msi/sdktools.psm1 b/Tools/msi/sdktools.psm1 deleted file mode 100644 index 81a74d3679d7..000000000000 --- a/Tools/msi/sdktools.psm1 +++ /dev/null @@ -1,43 +0,0 @@ -function Find-Tool { - param([string]$toolname) - - $kitroot = (gp 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots\').KitsRoot10 - $tool = (gci -r "$kitroot\Bin\*\x64\$toolname" | sort FullName -Desc | select -First 1) - if (-not $tool) { - throw "$toolname is not available" - } - Write-Host "Found $toolname at $($tool.FullName)" - return $tool.FullName -} - -Set-Alias SignTool (Find-Tool "signtool.exe") -Scope Script - -function Sign-File { - param([string]$certname, [string]$certsha1, [string]$certfile, [string]$description, [string[]]$files) - - if (-not $description) { - $description = $env:SigningDescription; - if (-not $description) { - $description = "Python"; - } - } - if (-not $certname) { - $certname = $env:SigningCertificate; - } - if (-not $certfile) { - $certfile = $env:SigningCertificateFile; - } - - foreach ($a in $files) { - if ($certsha1) { - SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a - } elseif ($certname) { - SignTool sign /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a - } elseif ($certfile) { - SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a - } else { - SignTool sign /a /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a - } - } -} - diff --git a/Tools/msi/sign_build.ps1 b/Tools/msi/sign_build.ps1 deleted file mode 100644 index 6668eb33a2d1..000000000000 --- a/Tools/msi/sign_build.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -<# -.Synopsis - Recursively signs the contents of a directory. -.Description - Given the file patterns, code signs the contents. -.Parameter root - The root directory to sign. -.Parameter patterns - The file patterns to sign -.Parameter description - The description to add to the signature (optional). -.Parameter certname - The name of the certificate to sign with (optional). -.Parameter certsha1 - The SHA1 hash of the certificate to sign with (optional). -#> -param( - [Parameter(Mandatory=$true)][string]$root, - [string[]]$patterns=@("*.exe", "*.dll", "*.pyd"), - [string]$description, - [string]$certname, - [string]$certsha1, - [string]$certfile -) - -$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; -Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force - -pushd $root -try { - Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files (gci -r $patterns) -} finally { - popd -} \ No newline at end of file diff --git a/Tools/nuget/make_pkg.proj b/Tools/nuget/make_pkg.proj index e093a6d0bd76..9843bc97ccdc 100644 --- a/Tools/nuget/make_pkg.proj +++ b/Tools/nuget/make_pkg.proj @@ -20,28 +20,25 @@ false $(OutputName).$(NuspecVersion) .nupkg - $(IntermediateOutputPath)\nuget_$(ArchName)\ + $(IntermediateOutputPath)\nuget_$(ArchName) - rmdir /q/s "$(IntermediateOutputPath.TrimEnd(`\`))" + rmdir /q/s "$(IntermediateOutputPath)" - "$(PythonExe)" "$(PySourcePath)PC\layout" - $(PythonArguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" - $(PythonArguments) -t "$(IntermediateOutputPath)obj" - $(PythonArguments) --copy "$(IntermediateOutputPath)pkg" - $(PythonArguments) --include-dev --include-tools --include-pip --include-stable --include-launcher --include-props + "$(PythonExe)" "$(MSBuildThisFileDirectory)\..\msi\make_zip.py" + $(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))" - "$(IntermediateOutputPath)pkg\pip.exe" -B -m pip install -U $(Packages) + "$(IntermediateOutputPath)\python.exe" -B -c "import sys; sys.path.append(r'$(PySourcePath)\Lib'); import ensurepip; ensurepip._main()" + "$(IntermediateOutputPath)\python.exe" -B -m pip install -U $(Packages) - "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)pkg" + "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)" "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))" $(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))" $(NugetArguments) -Version "$(NuspecVersion)" $(NugetArguments) -NoPackageAnalysis -NonInteractive + set DOC_FILENAME=python$(PythonVersion).chm $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset PYTHON_NUSPEC_VERSION=$(NuspecVersion) - $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=$(Platform) - $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=Win32 + $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) $(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" >nul 2>nul @@ -51,7 +48,22 @@ - + + + + + + <_PropsContents>$([System.IO.File]::ReadAllText('python.props')) + <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TAG$$', '$(MajorVersionNumber).$(MinorVersionNumber)')) + <_PropsContents>$(_PropsContents.Replace('$$PYTHON_VERSION$$', '$(NuspecVersion)')) + <_PropsContents Condition="$(Platform) == 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', 'Win32')) + <_PropsContents Condition="$(Platform) != 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', '$(Platform)')) + <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TARGET$$', '_GetPythonRuntimeFilesDependsOn$(MajorVersionNumber)$(MinorVersionNumber)_$(Platform)')) + <_ExistingContents Condition="Exists('$(IntermediateOutputPath)\python.props')">$([System.IO.File]::ReadAllText('$(IntermediateOutputPath)\python.props')) + + diff --git a/PC/layout/support/python.props b/Tools/nuget/python.props similarity index 100% rename from PC/layout/support/python.props rename to Tools/nuget/python.props From webhook-mailer at python.org Fri Dec 7 08:53:38 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 07 Dec 2018 13:53:38 -0000 Subject: [Python-checkins] bpo-22005: Fixed unpickling instances of datetime classes pickled by Python 2. (GH-11017) (GH-11022) (GH-11024) Message-ID: https://github.com/python/cpython/commit/19f6e83bf03b3ce22300638906bd90dd2dd5c463 commit: 19f6e83bf03b3ce22300638906bd90dd2dd5c463 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Serhiy Storchaka date: 2018-12-07T15:53:32+02:00 summary: bpo-22005: Fixed unpickling instances of datetime classes pickled by Python 2. (GH-11017) (GH-11022) (GH-11024) encoding='latin1' should be used for successful decoding. (cherry picked from commit 8452ca15f41061c8a6297d7956df22ab476d4df4) (cherry picked from commit 0d5730e6437b157f4aeaf5d2e67abca23448c29a) files: A Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst M Doc/library/pickle.rst M Lib/datetime.py M Lib/test/datetimetester.py M Modules/_datetimemodule.c diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 2b10ee2eb8ee..52cbb6241bc9 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -235,6 +235,9 @@ process more convenient: *errors* tell pickle how to decode 8-bit string instances pickled by Python 2; these default to 'ASCII' and 'strict', respectively. The *encoding* can be 'bytes' to read these 8-bit string instances as bytes objects. + Using ``encoding='latin1'`` is required for unpickling NumPy arrays and + instances of :class:`~datetime.datetime`, :class:`~datetime.date` and + :class:`~datetime.time` pickled by Python 2. .. function:: loads(bytes_object, \*, fix_imports=True, encoding="ASCII", errors="strict") @@ -252,6 +255,9 @@ process more convenient: *errors* tell pickle how to decode 8-bit string instances pickled by Python 2; these default to 'ASCII' and 'strict', respectively. The *encoding* can be 'bytes' to read these 8-bit string instances as bytes objects. + Using ``encoding='latin1'`` is required for unpickling NumPy arrays and + instances of :class:`~datetime.datetime`, :class:`~datetime.date` and + :class:`~datetime.time` pickled by Python 2. The :mod:`pickle` module defines three exceptions: diff --git a/Lib/datetime.py b/Lib/datetime.py index b8782fc8b363..2f3aa48c66e4 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -693,9 +693,19 @@ def __new__(cls, year, month=None, day=None): year, month, day (required, base 1) """ - if month is None and isinstance(year, bytes) and len(year) == 4 and \ - 1 <= year[2] <= 12: + if (month is None and + isinstance(year, (bytes, str)) and len(year) == 4 and + 1 <= ord(year[2:3]) <= 12): # Pickle support + if isinstance(year, str): + try: + year = year.encode('latin1') + except UnicodeEncodeError: + # More informative error message. + raise ValueError( + "Failed to encode latin1 string when unpickling " + "a date object. " + "pickle.load(data, encoding='latin1') is assumed.") self = object.__new__(cls) self.__setstate(year) self._hashcode = -1 @@ -1056,8 +1066,18 @@ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold tzinfo (default to None) fold (keyword only, default to zero) """ - if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24: + if (isinstance(hour, (bytes, str)) and len(hour) == 6 and + ord(hour[0:1])&0x7F < 24): # Pickle support + if isinstance(hour, str): + try: + hour = hour.encode('latin1') + except UnicodeEncodeError: + # More informative error message. + raise ValueError( + "Failed to encode latin1 string when unpickling " + "a time object. " + "pickle.load(data, encoding='latin1') is assumed.") self = object.__new__(cls) self.__setstate(hour, minute or None) self._hashcode = -1 @@ -1368,8 +1388,18 @@ class datetime(date): def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0): - if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12: + if (isinstance(year, (bytes, str)) and len(year) == 10 and + 1 <= ord(year[2:3])&0x7F <= 12): # Pickle support + if isinstance(year, str): + try: + year = bytes(year, 'latin1') + except UnicodeEncodeError: + # More informative error message. + raise ValueError( + "Failed to encode latin1 string when unpickling " + "a datetime object. " + "pickle.load(data, encoding='latin1') is assumed.") self = object.__new__(cls) self.__setstate(year, month) self._hashcode = -1 diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 54035ab57f8d..a11908a058bb 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -35,6 +35,7 @@ import _strptime # +pickle_loads = {pickle.loads, pickle._loads} pickle_choices = [(pickle, pickle, proto) for proto in range(pickle.HIGHEST_PROTOCOL + 1)] @@ -1404,6 +1405,19 @@ def test_pickling(self): self.assertEqual(orig, derived) self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) + def test_compat_unpickle(self): + tests = [ + b"cdatetime\ndate\n(S'\\x07\\xdf\\x0b\\x1b'\ntR.", + b'cdatetime\ndate\n(U\x04\x07\xdf\x0b\x1btR.', + b'\x80\x02cdatetime\ndate\nU\x04\x07\xdf\x0b\x1b\x85R.', + ] + args = 2015, 11, 27 + expected = self.theclass(*args) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + def test_compare(self): t1 = self.theclass(2, 3, 4) t2 = self.theclass(2, 3, 4) @@ -1930,6 +1944,24 @@ def test_pickling_subclass_datetime(self): derived = unpickler.loads(green) self.assertEqual(orig, derived) + def test_compat_unpickle(self): + tests = [ + b'cdatetime\ndatetime\n(' + b"S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x00\\x10\\x00'\ntR.", + + b'cdatetime\ndatetime\n(' + b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00tR.', + + b'\x80\x02cdatetime\ndatetime\n' + b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00\x85R.', + ] + args = 2015, 11, 27, 20, 59, 1, 64**2 + expected = self.theclass(*args) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + def test_more_compare(self): # The test_compare() inherited from TestDate covers the error cases. # We just want to test lexicographic ordering on the members datetime @@ -2609,6 +2641,19 @@ def test_pickling_subclass_time(self): derived = unpickler.loads(green) self.assertEqual(orig, derived) + def test_compat_unpickle(self): + tests = [ + b"cdatetime\ntime\n(S'\\x14;\\x10\\x00\\x10\\x00'\ntR.", + b'cdatetime\ntime\n(U\x06\x14;\x10\x00\x10\x00tR.', + b'\x80\x02cdatetime\ntime\nU\x06\x14;\x10\x00\x10\x00\x85R.', + ] + args = 20, 59, 16, 64**2 + expected = self.theclass(*args) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + def test_bool(self): # time is always True. cls = self.theclass @@ -2981,6 +3026,40 @@ def test_pickling(self): self.assertEqual(derived.tzname(), 'cookie') self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) + def test_compat_unpickle(self): + tests = [ + b"cdatetime\ntime\n(S'\\x05\\x06\\x07\\x01\\xe2@'\n" + b"ctest.datetimetester\nPicklableFixedOffset\n(tR" + b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n" + b"(I-1\nI68400\nI0\ntRs" + b"S'_FixedOffset__dstoffset'\nNs" + b"S'_FixedOffset__name'\nS'cookie'\nsbtR.", + + b'cdatetime\ntime\n(U\x06\x05\x06\x07\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieubtR.', + + b'\x80\x02cdatetime\ntime\nU\x06\x05\x06\x07\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieub\x86R.', + ] + + tinfo = PicklableFixedOffset(-300, 'cookie') + expected = self.theclass(5, 6, 7, 123456, tzinfo=tinfo) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected, repr(data)) + self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) + self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) + self.assertEqual(derived.tzname(), 'cookie') + def test_more_bool(self): # time is always True. cls = self.theclass @@ -3198,6 +3277,43 @@ def test_pickling(self): self.assertEqual(derived.tzname(), 'cookie') self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) + def test_compat_unpickle(self): + tests = [ + b'cdatetime\ndatetime\n' + b"(S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x01\\xe2@'\n" + b'ctest.datetimetester\nPicklableFixedOffset\n(tR' + b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n" + b'(I-1\nI68400\nI0\ntRs' + b"S'_FixedOffset__dstoffset'\nNs" + b"S'_FixedOffset__name'\nS'cookie'\nsbtR.", + + b'cdatetime\ndatetime\n' + b'(U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieubtR.', + + b'\x80\x02cdatetime\ndatetime\n' + b'U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@' + b'ctest.datetimetester\nPicklableFixedOffset\n)R' + b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n' + b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R' + b'U\x17_FixedOffset__dstoffsetN' + b'U\x12_FixedOffset__nameU\x06cookieub\x86R.', + ] + args = 2015, 11, 27, 20, 59, 1, 123456 + tinfo = PicklableFixedOffset(-300, 'cookie') + expected = self.theclass(*args, **{'tzinfo': tinfo}) + for data in tests: + for loads in pickle_loads: + derived = loads(data, encoding='latin1') + self.assertEqual(derived, expected) + self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) + self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) + self.assertEqual(derived.tzname(), 'cookie') + def test_extreme_hashes(self): # If an attempt is made to hash these via subtracting the offset # then hashing a datetime object, OverflowError results. The diff --git a/Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst b/Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst new file mode 100644 index 000000000000..951098d0a7a3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-10-12-22-39-55.bpo-22005.lGP-sc.rst @@ -0,0 +1,3 @@ +Implemented unpickling instances of :class:`~datetime.datetime`, +:class:`~datetime.date` and :class:`~datetime.time` pickled by Python 2. +``encoding='latin1'`` should be used for successful decoding. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 2edaf1b319c4..397b89886a78 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -2535,30 +2535,61 @@ static PyGetSetDef date_getset[] = { static char *date_kws[] = {"year", "month", "day", NULL}; +static PyObject * +date_from_pickle(PyTypeObject *type, PyObject *state) +{ + PyDateTime_Date *me; + + me = (PyDateTime_Date *) (type->tp_alloc(type, 0)); + if (me != NULL) { + const char *pdata = PyBytes_AS_STRING(state); + memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE); + me->hashcode = -1; + } + return (PyObject *)me; +} + static PyObject * date_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *state; int year; int month; int day; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) == 1 && - PyBytes_Check(state = PyTuple_GET_ITEM(args, 0)) && - PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE && - MONTH_IS_SANE(PyBytes_AS_STRING(state)[2])) - { - PyDateTime_Date *me; - - me = (PyDateTime_Date *) (type->tp_alloc(type, 0)); - if (me != NULL) { - char *pdata = PyBytes_AS_STRING(state); - memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE); - me->hashcode = -1; + if (PyTuple_GET_SIZE(args) == 1) { + PyObject *state = PyTuple_GET_ITEM(args, 0); + if (PyBytes_Check(state)) { + if (PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE && + MONTH_IS_SANE(PyBytes_AS_STRING(state)[2])) + { + return date_from_pickle(type, state); + } + } + else if (PyUnicode_Check(state)) { + if (PyUnicode_READY(state)) { + return NULL; + } + if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATE_DATASIZE && + MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2))) + { + state = PyUnicode_AsLatin1String(state); + if (state == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* More informative error message. */ + PyErr_SetString(PyExc_ValueError, + "Failed to encode latin1 string when unpickling " + "a date object. " + "pickle.load(data, encoding='latin1') is assumed."); + } + return NULL; + } + self = date_from_pickle(type, state); + Py_DECREF(state); + return self; + } } - return (PyObject *)me; } if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws, @@ -3591,11 +3622,43 @@ static PyGetSetDef time_getset[] = { static char *time_kws[] = {"hour", "minute", "second", "microsecond", "tzinfo", "fold", NULL}; +static PyObject * +time_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) +{ + PyDateTime_Time *me; + char aware = (char)(tzinfo != Py_None); + + if (aware && check_tzinfo_subclass(tzinfo) < 0) { + PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); + return NULL; + } + + me = (PyDateTime_Time *) (type->tp_alloc(type, aware)); + if (me != NULL) { + const char *pdata = PyBytes_AS_STRING(state); + + memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE); + me->hashcode = -1; + me->hastzinfo = aware; + if (aware) { + Py_INCREF(tzinfo); + me->tzinfo = tzinfo; + } + if (pdata[0] & (1 << 7)) { + me->data[0] -= 128; + me->fold = 1; + } + else { + me->fold = 0; + } + } + return (PyObject *)me; +} + static PyObject * time_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *state; int hour = 0; int minute = 0; int second = 0; @@ -3604,44 +3667,42 @@ time_new(PyTypeObject *type, PyObject *args, PyObject *kw) int fold = 0; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) >= 1 && - PyTuple_GET_SIZE(args) <= 2 && - PyBytes_Check(state = PyTuple_GET_ITEM(args, 0)) && - PyBytes_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE && - (0x7F & ((unsigned char) (PyBytes_AS_STRING(state)[0]))) < 24) - { - PyDateTime_Time *me; - char aware; - + if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) { + PyObject *state = PyTuple_GET_ITEM(args, 0); if (PyTuple_GET_SIZE(args) == 2) { tzinfo = PyTuple_GET_ITEM(args, 1); - if (check_tzinfo_subclass(tzinfo) < 0) { - PyErr_SetString(PyExc_TypeError, "bad " - "tzinfo state arg"); - return NULL; - } } - aware = (char)(tzinfo != Py_None); - me = (PyDateTime_Time *) (type->tp_alloc(type, aware)); - if (me != NULL) { - char *pdata = PyBytes_AS_STRING(state); - - memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE); - me->hashcode = -1; - me->hastzinfo = aware; - if (aware) { - Py_INCREF(tzinfo); - me->tzinfo = tzinfo; + if (PyBytes_Check(state)) { + if (PyBytes_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE && + (0x7F & ((unsigned char) (PyBytes_AS_STRING(state)[0]))) < 24) + { + return time_from_pickle(type, state, tzinfo); } - if (pdata[0] & (1 << 7)) { - me->data[0] -= 128; - me->fold = 1; + } + else if (PyUnicode_Check(state)) { + if (PyUnicode_READY(state)) { + return NULL; } - else { - me->fold = 0; + if (PyUnicode_GET_LENGTH(state) == _PyDateTime_TIME_DATASIZE && + (0x7F & PyUnicode_READ_CHAR(state, 2)) < 24) + { + state = PyUnicode_AsLatin1String(state); + if (state == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* More informative error message. */ + PyErr_SetString(PyExc_ValueError, + "Failed to encode latin1 string when unpickling " + "a time object. " + "pickle.load(data, encoding='latin1') is assumed."); + } + return NULL; + } + self = time_from_pickle(type, state, tzinfo); + Py_DECREF(state); + return self; } } - return (PyObject *)me; + tzinfo = Py_None; } if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO$i", time_kws, @@ -4174,11 +4235,43 @@ static char *datetime_kws[] = { "microsecond", "tzinfo", "fold", NULL }; +static PyObject * +datetime_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) +{ + PyDateTime_DateTime *me; + char aware = (char)(tzinfo != Py_None); + + if (aware && check_tzinfo_subclass(tzinfo) < 0) { + PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); + return NULL; + } + + me = (PyDateTime_DateTime *) (type->tp_alloc(type , aware)); + if (me != NULL) { + const char *pdata = PyBytes_AS_STRING(state); + + memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE); + me->hashcode = -1; + me->hastzinfo = aware; + if (aware) { + Py_INCREF(tzinfo); + me->tzinfo = tzinfo; + } + if (pdata[2] & (1 << 7)) { + me->data[2] -= 128; + me->fold = 1; + } + else { + me->fold = 0; + } + } + return (PyObject *)me; +} + static PyObject * datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *state; int year; int month; int day; @@ -4190,44 +4283,42 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) PyObject *tzinfo = Py_None; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) >= 1 && - PyTuple_GET_SIZE(args) <= 2 && - PyBytes_Check(state = PyTuple_GET_ITEM(args, 0)) && - PyBytes_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE && - MONTH_IS_SANE(PyBytes_AS_STRING(state)[2] & 0x7F)) - { - PyDateTime_DateTime *me; - char aware; - + if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) { + PyObject *state = PyTuple_GET_ITEM(args, 0); if (PyTuple_GET_SIZE(args) == 2) { tzinfo = PyTuple_GET_ITEM(args, 1); - if (check_tzinfo_subclass(tzinfo) < 0) { - PyErr_SetString(PyExc_TypeError, "bad " - "tzinfo state arg"); - return NULL; - } } - aware = (char)(tzinfo != Py_None); - me = (PyDateTime_DateTime *) (type->tp_alloc(type , aware)); - if (me != NULL) { - char *pdata = PyBytes_AS_STRING(state); - - memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE); - me->hashcode = -1; - me->hastzinfo = aware; - if (aware) { - Py_INCREF(tzinfo); - me->tzinfo = tzinfo; + if (PyBytes_Check(state)) { + if (PyBytes_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE && + MONTH_IS_SANE(PyBytes_AS_STRING(state)[2] & 0x7F)) + { + return datetime_from_pickle(type, state, tzinfo); } - if (pdata[2] & (1 << 7)) { - me->data[2] -= 128; - me->fold = 1; + } + else if (PyUnicode_Check(state)) { + if (PyUnicode_READY(state)) { + return NULL; } - else { - me->fold = 0; + if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATETIME_DATASIZE && + MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2) & 0x7F)) + { + state = PyUnicode_AsLatin1String(state); + if (state == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* More informative error message. */ + PyErr_SetString(PyExc_ValueError, + "Failed to encode latin1 string when unpickling " + "a datetime object. " + "pickle.load(data, encoding='latin1') is assumed."); + } + return NULL; + } + self = datetime_from_pickle(type, state, tzinfo); + Py_DECREF(state); + return self; } } - return (PyObject *)me; + tzinfo = Py_None; } if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO$i", datetime_kws, From webhook-mailer at python.org Fri Dec 7 09:48:32 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 07 Dec 2018 14:48:32 -0000 Subject: [Python-checkins] bpo-22005: Fix condition for unpickling a date object. (GH-11025) Message-ID: https://github.com/python/cpython/commit/1133a8c0efabf6b33a169039cf6e2e03bfe6cfe3 commit: 1133a8c0efabf6b33a169039cf6e2e03bfe6cfe3 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-07T16:48:21+02:00 summary: bpo-22005: Fix condition for unpickling a date object. (GH-11025) files: M Modules/_datetimemodule.c diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 87a88be6090c..16c7bffda8f6 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -2811,7 +2811,7 @@ date_new(PyTypeObject *type, PyObject *args, PyObject *kw) int day; /* Check for invocation from pickle with __getstate__ state */ - if (PyTuple_GET_SIZE(args) >= 1) { + if (PyTuple_GET_SIZE(args) == 1) { PyObject *state = PyTuple_GET_ITEM(args, 0); if (PyBytes_Check(state)) { if (PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE && From webhook-mailer at python.org Fri Dec 7 11:09:26 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 07 Dec 2018 16:09:26 -0000 Subject: [Python-checkins] bpo-35436: Add missing PyErr_NoMemory() calls and other minor bug fixes. (GH-11015) (GH-11020) (GH-11026) Message-ID: https://github.com/python/cpython/commit/2d6bc25dbc3dc5662f13917eb759f92842bf6de6 commit: 2d6bc25dbc3dc5662f13917eb759f92842bf6de6 branch: 3.6 author: Serhiy Storchaka committer: GitHub date: 2018-12-07T18:09:23+02:00 summary: bpo-35436: Add missing PyErr_NoMemory() calls and other minor bug fixes. (GH-11015) (GH-11020) (GH-11026) (cherry picked from commit 4c49da0cb7434c676d70b9ccf38aca82ac0d64a9) (cherry picked from commit 602d307ac5e8a2da38a193dca3bdfef5994dfe67) Co-authored-by: Zackery Spytz files: A Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst M Modules/_ctypes/_ctypes.c M Modules/_ctypes/callbacks.c M Modules/_io/winconsoleio.c M Modules/_multiprocessing/semaphore.c M Modules/_ssl.c M Modules/posixmodule.c M Objects/capsule.c M PC/getpathp.c M Parser/myreadline.c M Parser/tokenizer.c M Python/ast.c M Python/marshal.c M Python/pystrtod.c diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst new file mode 100644 index 000000000000..542fe93a00eb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-07-02-38-01.bpo-35436.0VW7p9.rst @@ -0,0 +1,2 @@ +Fix various issues with memory allocation error handling. Patch by Zackery +Spytz. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index c5b499c7aac2..24d64858edc7 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -306,8 +306,10 @@ _ctypes_alloc_format_string_for_type(char code, int big_endian) } result = PyMem_Malloc(3); - if (result == NULL) + if (result == NULL) { + PyErr_NoMemory(); return NULL; + } result[0] = big_endian ? '>' : '<'; result[1] = pep_code; @@ -367,8 +369,10 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, if (prefix) prefix_len += strlen(prefix); new_prefix = PyMem_Malloc(prefix_len); - if (new_prefix == NULL) + if (new_prefix == NULL) { + PyErr_NoMemory(); return NULL; + } new_prefix[0] = '\0'; if (prefix) strcpy(new_prefix, prefix); @@ -1856,6 +1860,10 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject #else suffix = PyUnicode_InternFromString("_be"); #endif + if (suffix == NULL) { + Py_DECREF(swapped_args); + return NULL; + } newname = PyUnicode_Concat(name, suffix); if (newname == NULL) { diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index b958f304c540..c8773a12e1a8 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -309,7 +309,6 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nArgs) p = PyObject_GC_NewVar(CThunkObject, &PyCThunk_Type, nArgs); if (p == NULL) { - PyErr_NoMemory(); return NULL; } @@ -347,7 +346,7 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable, assert(CThunk_CheckExact((PyObject *)p)); p->pcl_write = ffi_closure_alloc(sizeof(ffi_closure), - &p->pcl_exec); + &p->pcl_exec); if (p->pcl_write == NULL) { PyErr_NoMemory(); goto error; @@ -397,8 +396,8 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable, result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p); #else result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn, - p, - p->pcl_exec); + p, + p->pcl_exec); #endif if (result != FFI_OK) { PyErr_Format(PyExc_RuntimeError, diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 979bcfc49b9b..2f36806ef693 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -832,11 +832,13 @@ _io__WindowsConsoleIO_readall_impl(winconsoleio *self) } bufsize = newsize; - buf = PyMem_Realloc(buf, (bufsize + 1) * sizeof(wchar_t)); - if (!buf) { + wchar_t *tmp = PyMem_Realloc(buf, + (bufsize + 1) * sizeof(wchar_t)); + if (tmp == NULL) { PyMem_Free(buf); return NULL; } + buf = tmp; } subbuf = read_console_w(self->handle, bufsize - len, &n); diff --git a/Modules/_multiprocessing/semaphore.c b/Modules/_multiprocessing/semaphore.c index cea962ab26c3..4ff454f987b0 100644 --- a/Modules/_multiprocessing/semaphore.c +++ b/Modules/_multiprocessing/semaphore.c @@ -439,8 +439,9 @@ semlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!unlink) { name_copy = PyMem_Malloc(strlen(name) + 1); - if (name_copy == NULL) - goto failure; + if (name_copy == NULL) { + return PyErr_NoMemory(); + } strcpy(name_copy, name); } @@ -463,7 +464,9 @@ semlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (handle != SEM_FAILED) SEM_CLOSE(handle); PyMem_Free(name_copy); - _PyMp_SetError(NULL, MP_STANDARD_ERROR); + if (!PyErr_Occurred()) { + _PyMp_SetError(NULL, MP_STANDARD_ERROR); + } return NULL; } diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 2b043da280b8..a188d6a7291a 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -685,6 +685,11 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, PySSL_BEGIN_ALLOW_THREADS self->ssl = SSL_new(ctx); PySSL_END_ALLOW_THREADS + if (self->ssl == NULL) { + Py_DECREF(self); + _setSSLError(NULL, 0, __FILE__, __LINE__); + return NULL; + } SSL_set_app_data(self->ssl, self); if (sock) { SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int)); @@ -1040,6 +1045,10 @@ _get_peer_alt_names (X509 *certificate) { /* get a memory buffer */ biobuf = BIO_new(BIO_s_mem()); + if (biobuf == NULL) { + PyErr_SetString(PySSLErrorObject, "failed to allocate BIO"); + return NULL; + } i = -1; while ((i = X509_get_ext_by_NID( @@ -1415,6 +1424,10 @@ _decode_certificate(X509 *certificate) { /* get a memory buffer */ biobuf = BIO_new(BIO_s_mem()); + if (biobuf == NULL) { + PyErr_SetString(PySSLErrorObject, "failed to allocate BIO"); + goto fail0; + } (void) BIO_reset(biobuf); serialNumber = X509_get_serialNumber(certificate); diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 03825c3228ca..776a3d249a81 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6127,8 +6127,7 @@ os_getgroups_impl(PyObject *module) } else { alt_grouplist = PyMem_New(gid_t, n); if (alt_grouplist == NULL) { - errno = EINVAL; - return posix_error(); + return PyErr_NoMemory(); } } @@ -6153,8 +6152,7 @@ os_getgroups_impl(PyObject *module) } else { alt_grouplist = PyMem_New(gid_t, n); if (alt_grouplist == NULL) { - errno = EINVAL; - return posix_error(); + return PyErr_NoMemory(); } n = getgroups(n, alt_grouplist); if (n == -1) { diff --git a/Objects/capsule.c b/Objects/capsule.c index acd3de637dd5..4e15b440b170 100644 --- a/Objects/capsule.c +++ b/Objects/capsule.c @@ -201,7 +201,7 @@ PyCapsule_Import(const char *name, int no_block) char *name_dup = (char *)PyMem_MALLOC(name_length); if (!name_dup) { - return NULL; + return PyErr_NoMemory(); } memcpy(name_dup, name, name_length); diff --git a/PC/getpathp.c b/PC/getpathp.c index af4af766a70f..880ea7b966e5 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -586,6 +586,9 @@ read_pth_file(const wchar_t *path, wchar_t *prefix, int *isolated, int *nosite) size_t prefixlen = wcslen(prefix); wchar_t *buf = (wchar_t*)PyMem_RawMalloc(bufsiz * sizeof(wchar_t)); + if (buf == NULL) { + goto error; + } buf[0] = '\0'; while (!feof(sp_file)) { @@ -611,17 +614,22 @@ read_pth_file(const wchar_t *path, wchar_t *prefix, int *isolated, int *nosite) DWORD wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, NULL, 0); wchar_t *wline = (wchar_t*)PyMem_RawMalloc((wn + 1) * sizeof(wchar_t)); + if (wline == NULL) { + goto error; + } wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, wline, wn + 1); wline[wn] = '\0'; size_t usedsiz = wcslen(buf); while (usedsiz + wn + prefixlen + 4 > bufsiz) { bufsiz += MAXPATHLEN; - buf = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) * sizeof(wchar_t)); - if (!buf) { + wchar_t *tmp = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) * + sizeof(wchar_t)); + if (tmp == NULL) { PyMem_RawFree(wline); goto error; } + buf = tmp; } if (usedsiz) { diff --git a/Parser/myreadline.c b/Parser/myreadline.c index 476af717451b..34a6f8bf5d24 100644 --- a/Parser/myreadline.c +++ b/Parser/myreadline.c @@ -162,20 +162,37 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn) wbuf = (wchar_t*)PyMem_RawMalloc(wbuflen * sizeof(wchar_t)); if (wbuf) wcscpy_s(wbuf, wbuflen, wbuf_local); + else { + PyErr_NoMemory(); + goto exit; + } + } + else { + wchar_t *tmp = PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t)); + if (tmp == NULL) { + PyErr_NoMemory(); + goto exit; + } + wbuf = tmp; } - else - wbuf = (wchar_t*)PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t)); } if (wbuf[0] == '\x1a') { buf = PyMem_RawMalloc(1); if (buf) buf[0] = '\0'; + else { + PyErr_NoMemory(); + } goto exit; } u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, NULL, 0, NULL, NULL); buf = PyMem_RawMalloc(u8len + 1); + if (buf == NULL) { + PyErr_NoMemory(); + goto exit; + } u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, buf, u8len, NULL, NULL); buf[u8len] = '\0'; @@ -224,8 +241,12 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) int wlen; wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1, NULL, 0); - if (wlen && - (wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t)))) { + if (wlen) { + wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t)); + if (wbuf == NULL) { + PyErr_NoMemory(); + return NULL; + } wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1, wbuf, wlen); if (wlen) { @@ -249,8 +270,10 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) n = 100; p = (char *)PyMem_RawMalloc(n); - if (p == NULL) + if (p == NULL) { + PyErr_NoMemory(); return NULL; + } fflush(sys_stdout); if (prompt) @@ -328,6 +351,10 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) #ifdef WITH_THREAD if (_PyOS_ReadlineLock == NULL) { _PyOS_ReadlineLock = PyThread_allocate_lock(); + if (_PyOS_ReadlineLock == NULL) { + PyErr_SetString(PyExc_MemoryError, "can't allocate lock"); + return NULL; + } } #endif @@ -360,8 +387,12 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) len = strlen(rv) + 1; res = PyMem_Malloc(len); - if (res != NULL) + if (res != NULL) { memcpy(res, rv, len); + } + else { + PyErr_NoMemory(); + } PyMem_RawFree(rv); return res; diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index ab72f6145f28..0d3ad4dd7ae0 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -956,6 +956,11 @@ tok_nextc(struct tok_state *tok) buflen = PyBytes_GET_SIZE(u); buf = PyBytes_AS_STRING(u); newtok = PyMem_MALLOC(buflen+1); + if (newtok == NULL) { + Py_DECREF(u); + tok->done = E_NOMEM; + return EOF; + } strcpy(newtok, buf); Py_DECREF(u); } diff --git a/Python/ast.c b/Python/ast.c index 51175cdb55e6..675063eff068 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -4104,6 +4104,9 @@ parsenumber(struct compiling *c, const char *s) } /* Create a duplicate without underscores. */ dup = PyMem_Malloc(strlen(s) + 1); + if (dup == NULL) { + return PyErr_NoMemory(); + } end = dup; for (; *s; s++) { if (*s != '_') { @@ -4338,8 +4341,10 @@ fstring_compile_expr(const char *expr_start, const char *expr_end, len = expr_end - expr_start; /* Allocate 3 extra bytes: open paren, close paren, null byte. */ str = PyMem_RawMalloc(len + 3); - if (str == NULL) + if (str == NULL) { + PyErr_NoMemory(); return NULL; + } str[0] = '('; memcpy(str+1, expr_start, len); diff --git a/Python/marshal.c b/Python/marshal.c index 91a57c293b09..dbe75e3ba291 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -677,11 +677,12 @@ r_string(Py_ssize_t n, RFILE *p) p->buf_size = n; } else if (p->buf_size < n) { - p->buf = PyMem_REALLOC(p->buf, n); - if (p->buf == NULL) { + char *tmp = PyMem_REALLOC(p->buf, n); + if (tmp == NULL) { PyErr_NoMemory(); return NULL; } + p->buf = tmp; p->buf_size = n; } diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 1c635602340a..509048ccf55c 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -398,6 +398,9 @@ _Py_string_to_number_with_underscores( } dup = PyMem_Malloc(orig_len + 1); + if (dup == NULL) { + return PyErr_NoMemory(); + } end = dup; prev = '\0'; last = s + orig_len; @@ -433,8 +436,8 @@ _Py_string_to_number_with_underscores( error: PyMem_Free(dup); PyErr_Format(PyExc_ValueError, - "could not convert string to %s: " - "%R", what, obj); + "could not convert string to %s: " + "%R", what, obj); return NULL; } From webhook-mailer at python.org Fri Dec 7 11:54:26 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 07 Dec 2018 16:54:26 -0000 Subject: [Python-checkins] bpo-9566: Fix compiler warnings in peephole.c (GH-10652) Message-ID: https://github.com/python/cpython/commit/028f0ef4f3111d2b3fc5b971642e337ba7990873 commit: 028f0ef4f3111d2b3fc5b971642e337ba7990873 branch: master author: Victor Stinner committer: GitHub date: 2018-12-07T17:54:18+01:00 summary: bpo-9566: Fix compiler warnings in peephole.c (GH-10652) files: M Python/peephole.c diff --git a/Python/peephole.c b/Python/peephole.c index 77d134939f8d..cc244aa433ee 100644 --- a/Python/peephole.c +++ b/Python/peephole.c @@ -152,6 +152,15 @@ fold_tuple_on_constants(_Py_CODEUNIT *codestr, Py_ssize_t codelen, PyTuple_SET_ITEM(newconst, i, constant); } + Py_ssize_t index = PyList_GET_SIZE(consts); +#if SIZEOF_SIZE_T > SIZEOF_INT + if ((size_t)index >= UINT_MAX - 1) { + Py_DECREF(newconst); + PyErr_SetString(PyExc_OverflowError, "too many constants"); + return -1; + } +#endif + /* Append folded constant onto consts */ if (PyList_Append(consts, newconst)) { Py_DECREF(newconst); @@ -160,7 +169,7 @@ fold_tuple_on_constants(_Py_CODEUNIT *codestr, Py_ssize_t codelen, Py_DECREF(newconst); return copy_op_arg(codestr, c_start, LOAD_CONST, - PyList_GET_SIZE(consts)-1, opcode_end); + (unsigned int)index, opcode_end); } static unsigned int * @@ -221,7 +230,7 @@ PyObject * PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, PyObject *lnotab_obj) { - Py_ssize_t h, i, nexti, op_start, codelen, tgt; + Py_ssize_t h, i, nexti, op_start, tgt; unsigned int j, nops; unsigned char opcode, nextop; _Py_CODEUNIT *codestr = NULL; @@ -249,17 +258,22 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, the peephole optimizer doesn't modify line numbers. */ assert(PyBytes_Check(code)); - codelen = PyBytes_GET_SIZE(code); - assert(codelen % sizeof(_Py_CODEUNIT) == 0); + Py_ssize_t codesize = PyBytes_GET_SIZE(code); + assert(codesize % sizeof(_Py_CODEUNIT) == 0); + Py_ssize_t codelen = codesize / sizeof(_Py_CODEUNIT); + if (codelen > INT_MAX) { + /* Python assembler is limited to INT_MAX: see assembler.a_offset in + compile.c. */ + goto exitUnchanged; + } /* Make a modifiable copy of the code string */ - codestr = (_Py_CODEUNIT *)PyMem_Malloc(codelen); + codestr = (_Py_CODEUNIT *)PyMem_Malloc(codesize); if (codestr == NULL) { PyErr_NoMemory(); goto exitError; } - memcpy(codestr, PyBytes_AS_STRING(code), codelen); - codelen /= sizeof(_Py_CODEUNIT); + memcpy(codestr, PyBytes_AS_STRING(code), codesize); blocks = markblocks(codestr, codelen); if (blocks == NULL) @@ -357,7 +371,11 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, jump past it), and all conditional jumps pop their argument when they're not taken (so change the first jump to pop its argument when it's taken). */ - h = set_arg(codestr, i, (tgt + 1) * sizeof(_Py_CODEUNIT)); + Py_ssize_t arg = (tgt + 1); + /* cannot overflow: codelen <= INT_MAX */ + assert((size_t)arg <= UINT_MAX / sizeof(_Py_CODEUNIT)); + arg *= sizeof(_Py_CODEUNIT); + h = set_arg(codestr, i, (unsigned int)arg); j = opcode == JUMP_IF_TRUE_OR_POP ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE; } @@ -383,17 +401,20 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, codestr[op_start] = PACKOPARG(RETURN_VALUE, 0); fill_nops(codestr, op_start + 1, i + 1); } else if (UNCONDITIONAL_JUMP(_Py_OPCODE(codestr[tgt]))) { - j = GETJUMPTGT(codestr, tgt); + size_t arg = GETJUMPTGT(codestr, tgt); if (opcode == JUMP_FORWARD) { /* JMP_ABS can go backwards */ opcode = JUMP_ABSOLUTE; } else if (!ABSOLUTE_JUMP(opcode)) { - if ((Py_ssize_t)j < i + 1) { + if (arg < (size_t)(i + 1)) { break; /* No backward relative jumps */ } - j -= i + 1; /* Calc relative jump addr */ + arg -= i + 1; /* Calc relative jump addr */ } - j *= sizeof(_Py_CODEUNIT); - copy_op_arg(codestr, op_start, opcode, j, i + 1); + /* cannot overflow: codelen <= INT_MAX */ + assert(arg <= (UINT_MAX / sizeof(_Py_CODEUNIT))); + arg *= sizeof(_Py_CODEUNIT); + copy_op_arg(codestr, op_start, opcode, + (unsigned int)arg, i + 1); } break; @@ -427,11 +448,14 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, /* Fixup lnotab */ for (i = 0, nops = 0; i < codelen; i++) { - assert(i - nops <= INT_MAX); + size_t block = (size_t)i - nops; + /* cannot overflow: codelen <= INT_MAX */ + assert(block <= UINT_MAX); /* original code offset => new code offset */ - blocks[i] = i - nops; - if (_Py_OPCODE(codestr[i]) == NOP) + blocks[i] = (unsigned int)block; + if (_Py_OPCODE(codestr[i]) == NOP) { nops++; + } } cum_orig_offset = 0; last_offset = 0; @@ -476,12 +500,14 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names, j *= sizeof(_Py_CODEUNIT); break; } - nexti = i - op_start + 1; - if (instrsize(j) > nexti) + Py_ssize_t ilen = i - op_start + 1; + if (instrsize(j) > ilen) { goto exitUnchanged; - /* If instrsize(j) < nexti, we'll emit EXTENDED_ARG 0 */ - write_op_arg(codestr + h, opcode, j, nexti); - h += nexti; + } + assert(ilen <= INT_MAX); + /* If instrsize(j) < ilen, we'll emit EXTENDED_ARG 0 */ + write_op_arg(codestr + h, opcode, j, (int)ilen); + h += ilen; } assert(h + (Py_ssize_t)nops == codelen); From webhook-mailer at python.org Fri Dec 7 18:30:45 2018 From: webhook-mailer at python.org (Brett Cannon) Date: Fri, 07 Dec 2018 23:30:45 -0000 Subject: [Python-checkins] bpo-33747: Avoid mutating the global sys.modules dict in unittest.mock tests (GH-8520) Message-ID: https://github.com/python/cpython/commit/3cf74384b53b998fa846dc2590cedf9ad2a0d5fd commit: 3cf74384b53b998fa846dc2590cedf9ad2a0d5fd branch: master author: Anirudha Bose committer: Brett Cannon date: 2018-12-07T15:30:42-08:00 summary: bpo-33747: Avoid mutating the global sys.modules dict in unittest.mock tests (GH-8520) files: M Lib/unittest/test/testmock/testpatch.py diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py index fe4ecefd4405..f05225730daf 100644 --- a/Lib/unittest/test/testmock/testpatch.py +++ b/Lib/unittest/test/testmock/testpatch.py @@ -9,6 +9,7 @@ from unittest.test.testmock import support from unittest.test.testmock.support import SomeClass, is_instance +from test.test_importlib.util import uncache from unittest.mock import ( NonCallableMock, CallableMixin, sentinel, MagicMock, Mock, NonCallableMagicMock, patch, _patch, @@ -1660,20 +1661,19 @@ def test_mock_calls_with_patch(self): def test_patch_imports_lazily(self): - sys.modules.pop('squizz', None) - p1 = patch('squizz.squozz') self.assertRaises(ImportError, p1.start) - squizz = Mock() - squizz.squozz = 6 - sys.modules['squizz'] = squizz - p1 = patch('squizz.squozz') - squizz.squozz = 3 - p1.start() - p1.stop() - self.assertEqual(squizz.squozz, 3) + with uncache('squizz'): + squizz = Mock() + sys.modules['squizz'] = squizz + squizz.squozz = 6 + p1 = patch('squizz.squozz') + squizz.squozz = 3 + p1.start() + p1.stop() + self.assertEqual(squizz.squozz, 3) def test_patch_propogrates_exc_on_exit(self): class holder: @@ -1696,7 +1696,12 @@ def with_custom_patch(target): def test(mock): raise RuntimeError - self.assertRaises(RuntimeError, test) + with uncache('squizz'): + squizz = Mock() + sys.modules['squizz'] = squizz + + self.assertRaises(RuntimeError, test) + self.assertIs(holder.exc_info[0], RuntimeError) self.assertIsNotNone(holder.exc_info[1], 'exception value not propgated') From solipsis at pitrou.net Sat Dec 8 04:11:57 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 08 Dec 2018 09:11:57 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=77 Message-ID: <20181208091157.1.71F6BD0EA98DE11B@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [0, 7, 0] memory blocks, sum=7 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_forkserver leaked [44, 0, 0] references, sum=44 test_multiprocessing_forkserver leaked [20, 1, 0] memory blocks, sum=21 test_multiprocessing_forkserver leaked [2, 0, 0] file descriptors, sum=2 test_multiprocessing_spawn leaked [0, -2, 1] memory blocks, sum=-1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogG20Wfv', '--timeout', '7200'] From webhook-mailer at python.org Sat Dec 8 06:25:07 2018 From: webhook-mailer at python.org (Chris Withers) Date: Sat, 08 Dec 2018 11:25:07 -0000 Subject: [Python-checkins] bpo-35330: Don't call the wrapped object if `side_effect` is set (GH10973) Message-ID: https://github.com/python/cpython/commit/f05df0a4b679d0acfd0b1fe6187ba2d553b37afa commit: f05df0a4b679d0acfd0b1fe6187ba2d553b37afa branch: master author: Mario Corchero committer: Chris Withers date: 2018-12-08T11:25:02Z summary: bpo-35330: Don't call the wrapped object if `side_effect` is set (GH10973) * tests: Further validate `wraps` functionality in `unittest.mock.Mock` Add more tests to validate how `wraps` interacts with other features of mocks. * Don't call the wrapped object if `side_effect` is set When a object is wrapped using `Mock(wraps=...)`, if an user sets a `side_effect` in one of their methods, return the value of `side_effect` and don't call the original object. * Refactor what to be called on `mock_call` When a `Mock` is called, it should return looking up in the following order: `side_effect`, `return_value`, `wraps`. If any of the first two return `mock.DEFAULT`, lookup in the next option. It makes no sense to check for `wraps` returning default, as it is supposed to be the original implementation and there is nothing to fallback to. files: A Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.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 b75a1fa5cf1d..61ed80b976a7 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1027,28 +1027,27 @@ def _mock_call(_mock_self, *args, **kwargs): break seen.add(_new_parent_id) - ret_val = DEFAULT effect = self.side_effect if effect is not None: if _is_exception(effect): raise effect - - if not _callable(effect): + elif not _callable(effect): result = next(effect) if _is_exception(result): raise result - if result is DEFAULT: - result = self.return_value + else: + result = effect(*args, **kwargs) + + if result is not DEFAULT: return result - ret_val = effect(*args, **kwargs) + if self._mock_return_value is not DEFAULT: + return self.return_value - if (self._mock_wraps is not None and - self._mock_return_value is DEFAULT): + if self._mock_wraps is not None: return self._mock_wraps(*args, **kwargs) - if ret_val is DEFAULT: - ret_val = self.return_value - return ret_val + + return self.return_value diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 0a638b7c2107..193ae9f9acbf 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -558,6 +558,16 @@ def test_wraps_calls(self): real.assert_called_with(1, 2, fish=3) + def test_wraps_prevents_automatic_creation_of_mocks(self): + class Real(object): + pass + + real = Real() + mock = Mock(wraps=real) + + self.assertRaises(AttributeError, lambda: mock.new_attr()) + + def test_wraps_call_with_nondefault_return_value(self): real = Mock() @@ -584,6 +594,118 @@ class Real(object): self.assertEqual(result, Real.attribute.frog()) + def test_customize_wrapped_object_with_side_effect_iterable_with_default(self): + class Real(object): + def method(self): + return sentinel.ORIGINAL_VALUE + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, DEFAULT] + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.ORIGINAL_VALUE) + self.assertRaises(StopIteration, mock.method) + + + def test_customize_wrapped_object_with_side_effect_iterable(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2] + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.VALUE2) + self.assertRaises(StopIteration, mock.method) + + + def test_customize_wrapped_object_with_side_effect_exception(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = RuntimeError + + self.assertRaises(RuntimeError, mock.method) + + + def test_customize_wrapped_object_with_side_effect_function(self): + class Real(object): + def method(self): + raise NotImplementedError() + + def side_effect(): + return sentinel.VALUE + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = side_effect + + self.assertEqual(mock.method(), sentinel.VALUE) + + + def test_customize_wrapped_object_with_return_value(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.return_value = sentinel.VALUE + + self.assertEqual(mock.method(), sentinel.VALUE) + + + def test_customize_wrapped_object_with_return_value_and_side_effect(self): + # side_effect should always take precedence over return_value. + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2] + mock.method.return_value = sentinel.WRONG_VALUE + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.VALUE2) + self.assertRaises(StopIteration, mock.method) + + + def test_customize_wrapped_object_with_return_value_and_side_effect2(self): + # side_effect can return DEFAULT to default to return_value + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = lambda: DEFAULT + mock.method.return_value = sentinel.VALUE + + self.assertEqual(mock.method(), sentinel.VALUE) + + + def test_customize_wrapped_object_with_return_value_and_side_effect_default(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, DEFAULT] + mock.method.return_value = sentinel.RETURN + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.RETURN) + self.assertRaises(StopIteration, mock.method) + + def test_exceptional_side_effect(self): mock = Mock(side_effect=AttributeError) self.assertRaises(AttributeError, mock) diff --git a/Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.rst b/Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.rst new file mode 100644 index 000000000000..24d0ab84fb16 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.rst @@ -0,0 +1,4 @@ +When a :class:`Mock` instance was used to wrap an object, if `side_effect` +is used in one of the mocks of it methods, don't call the original +implementation and return the result of using the side effect the same way +that it is done with return_value. From webhook-mailer at python.org Sat Dec 8 06:41:58 2018 From: webhook-mailer at python.org (Chris Withers) Date: Sat, 08 Dec 2018 11:41:58 -0000 Subject: [Python-checkins] bpo-35330: Don't call the wrapped object if `side_effect` is set (GH11034) Message-ID: https://github.com/python/cpython/commit/12b9fb603eea9298c835bae5b8742db4fa52892e commit: 12b9fb603eea9298c835bae5b8742db4fa52892e branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-08T11:41:52Z summary: bpo-35330: Don't call the wrapped object if `side_effect` is set (GH11034) * tests: Further validate `wraps` functionality in `unittest.mock.Mock` Add more tests to validate how `wraps` interacts with other features of mocks. * Don't call the wrapped object if `side_effect` is set When a object is wrapped using `Mock(wraps=...)`, if an user sets a `side_effect` in one of their methods, return the value of `side_effect` and don't call the original object. * Refactor what to be called on `mock_call` When a `Mock` is called, it should return looking up in the following order: `side_effect`, `return_value`, `wraps`. If any of the first two return `mock.DEFAULT`, lookup in the next option. It makes no sense to check for `wraps` returning default, as it is supposed to be the original implementation and there is nothing to fallback to. (cherry picked from commit f05df0a4b679d0acfd0b1fe6187ba2d553b37afa) Co-authored-by: Mario Corchero files: A Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.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 707ef0b734d8..645d100960ef 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -993,28 +993,27 @@ def _mock_call(_mock_self, *args, **kwargs): break seen.add(_new_parent_id) - ret_val = DEFAULT effect = self.side_effect if effect is not None: if _is_exception(effect): raise effect - - if not _callable(effect): + elif not _callable(effect): result = next(effect) if _is_exception(result): raise result - if result is DEFAULT: - result = self.return_value + else: + result = effect(*args, **kwargs) + + if result is not DEFAULT: return result - ret_val = effect(*args, **kwargs) + if self._mock_return_value is not DEFAULT: + return self.return_value - if (self._mock_wraps is not None and - self._mock_return_value is DEFAULT): + if self._mock_wraps is not None: return self._mock_wraps(*args, **kwargs) - if ret_val is DEFAULT: - ret_val = self.return_value - return ret_val + + return self.return_value diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 14b1ecc84b33..5e873569e26e 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -549,6 +549,16 @@ def test_wraps_calls(self): real.assert_called_with(1, 2, fish=3) + def test_wraps_prevents_automatic_creation_of_mocks(self): + class Real(object): + pass + + real = Real() + mock = Mock(wraps=real) + + self.assertRaises(AttributeError, lambda: mock.new_attr()) + + def test_wraps_call_with_nondefault_return_value(self): real = Mock() @@ -575,6 +585,118 @@ class Real(object): self.assertEqual(result, Real.attribute.frog()) + def test_customize_wrapped_object_with_side_effect_iterable_with_default(self): + class Real(object): + def method(self): + return sentinel.ORIGINAL_VALUE + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, DEFAULT] + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.ORIGINAL_VALUE) + self.assertRaises(StopIteration, mock.method) + + + def test_customize_wrapped_object_with_side_effect_iterable(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2] + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.VALUE2) + self.assertRaises(StopIteration, mock.method) + + + def test_customize_wrapped_object_with_side_effect_exception(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = RuntimeError + + self.assertRaises(RuntimeError, mock.method) + + + def test_customize_wrapped_object_with_side_effect_function(self): + class Real(object): + def method(self): + raise NotImplementedError() + + def side_effect(): + return sentinel.VALUE + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = side_effect + + self.assertEqual(mock.method(), sentinel.VALUE) + + + def test_customize_wrapped_object_with_return_value(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.return_value = sentinel.VALUE + + self.assertEqual(mock.method(), sentinel.VALUE) + + + def test_customize_wrapped_object_with_return_value_and_side_effect(self): + # side_effect should always take precedence over return_value. + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2] + mock.method.return_value = sentinel.WRONG_VALUE + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.VALUE2) + self.assertRaises(StopIteration, mock.method) + + + def test_customize_wrapped_object_with_return_value_and_side_effect2(self): + # side_effect can return DEFAULT to default to return_value + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = lambda: DEFAULT + mock.method.return_value = sentinel.VALUE + + self.assertEqual(mock.method(), sentinel.VALUE) + + + def test_customize_wrapped_object_with_return_value_and_side_effect_default(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, DEFAULT] + mock.method.return_value = sentinel.RETURN + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.RETURN) + self.assertRaises(StopIteration, mock.method) + + def test_exceptional_side_effect(self): mock = Mock(side_effect=AttributeError) self.assertRaises(AttributeError, mock) diff --git a/Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.rst b/Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.rst new file mode 100644 index 000000000000..24d0ab84fb16 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.rst @@ -0,0 +1,4 @@ +When a :class:`Mock` instance was used to wrap an object, if `side_effect` +is used in one of the mocks of it methods, don't call the original +implementation and return the result of using the side effect the same way +that it is done with return_value. From webhook-mailer at python.org Sat Dec 8 06:47:04 2018 From: webhook-mailer at python.org (Chris Withers) Date: Sat, 08 Dec 2018 11:47:04 -0000 Subject: [Python-checkins] bpo-35330: Don't call the wrapped object if `side_effect` is set (GH11035) Message-ID: https://github.com/python/cpython/commit/ee2c5a8e2dcf662048dbcf4e49af9b4aaf81f7d3 commit: ee2c5a8e2dcf662048dbcf4e49af9b4aaf81f7d3 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-08T11:47:01Z summary: bpo-35330: Don't call the wrapped object if `side_effect` is set (GH11035) * tests: Further validate `wraps` functionality in `unittest.mock.Mock` Add more tests to validate how `wraps` interacts with other features of mocks. * Don't call the wrapped object if `side_effect` is set When a object is wrapped using `Mock(wraps=...)`, if an user sets a `side_effect` in one of their methods, return the value of `side_effect` and don't call the original object. * Refactor what to be called on `mock_call` When a `Mock` is called, it should return looking up in the following order: `side_effect`, `return_value`, `wraps`. If any of the first two return `mock.DEFAULT`, lookup in the next option. It makes no sense to check for `wraps` returning default, as it is supposed to be the original implementation and there is nothing to fallback to. (cherry picked from commit f05df0a4b679d0acfd0b1fe6187ba2d553b37afa) Co-authored-by: Mario Corchero files: A Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.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 0a6535d0fc44..f641e38dc54b 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1005,28 +1005,27 @@ def _mock_call(_mock_self, *args, **kwargs): break seen.add(_new_parent_id) - ret_val = DEFAULT effect = self.side_effect if effect is not None: if _is_exception(effect): raise effect - - if not _callable(effect): + elif not _callable(effect): result = next(effect) if _is_exception(result): raise result - if result is DEFAULT: - result = self.return_value + else: + result = effect(*args, **kwargs) + + if result is not DEFAULT: return result - ret_val = effect(*args, **kwargs) + if self._mock_return_value is not DEFAULT: + return self.return_value - if (self._mock_wraps is not None and - self._mock_return_value is DEFAULT): + if self._mock_wraps is not None: return self._mock_wraps(*args, **kwargs) - if ret_val is DEFAULT: - ret_val = self.return_value - return ret_val + + return self.return_value diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 401a01277b06..49ecbb446629 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -549,6 +549,16 @@ def test_wraps_calls(self): real.assert_called_with(1, 2, fish=3) + def test_wraps_prevents_automatic_creation_of_mocks(self): + class Real(object): + pass + + real = Real() + mock = Mock(wraps=real) + + self.assertRaises(AttributeError, lambda: mock.new_attr()) + + def test_wraps_call_with_nondefault_return_value(self): real = Mock() @@ -575,6 +585,118 @@ class Real(object): self.assertEqual(result, Real.attribute.frog()) + def test_customize_wrapped_object_with_side_effect_iterable_with_default(self): + class Real(object): + def method(self): + return sentinel.ORIGINAL_VALUE + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, DEFAULT] + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.ORIGINAL_VALUE) + self.assertRaises(StopIteration, mock.method) + + + def test_customize_wrapped_object_with_side_effect_iterable(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2] + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.VALUE2) + self.assertRaises(StopIteration, mock.method) + + + def test_customize_wrapped_object_with_side_effect_exception(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = RuntimeError + + self.assertRaises(RuntimeError, mock.method) + + + def test_customize_wrapped_object_with_side_effect_function(self): + class Real(object): + def method(self): + raise NotImplementedError() + + def side_effect(): + return sentinel.VALUE + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = side_effect + + self.assertEqual(mock.method(), sentinel.VALUE) + + + def test_customize_wrapped_object_with_return_value(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.return_value = sentinel.VALUE + + self.assertEqual(mock.method(), sentinel.VALUE) + + + def test_customize_wrapped_object_with_return_value_and_side_effect(self): + # side_effect should always take precedence over return_value. + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2] + mock.method.return_value = sentinel.WRONG_VALUE + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.VALUE2) + self.assertRaises(StopIteration, mock.method) + + + def test_customize_wrapped_object_with_return_value_and_side_effect2(self): + # side_effect can return DEFAULT to default to return_value + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = lambda: DEFAULT + mock.method.return_value = sentinel.VALUE + + self.assertEqual(mock.method(), sentinel.VALUE) + + + def test_customize_wrapped_object_with_return_value_and_side_effect_default(self): + class Real(object): + def method(self): + raise NotImplementedError() + + real = Real() + mock = Mock(wraps=real) + mock.method.side_effect = [sentinel.VALUE1, DEFAULT] + mock.method.return_value = sentinel.RETURN + + self.assertEqual(mock.method(), sentinel.VALUE1) + self.assertEqual(mock.method(), sentinel.RETURN) + self.assertRaises(StopIteration, mock.method) + + def test_exceptional_side_effect(self): mock = Mock(side_effect=AttributeError) self.assertRaises(AttributeError, mock) diff --git a/Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.rst b/Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.rst new file mode 100644 index 000000000000..24d0ab84fb16 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-06-00-43-13.bpo-35330.abB4BN.rst @@ -0,0 +1,4 @@ +When a :class:`Mock` instance was used to wrap an object, if `side_effect` +is used in one of the mocks of it methods, don't call the original +implementation and return the result of using the side effect the same way +that it is done with return_value. From webhook-mailer at python.org Sat Dec 8 09:17:03 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sat, 08 Dec 2018 14:17:03 -0000 Subject: [Python-checkins] bpo-35441: Remove dead and buggy code related to PyList_SetItem(). (GH-11033) Message-ID: https://github.com/python/cpython/commit/99d56b53560b3867844472ae381fb3f858760621 commit: 99d56b53560b3867844472ae381fb3f858760621 branch: master author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-08T16:16:55+02:00 summary: bpo-35441: Remove dead and buggy code related to PyList_SetItem(). (GH-11033) In _localemodule.c and selectmodule.c, remove dead code that would cause double decrefs if run. In addition, replace PyList_SetItem() with PyList_SET_ITEM() in cases where a new list is populated and there is no possibility of an error. In addition, check if the list changed size in the loop in array_array_fromlist(). files: M Modules/_localemodule.c M Modules/arraymodule.c M Modules/readline.c M Modules/selectmodule.c M PC/winreg.c M Python/ceval.c M Python/sysmodule.c diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index 4202cc401414..96ca68af178c 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -71,20 +71,13 @@ copy_grouping(const char* s) do { i++; val = PyLong_FromLong(s[i]); - if (!val) - break; - if (PyList_SetItem(result, i, val)) { - Py_DECREF(val); - val = NULL; - break; + if (val == NULL) { + Py_DECREF(result); + return NULL; } + PyList_SET_ITEM(result, i, val); } while (s[i] != '\0' && s[i] != CHAR_MAX); - if (!val) { - Py_DECREF(result); - return NULL; - } - return result; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 6a9ff3ec6252..aa7a4fb23c68 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1544,12 +1544,18 @@ array_array_fromlist(arrayobject *self, PyObject *list) if (array_resize(self, old_size + n) == -1) return NULL; for (i = 0; i < n; i++) { - PyObject *v = PyList_GetItem(list, i); + PyObject *v = PyList_GET_ITEM(list, i); if ((*self->ob_descr->setitem)(self, Py_SIZE(self) - n + i, v) != 0) { array_resize(self, old_size); return NULL; } + if (n != PyList_GET_SIZE(list)) { + PyErr_SetString(PyExc_RuntimeError, + "list changed size during iteration"); + array_resize(self, old_size); + return NULL; + } } } Py_RETURN_NONE; @@ -1574,8 +1580,7 @@ array_array_tolist_impl(arrayobject *self) PyObject *v = getarrayitem((PyObject *)self, i); if (v == NULL) goto error; - if (PyList_SetItem(list, i, v) < 0) - goto error; + PyList_SET_ITEM(list, i, v); } return list; diff --git a/Modules/readline.c b/Modules/readline.c index 7f366aabfb4f..57335fe911bf 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -936,8 +936,7 @@ on_completion_display_matches_hook(char **matches, s = decode(matches[i+1]); if (s == NULL) goto error; - if (PyList_SetItem(m, i, s) == -1) - goto error; + PyList_SET_ITEM(m, i, s); } sub = decode(matches[0]); r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook, diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index fe69cd58dc6a..7f62ab111d1f 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -693,10 +693,7 @@ select_poll_poll_impl(pollObject *self, PyObject *timeout_obj) goto error; } PyTuple_SET_ITEM(value, 1, num); - if ((PyList_SetItem(result_list, j, value)) == -1) { - Py_DECREF(value); - goto error; - } + PyList_SET_ITEM(result_list, j, value); i++; } return result_list; @@ -986,10 +983,7 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj) Py_DECREF(num2); if (value == NULL) goto error; - if ((PyList_SetItem(result_list, i, value)) == -1) { - Py_DECREF(value); - goto error; - } + PyList_SET_ITEM(result_list, i, value); } return result_list; diff --git a/PC/winreg.c b/PC/winreg.c index 0198097a1c92..4505c314d7b4 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -756,9 +756,13 @@ Reg2Py(BYTE *retDataBuf, DWORD retDataSize, DWORD typ) PyMem_Free(str); return NULL; } - PyList_SetItem(obData, - index, - PyUnicode_FromWideChar(str[index], len)); + PyObject *uni = PyUnicode_FromWideChar(str[index], len); + if (uni == NULL) { + Py_DECREF(obData); + PyMem_Free(str); + return NULL; + } + PyList_SET_ITEM(obData, index, uni); } PyMem_Free(str); diff --git a/Python/ceval.c b/Python/ceval.c index a4273adee48d..7ffc68a1e1fc 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5124,7 +5124,7 @@ getarray(long a[256]) Py_DECREF(l); return NULL; } - PyList_SetItem(l, i, x); + PyList_SET_ITEM(l, i, x); } for (i = 0; i < 256; i++) a[i] = 0; @@ -5146,7 +5146,7 @@ _Py_GetDXProfile(PyObject *self, PyObject *args) Py_DECREF(l); return NULL; } - PyList_SetItem(l, i, x); + PyList_SET_ITEM(l, i, x); } return l; #endif diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 0ca3de39f8bd..49fa3842b583 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2594,7 +2594,7 @@ makepathobject(const wchar_t *path, wchar_t delim) Py_DECREF(v); return NULL; } - PyList_SetItem(v, i, w); + PyList_SET_ITEM(v, i, w); if (*p == '\0') break; path = p+1; From webhook-mailer at python.org Sat Dec 8 09:34:54 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 08 Dec 2018 14:34:54 -0000 Subject: [Python-checkins] bpo-35441: Remove dead and buggy code related to PyList_SetItem(). (GH-11033) Message-ID: https://github.com/python/cpython/commit/8b7d8ac09cc0f736d0c3a39d838814d7ae253021 commit: 8b7d8ac09cc0f736d0c3a39d838814d7ae253021 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-08T06:34:49-08:00 summary: bpo-35441: Remove dead and buggy code related to PyList_SetItem(). (GH-11033) In _localemodule.c and selectmodule.c, remove dead code that would cause double decrefs if run. In addition, replace PyList_SetItem() with PyList_SET_ITEM() in cases where a new list is populated and there is no possibility of an error. In addition, check if the list changed size in the loop in array_array_fromlist(). (cherry picked from commit 99d56b53560b3867844472ae381fb3f858760621) Co-authored-by: Zackery Spytz files: M Modules/_localemodule.c M Modules/arraymodule.c M Modules/readline.c M Modules/selectmodule.c M PC/winreg.c M Python/ceval.c M Python/sysmodule.c diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index 716a7306d3dc..8efda35dd4e5 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -70,20 +70,13 @@ copy_grouping(const char* s) do { i++; val = PyLong_FromLong(s[i]); - if (!val) - break; - if (PyList_SetItem(result, i, val)) { - Py_DECREF(val); - val = NULL; - break; + if (val == NULL) { + Py_DECREF(result); + return NULL; } + PyList_SET_ITEM(result, i, val); } while (s[i] != '\0' && s[i] != CHAR_MAX); - if (!val) { - Py_DECREF(result); - return NULL; - } - return result; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 900c374c585e..7b4a4a3ad216 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1544,12 +1544,18 @@ array_array_fromlist(arrayobject *self, PyObject *list) if (array_resize(self, old_size + n) == -1) return NULL; for (i = 0; i < n; i++) { - PyObject *v = PyList_GetItem(list, i); + PyObject *v = PyList_GET_ITEM(list, i); if ((*self->ob_descr->setitem)(self, Py_SIZE(self) - n + i, v) != 0) { array_resize(self, old_size); return NULL; } + if (n != PyList_GET_SIZE(list)) { + PyErr_SetString(PyExc_RuntimeError, + "list changed size during iteration"); + array_resize(self, old_size); + return NULL; + } } } Py_RETURN_NONE; @@ -1574,8 +1580,7 @@ array_array_tolist_impl(arrayobject *self) PyObject *v = getarrayitem((PyObject *)self, i); if (v == NULL) goto error; - if (PyList_SetItem(list, i, v) < 0) - goto error; + PyList_SET_ITEM(list, i, v); } return list; diff --git a/Modules/readline.c b/Modules/readline.c index 7756e6b2bc75..fa7e7d18e59e 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -936,8 +936,7 @@ on_completion_display_matches_hook(char **matches, s = decode(matches[i+1]); if (s == NULL) goto error; - if (PyList_SetItem(m, i, s) == -1) - goto error; + PyList_SET_ITEM(m, i, s); } sub = decode(matches[0]); r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook, diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 88679e814495..93d896a37c22 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -652,10 +652,7 @@ poll_poll(pollObject *self, PyObject *args) goto error; } PyTuple_SET_ITEM(value, 1, num); - if ((PyList_SetItem(result_list, j, value)) == -1) { - Py_DECREF(value); - goto error; - } + PyList_SET_ITEM(result_list, j, value); i++; } return result_list; @@ -981,10 +978,7 @@ devpoll_poll(devpollObject *self, PyObject *args) Py_DECREF(num2); if (value == NULL) goto error; - if ((PyList_SetItem(result_list, i, value)) == -1) { - Py_DECREF(value); - goto error; - } + PyList_SET_ITEM(result_list, i, value); } return result_list; diff --git a/PC/winreg.c b/PC/winreg.c index ddaf3b1abc92..6f3106644b2c 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -757,9 +757,13 @@ Reg2Py(BYTE *retDataBuf, DWORD retDataSize, DWORD typ) PyMem_Free(str); return NULL; } - PyList_SetItem(obData, - index, - PyUnicode_FromWideChar(str[index], len)); + PyObject *uni = PyUnicode_FromWideChar(str[index], len); + if (uni == NULL) { + Py_DECREF(obData); + PyMem_Free(str); + return NULL; + } + PyList_SET_ITEM(obData, index, uni); } PyMem_Free(str); diff --git a/Python/ceval.c b/Python/ceval.c index 0d1519bef4f0..e371d345cc74 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5056,7 +5056,7 @@ getarray(long a[256]) Py_DECREF(l); return NULL; } - PyList_SetItem(l, i, x); + PyList_SET_ITEM(l, i, x); } for (i = 0; i < 256; i++) a[i] = 0; @@ -5078,7 +5078,7 @@ _Py_GetDXProfile(PyObject *self, PyObject *args) Py_DECREF(l); return NULL; } - PyList_SetItem(l, i, x); + PyList_SET_ITEM(l, i, x); } return l; #endif diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 498fa91fcc3d..f04403b76aa0 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2544,7 +2544,7 @@ makepathobject(const wchar_t *path, wchar_t delim) Py_DECREF(v); return NULL; } - PyList_SetItem(v, i, w); + PyList_SET_ITEM(v, i, w); if (*p == '\0') break; path = p+1; From webhook-mailer at python.org Sat Dec 8 09:39:41 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sat, 08 Dec 2018 14:39:41 -0000 Subject: [Python-checkins] bpo-35441: Remove dead and buggy code related to PyList_SetItem(). (GH-11033) Message-ID: https://github.com/python/cpython/commit/25555e0fbed15f809a247c7e16ab9d0a0088f806 commit: 25555e0fbed15f809a247c7e16ab9d0a0088f806 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-08T06:39:37-08:00 summary: bpo-35441: Remove dead and buggy code related to PyList_SetItem(). (GH-11033) In _localemodule.c and selectmodule.c, remove dead code that would cause double decrefs if run. In addition, replace PyList_SetItem() with PyList_SET_ITEM() in cases where a new list is populated and there is no possibility of an error. In addition, check if the list changed size in the loop in array_array_fromlist(). (cherry picked from commit 99d56b53560b3867844472ae381fb3f858760621) Co-authored-by: Zackery Spytz files: M Modules/_localemodule.c M Modules/arraymodule.c M Modules/readline.c M Modules/selectmodule.c M PC/winreg.c M Python/ceval.c M Python/sysmodule.c diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index f3421af65f3d..eba8289c301b 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -70,20 +70,13 @@ copy_grouping(const char* s) do { i++; val = PyLong_FromLong(s[i]); - if (!val) - break; - if (PyList_SetItem(result, i, val)) { - Py_DECREF(val); - val = NULL; - break; + if (val == NULL) { + Py_DECREF(result); + return NULL; } + PyList_SET_ITEM(result, i, val); } while (s[i] != '\0' && s[i] != CHAR_MAX); - if (!val) { - Py_DECREF(result); - return NULL; - } - return result; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 9254af59a6a2..bc3f62def3cc 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1499,12 +1499,18 @@ array_array_fromlist(arrayobject *self, PyObject *list) if (array_resize(self, old_size + n) == -1) return NULL; for (i = 0; i < n; i++) { - PyObject *v = PyList_GetItem(list, i); + PyObject *v = PyList_GET_ITEM(list, i); if ((*self->ob_descr->setitem)(self, Py_SIZE(self) - n + i, v) != 0) { array_resize(self, old_size); return NULL; } + if (n != PyList_GET_SIZE(list)) { + PyErr_SetString(PyExc_RuntimeError, + "list changed size during iteration"); + array_resize(self, old_size); + return NULL; + } } } Py_INCREF(Py_None); @@ -1530,8 +1536,7 @@ array_array_tolist_impl(arrayobject *self) PyObject *v = getarrayitem((PyObject *)self, i); if (v == NULL) goto error; - if (PyList_SetItem(list, i, v) < 0) - goto error; + PyList_SET_ITEM(list, i, v); } return list; diff --git a/Modules/readline.c b/Modules/readline.c index 575479cff249..096571548c53 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -947,8 +947,7 @@ on_completion_display_matches_hook(char **matches, s = decode(matches[i+1]); if (s == NULL) goto error; - if (PyList_SetItem(m, i, s) == -1) - goto error; + PyList_SET_ITEM(m, i, s); } sub = decode(matches[0]); r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook, diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 4b9965724915..2bc7b9ff79e5 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -655,10 +655,7 @@ poll_poll(pollObject *self, PyObject *args) goto error; } PyTuple_SET_ITEM(value, 1, num); - if ((PyList_SetItem(result_list, j, value)) == -1) { - Py_DECREF(value); - goto error; - } + PyList_SET_ITEM(result_list, j, value); i++; } return result_list; @@ -984,10 +981,7 @@ devpoll_poll(devpollObject *self, PyObject *args) Py_DECREF(num2); if (value == NULL) goto error; - if ((PyList_SetItem(result_list, i, value)) == -1) { - Py_DECREF(value); - goto error; - } + PyList_SET_ITEM(result_list, i, value); } return result_list; diff --git a/PC/winreg.c b/PC/winreg.c index 5efdc5e0efec..3fde04d746b5 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -757,9 +757,13 @@ Reg2Py(BYTE *retDataBuf, DWORD retDataSize, DWORD typ) PyMem_Free(str); return NULL; } - PyList_SetItem(obData, - index, - PyUnicode_FromWideChar(str[index], len)); + PyObject *uni = PyUnicode_FromWideChar(str[index], len); + if (uni == NULL) { + Py_DECREF(obData); + PyMem_Free(str); + return NULL; + } + PyList_SET_ITEM(obData, index, uni); } PyMem_Free(str); diff --git a/Python/ceval.c b/Python/ceval.c index 50834f82c746..38d1d73845fb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5500,7 +5500,7 @@ getarray(long a[256]) Py_DECREF(l); return NULL; } - PyList_SetItem(l, i, x); + PyList_SET_ITEM(l, i, x); } for (i = 0; i < 256; i++) a[i] = 0; @@ -5522,7 +5522,7 @@ _Py_GetDXProfile(PyObject *self, PyObject *args) Py_DECREF(l); return NULL; } - PyList_SetItem(l, i, x); + PyList_SET_ITEM(l, i, x); } return l; #endif diff --git a/Python/sysmodule.c b/Python/sysmodule.c index b09268b0e538..7d1493cbe145 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2109,7 +2109,7 @@ makepathobject(const wchar_t *path, wchar_t delim) Py_DECREF(v); return NULL; } - PyList_SetItem(v, i, w); + PyList_SET_ITEM(v, i, w); if (*p == '\0') break; path = p+1; From webhook-mailer at python.org Sun Dec 9 00:21:59 2018 From: webhook-mailer at python.org (Ned Deily) Date: Sun, 09 Dec 2018 05:21:59 -0000 Subject: [Python-checkins] [3.7] Doc: Bump sphinx. (GH-10676) (GH-10803) Message-ID: https://github.com/python/cpython/commit/2db96ae7444880d66d4ef65abab8a5e6ff328711 commit: 2db96ae7444880d66d4ef65abab8a5e6ff328711 branch: 3.7 author: Julien Palard committer: Ned Deily date: 2018-12-09T00:21:54-05:00 summary: [3.7] Doc: Bump sphinx. (GH-10676) (GH-10803) files: M .azure-pipelines/docs-steps.yml M .travis.yml diff --git a/.azure-pipelines/docs-steps.yml b/.azure-pipelines/docs-steps.yml index c0404aebdcc5..492e4e34bb2d 100644 --- a/.azure-pipelines/docs-steps.yml +++ b/.azure-pipelines/docs-steps.yml @@ -12,7 +12,7 @@ steps: inputs: versionSpec: '>=3.6' -- script: python -m pip install sphinx~=1.6.1 blurb python-docs-theme +- script: python -m pip install sphinx==1.8.2 blurb python-docs-theme displayName: 'Install build dependencies' - ${{ if ne(parameters.latex, 'true') }}: diff --git a/.travis.yml b/.travis.yml index 27a240f1e684..f44cc741ef46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,7 +53,7 @@ matrix: - cd Doc # Sphinx is pinned so that new versions that introduce new warnings won't suddenly cause build failures. # (Updating the version is fine as long as no warnings are raised by doing so.) - - python -m pip install sphinx~=1.6.1 blurb + - python -m pip install sphinx==1.8.2 blurb script: - make check suspicious html SPHINXOPTS="-q -W -j4" - os: linux From webhook-mailer at python.org Sun Dec 9 00:34:34 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 09 Dec 2018 05:34:34 -0000 Subject: [Python-checkins] [3.7] Doc: Bump sphinx. (GH-10676) (GH-10803) Message-ID: https://github.com/python/cpython/commit/23a98abd4256f931ed89b65ec6babd4f06dbff97 commit: 23a98abd4256f931ed89b65ec6babd4f06dbff97 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-08T21:34:30-08:00 summary: [3.7] Doc: Bump sphinx. (GH-10676) (GH-10803) (cherry picked from commit 2db96ae7444880d66d4ef65abab8a5e6ff328711) Co-authored-by: Julien Palard files: M .azure-pipelines/docs-steps.yml M .travis.yml diff --git a/.azure-pipelines/docs-steps.yml b/.azure-pipelines/docs-steps.yml index c0404aebdcc5..492e4e34bb2d 100644 --- a/.azure-pipelines/docs-steps.yml +++ b/.azure-pipelines/docs-steps.yml @@ -12,7 +12,7 @@ steps: inputs: versionSpec: '>=3.6' -- script: python -m pip install sphinx~=1.6.1 blurb python-docs-theme +- script: python -m pip install sphinx==1.8.2 blurb python-docs-theme displayName: 'Install build dependencies' - ${{ if ne(parameters.latex, 'true') }}: diff --git a/.travis.yml b/.travis.yml index ed4e46c782f7..8250fd91b3d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,7 +43,7 @@ matrix: - cd Doc # Sphinx is pinned so that new versions that introduce new warnings won't suddenly cause build failures. # (Updating the version is fine as long as no warnings are raised by doing so.) - - python -m pip install sphinx~=1.6.1 blurb + - python -m pip install sphinx==1.8.2 blurb script: - make check suspicious html SPHINXOPTS="-q -W -j4" - os: linux From webhook-mailer at python.org Sun Dec 9 00:57:04 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 09 Dec 2018 05:57:04 -0000 Subject: [Python-checkins] [3.7] Doc: Disable smartquotes for zh-tw, zh-cn, fr and ja translations (GH-9423) (GH-10344) Message-ID: https://github.com/python/cpython/commit/b6b77955f2e3363f3271a56ee323c836c81f8997 commit: b6b77955f2e3363f3271a56ee323c836c81f8997 branch: 3.7 author: Julien Palard committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-08T21:56:59-08:00 summary: [3.7] Doc: Disable smartquotes for zh-tw, zh-cn, fr and ja translations (GH-9423) (GH-10344) (cherry picked from commit c03bf0ae794c3bec9b56f38164535fd1f5bfc04a) files: D Doc/docutils.conf M Doc/conf.py diff --git a/Doc/conf.py b/Doc/conf.py index b7bb5a09c50a..124cb7aff6b3 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -34,13 +34,18 @@ # By default, highlight as Python 3. highlight_language = 'python3' -# Require Sphinx 1.2 for build. -needs_sphinx = '1.2' +# Require Sphinx 1.7 for build. +needs_sphinx = '1.7' # Ignore any .rst files in the venv/ directory. venvdir = os.getenv('VENVDIR', 'venv') exclude_patterns = [venvdir+'/*', 'README.rst'] +# Disable Docutils smartquotes for several translations +smartquotes_excludes = { + 'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'], 'builders': ['man', 'text'], +} + # Options for HTML output # ----------------------- diff --git a/Doc/docutils.conf b/Doc/docutils.conf deleted file mode 100644 index bda4f5dc2351..000000000000 --- a/Doc/docutils.conf +++ /dev/null @@ -1,2 +0,0 @@ -[restructuredtext parser] -smartquotes-locales: ja: ""'' From webhook-mailer at python.org Sun Dec 9 01:50:20 2018 From: webhook-mailer at python.org (Ned Deily) Date: Sun, 09 Dec 2018 06:50:20 -0000 Subject: [Python-checkins] bpo-33725: skip test_multiprocessing_fork on macOS (GH-11043) Message-ID: https://github.com/python/cpython/commit/ac218bc5dbfabbd61c76ce8a17de088611e21981 commit: ac218bc5dbfabbd61c76ce8a17de088611e21981 branch: master author: Ned Deily committer: GitHub date: 2018-12-09T01:50:15-05:00 summary: bpo-33725: skip test_multiprocessing_fork on macOS (GH-11043) files: A Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst M Lib/test/test_multiprocessing_fork.py diff --git a/Lib/test/test_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py index 9f22500e8fca..daadcd3cc312 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -1,11 +1,14 @@ import unittest import test._test_multiprocessing +import sys from test import support if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") +if sys.platform == 'darwin': + raise unittest.SkipTest("test may crash on macOS (bpo-33725)") test._test_multiprocessing.install_tests_in_module_dict(globals(), 'fork') diff --git a/Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst b/Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst new file mode 100644 index 000000000000..425048cb37cf --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst @@ -0,0 +1,2 @@ +test_multiprocessing_fork may crash on recent versions of macOS. Until the +issue is resolved, skip the test on macOS. From webhook-mailer at python.org Sun Dec 9 02:00:26 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 09 Dec 2018 07:00:26 -0000 Subject: [Python-checkins] Fix numbered lists in stdtypes.rst. (GH-10989) Message-ID: https://github.com/python/cpython/commit/de9e9b476ec4abfb0b9161cff0e86bb7085ca8c6 commit: de9e9b476ec4abfb0b9161cff0e86bb7085ca8c6 branch: master author: Andre Delfino committer: Serhiy Storchaka date: 2018-12-09T09:00:20+02:00 summary: Fix numbered lists in stdtypes.rst. (GH-10989) files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 6ddf41a74a4c..86e7d68dd2d6 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2142,28 +2142,26 @@ object. [5]_ Otherwise, *values* must be a tuple with exactly the number of items specified by the format string, or a single mapping object (for example, a dictionary). +.. index:: + single: () (parentheses); in printf-style formatting + single: * (asterisk); in printf-style formatting + single: . (dot); in printf-style formatting + A conversion specifier contains two or more characters and has the following components, which must occur in this order: #. The ``'%'`` character, which marks the start of the specifier. -.. index:: - single: () (parentheses); in printf-style formatting - #. Mapping key (optional), consisting of a parenthesised sequence of characters (for example, ``(somename)``). #. Conversion flags (optional), which affect the result of some conversion types. -.. index:: single: * (asterisk); in printf-style formatting - #. Minimum field width (optional). If specified as an ``'*'`` (asterisk), the actual width is read from the next element of the tuple in *values*, and the object to convert comes after the minimum field width and optional precision. -.. index:: single: . (dot); in printf-style formatting - #. Precision (optional), given as a ``'.'`` (dot) followed by the precision. If specified as ``'*'`` (an asterisk), the actual precision is read from the next element of the tuple in *values*, and the value to convert comes after the @@ -3276,28 +3274,26 @@ object. [5]_ Otherwise, *values* must be a tuple with exactly the number of items specified by the format bytes object, or a single mapping object (for example, a dictionary). +.. index:: + single: () (parentheses); in printf-style formatting + single: * (asterisk); in printf-style formatting + single: . (dot); in printf-style formatting + A conversion specifier contains two or more characters and has the following components, which must occur in this order: #. The ``'%'`` character, which marks the start of the specifier. -.. index:: - single: () (parentheses); in printf-style formatting - #. Mapping key (optional), consisting of a parenthesised sequence of characters (for example, ``(somename)``). #. Conversion flags (optional), which affect the result of some conversion types. -.. index:: single: * (asterisk); in printf-style formatting - #. Minimum field width (optional). If specified as an ``'*'`` (asterisk), the actual width is read from the next element of the tuple in *values*, and the object to convert comes after the minimum field width and optional precision. -.. index:: single: . (dot); in printf-style formatting - #. Precision (optional), given as a ``'.'`` (dot) followed by the precision. If specified as ``'*'`` (an asterisk), the actual precision is read from the next element of the tuple in *values*, and the value to convert comes after the From webhook-mailer at python.org Sun Dec 9 02:06:56 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 09 Dec 2018 07:06:56 -0000 Subject: [Python-checkins] bpo-33725: skip test_multiprocessing_fork on macOS (GH-11043) Message-ID: https://github.com/python/cpython/commit/d4bcf13e06d33b8ec66a68db20df34a029e66882 commit: d4bcf13e06d33b8ec66a68db20df34a029e66882 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-08T23:06:53-08:00 summary: bpo-33725: skip test_multiprocessing_fork on macOS (GH-11043) (cherry picked from commit ac218bc5dbfabbd61c76ce8a17de088611e21981) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst M Lib/test/test_multiprocessing_fork.py diff --git a/Lib/test/test_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py index 9f22500e8fca..daadcd3cc312 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -1,11 +1,14 @@ import unittest import test._test_multiprocessing +import sys from test import support if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") +if sys.platform == 'darwin': + raise unittest.SkipTest("test may crash on macOS (bpo-33725)") test._test_multiprocessing.install_tests_in_module_dict(globals(), 'fork') diff --git a/Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst b/Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst new file mode 100644 index 000000000000..425048cb37cf --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst @@ -0,0 +1,2 @@ +test_multiprocessing_fork may crash on recent versions of macOS. Until the +issue is resolved, skip the test on macOS. From webhook-mailer at python.org Sun Dec 9 02:11:34 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 09 Dec 2018 07:11:34 -0000 Subject: [Python-checkins] bpo-33725: skip test_multiprocessing_fork on macOS (GH-11043) Message-ID: https://github.com/python/cpython/commit/df5d884defc8f1a94013ff9beb493f1428bd55b5 commit: df5d884defc8f1a94013ff9beb493f1428bd55b5 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-08T23:11:31-08:00 summary: bpo-33725: skip test_multiprocessing_fork on macOS (GH-11043) (cherry picked from commit ac218bc5dbfabbd61c76ce8a17de088611e21981) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst M Lib/test/test_multiprocessing_fork.py diff --git a/Lib/test/test_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py index 9f22500e8fca..daadcd3cc312 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -1,11 +1,14 @@ import unittest import test._test_multiprocessing +import sys from test import support if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") +if sys.platform == 'darwin': + raise unittest.SkipTest("test may crash on macOS (bpo-33725)") test._test_multiprocessing.install_tests_in_module_dict(globals(), 'fork') diff --git a/Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst b/Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst new file mode 100644 index 000000000000..425048cb37cf --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-09-01-27-29.bpo-33725.TaGayj.rst @@ -0,0 +1,2 @@ +test_multiprocessing_fork may crash on recent versions of macOS. Until the +issue is resolved, skip the test on macOS. From webhook-mailer at python.org Sun Dec 9 02:46:54 2018 From: webhook-mailer at python.org (Ned Deily) Date: Sun, 09 Dec 2018 07:46:54 -0000 Subject: [Python-checkins] bpo-34245: install Python shared library with more standard 0755 mode (GH-8492) Message-ID: https://github.com/python/cpython/commit/25648d05ac3d74c436f951579bbb716372fb8cc7 commit: 25648d05ac3d74c436f951579bbb716372fb8cc7 branch: master author: jdemeyer committer: Ned Deily date: 2018-12-09T02:46:50-05:00 summary: bpo-34245: install Python shared library with more standard 0755 mode (GH-8492) files: A Misc/NEWS.d/next/Build/2018-07-27-09-52-48.bpo-34245.bBV0NI.rst M Makefile.pre.in diff --git a/Makefile.pre.in b/Makefile.pre.in index 02ce0af55ce2..f16eb96418e9 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -69,8 +69,7 @@ INSTALL_SCRIPT= @INSTALL_SCRIPT@ INSTALL_DATA= @INSTALL_DATA@ # Shared libraries must be installed with executable mode on some systems; # rather than figuring out exactly which, we always give them executable mode. -# Also, making them read-only seems to be a good idea... -INSTALL_SHARED= ${INSTALL} -m 555 +INSTALL_SHARED= ${INSTALL} -m 755 MKDIR_P= @MKDIR_P@ diff --git a/Misc/NEWS.d/next/Build/2018-07-27-09-52-48.bpo-34245.bBV0NI.rst b/Misc/NEWS.d/next/Build/2018-07-27-09-52-48.bpo-34245.bBV0NI.rst new file mode 100644 index 000000000000..3822bb0e4f08 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-07-27-09-52-48.bpo-34245.bBV0NI.rst @@ -0,0 +1,2 @@ +The Python shared library is now installed with write permission (mode 0755), +which is the standard way of installing such libraries. From webhook-mailer at python.org Sun Dec 9 03:08:48 2018 From: webhook-mailer at python.org (Ned Deily) Date: Sun, 09 Dec 2018 08:08:48 -0000 Subject: [Python-checkins] bpo-28015: Support LTO build with clang (GH-9908) (GH-10922) Message-ID: https://github.com/python/cpython/commit/f83ee476d48dbeb90ddf3526b04936a49a87973a commit: f83ee476d48dbeb90ddf3526b04936a49a87973a branch: 3.6 author: stratakis committer: Ned Deily date: 2018-12-09T03:08:43-05:00 summary: bpo-28015: Support LTO build with clang (GH-9908) (GH-10922) .o generated by clang in LTO mode actually are LLVM bitcode files, which leads to a few errors during configure/build step: - add lto flags to the BASECFLAGS instead of CFLAGS, as CFLAGS are used to build autoconf test case, and some are not compatible with clang LTO (they assume binary in the .o, not bitcode) - force llvm-ar instead of ar, as ar is not aware of .o files generated by clang -flto (cherry picked from commit 5ad36f9b21a3aa3b2265b1b43d73522cc3322df2) Co-authored-by: serge-sans-paille files: A Misc/NEWS.d/next/Build/2018-10-16-12-22-36.bpo-28015.ylSgFh.rst M aclocal.m4 M configure M configure.ac diff --git a/Misc/NEWS.d/next/Build/2018-10-16-12-22-36.bpo-28015.ylSgFh.rst b/Misc/NEWS.d/next/Build/2018-10-16-12-22-36.bpo-28015.ylSgFh.rst new file mode 100644 index 000000000000..5a7a43c77307 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-10-16-12-22-36.bpo-28015.ylSgFh.rst @@ -0,0 +1 @@ +Have --with-lto works correctly with clang. diff --git a/aclocal.m4 b/aclocal.m4 index 5e29449ae2a1..61b2539d2e9d 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -13,7 +13,7 @@ m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -dnl serial 11 (pkg-config-0.29.1) +dnl serial 11 (pkg-config-0.29) dnl dnl Copyright ? 2004 Scott James Remnant . dnl Copyright ? 2012-2015 Dan Nicholson @@ -55,7 +55,7 @@ dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], -[m4_define([PKG_MACROS_VERSION], [0.29.1]) +[m4_define([PKG_MACROS_VERSION], [0.29]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ diff --git a/configure b/configure index ac29d65a6c08..5a494d987346 100755 --- a/configure +++ b/configure @@ -671,16 +671,18 @@ BASECFLAGS CFLAGS_ALIASING OPT LLVM_PROF_FOUND -target_os -target_vendor -target_cpu -target LLVM_PROFDATA LLVM_PROF_ERR LLVM_PROF_FILE LLVM_PROF_MERGER PGO_PROF_USE_FLAG PGO_PROF_GEN_FLAG +LLVM_AR_FOUND +target_os +target_vendor +target_cpu +target +LLVM_AR DEF_MAKE_RULE DEF_MAKE_ALL_RULE ABIFLAGS @@ -6493,6 +6495,26 @@ else DEF_MAKE_RULE="all" fi +# 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. + +llvm_bin_dir='' +llvm_path="${PATH}" +if test "${CC}" = "clang" +then + clang_bin=`which clang` + # Some systems install clang elsewhere as a symlink to the real path + # which is where the related llvm tools are located. + if test -L "${clang_bin}" + then + clang_dir=`dirname "${clang_bin}"` + clang_bin=`readlink "${clang_bin}"` + llvm_bin_dir="${clang_dir}/"`dirname "${clang_bin}"` + llvm_path="${llvm_path}${PATH_SEPARATOR}${llvm_bin_dir}" + fi +fi + # Enable LTO flags { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-lto" >&5 $as_echo_n "checking for --with-lto... " >&6; } @@ -6518,65 +6540,8 @@ fi if test "$Py_LTO" = 'true' ; then case $CC in *clang*) - case $ac_sys_system in - Darwin*) - # Any changes made here should be reflected in the GCC+Darwin case below - LTOFLAGS="-flto -Wl,-export_dynamic" - ;; - *) - LTOFLAGS="-flto" - ;; - esac - ;; - *gcc*) - case $ac_sys_system in - Darwin*) - LTOFLAGS="-flto -Wl,-export_dynamic" - ;; - *) - LTOFLAGS="-flto -fuse-linker-plugin -ffat-lto-objects -flto-partition=none" - ;; - esac - ;; - esac - if test "$ac_cv_prog_cc_g" = "yes" - then - # bpo-30345: Add -g to LDFLAGS when compiling with LTO - # to get debug symbols. - LTOFLAGS="$LTOFLAGS -g" - fi - - CFLAGS="$CFLAGS $LTOFLAGS" - LDFLAGS="$LDFLAGS $LTOFLAGS" -fi - -# Enable PGO flags. - - - - - -# Make this 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. -llvm_bin_dir='' -llvm_path="${PATH}" -if test "${CC}" = "clang" -then - clang_bin=`which clang` - # Some systems install clang elsewhere as a symlink to the real path - # which is where the related llvm tools are located. - if test -L "${clang_bin}" - then - clang_dir=`dirname "${clang_bin}"` - clang_bin=`readlink "${clang_bin}"` - llvm_bin_dir="${clang_dir}/"`dirname "${clang_bin}"` - llvm_path="${llvm_path}${PATH_SEPARATOR}${llvm_bin_dir}" - fi -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 $as_echo_n "checking target system type... " >&6; } if ${ac_cv_target+:} false; then : $as_echo_n "(cached) " >&6 @@ -6615,6 +6580,163 @@ test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- +# Extract the first word of "$target_alias-llvm-ar", so it can be a program name with args. +set dummy $target_alias-llvm-ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_LLVM_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LLVM_AR in + [\\/]* | ?:[\\/]*) + ac_cv_path_LLVM_AR="$LLVM_AR" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in ${llvm_path} +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_LLVM_AR="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +LLVM_AR=$ac_cv_path_LLVM_AR +if test -n "$LLVM_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LLVM_AR" >&5 +$as_echo "$LLVM_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +if test -z "$ac_cv_path_LLVM_AR"; then + if test "$build" = "$target"; then + ac_pt_LLVM_AR=$LLVM_AR + # Extract the first word of "llvm-ar", so it can be a program name with args. +set dummy llvm-ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_LLVM_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_LLVM_AR in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_LLVM_AR="$ac_pt_LLVM_AR" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in ${llvm_path} +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_LLVM_AR="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_ac_pt_LLVM_AR" && ac_cv_path_ac_pt_LLVM_AR="''" + ;; +esac +fi +ac_pt_LLVM_AR=$ac_cv_path_ac_pt_LLVM_AR +if test -n "$ac_pt_LLVM_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_LLVM_AR" >&5 +$as_echo "$ac_pt_LLVM_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + LLVM_AR=$ac_pt_LLVM_AR + else + LLVM_AR="''" + fi +else + LLVM_AR="$ac_cv_path_LLVM_AR" +fi + + + if test -n "${LLVM_AR}" -a -x "${LLVM_AR}" + then + LLVM_AR_FOUND="found" + else + LLVM_AR_FOUND="not-found" + fi + if test "$ac_sys_system" = "Darwin" -a "${LLVM_AR_FOUND}" = "not-found" + then + found_llvm_ar=`/usr/bin/xcrun -find llvm-ar 2>/dev/null` + if test -n "${found_llvm_ar}" + then + LLVM_AR='/usr/bin/xcrun llvm-ar' + LLVM_AR_FOUND=found + { $as_echo "$as_me:${as_lineno-$LINENO}: llvm-ar found via xcrun: ${LLVM_AR}" >&5 +$as_echo "$as_me: llvm-ar found via xcrun: ${LLVM_AR}" >&6;} + fi + fi + if test $LLVM_AR_FOUND = not-found + then + LLVM_PROFR_ERR=yes + as_fn_error $? "llvm-ar is required for a --with-lto build with clang but could not be found." "$LINENO" 5 + else + LLVM_AR_ERR=no + fi + AR="${LLVM_AR}" + case $ac_sys_system in + Darwin*) + # Any changes made here should be reflected in the GCC+Darwin case below + LTOFLAGS="-flto -Wl,-export_dynamic" + ;; + *) + LTOFLAGS="-flto" + ;; + esac + ;; + *gcc*) + case $ac_sys_system in + Darwin*) + LTOFLAGS="-flto -Wl,-export_dynamic" + ;; + *) + LTOFLAGS="-flto -fuse-linker-plugin -ffat-lto-objects -flto-partition=none" + ;; + esac + ;; + esac + + if test "$ac_cv_prog_cc_g" = "yes" + then + # bpo-30345: Add -g to LDFLAGS when compiling with LTO + # to get debug symbols. + LTOFLAGS="$LTOFLAGS -g" + fi + + BASECFLAGS="$BASECFLAGS $LTOFLAGS" + LDFLAGS="$LDFLAGS $LTOFLAGS" +fi + +# Enable PGO flags. + + + + + + # Extract the first word of "$target_alias-llvm-profdata", so it can be a program name with args. set dummy $target_alias-llvm-profdata; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 diff --git a/configure.ac b/configure.ac index c003025e3d9b..40ecb7d93d8c 100644 --- a/configure.ac +++ b/configure.ac @@ -1308,6 +1308,26 @@ else DEF_MAKE_RULE="all" fi +# 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. + +llvm_bin_dir='' +llvm_path="${PATH}" +if test "${CC}" = "clang" +then + clang_bin=`which clang` + # Some systems install clang elsewhere as a symlink to the real path + # which is where the related llvm tools are located. + if test -L "${clang_bin}" + then + clang_dir=`dirname "${clang_bin}"` + clang_bin=`readlink "${clang_bin}"` + llvm_bin_dir="${clang_dir}/"`dirname "${clang_bin}"` + llvm_path="${llvm_path}${PATH_SEPARATOR}${llvm_bin_dir}" + fi +fi + # Enable LTO flags AC_MSG_CHECKING(for --with-lto) AC_ARG_WITH(lto, AS_HELP_STRING([--with-lto], [Enable Link Time Optimization in any build. Disabled by default.]), @@ -1324,6 +1344,33 @@ fi], if test "$Py_LTO" = 'true' ; then case $CC in *clang*) + AC_SUBST(LLVM_AR) + AC_PATH_TARGET_TOOL(LLVM_AR, llvm-ar, '', ${llvm_path}) + AC_SUBST(LLVM_AR_FOUND) + if test -n "${LLVM_AR}" -a -x "${LLVM_AR}" + then + LLVM_AR_FOUND="found" + else + LLVM_AR_FOUND="not-found" + fi + if test "$ac_sys_system" = "Darwin" -a "${LLVM_AR_FOUND}" = "not-found" + then + found_llvm_ar=`/usr/bin/xcrun -find llvm-ar 2>/dev/null` + if test -n "${found_llvm_ar}" + then + LLVM_AR='/usr/bin/xcrun llvm-ar' + LLVM_AR_FOUND=found + AC_MSG_NOTICE([llvm-ar found via xcrun: ${LLVM_AR}]) + fi + fi + if test $LLVM_AR_FOUND = not-found + then + LLVM_PROFR_ERR=yes + AC_MSG_ERROR([llvm-ar is required for a --with-lto build with clang but could not be found.]) + else + LLVM_AR_ERR=no + fi + AR="${LLVM_AR}" case $ac_sys_system in Darwin*) # Any changes made here should be reflected in the GCC+Darwin case below @@ -1353,7 +1400,7 @@ if test "$Py_LTO" = 'true' ; then LTOFLAGS="$LTOFLAGS -g" fi - CFLAGS="$CFLAGS $LTOFLAGS" + BASECFLAGS="$BASECFLAGS $LTOFLAGS" LDFLAGS="$LDFLAGS $LTOFLAGS" fi @@ -1363,24 +1410,6 @@ AC_SUBST(PGO_PROF_USE_FLAG) AC_SUBST(LLVM_PROF_MERGER) AC_SUBST(LLVM_PROF_FILE) AC_SUBST(LLVM_PROF_ERR) -# Make this 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. -llvm_bin_dir='' -llvm_path="${PATH}" -if test "${CC}" = "clang" -then - clang_bin=`which clang` - # Some systems install clang elsewhere as a symlink to the real path - # which is where the related llvm tools are located. - if test -L "${clang_bin}" - then - clang_dir=`dirname "${clang_bin}"` - clang_bin=`readlink "${clang_bin}"` - llvm_bin_dir="${clang_dir}/"`dirname "${clang_bin}"` - llvm_path="${llvm_path}${PATH_SEPARATOR}${llvm_bin_dir}" - fi -fi AC_SUBST(LLVM_PROFDATA) AC_PATH_TARGET_TOOL(LLVM_PROFDATA, llvm-profdata, '', ${llvm_path}) AC_SUBST(LLVM_PROF_FOUND) From webhook-mailer at python.org Sun Dec 9 03:35:16 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 09 Dec 2018 08:35:16 -0000 Subject: [Python-checkins] bpo-35351: Pass link time optimization flags to CFLAGS_NODIST (GH-10797) Message-ID: https://github.com/python/cpython/commit/f2d2cb12f2d3bd68a13c4098311e725f776768ad commit: f2d2cb12f2d3bd68a13c4098311e725f776768ad branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-09T00:35:13-08:00 summary: bpo-35351: Pass link time optimization flags to CFLAGS_NODIST (GH-10797) When using link time optimizations, the -flto flag is passed to BASECFLAGS, which makes it propagate to distutils. Those flags should be reserved for the interpreter and the stdlib extension modules only, thus moving those flags to CFLAGS_NODIST. (cherry picked from commit f92c7aa1ae81efa475b5aecf66e4711ef0f52c4c) Co-authored-by: stratakis files: A Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst M configure M configure.ac diff --git a/Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst b/Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst new file mode 100644 index 000000000000..ee6c870b060f --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-04-15-33-28.bpo-35351.ZhhBfT.rst @@ -0,0 +1,2 @@ +When building Python with clang and LTO, LTO flags are no longer passed into +CFLAGS to build third-party C extensions through distutils. diff --git a/configure b/configure index 5a494d987346..8cd730d219e3 100755 --- a/configure +++ b/configure @@ -6726,7 +6726,7 @@ $as_echo "$as_me: llvm-ar found via xcrun: ${LLVM_AR}" >&6;} LTOFLAGS="$LTOFLAGS -g" fi - BASECFLAGS="$BASECFLAGS $LTOFLAGS" + CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" LDFLAGS="$LDFLAGS $LTOFLAGS" fi diff --git a/configure.ac b/configure.ac index 40ecb7d93d8c..bb14a87e5463 100644 --- a/configure.ac +++ b/configure.ac @@ -1400,7 +1400,7 @@ if test "$Py_LTO" = 'true' ; then LTOFLAGS="$LTOFLAGS -g" fi - BASECFLAGS="$BASECFLAGS $LTOFLAGS" + CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" LDFLAGS="$LDFLAGS $LTOFLAGS" fi From solipsis at pitrou.net Sun Dec 9 04:08:32 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 09 Dec 2018 09:08:32 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=8 Message-ID: <20181209090832.1.33AEA0B54B52ABE3@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_asyncio leaked [0, 3, 0] memory blocks, sum=3 test_collections leaked [7, -7, 1] memory blocks, sum=1 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_fork leaked [-2, 1, 0] memory blocks, sum=-1 test_multiprocessing_forkserver leaked [1, -2, 2] memory blocks, sum=1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflog9aBcbg', '--timeout', '7200'] From webhook-mailer at python.org Sun Dec 9 14:43:01 2018 From: webhook-mailer at python.org (Gregory P. Smith) Date: Sun, 09 Dec 2018 19:43:01 -0000 Subject: [Python-checkins] bpo-22005: Document the reality of pickle compatibility. (GH-11054) Message-ID: https://github.com/python/cpython/commit/e328753d91379274b699b93decff45de07854617 commit: e328753d91379274b699b93decff45de07854617 branch: master author: Gregory P. Smith committer: GitHub date: 2018-12-09T11:42:58-08:00 summary: bpo-22005: Document the reality of pickle compatibility. (GH-11054) files: M Doc/library/pickle.rst diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 5fe49a013bc4..100a6a134508 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -71,7 +71,9 @@ The :mod:`pickle` module differs from :mod:`marshal` in several significant ways :file:`.pyc` files, the Python implementers reserve the right to change the serialization format in non-backwards compatible ways should the need arise. The :mod:`pickle` serialization format is guaranteed to be backwards compatible - across Python releases. + across Python releases provided a compatible pickle protocol is chosen and + pickling and unpickling code deals with Python 2 to Python 3 type differences + if your data is crossing that unique breaking change language boundary. Comparison with ``json`` ^^^^^^^^^^^^^^^^^^^^^^^^ From webhook-mailer at python.org Sun Dec 9 14:48:39 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Sun, 09 Dec 2018 19:48:39 -0000 Subject: [Python-checkins] bpo-22005: Document the reality of pickle compatibility. (GH-11054) Message-ID: https://github.com/python/cpython/commit/331bfa4f2c3026a35e111303df0f198d06b4e0c8 commit: 331bfa4f2c3026a35e111303df0f198d06b4e0c8 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-09T11:48:35-08:00 summary: bpo-22005: Document the reality of pickle compatibility. (GH-11054) (cherry picked from commit e328753d91379274b699b93decff45de07854617) Co-authored-by: Gregory P. Smith files: M Doc/library/pickle.rst diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 52cbb6241bc9..36f66a159bf2 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -71,7 +71,9 @@ The :mod:`pickle` module differs from :mod:`marshal` in several significant ways :file:`.pyc` files, the Python implementers reserve the right to change the serialization format in non-backwards compatible ways should the need arise. The :mod:`pickle` serialization format is guaranteed to be backwards compatible - across Python releases. + across Python releases provided a compatible pickle protocol is chosen and + pickling and unpickling code deals with Python 2 to Python 3 type differences + if your data is crossing that unique breaking change language boundary. Comparison with ``json`` ^^^^^^^^^^^^^^^^^^^^^^^^ From webhook-mailer at python.org Sun Dec 9 15:15:33 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Sun, 09 Dec 2018 20:15:33 -0000 Subject: [Python-checkins] Remove a duplicate descriptor in gdbm. (GH-11053) Message-ID: https://github.com/python/cpython/commit/e448f9c99e75568dac7c92ea34ee5bef76bc6995 commit: e448f9c99e75568dac7c92ea34ee5bef76bc6995 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-09T22:15:29+02:00 summary: Remove a duplicate descriptor in gdbm. (GH-11053) files: M Modules/_gdbmmodule.c diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index ceb744b99ba8..46bb59ac9521 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -485,7 +485,6 @@ static PyMethodDef dbm_methods[] = { _GDBM_GDBM_REORGANIZE_METHODDEF _GDBM_GDBM_SYNC_METHODDEF _GDBM_GDBM_GET_METHODDEF - _GDBM_GDBM_GET_METHODDEF _GDBM_GDBM_SETDEFAULT_METHODDEF {"__enter__", dbm__enter__, METH_NOARGS, NULL}, {"__exit__", dbm__exit__, METH_VARARGS, NULL}, From webhook-mailer at python.org Sun Dec 9 22:59:38 2018 From: webhook-mailer at python.org (Steve Dower) Date: Mon, 10 Dec 2018 03:59:38 -0000 Subject: [Python-checkins] bpo-35433: Properly detect installed SDK versions (GH-11009) Message-ID: https://github.com/python/cpython/commit/f46eccd0ffe65333035c3820886295b71c41ab6e commit: f46eccd0ffe65333035c3820886295b71c41ab6e branch: master author: Jeremy Kloth committer: Steve Dower date: 2018-12-09T19:59:32-08:00 summary: bpo-35433: Properly detect installed SDK versions (GH-11009) files: M PCbuild/build.bat M PCbuild/python.props diff --git a/PCbuild/build.bat b/PCbuild/build.bat index d4aebf555137..759aa5221b42 100644 --- a/PCbuild/build.bat +++ b/PCbuild/build.bat @@ -148,4 +148,5 @@ goto :eof :Version rem Display the current build version information -%MSBUILD% "%dir%python.props" /t:ShowVersionInfo /v:m /nologo %1 %2 %3 %4 %5 %6 %7 %8 %9 +call "%dir%find_msbuild.bat" %MSBUILD% +if not ERRORLEVEL 1 %MSBUILD% "%dir%pythoncore.vcxproj" /t:ShowVersionInfo /v:m /nologo %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/PCbuild/python.props b/PCbuild/python.props index 09f11d3bba8c..f474e6f07e6d 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -77,14 +77,18 @@ --> <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) - 10.0.17134.0 - 10.0.16299.0 - 10.0.15063.0 - 10.0.14393.0 - 10.0.10586.0 - 10.0.10240.0 + + <_RegistryVersion Condition="$(_RegistryVersion) != '' and !$(_RegistryVersion.EndsWith('.0'))">$(_RegistryVersion).0 + + + 10.0.10586.0 + $(_RegistryVersion) + + $(DefaultWindowsSDKVersion) + + <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) - 10.0.17134.0 - 10.0.16299.0 - 10.0.15063.0 - 10.0.14393.0 - 10.0.10586.0 - 10.0.10240.0 + + <_RegistryVersion Condition="$(_RegistryVersion) != '' and !$(_RegistryVersion.EndsWith('.0'))">$(_RegistryVersion).0 + + + 10.0.10586.0 + $(_RegistryVersion) + + $(DefaultWindowsSDKVersion) + + + + diff --git a/PCbuild/venvlauncher.vcxproj b/PCbuild/venvlauncher.vcxproj new file mode 100644 index 000000000000..295b36304733 --- /dev/null +++ b/PCbuild/venvlauncher.vcxproj @@ -0,0 +1,85 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D} + venvlauncher + venvlauncher + false + + + + + Application + MultiByte + + + + + + ClCompile + + + + + + + + + _CONSOLE;VENV_REDIRECT;%(PreprocessorDefinitions) + MultiThreaded + + + PY_ICON;%(PreprocessorDefinitions) + + + version.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + diff --git a/PCbuild/venvwlauncher.vcxproj b/PCbuild/venvwlauncher.vcxproj new file mode 100644 index 000000000000..e7ba25da41eb --- /dev/null +++ b/PCbuild/venvwlauncher.vcxproj @@ -0,0 +1,85 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D} + venvwlauncher + venvwlauncher + false + + + + + Application + MultiByte + + + + + + ClCompile + + + + + + + + + _WINDOWS;VENV_REDIRECT;%(PreprocessorDefinitions) + MultiThreaded + + + PYW_ICON;%(PreprocessorDefinitions) + + + version.lib;%(AdditionalDependencies) + Windows + + + + + + + + + + + + + + + diff --git a/Tools/msi/lib/lib_files.wxs b/Tools/msi/lib/lib_files.wxs index da9d1c9f346c..4bd0c57e3229 100644 --- a/Tools/msi/lib/lib_files.wxs +++ b/Tools/msi/lib/lib_files.wxs @@ -2,6 +2,8 @@ + + @@ -20,10 +22,25 @@ + + + + + + + + + + + + + + + @@ -42,6 +59,12 @@ + + + + + + From webhook-mailer at python.org Mon Dec 10 11:11:38 2018 From: webhook-mailer at python.org (Steve Dower) Date: Mon, 10 Dec 2018 16:11:38 -0000 Subject: [Python-checkins] [3.7] bpo-34977: Use venv redirector instead of original python.exe on Windows (GH-11029) Message-ID: https://github.com/python/cpython/commit/b264c609853eae9dbb45c6dbee11e84ae3927e88 commit: b264c609853eae9dbb45c6dbee11e84ae3927e88 branch: 3.7 author: Steve Dower committer: GitHub date: 2018-12-10T08:11:34-08:00 summary: [3.7] bpo-34977: Use venv redirector instead of original python.exe on Windows (GH-11029) files: A Misc/NEWS.d/next/Windows/2018-12-07-10-00-38.bpo-34977.agQJbD.rst A PCbuild/venvlauncher.vcxproj A PCbuild/venvwlauncher.vcxproj M Doc/library/venv.rst M Doc/whatsnew/3.7.rst M Lib/test/test_venv.py M Lib/venv/__init__.py M PC/getpathp.c M PC/launcher.c M PCbuild/pcbuild.proj M Tools/msi/lib/lib_files.wxs diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index def926b08526..53f3d2640566 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -135,7 +135,6 @@ creation according to their needs, the :class:`EnvBuilder` class. .. versionadded:: 3.6 Added the ``prompt`` parameter - Creators of third-party virtual environment tools will be free to use the provided ``EnvBuilder`` class as a base class. @@ -182,16 +181,15 @@ creation according to their needs, the :class:`EnvBuilder` class. .. method:: setup_python(context) - Creates a copy of the Python executable (and, under Windows, DLLs) in - the environment. On a POSIX system, if a specific executable - ``python3.x`` was used, symlinks to ``python`` and ``python3`` will be - created pointing to that executable, unless files with those names - already exist. + Creates a copy of the Python executable in the environment on POSIX + systems. If a specific executable ``python3.x`` was used, symlinks to + ``python`` and ``python3`` will be created pointing to that executable, + unless files with those names already exist. .. method:: setup_scripts(context) Installs activation scripts appropriate to the platform into the virtual - environment. + environment. On Windows, also installs the ``python[w].exe`` scripts. .. method:: post_setup(context) @@ -199,6 +197,11 @@ creation according to their needs, the :class:`EnvBuilder` class. implementations to pre-install packages in the virtual environment or perform other post-creation steps. + .. versionchanged:: 3.7.2 + Windows now uses redirector scripts for ``python[w].exe`` instead of + copying the actual binaries, and so :meth:`setup_python` does nothing + unless running from a build in the source tree. + In addition, :class:`EnvBuilder` provides this utility method that can be called from :meth:`setup_scripts` or :meth:`post_setup` in subclasses to assist in installing custom scripts into the virtual environment. diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index aa690aacad9e..7d4c4f91993b 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -2528,3 +2528,13 @@ In 3.7.1 the :mod:`tokenize` module now implicitly emits a ``NEWLINE`` token when provided with input that does not have a trailing new line. This behavior now matches what the C tokenizer does internally. (Contributed by Ammar Askar in :issue:`33899`.) + +Notable changes in Python 3.7.2 +=============================== + +In 3.7.2, :mod:`venv` on Windows no longer copies the original binaries, but +creates redirector scripts named ``python.exe`` and ``pythonw.exe`` instead. +This resolves a long standing issue where all virtual environments would have +to be upgraded or recreated with each Python update. However, note that this +release will still require recreation of virtual environments in order to get +the new scripts. diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 461fe7afd213..22a3b78852f8 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -243,6 +243,7 @@ def test_isolation(self): self.assertIn('include-system-site-packages = %s\n' % s, data) @unittest.skipUnless(can_symlink(), 'Needs symlinks') + @unittest.skipIf(os.name == 'nt', 'Symlinks are never used on Windows') def test_symlinking(self): """ Test symlinking works as expected diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index 716129d13987..5438b0d4e508 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -9,6 +9,7 @@ import shutil import subprocess import sys +import sysconfig import types logger = logging.getLogger(__name__) @@ -63,10 +64,11 @@ def create(self, env_dir): self.system_site_packages = False self.create_configuration(context) self.setup_python(context) + if not self.upgrade: + self.setup_scripts(context) if self.with_pip: self._setup_pip(context) if not self.upgrade: - self.setup_scripts(context) self.post_setup(context) if true_system_site_packages: # We had set it to False before, now @@ -157,14 +159,6 @@ def create_configuration(self, context): f.write('include-system-site-packages = %s\n' % incl) f.write('version = %d.%d.%d\n' % sys.version_info[:3]) - if os.name == 'nt': - def include_binary(self, f): - if f.endswith(('.pyd', '.dll')): - result = True - else: - result = f.startswith('python') and f.endswith('.exe') - return result - def symlink_or_copy(self, src, dst, relative_symlinks_ok=False): """ Try symlinking a file, and if that fails, fall back to copying. @@ -194,9 +188,9 @@ def setup_python(self, context): binpath = context.bin_path path = context.env_exe copier = self.symlink_or_copy - copier(context.executable, path) dirname = context.python_dir if os.name != 'nt': + copier(context.executable, path) if not os.path.islink(path): os.chmod(path, 0o755) for suffix in ('python', 'python3'): @@ -208,32 +202,33 @@ def setup_python(self, context): if not os.path.islink(path): os.chmod(path, 0o755) else: - subdir = 'DLLs' - include = self.include_binary - files = [f for f in os.listdir(dirname) if include(f)] - for f in files: - src = os.path.join(dirname, f) - dst = os.path.join(binpath, f) - if dst != context.env_exe: # already done, above - copier(src, dst) - dirname = os.path.join(dirname, subdir) - if os.path.isdir(dirname): - files = [f for f in os.listdir(dirname) if include(f)] - for f in files: - src = os.path.join(dirname, f) - dst = os.path.join(binpath, f) - copier(src, dst) - # copy init.tcl over - for root, dirs, files in os.walk(context.python_dir): - if 'init.tcl' in files: - tcldir = os.path.basename(root) - tcldir = os.path.join(context.env_dir, 'Lib', tcldir) - if not os.path.exists(tcldir): - os.makedirs(tcldir) - src = os.path.join(root, 'init.tcl') - dst = os.path.join(tcldir, 'init.tcl') - shutil.copyfile(src, dst) - break + # For normal cases, the venvlauncher will be copied from + # our scripts folder. For builds, we need to copy it + # manually. + if sysconfig.is_python_build(True): + suffix = '.exe' + if context.python_exe.lower().endswith('_d.exe'): + suffix = '_d.exe' + + src = os.path.join(dirname, "venvlauncher" + suffix) + dst = os.path.join(binpath, context.python_exe) + copier(src, dst) + + src = os.path.join(dirname, "venvwlauncher" + suffix) + dst = os.path.join(binpath, "pythonw" + suffix) + copier(src, dst) + + # copy init.tcl over + for root, dirs, files in os.walk(context.python_dir): + if 'init.tcl' in files: + tcldir = os.path.basename(root) + tcldir = os.path.join(context.env_dir, 'Lib', tcldir) + if not os.path.exists(tcldir): + os.makedirs(tcldir) + src = os.path.join(root, 'init.tcl') + dst = os.path.join(tcldir, 'init.tcl') + shutil.copyfile(src, dst) + break def _setup_pip(self, context): """Installs or upgrades pip in a virtual environment""" @@ -320,7 +315,7 @@ def install_scripts(self, context, path): dstfile = os.path.join(dstdir, f) with open(srcfile, 'rb') as f: data = f.read() - if not srcfile.endswith('.exe'): + if not srcfile.endswith(('.exe', '.pdb')): try: data = data.decode('utf-8') data = self.replace_variables(data, context) diff --git a/Misc/NEWS.d/next/Windows/2018-12-07-10-00-38.bpo-34977.agQJbD.rst b/Misc/NEWS.d/next/Windows/2018-12-07-10-00-38.bpo-34977.agQJbD.rst new file mode 100644 index 000000000000..12d8db2ab46a --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-12-07-10-00-38.bpo-34977.agQJbD.rst @@ -0,0 +1,2 @@ +venv on Windows will now use a python.exe redirector rather than copying the +actual binaries from the base environment. diff --git a/PC/getpathp.c b/PC/getpathp.c index 599b41b1efc1..1b553d53affa 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -536,10 +536,16 @@ static _PyInitError get_program_full_path(const _PyCoreConfig *core_config, PyCalculatePath *calculate, _PyPathConfig *config) { + const wchar_t *pyvenv_launcher; wchar_t program_full_path[MAXPATHLEN+1]; memset(program_full_path, 0, sizeof(program_full_path)); - if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { + /* The launcher may need to force the executable path to a + * different environment, so override it here. */ + pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__"); + if (pyvenv_launcher && pyvenv_launcher[0]) { + wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher); + } else if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) { /* GetModuleFileName should never fail when passed NULL */ return _Py_INIT_ERR("Cannot determine program path"); } diff --git a/PC/launcher.c b/PC/launcher.c index 7d666aae4ab1..f3a7ddc5439c 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -28,7 +28,7 @@ #define RC_NO_PYTHON 103 #define RC_NO_MEMORY 104 /* - * SCRIPT_WRAPPER is used to choose between two variants of an executable built + * SCRIPT_WRAPPER is used to choose one of the variants of an executable built * from this source file. If not defined, the PEP 397 Python launcher is built; * if defined, a script launcher of the type used by setuptools is built, which * looks for a script name related to the executable name and runs that script @@ -40,6 +40,15 @@ #if defined(SCRIPT_WRAPPER) #define RC_NO_SCRIPT 105 #endif +/* + * VENV_REDIRECT is used to choose the variant that looks for an adjacent or + * one-level-higher pyvenv.cfg, and uses its "home" property to locate and + * launch the original python.exe. + */ +#if defined(VENV_REDIRECT) +#define RC_NO_VENV_CFG 106 +#define RC_BAD_VENV_CFG 107 +#endif /* Just for now - static definition */ @@ -97,7 +106,7 @@ error(int rc, wchar_t * format, ... ) #if !defined(_WINDOWS) fwprintf(stderr, L"%ls\n", message); #else - MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), + MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...", MB_OK); #endif exit(rc); @@ -131,6 +140,17 @@ static wchar_t * get_env(wchar_t * key) return buf; } +#if defined(_DEBUG) +#if defined(_WINDOWS) + +#define PYTHON_EXECUTABLE L"pythonw_d.exe" + +#else + +#define PYTHON_EXECUTABLE L"python_d.exe" + +#endif +#else #if defined(_WINDOWS) #define PYTHON_EXECUTABLE L"pythonw.exe" @@ -139,6 +159,7 @@ static wchar_t * get_env(wchar_t * key) #define PYTHON_EXECUTABLE L"python.exe" +#endif #endif #define MAX_VERSION_SIZE 4 @@ -1456,6 +1477,87 @@ show_python_list(wchar_t ** argv) return FALSE; /* If this has been called we cannot continue */ } +#if defined(VENV_REDIRECT) + +static int +find_home_value(const char *buffer, const char **start, DWORD *length) +{ + for (const char *s = strstr(buffer, "home"); s; s = strstr(s + 1, "\nhome")) { + if (*s == '\n') { + ++s; + } + for (int i = 4; i > 0 && *s; --i, ++s); + + while (*s && iswspace(*s)) { + ++s; + } + if (*s != L'=') { + continue; + } + + do { + ++s; + } while (*s && iswspace(*s)); + + *start = s; + char *nl = strchr(s, '\n'); + if (nl) { + *length = (DWORD)((ptrdiff_t)nl - (ptrdiff_t)s); + } else { + *length = (DWORD)strlen(s); + } + return 1; + } + return 0; +} +#endif + +static wchar_t * +wcsdup_pad(const wchar_t *s, int padding, int *newlen) +{ + size_t len = wcslen(s); + len += 1 + padding; + wchar_t *r = (wchar_t *)malloc(len * sizeof(wchar_t)); + if (!r) { + return NULL; + } + if (wcscpy_s(r, len, s)) { + free(r); + return NULL; + } + *newlen = len < MAXINT ? (int)len : MAXINT; + return r; +} + +static wchar_t * +get_process_name() +{ + DWORD bufferLen = MAX_PATH; + DWORD len = bufferLen; + wchar_t *r = NULL; + + while (!r) { + r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); + if (!r) { + error(RC_NO_MEMORY, L"out of memory"); + return NULL; + } + len = GetModuleFileNameW(NULL, r, bufferLen); + if (len == 0) { + free(r); + error(0, L"Failed to get module name"); + return NULL; + } else if (len == bufferLen && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(r); + r = NULL; + bufferLen *= 2; + } + } + + return r; +} + static int process(int argc, wchar_t ** argv) { @@ -1463,21 +1565,27 @@ process(int argc, wchar_t ** argv) wchar_t * command; wchar_t * executable; wchar_t * p; + wchar_t * argv0; int rc = 0; - size_t plen; INSTALLED_PYTHON * ip; BOOL valid; DWORD size, attrs; - HRESULT hr; wchar_t message[MSGSIZE]; void * version_data; VS_FIXEDFILEINFO * file_info; UINT block_size; - int index; -#if defined(SCRIPT_WRAPPER) +#if defined(VENV_REDIRECT) + wchar_t * venv_cfg_path; int newlen; +#elif defined(SCRIPT_WRAPPER) wchar_t * newcommand; wchar_t * av[2]; + int newlen; + HRESULT hr; + int index; +#else + HRESULT hr; + int index; #endif setvbuf(stderr, (char *)NULL, _IONBF, 0); @@ -1495,6 +1603,7 @@ process(int argc, wchar_t ** argv) #else debug(L"launcher executable: Console\n"); #endif +#if !defined(VENV_REDIRECT) /* Get the local appdata folder (non-roaming) */ hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appdata_ini_path); @@ -1503,9 +1612,7 @@ process(int argc, wchar_t ** argv) appdata_ini_path[0] = L'\0'; } else { - plen = wcslen(appdata_ini_path); - p = &appdata_ini_path[plen]; - wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE); + wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE); attrs = GetFileAttributesW(appdata_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", appdata_ini_path); @@ -1514,8 +1621,9 @@ process(int argc, wchar_t ** argv) debug(L"Using local configuration file '%ls'\n", appdata_ini_path); } } - plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH); - size = GetFileVersionInfoSizeW(launcher_ini_path, &size); +#endif + argv0 = get_process_name(); + size = GetFileVersionInfoSizeW(argv0, &size); if (size == 0) { winerror(GetLastError(), message, MSGSIZE); debug(L"GetFileVersionInfoSize failed: %ls\n", message); @@ -1523,7 +1631,7 @@ process(int argc, wchar_t ** argv) else { version_data = malloc(size); if (version_data) { - valid = GetFileVersionInfoW(launcher_ini_path, 0, size, + valid = GetFileVersionInfoW(argv0, 0, size, version_data); if (!valid) debug(L"GetFileVersionInfo failed: %X\n", GetLastError()); @@ -1540,15 +1648,51 @@ process(int argc, wchar_t ** argv) free(version_data); } } + +#if defined(VENV_REDIRECT) + /* Allocate some extra space for new filenames */ + venv_cfg_path = wcsdup_pad(argv0, 32, &newlen); + if (!venv_cfg_path) { + error(RC_NO_MEMORY, L"Failed to copy module name"); + } + p = wcsrchr(venv_cfg_path, L'\\'); + + if (p == NULL) { + error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); + } + p[0] = L'\0'; + wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); + attrs = GetFileAttributesW(venv_cfg_path); + if (attrs == INVALID_FILE_ATTRIBUTES) { + debug(L"File '%ls' non-existent\n", venv_cfg_path); + p[0] = '\0'; + p = wcsrchr(venv_cfg_path, L'\\'); + if (p != NULL) { + p[0] = '\0'; + wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); + attrs = GetFileAttributesW(venv_cfg_path); + if (attrs == INVALID_FILE_ATTRIBUTES) { + debug(L"File '%ls' non-existent\n", venv_cfg_path); + error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); + } + } + } + debug(L"Using venv configuration file '%ls'\n", venv_cfg_path); +#else + /* Allocate some extra space for new filenames */ + if (wcscpy_s(launcher_ini_path, MAX_PATH, argv0)) { + error(RC_NO_MEMORY, L"Failed to copy module name"); + } p = wcsrchr(launcher_ini_path, L'\\'); + if (p == NULL) { debug(L"GetModuleFileNameW returned value has no backslash: %ls\n", launcher_ini_path); launcher_ini_path[0] = L'\0'; } else { - wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini", - _TRUNCATE); + p[0] = L'\0'; + wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini"); attrs = GetFileAttributesW(launcher_ini_path); if (attrs == INVALID_FILE_ATTRIBUTES) { debug(L"File '%ls' non-existent\n", launcher_ini_path); @@ -1557,6 +1701,7 @@ process(int argc, wchar_t ** argv) debug(L"Using global configuration file '%ls'\n", launcher_ini_path); } } +#endif command = skip_me(GetCommandLineW()); debug(L"Called with command line: %ls\n", command); @@ -1592,6 +1737,55 @@ process(int argc, wchar_t ** argv) command = newcommand; valid = FALSE; } +#elif defined(VENV_REDIRECT) + { + FILE *f; + char buffer[4096]; /* 4KB should be enough for anybody */ + char *start; + DWORD len, cch, cch_actual; + size_t cb; + if (_wfopen_s(&f, venv_cfg_path, L"r")) { + error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path); + } + cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]), + sizeof(buffer) / sizeof(buffer[0]), f); + fclose(f); + + if (!find_home_value(buffer, &start, &len)) { + error(RC_BAD_VENV_CFG, L"Cannot find home in '%ls'", + venv_cfg_path); + } + + cch = MultiByteToWideChar(CP_UTF8, 0, start, len, NULL, 0); + if (!cch) { + error(0, L"Cannot determine memory for home path"); + } + cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 1 + 1; /* include sep and null */ + executable = (wchar_t *)malloc(cch * sizeof(wchar_t)); + if (executable == NULL) { + error(RC_NO_MEMORY, L"A memory allocation failed"); + } + cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, executable, cch); + if (!cch_actual) { + error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'", + venv_cfg_path); + } + if (executable[cch_actual - 1] != L'\\') { + executable[cch_actual++] = L'\\'; + executable[cch_actual] = L'\0'; + } + if (wcscat_s(executable, cch, PYTHON_EXECUTABLE)) { + error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'", + venv_cfg_path); + } + if (GetFileAttributesW(executable) == INVALID_FILE_ATTRIBUTES) { + error(RC_NO_PYTHON, L"No Python at '%ls'", executable); + } + if (!SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", argv0)) { + error(0, L"Failed to set launcher environment"); + } + valid = 1; + } #else if (argc <= 1) { valid = FALSE; @@ -1599,7 +1793,6 @@ process(int argc, wchar_t ** argv) } else { p = argv[1]; - plen = wcslen(p); if ((argc == 2) && // list version args (!wcsncmp(p, L"-0", wcslen(L"-0")) || !wcsncmp(p, L"--list", wcslen(L"--list")))) diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index 9e103e12103f..77341b44a1ee 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -70,6 +70,8 @@ + + diff --git a/PCbuild/venvlauncher.vcxproj b/PCbuild/venvlauncher.vcxproj new file mode 100644 index 000000000000..295b36304733 --- /dev/null +++ b/PCbuild/venvlauncher.vcxproj @@ -0,0 +1,85 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D} + venvlauncher + venvlauncher + false + + + + + Application + MultiByte + + + + + + ClCompile + + + + + + + + + _CONSOLE;VENV_REDIRECT;%(PreprocessorDefinitions) + MultiThreaded + + + PY_ICON;%(PreprocessorDefinitions) + + + version.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + diff --git a/PCbuild/venvwlauncher.vcxproj b/PCbuild/venvwlauncher.vcxproj new file mode 100644 index 000000000000..e7ba25da41eb --- /dev/null +++ b/PCbuild/venvwlauncher.vcxproj @@ -0,0 +1,85 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D} + venvwlauncher + venvwlauncher + false + + + + + Application + MultiByte + + + + + + ClCompile + + + + + + + + + _WINDOWS;VENV_REDIRECT;%(PreprocessorDefinitions) + MultiThreaded + + + PYW_ICON;%(PreprocessorDefinitions) + + + version.lib;%(AdditionalDependencies) + Windows + + + + + + + + + + + + + + + diff --git a/Tools/msi/lib/lib_files.wxs b/Tools/msi/lib/lib_files.wxs index da9d1c9f346c..4bd0c57e3229 100644 --- a/Tools/msi/lib/lib_files.wxs +++ b/Tools/msi/lib/lib_files.wxs @@ -2,6 +2,8 @@ + + @@ -20,10 +22,25 @@ + + + + + + + + + + + + + + + @@ -42,6 +59,12 @@ + + + + + + From webhook-mailer at python.org Mon Dec 10 15:31:43 2018 From: webhook-mailer at python.org (Steve Dower) Date: Mon, 10 Dec 2018 20:31:43 -0000 Subject: [Python-checkins] [3.6] bpo-35433: Properly detect installed SDK versions (GH-11009) Message-ID: https://github.com/python/cpython/commit/f04cc5fc0d2f644cccb57543aae487ee30091924 commit: f04cc5fc0d2f644cccb57543aae487ee30091924 branch: 3.6 author: Jeremy Kloth committer: Steve Dower date: 2018-12-10T12:31:37-08:00 summary: [3.6] bpo-35433: Properly detect installed SDK versions (GH-11009) files: M PCbuild/build.bat M PCbuild/python.props diff --git a/PCbuild/build.bat b/PCbuild/build.bat index 5828b518b11a..0df64ee6e143 100644 --- a/PCbuild/build.bat +++ b/PCbuild/build.bat @@ -141,4 +141,5 @@ goto :eof :Version rem Display the current build version information -%MSBUILD% "%dir%python.props" /t:ShowVersionInfo /v:m /nologo %1 %2 %3 %4 %5 %6 %7 %8 %9 +call "%dir%find_msbuild.bat" %MSBUILD% +if not ERRORLEVEL 1 %MSBUILD% "%dir%pythoncore.vcxproj" /t:ShowVersionInfo /v:m /nologo %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/PCbuild/python.props b/PCbuild/python.props index 570f7fa88345..b29669b732b1 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -74,12 +74,17 @@ possible version). Since we limit WINVER to Windows 7 anyway, it doesn't really matter which WinSDK version we use. --> - <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) - <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) - 10.0.15063.0 - 10.0.14393.0 - 10.0.10586.0 - 10.0.10240.0 + <_KitsRoot>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at InstallationFolder) + <_KitsRoot Condition="$(_KitsRoot) == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0 at InstallationFolder) + + + 10.0.10586.0 + 10.0.14393.0 + 10.0.15063.0 + + + + $(DefaultWindowsSDKVersion) @@ -187,5 +192,6 @@ + From webhook-mailer at python.org Mon Dec 10 15:48:13 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 10 Dec 2018 20:48:13 -0000 Subject: [Python-checkins] Fix numbered lists in stdtypes.rst. (GH-10989) Message-ID: https://github.com/python/cpython/commit/b1f98d4c723f746cea9447d7fd9e341ee8da1371 commit: b1f98d4c723f746cea9447d7fd9e341ee8da1371 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T12:48:10-08:00 summary: Fix numbered lists in stdtypes.rst. (GH-10989) (cherry picked from commit de9e9b476ec4abfb0b9161cff0e86bb7085ca8c6) Co-authored-by: Andre Delfino files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index eed37454bc35..00e1f4c1bf0c 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2113,28 +2113,26 @@ object. [5]_ Otherwise, *values* must be a tuple with exactly the number of items specified by the format string, or a single mapping object (for example, a dictionary). +.. index:: + single: () (parentheses); in printf-style formatting + single: * (asterisk); in printf-style formatting + single: . (dot); in printf-style formatting + A conversion specifier contains two or more characters and has the following components, which must occur in this order: #. The ``'%'`` character, which marks the start of the specifier. -.. index:: - single: () (parentheses); in printf-style formatting - #. Mapping key (optional), consisting of a parenthesised sequence of characters (for example, ``(somename)``). #. Conversion flags (optional), which affect the result of some conversion types. -.. index:: single: * (asterisk); in printf-style formatting - #. Minimum field width (optional). If specified as an ``'*'`` (asterisk), the actual width is read from the next element of the tuple in *values*, and the object to convert comes after the minimum field width and optional precision. -.. index:: single: . (dot); in printf-style formatting - #. Precision (optional), given as a ``'.'`` (dot) followed by the precision. If specified as ``'*'`` (an asterisk), the actual precision is read from the next element of the tuple in *values*, and the value to convert comes after the @@ -3229,28 +3227,26 @@ object. [5]_ Otherwise, *values* must be a tuple with exactly the number of items specified by the format bytes object, or a single mapping object (for example, a dictionary). +.. index:: + single: () (parentheses); in printf-style formatting + single: * (asterisk); in printf-style formatting + single: . (dot); in printf-style formatting + A conversion specifier contains two or more characters and has the following components, which must occur in this order: #. The ``'%'`` character, which marks the start of the specifier. -.. index:: - single: () (parentheses); in printf-style formatting - #. Mapping key (optional), consisting of a parenthesised sequence of characters (for example, ``(somename)``). #. Conversion flags (optional), which affect the result of some conversion types. -.. index:: single: * (asterisk); in printf-style formatting - #. Minimum field width (optional). If specified as an ``'*'`` (asterisk), the actual width is read from the next element of the tuple in *values*, and the object to convert comes after the minimum field width and optional precision. -.. index:: single: . (dot); in printf-style formatting - #. Precision (optional), given as a ``'.'`` (dot) followed by the precision. If specified as ``'*'`` (an asterisk), the actual precision is read from the next element of the tuple in *values*, and the value to convert comes after the From webhook-mailer at python.org Mon Dec 10 15:51:37 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 10 Dec 2018 20:51:37 -0000 Subject: [Python-checkins] Fix numbered lists in stdtypes.rst. (GH-10989) Message-ID: https://github.com/python/cpython/commit/b3ffe47bcbb83e31b71644031d4092f7f9456a34 commit: b3ffe47bcbb83e31b71644031d4092f7f9456a34 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T12:51:32-08:00 summary: Fix numbered lists in stdtypes.rst. (GH-10989) (cherry picked from commit de9e9b476ec4abfb0b9161cff0e86bb7085ca8c6) Co-authored-by: Andre Delfino files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 416790b796f5..42c92824b25e 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2123,28 +2123,26 @@ object. [5]_ Otherwise, *values* must be a tuple with exactly the number of items specified by the format string, or a single mapping object (for example, a dictionary). +.. index:: + single: () (parentheses); in printf-style formatting + single: * (asterisk); in printf-style formatting + single: . (dot); in printf-style formatting + A conversion specifier contains two or more characters and has the following components, which must occur in this order: #. The ``'%'`` character, which marks the start of the specifier. -.. index:: - single: () (parentheses); in printf-style formatting - #. Mapping key (optional), consisting of a parenthesised sequence of characters (for example, ``(somename)``). #. Conversion flags (optional), which affect the result of some conversion types. -.. index:: single: * (asterisk); in printf-style formatting - #. Minimum field width (optional). If specified as an ``'*'`` (asterisk), the actual width is read from the next element of the tuple in *values*, and the object to convert comes after the minimum field width and optional precision. -.. index:: single: . (dot); in printf-style formatting - #. Precision (optional), given as a ``'.'`` (dot) followed by the precision. If specified as ``'*'`` (an asterisk), the actual precision is read from the next element of the tuple in *values*, and the value to convert comes after the @@ -3257,28 +3255,26 @@ object. [5]_ Otherwise, *values* must be a tuple with exactly the number of items specified by the format bytes object, or a single mapping object (for example, a dictionary). +.. index:: + single: () (parentheses); in printf-style formatting + single: * (asterisk); in printf-style formatting + single: . (dot); in printf-style formatting + A conversion specifier contains two or more characters and has the following components, which must occur in this order: #. The ``'%'`` character, which marks the start of the specifier. -.. index:: - single: () (parentheses); in printf-style formatting - #. Mapping key (optional), consisting of a parenthesised sequence of characters (for example, ``(somename)``). #. Conversion flags (optional), which affect the result of some conversion types. -.. index:: single: * (asterisk); in printf-style formatting - #. Minimum field width (optional). If specified as an ``'*'`` (asterisk), the actual width is read from the next element of the tuple in *values*, and the object to convert comes after the minimum field width and optional precision. -.. index:: single: . (dot); in printf-style formatting - #. Precision (optional), given as a ``'.'`` (dot) followed by the precision. If specified as ``'*'`` (an asterisk), the actual precision is read from the next element of the tuple in *values*, and the value to convert comes after the From webhook-mailer at python.org Mon Dec 10 21:53:10 2018 From: webhook-mailer at python.org (Steve Dower) Date: Tue, 11 Dec 2018 02:53:10 -0000 Subject: [Python-checkins] bpo-34977: Add Windows App Store package (GH-11027) Message-ID: https://github.com/python/cpython/commit/0cd6391fd890368ea1743dac50c366b42f2fd126 commit: 0cd6391fd890368ea1743dac50c366b42f2fd126 branch: master author: Steve Dower committer: GitHub date: 2018-12-10T18:52:57-08:00 summary: bpo-34977: Add Windows App Store package (GH-11027) Also adds the PC/layout script for generating layouts on Windows. files: A .azure-pipelines/windows-appx-test.yml A Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst A PC/classicAppCompat.can.xml A PC/classicAppCompat.cat A PC/classicAppCompat.sccd A PC/icons/pythonwx150.png A PC/icons/pythonwx44.png A PC/icons/pythonx150.png A PC/icons/pythonx44.png A PC/icons/pythonx50.png A PC/layout/__init__.py A PC/layout/__main__.py A PC/layout/main.py A PC/layout/support/__init__.py A PC/layout/support/appxmanifest.py A PC/layout/support/catalog.py A PC/layout/support/constants.py A PC/layout/support/distutils.command.bdist_wininst.py A PC/layout/support/filesets.py A PC/layout/support/logging.py A PC/layout/support/options.py A PC/layout/support/pip.py A PC/layout/support/props.py A PC/layout/support/python.props A PC/python_uwp.cpp A PC/store_info.txt A PCbuild/python_uwp.vcxproj A PCbuild/pythonw_uwp.vcxproj A Tools/msi/make_appx.ps1 A Tools/msi/make_cat.ps1 A Tools/msi/sdktools.psm1 A Tools/msi/sign_build.ps1 D Tools/msi/make_zip.py D Tools/nuget/python.props M .azure-pipelines/windows-steps.yml M .gitattributes M Doc/make.bat M Doc/using/windows.rst M Lib/test/test_pathlib.py M PC/pylauncher.rc M PCbuild/_tkinter.vcxproj M PCbuild/find_msbuild.bat M PCbuild/pcbuild.proj M PCbuild/pcbuild.sln M PCbuild/pythoncore.vcxproj M Tools/msi/buildrelease.bat M Tools/msi/make_zip.proj M Tools/nuget/make_pkg.proj diff --git a/.azure-pipelines/windows-appx-test.yml b/.azure-pipelines/windows-appx-test.yml new file mode 100644 index 000000000000..5f3fe6c94578 --- /dev/null +++ b/.azure-pipelines/windows-appx-test.yml @@ -0,0 +1,67 @@ +jobs: +- job: Prebuild + displayName: Pre-build checks + + pool: + vmImage: ubuntu-16.04 + + steps: + - template: ./prebuild-checks.yml + + +- job: Windows_Appx_Tests + displayName: Windows Appx Tests + dependsOn: Prebuild + condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) + + pool: + vmImage: vs2017-win2016 + + strategy: + matrix: + win64: + arch: amd64 + buildOpt: '-p x64' + testRunTitle: '$(Build.SourceBranchName)-win64-appx' + testRunPlatform: win64 + maxParallel: 2 + + steps: + - checkout: self + clean: true + fetchDepth: 5 + + - powershell: | + # Relocate build outputs outside of source directory to make cleaning faster + Write-Host '##vso[task.setvariable variable=Py_IntDir]$(Build.BinariesDirectory)\obj' + # UNDONE: Do not build to a different directory because of broken tests + Write-Host '##vso[task.setvariable variable=Py_OutDir]$(Build.SourcesDirectory)\PCbuild' + Write-Host '##vso[task.setvariable variable=EXTERNAL_DIR]$(Build.BinariesDirectory)\externals' + displayName: Update build locations + + - script: PCbuild\build.bat -e $(buildOpt) + displayName: 'Build CPython' + env: + IncludeUwp: true + + - script: python.bat PC\layout -vv -s "$(Build.SourcesDirectory)" -b "$(Py_OutDir)\$(arch)" -t "$(Py_IntDir)\layout-tmp-$(arch)" --copy "$(Py_IntDir)\layout-$(arch)" --precompile --preset-appx --include-tests + displayName: 'Create APPX layout' + + - script: .\python.exe -m test.pythoninfo + workingDirectory: $(Py_IntDir)\layout-$(arch) + displayName: 'Display build info' + + - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml" --tempdir "$(Py_IntDir)\tmp-$(arch)" + workingDirectory: $(Py_IntDir)\layout-$(arch) + displayName: 'Tests' + env: + PREFIX: $(Py_IntDir)\layout-$(arch) + + - task: PublishTestResults at 2 + displayName: 'Publish Test Results' + inputs: + testResultsFiles: '$(Build.BinariesDirectory)\test-results.xml' + mergeTestResults: true + testRunTitle: $(testRunTitle) + platform: $(testRunPlatform) + condition: succeededOrFailed() diff --git a/.azure-pipelines/windows-steps.yml b/.azure-pipelines/windows-steps.yml index c3175841a9b8..cba00158ad13 100644 --- a/.azure-pipelines/windows-steps.yml +++ b/.azure-pipelines/windows-steps.yml @@ -13,6 +13,8 @@ steps: - script: PCbuild\build.bat -e $(buildOpt) displayName: 'Build CPython' + env: + IncludeUwp: true - script: python.bat -m test.pythoninfo displayName: 'Display build info' diff --git a/.gitattributes b/.gitattributes index 4a487c3c2a14..16237bb2b3ac 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,6 +19,7 @@ # Specific binary files Lib/test/sndhdrdata/sndhdr.* binary +PC/classicAppCompat.* binary # Text files that should not be subject to eol conversion Lib/test/cjkencodings/* -text diff --git a/Doc/make.bat b/Doc/make.bat index d28dae78e86d..077a1bc74069 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -115,12 +115,16 @@ goto end :build if not exist "%BUILDDIR%" mkdir "%BUILDDIR%" +rem We ought to move NEWS to %BUILDDIR%\NEWS and point +rem Sphinx at the right location. if exist ..\Misc\NEWS ( echo.Copying Misc\NEWS to build\NEWS + if not exist build mkdir build copy ..\Misc\NEWS build\NEWS > nul ) else if exist ..\Misc\NEWS.D ( if defined BLURB ( echo.Merging Misc/NEWS with %BLURB% + if not exist build mkdir build %BLURB% merge -f build\NEWS ) else ( echo.No Misc/NEWS file and Blurb is not available. diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 296d51b5a587..0165fff09cc5 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -12,9 +12,6 @@ This document aims to give an overview of Windows-specific behaviour you should know about when using Python on Microsoft Windows. -Installing Python -================= - Unlike most Unix systems and services, Windows does not include a system supported installation of Python. To make Python available, the CPython team has compiled Windows installers (MSI packages) with every `release @@ -24,15 +21,37 @@ core interpreter and library being used by a single user. The installer is also able to install for all users of a single machine, and a separate ZIP file is available for application-local distributions. -Supported Versions ------------------- - As specified in :pep:`11`, a Python release only supports a Windows platform while Microsoft considers the platform under extended support. This means that Python |version| supports Windows Vista and newer. If you require Windows XP support then please install Python 3.4. -Installation Steps +There are a number of different installers available for Windows, each with +certain benefits and downsides. + +:ref:`windows-full` contains all components and is the best option for +developers using Python for any kind of project. + +:ref:`windows-store` is a simple installation of Python that is suitable for +running scripts and packages, and using IDLE or other development environments. +It requires Windows 10, but can be safely installed without corrupting other +programs. It also provides many convenient commands for launching Python and +its tools. + +:ref:`windows-nuget` are lightweight installations intended for continuous +integration systems. It can be used to build Python packages or run scripts, +but is not updateable and has no user interface tools. + +:ref:`windows-embeddable` is a minimal package of Python suitable for +embedding into a larger application. + + +.. _windows-full: + +The full installer +================== + +Installation steps ------------------ Four Python |version| installers are available for download - two each for the @@ -264,39 +283,199 @@ settings and replace any that have been removed or modified. "Uninstall" will remove Python entirely, with the exception of the :ref:`launcher`, which has its own entry in Programs and Features. -Other Platforms ---------------- -With ongoing development of Python, some platforms that used to be supported -earlier are no longer supported (due to the lack of users or developers). -Check :pep:`11` for details on all unsupported platforms. +.. _windows-store: -* `Windows CE `_ is still supported. -* The `Cygwin `_ installer offers to install the Python - interpreter as well (cf. `Cygwin package source - `_, `Maintainer releases - `_) +The Microsoft Store package +=========================== -See `Python for Windows `_ -for detailed information about platforms with pre-compiled installers. +.. versionadded:: 3.7.2 -.. seealso:: +.. note:: + The Microsoft Store package is currently considered unstable while its + interactions with other tools and other copies of Python are evaluated. + While Python itself is stable, this installation method may change its + behavior and capabilities during Python 3.7 releases. - `Python on XP `_ - "7 Minutes to "Hello World!"" - by Richard Dooling, 2006 +The Microsoft Store package is an easily installable Python interpreter that +is intended mainly for interactive use, for example, by students. - `Installing on Windows `_ - in "`Dive into Python: Python from novice to pro - `_" - by Mark Pilgrim, 2004, - ISBN 1-59059-356-1 +To install the package, ensure you have the latest Windows 10 updates and +search the Microsoft Store app for "Python |version|". Ensure that the app +you select is published by the Python Software Foundation, and install it. - `For Windows users `_ - in "Installing Python" - in "`A Byte of Python `_" - by Swaroop C H, 2003 +.. warning:: + Python will always be available for free on the Microsoft Store. If you + are asked to pay for it, you have not selected the correct package. + +After installation, Python may be launched by finding it in Start. +Alternatively, it will be available from any Command Prompt or PowerShell +session by typing ``python``. Further, pip and IDLE may be used by typing +``pip`` or ``idle``. IDLE can also be found in Start. + +All three commands are also available with version number suffixes, for +example, as ``python3.exe`` and ``python3.x.exe`` as well as +``python.exe`` (where ``3.x`` is the specific version you want to launch, +such as |version|). + +Virtual environments can be created with ``python -m venv`` and activated +and used as normal. + +If you have installed another version of Python and added it to your +``PATH`` variable, it will be available as ``python.exe`` rather than the +one from the Microsoft Store. To access the new installation, use +``python3.exe`` or ``python3.x.exe``. + +To remove Python, open Settings and use Apps and Features, or else find +Python in Start and right-click to select Uninstall. Uninstalling will +remove all packages you installed directly into this Python installation, but +will not remove any virtual environments + +Known Issues +------------ + +Currently, the ``py.exe`` launcher cannot be used to start Python when it +has been installed from the Microsoft Store. + +Because of restrictions on Microsoft Store apps, Python scripts may not have +full write access to shared locations such as ``TEMP`` and the registry. +Instead, it will write to a private copy. If your scripts must modify the +shared locations, you will need to install the full installer. + + +.. _windows-nuget: + +The nuget.org packages +====================== + +.. versionadded:: 3.5.2 + +The nuget.org package is a reduced size Python environment intended for use on +continuous integration and build systems that do not have a system-wide +install of Python. While nuget is "the package manager for .NET", it also works +perfectly fine for packages containing build-time tools. + +Visit `nuget.org `_ for the most up-to-date information +on using nuget. What follows is a summary that is sufficient for Python +developers. + +The ``nuget.exe`` command line tool may be downloaded directly from +``https://aka.ms/nugetclidl``, for example, using curl or PowerShell. With the +tool, the latest version of Python for 64-bit or 32-bit machines is installed +using:: + + nuget.exe install python -ExcludeVersion -OutputDirectory . + nuget.exe install pythonx86 -ExcludeVersion -OutputDirectory . + +To select a particular version, add a ``-Version 3.x.y``. The output directory +may be changed from ``.``, and the package will be installed into a +subdirectory. By default, the subdirectory is named the same as the package, +and without the ``-ExcludeVersion`` option this name will include the specific +version installed. Inside the subdirectory is a ``tools`` directory that +contains the Python installation:: + + # Without -ExcludeVersion + > .\python.3.5.2\tools\python.exe -V + Python 3.5.2 + + # With -ExcludeVersion + > .\python\tools\python.exe -V + Python 3.5.2 + +In general, nuget packages are not upgradeable, and newer versions should be +installed side-by-side and referenced using the full path. Alternatively, +delete the package directory manually and install it again. Many CI systems +will do this automatically if they do not preserve files between builds. + +Alongside the ``tools`` directory is a ``build\native`` directory. This +contains a MSBuild properties file ``python.props`` that can be used in a +C++ project to reference the Python install. Including the settings will +automatically use the headers and import libraries in your build. + +The package information pages on nuget.org are +`www.nuget.org/packages/python `_ +for the 64-bit version and `www.nuget.org/packages/pythonx86 +`_ for the 32-bit version. + + +.. _windows-embeddable: + +The embeddable package +====================== + +.. versionadded:: 3.5 + +The embedded distribution is a ZIP file containing a minimal Python environment. +It is intended for acting as part of another application, rather than being +directly accessed by end-users. + +When extracted, the embedded distribution is (almost) fully isolated from the +user's system, including environment variables, system registry settings, and +installed packages. The standard library is included as pre-compiled and +optimized ``.pyc`` files in a ZIP, and ``python3.dll``, ``python37.dll``, +``python.exe`` and ``pythonw.exe`` are all provided. Tcl/tk (including all +dependants, such as Idle), pip and the Python documentation are not included. + +.. note:: + + The embedded distribution does not include the `Microsoft C Runtime + `_ and it is + the responsibility of the application installer to provide this. The + runtime may have already been installed on a user's system previously or + automatically via Windows Update, and can be detected by finding + ``ucrtbase.dll`` in the system directory. + +Third-party packages should be installed by the application installer alongside +the embedded distribution. Using pip to manage dependencies as for a regular +Python installation is not supported with this distribution, though with some +care it may be possible to include and use pip for automatic updates. In +general, third-party packages should be treated as part of the application +("vendoring") so that the developer can ensure compatibility with newer +versions before providing updates to users. + +The two recommended use cases for this distribution are described below. + +Python Application +------------------ + +An application written in Python does not necessarily require users to be aware +of that fact. The embedded distribution may be used in this case to include a +private version of Python in an install package. Depending on how transparent it +should be (or conversely, how professional it should appear), there are two +options. + +Using a specialized executable as a launcher requires some coding, but provides +the most transparent experience for users. With a customized launcher, there are +no obvious indications that the program is running on Python: icons can be +customized, company and version information can be specified, and file +associations behave properly. In most cases, a custom launcher should simply be +able to call ``Py_Main`` with a hard-coded command line. + +The simpler approach is to provide a batch file or generated shortcut that +directly calls the ``python.exe`` or ``pythonw.exe`` with the required +command-line arguments. In this case, the application will appear to be Python +and not its actual name, and users may have trouble distinguishing it from other +running Python processes or file associations. + +With the latter approach, packages should be installed as directories alongside +the Python executable to ensure they are available on the path. With the +specialized launcher, packages can be located in other locations as there is an +opportunity to specify the search path before launching the application. + +Embedding Python +---------------- + +Applications written in native code often require some form of scripting +language, and the embedded Python distribution can be used for this purpose. In +general, the majority of the application is in native code, and some part will +either invoke ``python.exe`` or directly use ``python3.dll``. For either case, +extracting the embedded distribution to a subdirectory of the application +installation is sufficient to provide a loadable Python interpreter. + +As with the application use, packages can be installed to any location as there +is an opportunity to specify search paths before initializing the interpreter. +Otherwise, there is no fundamental differences between using the embedded +distribution and a regular installation. Alternative bundles @@ -441,6 +620,8 @@ appropriate version of Python. It will prefer per-user installations over system-wide ones, and orders by language version rather than using the most recently installed version. +The launcher was originally specified in :pep:`397`. + Getting started --------------- @@ -922,95 +1103,19 @@ For extension modules, consult :ref:`building-on-windows`. by Trent Apted et al, 2007 -Embedded Distribution -===================== - -.. versionadded:: 3.5 - -The embedded distribution is a ZIP file containing a minimal Python environment. -It is intended for acting as part of another application, rather than being -directly accessed by end-users. - -When extracted, the embedded distribution is (almost) fully isolated from the -user's system, including environment variables, system registry settings, and -installed packages. The standard library is included as pre-compiled and -optimized ``.pyc`` files in a ZIP, and ``python3.dll``, ``python37.dll``, -``python.exe`` and ``pythonw.exe`` are all provided. Tcl/tk (including all -dependants, such as Idle), pip and the Python documentation are not included. - -.. note:: - - The embedded distribution does not include the `Microsoft C Runtime - `_ and it is - the responsibility of the application installer to provide this. The - runtime may have already been installed on a user's system previously or - automatically via Windows Update, and can be detected by finding - ``ucrtbase.dll`` in the system directory. - -Third-party packages should be installed by the application installer alongside -the embedded distribution. Using pip to manage dependencies as for a regular -Python installation is not supported with this distribution, though with some -care it may be possible to include and use pip for automatic updates. In -general, third-party packages should be treated as part of the application -("vendoring") so that the developer can ensure compatibility with newer -versions before providing updates to users. - -The two recommended use cases for this distribution are described below. - -Python Application ------------------- - -An application written in Python does not necessarily require users to be aware -of that fact. The embedded distribution may be used in this case to include a -private version of Python in an install package. Depending on how transparent it -should be (or conversely, how professional it should appear), there are two -options. - -Using a specialized executable as a launcher requires some coding, but provides -the most transparent experience for users. With a customized launcher, there are -no obvious indications that the program is running on Python: icons can be -customized, company and version information can be specified, and file -associations behave properly. In most cases, a custom launcher should simply be -able to call ``Py_Main`` with a hard-coded command line. - -The simpler approach is to provide a batch file or generated shortcut that -directly calls the ``python.exe`` or ``pythonw.exe`` with the required -command-line arguments. In this case, the application will appear to be Python -and not its actual name, and users may have trouble distinguishing it from other -running Python processes or file associations. - -With the latter approach, packages should be installed as directories alongside -the Python executable to ensure they are available on the path. With the -specialized launcher, packages can be located in other locations as there is an -opportunity to specify the search path before launching the application. - -Embedding Python ----------------- - -Applications written in native code often require some form of scripting -language, and the embedded Python distribution can be used for this purpose. In -general, the majority of the application is in native code, and some part will -either invoke ``python.exe`` or directly use ``python3.dll``. For either case, -extracting the embedded distribution to a subdirectory of the application -installation is sufficient to provide a loadable Python interpreter. - -As with the application use, packages can be installed to any location as there -is an opportunity to specify search paths before initializing the interpreter. -Otherwise, there is no fundamental differences between using the embedded -distribution and a regular installation. - -Other resources +Other Platforms =============== -.. seealso:: - - `Python Programming On Win32 `_ - "Help for Windows Programmers" - by Mark Hammond and Andy Robinson, O'Reilly Media, 2000, - ISBN 1-56592-621-8 +With ongoing development of Python, some platforms that used to be supported +earlier are no longer supported (due to the lack of users or developers). +Check :pep:`11` for details on all unsupported platforms. - `A Python for Windows Tutorial `_ - by Amanda Birmingham, 2004 +* `Windows CE `_ is still supported. +* The `Cygwin `_ installer offers to install the Python + interpreter as well (cf. `Cygwin package source + `_, `Maintainer releases + `_) - :pep:`397` - Python launcher for Windows - The proposal for the launcher to be included in the Python distribution. +See `Python for Windows `_ +for detailed information about platforms with pre-compiled installers. diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 876eecccfd5f..d3fd4bd9e6b7 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1521,7 +1521,7 @@ def test_resolve_common(self): # resolves to 'dirB/..' first before resolving to parent of dirB. self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) # Now create absolute symlinks - d = support._longpath(tempfile.mkdtemp(suffix='-dirD')) + d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) self.addCleanup(support.rmtree, d) os.symlink(os.path.join(d), join('dirA', 'linkX')) os.symlink(join('dirB'), os.path.join(d, 'linkY')) diff --git a/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst new file mode 100644 index 000000000000..8e1a4ba84880 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst @@ -0,0 +1 @@ +Adds support for building a Windows App Store package diff --git a/PC/classicAppCompat.can.xml b/PC/classicAppCompat.can.xml new file mode 100644 index 000000000000..f00475c8da31 --- /dev/null +++ b/PC/classicAppCompat.can.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/PC/classicAppCompat.cat b/PC/classicAppCompat.cat new file mode 100644 index 000000000000..3d213596accf Binary files /dev/null and b/PC/classicAppCompat.cat differ diff --git a/PC/classicAppCompat.sccd b/PC/classicAppCompat.sccd new file mode 100644 index 000000000000..97648985a2cc --- /dev/null +++ b/PC/classicAppCompat.sccd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + MIIq5AYJKoZIhvcNAQcCoIIq1TCCKtECAQExDzANBglghkgBZQMEAgEFADCCARAGCSsGAQQBgjcKAaCCAQEwgf4wDAYKKwYBBAGCNwwBAQQQaM+L42jwBUGvBczrtolMmhcNMTgxMTMwMDA1OTAzWjAOBgorBgEEAYI3DAEDBQAwgbwwKgQUWKcU3R38DGPlKK33XGIwKtVL1r4xEjAQBgorBgEEAYI3DAIDMQKCADCBjQQg3K+KBOQX7HfxjRNZC9cx8gIPkEhPRO1nJFRdWQrVEJ4xaTAQBgorBgEEAYI3DAIDMQKCADBVBgorBgEEAYI3AgEEMUcwRTAQBgorBgEEAYI3AgEZogKAADAxMA0GCWCGSAFlAwQCAQUABCDcr4oE5Bfsd/GNE1kL1zHyAg+QSE9E7WckVF1ZCtUQnqCCFFAwggZSMIIEOqADAgECAhMzAAMu49KhfNamygpWAAIAAy7jMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQDEx5NaWNyb3NvZnQgTWFya2V0cGxhY2UgQ0EgRyAwMTMwHhcNMTgxMTMwMDA1NTA1WhcNMTgxMjAzMDA1NTA1WjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwpcimfAx3HEpba1GLL/gDaRVddHE5PXTRmwlgaz8kt6/rq5rlrPFnCnbIc5818v0xJIznastbmrq26xyCEHyMLBKnyneTKE36I7+TGjcY0D7ow+o2vY7LDKMCTGlh31fx1Tvrl+5xTbWX5jdLU/3MB5faeOGh+0Knzwx1KDoXWgPtfXnD8I5jxJieoWoCwCjKTJgBOklLy9nbOalxf0h+xQRy2p5fj+PxAwQPgHWft36AF7/IMbt9FcXMtg4xdpnTYz4OV3dFOPz4m3M8HwVgNMv89W/1Ozc7uOyZt0Ij1baT6r2L3IjYg5ftzpGqaDOFcWlyDFSdhMR6BIKW8xEpAgMBAAGjggHCMIIBvjAYBgNVHSUBAf8EDjAMBgorBgEEAYI3TBwBMB0GA1UdDgQWBBRdpGYiCytx83FYzPSl+o97YzpxGzAPBgNVHREECDAGggRNT1BSMB8GA1UdIwQYMBaAFEnYB1RFhpclHtZZcRLDcpt0OE3oMGIGA1UdHwRbMFkwV6BVoFOGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyME1hcmtldHBsYWNlJTIwQ0ElMjBHJTIwMDEzKDIpLmNybDBvBggrBgEFBQcBAQRjMGEwXwYIKwYBBQUHMAKGU2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwTWFya2V0cGxhY2UlMjBDQSUyMEclMjAwMTMoMikuY3J0MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXgMDwGCSsGAQQBgjcVBwQvMC0GJSsGAQQBgjcVCIOS9kTqrxCDkY0wgqzgLIKinDE0g+6NOIaE7wACAWQCARYwIAYJKwYBBAGCNxUKAQH/BBAwDjAMBgorBgEEAYI3TBwBMA0GCSqGSIb3DQEBCwUAA4ICAQB3Dk3rXH52CDq/z1fwqn9xI5WGjGmu6oAE4HSc3sNdFrSVMMGm4gTlYGWSZ0wJUUf16mVr/rdXhxuR3MZn+m4Bhdl8KQqYjYbIvCUVj0o9nZ+yT6foeY8bKnB+K5h6rol+mjDj5IfcutC4x2Kx5RrtDtRTSoKA63iZ74DYngPpBGBBgaS2c/QzgqPRAMMRqy2KBDP0miCnpR3F4YlzHGyOZwyHhESjYd9kwF47+msuHS04JZpnGHIvBppKN9XQzH3WezNnnX3lz4AyAUMsMFuARqEnacUhrAHL9n5zMv9CzxDYN1r1/aDh/788RuGuZM+E3NtmbxJJ7j6T5/VtXNBRgKtIq8d2+11j6qvKLigOTxSC25/A70BZBEvllLFnvc1vA2LrC9drwt1KpSmWie1nvpilw7o+gHMOG9utUxGha2VuVizuVNGCywTRRjvmGS1QqTfaun1URVrLfnDINXuTgN1Vwp0J5IGpJ3D8yj01NDQ/RworE+3W/R531NBYova9QRhU/igEw/Aa/q8wjZ4Pzxr9oBIo0Ta3Tv6qIggaWXw0U9+F0J7SCqIhn0d0ATO+E1Qs/SxZIAICLwmqzoLYUAh8q153esBs4uesueqgt5ueyHK8V3WjMS4wxEyVN5ZMET3hFtEshsZC31tLDdjq750U4SgQVmoYSm3F3ZOKQDCCBtcwggS/oAMCAQICCmESRKIAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDMyODIxMDkzOVoXDTMxMDMyODIxMTkzOVowfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAubUaSwGYVsE3MAnPfvmozUhAB3qxBABgJRW1vDp4+tVinXxD32f7k1K89JQ6zDOgS/iDgULC+yFK1K/1Qjac/0M7P6c8v5LSjnWGlERLa/qY32j46S7SLQcit3g2jgoTTO03eUG+9yHZUTGV/FJdRYB8uXhrznJBa+Y+yGwiQKF+m6XFeBH/KORoKFx+dmMoy9EWJ/m/o9IiUj2kzm9C691+vZ/I2w0Bj93W9SPPkV2PCNHlzgfIAoeajWpHmi38Wi3xZHonkzAVBHxPsCBppOoNsWvmAfUM7eBthkSPvFruekyDCPNEYhfGqgqtqLkoBebXLZCOVybF7wTQaLvse60//3P003icRcCoQYgY4NAqrF7j80o5U7DkeXxcB0xvengsaKgiAaV1DKkRbpe98wCqr1AASvm5rAJUYMU+mXmOieV2EelY2jGrenWe9FQpNXYV1NoWBh0WKoFxttoWYAnF705bIWtSZsz08ZfK6WLX4GXNLcPBlgCzfTm1sdKYASWdBbH2haaNhPapFhQQBJHKwnVW2iXErImhuPi45W3MVTZ5D9ASshZx69cLYY6xAdIa+89Kf/uRrsGOVZfahDuDw+NI183iAyzC8z/QRt2P32LYxP0xrCdqVh+DJo2i4NoE8Uk1usCdbVRuBMBQl/AwpOTq7IMvHGElf65CqzUCAwEAAaOCAUswggFHMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBQPU8s/FmEl/mCJHdO5fOiQrbOU0TAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCjuZmM8ZVNDgp9wHsL4RY8KJ8nLinvxFTphNGCrxaLknkYG5pmMhVlX+UB/tSiW8W13W60nggz9u5xwMx7v/1t/Tgm6g2brVyOKI5A7u6/2SIJwkJKFw953K0YIKVT28w9zl8dSJnmRnyR0G86ncWbF6CLQ6A6lBQ9o2mTGVqDr4m35WKAnc6YxUUM1y74mbzFFZr63VHsCcOp3pXWnUqAY1rb6Q6NX1b3clncKqLFm0EjKHcQ56grTbwuuB7pMdh/IFCJR01MQzQbDtpEisbOeZUi43YVAAHKqI1EO9bRwg3frCjwAbml9MmI4utMW94gWFgvrMxIX+n42RBDIjf3Ot3jkT6gt3XeTTmO9bptgblZimhERdkFRUFpVtkocJeLoGuuzP93uH/Yp032wzRH+XmMgujfZv+vnfllJqxdowoQLx55FxLLeTeYfwi/xMSjZO2gNven3U/3KeSCd1kUOFS3AOrwZ0UNOXJeW5JQC6Vfd1BavFZ6FAta1fMLu3WFvNB+FqeHUaU3ya7rmtxJnzk29DeSqXgGNmVSywBS4NajI5jJIKAA6UhNJlsg8CHYwUOKf5ej8OoQCkbadUxXygAfxCfW2YBbujtI+PoyejRFxWUjYFWO5LeTI62UMyqfOEiqugoYjNxmQZla2s4YHVuqIC34R85FQlg9pKQBsDCCBxswggUDoAMCAQICEzMAAABCs21EHGjyqKYAAAAAAEIwDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMB4XDTE4MDQyMDE2NDI0NFoXDTIxMDQyMDE2NDI0NFowgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOZ2KM9Pq1YCOiqWOivmHjUtkMgznTMP/Mr2YfzZeIIJySg1F4WxFZc4jagGHHNof9NRT+GGnktWsXkZuH1DzQEG4Ps1ln8+4vhbDglqu5ymDnd6RmsyoD+8xfc8bBIvE5o6R+ES4/GVD5TqNsOrWbwETaIZVbmTulJLoTS1WSsSjowmbc+sHqZiY8BNJNThUEmXSjuHqkQKKshuiFWYEqOTitp71mBLyH1wN7/jThRzGpolOeFusRNJdb8sEqvNzEN9Qh+Kp6ndzrnjE+t8ixXW3lShyyOOZqQMwsQn9q9T0v7Q69GuojBTFBOHKwigcCHr4xahuN+ZYMk0xGg+sm3Uj7I9mrWTSTiIRMZNIWq3sFg4+rFg48NYfRlXUpONmL7vXq6v1pIU99d2MXQ6uUrnUr1/n5ZiHGCeFcvWwqO8BYHdcTlrSOkayfFp7W9oCk9QO4Xy0h9cQRedRo2kvdTHxIuJS70Hdv6oePPF2ZFaLucUzzwsR4/XMAVKY8Vsm950omsSSOImsMtzavUdQM+wZFxvHTRqVDkF3quPdME0bCZOWB4hQJmd+o2clw+1mpwPu0/M92nA9FJg7MGPxkFaYW7g26jSqUJZ9AcX+Xa5TSIeqMZt3cRVjMTx0T/v73Sv8TpalqIQ5Fde1+hFK07sOAm3TwgzvlVJnbYgp0/rAgMBAAGjggGCMIIBfjASBgkrBgEEAYI3FQEEBQIDAgACMCMGCSsGAQQBgjcVAgQWBBSbJnDhuc3nQXuKuACsPflEbwjbozAdBgNVHQ4EFgQUSdgHVEWGlyUe1llxEsNym3Q4TegwEQYDVR0gBAowCDAGBgRVHSAAMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFA9Tyz8WYSX+YIkd07l86JCts5TRMFcGA1UdHwRQME4wTKBKoEiGRmh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcmwwWwYIKwYBBQUHAQEETzBNMEsGCCsGAQUFBzAChj9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAIa2oa6kvuIHCNfz7anlL0W9tOCt8gQNkxOGRK3yliQIelNQahDJojyEFlHQ2BcHL5oZit3WeSDoYddhojx6YzJIWwfGwtVqgc0JFDKJJ2ZXRYMRsuy01Hn25xob+zRMS6VmV1axQn6uwOSMcgYmzoroh6edjPKu7qXcpt6LmhF2qFvLySA7wBCwfI/rR5/PX6I7a07Av7PpbY6/+2ujd8m1H3hwMrb4Hq3z6gcq62zJ3nDXUbC0Bp6Jt2kV9f0rEFpDK9oxE2qrGBUf8c3O2XirHOgAjRyWjWWtVms+MP8qBIA1NSLrBmToEWVP3sEkQZWMkoZWo4rYEJZpX7UIgdDc9zYNakgTCJqPhqn8AE1sgSSnpqAdMkkP41rTlFCv2ig2QVzDerjGfEv+uPDnlAT0kucbBJxHHvUC4aqUxaTSa0sy2bZ6NWFx8/u0gW8JahzxYvvvZL8SfwaA9P4ETb8pH1jw+6N/LfM2zJrNKhf5hjKa0VDOXUpkYq60OqVVnWJ6oJaSIWNkZKfzPnl/UHA8Bh4qfVrhc9H5PExPhhB9WVTsjf4r+OOVuolJldThcWQqljiPjk5rultr63G5xLyFpxNi4BCrcNQBJFB5wKgOWOyjQTVWTmh2ESaeqZ2aWBjftFHlxJ/qYc7WOGJV0+cHGkB/dvFxmKnv6tuWexiMMYIVUTCCFU0CAQEwgaQwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMwITMwADLuPSoXzWpsoKVgACAAMu4zANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKwYBBAGCNwoBMC8GCSqGSIb3DQEJBDEiBCAS0d3bw2YOODvKFr0S4e3BDnaDcZXUKeBO77yvkWzVojBIBgorBgEEAYI3AgEMMTowOKAegBwATQBpAGMAcgBvAHMAbwBmAHQAIABDAG8AcgBwoRaAFGh0dHA6Ly9NaWNyb3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIBABoap3Y+2k+zFz2cCmkc8xxHnpIygLsUSRMXeXdjPVcYx3o5cPLIixnL6p8+LIrlIagPg23mzTEmnjZaO4aaexk+3XojlHj22w/bEigEDnKyWt5bHeS0UNHJbxEFYRfd84IP1+mSH4c4+GuU9p3LsAMh6wN03MYrGmczUOnlP6YlxHNQbQxnV0sl14yOE5ni9oT4y+l+SllvbV3/Jhwpov68aoP/2MazqxR4QyGfSxhCPJ4UuDHU7IrpnTxGBTL1/oUU8ED0FxyDoH/Sc5OhTLInFqbZaVzm5Mpr12wYUBL4nE5h0Kf6BCKdgM8a+Ti3wMUsBoC79ff3jE9U/xwSneOhghLlMIIS4QYKKwYBBAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQghPy22lwuCYESw8jYhb4F9ZDPJ1LPgSSZgJDkyXYzVt4CBlv98KtAoBgTMjAxODExMzAwMTA1MTkuMTM4WjAEgAIB9KCB0KSBzTCByjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RDA4Mi00QkZELUVFQkExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAOIYOHtm6erB2AAAAAAA4jANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0xODA4MjMyMDI3MDNaFw0xOTExMjMyMDI3MDNaMIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEMDgyLTRCRkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgc2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKirA72FF3NCLW5mfLO/D0EZ5Ycs00oiMSissXLB6WF9GNdP78QzFwAypxW/+qZSczqaHbDH8hlbxkzf3DiYgAdpQjnGkLujwKtWSaP29/lVf7jFqHy9v6eH+LdOi0LvtrPRW34MyCvpxZyOW4H1h3PkxCBL5Ra21sDqgcVL1me0osw8QTURXmI4LyeLdTH3CcI2AgNDXTjsFBf3QsO+JYyAOYWrTcLnywVN6DrigmgrDJk5w+wR4VrHfl2T9PRZbZ+UDt13wwyB9d6IURuzV8lHsAVfF8t9S0aGVPmkQ3c2waOhHpsp6VEM+T5D2Ph8xJX1r82z67WRlmGcOP2NWC0CAwEAAaOCARswggEXMB0GA1UdDgQWBBSJPpD6BsP2p+crDJL232voEtLxezAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQARQHu7ISeBuJSHKuDRI04704cH0B7BYzeEIrD15awviMRcYIfIOHpvGzZOWQgP2Hm0Rr7kvTUu1VrSSaQ7i1gPWdhqMmw5WBnSS5bxeMhhx9UsASeE84vUu82NeZapGSjH38YAb4WT+TtiTkcoI59rA+CTCq108ttIxVfZcr3id76OETIH0HvhlnxOOWjwGy4ul6Za5RoTLG/oo2rrGmVi3FwrNWGezYLBODuEsjzG36lCRtBKC2ZAHfbOz5wtkUHbqh79mUKocjP4r3qxf5TN87yf6g1uTx+J8pdnAi5iHt+ZtangWqnVTE8PoIREWhBVlGFfQdkELUx2Or90aAqWMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQwODItNEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBzZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQByQCUheEOevaI9Zc/3QGrkX42iC6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA36ppYDAiGA8yMDE4MTEyOTIxMzQyNFoYDzIwMTgxMTMwMjEzNDI0WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDfqmlgAgEAMAoCAQACAitfAgH/MAcCAQACAhGtMAoCBQDfq7rgAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbAXXPR9wy4NA0892GGqetaZF+pNClpGcfEpSuHABaZ4Gzr1nY1nmrhexTtr/U6omHALRWzkQwthk0cy+mnEHXyOZGmoEEpgrLgK3AAP5NbK/XbtHQRyZJQyhZScFbOyQycoE8QQalSVOhWxk/bbBMQaQiYVMIexNd/T0KgaDDUMxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAOIYOHtm6erB2AAAAAAA4jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCr9IiSbx6s8MLdxldRG49+4h6CbicW8hWXAicI3jNmhDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIN8BpJSmQCGubWwVa4tW+aMveoHMX/nDnVN8fiDOMsrLMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADiGDh7ZunqwdgAAAAAAOIwIgQgTkOfRvGEZNbr5/hgWclsL4/Q7SOZihE/U0lz2wEMIGcwDQYJKoZIhvcNAQELBQAEggEATlxnCfTzFfTMDvK085zlYPVCroKYW6gKFYnbAhNmrNzcxqALKmIYXpFU7B6HH/vYzkUfCyXpf5tsyEWu0oTySOjyAZ9+2vdaG8nEgjOp0L737lcitgusIjpWtta3Ik0b+mzffnvyjrgTSuKDDni3mxGfvJU77k1Ctempma4H2FJso6Bur0PRH99vIYDu4lHigOSLbeyjR5CiDciBwEVUSA0FxhoFNX1yfpxz3sukOvkaoTduREIjH5LxUjNI1ZTMK/ZkeETI8IPRpWVzAc8q7CujErHKo4sdKej/O2cfUTUHplFLVCGGExpJUCg5FH5jVUUFt75ad8503sdGplggVQ== diff --git a/PC/icons/pythonwx150.png b/PC/icons/pythonwx150.png new file mode 100644 index 000000000000..4c3eb316739c Binary files /dev/null and b/PC/icons/pythonwx150.png differ diff --git a/PC/icons/pythonwx44.png b/PC/icons/pythonwx44.png new file mode 100644 index 000000000000..e3b32a871f90 Binary files /dev/null and b/PC/icons/pythonwx44.png differ diff --git a/PC/icons/pythonx150.png b/PC/icons/pythonx150.png new file mode 100644 index 000000000000..5f8d30418386 Binary files /dev/null and b/PC/icons/pythonx150.png differ diff --git a/PC/icons/pythonx44.png b/PC/icons/pythonx44.png new file mode 100644 index 000000000000..3881daaef233 Binary files /dev/null and b/PC/icons/pythonx44.png differ diff --git a/PC/icons/pythonx50.png b/PC/icons/pythonx50.png new file mode 100644 index 000000000000..7cc3aecd0242 Binary files /dev/null and b/PC/icons/pythonx50.png differ diff --git a/PC/layout/__init__.py b/PC/layout/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/PC/layout/__main__.py b/PC/layout/__main__.py new file mode 100644 index 000000000000..f7aa1e6d261f --- /dev/null +++ b/PC/layout/__main__.py @@ -0,0 +1,14 @@ +import sys + +try: + import layout +except ImportError: + # Failed to import our package, which likely means we were started directly + # Add the additional search path needed to locate our module. + from pathlib import Path + + sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +from layout.main import main + +sys.exit(int(main() or 0)) diff --git a/PC/layout/main.py b/PC/layout/main.py new file mode 100644 index 000000000000..217b2b096e07 --- /dev/null +++ b/PC/layout/main.py @@ -0,0 +1,616 @@ +""" +Generates a layout of Python for Windows from a build. + +See python make_layout.py --help for usage. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import argparse +import functools +import os +import re +import shutil +import subprocess +import sys +import tempfile +import zipfile + +from pathlib import Path + +if __name__ == "__main__": + # Started directly, so enable relative imports + __path__ = [str(Path(__file__).resolve().parent)] + +from .support.appxmanifest import * +from .support.catalog import * +from .support.constants import * +from .support.filesets import * +from .support.logging import * +from .support.options import * +from .support.pip import * +from .support.props import * + +BDIST_WININST_FILES_ONLY = FileNameSet("wininst-*", "bdist_wininst.py") +BDIST_WININST_STUB = "PC/layout/support/distutils.command.bdist_wininst.py" + +TEST_PYDS_ONLY = FileStemSet("xxlimited", "_ctypes_test", "_test*") +TEST_DIRS_ONLY = FileNameSet("test", "tests") + +IDLE_DIRS_ONLY = FileNameSet("idlelib") + +TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter") +TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo") +TCLTK_FILES_ONLY = FileNameSet("turtle.py") + +VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip") + +EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext") +EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle") +EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt") +EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*") +EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll") + +REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*") + +LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt") + +PY_FILES = FileSuffixSet(".py") +PYC_FILES = FileSuffixSet(".pyc") +CAT_FILES = FileSuffixSet(".cat") +CDF_FILES = FileSuffixSet(".cdf") + +DATA_DIRS = FileNameSet("data") + +TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser") +TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt") + + +def get_lib_layout(ns): + def _c(f): + if f in EXCLUDE_FROM_LIB: + return False + if f.is_dir(): + if f in TEST_DIRS_ONLY: + return ns.include_tests + if f in TCLTK_DIRS_ONLY: + return ns.include_tcltk + if f in IDLE_DIRS_ONLY: + return ns.include_idle + if f in VENV_DIRS_ONLY: + return ns.include_venv + else: + if f in TCLTK_FILES_ONLY: + return ns.include_tcltk + if f in BDIST_WININST_FILES_ONLY: + return ns.include_bdist_wininst + return True + + for dest, src in rglob(ns.source / "Lib", "**/*", _c): + yield dest, src + + if not ns.include_bdist_wininst: + src = ns.source / BDIST_WININST_STUB + yield Path("distutils/command/bdist_wininst.py"), src + + +def get_tcltk_lib(ns): + if not ns.include_tcltk: + return + + tcl_lib = os.getenv("TCL_LIBRARY") + if not tcl_lib or not os.path.isdir(tcl_lib): + try: + with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f: + tcl_lib = f.read().strip() + except FileNotFoundError: + pass + if not tcl_lib or not os.path.isdir(tcl_lib): + warn("Failed to find TCL_LIBRARY") + return + + for dest, src in rglob(Path(tcl_lib).parent, "**/*"): + yield "tcl/{}".format(dest), src + + +def get_layout(ns): + def in_build(f, dest="", new_name=None): + n, _, x = f.rpartition(".") + n = new_name or n + src = ns.build / f + if ns.debug and src not in REQUIRED_DLLS: + if not src.stem.endswith("_d"): + src = src.parent / (src.stem + "_d" + src.suffix) + if not n.endswith("_d"): + n += "_d" + f = n + "." + x + yield dest + n + "." + x, src + if ns.include_symbols: + pdb = src.with_suffix(".pdb") + if pdb.is_file(): + yield dest + n + ".pdb", pdb + if ns.include_dev: + lib = src.with_suffix(".lib") + if lib.is_file(): + yield "libs/" + n + ".lib", lib + + if ns.include_appxmanifest: + yield from in_build("python_uwp.exe", new_name="python") + yield from in_build("pythonw_uwp.exe", new_name="pythonw") + else: + yield from in_build("python.exe", new_name="python") + yield from in_build("pythonw.exe", new_name="pythonw") + + yield from in_build(PYTHON_DLL_NAME) + + if ns.include_launchers and ns.include_appxmanifest: + if ns.include_pip: + yield from in_build("python_uwp.exe", new_name="pip") + if ns.include_idle: + yield from in_build("pythonw_uwp.exe", new_name="idle") + + if ns.include_stable: + yield from in_build(PYTHON_STABLE_DLL_NAME) + + for dest, src in rglob(ns.build, "vcruntime*.dll"): + yield dest, src + + for dest, src in rglob(ns.build, ("*.pyd", "*.dll")): + if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS: + continue + if src in EXCLUDE_FROM_PYDS: + continue + if src in TEST_PYDS_ONLY and not ns.include_tests: + continue + if src in TCLTK_PYDS_ONLY and not ns.include_tcltk: + continue + + yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/") + + if ns.zip_lib: + zip_name = PYTHON_ZIP_NAME + yield zip_name, ns.temp / zip_name + else: + for dest, src in get_lib_layout(ns): + yield "Lib/{}".format(dest), src + + if ns.include_venv: + yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python") + yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw") + + if ns.include_tools: + + def _c(d): + if d.is_dir(): + return d in TOOLS_DIRS + return d in TOOLS_FILES + + for dest, src in rglob(ns.source / "Tools", "**/*", _c): + yield "Tools/{}".format(dest), src + + if ns.include_underpth: + yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME + + if ns.include_dev: + + def _c(d): + if d.is_dir(): + return d.name != "internal" + return True + + for dest, src in rglob(ns.source / "Include", "**/*.h", _c): + yield "include/{}".format(dest), src + src = ns.source / "PC" / "pyconfig.h" + yield "include/pyconfig.h", src + + for dest, src in get_tcltk_lib(ns): + yield dest, src + + if ns.include_pip: + pip_dir = get_pip_dir(ns) + if not pip_dir.is_dir(): + log_warning("Failed to find {} - pip will not be included", pip_dir) + else: + pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}" + for dest, src in rglob(pip_dir, "**/*"): + if src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB: + continue + yield pkg_root.format(dest), src + + if ns.include_chm: + for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME): + yield "Doc/{}".format(dest), src + + if ns.include_html_doc: + for dest, src in rglob(ns.doc_build / "html", "**/*"): + yield "Doc/html/{}".format(dest), src + + if ns.include_props: + for dest, src in get_props_layout(ns): + yield dest, src + + for dest, src in get_appx_layout(ns): + yield dest, src + + if ns.include_cat: + if ns.flat_dlls: + yield ns.include_cat.name, ns.include_cat + else: + yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat + + +def _compile_one_py(src, dest, name, optimize): + import py_compile + + if dest is not None: + dest = str(dest) + + try: + return Path( + py_compile.compile( + str(src), + dest, + str(name), + doraise=True, + optimize=optimize, + invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, + ) + ) + except py_compile.PyCompileError: + log_warning("Failed to compile {}", src) + return None + + +def _py_temp_compile(src, ns, dest_dir=None): + 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") + return _compile_one_py(src, dest.with_suffix(".pyc"), dest, optimize=2) + + +def _write_to_zip(zf, dest, src, ns): + pyc = _py_temp_compile(src, ns) + if pyc: + try: + zf.write(str(pyc), dest.with_suffix(".pyc")) + finally: + try: + pyc.unlink() + except: + log_exception("Failed to delete {}", pyc) + return + + if src in LIB2TO3_GRAMMAR_FILES: + from lib2to3.pgen2.driver import load_grammar + + tmp = ns.temp / src.name + try: + shutil.copy(src, tmp) + load_grammar(str(tmp)) + for f in ns.temp.glob(src.stem + "*.pickle"): + zf.write(str(f), str(dest.parent / f.name)) + try: + f.unlink() + except: + log_exception("Failed to delete {}", f) + except: + log_exception("Failed to compile {}", src) + finally: + try: + tmp.unlink() + except: + log_exception("Failed to delete {}", tmp) + + zf.write(str(src), str(dest)) + + +def generate_source_files(ns): + if ns.zip_lib: + zip_name = PYTHON_ZIP_NAME + zip_path = ns.temp / zip_name + if zip_path.is_file(): + zip_path.unlink() + elif zip_path.is_dir(): + log_error( + "Cannot create zip file because a directory exists by the same name" + ) + return + log_info("Generating {} in {}", zip_name, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: + for dest, src in get_lib_layout(ns): + _write_to_zip(zf, dest, src, ns) + + if ns.include_underpth: + log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f: + if ns.zip_lib: + print(PYTHON_ZIP_NAME, file=f) + if ns.include_pip: + print("packages", file=f) + else: + print("Lib", file=f) + print("Lib/site-packages", file=f) + if not ns.flat_dlls: + print("DLLs", file=f) + print(".", file=f) + print(file=f) + print("# Uncomment to run site.main() automatically", file=f) + print("#import site", file=f) + + if ns.include_appxmanifest: + log_info("Generating AppxManifest.xml in {}", ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + + with open(ns.temp / "AppxManifest.xml", "wb") as f: + f.write(get_appxmanifest(ns)) + + with open(ns.temp / "_resources.xml", "wb") as f: + f.write(get_resources_xml(ns)) + + if ns.include_pip: + pip_dir = get_pip_dir(ns) + if not (pip_dir / "pip").is_dir(): + log_info("Extracting pip to {}", pip_dir) + pip_dir.mkdir(parents=True, exist_ok=True) + extract_pip_files(ns) + + if ns.include_props: + log_info("Generating {} in {}", PYTHON_PROPS_NAME, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / PYTHON_PROPS_NAME, "wb") as f: + f.write(get_props(ns)) + + +def _create_zip_file(ns): + if not ns.zip: + return None + + if ns.zip.is_file(): + try: + ns.zip.unlink() + except OSError: + log_exception("Unable to remove {}", ns.zip) + sys.exit(8) + elif ns.zip.is_dir(): + log_error("Cannot create ZIP file because {} is a directory", ns.zip) + sys.exit(8) + + ns.zip.parent.mkdir(parents=True, exist_ok=True) + return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED) + + +def copy_files(files, ns): + if ns.copy: + ns.copy.mkdir(parents=True, exist_ok=True) + + try: + total = len(files) + except TypeError: + total = None + count = 0 + + zip_file = _create_zip_file(ns) + try: + need_compile = [] + in_catalog = [] + + for dest, src in files: + count += 1 + if count % 10 == 0: + if total: + log_info("Processed {:>4} of {} files", count, total) + else: + log_info("Processed {} files", count) + log_debug("Processing {!s}", src) + + if ( + ns.precompile + and src in PY_FILES + and src not in EXCLUDE_FROM_COMPILE + and src.parent not in DATA_DIRS + and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib")) + ): + if ns.copy: + need_compile.append((dest, ns.copy / dest)) + else: + (ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(src, ns.temp / "Lib" / dest) + need_compile.append((dest, ns.temp / "Lib" / dest)) + + if src not in EXCLUDE_FROM_CATALOG: + in_catalog.append((src.name, src)) + + if ns.copy: + log_debug("Copy {} -> {}", src, ns.copy / dest) + (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True) + try: + shutil.copy2(src, ns.copy / dest) + except shutil.SameFileError: + pass + + if ns.zip: + log_debug("Zip {} into {}", src, ns.zip) + zip_file.write(src, str(dest)) + + if need_compile: + for dest, src in need_compile: + compiled = [ + _compile_one_py(src, None, dest, optimize=0), + _compile_one_py(src, None, dest, optimize=1), + _compile_one_py(src, None, dest, optimize=2), + ] + for c in compiled: + if not c: + continue + cdest = Path(dest).parent / Path(c).relative_to(src.parent) + if ns.zip: + log_debug("Zip {} into {}", c, ns.zip) + zip_file.write(c, str(cdest)) + in_catalog.append((cdest.name, cdest)) + + if ns.catalog: + # Just write out the CDF now. Compilation and signing is + # an extra step + log_info("Generating {}", ns.catalog) + ns.catalog.parent.mkdir(parents=True, exist_ok=True) + write_catalog(ns.catalog, in_catalog) + + finally: + if zip_file: + zip_file.close() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-v", help="Increase verbosity", action="count") + parser.add_argument( + "-s", + "--source", + metavar="dir", + help="The directory containing the repository root", + type=Path, + default=None, + ) + parser.add_argument( + "-b", "--build", metavar="dir", help="Specify the build directory", type=Path + ) + parser.add_argument( + "--doc-build", + metavar="dir", + help="Specify the docs build directory", + type=Path, + default=None, + ) + parser.add_argument( + "--copy", + metavar="directory", + help="The name of the directory to copy an extracted layout to", + type=Path, + default=None, + ) + parser.add_argument( + "--zip", + metavar="file", + help="The ZIP file to write all files to", + type=Path, + default=None, + ) + parser.add_argument( + "--catalog", + metavar="file", + help="The CDF file to write catalog entries to", + type=Path, + default=None, + ) + parser.add_argument( + "--log", + metavar="file", + help="Write all operations to the specified file", + type=Path, + default=None, + ) + parser.add_argument( + "-t", + "--temp", + metavar="file", + help="A temporary working directory", + type=Path, + default=None, + ) + parser.add_argument( + "-d", "--debug", help="Include debug build", action="store_true" + ) + parser.add_argument( + "-p", + "--precompile", + help="Include .pyc files instead of .py", + action="store_true", + ) + parser.add_argument( + "-z", "--zip-lib", help="Include library in a ZIP file", action="store_true" + ) + parser.add_argument( + "--flat-dlls", help="Does not create a DLLs directory", action="store_true" + ) + parser.add_argument( + "-a", + "--include-all", + help="Include all optional components", + action="store_true", + ) + parser.add_argument( + "--include-cat", + metavar="file", + help="Specify the catalog file to include", + type=Path, + default=None, + ) + for opt, help in get_argparse_options(): + parser.add_argument(opt, help=help, action="store_true") + + ns = parser.parse_args() + update_presets(ns) + + ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent) + ns.build = ns.build or Path(sys.executable).parent + ns.temp = ns.temp or Path(tempfile.mkdtemp()) + ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build") + if not ns.source.is_absolute(): + ns.source = (Path.cwd() / ns.source).resolve() + if not ns.build.is_absolute(): + ns.build = (Path.cwd() / ns.build).resolve() + if not ns.temp.is_absolute(): + ns.temp = (Path.cwd() / ns.temp).resolve() + if not ns.doc_build.is_absolute(): + ns.doc_build = (Path.cwd() / ns.doc_build).resolve() + if ns.include_cat and not ns.include_cat.is_absolute(): + ns.include_cat = (Path.cwd() / ns.include_cat).resolve() + + if ns.copy and not ns.copy.is_absolute(): + ns.copy = (Path.cwd() / ns.copy).resolve() + if ns.zip and not ns.zip.is_absolute(): + ns.zip = (Path.cwd() / ns.zip).resolve() + if ns.catalog and not ns.catalog.is_absolute(): + ns.catalog = (Path.cwd() / ns.catalog).resolve() + + configure_logger(ns) + + log_info( + """OPTIONS +Source: {ns.source} +Build: {ns.build} +Temp: {ns.temp} + +Copy to: {ns.copy} +Zip to: {ns.zip} +Catalog: {ns.catalog}""", + ns=ns, + ) + + if ns.include_idle and not ns.include_tcltk: + log_warning("Assuming --include-tcltk to support --include-idle") + ns.include_tcltk = True + + try: + generate_source_files(ns) + files = list(get_layout(ns)) + copy_files(files, ns) + except KeyboardInterrupt: + log_info("Interrupted by Ctrl+C") + return 3 + except SystemExit: + raise + except: + log_exception("Unhandled error") + + if error_was_logged(): + log_error("Errors occurred.") + return 1 + + +if __name__ == "__main__": + sys.exit(int(main() or 0)) diff --git a/PC/layout/support/__init__.py b/PC/layout/support/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py new file mode 100644 index 000000000000..c5dda70c7ef8 --- /dev/null +++ b/PC/layout/support/appxmanifest.py @@ -0,0 +1,487 @@ +""" +File generation for APPX/MSIX manifests. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import collections +import ctypes +import io +import os +import sys + +from pathlib import Path, PureWindowsPath +from xml.etree import ElementTree as ET + +from .constants import * + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + +APPX_DATA = dict( + Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT), + Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3), + Publisher=os.getenv( + "APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B" + ), + DisplayName="Python {}".format(VER_DOT), + Description="The Python {} runtime and console.".format(VER_DOT), + ProcessorArchitecture="x64" if IS_X64 else "x86", +) + +PYTHON_VE_DATA = dict( + DisplayName="Python {}".format(VER_DOT), + Description="Python interactive console", + Square150x150Logo="_resources/pythonx150.png", + Square44x44Logo="_resources/pythonx44.png", + BackgroundColor="transparent", +) + +PYTHONW_VE_DATA = dict( + DisplayName="Python {} (Windowed)".format(VER_DOT), + Description="Python windowed app launcher", + Square150x150Logo="_resources/pythonwx150.png", + Square44x44Logo="_resources/pythonwx44.png", + BackgroundColor="transparent", + AppListEntry="none", +) + +PIP_VE_DATA = dict( + DisplayName="pip (Python {})".format(VER_DOT), + Description="pip package manager for Python {}".format(VER_DOT), + Square150x150Logo="_resources/pythonx150.png", + Square44x44Logo="_resources/pythonx44.png", + BackgroundColor="transparent", + AppListEntry="none", +) + +IDLE_VE_DATA = dict( + DisplayName="IDLE (Python {})".format(VER_DOT), + Description="IDLE editor for Python {}".format(VER_DOT), + Square150x150Logo="_resources/pythonwx150.png", + Square44x44Logo="_resources/pythonwx44.png", + BackgroundColor="transparent", +) + +APPXMANIFEST_NS = { + "": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", + "m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", + "uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10", + "rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities", + "rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4", + "desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4", + "desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6", + "uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3", + "uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4", + "uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5", +} + +APPXMANIFEST_TEMPLATE = """ + + + + + Python Software Foundation + + _resources/pythonx50.png + + + + + + + + + + + + + + +""" + + +RESOURCES_XML_TEMPLATE = r""" + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + + +SCCD_FILENAME = "PC/classicAppCompat.sccd" + +REGISTRY = { + "HKCU\\Software\\Python\\PythonCore": { + VER_DOT: { + "DisplayName": APPX_DATA["DisplayName"], + "SupportUrl": "https://www.python.org/", + "SysArchitecture": "64bit" if IS_X64 else "32bit", + "SysVersion": VER_DOT, + "Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO), + "InstallPath": { + # I have no idea why the trailing spaces are needed, but they seem to be needed. + "": "[{AppVPackageRoot}][ ]", + "ExecutablePath": "[{AppVPackageRoot}]python.exe[ ]", + "WindowedExecutablePath": "[{AppVPackageRoot}]pythonw.exe[ ]", + }, + "Help": { + "Main Python Documentation": { + "_condition": lambda ns: ns.include_chm, + "": "[{{AppVPackageRoot}}]Doc\\{}[ ]".format( + PYTHON_CHM_NAME + ), + }, + "Local Python Documentation": { + "_condition": lambda ns: ns.include_html_doc, + "": "[{AppVPackageRoot}]Doc\\html\\index.html[ ]", + }, + "Online Python Documentation": { + "": "https://docs.python.org/{}".format(VER_DOT) + }, + }, + "Idle": { + "_condition": lambda ns: ns.include_idle, + "": "[{AppVPackageRoot}]Lib\\idlelib\\idle.pyw[ ]", + }, + } + } +} + + +def get_packagefamilyname(name, publisher_id): + class PACKAGE_ID(ctypes.Structure): + _fields_ = [ + ("reserved", ctypes.c_uint32), + ("processorArchitecture", ctypes.c_uint32), + ("version", ctypes.c_uint64), + ("name", ctypes.c_wchar_p), + ("publisher", ctypes.c_wchar_p), + ("resourceId", ctypes.c_wchar_p), + ("publisherId", ctypes.c_wchar_p), + ] + _pack_ = 4 + + pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None) + result = ctypes.create_unicode_buffer(256) + result_len = ctypes.c_uint32(256) + r = ctypes.windll.kernel32.PackageFamilyNameFromId( + pid, ctypes.byref(result_len), result + ) + if r: + raise OSError(r, "failed to get package family name") + return result.value[: result_len.value] + + +def _fixup_sccd(ns, sccd, new_hash=None): + if not new_hash: + return sccd + + NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd") + with open(sccd, "rb") as f: + xml = ET.parse(f) + + pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"]) + + ae = xml.find("s:AuthorizedEntities", NS) + ae.clear() + + e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity")) + e.set("AppPackageFamilyName", pfn) + e.set("CertificateSignatureHash", new_hash) + + for e in xml.findall("s:Catalog", NS): + e.text = "FFFF" + + sccd = ns.temp / sccd.name + sccd.parent.mkdir(parents=True, exist_ok=True) + with open(sccd, "wb") as f: + xml.write(f, encoding="utf-8") + + return sccd + + + at public +def get_appx_layout(ns): + if not ns.include_appxmanifest: + return + + yield "AppxManifest.xml", ns.temp / "AppxManifest.xml" + yield "_resources.xml", ns.temp / "_resources.xml" + icons = ns.source / "PC" / "icons" + yield "_resources/pythonx44.png", icons / "pythonx44.png" + yield "_resources/pythonx44$targetsize-44_altform-unplated.png", icons / "pythonx44.png" + yield "_resources/pythonx50.png", icons / "pythonx50.png" + yield "_resources/pythonx50$targetsize-50_altform-unplated.png", icons / "pythonx50.png" + yield "_resources/pythonx150.png", icons / "pythonx150.png" + yield "_resources/pythonx150$targetsize-150_altform-unplated.png", icons / "pythonx150.png" + yield "_resources/pythonwx44.png", icons / "pythonwx44.png" + yield "_resources/pythonwx44$targetsize-44_altform-unplated.png", icons / "pythonwx44.png" + yield "_resources/pythonwx150.png", icons / "pythonwx150.png" + yield "_resources/pythonwx150$targetsize-150_altform-unplated.png", icons / "pythonwx150.png" + sccd = ns.source / SCCD_FILENAME + if sccd.is_file(): + # This should only be set for side-loading purposes. + sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256")) + yield sccd.name, sccd + + +def find_or_add(xml, element, attr=None, always_add=False): + if always_add: + e = None + else: + q = element + if attr: + q += "[@{}='{}']".format(*attr) + e = xml.find(q, APPXMANIFEST_NS) + if e is None: + prefix, _, name = element.partition(":") + name = ET.QName(APPXMANIFEST_NS[prefix or ""], name) + e = ET.SubElement(xml, name) + if attr: + e.set(*attr) + return e + + +def _get_app(xml, appid): + if appid: + app = xml.find( + "m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS + ) + if app is None: + raise LookupError(appid) + else: + app = xml + return app + + +def add_visual(xml, appid, data): + app = _get_app(xml, appid) + e = find_or_add(app, "uap:VisualElements") + for i in data.items(): + e.set(*i) + return e + + +def add_alias(xml, appid, alias, subsystem="windows"): + app = _get_app(xml, appid) + e = find_or_add(app, "m:Extensions") + e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias")) + e = find_or_add(e, "uap5:AppExecutionAlias") + e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem) + e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias)) + + +def add_file_type(xml, appid, name, suffix, parameters='"%1"'): + app = _get_app(xml, appid) + e = find_or_add(app, "m:Extensions") + e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation")) + e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name)) + e.set("Parameters", parameters) + e = find_or_add(e, "uap:SupportedFileTypes") + if isinstance(suffix, str): + suffix = [suffix] + for s in suffix: + ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s + + +def add_application( + ns, xml, appid, executable, aliases, visual_element, subsystem, file_types +): + node = xml.find("m:Applications", APPXMANIFEST_NS) + suffix = "_d.exe" if ns.debug else ".exe" + app = ET.SubElement( + node, + ET.QName(APPXMANIFEST_NS[""], "Application"), + { + "Id": appid, + "Executable": executable + suffix, + "EntryPoint": "Windows.FullTrustApplication", + ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true", + }, + ) + if visual_element: + add_visual(app, None, visual_element) + for alias in aliases: + add_alias(app, None, alias + suffix, subsystem) + if file_types: + add_file_type(app, None, *file_types) + return app + + +def _get_registry_entries(ns, root="", d=None): + r = root if root else PureWindowsPath("") + if d is None: + d = REGISTRY + for key, value in d.items(): + if key == "_condition": + continue + elif isinstance(value, dict): + cond = value.get("_condition") + if cond and not cond(ns): + continue + fullkey = r + for part in PureWindowsPath(key).parts: + fullkey /= part + if len(fullkey.parts) > 1: + yield str(fullkey), None, None + yield from _get_registry_entries(ns, fullkey, value) + elif len(r.parts) > 1: + yield str(r), key, value + + +def add_registry_entries(ns, xml): + e = find_or_add(xml, "m:Extensions") + e = find_or_add(e, "rescap4:Extension") + e.set("Category", "windows.classicAppCompatKeys") + e.set("EntryPoint", "Windows.FullTrustApplication") + e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys")) + for name, valuename, value in _get_registry_entries(ns): + k = ET.SubElement( + e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey") + ) + k.set("Name", name) + if value: + k.set("ValueName", valuename) + k.set("Value", value) + k.set("ValueType", "REG_SZ") + + +def disable_registry_virtualization(xml): + e = find_or_add(xml, "m:Properties") + e = find_or_add(e, "desktop6:RegistryWriteVirtualization") + e.text = "disabled" + e = find_or_add(xml, "m:Capabilities") + e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources")) + + + at public +def get_appxmanifest(ns): + for k, v in APPXMANIFEST_NS.items(): + ET.register_namespace(k, v) + ET.register_namespace("", APPXMANIFEST_NS["m"]) + + xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE)) + NS = APPXMANIFEST_NS + QN = ET.QName + + node = xml.find("m:Identity", NS) + for k in node.keys(): + value = APPX_DATA.get(k) + if value: + node.set(k, value) + + for node in xml.find("m:Properties", NS): + value = APPX_DATA.get(node.tag.rpartition("}")[2]) + if value: + node.text = value + + winver = sys.getwindowsversion()[:3] + if winver < (10, 0, 17763): + winver = 10, 0, 17763 + find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set( + "MaxVersionTested", "{}.{}.{}.0".format(*winver) + ) + + if winver > (10, 0, 17763): + disable_registry_virtualization(xml) + + app = add_application( + ns, + xml, + "Python", + "python", + ["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)], + PYTHON_VE_DATA, + "console", + ("python.file", [".py"]), + ) + + add_application( + ns, + xml, + "PythonW", + "pythonw", + ["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)], + PYTHONW_VE_DATA, + "windows", + ("python.windowedfile", [".pyw"]), + ) + + if ns.include_pip and ns.include_launchers: + add_application( + ns, + xml, + "Pip", + "pip", + ["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)], + PIP_VE_DATA, + "console", + ("python.wheel", [".whl"], 'install "%1"'), + ) + + if ns.include_idle and ns.include_launchers: + add_application( + ns, + xml, + "Idle", + "idle", + ["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)], + IDLE_VE_DATA, + "windows", + None, + ) + + if (ns.source / SCCD_FILENAME).is_file(): + add_registry_entries(ns, xml) + node = xml.find("m:Capabilities", NS) + node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability")) + node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe") + + buffer = io.BytesIO() + xml.write(buffer, encoding="utf-8", xml_declaration=True) + return buffer.getbuffer() + + + at public +def get_resources_xml(ns): + return RESOURCES_XML_TEMPLATE.encode("utf-8") diff --git a/PC/layout/support/catalog.py b/PC/layout/support/catalog.py new file mode 100644 index 000000000000..43121187ed18 --- /dev/null +++ b/PC/layout/support/catalog.py @@ -0,0 +1,44 @@ +""" +File generation for catalog signing non-binary contents. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import sys + +__all__ = ["PYTHON_CAT_NAME", "PYTHON_CDF_NAME"] + + +def public(f): + __all__.append(f.__name__) + return f + + +PYTHON_CAT_NAME = "python.cat" +PYTHON_CDF_NAME = "python.cdf" + + +CATALOG_TEMPLATE = r"""[CatalogHeader] +Name={target.stem}.cat +ResultDir={target.parent} +PublicVersion=1 +CatalogVersion=2 +HashAlgorithms=SHA256 +PageHashes=false +EncodingType= + +[CatalogFiles] +""" + + +def can_sign(file): + return file.is_file() and file.stat().st_size + + + at public +def write_catalog(target, files): + with target.open("w", encoding="utf-8") as cat: + cat.write(CATALOG_TEMPLATE.format(target=target)) + cat.writelines("{}={}\n".format(n, f) for n, f in files if can_sign(f)) diff --git a/PC/layout/support/constants.py b/PC/layout/support/constants.py new file mode 100644 index 000000000000..88ea410b340e --- /dev/null +++ b/PC/layout/support/constants.py @@ -0,0 +1,28 @@ +""" +Constants for generating the layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import struct +import sys + +VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = struct.pack(">i", sys.hexversion) +VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4 +VER_NAME = {"alpha": "a", "beta": "b", "rc": "rc"}.get( + sys.version_info.releaselevel, "" +) +VER_SERIAL = sys.version_info.serial if VER_NAME else "" +VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR) + +PYTHON_DLL_NAME = "python{}{}.dll".format(VER_MAJOR, VER_MINOR) +PYTHON_STABLE_DLL_NAME = "python{}.dll".format(VER_MAJOR) +PYTHON_ZIP_NAME = "python{}{}.zip".format(VER_MAJOR, VER_MINOR) +PYTHON_PTH_NAME = "python{}{}._pth".format(VER_MAJOR, VER_MINOR) + +PYTHON_CHM_NAME = "python{}{}{}{}{}.chm".format( + VER_MAJOR, VER_MINOR, VER_MICRO, VER_NAME, VER_SERIAL +) + +IS_X64 = sys.maxsize > 2 ** 32 diff --git a/PC/layout/support/distutils.command.bdist_wininst.py b/PC/layout/support/distutils.command.bdist_wininst.py new file mode 100644 index 000000000000..6e9b49fe42df --- /dev/null +++ b/PC/layout/support/distutils.command.bdist_wininst.py @@ -0,0 +1,25 @@ +"""distutils.command.bdist_wininst + +Suppress the 'bdist_wininst' command, while still allowing +setuptools to import it without breaking.""" + +from distutils.core import Command +from distutils.errors import DistutilsPlatformError + + +class bdist_wininst(Command): + description = "create an executable installer for MS Windows" + + # Marker for tests that we have the unsupported bdist_wininst + _unsupported = True + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + raise DistutilsPlatformError( + "bdist_wininst is not supported in this Python distribution" + ) diff --git a/PC/layout/support/filesets.py b/PC/layout/support/filesets.py new file mode 100644 index 000000000000..47f727c05784 --- /dev/null +++ b/PC/layout/support/filesets.py @@ -0,0 +1,100 @@ +""" +File sets and globbing helper for make_layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import os + + +class FileStemSet: + def __init__(self, *patterns): + self._names = set() + self._prefixes = [] + self._suffixes = [] + for p in map(os.path.normcase, patterns): + if p.endswith("*"): + self._prefixes.append(p[:-1]) + elif p.startswith("*"): + self._suffixes.append(p[1:]) + else: + self._names.add(p) + + def _make_name(self, f): + return os.path.normcase(f.stem) + + def __contains__(self, f): + bn = self._make_name(f) + return ( + bn in self._names + or any(map(bn.startswith, self._prefixes)) + or any(map(bn.endswith, self._suffixes)) + ) + + +class FileNameSet(FileStemSet): + def _make_name(self, f): + return os.path.normcase(f.name) + + +class FileSuffixSet: + def __init__(self, *patterns): + self._names = set() + self._prefixes = [] + self._suffixes = [] + for p in map(os.path.normcase, patterns): + if p.startswith("*."): + self._names.add(p[1:]) + elif p.startswith("*"): + self._suffixes.append(p[1:]) + elif p.endswith("*"): + self._prefixes.append(p[:-1]) + elif p.startswith("."): + self._names.add(p) + else: + self._names.add("." + p) + + def _make_name(self, f): + return os.path.normcase(f.suffix) + + def __contains__(self, f): + bn = self._make_name(f) + return ( + bn in self._names + or any(map(bn.startswith, self._prefixes)) + or any(map(bn.endswith, self._suffixes)) + ) + + +def _rglob(root, pattern, condition): + dirs = [root] + recurse = pattern[:3] in {"**/", "**\\"} + if recurse: + pattern = pattern[3:] + + while dirs: + d = dirs.pop(0) + if recurse: + dirs.extend( + filter( + condition, (type(root)(f2) for f2 in os.scandir(d) if f2.is_dir()) + ) + ) + yield from ( + (f.relative_to(root), f) + for f in d.glob(pattern) + if f.is_file() and condition(f) + ) + + +def _return_true(f): + return True + + +def rglob(root, patterns, condition=None): + if isinstance(patterns, tuple): + for p in patterns: + yield from _rglob(root, p, condition or _return_true) + else: + yield from _rglob(root, patterns, condition or _return_true) diff --git a/PC/layout/support/logging.py b/PC/layout/support/logging.py new file mode 100644 index 000000000000..30869b949a1c --- /dev/null +++ b/PC/layout/support/logging.py @@ -0,0 +1,93 @@ +""" +Logging support for make_layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import logging +import sys + +__all__ = [] + +LOG = None +HAS_ERROR = False + + +def public(f): + __all__.append(f.__name__) + return f + + + at public +def configure_logger(ns): + global LOG + if LOG: + return + + LOG = logging.getLogger("make_layout") + LOG.level = logging.DEBUG + + if ns.v: + s_level = max(logging.ERROR - ns.v * 10, logging.DEBUG) + f_level = max(logging.WARNING - ns.v * 10, logging.DEBUG) + else: + s_level = logging.ERROR + f_level = logging.INFO + + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(logging.Formatter("{levelname:8s} {message}", style="{")) + handler.setLevel(s_level) + LOG.addHandler(handler) + + if ns.log: + handler = logging.FileHandler(ns.log, encoding="utf-8", delay=True) + handler.setFormatter( + logging.Formatter("[{asctime}]{levelname:8s}: {message}", style="{") + ) + handler.setLevel(f_level) + LOG.addHandler(handler) + + +class BraceMessage: + def __init__(self, fmt, *args, **kwargs): + self.fmt = fmt + self.args = args + self.kwargs = kwargs + + def __str__(self): + return self.fmt.format(*self.args, **self.kwargs) + + + at public +def log_debug(msg, *args, **kwargs): + return LOG.debug(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_info(msg, *args, **kwargs): + return LOG.info(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_warning(msg, *args, **kwargs): + return LOG.warning(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_error(msg, *args, **kwargs): + global HAS_ERROR + HAS_ERROR = True + return LOG.error(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_exception(msg, *args, **kwargs): + global HAS_ERROR + HAS_ERROR = True + return LOG.exception(BraceMessage(msg, *args, **kwargs)) + + + at public +def error_was_logged(): + return HAS_ERROR diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py new file mode 100644 index 000000000000..76d9e34e1f46 --- /dev/null +++ b/PC/layout/support/options.py @@ -0,0 +1,122 @@ +""" +List of optional components. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + +OPTIONS = { + "stable": {"help": "stable ABI stub"}, + "pip": {"help": "pip"}, + "distutils": {"help": "distutils"}, + "tcltk": {"help": "Tcl, Tk and tkinter"}, + "idle": {"help": "Idle"}, + "tests": {"help": "test suite"}, + "tools": {"help": "tools"}, + "venv": {"help": "venv"}, + "dev": {"help": "headers and libs"}, + "symbols": {"help": "symbols"}, + "bdist-wininst": {"help": "bdist_wininst support"}, + "underpth": {"help": "a python._pth file", "not-in-all": True}, + "launchers": {"help": "specific launchers"}, + "appxmanifest": {"help": "an appxmanifest"}, + "props": {"help": "a python.props file"}, + "chm": {"help": "the CHM documentation"}, + "html-doc": {"help": "the HTML documentation"}, +} + + +PRESETS = { + "appx": { + "help": "APPX package", + "options": [ + "stable", + "pip", + "distutils", + "tcltk", + "idle", + "venv", + "dev", + "launchers", + "appxmanifest", + # XXX: Disabled for now "precompile", + ], + }, + "nuget": { + "help": "nuget package", + "options": ["stable", "pip", "distutils", "dev", "props"], + }, + "default": { + "help": "development kit package", + "options": [ + "stable", + "pip", + "distutils", + "tcltk", + "idle", + "tests", + "tools", + "venv", + "dev", + "symbols", + "bdist-wininst", + "chm", + ], + }, + "embed": { + "help": "embeddable package", + "options": ["stable", "zip-lib", "flat-dlls", "underpth", "precompile"], + }, +} + + + at public +def get_argparse_options(): + for opt, info in OPTIONS.items(): + help = "When specified, includes {}".format(info["help"]) + if info.get("not-in-all"): + help = "{}. Not affected by --include-all".format(help) + + yield "--include-{}".format(opt), help + + for opt, info in PRESETS.items(): + help = "When specified, includes default options for {}".format(info["help"]) + yield "--preset-{}".format(opt), help + + +def ns_get(ns, key, default=False): + return getattr(ns, key.replace("-", "_"), default) + + +def ns_set(ns, key, value=True): + k1 = key.replace("-", "_") + k2 = "include_{}".format(k1) + if hasattr(ns, k2): + setattr(ns, k2, value) + elif hasattr(ns, k1): + setattr(ns, k1, value) + else: + raise AttributeError("no argument named '{}'".format(k1)) + + + at public +def update_presets(ns): + for preset, info in PRESETS.items(): + if ns_get(ns, "preset-{}".format(preset)): + for opt in info["options"]: + ns_set(ns, opt) + + if ns.include_all: + for opt in OPTIONS: + if OPTIONS[opt].get("not-in-all"): + continue + ns_set(ns, opt) diff --git a/PC/layout/support/pip.py b/PC/layout/support/pip.py new file mode 100644 index 000000000000..369a923ce139 --- /dev/null +++ b/PC/layout/support/pip.py @@ -0,0 +1,79 @@ +""" +Extraction and file list generation for pip. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import os +import shutil +import subprocess +import sys + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + + at public +def get_pip_dir(ns): + if ns.copy: + if ns.zip_lib: + return ns.copy / "packages" + return ns.copy / "Lib" / "site-packages" + else: + return ns.temp / "packages" + + + at public +def extract_pip_files(ns): + dest = get_pip_dir(ns) + dest.mkdir(parents=True, exist_ok=True) + + src = ns.source / "Lib" / "ensurepip" / "_bundled" + + ns.temp.mkdir(parents=True, exist_ok=True) + wheels = [shutil.copy(whl, ns.temp) for whl in src.glob("*.whl")] + search_path = os.pathsep.join(wheels) + if os.environ.get("PYTHONPATH"): + search_path += ";" + os.environ["PYTHONPATH"] + + env = os.environ.copy() + env["PYTHONPATH"] = search_path + + output = subprocess.check_output( + [ + sys.executable, + "-m", + "pip", + "--no-color", + "install", + "pip", + "setuptools", + "--upgrade", + "--target", + str(dest), + "--no-index", + "--no-cache-dir", + "-f", + str(src), + "--only-binary", + ":all:", + ], + env=env, + ) + + try: + shutil.rmtree(dest / "bin") + except OSError: + pass + + for file in wheels: + try: + os.remove(file) + except OSError: + pass diff --git a/PC/layout/support/props.py b/PC/layout/support/props.py new file mode 100644 index 000000000000..3a047d215058 --- /dev/null +++ b/PC/layout/support/props.py @@ -0,0 +1,110 @@ +""" +Provides .props file. +""" + +import os + +from .constants import * + +__all__ = ["PYTHON_PROPS_NAME"] + + +def public(f): + __all__.append(f.__name__) + return f + + +PYTHON_PROPS_NAME = "python.props" + +PROPS_DATA = { + "PYTHON_TAG": VER_DOT, + "PYTHON_VERSION": os.getenv("PYTHON_NUSPEC_VERSION"), + "PYTHON_PLATFORM": os.getenv("PYTHON_PROPS_PLATFORM"), + "PYTHON_TARGET": "", +} + +if not PROPS_DATA["PYTHON_VERSION"]: + if VER_NAME: + PROPS_DATA["PYTHON_VERSION"] = "{}.{}-{}{}".format( + VER_DOT, VER_MICRO, VER_NAME, VER_SERIAL + ) + else: + PROPS_DATA["PYTHON_VERSION"] = "{}.{}".format(VER_DOT, VER_MICRO) + +if not PROPS_DATA["PYTHON_PLATFORM"]: + PROPS_DATA["PYTHON_PLATFORM"] = "x64" if IS_X64 else "Win32" + +PROPS_DATA["PYTHON_TARGET"] = "_GetPythonRuntimeFilesDependsOn{}{}_{}".format( + VER_MAJOR, VER_MINOR, PROPS_DATA["PYTHON_PLATFORM"] +) + +PROPS_TEMPLATE = r""" + + + $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python_d.exe") + $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python.exe") + $(PythonHome)\include + $(PythonHome)\libs + {PYTHON_TAG} + {PYTHON_VERSION} + + true + false + false + false + + {PYTHON_TARGET};$(GetPythonRuntimeFilesDependsOn) + + + + + $(PythonInclude);%(AdditionalIncludeDirectories) + MultiThreadedDLL + + + $(PythonLibs);%(AdditionalLibraryDirectories) + + + + + + + + <_PythonRuntimeExe Include="$(PythonHome)\python*.dll" /> + <_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" /> + <_PythonRuntimeExe> + %(Filename)%(Extension) + + <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" /> + <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" /> + <_PythonRuntimeDlls> + DLLs\%(Filename)%(Extension) + + <_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" /> + <_PythonRuntimeLib> + Lib\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + +""" + + + at public +def get_props_layout(ns): + if ns.include_all or ns.include_props: + yield "python.props", ns.temp / "python.props" + + + at public +def get_props(ns): + # TODO: Filter contents of props file according to included/excluded items + props = PROPS_TEMPLATE.format_map(PROPS_DATA) + return props.encode("utf-8") diff --git a/Tools/nuget/python.props b/PC/layout/support/python.props similarity index 100% rename from Tools/nuget/python.props rename to PC/layout/support/python.props diff --git a/PC/pylauncher.rc b/PC/pylauncher.rc index 3da3445f5fc4..92987af7138d 100644 --- a/PC/pylauncher.rc +++ b/PC/pylauncher.rc @@ -7,6 +7,11 @@ #include 1 RT_MANIFEST "python.manifest" +#if defined(PY_ICON) +1 ICON DISCARDABLE "icons\python.ico" +#elif defined(PYW_ICON) +1 ICON DISCARDABLE "icons\pythonw.ico" +#else 1 ICON DISCARDABLE "icons\launcher.ico" 2 ICON DISCARDABLE "icons\py.ico" 3 ICON DISCARDABLE "icons\pyc.ico" @@ -14,6 +19,7 @@ 5 ICON DISCARDABLE "icons\python.ico" 6 ICON DISCARDABLE "icons\pythonw.ico" 7 ICON DISCARDABLE "icons\setup.ico" +#endif ///////////////////////////////////////////////////////////////////////////// // diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp new file mode 100644 index 000000000000..b015abd59e66 --- /dev/null +++ b/PC/python_uwp.cpp @@ -0,0 +1,239 @@ +/* Main program when embedded in a UWP application on Windows */ + +#include "Python.h" +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#ifdef PYTHON_UWP_SUPPORTED +#include +#include +#else +#include +#endif + +#ifdef PYTHONW +#ifdef _DEBUG +const wchar_t *PROGNAME = L"pythonw_d.exe"; +#else +const wchar_t *PROGNAME = L"pythonw.exe"; +#endif +#else +#ifdef _DEBUG +const wchar_t *PROGNAME = L"python_d.exe"; +#else +const wchar_t *PROGNAME = L"python.exe"; +#endif +#endif + +static void +set_user_base() +{ +#ifdef PYTHON_UWP_SUPPORTED + wchar_t envBuffer[2048]; + try { + const auto appData = winrt::Windows::Storage::ApplicationData::Current(); + if (appData) { + const auto localCache = appData.LocalCacheFolder(); + if (localCache) { + auto path = localCache.Path(); + if (!path.empty() && + !wcscpy_s(envBuffer, path.c_str()) && + !wcscat_s(envBuffer, L"\\local-packages") + ) { + _wputenv_s(L"PYTHONUSERBASE", envBuffer); + } + } + } + } catch (...) { + } +#endif +} + +static const wchar_t * +get_argv0(const wchar_t *argv0) +{ +#ifdef PYTHON_UWP_SUPPORTED + winrt::hstring installPath; +#else + std::wstring installPath; +#endif + const wchar_t *launcherPath; + wchar_t *buffer; + size_t len; + + launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__"); + if (launcherPath && launcherPath[0]) { + len = wcslen(launcherPath) + 1; + buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); + if (!buffer) { + Py_FatalError("out of memory"); + return NULL; + } + if (wcscpy_s(buffer, len, launcherPath)) { + Py_FatalError("failed to copy to buffer"); + return NULL; + } + return buffer; + } + +#ifdef PYTHON_UWP_SUPPORTED + try { + const auto package = winrt::Windows::ApplicationModel::Package::Current(); + if (package) { + const auto install = package.InstalledLocation(); + if (install) { + installPath = install.Path(); + } + } + } + catch (...) { + } +#endif + + if (!installPath.empty()) { + len = installPath.size() + wcslen(PROGNAME) + 2; + } else { + len = wcslen(argv0) + wcslen(PROGNAME) + 1; + } + + buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); + if (!buffer) { + Py_FatalError("out of memory"); + return NULL; + } + + if (!installPath.empty()) { + if (wcscpy_s(buffer, len, installPath.c_str())) { + Py_FatalError("failed to copy to buffer"); + return NULL; + } + if (wcscat_s(buffer, len, L"\\")) { + Py_FatalError("failed to concatenate backslash"); + return NULL; + } + } else { + if (wcscpy_s(buffer, len, argv0)) { + Py_FatalError("failed to copy argv[0]"); + return NULL; + } + + wchar_t *name = wcsrchr(buffer, L'\\'); + if (name) { + name[1] = L'\0'; + } else { + buffer[0] = L'\0'; + } + } + + if (wcscat_s(buffer, len, PROGNAME)) { + Py_FatalError("failed to concatenate program name"); + return NULL; + } + + return buffer; +} + +static wchar_t * +get_process_name() +{ + DWORD bufferLen = MAX_PATH; + DWORD len = bufferLen; + wchar_t *r = NULL; + + while (!r) { + r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); + if (!r) { + Py_FatalError("out of memory"); + return NULL; + } + len = GetModuleFileNameW(NULL, r, bufferLen); + if (len == 0) { + free((void *)r); + return NULL; + } else if (len == bufferLen && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(r); + r = NULL; + bufferLen *= 2; + } + } + + return r; +} + +int +wmain(int argc, wchar_t **argv) +{ + const wchar_t **new_argv; + int new_argc; + const wchar_t *exeName; + + new_argc = argc; + new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2)); + if (new_argv == NULL) { + Py_FatalError("out of memory"); + return -1; + } + + exeName = get_process_name(); + + new_argv[0] = get_argv0(exeName ? exeName : argv[0]); + for (int i = 1; i < argc; ++i) { + new_argv[i] = argv[i]; + } + + set_user_base(); + + if (exeName) { + const wchar_t *p = wcsrchr(exeName, L'\\'); + if (p) { + const wchar_t *moduleName = NULL; + if (*p++ == L'\\') { + if (wcsnicmp(p, L"pip", 3) == 0) { + moduleName = L"pip"; + _wputenv_s(L"PIP_USER", L"true"); + } + else if (wcsnicmp(p, L"idle", 4) == 0) { + moduleName = L"idlelib"; + } + } + + if (moduleName) { + new_argc += 2; + for (int i = argc; i >= 1; --i) { + new_argv[i + 2] = new_argv[i]; + } + new_argv[1] = L"-m"; + new_argv[2] = moduleName; + } + } + } + + /* Override program_full_path from here so that + sys.executable is set correctly. */ + _Py_SetProgramFullPath(new_argv[0]); + + int result = Py_Main(new_argc, (wchar_t **)new_argv); + + free((void *)exeName); + free((void *)new_argv); + + return result; +} + +#ifdef PYTHONW + +int WINAPI wWinMain( + HINSTANCE hInstance, /* handle to current instance */ + HINSTANCE hPrevInstance, /* handle to previous instance */ + LPWSTR lpCmdLine, /* pointer to command line */ + int nCmdShow /* show state of window */ +) +{ + return wmain(__argc, __wargv); +} + +#endif diff --git a/PC/store_info.txt b/PC/store_info.txt new file mode 100644 index 000000000000..89f36998370f --- /dev/null +++ b/PC/store_info.txt @@ -0,0 +1,146 @@ +# Overview + +NOTE: This file requires more content. + +Since Python 3.7.2, releases have been made through the Microsoft Store +to allow easy installation on Windows 10.0.17763.0 and later. + +# Building + +To build the store package, the PC/layout script should be used. +Execute the directory with the build of Python to package, and pass +"-h" for full command-line options. + +To sideload test builds, you will need a local certificate. +Instructions are available at +https://docs.microsoft.com/windows/uwp/packaging/create-certificate-package-signing. + +After exporting your certificate, you will need the subject name and +SHA256 hash. The `certutil -dump ` command will display this +information. + +To build for sideloading, use these commands in PowerShell: + +``` +$env:APPX_DATA_PUBLISHER= +$env:APPX_DATA_SHA256= +$env:SigningCertificateFile= + +python PC/layout --copy --include-appxmanifest +Tools/msi/make_appx.ps1 python.msix -sign + +Add-AppxPackage python.msix +``` + +(Note that only the last command requires PowerShell, and the others +can be used from Command Prompt. You can also double-click to install +the final package.) + +To build for publishing to the Store, use these commands: + +``` +$env:APPX_DATA_PUBLISHER = $null +$env:APPX_DATA_SHA256 = $null + +python PC/layout --copy --preset-appxmanifest --precompile +Tools/msi/make_appx.ps1 python.msix +``` + +Note that this package cannot be installed locally. It may only be +added to a submission for the store. + + +# Submission Metadata + +This file contains the text that we use to fill out the store listing +for the Microsoft Store. It needs to be entered manually when creating +a new submission via the dashboard at +https://partner.microsoft.com/dashboard. + +We keep it here for convenience and to allow it to be updated via pull +requests. + +## Title + +Python 3.8 + +## Short Title + +Python + +## Description + +Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python?s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms. + +The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python Web site, https://www.python.org/, and may be freely distributed. The same site also contains distributions of and pointers to many free third party Python modules, programs and tools, and additional documentation. + +The Python interpreter is easily extended with new functions and data types implemented in C or C++ (or other languages callable from C). Python is also suitable as an extension language for customizable applications. + +## ShortDescription + +The Python 3.8 interpreter and runtime. + +## Copyright Trademark Information + +(c) Python Software Foundation + +## Additional License Terms + +Visit https://docs.python.org/3.8/license.html for latest license terms. + +PSF LICENSE AGREEMENT FOR PYTHON 3.8 + +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.8 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.8 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright ? 2001-2018 Python Software Foundation; All Rights + Reserved" are retained in Python 3.8 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.8 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.8. + +4. PSF is making Python 3.8 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.8 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.8 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.8, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.8, Licensee agrees + to be bound by the terms and conditions of this License Agreement. + +## Features + +* Easy to install Python runtime +* Supported by core CPython team +* Find Python, Pip and Idle on PATH + +## Search Terms + +* Python +* Scripting +* Interpreter + diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj index 95e3cd50eca5..bd61c0d4f689 100644 --- a/PCbuild/_tkinter.vcxproj +++ b/PCbuild/_tkinter.vcxproj @@ -95,4 +95,10 @@ + + + + + + \ No newline at end of file diff --git a/PCbuild/find_msbuild.bat b/PCbuild/find_msbuild.bat index 57512a01927e..a2810f09c45e 100644 --- a/PCbuild/find_msbuild.bat +++ b/PCbuild/find_msbuild.bat @@ -29,6 +29,16 @@ @where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc" @if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found + at rem VS 2017 and later provide vswhere.exe, which can be used + at if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere + at set _Py_MSBuild_Root= + at for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest') DO @(set _Py_MSBuild_Root=%%i\MSBuild) + at if not defined _Py_MSBuild_Root goto :skip_vswhere + at for %%j in (Current 15.0) DO @if exist "%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe" (set MSBUILD="%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe") + at set _Py_MSBuild_Root= + at if defined MSBUILD @if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio installation) & goto :found +:skip_vswhere + @rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there. @reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul @if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @( diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index 77341b44a1ee..befaa1fed76b 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -9,6 +9,7 @@ true true true + false @@ -52,6 +53,8 @@ + + @@ -70,6 +73,7 @@ + diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index 59b3861ed406..c212d9f8f32c 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -93,6 +93,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_uwp", "python_uwp.vcxproj", "{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvlauncher", "venvlauncher.vcxproj", "{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvwlauncher", "venvwlauncher.vcxproj", "{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythonw_uwp", "pythonw_uwp.vcxproj", "{AB603547-1E2A-45B3-9E09-B04596006393}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -693,6 +701,70 @@ Global {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.Build.0 = Debug|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.ActiveCfg = Debug|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.Build.0 = Debug|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.ActiveCfg = Release|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.Build.0 = Release|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.ActiveCfg = Release|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.Build.0 = Release|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.ActiveCfg = Debug|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.Build.0 = Debug|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.ActiveCfg = Debug|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.Build.0 = Debug|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.ActiveCfg = Release|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.Build.0 = Release|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.ActiveCfg = Release|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.Build.0 = Release|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.ActiveCfg = Debug|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.Build.0 = Debug|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.ActiveCfg = Debug|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.Build.0 = Debug|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.ActiveCfg = Release|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.Build.0 = Release|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.ActiveCfg = Release|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.Build.0 = Release|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.ActiveCfg = Debug|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.Build.0 = Debug|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.ActiveCfg = Debug|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.Build.0 = Debug|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.ActiveCfg = Release|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.Build.0 = Release|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.ActiveCfg = Release|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PCbuild/python_uwp.vcxproj b/PCbuild/python_uwp.vcxproj new file mode 100644 index 000000000000..af187dd4df30 --- /dev/null +++ b/PCbuild/python_uwp.vcxproj @@ -0,0 +1,86 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF} + + + + + Application + false + Unicode + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + %(PreprocessorDefinitions) + /EHsc /std:c++17 %(AdditionalOptions) + + + windowsapp.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} + false + + + + + + diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 4ae2d692eee1..78ec9a16efa7 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -479,4 +479,19 @@ + + + $(VCInstallDir)\Redist\MSVC\$(VCToolsRedistVersion)\ + $(VCRedistDir)x86\ + $(VCRedistDir)$(Platform)\ + + + + + + + + + + diff --git a/PCbuild/pythonw_uwp.vcxproj b/PCbuild/pythonw_uwp.vcxproj new file mode 100644 index 000000000000..79e105877fbe --- /dev/null +++ b/PCbuild/pythonw_uwp.vcxproj @@ -0,0 +1,86 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {AB603547-1E2A-45B3-9E09-B04596006393} + + + + + Application + false + Unicode + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + PYTHONW;%(PreprocessorDefinitions) + /EHsc /std:c++17 %(AdditionalOptions) + + + windowsapp.lib;%(AdditionalDependencies) + Windows + + + + + + + + + + + + + + {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} + false + + + + + + diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat index 4178981195ee..45e189b537f6 100644 --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -37,6 +37,7 @@ set BUILDX64= set TARGET=Rebuild set TESTTARGETDIR= set PGO=-m test -q --pgo +set BUILDMSI=1 set BUILDNUGET=1 set BUILDZIP=1 @@ -61,6 +62,7 @@ if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts +if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 @@ -174,10 +176,12 @@ if "%OUTDIR_PLAT%" EQU "win32" ( ) set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI% -%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true -if errorlevel 1 exit /B -%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false -if errorlevel 1 exit /B +if defined BUILDMSI ( + %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true + if errorlevel 1 exit /B + %MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false + if errorlevel 1 exit /B +) if defined BUILDZIP ( %MSBUILD% "%D%make_zip.proj" /t:Build %BUILDOPTS% %CERTOPTS% /p:OutputPath="%BUILD%en-us" @@ -214,6 +218,7 @@ echo --skip-build (-B) Do not build Python (just do the installers) echo --skip-doc (-D) Do not build documentation echo --pgo Specify PGO command for x64 installers echo --skip-pgo Build x64 installers without using PGO +echo --skip-msi Do not build executable/MSI packages echo --skip-nuget Do not build Nuget packages echo --skip-zip Do not build embeddable package echo --download Specify the full download URL for MSIs diff --git a/Tools/msi/make_appx.ps1 b/Tools/msi/make_appx.ps1 new file mode 100644 index 000000000000..e32bd76a37f1 --- /dev/null +++ b/Tools/msi/make_appx.ps1 @@ -0,0 +1,71 @@ +<# +.Synopsis + Compiles and signs an APPX package +.Description + Given the file listing, ensures all the contents are signed + and builds and signs the final package. +.Parameter mapfile + The location on disk of the text mapping file. +.Parameter msix + The path and name to store the APPX/MSIX. +.Parameter sign + When set, signs the APPX/MSIX. Packages to be published to + the store should not be signed. +.Parameter description + Description to embed in the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$layout, + [Parameter(Mandatory=$true)][string]$msix, + [switch]$sign, + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +Set-Alias makeappx (Find-Tool "makeappx.exe") -Scope Script +Set-Alias makepri (Find-Tool "makepri.exe") -Scope Script + +$msixdir = Split-Path $msix -Parent +if ($msixdir) { + $msixdir = (mkdir -Force $msixdir).FullName +} else { + $msixdir = Get-Location +} +$msix = Join-Path $msixdir (Split-Path $msix -Leaf) + +pushd $layout +try { + if (Test-Path resources.pri) { + del resources.pri + } + $name = ([xml](gc AppxManifest.xml)).Package.Identity.Name + makepri new /pr . /mn AppxManifest.xml /in $name /cf _resources.xml /of _resources.pri /mf appx /o + if (-not $? -or -not (Test-Path _resources.map.txt)) { + throw "makepri step failed" + } + $lines = gc _resources.map.txt + $lines | ?{ -not ($_ -match '"_resources[\w\.]+?"') } | Out-File _resources.map.txt -Encoding utf8 + makeappx pack /f _resources.map.txt /m AppxManifest.xml /o /p $msix + if (-not $?) { + throw "makeappx step failed" + } +} finally { + popd +} + +if ($sign) { + Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files $msix + + if (-not $?) { + throw "Package signing failed" + } +} diff --git a/Tools/msi/make_cat.ps1 b/Tools/msi/make_cat.ps1 new file mode 100644 index 000000000000..70741439869a --- /dev/null +++ b/Tools/msi/make_cat.ps1 @@ -0,0 +1,34 @@ +<# +.Synopsis + Compiles and signs a catalog file. +.Description + Given the CDF definition file, builds and signs a catalog. +.Parameter catalog + The path to the catalog definition file to compile and + sign. It is assumed that the .cat file will be the same + name with a new extension. +.Parameter description + The description to add to the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$catalog, + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +Set-Alias MakeCat (Find-Tool "makecat.exe") -Scope Script + +MakeCat $catalog +if (-not $?) { + throw "Catalog compilation failed" +} +Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files @($catalog -replace 'cdf$', 'cat') diff --git a/Tools/msi/make_zip.proj b/Tools/msi/make_zip.proj index 214111734219..125a434e51f4 100644 --- a/Tools/msi/make_zip.proj +++ b/Tools/msi/make_zip.proj @@ -15,11 +15,12 @@ .zip $(OutputPath)\$(TargetName)$(TargetExt) rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)" - "$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py" - $(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -b "$(BuildPath.TrimEnd(`\`))" - set DOC_FILENAME=python$(PythonVersion).chm + "$(PythonExe)" "$(PySourcePath)PC\layout" + $(Arguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" + $(Arguments) -t "$(IntermediateOutputPath)\zip_$(ArchName)" + $(Arguments) --zip "$(TargetPath)" + $(Arguments) --precompile --zip-lib --include-underpth --include-stable --flat-dlls $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) diff --git a/Tools/msi/make_zip.py b/Tools/msi/make_zip.py deleted file mode 100644 index 58f3b15ef852..000000000000 --- a/Tools/msi/make_zip.py +++ /dev/null @@ -1,250 +0,0 @@ -import argparse -import py_compile -import re -import sys -import shutil -import stat -import os -import tempfile - -from itertools import chain -from pathlib import Path -from zipfile import ZipFile, ZIP_DEFLATED - - -TKTCL_RE = re.compile(r'^(_?tk|tcl).+\.(pyd|dll)', re.IGNORECASE) -DEBUG_RE = re.compile(r'_d\.(pyd|dll|exe|pdb|lib)$', re.IGNORECASE) -PYTHON_DLL_RE = re.compile(r'python\d\d?\.dll$', re.IGNORECASE) - -DEBUG_FILES = { - '_ctypes_test', - '_testbuffer', - '_testcapi', - '_testconsole', - '_testimportmultiple', - '_testmultiphase', - 'xxlimited', - 'python3_dstub', -} - -EXCLUDE_FROM_LIBRARY = { - '__pycache__', - 'idlelib', - 'pydoc_data', - 'site-packages', - 'tkinter', - 'turtledemo', -} - -EXCLUDE_FROM_EMBEDDABLE_LIBRARY = { - 'ensurepip', - 'venv', -} - -EXCLUDE_FILE_FROM_LIBRARY = { - 'bdist_wininst.py', -} - -EXCLUDE_FILE_FROM_LIBS = { - 'liblzma', - 'python3stub', -} - -EXCLUDED_FILES = { - 'pyshellext', -} - -def is_not_debug(p): - if DEBUG_RE.search(p.name): - return False - - if TKTCL_RE.search(p.name): - return False - - return p.stem.lower() not in DEBUG_FILES and p.stem.lower() not in EXCLUDED_FILES - -def is_not_debug_or_python(p): - return is_not_debug(p) and not PYTHON_DLL_RE.search(p.name) - -def include_in_lib(p): - name = p.name.lower() - if p.is_dir(): - if name in EXCLUDE_FROM_LIBRARY: - return False - if name == 'test' and p.parts[-2].lower() == 'lib': - return False - if name in {'test', 'tests'} and p.parts[-3].lower() == 'lib': - return False - return True - - if name in EXCLUDE_FILE_FROM_LIBRARY: - return False - - suffix = p.suffix.lower() - return suffix not in {'.pyc', '.pyo', '.exe'} - -def include_in_embeddable_lib(p): - if p.is_dir() and p.name.lower() in EXCLUDE_FROM_EMBEDDABLE_LIBRARY: - return False - - return include_in_lib(p) - -def include_in_libs(p): - if not is_not_debug(p): - return False - - return p.stem.lower() not in EXCLUDE_FILE_FROM_LIBS - -def include_in_tools(p): - if p.is_dir() and p.name.lower() in {'scripts', 'i18n', 'pynche', 'demo', 'parser'}: - return True - - return p.suffix.lower() in {'.py', '.pyw', '.txt'} - -BASE_NAME = 'python{0.major}{0.minor}'.format(sys.version_info) - -FULL_LAYOUT = [ - ('/', '$build', 'python.exe', is_not_debug), - ('/', '$build', 'pythonw.exe', is_not_debug), - ('/', '$build', 'python{}.dll'.format(sys.version_info.major), is_not_debug), - ('/', '$build', '{}.dll'.format(BASE_NAME), is_not_debug), - ('DLLs/', '$build', '*.pyd', is_not_debug), - ('DLLs/', '$build', '*.dll', is_not_debug_or_python), - ('include/', 'include', '*.h', None), - ('include/', 'PC', 'pyconfig.h', None), - ('Lib/', 'Lib', '**/*', include_in_lib), - ('libs/', '$build', '*.lib', include_in_libs), - ('Tools/', 'Tools', '**/*', include_in_tools), -] - -EMBED_LAYOUT = [ - ('/', '$build', 'python*.exe', is_not_debug), - ('/', '$build', '*.pyd', is_not_debug), - ('/', '$build', '*.dll', is_not_debug), - ('{}.zip'.format(BASE_NAME), 'Lib', '**/*', include_in_embeddable_lib), -] - -if os.getenv('DOC_FILENAME'): - FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None)) -if os.getenv('VCREDIST_PATH'): - FULL_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) - EMBED_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) - -def copy_to_layout(target, rel_sources): - count = 0 - - if target.suffix.lower() == '.zip': - if target.exists(): - target.unlink() - - with ZipFile(str(target), 'w', ZIP_DEFLATED) as f: - with tempfile.TemporaryDirectory() as tmpdir: - for s, rel in rel_sources: - if rel.suffix.lower() == '.py': - pyc = Path(tmpdir) / rel.with_suffix('.pyc').name - try: - py_compile.compile(str(s), str(pyc), str(rel), doraise=True, optimize=2) - except py_compile.PyCompileError: - f.write(str(s), str(rel)) - else: - f.write(str(pyc), str(rel.with_suffix('.pyc'))) - else: - f.write(str(s), str(rel)) - count += 1 - - else: - for s, rel in rel_sources: - dest = target / rel - try: - dest.parent.mkdir(parents=True) - except FileExistsError: - pass - if dest.is_file(): - dest.chmod(stat.S_IWRITE) - shutil.copy(str(s), str(dest)) - if dest.is_file(): - dest.chmod(stat.S_IWRITE) - count += 1 - - return count - -def rglob(root, pattern, condition): - dirs = [root] - recurse = pattern[:3] in {'**/', '**\\'} - while dirs: - d = dirs.pop(0) - for f in d.glob(pattern[3:] if recurse else pattern): - if recurse and f.is_dir() and (not condition or condition(f)): - dirs.append(f) - elif f.is_file() and (not condition or condition(f)): - yield f, f.relative_to(root) - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('-s', '--source', metavar='dir', help='The directory containing the repository root', type=Path) - parser.add_argument('-o', '--out', metavar='file', help='The name of the output archive', type=Path, default=None) - parser.add_argument('-t', '--temp', metavar='dir', help='A directory to temporarily extract files into', type=Path, default=None) - parser.add_argument('-e', '--embed', help='Create an embedding layout', action='store_true', default=False) - parser.add_argument('-b', '--build', help='Specify the build directory', type=Path, default=None) - ns = parser.parse_args() - - source = ns.source or (Path(__file__).resolve().parent.parent.parent) - out = ns.out - build = ns.build or Path(sys.exec_prefix) - assert isinstance(source, Path) - assert not out or isinstance(out, Path) - assert isinstance(build, Path) - - if ns.temp: - temp = ns.temp - delete_temp = False - else: - temp = Path(tempfile.mkdtemp()) - delete_temp = True - - if out: - try: - out.parent.mkdir(parents=True) - except FileExistsError: - pass - try: - temp.mkdir(parents=True) - except FileExistsError: - pass - - layout = EMBED_LAYOUT if ns.embed else FULL_LAYOUT - - try: - for t, s, p, c in layout: - if s == '$build': - fs = build - else: - fs = source / s - files = rglob(fs, p, c) - extra_files = [] - if s == 'Lib' and p == '**/*': - extra_files.append(( - source / 'tools' / 'msi' / 'distutils.command.bdist_wininst.py', - Path('distutils') / 'command' / 'bdist_wininst.py' - )) - copied = copy_to_layout(temp / t.rstrip('/'), chain(files, extra_files)) - print('Copied {} files'.format(copied)) - - if ns.embed: - with open(str(temp / (BASE_NAME + '._pth')), 'w') as f: - print(BASE_NAME + '.zip', file=f) - print('.', file=f) - print('', file=f) - print('# Uncomment to run site.main() automatically', file=f) - print('#import site', file=f) - - if out: - total = copy_to_layout(out, rglob(temp, '**/*', None)) - print('Wrote {} files to {}'.format(total, out)) - finally: - if delete_temp: - shutil.rmtree(temp, True) - - -if __name__ == "__main__": - sys.exit(int(main() or 0)) diff --git a/Tools/msi/sdktools.psm1 b/Tools/msi/sdktools.psm1 new file mode 100644 index 000000000000..81a74d3679d7 --- /dev/null +++ b/Tools/msi/sdktools.psm1 @@ -0,0 +1,43 @@ +function Find-Tool { + param([string]$toolname) + + $kitroot = (gp 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots\').KitsRoot10 + $tool = (gci -r "$kitroot\Bin\*\x64\$toolname" | sort FullName -Desc | select -First 1) + if (-not $tool) { + throw "$toolname is not available" + } + Write-Host "Found $toolname at $($tool.FullName)" + return $tool.FullName +} + +Set-Alias SignTool (Find-Tool "signtool.exe") -Scope Script + +function Sign-File { + param([string]$certname, [string]$certsha1, [string]$certfile, [string]$description, [string[]]$files) + + if (-not $description) { + $description = $env:SigningDescription; + if (-not $description) { + $description = "Python"; + } + } + if (-not $certname) { + $certname = $env:SigningCertificate; + } + if (-not $certfile) { + $certfile = $env:SigningCertificateFile; + } + + foreach ($a in $files) { + if ($certsha1) { + SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } elseif ($certname) { + SignTool sign /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } elseif ($certfile) { + SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } else { + SignTool sign /a /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } + } +} + diff --git a/Tools/msi/sign_build.ps1 b/Tools/msi/sign_build.ps1 new file mode 100644 index 000000000000..6668eb33a2d1 --- /dev/null +++ b/Tools/msi/sign_build.ps1 @@ -0,0 +1,34 @@ +<# +.Synopsis + Recursively signs the contents of a directory. +.Description + Given the file patterns, code signs the contents. +.Parameter root + The root directory to sign. +.Parameter patterns + The file patterns to sign +.Parameter description + The description to add to the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$root, + [string[]]$patterns=@("*.exe", "*.dll", "*.pyd"), + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +pushd $root +try { + Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files (gci -r $patterns) +} finally { + popd +} \ No newline at end of file diff --git a/Tools/nuget/make_pkg.proj b/Tools/nuget/make_pkg.proj index 9843bc97ccdc..e093a6d0bd76 100644 --- a/Tools/nuget/make_pkg.proj +++ b/Tools/nuget/make_pkg.proj @@ -20,25 +20,28 @@ false $(OutputName).$(NuspecVersion) .nupkg - $(IntermediateOutputPath)\nuget_$(ArchName) + $(IntermediateOutputPath)\nuget_$(ArchName)\ - rmdir /q/s "$(IntermediateOutputPath)" + rmdir /q/s "$(IntermediateOutputPath.TrimEnd(`\`))" - "$(PythonExe)" "$(MSBuildThisFileDirectory)\..\msi\make_zip.py" - $(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))" + "$(PythonExe)" "$(PySourcePath)PC\layout" + $(PythonArguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" + $(PythonArguments) -t "$(IntermediateOutputPath)obj" + $(PythonArguments) --copy "$(IntermediateOutputPath)pkg" + $(PythonArguments) --include-dev --include-tools --include-pip --include-stable --include-launcher --include-props - "$(IntermediateOutputPath)\python.exe" -B -c "import sys; sys.path.append(r'$(PySourcePath)\Lib'); import ensurepip; ensurepip._main()" - "$(IntermediateOutputPath)\python.exe" -B -m pip install -U $(Packages) + "$(IntermediateOutputPath)pkg\pip.exe" -B -m pip install -U $(Packages) - "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)" + "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)pkg" "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))" $(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))" $(NugetArguments) -Version "$(NuspecVersion)" $(NugetArguments) -NoPackageAnalysis -NonInteractive - set DOC_FILENAME=python$(PythonVersion).chm $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) + $(Environment)%0D%0Aset PYTHON_NUSPEC_VERSION=$(NuspecVersion) + $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=$(Platform) + $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=Win32 $(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" >nul 2>nul @@ -48,22 +51,7 @@ - - - - - - <_PropsContents>$([System.IO.File]::ReadAllText('python.props')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TAG$$', '$(MajorVersionNumber).$(MinorVersionNumber)')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_VERSION$$', '$(NuspecVersion)')) - <_PropsContents Condition="$(Platform) == 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', 'Win32')) - <_PropsContents Condition="$(Platform) != 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', '$(Platform)')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TARGET$$', '_GetPythonRuntimeFilesDependsOn$(MajorVersionNumber)$(MinorVersionNumber)_$(Platform)')) - <_ExistingContents Condition="Exists('$(IntermediateOutputPath)\python.props')">$([System.IO.File]::ReadAllText('$(IntermediateOutputPath)\python.props')) - - + From webhook-mailer at python.org Mon Dec 10 22:52:41 2018 From: webhook-mailer at python.org (Steve Dower) Date: Tue, 11 Dec 2018 03:52:41 -0000 Subject: [Python-checkins] bpo-35401: Update Windows build to OpenSSL 1.1.0j (GH-11088) Message-ID: https://github.com/python/cpython/commit/4824385fec0a1de99b4183f995a3e4923771bf64 commit: 4824385fec0a1de99b4183f995a3e4923771bf64 branch: master author: Steve Dower committer: GitHub date: 2018-12-10T19:52:36-08:00 summary: bpo-35401: Update Windows build to OpenSSL 1.1.0j (GH-11088) files: A Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst M PCbuild/get_externals.bat M PCbuild/prepare_ssl.bat M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst b/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst new file mode 100644 index 000000000000..5854388d067d --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst @@ -0,0 +1 @@ +Updates Windows build to OpenSSL 1.1.0j diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 9d4faee5f7cf..7a8de1e7612c 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -49,7 +49,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.6 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.0i +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.0j set libraries=%libraries% sqlite-3.21.0.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.8.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.8.0 @@ -72,7 +72,7 @@ for %%e in (%libraries%) do ( echo.Fetching external binaries... set binaries= -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.0i +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.0j if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.8.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/prepare_ssl.bat b/PCbuild/prepare_ssl.bat index bfdac5404423..bd4b548528c5 100644 --- a/PCbuild/prepare_ssl.bat +++ b/PCbuild/prepare_ssl.bat @@ -23,8 +23,6 @@ setlocal if "%PCBUILD%"=="" (set PCBUILD=%~dp0) if "%EXTERNALS_DIR%"=="" (set EXTERNALS_DIR=%PCBUILD%\..\externals) -set OUT= -set SRC= set ORG_SETTING= :CheckOpts @@ -32,19 +30,12 @@ if "%~1"=="-h" shift & goto Usage if "%~1"=="--certificate" (set SigningCertificate=%~2) && shift && shift & goto CheckOpts if "%~1"=="-c" (set SigningCertificate=%~2) && shift && shift & goto CheckOpts if "%~1"=="--organization" (set ORG_SETTING=--organization "%~2") && shift && shift && goto CheckOpts -if "%~1"=="-i" (SET SRC=$~2) && shift && shift && goto CheckOpts -if "%~1"=="--in" (SET SRC=$~2) && shift && shift && goto CheckOpts -if "%~1"=="-o" (set OUT=$~2) && shift && shift && goto CheckOpts -if "%~1"=="--out" (set OUT=$~2) && shift && shift && goto CheckOpts if "%~1"=="" goto Build echo Unrecognized option: %1 goto Usage :Build -if not defined SRC (echo --in directory is required & exit /b 1) -if not defined OUT (echo --out directory is required & exit /b 1) - call "%PCBUILD%\find_msbuild.bat" %MSBUILD% if ERRORLEVEL 1 (echo Cannot locate MSBuild.exe on PATH or as MSBUILD variable & exit /b 2) diff --git a/PCbuild/python.props b/PCbuild/python.props index f474e6f07e6d..f83d4df0d59f 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -49,8 +49,8 @@ $(ExternalsDir)sqlite-3.21.0.0\ $(ExternalsDir)bzip2-1.0.6\ $(ExternalsDir)xz-5.2.2\ - $(ExternalsDir)openssl-1.1.0i\ - $(ExternalsDir)openssl-bin-1.1.0i\$(ArchName)\ + $(ExternalsDir)openssl-1.1.0j\ + $(ExternalsDir)openssl-bin-1.1.0j\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.11\ From webhook-mailer at python.org Mon Dec 10 22:52:54 2018 From: webhook-mailer at python.org (Steve Dower) Date: Tue, 11 Dec 2018 03:52:54 -0000 Subject: [Python-checkins] bpo-35401: Updates Windows build to OpenSSL 1.0.2q (GH-11089) Message-ID: https://github.com/python/cpython/commit/309d7207f691b3eaa988d2293b9d023943982a9f commit: 309d7207f691b3eaa988d2293b9d023943982a9f branch: 3.6 author: Steve Dower committer: GitHub date: 2018-12-10T19:52:51-08:00 summary: bpo-35401: Updates Windows build to OpenSSL 1.0.2q (GH-11089) files: A Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst M PCbuild/get_externals.bat M PCbuild/libeay.vcxproj M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst b/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst new file mode 100644 index 000000000000..a804473fd6db --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst @@ -0,0 +1 @@ +Updates Windows build to OpenSSL 1.0.2q diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index aff44786b706..016ecdd752b1 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -41,7 +41,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.6 -if NOT "%IncludeSSL%"=="false" set libraries=%libraries% openssl-1.0.2p +if NOT "%IncludeSSL%"=="false" set libraries=%libraries% openssl-1.0.2q set libraries=%libraries% sqlite-3.21.0.0 if NOT "%IncludeTkinter%"=="false" set libraries=%libraries% tcl-core-8.6.6.0 if NOT "%IncludeTkinter%"=="false" set libraries=%libraries% tk-8.6.6.0 diff --git a/PCbuild/libeay.vcxproj b/PCbuild/libeay.vcxproj index 9662cd4b7150..31591bc13508 100644 --- a/PCbuild/libeay.vcxproj +++ b/PCbuild/libeay.vcxproj @@ -533,6 +533,7 @@ + @@ -725,9 +726,7 @@ - diff --git a/PCbuild/python.props b/PCbuild/python.props index b29669b732b1..3022ca50c247 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -49,7 +49,7 @@ $(ExternalsDir)sqlite-3.21.0.0\ $(ExternalsDir)bzip2-1.0.6\ $(ExternalsDir)xz-5.2.2\ - $(ExternalsDir)openssl-1.0.2p\ + $(ExternalsDir)openssl-1.0.2q\ $(opensslDir)include32 $(opensslDir)include64 $(ExternalsDir)\nasm-2.11.06\ From webhook-mailer at python.org Mon Dec 10 22:58:56 2018 From: webhook-mailer at python.org (Steve Dower) Date: Tue, 11 Dec 2018 03:58:56 -0000 Subject: [Python-checkins] bpo-34977: Add Windows App Store package (GH-11027) Message-ID: https://github.com/python/cpython/commit/0e4ad88ff8956d9289ab0a1314636ac15b374459 commit: 0e4ad88ff8956d9289ab0a1314636ac15b374459 branch: 3.7 author: Steve Dower committer: GitHub date: 2018-12-10T19:58:52-08:00 summary: bpo-34977: Add Windows App Store package (GH-11027) Also adds the PC/layout script for generating layouts on Windows. files: A .azure-pipelines/windows-appx-test.yml A Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst A PC/classicAppCompat.can.xml A PC/classicAppCompat.cat A PC/classicAppCompat.sccd A PC/icons/pythonwx150.png A PC/icons/pythonwx44.png A PC/icons/pythonx150.png A PC/icons/pythonx44.png A PC/icons/pythonx50.png A PC/layout/__init__.py A PC/layout/__main__.py A PC/layout/main.py A PC/layout/support/__init__.py A PC/layout/support/appxmanifest.py A PC/layout/support/catalog.py A PC/layout/support/constants.py A PC/layout/support/distutils.command.bdist_wininst.py A PC/layout/support/filesets.py A PC/layout/support/logging.py A PC/layout/support/options.py A PC/layout/support/pip.py A PC/layout/support/props.py A PC/layout/support/python.props A PC/python_uwp.cpp A PC/store_info.txt A PCbuild/python_uwp.vcxproj A PCbuild/pythonw_uwp.vcxproj A Tools/msi/make_appx.ps1 A Tools/msi/make_cat.ps1 A Tools/msi/sdktools.psm1 A Tools/msi/sign_build.ps1 D Tools/msi/make_zip.py D Tools/nuget/python.props M .azure-pipelines/windows-steps.yml M .gitattributes M Doc/make.bat M Doc/using/windows.rst M Lib/test/test_pathlib.py M PC/launcher.c M PC/pylauncher.rc M PCbuild/_tkinter.vcxproj M PCbuild/find_msbuild.bat M PCbuild/pcbuild.proj M PCbuild/pcbuild.sln M PCbuild/pythoncore.vcxproj M Tools/msi/buildrelease.bat M Tools/msi/make_zip.proj M Tools/nuget/make_pkg.proj diff --git a/.azure-pipelines/windows-appx-test.yml b/.azure-pipelines/windows-appx-test.yml new file mode 100644 index 000000000000..5f3fe6c94578 --- /dev/null +++ b/.azure-pipelines/windows-appx-test.yml @@ -0,0 +1,67 @@ +jobs: +- job: Prebuild + displayName: Pre-build checks + + pool: + vmImage: ubuntu-16.04 + + steps: + - template: ./prebuild-checks.yml + + +- job: Windows_Appx_Tests + displayName: Windows Appx Tests + dependsOn: Prebuild + condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true')) + + pool: + vmImage: vs2017-win2016 + + strategy: + matrix: + win64: + arch: amd64 + buildOpt: '-p x64' + testRunTitle: '$(Build.SourceBranchName)-win64-appx' + testRunPlatform: win64 + maxParallel: 2 + + steps: + - checkout: self + clean: true + fetchDepth: 5 + + - powershell: | + # Relocate build outputs outside of source directory to make cleaning faster + Write-Host '##vso[task.setvariable variable=Py_IntDir]$(Build.BinariesDirectory)\obj' + # UNDONE: Do not build to a different directory because of broken tests + Write-Host '##vso[task.setvariable variable=Py_OutDir]$(Build.SourcesDirectory)\PCbuild' + Write-Host '##vso[task.setvariable variable=EXTERNAL_DIR]$(Build.BinariesDirectory)\externals' + displayName: Update build locations + + - script: PCbuild\build.bat -e $(buildOpt) + displayName: 'Build CPython' + env: + IncludeUwp: true + + - script: python.bat PC\layout -vv -s "$(Build.SourcesDirectory)" -b "$(Py_OutDir)\$(arch)" -t "$(Py_IntDir)\layout-tmp-$(arch)" --copy "$(Py_IntDir)\layout-$(arch)" --precompile --preset-appx --include-tests + displayName: 'Create APPX layout' + + - script: .\python.exe -m test.pythoninfo + workingDirectory: $(Py_IntDir)\layout-$(arch) + displayName: 'Display build info' + + - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml" --tempdir "$(Py_IntDir)\tmp-$(arch)" + workingDirectory: $(Py_IntDir)\layout-$(arch) + displayName: 'Tests' + env: + PREFIX: $(Py_IntDir)\layout-$(arch) + + - task: PublishTestResults at 2 + displayName: 'Publish Test Results' + inputs: + testResultsFiles: '$(Build.BinariesDirectory)\test-results.xml' + mergeTestResults: true + testRunTitle: $(testRunTitle) + platform: $(testRunPlatform) + condition: succeededOrFailed() diff --git a/.azure-pipelines/windows-steps.yml b/.azure-pipelines/windows-steps.yml index c3175841a9b8..cba00158ad13 100644 --- a/.azure-pipelines/windows-steps.yml +++ b/.azure-pipelines/windows-steps.yml @@ -13,6 +13,8 @@ steps: - script: PCbuild\build.bat -e $(buildOpt) displayName: 'Build CPython' + env: + IncludeUwp: true - script: python.bat -m test.pythoninfo displayName: 'Display build info' diff --git a/.gitattributes b/.gitattributes index 4a487c3c2a14..16237bb2b3ac 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,6 +19,7 @@ # Specific binary files Lib/test/sndhdrdata/sndhdr.* binary +PC/classicAppCompat.* binary # Text files that should not be subject to eol conversion Lib/test/cjkencodings/* -text diff --git a/Doc/make.bat b/Doc/make.bat index d28dae78e86d..077a1bc74069 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -115,12 +115,16 @@ goto end :build if not exist "%BUILDDIR%" mkdir "%BUILDDIR%" +rem We ought to move NEWS to %BUILDDIR%\NEWS and point +rem Sphinx at the right location. if exist ..\Misc\NEWS ( echo.Copying Misc\NEWS to build\NEWS + if not exist build mkdir build copy ..\Misc\NEWS build\NEWS > nul ) else if exist ..\Misc\NEWS.D ( if defined BLURB ( echo.Merging Misc/NEWS with %BLURB% + if not exist build mkdir build %BLURB% merge -f build\NEWS ) else ( echo.No Misc/NEWS file and Blurb is not available. diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 087ab6f4877a..8654bc2b024a 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -12,9 +12,6 @@ This document aims to give an overview of Windows-specific behaviour you should know about when using Python on Microsoft Windows. -Installing Python -================= - Unlike most Unix systems and services, Windows does not include a system supported installation of Python. To make Python available, the CPython team has compiled Windows installers (MSI packages) with every `release @@ -24,15 +21,37 @@ core interpreter and library being used by a single user. The installer is also able to install for all users of a single machine, and a separate ZIP file is available for application-local distributions. -Supported Versions ------------------- - As specified in :pep:`11`, a Python release only supports a Windows platform while Microsoft considers the platform under extended support. This means that Python |version| supports Windows Vista and newer. If you require Windows XP support then please install Python 3.4. -Installation Steps +There are a number of different installers available for Windows, each with +certain benefits and downsides. + +:ref:`windows-full` contains all components and is the best option for +developers using Python for any kind of project. + +:ref:`windows-store` is a simple installation of Python that is suitable for +running scripts and packages, and using IDLE or other development environments. +It requires Windows 10, but can be safely installed without corrupting other +programs. It also provides many convenient commands for launching Python and +its tools. + +:ref:`windows-nuget` are lightweight installations intended for continuous +integration systems. It can be used to build Python packages or run scripts, +but is not updateable and has no user interface tools. + +:ref:`windows-embeddable` is a minimal package of Python suitable for +embedding into a larger application. + + +.. _windows-full: + +The full installer +================== + +Installation steps ------------------ Four Python |version| installers are available for download - two each for the @@ -264,39 +283,199 @@ settings and replace any that have been removed or modified. "Uninstall" will remove Python entirely, with the exception of the :ref:`launcher`, which has its own entry in Programs and Features. -Other Platforms ---------------- -With ongoing development of Python, some platforms that used to be supported -earlier are no longer supported (due to the lack of users or developers). -Check :pep:`11` for details on all unsupported platforms. +.. _windows-store: -* `Windows CE `_ is still supported. -* The `Cygwin `_ installer offers to install the Python - interpreter as well (cf. `Cygwin package source - `_, `Maintainer releases - `_) +The Microsoft Store package +=========================== -See `Python for Windows `_ -for detailed information about platforms with pre-compiled installers. +.. versionadded:: 3.7.2 -.. seealso:: +.. note:: + The Microsoft Store package is currently considered unstable while its + interactions with other tools and other copies of Python are evaluated. + While Python itself is stable, this installation method may change its + behavior and capabilities during Python 3.7 releases. - `Python on XP `_ - "7 Minutes to "Hello World!"" - by Richard Dooling, 2006 +The Microsoft Store package is an easily installable Python interpreter that +is intended mainly for interactive use, for example, by students. - `Installing on Windows `_ - in "`Dive into Python: Python from novice to pro - `_" - by Mark Pilgrim, 2004, - ISBN 1-59059-356-1 +To install the package, ensure you have the latest Windows 10 updates and +search the Microsoft Store app for "Python |version|". Ensure that the app +you select is published by the Python Software Foundation, and install it. - `For Windows users `_ - in "Installing Python" - in "`A Byte of Python `_" - by Swaroop C H, 2003 +.. warning:: + Python will always be available for free on the Microsoft Store. If you + are asked to pay for it, you have not selected the correct package. + +After installation, Python may be launched by finding it in Start. +Alternatively, it will be available from any Command Prompt or PowerShell +session by typing ``python``. Further, pip and IDLE may be used by typing +``pip`` or ``idle``. IDLE can also be found in Start. + +All three commands are also available with version number suffixes, for +example, as ``python3.exe`` and ``python3.x.exe`` as well as +``python.exe`` (where ``3.x`` is the specific version you want to launch, +such as |version|). + +Virtual environments can be created with ``python -m venv`` and activated +and used as normal. + +If you have installed another version of Python and added it to your +``PATH`` variable, it will be available as ``python.exe`` rather than the +one from the Microsoft Store. To access the new installation, use +``python3.exe`` or ``python3.x.exe``. + +To remove Python, open Settings and use Apps and Features, or else find +Python in Start and right-click to select Uninstall. Uninstalling will +remove all packages you installed directly into this Python installation, but +will not remove any virtual environments + +Known Issues +------------ + +Currently, the ``py.exe`` launcher cannot be used to start Python when it +has been installed from the Microsoft Store. + +Because of restrictions on Microsoft Store apps, Python scripts may not have +full write access to shared locations such as ``TEMP`` and the registry. +Instead, it will write to a private copy. If your scripts must modify the +shared locations, you will need to install the full installer. + + +.. _windows-nuget: + +The nuget.org packages +====================== + +.. versionadded:: 3.5.2 + +The nuget.org package is a reduced size Python environment intended for use on +continuous integration and build systems that do not have a system-wide +install of Python. While nuget is "the package manager for .NET", it also works +perfectly fine for packages containing build-time tools. + +Visit `nuget.org `_ for the most up-to-date information +on using nuget. What follows is a summary that is sufficient for Python +developers. + +The ``nuget.exe`` command line tool may be downloaded directly from +``https://aka.ms/nugetclidl``, for example, using curl or PowerShell. With the +tool, the latest version of Python for 64-bit or 32-bit machines is installed +using:: + + nuget.exe install python -ExcludeVersion -OutputDirectory . + nuget.exe install pythonx86 -ExcludeVersion -OutputDirectory . + +To select a particular version, add a ``-Version 3.x.y``. The output directory +may be changed from ``.``, and the package will be installed into a +subdirectory. By default, the subdirectory is named the same as the package, +and without the ``-ExcludeVersion`` option this name will include the specific +version installed. Inside the subdirectory is a ``tools`` directory that +contains the Python installation:: + + # Without -ExcludeVersion + > .\python.3.5.2\tools\python.exe -V + Python 3.5.2 + + # With -ExcludeVersion + > .\python\tools\python.exe -V + Python 3.5.2 + +In general, nuget packages are not upgradeable, and newer versions should be +installed side-by-side and referenced using the full path. Alternatively, +delete the package directory manually and install it again. Many CI systems +will do this automatically if they do not preserve files between builds. + +Alongside the ``tools`` directory is a ``build\native`` directory. This +contains a MSBuild properties file ``python.props`` that can be used in a +C++ project to reference the Python install. Including the settings will +automatically use the headers and import libraries in your build. + +The package information pages on nuget.org are +`www.nuget.org/packages/python `_ +for the 64-bit version and `www.nuget.org/packages/pythonx86 +`_ for the 32-bit version. + + +.. _windows-embeddable: + +The embeddable package +====================== + +.. versionadded:: 3.5 + +The embedded distribution is a ZIP file containing a minimal Python environment. +It is intended for acting as part of another application, rather than being +directly accessed by end-users. + +When extracted, the embedded distribution is (almost) fully isolated from the +user's system, including environment variables, system registry settings, and +installed packages. The standard library is included as pre-compiled and +optimized ``.pyc`` files in a ZIP, and ``python3.dll``, ``python37.dll``, +``python.exe`` and ``pythonw.exe`` are all provided. Tcl/tk (including all +dependants, such as Idle), pip and the Python documentation are not included. + +.. note:: + + The embedded distribution does not include the `Microsoft C Runtime + `_ and it is + the responsibility of the application installer to provide this. The + runtime may have already been installed on a user's system previously or + automatically via Windows Update, and can be detected by finding + ``ucrtbase.dll`` in the system directory. + +Third-party packages should be installed by the application installer alongside +the embedded distribution. Using pip to manage dependencies as for a regular +Python installation is not supported with this distribution, though with some +care it may be possible to include and use pip for automatic updates. In +general, third-party packages should be treated as part of the application +("vendoring") so that the developer can ensure compatibility with newer +versions before providing updates to users. + +The two recommended use cases for this distribution are described below. + +Python Application +------------------ + +An application written in Python does not necessarily require users to be aware +of that fact. The embedded distribution may be used in this case to include a +private version of Python in an install package. Depending on how transparent it +should be (or conversely, how professional it should appear), there are two +options. + +Using a specialized executable as a launcher requires some coding, but provides +the most transparent experience for users. With a customized launcher, there are +no obvious indications that the program is running on Python: icons can be +customized, company and version information can be specified, and file +associations behave properly. In most cases, a custom launcher should simply be +able to call ``Py_Main`` with a hard-coded command line. + +The simpler approach is to provide a batch file or generated shortcut that +directly calls the ``python.exe`` or ``pythonw.exe`` with the required +command-line arguments. In this case, the application will appear to be Python +and not its actual name, and users may have trouble distinguishing it from other +running Python processes or file associations. + +With the latter approach, packages should be installed as directories alongside +the Python executable to ensure they are available on the path. With the +specialized launcher, packages can be located in other locations as there is an +opportunity to specify the search path before launching the application. + +Embedding Python +---------------- + +Applications written in native code often require some form of scripting +language, and the embedded Python distribution can be used for this purpose. In +general, the majority of the application is in native code, and some part will +either invoke ``python.exe`` or directly use ``python3.dll``. For either case, +extracting the embedded distribution to a subdirectory of the application +installation is sufficient to provide a loadable Python interpreter. + +As with the application use, packages can be installed to any location as there +is an opportunity to specify search paths before initializing the interpreter. +Otherwise, there is no fundamental differences between using the embedded +distribution and a regular installation. Alternative bundles @@ -441,6 +620,8 @@ appropriate version of Python. It will prefer per-user installations over system-wide ones, and orders by language version rather than using the most recently installed version. +The launcher was originally specified in :pep:`397`. + Getting started --------------- @@ -922,95 +1103,19 @@ For extension modules, consult :ref:`building-on-windows`. by Trent Apted et al, 2007 -Embedded Distribution -===================== - -.. versionadded:: 3.5 - -The embedded distribution is a ZIP file containing a minimal Python environment. -It is intended for acting as part of another application, rather than being -directly accessed by end-users. - -When extracted, the embedded distribution is (almost) fully isolated from the -user's system, including environment variables, system registry settings, and -installed packages. The standard library is included as pre-compiled and -optimized ``.pyc`` files in a ZIP, and ``python3.dll``, ``python37.dll``, -``python.exe`` and ``pythonw.exe`` are all provided. Tcl/tk (including all -dependants, such as Idle), pip and the Python documentation are not included. - -.. note:: - - The embedded distribution does not include the `Microsoft C Runtime - `_ and it is - the responsibility of the application installer to provide this. The - runtime may have already been installed on a user's system previously or - automatically via Windows Update, and can be detected by finding - ``ucrtbase.dll`` in the system directory. - -Third-party packages should be installed by the application installer alongside -the embedded distribution. Using pip to manage dependencies as for a regular -Python installation is not supported with this distribution, though with some -care it may be possible to include and use pip for automatic updates. In -general, third-party packages should be treated as part of the application -("vendoring") so that the developer can ensure compatibility with newer -versions before providing updates to users. - -The two recommended use cases for this distribution are described below. - -Python Application ------------------- - -An application written in Python does not necessarily require users to be aware -of that fact. The embedded distribution may be used in this case to include a -private version of Python in an install package. Depending on how transparent it -should be (or conversely, how professional it should appear), there are two -options. - -Using a specialized executable as a launcher requires some coding, but provides -the most transparent experience for users. With a customized launcher, there are -no obvious indications that the program is running on Python: icons can be -customized, company and version information can be specified, and file -associations behave properly. In most cases, a custom launcher should simply be -able to call ``Py_Main`` with a hard-coded command line. - -The simpler approach is to provide a batch file or generated shortcut that -directly calls the ``python.exe`` or ``pythonw.exe`` with the required -command-line arguments. In this case, the application will appear to be Python -and not its actual name, and users may have trouble distinguishing it from other -running Python processes or file associations. - -With the latter approach, packages should be installed as directories alongside -the Python executable to ensure they are available on the path. With the -specialized launcher, packages can be located in other locations as there is an -opportunity to specify the search path before launching the application. - -Embedding Python ----------------- - -Applications written in native code often require some form of scripting -language, and the embedded Python distribution can be used for this purpose. In -general, the majority of the application is in native code, and some part will -either invoke ``python.exe`` or directly use ``python3.dll``. For either case, -extracting the embedded distribution to a subdirectory of the application -installation is sufficient to provide a loadable Python interpreter. - -As with the application use, packages can be installed to any location as there -is an opportunity to specify search paths before initializing the interpreter. -Otherwise, there is no fundamental differences between using the embedded -distribution and a regular installation. - -Other resources +Other Platforms =============== -.. seealso:: - - `Python Programming On Win32 `_ - "Help for Windows Programmers" - by Mark Hammond and Andy Robinson, O'Reilly Media, 2000, - ISBN 1-56592-621-8 +With ongoing development of Python, some platforms that used to be supported +earlier are no longer supported (due to the lack of users or developers). +Check :pep:`11` for details on all unsupported platforms. - `A Python for Windows Tutorial `_ - by Amanda Birmingham, 2004 +* `Windows CE `_ is still supported. +* The `Cygwin `_ installer offers to install the Python + interpreter as well (cf. `Cygwin package source + `_, `Maintainer releases + `_) - :pep:`397` - Python launcher for Windows - The proposal for the launcher to be included in the Python distribution. +See `Python for Windows `_ +for detailed information about platforms with pre-compiled installers. diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index e436db995ce4..056507ef6fe8 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1519,7 +1519,7 @@ def test_resolve_common(self): # resolves to 'dirB/..' first before resolving to parent of dirB. self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) # Now create absolute symlinks - d = support._longpath(tempfile.mkdtemp(suffix='-dirD')) + d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) self.addCleanup(support.rmtree, d) os.symlink(os.path.join(d), join('dirA', 'linkX')) os.symlink(join('dirB'), os.path.join(d, 'linkY')) diff --git a/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst new file mode 100644 index 000000000000..8e1a4ba84880 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-10-30-13-39-17.bpo-34977.0l7_QV.rst @@ -0,0 +1 @@ +Adds support for building a Windows App Store package diff --git a/PC/classicAppCompat.can.xml b/PC/classicAppCompat.can.xml new file mode 100644 index 000000000000..f00475c8da31 --- /dev/null +++ b/PC/classicAppCompat.can.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/PC/classicAppCompat.cat b/PC/classicAppCompat.cat new file mode 100644 index 000000000000..3d213596accf Binary files /dev/null and b/PC/classicAppCompat.cat differ diff --git a/PC/classicAppCompat.sccd b/PC/classicAppCompat.sccd new file mode 100644 index 000000000000..97648985a2cc --- /dev/null +++ b/PC/classicAppCompat.sccd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + MIIq5AYJKoZIhvcNAQcCoIIq1TCCKtECAQExDzANBglghkgBZQMEAgEFADCCARAGCSsGAQQBgjcKAaCCAQEwgf4wDAYKKwYBBAGCNwwBAQQQaM+L42jwBUGvBczrtolMmhcNMTgxMTMwMDA1OTAzWjAOBgorBgEEAYI3DAEDBQAwgbwwKgQUWKcU3R38DGPlKK33XGIwKtVL1r4xEjAQBgorBgEEAYI3DAIDMQKCADCBjQQg3K+KBOQX7HfxjRNZC9cx8gIPkEhPRO1nJFRdWQrVEJ4xaTAQBgorBgEEAYI3DAIDMQKCADBVBgorBgEEAYI3AgEEMUcwRTAQBgorBgEEAYI3AgEZogKAADAxMA0GCWCGSAFlAwQCAQUABCDcr4oE5Bfsd/GNE1kL1zHyAg+QSE9E7WckVF1ZCtUQnqCCFFAwggZSMIIEOqADAgECAhMzAAMu49KhfNamygpWAAIAAy7jMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQDEx5NaWNyb3NvZnQgTWFya2V0cGxhY2UgQ0EgRyAwMTMwHhcNMTgxMTMwMDA1NTA1WhcNMTgxMjAzMDA1NTA1WjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwpcimfAx3HEpba1GLL/gDaRVddHE5PXTRmwlgaz8kt6/rq5rlrPFnCnbIc5818v0xJIznastbmrq26xyCEHyMLBKnyneTKE36I7+TGjcY0D7ow+o2vY7LDKMCTGlh31fx1Tvrl+5xTbWX5jdLU/3MB5faeOGh+0Knzwx1KDoXWgPtfXnD8I5jxJieoWoCwCjKTJgBOklLy9nbOalxf0h+xQRy2p5fj+PxAwQPgHWft36AF7/IMbt9FcXMtg4xdpnTYz4OV3dFOPz4m3M8HwVgNMv89W/1Ozc7uOyZt0Ij1baT6r2L3IjYg5ftzpGqaDOFcWlyDFSdhMR6BIKW8xEpAgMBAAGjggHCMIIBvjAYBgNVHSUBAf8EDjAMBgorBgEEAYI3TBwBMB0GA1UdDgQWBBRdpGYiCytx83FYzPSl+o97YzpxGzAPBgNVHREECDAGggRNT1BSMB8GA1UdIwQYMBaAFEnYB1RFhpclHtZZcRLDcpt0OE3oMGIGA1UdHwRbMFkwV6BVoFOGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyME1hcmtldHBsYWNlJTIwQ0ElMjBHJTIwMDEzKDIpLmNybDBvBggrBgEFBQcBAQRjMGEwXwYIKwYBBQUHMAKGU2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwTWFya2V0cGxhY2UlMjBDQSUyMEclMjAwMTMoMikuY3J0MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXgMDwGCSsGAQQBgjcVBwQvMC0GJSsGAQQBgjcVCIOS9kTqrxCDkY0wgqzgLIKinDE0g+6NOIaE7wACAWQCARYwIAYJKwYBBAGCNxUKAQH/BBAwDjAMBgorBgEEAYI3TBwBMA0GCSqGSIb3DQEBCwUAA4ICAQB3Dk3rXH52CDq/z1fwqn9xI5WGjGmu6oAE4HSc3sNdFrSVMMGm4gTlYGWSZ0wJUUf16mVr/rdXhxuR3MZn+m4Bhdl8KQqYjYbIvCUVj0o9nZ+yT6foeY8bKnB+K5h6rol+mjDj5IfcutC4x2Kx5RrtDtRTSoKA63iZ74DYngPpBGBBgaS2c/QzgqPRAMMRqy2KBDP0miCnpR3F4YlzHGyOZwyHhESjYd9kwF47+msuHS04JZpnGHIvBppKN9XQzH3WezNnnX3lz4AyAUMsMFuARqEnacUhrAHL9n5zMv9CzxDYN1r1/aDh/788RuGuZM+E3NtmbxJJ7j6T5/VtXNBRgKtIq8d2+11j6qvKLigOTxSC25/A70BZBEvllLFnvc1vA2LrC9drwt1KpSmWie1nvpilw7o+gHMOG9utUxGha2VuVizuVNGCywTRRjvmGS1QqTfaun1URVrLfnDINXuTgN1Vwp0J5IGpJ3D8yj01NDQ/RworE+3W/R531NBYova9QRhU/igEw/Aa/q8wjZ4Pzxr9oBIo0Ta3Tv6qIggaWXw0U9+F0J7SCqIhn0d0ATO+E1Qs/SxZIAICLwmqzoLYUAh8q153esBs4uesueqgt5ueyHK8V3WjMS4wxEyVN5ZMET3hFtEshsZC31tLDdjq750U4SgQVmoYSm3F3ZOKQDCCBtcwggS/oAMCAQICCmESRKIAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDMyODIxMDkzOVoXDTMxMDMyODIxMTkzOVowfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAubUaSwGYVsE3MAnPfvmozUhAB3qxBABgJRW1vDp4+tVinXxD32f7k1K89JQ6zDOgS/iDgULC+yFK1K/1Qjac/0M7P6c8v5LSjnWGlERLa/qY32j46S7SLQcit3g2jgoTTO03eUG+9yHZUTGV/FJdRYB8uXhrznJBa+Y+yGwiQKF+m6XFeBH/KORoKFx+dmMoy9EWJ/m/o9IiUj2kzm9C691+vZ/I2w0Bj93W9SPPkV2PCNHlzgfIAoeajWpHmi38Wi3xZHonkzAVBHxPsCBppOoNsWvmAfUM7eBthkSPvFruekyDCPNEYhfGqgqtqLkoBebXLZCOVybF7wTQaLvse60//3P003icRcCoQYgY4NAqrF7j80o5U7DkeXxcB0xvengsaKgiAaV1DKkRbpe98wCqr1AASvm5rAJUYMU+mXmOieV2EelY2jGrenWe9FQpNXYV1NoWBh0WKoFxttoWYAnF705bIWtSZsz08ZfK6WLX4GXNLcPBlgCzfTm1sdKYASWdBbH2haaNhPapFhQQBJHKwnVW2iXErImhuPi45W3MVTZ5D9ASshZx69cLYY6xAdIa+89Kf/uRrsGOVZfahDuDw+NI183iAyzC8z/QRt2P32LYxP0xrCdqVh+DJo2i4NoE8Uk1usCdbVRuBMBQl/AwpOTq7IMvHGElf65CqzUCAwEAAaOCAUswggFHMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBQPU8s/FmEl/mCJHdO5fOiQrbOU0TAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCjuZmM8ZVNDgp9wHsL4RY8KJ8nLinvxFTphNGCrxaLknkYG5pmMhVlX+UB/tSiW8W13W60nggz9u5xwMx7v/1t/Tgm6g2brVyOKI5A7u6/2SIJwkJKFw953K0YIKVT28w9zl8dSJnmRnyR0G86ncWbF6CLQ6A6lBQ9o2mTGVqDr4m35WKAnc6YxUUM1y74mbzFFZr63VHsCcOp3pXWnUqAY1rb6Q6NX1b3clncKqLFm0EjKHcQ56grTbwuuB7pMdh/IFCJR01MQzQbDtpEisbOeZUi43YVAAHKqI1EO9bRwg3frCjwAbml9MmI4utMW94gWFgvrMxIX+n42RBDIjf3Ot3jkT6gt3XeTTmO9bptgblZimhERdkFRUFpVtkocJeLoGuuzP93uH/Yp032wzRH+XmMgujfZv+vnfllJqxdowoQLx55FxLLeTeYfwi/xMSjZO2gNven3U/3KeSCd1kUOFS3AOrwZ0UNOXJeW5JQC6Vfd1BavFZ6FAta1fMLu3WFvNB+FqeHUaU3ya7rmtxJnzk29DeSqXgGNmVSywBS4NajI5jJIKAA6UhNJlsg8CHYwUOKf5ej8OoQCkbadUxXygAfxCfW2YBbujtI+PoyejRFxWUjYFWO5LeTI62UMyqfOEiqugoYjNxmQZla2s4YHVuqIC34R85FQlg9pKQBsDCCBxswggUDoAMCAQICEzMAAABCs21EHGjyqKYAAAAAAEIwDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMB4XDTE4MDQyMDE2NDI0NFoXDTIxMDQyMDE2NDI0NFowgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOZ2KM9Pq1YCOiqWOivmHjUtkMgznTMP/Mr2YfzZeIIJySg1F4WxFZc4jagGHHNof9NRT+GGnktWsXkZuH1DzQEG4Ps1ln8+4vhbDglqu5ymDnd6RmsyoD+8xfc8bBIvE5o6R+ES4/GVD5TqNsOrWbwETaIZVbmTulJLoTS1WSsSjowmbc+sHqZiY8BNJNThUEmXSjuHqkQKKshuiFWYEqOTitp71mBLyH1wN7/jThRzGpolOeFusRNJdb8sEqvNzEN9Qh+Kp6ndzrnjE+t8ixXW3lShyyOOZqQMwsQn9q9T0v7Q69GuojBTFBOHKwigcCHr4xahuN+ZYMk0xGg+sm3Uj7I9mrWTSTiIRMZNIWq3sFg4+rFg48NYfRlXUpONmL7vXq6v1pIU99d2MXQ6uUrnUr1/n5ZiHGCeFcvWwqO8BYHdcTlrSOkayfFp7W9oCk9QO4Xy0h9cQRedRo2kvdTHxIuJS70Hdv6oePPF2ZFaLucUzzwsR4/XMAVKY8Vsm950omsSSOImsMtzavUdQM+wZFxvHTRqVDkF3quPdME0bCZOWB4hQJmd+o2clw+1mpwPu0/M92nA9FJg7MGPxkFaYW7g26jSqUJZ9AcX+Xa5TSIeqMZt3cRVjMTx0T/v73Sv8TpalqIQ5Fde1+hFK07sOAm3TwgzvlVJnbYgp0/rAgMBAAGjggGCMIIBfjASBgkrBgEEAYI3FQEEBQIDAgACMCMGCSsGAQQBgjcVAgQWBBSbJnDhuc3nQXuKuACsPflEbwjbozAdBgNVHQ4EFgQUSdgHVEWGlyUe1llxEsNym3Q4TegwEQYDVR0gBAowCDAGBgRVHSAAMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFA9Tyz8WYSX+YIkd07l86JCts5TRMFcGA1UdHwRQME4wTKBKoEiGRmh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcmwwWwYIKwYBBQUHAQEETzBNMEsGCCsGAQUFBzAChj9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAIa2oa6kvuIHCNfz7anlL0W9tOCt8gQNkxOGRK3yliQIelNQahDJojyEFlHQ2BcHL5oZit3WeSDoYddhojx6YzJIWwfGwtVqgc0JFDKJJ2ZXRYMRsuy01Hn25xob+zRMS6VmV1axQn6uwOSMcgYmzoroh6edjPKu7qXcpt6LmhF2qFvLySA7wBCwfI/rR5/PX6I7a07Av7PpbY6/+2ujd8m1H3hwMrb4Hq3z6gcq62zJ3nDXUbC0Bp6Jt2kV9f0rEFpDK9oxE2qrGBUf8c3O2XirHOgAjRyWjWWtVms+MP8qBIA1NSLrBmToEWVP3sEkQZWMkoZWo4rYEJZpX7UIgdDc9zYNakgTCJqPhqn8AE1sgSSnpqAdMkkP41rTlFCv2ig2QVzDerjGfEv+uPDnlAT0kucbBJxHHvUC4aqUxaTSa0sy2bZ6NWFx8/u0gW8JahzxYvvvZL8SfwaA9P4ETb8pH1jw+6N/LfM2zJrNKhf5hjKa0VDOXUpkYq60OqVVnWJ6oJaSIWNkZKfzPnl/UHA8Bh4qfVrhc9H5PExPhhB9WVTsjf4r+OOVuolJldThcWQqljiPjk5rultr63G5xLyFpxNi4BCrcNQBJFB5wKgOWOyjQTVWTmh2ESaeqZ2aWBjftFHlxJ/qYc7WOGJV0+cHGkB/dvFxmKnv6tuWexiMMYIVUTCCFU0CAQEwgaQwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMwITMwADLuPSoXzWpsoKVgACAAMu4zANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKwYBBAGCNwoBMC8GCSqGSIb3DQEJBDEiBCAS0d3bw2YOODvKFr0S4e3BDnaDcZXUKeBO77yvkWzVojBIBgorBgEEAYI3AgEMMTowOKAegBwATQBpAGMAcgBvAHMAbwBmAHQAIABDAG8AcgBwoRaAFGh0dHA6Ly9NaWNyb3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIBABoap3Y+2k+zFz2cCmkc8xxHnpIygLsUSRMXeXdjPVcYx3o5cPLIixnL6p8+LIrlIagPg23mzTEmnjZaO4aaexk+3XojlHj22w/bEigEDnKyWt5bHeS0UNHJbxEFYRfd84IP1+mSH4c4+GuU9p3LsAMh6wN03MYrGmczUOnlP6YlxHNQbQxnV0sl14yOE5ni9oT4y+l+SllvbV3/Jhwpov68aoP/2MazqxR4QyGfSxhCPJ4UuDHU7IrpnTxGBTL1/oUU8ED0FxyDoH/Sc5OhTLInFqbZaVzm5Mpr12wYUBL4nE5h0Kf6BCKdgM8a+Ti3wMUsBoC79ff3jE9U/xwSneOhghLlMIIS4QYKKwYBBAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQghPy22lwuCYESw8jYhb4F9ZDPJ1LPgSSZgJDkyXYzVt4CBlv98KtAoBgTMjAxODExMzAwMTA1MTkuMTM4WjAEgAIB9KCB0KSBzTCByjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RDA4Mi00QkZELUVFQkExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAOIYOHtm6erB2AAAAAAA4jANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0xODA4MjMyMDI3MDNaFw0xOTExMjMyMDI3MDNaMIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEMDgyLTRCRkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgc2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKirA72FF3NCLW5mfLO/D0EZ5Ycs00oiMSissXLB6WF9GNdP78QzFwAypxW/+qZSczqaHbDH8hlbxkzf3DiYgAdpQjnGkLujwKtWSaP29/lVf7jFqHy9v6eH+LdOi0LvtrPRW34MyCvpxZyOW4H1h3PkxCBL5Ra21sDqgcVL1me0osw8QTURXmI4LyeLdTH3CcI2AgNDXTjsFBf3QsO+JYyAOYWrTcLnywVN6DrigmgrDJk5w+wR4VrHfl2T9PRZbZ+UDt13wwyB9d6IURuzV8lHsAVfF8t9S0aGVPmkQ3c2waOhHpsp6VEM+T5D2Ph8xJX1r82z67WRlmGcOP2NWC0CAwEAAaOCARswggEXMB0GA1UdDgQWBBSJPpD6BsP2p+crDJL232voEtLxezAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQARQHu7ISeBuJSHKuDRI04704cH0B7BYzeEIrD15awviMRcYIfIOHpvGzZOWQgP2Hm0Rr7kvTUu1VrSSaQ7i1gPWdhqMmw5WBnSS5bxeMhhx9UsASeE84vUu82NeZapGSjH38YAb4WT+TtiTkcoI59rA+CTCq108ttIxVfZcr3id76OETIH0HvhlnxOOWjwGy4ul6Za5RoTLG/oo2rrGmVi3FwrNWGezYLBODuEsjzG36lCRtBKC2ZAHfbOz5wtkUHbqh79mUKocjP4r3qxf5TN87yf6g1uTx+J8pdnAi5iHt+ZtangWqnVTE8PoIREWhBVlGFfQdkELUx2Or90aAqWMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQwODItNEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBzZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQByQCUheEOevaI9Zc/3QGrkX42iC6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA36ppYDAiGA8yMDE4MTEyOTIxMzQyNFoYDzIwMTgxMTMwMjEzNDI0WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDfqmlgAgEAMAoCAQACAitfAgH/MAcCAQACAhGtMAoCBQDfq7rgAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbAXXPR9wy4NA0892GGqetaZF+pNClpGcfEpSuHABaZ4Gzr1nY1nmrhexTtr/U6omHALRWzkQwthk0cy+mnEHXyOZGmoEEpgrLgK3AAP5NbK/XbtHQRyZJQyhZScFbOyQycoE8QQalSVOhWxk/bbBMQaQiYVMIexNd/T0KgaDDUMxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAOIYOHtm6erB2AAAAAAA4jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCr9IiSbx6s8MLdxldRG49+4h6CbicW8hWXAicI3jNmhDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIN8BpJSmQCGubWwVa4tW+aMveoHMX/nDnVN8fiDOMsrLMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADiGDh7ZunqwdgAAAAAAOIwIgQgTkOfRvGEZNbr5/hgWclsL4/Q7SOZihE/U0lz2wEMIGcwDQYJKoZIhvcNAQELBQAEggEATlxnCfTzFfTMDvK085zlYPVCroKYW6gKFYnbAhNmrNzcxqALKmIYXpFU7B6HH/vYzkUfCyXpf5tsyEWu0oTySOjyAZ9+2vdaG8nEgjOp0L737lcitgusIjpWtta3Ik0b+mzffnvyjrgTSuKDDni3mxGfvJU77k1Ctempma4H2FJso6Bur0PRH99vIYDu4lHigOSLbeyjR5CiDciBwEVUSA0FxhoFNX1yfpxz3sukOvkaoTduREIjH5LxUjNI1ZTMK/ZkeETI8IPRpWVzAc8q7CujErHKo4sdKej/O2cfUTUHplFLVCGGExpJUCg5FH5jVUUFt75ad8503sdGplggVQ== diff --git a/PC/icons/pythonwx150.png b/PC/icons/pythonwx150.png new file mode 100644 index 000000000000..4c3eb316739c Binary files /dev/null and b/PC/icons/pythonwx150.png differ diff --git a/PC/icons/pythonwx44.png b/PC/icons/pythonwx44.png new file mode 100644 index 000000000000..e3b32a871f90 Binary files /dev/null and b/PC/icons/pythonwx44.png differ diff --git a/PC/icons/pythonx150.png b/PC/icons/pythonx150.png new file mode 100644 index 000000000000..5f8d30418386 Binary files /dev/null and b/PC/icons/pythonx150.png differ diff --git a/PC/icons/pythonx44.png b/PC/icons/pythonx44.png new file mode 100644 index 000000000000..3881daaef233 Binary files /dev/null and b/PC/icons/pythonx44.png differ diff --git a/PC/icons/pythonx50.png b/PC/icons/pythonx50.png new file mode 100644 index 000000000000..7cc3aecd0242 Binary files /dev/null and b/PC/icons/pythonx50.png differ diff --git a/PC/launcher.c b/PC/launcher.c index f3a7ddc5439c..4c620dab7c09 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -1139,6 +1139,7 @@ static PYC_MAGIC magic_values[] = { { 3320, 3351, L"3.5" }, { 3360, 3379, L"3.6" }, { 3390, 3399, L"3.7" }, + { 3400, 3409, L"3.8" }, { 0 } }; diff --git a/PC/layout/__init__.py b/PC/layout/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/PC/layout/__main__.py b/PC/layout/__main__.py new file mode 100644 index 000000000000..f7aa1e6d261f --- /dev/null +++ b/PC/layout/__main__.py @@ -0,0 +1,14 @@ +import sys + +try: + import layout +except ImportError: + # Failed to import our package, which likely means we were started directly + # Add the additional search path needed to locate our module. + from pathlib import Path + + sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +from layout.main import main + +sys.exit(int(main() or 0)) diff --git a/PC/layout/main.py b/PC/layout/main.py new file mode 100644 index 000000000000..217b2b096e07 --- /dev/null +++ b/PC/layout/main.py @@ -0,0 +1,616 @@ +""" +Generates a layout of Python for Windows from a build. + +See python make_layout.py --help for usage. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import argparse +import functools +import os +import re +import shutil +import subprocess +import sys +import tempfile +import zipfile + +from pathlib import Path + +if __name__ == "__main__": + # Started directly, so enable relative imports + __path__ = [str(Path(__file__).resolve().parent)] + +from .support.appxmanifest import * +from .support.catalog import * +from .support.constants import * +from .support.filesets import * +from .support.logging import * +from .support.options import * +from .support.pip import * +from .support.props import * + +BDIST_WININST_FILES_ONLY = FileNameSet("wininst-*", "bdist_wininst.py") +BDIST_WININST_STUB = "PC/layout/support/distutils.command.bdist_wininst.py" + +TEST_PYDS_ONLY = FileStemSet("xxlimited", "_ctypes_test", "_test*") +TEST_DIRS_ONLY = FileNameSet("test", "tests") + +IDLE_DIRS_ONLY = FileNameSet("idlelib") + +TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter") +TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo") +TCLTK_FILES_ONLY = FileNameSet("turtle.py") + +VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip") + +EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext") +EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle") +EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt") +EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*") +EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll") + +REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*") + +LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt") + +PY_FILES = FileSuffixSet(".py") +PYC_FILES = FileSuffixSet(".pyc") +CAT_FILES = FileSuffixSet(".cat") +CDF_FILES = FileSuffixSet(".cdf") + +DATA_DIRS = FileNameSet("data") + +TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser") +TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt") + + +def get_lib_layout(ns): + def _c(f): + if f in EXCLUDE_FROM_LIB: + return False + if f.is_dir(): + if f in TEST_DIRS_ONLY: + return ns.include_tests + if f in TCLTK_DIRS_ONLY: + return ns.include_tcltk + if f in IDLE_DIRS_ONLY: + return ns.include_idle + if f in VENV_DIRS_ONLY: + return ns.include_venv + else: + if f in TCLTK_FILES_ONLY: + return ns.include_tcltk + if f in BDIST_WININST_FILES_ONLY: + return ns.include_bdist_wininst + return True + + for dest, src in rglob(ns.source / "Lib", "**/*", _c): + yield dest, src + + if not ns.include_bdist_wininst: + src = ns.source / BDIST_WININST_STUB + yield Path("distutils/command/bdist_wininst.py"), src + + +def get_tcltk_lib(ns): + if not ns.include_tcltk: + return + + tcl_lib = os.getenv("TCL_LIBRARY") + if not tcl_lib or not os.path.isdir(tcl_lib): + try: + with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f: + tcl_lib = f.read().strip() + except FileNotFoundError: + pass + if not tcl_lib or not os.path.isdir(tcl_lib): + warn("Failed to find TCL_LIBRARY") + return + + for dest, src in rglob(Path(tcl_lib).parent, "**/*"): + yield "tcl/{}".format(dest), src + + +def get_layout(ns): + def in_build(f, dest="", new_name=None): + n, _, x = f.rpartition(".") + n = new_name or n + src = ns.build / f + if ns.debug and src not in REQUIRED_DLLS: + if not src.stem.endswith("_d"): + src = src.parent / (src.stem + "_d" + src.suffix) + if not n.endswith("_d"): + n += "_d" + f = n + "." + x + yield dest + n + "." + x, src + if ns.include_symbols: + pdb = src.with_suffix(".pdb") + if pdb.is_file(): + yield dest + n + ".pdb", pdb + if ns.include_dev: + lib = src.with_suffix(".lib") + if lib.is_file(): + yield "libs/" + n + ".lib", lib + + if ns.include_appxmanifest: + yield from in_build("python_uwp.exe", new_name="python") + yield from in_build("pythonw_uwp.exe", new_name="pythonw") + else: + yield from in_build("python.exe", new_name="python") + yield from in_build("pythonw.exe", new_name="pythonw") + + yield from in_build(PYTHON_DLL_NAME) + + if ns.include_launchers and ns.include_appxmanifest: + if ns.include_pip: + yield from in_build("python_uwp.exe", new_name="pip") + if ns.include_idle: + yield from in_build("pythonw_uwp.exe", new_name="idle") + + if ns.include_stable: + yield from in_build(PYTHON_STABLE_DLL_NAME) + + for dest, src in rglob(ns.build, "vcruntime*.dll"): + yield dest, src + + for dest, src in rglob(ns.build, ("*.pyd", "*.dll")): + if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS: + continue + if src in EXCLUDE_FROM_PYDS: + continue + if src in TEST_PYDS_ONLY and not ns.include_tests: + continue + if src in TCLTK_PYDS_ONLY and not ns.include_tcltk: + continue + + yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/") + + if ns.zip_lib: + zip_name = PYTHON_ZIP_NAME + yield zip_name, ns.temp / zip_name + else: + for dest, src in get_lib_layout(ns): + yield "Lib/{}".format(dest), src + + if ns.include_venv: + yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python") + yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw") + + if ns.include_tools: + + def _c(d): + if d.is_dir(): + return d in TOOLS_DIRS + return d in TOOLS_FILES + + for dest, src in rglob(ns.source / "Tools", "**/*", _c): + yield "Tools/{}".format(dest), src + + if ns.include_underpth: + yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME + + if ns.include_dev: + + def _c(d): + if d.is_dir(): + return d.name != "internal" + return True + + for dest, src in rglob(ns.source / "Include", "**/*.h", _c): + yield "include/{}".format(dest), src + src = ns.source / "PC" / "pyconfig.h" + yield "include/pyconfig.h", src + + for dest, src in get_tcltk_lib(ns): + yield dest, src + + if ns.include_pip: + pip_dir = get_pip_dir(ns) + if not pip_dir.is_dir(): + log_warning("Failed to find {} - pip will not be included", pip_dir) + else: + pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}" + for dest, src in rglob(pip_dir, "**/*"): + if src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB: + continue + yield pkg_root.format(dest), src + + if ns.include_chm: + for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME): + yield "Doc/{}".format(dest), src + + if ns.include_html_doc: + for dest, src in rglob(ns.doc_build / "html", "**/*"): + yield "Doc/html/{}".format(dest), src + + if ns.include_props: + for dest, src in get_props_layout(ns): + yield dest, src + + for dest, src in get_appx_layout(ns): + yield dest, src + + if ns.include_cat: + if ns.flat_dlls: + yield ns.include_cat.name, ns.include_cat + else: + yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat + + +def _compile_one_py(src, dest, name, optimize): + import py_compile + + if dest is not None: + dest = str(dest) + + try: + return Path( + py_compile.compile( + str(src), + dest, + str(name), + doraise=True, + optimize=optimize, + invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, + ) + ) + except py_compile.PyCompileError: + log_warning("Failed to compile {}", src) + return None + + +def _py_temp_compile(src, ns, dest_dir=None): + 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") + return _compile_one_py(src, dest.with_suffix(".pyc"), dest, optimize=2) + + +def _write_to_zip(zf, dest, src, ns): + pyc = _py_temp_compile(src, ns) + if pyc: + try: + zf.write(str(pyc), dest.with_suffix(".pyc")) + finally: + try: + pyc.unlink() + except: + log_exception("Failed to delete {}", pyc) + return + + if src in LIB2TO3_GRAMMAR_FILES: + from lib2to3.pgen2.driver import load_grammar + + tmp = ns.temp / src.name + try: + shutil.copy(src, tmp) + load_grammar(str(tmp)) + for f in ns.temp.glob(src.stem + "*.pickle"): + zf.write(str(f), str(dest.parent / f.name)) + try: + f.unlink() + except: + log_exception("Failed to delete {}", f) + except: + log_exception("Failed to compile {}", src) + finally: + try: + tmp.unlink() + except: + log_exception("Failed to delete {}", tmp) + + zf.write(str(src), str(dest)) + + +def generate_source_files(ns): + if ns.zip_lib: + zip_name = PYTHON_ZIP_NAME + zip_path = ns.temp / zip_name + if zip_path.is_file(): + zip_path.unlink() + elif zip_path.is_dir(): + log_error( + "Cannot create zip file because a directory exists by the same name" + ) + return + log_info("Generating {} in {}", zip_name, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: + for dest, src in get_lib_layout(ns): + _write_to_zip(zf, dest, src, ns) + + if ns.include_underpth: + log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f: + if ns.zip_lib: + print(PYTHON_ZIP_NAME, file=f) + if ns.include_pip: + print("packages", file=f) + else: + print("Lib", file=f) + print("Lib/site-packages", file=f) + if not ns.flat_dlls: + print("DLLs", file=f) + print(".", file=f) + print(file=f) + print("# Uncomment to run site.main() automatically", file=f) + print("#import site", file=f) + + if ns.include_appxmanifest: + log_info("Generating AppxManifest.xml in {}", ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + + with open(ns.temp / "AppxManifest.xml", "wb") as f: + f.write(get_appxmanifest(ns)) + + with open(ns.temp / "_resources.xml", "wb") as f: + f.write(get_resources_xml(ns)) + + if ns.include_pip: + pip_dir = get_pip_dir(ns) + if not (pip_dir / "pip").is_dir(): + log_info("Extracting pip to {}", pip_dir) + pip_dir.mkdir(parents=True, exist_ok=True) + extract_pip_files(ns) + + if ns.include_props: + log_info("Generating {} in {}", PYTHON_PROPS_NAME, ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / PYTHON_PROPS_NAME, "wb") as f: + f.write(get_props(ns)) + + +def _create_zip_file(ns): + if not ns.zip: + return None + + if ns.zip.is_file(): + try: + ns.zip.unlink() + except OSError: + log_exception("Unable to remove {}", ns.zip) + sys.exit(8) + elif ns.zip.is_dir(): + log_error("Cannot create ZIP file because {} is a directory", ns.zip) + sys.exit(8) + + ns.zip.parent.mkdir(parents=True, exist_ok=True) + return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED) + + +def copy_files(files, ns): + if ns.copy: + ns.copy.mkdir(parents=True, exist_ok=True) + + try: + total = len(files) + except TypeError: + total = None + count = 0 + + zip_file = _create_zip_file(ns) + try: + need_compile = [] + in_catalog = [] + + for dest, src in files: + count += 1 + if count % 10 == 0: + if total: + log_info("Processed {:>4} of {} files", count, total) + else: + log_info("Processed {} files", count) + log_debug("Processing {!s}", src) + + if ( + ns.precompile + and src in PY_FILES + and src not in EXCLUDE_FROM_COMPILE + and src.parent not in DATA_DIRS + and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib")) + ): + if ns.copy: + need_compile.append((dest, ns.copy / dest)) + else: + (ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(src, ns.temp / "Lib" / dest) + need_compile.append((dest, ns.temp / "Lib" / dest)) + + if src not in EXCLUDE_FROM_CATALOG: + in_catalog.append((src.name, src)) + + if ns.copy: + log_debug("Copy {} -> {}", src, ns.copy / dest) + (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True) + try: + shutil.copy2(src, ns.copy / dest) + except shutil.SameFileError: + pass + + if ns.zip: + log_debug("Zip {} into {}", src, ns.zip) + zip_file.write(src, str(dest)) + + if need_compile: + for dest, src in need_compile: + compiled = [ + _compile_one_py(src, None, dest, optimize=0), + _compile_one_py(src, None, dest, optimize=1), + _compile_one_py(src, None, dest, optimize=2), + ] + for c in compiled: + if not c: + continue + cdest = Path(dest).parent / Path(c).relative_to(src.parent) + if ns.zip: + log_debug("Zip {} into {}", c, ns.zip) + zip_file.write(c, str(cdest)) + in_catalog.append((cdest.name, cdest)) + + if ns.catalog: + # Just write out the CDF now. Compilation and signing is + # an extra step + log_info("Generating {}", ns.catalog) + ns.catalog.parent.mkdir(parents=True, exist_ok=True) + write_catalog(ns.catalog, in_catalog) + + finally: + if zip_file: + zip_file.close() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-v", help="Increase verbosity", action="count") + parser.add_argument( + "-s", + "--source", + metavar="dir", + help="The directory containing the repository root", + type=Path, + default=None, + ) + parser.add_argument( + "-b", "--build", metavar="dir", help="Specify the build directory", type=Path + ) + parser.add_argument( + "--doc-build", + metavar="dir", + help="Specify the docs build directory", + type=Path, + default=None, + ) + parser.add_argument( + "--copy", + metavar="directory", + help="The name of the directory to copy an extracted layout to", + type=Path, + default=None, + ) + parser.add_argument( + "--zip", + metavar="file", + help="The ZIP file to write all files to", + type=Path, + default=None, + ) + parser.add_argument( + "--catalog", + metavar="file", + help="The CDF file to write catalog entries to", + type=Path, + default=None, + ) + parser.add_argument( + "--log", + metavar="file", + help="Write all operations to the specified file", + type=Path, + default=None, + ) + parser.add_argument( + "-t", + "--temp", + metavar="file", + help="A temporary working directory", + type=Path, + default=None, + ) + parser.add_argument( + "-d", "--debug", help="Include debug build", action="store_true" + ) + parser.add_argument( + "-p", + "--precompile", + help="Include .pyc files instead of .py", + action="store_true", + ) + parser.add_argument( + "-z", "--zip-lib", help="Include library in a ZIP file", action="store_true" + ) + parser.add_argument( + "--flat-dlls", help="Does not create a DLLs directory", action="store_true" + ) + parser.add_argument( + "-a", + "--include-all", + help="Include all optional components", + action="store_true", + ) + parser.add_argument( + "--include-cat", + metavar="file", + help="Specify the catalog file to include", + type=Path, + default=None, + ) + for opt, help in get_argparse_options(): + parser.add_argument(opt, help=help, action="store_true") + + ns = parser.parse_args() + update_presets(ns) + + ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent) + ns.build = ns.build or Path(sys.executable).parent + ns.temp = ns.temp or Path(tempfile.mkdtemp()) + ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build") + if not ns.source.is_absolute(): + ns.source = (Path.cwd() / ns.source).resolve() + if not ns.build.is_absolute(): + ns.build = (Path.cwd() / ns.build).resolve() + if not ns.temp.is_absolute(): + ns.temp = (Path.cwd() / ns.temp).resolve() + if not ns.doc_build.is_absolute(): + ns.doc_build = (Path.cwd() / ns.doc_build).resolve() + if ns.include_cat and not ns.include_cat.is_absolute(): + ns.include_cat = (Path.cwd() / ns.include_cat).resolve() + + if ns.copy and not ns.copy.is_absolute(): + ns.copy = (Path.cwd() / ns.copy).resolve() + if ns.zip and not ns.zip.is_absolute(): + ns.zip = (Path.cwd() / ns.zip).resolve() + if ns.catalog and not ns.catalog.is_absolute(): + ns.catalog = (Path.cwd() / ns.catalog).resolve() + + configure_logger(ns) + + log_info( + """OPTIONS +Source: {ns.source} +Build: {ns.build} +Temp: {ns.temp} + +Copy to: {ns.copy} +Zip to: {ns.zip} +Catalog: {ns.catalog}""", + ns=ns, + ) + + if ns.include_idle and not ns.include_tcltk: + log_warning("Assuming --include-tcltk to support --include-idle") + ns.include_tcltk = True + + try: + generate_source_files(ns) + files = list(get_layout(ns)) + copy_files(files, ns) + except KeyboardInterrupt: + log_info("Interrupted by Ctrl+C") + return 3 + except SystemExit: + raise + except: + log_exception("Unhandled error") + + if error_was_logged(): + log_error("Errors occurred.") + return 1 + + +if __name__ == "__main__": + sys.exit(int(main() or 0)) diff --git a/PC/layout/support/__init__.py b/PC/layout/support/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py new file mode 100644 index 000000000000..c5dda70c7ef8 --- /dev/null +++ b/PC/layout/support/appxmanifest.py @@ -0,0 +1,487 @@ +""" +File generation for APPX/MSIX manifests. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import collections +import ctypes +import io +import os +import sys + +from pathlib import Path, PureWindowsPath +from xml.etree import ElementTree as ET + +from .constants import * + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + +APPX_DATA = dict( + Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT), + Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3), + Publisher=os.getenv( + "APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B" + ), + DisplayName="Python {}".format(VER_DOT), + Description="The Python {} runtime and console.".format(VER_DOT), + ProcessorArchitecture="x64" if IS_X64 else "x86", +) + +PYTHON_VE_DATA = dict( + DisplayName="Python {}".format(VER_DOT), + Description="Python interactive console", + Square150x150Logo="_resources/pythonx150.png", + Square44x44Logo="_resources/pythonx44.png", + BackgroundColor="transparent", +) + +PYTHONW_VE_DATA = dict( + DisplayName="Python {} (Windowed)".format(VER_DOT), + Description="Python windowed app launcher", + Square150x150Logo="_resources/pythonwx150.png", + Square44x44Logo="_resources/pythonwx44.png", + BackgroundColor="transparent", + AppListEntry="none", +) + +PIP_VE_DATA = dict( + DisplayName="pip (Python {})".format(VER_DOT), + Description="pip package manager for Python {}".format(VER_DOT), + Square150x150Logo="_resources/pythonx150.png", + Square44x44Logo="_resources/pythonx44.png", + BackgroundColor="transparent", + AppListEntry="none", +) + +IDLE_VE_DATA = dict( + DisplayName="IDLE (Python {})".format(VER_DOT), + Description="IDLE editor for Python {}".format(VER_DOT), + Square150x150Logo="_resources/pythonwx150.png", + Square44x44Logo="_resources/pythonwx44.png", + BackgroundColor="transparent", +) + +APPXMANIFEST_NS = { + "": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", + "m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", + "uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10", + "rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities", + "rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4", + "desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4", + "desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6", + "uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3", + "uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4", + "uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5", +} + +APPXMANIFEST_TEMPLATE = """ + + + + + Python Software Foundation + + _resources/pythonx50.png + + + + + + + + + + + + + + +""" + + +RESOURCES_XML_TEMPLATE = r""" + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + + +SCCD_FILENAME = "PC/classicAppCompat.sccd" + +REGISTRY = { + "HKCU\\Software\\Python\\PythonCore": { + VER_DOT: { + "DisplayName": APPX_DATA["DisplayName"], + "SupportUrl": "https://www.python.org/", + "SysArchitecture": "64bit" if IS_X64 else "32bit", + "SysVersion": VER_DOT, + "Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO), + "InstallPath": { + # I have no idea why the trailing spaces are needed, but they seem to be needed. + "": "[{AppVPackageRoot}][ ]", + "ExecutablePath": "[{AppVPackageRoot}]python.exe[ ]", + "WindowedExecutablePath": "[{AppVPackageRoot}]pythonw.exe[ ]", + }, + "Help": { + "Main Python Documentation": { + "_condition": lambda ns: ns.include_chm, + "": "[{{AppVPackageRoot}}]Doc\\{}[ ]".format( + PYTHON_CHM_NAME + ), + }, + "Local Python Documentation": { + "_condition": lambda ns: ns.include_html_doc, + "": "[{AppVPackageRoot}]Doc\\html\\index.html[ ]", + }, + "Online Python Documentation": { + "": "https://docs.python.org/{}".format(VER_DOT) + }, + }, + "Idle": { + "_condition": lambda ns: ns.include_idle, + "": "[{AppVPackageRoot}]Lib\\idlelib\\idle.pyw[ ]", + }, + } + } +} + + +def get_packagefamilyname(name, publisher_id): + class PACKAGE_ID(ctypes.Structure): + _fields_ = [ + ("reserved", ctypes.c_uint32), + ("processorArchitecture", ctypes.c_uint32), + ("version", ctypes.c_uint64), + ("name", ctypes.c_wchar_p), + ("publisher", ctypes.c_wchar_p), + ("resourceId", ctypes.c_wchar_p), + ("publisherId", ctypes.c_wchar_p), + ] + _pack_ = 4 + + pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None) + result = ctypes.create_unicode_buffer(256) + result_len = ctypes.c_uint32(256) + r = ctypes.windll.kernel32.PackageFamilyNameFromId( + pid, ctypes.byref(result_len), result + ) + if r: + raise OSError(r, "failed to get package family name") + return result.value[: result_len.value] + + +def _fixup_sccd(ns, sccd, new_hash=None): + if not new_hash: + return sccd + + NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd") + with open(sccd, "rb") as f: + xml = ET.parse(f) + + pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"]) + + ae = xml.find("s:AuthorizedEntities", NS) + ae.clear() + + e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity")) + e.set("AppPackageFamilyName", pfn) + e.set("CertificateSignatureHash", new_hash) + + for e in xml.findall("s:Catalog", NS): + e.text = "FFFF" + + sccd = ns.temp / sccd.name + sccd.parent.mkdir(parents=True, exist_ok=True) + with open(sccd, "wb") as f: + xml.write(f, encoding="utf-8") + + return sccd + + + at public +def get_appx_layout(ns): + if not ns.include_appxmanifest: + return + + yield "AppxManifest.xml", ns.temp / "AppxManifest.xml" + yield "_resources.xml", ns.temp / "_resources.xml" + icons = ns.source / "PC" / "icons" + yield "_resources/pythonx44.png", icons / "pythonx44.png" + yield "_resources/pythonx44$targetsize-44_altform-unplated.png", icons / "pythonx44.png" + yield "_resources/pythonx50.png", icons / "pythonx50.png" + yield "_resources/pythonx50$targetsize-50_altform-unplated.png", icons / "pythonx50.png" + yield "_resources/pythonx150.png", icons / "pythonx150.png" + yield "_resources/pythonx150$targetsize-150_altform-unplated.png", icons / "pythonx150.png" + yield "_resources/pythonwx44.png", icons / "pythonwx44.png" + yield "_resources/pythonwx44$targetsize-44_altform-unplated.png", icons / "pythonwx44.png" + yield "_resources/pythonwx150.png", icons / "pythonwx150.png" + yield "_resources/pythonwx150$targetsize-150_altform-unplated.png", icons / "pythonwx150.png" + sccd = ns.source / SCCD_FILENAME + if sccd.is_file(): + # This should only be set for side-loading purposes. + sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256")) + yield sccd.name, sccd + + +def find_or_add(xml, element, attr=None, always_add=False): + if always_add: + e = None + else: + q = element + if attr: + q += "[@{}='{}']".format(*attr) + e = xml.find(q, APPXMANIFEST_NS) + if e is None: + prefix, _, name = element.partition(":") + name = ET.QName(APPXMANIFEST_NS[prefix or ""], name) + e = ET.SubElement(xml, name) + if attr: + e.set(*attr) + return e + + +def _get_app(xml, appid): + if appid: + app = xml.find( + "m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS + ) + if app is None: + raise LookupError(appid) + else: + app = xml + return app + + +def add_visual(xml, appid, data): + app = _get_app(xml, appid) + e = find_or_add(app, "uap:VisualElements") + for i in data.items(): + e.set(*i) + return e + + +def add_alias(xml, appid, alias, subsystem="windows"): + app = _get_app(xml, appid) + e = find_or_add(app, "m:Extensions") + e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias")) + e = find_or_add(e, "uap5:AppExecutionAlias") + e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem) + e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias)) + + +def add_file_type(xml, appid, name, suffix, parameters='"%1"'): + app = _get_app(xml, appid) + e = find_or_add(app, "m:Extensions") + e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation")) + e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name)) + e.set("Parameters", parameters) + e = find_or_add(e, "uap:SupportedFileTypes") + if isinstance(suffix, str): + suffix = [suffix] + for s in suffix: + ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s + + +def add_application( + ns, xml, appid, executable, aliases, visual_element, subsystem, file_types +): + node = xml.find("m:Applications", APPXMANIFEST_NS) + suffix = "_d.exe" if ns.debug else ".exe" + app = ET.SubElement( + node, + ET.QName(APPXMANIFEST_NS[""], "Application"), + { + "Id": appid, + "Executable": executable + suffix, + "EntryPoint": "Windows.FullTrustApplication", + ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true", + }, + ) + if visual_element: + add_visual(app, None, visual_element) + for alias in aliases: + add_alias(app, None, alias + suffix, subsystem) + if file_types: + add_file_type(app, None, *file_types) + return app + + +def _get_registry_entries(ns, root="", d=None): + r = root if root else PureWindowsPath("") + if d is None: + d = REGISTRY + for key, value in d.items(): + if key == "_condition": + continue + elif isinstance(value, dict): + cond = value.get("_condition") + if cond and not cond(ns): + continue + fullkey = r + for part in PureWindowsPath(key).parts: + fullkey /= part + if len(fullkey.parts) > 1: + yield str(fullkey), None, None + yield from _get_registry_entries(ns, fullkey, value) + elif len(r.parts) > 1: + yield str(r), key, value + + +def add_registry_entries(ns, xml): + e = find_or_add(xml, "m:Extensions") + e = find_or_add(e, "rescap4:Extension") + e.set("Category", "windows.classicAppCompatKeys") + e.set("EntryPoint", "Windows.FullTrustApplication") + e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys")) + for name, valuename, value in _get_registry_entries(ns): + k = ET.SubElement( + e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey") + ) + k.set("Name", name) + if value: + k.set("ValueName", valuename) + k.set("Value", value) + k.set("ValueType", "REG_SZ") + + +def disable_registry_virtualization(xml): + e = find_or_add(xml, "m:Properties") + e = find_or_add(e, "desktop6:RegistryWriteVirtualization") + e.text = "disabled" + e = find_or_add(xml, "m:Capabilities") + e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources")) + + + at public +def get_appxmanifest(ns): + for k, v in APPXMANIFEST_NS.items(): + ET.register_namespace(k, v) + ET.register_namespace("", APPXMANIFEST_NS["m"]) + + xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE)) + NS = APPXMANIFEST_NS + QN = ET.QName + + node = xml.find("m:Identity", NS) + for k in node.keys(): + value = APPX_DATA.get(k) + if value: + node.set(k, value) + + for node in xml.find("m:Properties", NS): + value = APPX_DATA.get(node.tag.rpartition("}")[2]) + if value: + node.text = value + + winver = sys.getwindowsversion()[:3] + if winver < (10, 0, 17763): + winver = 10, 0, 17763 + find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set( + "MaxVersionTested", "{}.{}.{}.0".format(*winver) + ) + + if winver > (10, 0, 17763): + disable_registry_virtualization(xml) + + app = add_application( + ns, + xml, + "Python", + "python", + ["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)], + PYTHON_VE_DATA, + "console", + ("python.file", [".py"]), + ) + + add_application( + ns, + xml, + "PythonW", + "pythonw", + ["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)], + PYTHONW_VE_DATA, + "windows", + ("python.windowedfile", [".pyw"]), + ) + + if ns.include_pip and ns.include_launchers: + add_application( + ns, + xml, + "Pip", + "pip", + ["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)], + PIP_VE_DATA, + "console", + ("python.wheel", [".whl"], 'install "%1"'), + ) + + if ns.include_idle and ns.include_launchers: + add_application( + ns, + xml, + "Idle", + "idle", + ["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)], + IDLE_VE_DATA, + "windows", + None, + ) + + if (ns.source / SCCD_FILENAME).is_file(): + add_registry_entries(ns, xml) + node = xml.find("m:Capabilities", NS) + node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability")) + node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe") + + buffer = io.BytesIO() + xml.write(buffer, encoding="utf-8", xml_declaration=True) + return buffer.getbuffer() + + + at public +def get_resources_xml(ns): + return RESOURCES_XML_TEMPLATE.encode("utf-8") diff --git a/PC/layout/support/catalog.py b/PC/layout/support/catalog.py new file mode 100644 index 000000000000..43121187ed18 --- /dev/null +++ b/PC/layout/support/catalog.py @@ -0,0 +1,44 @@ +""" +File generation for catalog signing non-binary contents. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import sys + +__all__ = ["PYTHON_CAT_NAME", "PYTHON_CDF_NAME"] + + +def public(f): + __all__.append(f.__name__) + return f + + +PYTHON_CAT_NAME = "python.cat" +PYTHON_CDF_NAME = "python.cdf" + + +CATALOG_TEMPLATE = r"""[CatalogHeader] +Name={target.stem}.cat +ResultDir={target.parent} +PublicVersion=1 +CatalogVersion=2 +HashAlgorithms=SHA256 +PageHashes=false +EncodingType= + +[CatalogFiles] +""" + + +def can_sign(file): + return file.is_file() and file.stat().st_size + + + at public +def write_catalog(target, files): + with target.open("w", encoding="utf-8") as cat: + cat.write(CATALOG_TEMPLATE.format(target=target)) + cat.writelines("{}={}\n".format(n, f) for n, f in files if can_sign(f)) diff --git a/PC/layout/support/constants.py b/PC/layout/support/constants.py new file mode 100644 index 000000000000..88ea410b340e --- /dev/null +++ b/PC/layout/support/constants.py @@ -0,0 +1,28 @@ +""" +Constants for generating the layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import struct +import sys + +VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = struct.pack(">i", sys.hexversion) +VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4 +VER_NAME = {"alpha": "a", "beta": "b", "rc": "rc"}.get( + sys.version_info.releaselevel, "" +) +VER_SERIAL = sys.version_info.serial if VER_NAME else "" +VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR) + +PYTHON_DLL_NAME = "python{}{}.dll".format(VER_MAJOR, VER_MINOR) +PYTHON_STABLE_DLL_NAME = "python{}.dll".format(VER_MAJOR) +PYTHON_ZIP_NAME = "python{}{}.zip".format(VER_MAJOR, VER_MINOR) +PYTHON_PTH_NAME = "python{}{}._pth".format(VER_MAJOR, VER_MINOR) + +PYTHON_CHM_NAME = "python{}{}{}{}{}.chm".format( + VER_MAJOR, VER_MINOR, VER_MICRO, VER_NAME, VER_SERIAL +) + +IS_X64 = sys.maxsize > 2 ** 32 diff --git a/PC/layout/support/distutils.command.bdist_wininst.py b/PC/layout/support/distutils.command.bdist_wininst.py new file mode 100644 index 000000000000..6e9b49fe42df --- /dev/null +++ b/PC/layout/support/distutils.command.bdist_wininst.py @@ -0,0 +1,25 @@ +"""distutils.command.bdist_wininst + +Suppress the 'bdist_wininst' command, while still allowing +setuptools to import it without breaking.""" + +from distutils.core import Command +from distutils.errors import DistutilsPlatformError + + +class bdist_wininst(Command): + description = "create an executable installer for MS Windows" + + # Marker for tests that we have the unsupported bdist_wininst + _unsupported = True + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + raise DistutilsPlatformError( + "bdist_wininst is not supported in this Python distribution" + ) diff --git a/PC/layout/support/filesets.py b/PC/layout/support/filesets.py new file mode 100644 index 000000000000..47f727c05784 --- /dev/null +++ b/PC/layout/support/filesets.py @@ -0,0 +1,100 @@ +""" +File sets and globbing helper for make_layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import os + + +class FileStemSet: + def __init__(self, *patterns): + self._names = set() + self._prefixes = [] + self._suffixes = [] + for p in map(os.path.normcase, patterns): + if p.endswith("*"): + self._prefixes.append(p[:-1]) + elif p.startswith("*"): + self._suffixes.append(p[1:]) + else: + self._names.add(p) + + def _make_name(self, f): + return os.path.normcase(f.stem) + + def __contains__(self, f): + bn = self._make_name(f) + return ( + bn in self._names + or any(map(bn.startswith, self._prefixes)) + or any(map(bn.endswith, self._suffixes)) + ) + + +class FileNameSet(FileStemSet): + def _make_name(self, f): + return os.path.normcase(f.name) + + +class FileSuffixSet: + def __init__(self, *patterns): + self._names = set() + self._prefixes = [] + self._suffixes = [] + for p in map(os.path.normcase, patterns): + if p.startswith("*."): + self._names.add(p[1:]) + elif p.startswith("*"): + self._suffixes.append(p[1:]) + elif p.endswith("*"): + self._prefixes.append(p[:-1]) + elif p.startswith("."): + self._names.add(p) + else: + self._names.add("." + p) + + def _make_name(self, f): + return os.path.normcase(f.suffix) + + def __contains__(self, f): + bn = self._make_name(f) + return ( + bn in self._names + or any(map(bn.startswith, self._prefixes)) + or any(map(bn.endswith, self._suffixes)) + ) + + +def _rglob(root, pattern, condition): + dirs = [root] + recurse = pattern[:3] in {"**/", "**\\"} + if recurse: + pattern = pattern[3:] + + while dirs: + d = dirs.pop(0) + if recurse: + dirs.extend( + filter( + condition, (type(root)(f2) for f2 in os.scandir(d) if f2.is_dir()) + ) + ) + yield from ( + (f.relative_to(root), f) + for f in d.glob(pattern) + if f.is_file() and condition(f) + ) + + +def _return_true(f): + return True + + +def rglob(root, patterns, condition=None): + if isinstance(patterns, tuple): + for p in patterns: + yield from _rglob(root, p, condition or _return_true) + else: + yield from _rglob(root, patterns, condition or _return_true) diff --git a/PC/layout/support/logging.py b/PC/layout/support/logging.py new file mode 100644 index 000000000000..30869b949a1c --- /dev/null +++ b/PC/layout/support/logging.py @@ -0,0 +1,93 @@ +""" +Logging support for make_layout. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + +import logging +import sys + +__all__ = [] + +LOG = None +HAS_ERROR = False + + +def public(f): + __all__.append(f.__name__) + return f + + + at public +def configure_logger(ns): + global LOG + if LOG: + return + + LOG = logging.getLogger("make_layout") + LOG.level = logging.DEBUG + + if ns.v: + s_level = max(logging.ERROR - ns.v * 10, logging.DEBUG) + f_level = max(logging.WARNING - ns.v * 10, logging.DEBUG) + else: + s_level = logging.ERROR + f_level = logging.INFO + + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(logging.Formatter("{levelname:8s} {message}", style="{")) + handler.setLevel(s_level) + LOG.addHandler(handler) + + if ns.log: + handler = logging.FileHandler(ns.log, encoding="utf-8", delay=True) + handler.setFormatter( + logging.Formatter("[{asctime}]{levelname:8s}: {message}", style="{") + ) + handler.setLevel(f_level) + LOG.addHandler(handler) + + +class BraceMessage: + def __init__(self, fmt, *args, **kwargs): + self.fmt = fmt + self.args = args + self.kwargs = kwargs + + def __str__(self): + return self.fmt.format(*self.args, **self.kwargs) + + + at public +def log_debug(msg, *args, **kwargs): + return LOG.debug(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_info(msg, *args, **kwargs): + return LOG.info(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_warning(msg, *args, **kwargs): + return LOG.warning(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_error(msg, *args, **kwargs): + global HAS_ERROR + HAS_ERROR = True + return LOG.error(BraceMessage(msg, *args, **kwargs)) + + + at public +def log_exception(msg, *args, **kwargs): + global HAS_ERROR + HAS_ERROR = True + return LOG.exception(BraceMessage(msg, *args, **kwargs)) + + + at public +def error_was_logged(): + return HAS_ERROR diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py new file mode 100644 index 000000000000..76d9e34e1f46 --- /dev/null +++ b/PC/layout/support/options.py @@ -0,0 +1,122 @@ +""" +List of optional components. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + +OPTIONS = { + "stable": {"help": "stable ABI stub"}, + "pip": {"help": "pip"}, + "distutils": {"help": "distutils"}, + "tcltk": {"help": "Tcl, Tk and tkinter"}, + "idle": {"help": "Idle"}, + "tests": {"help": "test suite"}, + "tools": {"help": "tools"}, + "venv": {"help": "venv"}, + "dev": {"help": "headers and libs"}, + "symbols": {"help": "symbols"}, + "bdist-wininst": {"help": "bdist_wininst support"}, + "underpth": {"help": "a python._pth file", "not-in-all": True}, + "launchers": {"help": "specific launchers"}, + "appxmanifest": {"help": "an appxmanifest"}, + "props": {"help": "a python.props file"}, + "chm": {"help": "the CHM documentation"}, + "html-doc": {"help": "the HTML documentation"}, +} + + +PRESETS = { + "appx": { + "help": "APPX package", + "options": [ + "stable", + "pip", + "distutils", + "tcltk", + "idle", + "venv", + "dev", + "launchers", + "appxmanifest", + # XXX: Disabled for now "precompile", + ], + }, + "nuget": { + "help": "nuget package", + "options": ["stable", "pip", "distutils", "dev", "props"], + }, + "default": { + "help": "development kit package", + "options": [ + "stable", + "pip", + "distutils", + "tcltk", + "idle", + "tests", + "tools", + "venv", + "dev", + "symbols", + "bdist-wininst", + "chm", + ], + }, + "embed": { + "help": "embeddable package", + "options": ["stable", "zip-lib", "flat-dlls", "underpth", "precompile"], + }, +} + + + at public +def get_argparse_options(): + for opt, info in OPTIONS.items(): + help = "When specified, includes {}".format(info["help"]) + if info.get("not-in-all"): + help = "{}. Not affected by --include-all".format(help) + + yield "--include-{}".format(opt), help + + for opt, info in PRESETS.items(): + help = "When specified, includes default options for {}".format(info["help"]) + yield "--preset-{}".format(opt), help + + +def ns_get(ns, key, default=False): + return getattr(ns, key.replace("-", "_"), default) + + +def ns_set(ns, key, value=True): + k1 = key.replace("-", "_") + k2 = "include_{}".format(k1) + if hasattr(ns, k2): + setattr(ns, k2, value) + elif hasattr(ns, k1): + setattr(ns, k1, value) + else: + raise AttributeError("no argument named '{}'".format(k1)) + + + at public +def update_presets(ns): + for preset, info in PRESETS.items(): + if ns_get(ns, "preset-{}".format(preset)): + for opt in info["options"]: + ns_set(ns, opt) + + if ns.include_all: + for opt in OPTIONS: + if OPTIONS[opt].get("not-in-all"): + continue + ns_set(ns, opt) diff --git a/PC/layout/support/pip.py b/PC/layout/support/pip.py new file mode 100644 index 000000000000..369a923ce139 --- /dev/null +++ b/PC/layout/support/pip.py @@ -0,0 +1,79 @@ +""" +Extraction and file list generation for pip. +""" + +__author__ = "Steve Dower " +__version__ = "3.8" + + +import os +import shutil +import subprocess +import sys + +__all__ = [] + + +def public(f): + __all__.append(f.__name__) + return f + + + at public +def get_pip_dir(ns): + if ns.copy: + if ns.zip_lib: + return ns.copy / "packages" + return ns.copy / "Lib" / "site-packages" + else: + return ns.temp / "packages" + + + at public +def extract_pip_files(ns): + dest = get_pip_dir(ns) + dest.mkdir(parents=True, exist_ok=True) + + src = ns.source / "Lib" / "ensurepip" / "_bundled" + + ns.temp.mkdir(parents=True, exist_ok=True) + wheels = [shutil.copy(whl, ns.temp) for whl in src.glob("*.whl")] + search_path = os.pathsep.join(wheels) + if os.environ.get("PYTHONPATH"): + search_path += ";" + os.environ["PYTHONPATH"] + + env = os.environ.copy() + env["PYTHONPATH"] = search_path + + output = subprocess.check_output( + [ + sys.executable, + "-m", + "pip", + "--no-color", + "install", + "pip", + "setuptools", + "--upgrade", + "--target", + str(dest), + "--no-index", + "--no-cache-dir", + "-f", + str(src), + "--only-binary", + ":all:", + ], + env=env, + ) + + try: + shutil.rmtree(dest / "bin") + except OSError: + pass + + for file in wheels: + try: + os.remove(file) + except OSError: + pass diff --git a/PC/layout/support/props.py b/PC/layout/support/props.py new file mode 100644 index 000000000000..3a047d215058 --- /dev/null +++ b/PC/layout/support/props.py @@ -0,0 +1,110 @@ +""" +Provides .props file. +""" + +import os + +from .constants import * + +__all__ = ["PYTHON_PROPS_NAME"] + + +def public(f): + __all__.append(f.__name__) + return f + + +PYTHON_PROPS_NAME = "python.props" + +PROPS_DATA = { + "PYTHON_TAG": VER_DOT, + "PYTHON_VERSION": os.getenv("PYTHON_NUSPEC_VERSION"), + "PYTHON_PLATFORM": os.getenv("PYTHON_PROPS_PLATFORM"), + "PYTHON_TARGET": "", +} + +if not PROPS_DATA["PYTHON_VERSION"]: + if VER_NAME: + PROPS_DATA["PYTHON_VERSION"] = "{}.{}-{}{}".format( + VER_DOT, VER_MICRO, VER_NAME, VER_SERIAL + ) + else: + PROPS_DATA["PYTHON_VERSION"] = "{}.{}".format(VER_DOT, VER_MICRO) + +if not PROPS_DATA["PYTHON_PLATFORM"]: + PROPS_DATA["PYTHON_PLATFORM"] = "x64" if IS_X64 else "Win32" + +PROPS_DATA["PYTHON_TARGET"] = "_GetPythonRuntimeFilesDependsOn{}{}_{}".format( + VER_MAJOR, VER_MINOR, PROPS_DATA["PYTHON_PLATFORM"] +) + +PROPS_TEMPLATE = r""" + + + $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python_d.exe") + $([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python.exe") + $(PythonHome)\include + $(PythonHome)\libs + {PYTHON_TAG} + {PYTHON_VERSION} + + true + false + false + false + + {PYTHON_TARGET};$(GetPythonRuntimeFilesDependsOn) + + + + + $(PythonInclude);%(AdditionalIncludeDirectories) + MultiThreadedDLL + + + $(PythonLibs);%(AdditionalLibraryDirectories) + + + + + + + + <_PythonRuntimeExe Include="$(PythonHome)\python*.dll" /> + <_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" /> + <_PythonRuntimeExe> + %(Filename)%(Extension) + + <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" /> + <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" /> + <_PythonRuntimeDlls> + DLLs\%(Filename)%(Extension) + + <_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" /> + <_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" /> + <_PythonRuntimeLib> + Lib\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + +""" + + + at public +def get_props_layout(ns): + if ns.include_all or ns.include_props: + yield "python.props", ns.temp / "python.props" + + + at public +def get_props(ns): + # TODO: Filter contents of props file according to included/excluded items + props = PROPS_TEMPLATE.format_map(PROPS_DATA) + return props.encode("utf-8") diff --git a/Tools/nuget/python.props b/PC/layout/support/python.props similarity index 100% rename from Tools/nuget/python.props rename to PC/layout/support/python.props diff --git a/PC/pylauncher.rc b/PC/pylauncher.rc index 3da3445f5fc4..92987af7138d 100644 --- a/PC/pylauncher.rc +++ b/PC/pylauncher.rc @@ -7,6 +7,11 @@ #include 1 RT_MANIFEST "python.manifest" +#if defined(PY_ICON) +1 ICON DISCARDABLE "icons\python.ico" +#elif defined(PYW_ICON) +1 ICON DISCARDABLE "icons\pythonw.ico" +#else 1 ICON DISCARDABLE "icons\launcher.ico" 2 ICON DISCARDABLE "icons\py.ico" 3 ICON DISCARDABLE "icons\pyc.ico" @@ -14,6 +19,7 @@ 5 ICON DISCARDABLE "icons\python.ico" 6 ICON DISCARDABLE "icons\pythonw.ico" 7 ICON DISCARDABLE "icons\setup.ico" +#endif ///////////////////////////////////////////////////////////////////////////// // diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp new file mode 100644 index 000000000000..b015abd59e66 --- /dev/null +++ b/PC/python_uwp.cpp @@ -0,0 +1,239 @@ +/* Main program when embedded in a UWP application on Windows */ + +#include "Python.h" +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#ifdef PYTHON_UWP_SUPPORTED +#include +#include +#else +#include +#endif + +#ifdef PYTHONW +#ifdef _DEBUG +const wchar_t *PROGNAME = L"pythonw_d.exe"; +#else +const wchar_t *PROGNAME = L"pythonw.exe"; +#endif +#else +#ifdef _DEBUG +const wchar_t *PROGNAME = L"python_d.exe"; +#else +const wchar_t *PROGNAME = L"python.exe"; +#endif +#endif + +static void +set_user_base() +{ +#ifdef PYTHON_UWP_SUPPORTED + wchar_t envBuffer[2048]; + try { + const auto appData = winrt::Windows::Storage::ApplicationData::Current(); + if (appData) { + const auto localCache = appData.LocalCacheFolder(); + if (localCache) { + auto path = localCache.Path(); + if (!path.empty() && + !wcscpy_s(envBuffer, path.c_str()) && + !wcscat_s(envBuffer, L"\\local-packages") + ) { + _wputenv_s(L"PYTHONUSERBASE", envBuffer); + } + } + } + } catch (...) { + } +#endif +} + +static const wchar_t * +get_argv0(const wchar_t *argv0) +{ +#ifdef PYTHON_UWP_SUPPORTED + winrt::hstring installPath; +#else + std::wstring installPath; +#endif + const wchar_t *launcherPath; + wchar_t *buffer; + size_t len; + + launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__"); + if (launcherPath && launcherPath[0]) { + len = wcslen(launcherPath) + 1; + buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); + if (!buffer) { + Py_FatalError("out of memory"); + return NULL; + } + if (wcscpy_s(buffer, len, launcherPath)) { + Py_FatalError("failed to copy to buffer"); + return NULL; + } + return buffer; + } + +#ifdef PYTHON_UWP_SUPPORTED + try { + const auto package = winrt::Windows::ApplicationModel::Package::Current(); + if (package) { + const auto install = package.InstalledLocation(); + if (install) { + installPath = install.Path(); + } + } + } + catch (...) { + } +#endif + + if (!installPath.empty()) { + len = installPath.size() + wcslen(PROGNAME) + 2; + } else { + len = wcslen(argv0) + wcslen(PROGNAME) + 1; + } + + buffer = (wchar_t *)malloc(sizeof(wchar_t) * len); + if (!buffer) { + Py_FatalError("out of memory"); + return NULL; + } + + if (!installPath.empty()) { + if (wcscpy_s(buffer, len, installPath.c_str())) { + Py_FatalError("failed to copy to buffer"); + return NULL; + } + if (wcscat_s(buffer, len, L"\\")) { + Py_FatalError("failed to concatenate backslash"); + return NULL; + } + } else { + if (wcscpy_s(buffer, len, argv0)) { + Py_FatalError("failed to copy argv[0]"); + return NULL; + } + + wchar_t *name = wcsrchr(buffer, L'\\'); + if (name) { + name[1] = L'\0'; + } else { + buffer[0] = L'\0'; + } + } + + if (wcscat_s(buffer, len, PROGNAME)) { + Py_FatalError("failed to concatenate program name"); + return NULL; + } + + return buffer; +} + +static wchar_t * +get_process_name() +{ + DWORD bufferLen = MAX_PATH; + DWORD len = bufferLen; + wchar_t *r = NULL; + + while (!r) { + r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); + if (!r) { + Py_FatalError("out of memory"); + return NULL; + } + len = GetModuleFileNameW(NULL, r, bufferLen); + if (len == 0) { + free((void *)r); + return NULL; + } else if (len == bufferLen && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(r); + r = NULL; + bufferLen *= 2; + } + } + + return r; +} + +int +wmain(int argc, wchar_t **argv) +{ + const wchar_t **new_argv; + int new_argc; + const wchar_t *exeName; + + new_argc = argc; + new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2)); + if (new_argv == NULL) { + Py_FatalError("out of memory"); + return -1; + } + + exeName = get_process_name(); + + new_argv[0] = get_argv0(exeName ? exeName : argv[0]); + for (int i = 1; i < argc; ++i) { + new_argv[i] = argv[i]; + } + + set_user_base(); + + if (exeName) { + const wchar_t *p = wcsrchr(exeName, L'\\'); + if (p) { + const wchar_t *moduleName = NULL; + if (*p++ == L'\\') { + if (wcsnicmp(p, L"pip", 3) == 0) { + moduleName = L"pip"; + _wputenv_s(L"PIP_USER", L"true"); + } + else if (wcsnicmp(p, L"idle", 4) == 0) { + moduleName = L"idlelib"; + } + } + + if (moduleName) { + new_argc += 2; + for (int i = argc; i >= 1; --i) { + new_argv[i + 2] = new_argv[i]; + } + new_argv[1] = L"-m"; + new_argv[2] = moduleName; + } + } + } + + /* Override program_full_path from here so that + sys.executable is set correctly. */ + _Py_SetProgramFullPath(new_argv[0]); + + int result = Py_Main(new_argc, (wchar_t **)new_argv); + + free((void *)exeName); + free((void *)new_argv); + + return result; +} + +#ifdef PYTHONW + +int WINAPI wWinMain( + HINSTANCE hInstance, /* handle to current instance */ + HINSTANCE hPrevInstance, /* handle to previous instance */ + LPWSTR lpCmdLine, /* pointer to command line */ + int nCmdShow /* show state of window */ +) +{ + return wmain(__argc, __wargv); +} + +#endif diff --git a/PC/store_info.txt b/PC/store_info.txt new file mode 100644 index 000000000000..e252692b73cc --- /dev/null +++ b/PC/store_info.txt @@ -0,0 +1,146 @@ +# Overview + +NOTE: This file requires more content. + +Since Python 3.7.2, releases have been made through the Microsoft Store +to allow easy installation on Windows 10.0.17763.0 and later. + +# Building + +To build the store package, the PC/layout script should be used. +Execute the directory with the build of Python to package, and pass +"-h" for full command-line options. + +To sideload test builds, you will need a local certificate. +Instructions are available at +https://docs.microsoft.com/windows/uwp/packaging/create-certificate-package-signing. + +After exporting your certificate, you will need the subject name and +SHA256 hash. The `certutil -dump ` command will display this +information. + +To build for sideloading, use these commands in PowerShell: + +``` +$env:APPX_DATA_PUBLISHER= +$env:APPX_DATA_SHA256= +$env:SigningCertificateFile= + +python PC/layout --copy --include-appxmanifest +Tools/msi/make_appx.ps1 python.msix -sign + +Add-AppxPackage python.msix +``` + +(Note that only the last command requires PowerShell, and the others +can be used from Command Prompt. You can also double-click to install +the final package.) + +To build for publishing to the Store, use these commands: + +``` +$env:APPX_DATA_PUBLISHER = $null +$env:APPX_DATA_SHA256 = $null + +python PC/layout --copy --preset-appxmanifest --precompile +Tools/msi/make_appx.ps1 python.msix +``` + +Note that this package cannot be installed locally. It may only be +added to a submission for the store. + + +# Submission Metadata + +This file contains the text that we use to fill out the store listing +for the Microsoft Store. It needs to be entered manually when creating +a new submission via the dashboard at +https://partner.microsoft.com/dashboard. + +We keep it here for convenience and to allow it to be updated via pull +requests. + +## Title + +Python 3.7 + +## Short Title + +Python + +## Description + +Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python?s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms. + +The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python Web site, https://www.python.org/, and may be freely distributed. The same site also contains distributions of and pointers to many free third party Python modules, programs and tools, and additional documentation. + +The Python interpreter is easily extended with new functions and data types implemented in C or C++ (or other languages callable from C). Python is also suitable as an extension language for customizable applications. + +## ShortDescription + +The Python 3.7 interpreter and runtime. + +## Copyright Trademark Information + +(c) Python Software Foundation + +## Additional License Terms + +Visit https://docs.python.org/3.7/license.html for latest license terms. + +PSF LICENSE AGREEMENT FOR PYTHON 3.7 + +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.7 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.7 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright ? 2001-2018 Python Software Foundation; All Rights + Reserved" are retained in Python 3.7 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.7 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.7. + +4. PSF is making Python 3.7 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.7 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.7, Licensee agrees + to be bound by the terms and conditions of this License Agreement. + +## Features + +* Easy to install Python runtime +* Supported by core CPython team +* Find Python, Pip and Idle on PATH + +## Search Terms + +* Python +* Scripting +* Interpreter + diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj index 95e3cd50eca5..bd61c0d4f689 100644 --- a/PCbuild/_tkinter.vcxproj +++ b/PCbuild/_tkinter.vcxproj @@ -95,4 +95,10 @@ + + + + + + \ No newline at end of file diff --git a/PCbuild/find_msbuild.bat b/PCbuild/find_msbuild.bat index 57512a01927e..a2810f09c45e 100644 --- a/PCbuild/find_msbuild.bat +++ b/PCbuild/find_msbuild.bat @@ -29,6 +29,16 @@ @where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc" @if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found + at rem VS 2017 and later provide vswhere.exe, which can be used + at if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere + at set _Py_MSBuild_Root= + at for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest') DO @(set _Py_MSBuild_Root=%%i\MSBuild) + at if not defined _Py_MSBuild_Root goto :skip_vswhere + at for %%j in (Current 15.0) DO @if exist "%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe" (set MSBUILD="%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe") + at set _Py_MSBuild_Root= + at if defined MSBUILD @if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio installation) & goto :found +:skip_vswhere + @rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there. @reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul @if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @( diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index 77341b44a1ee..befaa1fed76b 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -9,6 +9,7 @@ true true true + false @@ -52,6 +53,8 @@ + + @@ -70,6 +73,7 @@ + diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index 59b3861ed406..c212d9f8f32c 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -93,6 +93,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_uwp", "python_uwp.vcxproj", "{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvlauncher", "venvlauncher.vcxproj", "{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvwlauncher", "venvwlauncher.vcxproj", "{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythonw_uwp", "pythonw_uwp.vcxproj", "{AB603547-1E2A-45B3-9E09-B04596006393}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -693,6 +701,70 @@ Global {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64 {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.Build.0 = Debug|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.ActiveCfg = Debug|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.Build.0 = Debug|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.ActiveCfg = Release|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.Build.0 = Release|Win32 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.ActiveCfg = Release|x64 + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.Build.0 = Release|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.ActiveCfg = Debug|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.Build.0 = Debug|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.ActiveCfg = Debug|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.Build.0 = Debug|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.ActiveCfg = Release|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.Build.0 = Release|Win32 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.ActiveCfg = Release|x64 + {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.Build.0 = Release|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.ActiveCfg = Debug|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.Build.0 = Debug|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.ActiveCfg = Debug|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.Build.0 = Debug|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.ActiveCfg = Release|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.Build.0 = Release|Win32 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.ActiveCfg = Release|x64 + {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.Build.0 = Release|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.ActiveCfg = Debug|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.Build.0 = Debug|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.ActiveCfg = Debug|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.Build.0 = Debug|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.ActiveCfg = Release|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.Build.0 = Release|Win32 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.ActiveCfg = Release|x64 + {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PCbuild/python_uwp.vcxproj b/PCbuild/python_uwp.vcxproj new file mode 100644 index 000000000000..af187dd4df30 --- /dev/null +++ b/PCbuild/python_uwp.vcxproj @@ -0,0 +1,86 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF} + + + + + Application + false + Unicode + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + %(PreprocessorDefinitions) + /EHsc /std:c++17 %(AdditionalOptions) + + + windowsapp.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} + false + + + + + + diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index d19b5f5acf89..2ee88225b62c 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -460,4 +460,19 @@ + + + $(VCInstallDir)\Redist\MSVC\$(VCToolsRedistVersion)\ + $(VCRedistDir)x86\ + $(VCRedistDir)$(Platform)\ + + + + + + + + + + diff --git a/PCbuild/pythonw_uwp.vcxproj b/PCbuild/pythonw_uwp.vcxproj new file mode 100644 index 000000000000..79e105877fbe --- /dev/null +++ b/PCbuild/pythonw_uwp.vcxproj @@ -0,0 +1,86 @@ +? + + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + Win32 + + + Release + x64 + + + + {AB603547-1E2A-45B3-9E09-B04596006393} + + + + + Application + false + Unicode + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + + + + PYTHONW;%(PreprocessorDefinitions) + /EHsc /std:c++17 %(AdditionalOptions) + + + windowsapp.lib;%(AdditionalDependencies) + Windows + + + + + + + + + + + + + + {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} + false + + + + + + diff --git a/Tools/msi/buildrelease.bat b/Tools/msi/buildrelease.bat index 4178981195ee..45e189b537f6 100644 --- a/Tools/msi/buildrelease.bat +++ b/Tools/msi/buildrelease.bat @@ -37,6 +37,7 @@ set BUILDX64= set TARGET=Rebuild set TESTTARGETDIR= set PGO=-m test -q --pgo +set BUILDMSI=1 set BUILDNUGET=1 set BUILDZIP=1 @@ -61,6 +62,7 @@ if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts +if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 @@ -174,10 +176,12 @@ if "%OUTDIR_PLAT%" EQU "win32" ( ) set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI% -%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true -if errorlevel 1 exit /B -%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false -if errorlevel 1 exit /B +if defined BUILDMSI ( + %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true + if errorlevel 1 exit /B + %MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false + if errorlevel 1 exit /B +) if defined BUILDZIP ( %MSBUILD% "%D%make_zip.proj" /t:Build %BUILDOPTS% %CERTOPTS% /p:OutputPath="%BUILD%en-us" @@ -214,6 +218,7 @@ echo --skip-build (-B) Do not build Python (just do the installers) echo --skip-doc (-D) Do not build documentation echo --pgo Specify PGO command for x64 installers echo --skip-pgo Build x64 installers without using PGO +echo --skip-msi Do not build executable/MSI packages echo --skip-nuget Do not build Nuget packages echo --skip-zip Do not build embeddable package echo --download Specify the full download URL for MSIs diff --git a/Tools/msi/make_appx.ps1 b/Tools/msi/make_appx.ps1 new file mode 100644 index 000000000000..e32bd76a37f1 --- /dev/null +++ b/Tools/msi/make_appx.ps1 @@ -0,0 +1,71 @@ +<# +.Synopsis + Compiles and signs an APPX package +.Description + Given the file listing, ensures all the contents are signed + and builds and signs the final package. +.Parameter mapfile + The location on disk of the text mapping file. +.Parameter msix + The path and name to store the APPX/MSIX. +.Parameter sign + When set, signs the APPX/MSIX. Packages to be published to + the store should not be signed. +.Parameter description + Description to embed in the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$layout, + [Parameter(Mandatory=$true)][string]$msix, + [switch]$sign, + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +Set-Alias makeappx (Find-Tool "makeappx.exe") -Scope Script +Set-Alias makepri (Find-Tool "makepri.exe") -Scope Script + +$msixdir = Split-Path $msix -Parent +if ($msixdir) { + $msixdir = (mkdir -Force $msixdir).FullName +} else { + $msixdir = Get-Location +} +$msix = Join-Path $msixdir (Split-Path $msix -Leaf) + +pushd $layout +try { + if (Test-Path resources.pri) { + del resources.pri + } + $name = ([xml](gc AppxManifest.xml)).Package.Identity.Name + makepri new /pr . /mn AppxManifest.xml /in $name /cf _resources.xml /of _resources.pri /mf appx /o + if (-not $? -or -not (Test-Path _resources.map.txt)) { + throw "makepri step failed" + } + $lines = gc _resources.map.txt + $lines | ?{ -not ($_ -match '"_resources[\w\.]+?"') } | Out-File _resources.map.txt -Encoding utf8 + makeappx pack /f _resources.map.txt /m AppxManifest.xml /o /p $msix + if (-not $?) { + throw "makeappx step failed" + } +} finally { + popd +} + +if ($sign) { + Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files $msix + + if (-not $?) { + throw "Package signing failed" + } +} diff --git a/Tools/msi/make_cat.ps1 b/Tools/msi/make_cat.ps1 new file mode 100644 index 000000000000..70741439869a --- /dev/null +++ b/Tools/msi/make_cat.ps1 @@ -0,0 +1,34 @@ +<# +.Synopsis + Compiles and signs a catalog file. +.Description + Given the CDF definition file, builds and signs a catalog. +.Parameter catalog + The path to the catalog definition file to compile and + sign. It is assumed that the .cat file will be the same + name with a new extension. +.Parameter description + The description to add to the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$catalog, + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +Set-Alias MakeCat (Find-Tool "makecat.exe") -Scope Script + +MakeCat $catalog +if (-not $?) { + throw "Catalog compilation failed" +} +Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files @($catalog -replace 'cdf$', 'cat') diff --git a/Tools/msi/make_zip.proj b/Tools/msi/make_zip.proj index 214111734219..125a434e51f4 100644 --- a/Tools/msi/make_zip.proj +++ b/Tools/msi/make_zip.proj @@ -15,11 +15,12 @@ .zip $(OutputPath)\$(TargetName)$(TargetExt) rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)" - "$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py" - $(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -b "$(BuildPath.TrimEnd(`\`))" - set DOC_FILENAME=python$(PythonVersion).chm + "$(PythonExe)" "$(PySourcePath)PC\layout" + $(Arguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" + $(Arguments) -t "$(IntermediateOutputPath)\zip_$(ArchName)" + $(Arguments) --zip "$(TargetPath)" + $(Arguments) --precompile --zip-lib --include-underpth --include-stable --flat-dlls $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) diff --git a/Tools/msi/make_zip.py b/Tools/msi/make_zip.py deleted file mode 100644 index 58f3b15ef852..000000000000 --- a/Tools/msi/make_zip.py +++ /dev/null @@ -1,250 +0,0 @@ -import argparse -import py_compile -import re -import sys -import shutil -import stat -import os -import tempfile - -from itertools import chain -from pathlib import Path -from zipfile import ZipFile, ZIP_DEFLATED - - -TKTCL_RE = re.compile(r'^(_?tk|tcl).+\.(pyd|dll)', re.IGNORECASE) -DEBUG_RE = re.compile(r'_d\.(pyd|dll|exe|pdb|lib)$', re.IGNORECASE) -PYTHON_DLL_RE = re.compile(r'python\d\d?\.dll$', re.IGNORECASE) - -DEBUG_FILES = { - '_ctypes_test', - '_testbuffer', - '_testcapi', - '_testconsole', - '_testimportmultiple', - '_testmultiphase', - 'xxlimited', - 'python3_dstub', -} - -EXCLUDE_FROM_LIBRARY = { - '__pycache__', - 'idlelib', - 'pydoc_data', - 'site-packages', - 'tkinter', - 'turtledemo', -} - -EXCLUDE_FROM_EMBEDDABLE_LIBRARY = { - 'ensurepip', - 'venv', -} - -EXCLUDE_FILE_FROM_LIBRARY = { - 'bdist_wininst.py', -} - -EXCLUDE_FILE_FROM_LIBS = { - 'liblzma', - 'python3stub', -} - -EXCLUDED_FILES = { - 'pyshellext', -} - -def is_not_debug(p): - if DEBUG_RE.search(p.name): - return False - - if TKTCL_RE.search(p.name): - return False - - return p.stem.lower() not in DEBUG_FILES and p.stem.lower() not in EXCLUDED_FILES - -def is_not_debug_or_python(p): - return is_not_debug(p) and not PYTHON_DLL_RE.search(p.name) - -def include_in_lib(p): - name = p.name.lower() - if p.is_dir(): - if name in EXCLUDE_FROM_LIBRARY: - return False - if name == 'test' and p.parts[-2].lower() == 'lib': - return False - if name in {'test', 'tests'} and p.parts[-3].lower() == 'lib': - return False - return True - - if name in EXCLUDE_FILE_FROM_LIBRARY: - return False - - suffix = p.suffix.lower() - return suffix not in {'.pyc', '.pyo', '.exe'} - -def include_in_embeddable_lib(p): - if p.is_dir() and p.name.lower() in EXCLUDE_FROM_EMBEDDABLE_LIBRARY: - return False - - return include_in_lib(p) - -def include_in_libs(p): - if not is_not_debug(p): - return False - - return p.stem.lower() not in EXCLUDE_FILE_FROM_LIBS - -def include_in_tools(p): - if p.is_dir() and p.name.lower() in {'scripts', 'i18n', 'pynche', 'demo', 'parser'}: - return True - - return p.suffix.lower() in {'.py', '.pyw', '.txt'} - -BASE_NAME = 'python{0.major}{0.minor}'.format(sys.version_info) - -FULL_LAYOUT = [ - ('/', '$build', 'python.exe', is_not_debug), - ('/', '$build', 'pythonw.exe', is_not_debug), - ('/', '$build', 'python{}.dll'.format(sys.version_info.major), is_not_debug), - ('/', '$build', '{}.dll'.format(BASE_NAME), is_not_debug), - ('DLLs/', '$build', '*.pyd', is_not_debug), - ('DLLs/', '$build', '*.dll', is_not_debug_or_python), - ('include/', 'include', '*.h', None), - ('include/', 'PC', 'pyconfig.h', None), - ('Lib/', 'Lib', '**/*', include_in_lib), - ('libs/', '$build', '*.lib', include_in_libs), - ('Tools/', 'Tools', '**/*', include_in_tools), -] - -EMBED_LAYOUT = [ - ('/', '$build', 'python*.exe', is_not_debug), - ('/', '$build', '*.pyd', is_not_debug), - ('/', '$build', '*.dll', is_not_debug), - ('{}.zip'.format(BASE_NAME), 'Lib', '**/*', include_in_embeddable_lib), -] - -if os.getenv('DOC_FILENAME'): - FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None)) -if os.getenv('VCREDIST_PATH'): - FULL_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) - EMBED_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None)) - -def copy_to_layout(target, rel_sources): - count = 0 - - if target.suffix.lower() == '.zip': - if target.exists(): - target.unlink() - - with ZipFile(str(target), 'w', ZIP_DEFLATED) as f: - with tempfile.TemporaryDirectory() as tmpdir: - for s, rel in rel_sources: - if rel.suffix.lower() == '.py': - pyc = Path(tmpdir) / rel.with_suffix('.pyc').name - try: - py_compile.compile(str(s), str(pyc), str(rel), doraise=True, optimize=2) - except py_compile.PyCompileError: - f.write(str(s), str(rel)) - else: - f.write(str(pyc), str(rel.with_suffix('.pyc'))) - else: - f.write(str(s), str(rel)) - count += 1 - - else: - for s, rel in rel_sources: - dest = target / rel - try: - dest.parent.mkdir(parents=True) - except FileExistsError: - pass - if dest.is_file(): - dest.chmod(stat.S_IWRITE) - shutil.copy(str(s), str(dest)) - if dest.is_file(): - dest.chmod(stat.S_IWRITE) - count += 1 - - return count - -def rglob(root, pattern, condition): - dirs = [root] - recurse = pattern[:3] in {'**/', '**\\'} - while dirs: - d = dirs.pop(0) - for f in d.glob(pattern[3:] if recurse else pattern): - if recurse and f.is_dir() and (not condition or condition(f)): - dirs.append(f) - elif f.is_file() and (not condition or condition(f)): - yield f, f.relative_to(root) - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('-s', '--source', metavar='dir', help='The directory containing the repository root', type=Path) - parser.add_argument('-o', '--out', metavar='file', help='The name of the output archive', type=Path, default=None) - parser.add_argument('-t', '--temp', metavar='dir', help='A directory to temporarily extract files into', type=Path, default=None) - parser.add_argument('-e', '--embed', help='Create an embedding layout', action='store_true', default=False) - parser.add_argument('-b', '--build', help='Specify the build directory', type=Path, default=None) - ns = parser.parse_args() - - source = ns.source or (Path(__file__).resolve().parent.parent.parent) - out = ns.out - build = ns.build or Path(sys.exec_prefix) - assert isinstance(source, Path) - assert not out or isinstance(out, Path) - assert isinstance(build, Path) - - if ns.temp: - temp = ns.temp - delete_temp = False - else: - temp = Path(tempfile.mkdtemp()) - delete_temp = True - - if out: - try: - out.parent.mkdir(parents=True) - except FileExistsError: - pass - try: - temp.mkdir(parents=True) - except FileExistsError: - pass - - layout = EMBED_LAYOUT if ns.embed else FULL_LAYOUT - - try: - for t, s, p, c in layout: - if s == '$build': - fs = build - else: - fs = source / s - files = rglob(fs, p, c) - extra_files = [] - if s == 'Lib' and p == '**/*': - extra_files.append(( - source / 'tools' / 'msi' / 'distutils.command.bdist_wininst.py', - Path('distutils') / 'command' / 'bdist_wininst.py' - )) - copied = copy_to_layout(temp / t.rstrip('/'), chain(files, extra_files)) - print('Copied {} files'.format(copied)) - - if ns.embed: - with open(str(temp / (BASE_NAME + '._pth')), 'w') as f: - print(BASE_NAME + '.zip', file=f) - print('.', file=f) - print('', file=f) - print('# Uncomment to run site.main() automatically', file=f) - print('#import site', file=f) - - if out: - total = copy_to_layout(out, rglob(temp, '**/*', None)) - print('Wrote {} files to {}'.format(total, out)) - finally: - if delete_temp: - shutil.rmtree(temp, True) - - -if __name__ == "__main__": - sys.exit(int(main() or 0)) diff --git a/Tools/msi/sdktools.psm1 b/Tools/msi/sdktools.psm1 new file mode 100644 index 000000000000..81a74d3679d7 --- /dev/null +++ b/Tools/msi/sdktools.psm1 @@ -0,0 +1,43 @@ +function Find-Tool { + param([string]$toolname) + + $kitroot = (gp 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots\').KitsRoot10 + $tool = (gci -r "$kitroot\Bin\*\x64\$toolname" | sort FullName -Desc | select -First 1) + if (-not $tool) { + throw "$toolname is not available" + } + Write-Host "Found $toolname at $($tool.FullName)" + return $tool.FullName +} + +Set-Alias SignTool (Find-Tool "signtool.exe") -Scope Script + +function Sign-File { + param([string]$certname, [string]$certsha1, [string]$certfile, [string]$description, [string[]]$files) + + if (-not $description) { + $description = $env:SigningDescription; + if (-not $description) { + $description = "Python"; + } + } + if (-not $certname) { + $certname = $env:SigningCertificate; + } + if (-not $certfile) { + $certfile = $env:SigningCertificateFile; + } + + foreach ($a in $files) { + if ($certsha1) { + SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } elseif ($certname) { + SignTool sign /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } elseif ($certfile) { + SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } else { + SignTool sign /a /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + } + } +} + diff --git a/Tools/msi/sign_build.ps1 b/Tools/msi/sign_build.ps1 new file mode 100644 index 000000000000..6668eb33a2d1 --- /dev/null +++ b/Tools/msi/sign_build.ps1 @@ -0,0 +1,34 @@ +<# +.Synopsis + Recursively signs the contents of a directory. +.Description + Given the file patterns, code signs the contents. +.Parameter root + The root directory to sign. +.Parameter patterns + The file patterns to sign +.Parameter description + The description to add to the signature (optional). +.Parameter certname + The name of the certificate to sign with (optional). +.Parameter certsha1 + The SHA1 hash of the certificate to sign with (optional). +#> +param( + [Parameter(Mandatory=$true)][string]$root, + [string[]]$patterns=@("*.exe", "*.dll", "*.pyd"), + [string]$description, + [string]$certname, + [string]$certsha1, + [string]$certfile +) + +$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force + +pushd $root +try { + Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files (gci -r $patterns) +} finally { + popd +} \ No newline at end of file diff --git a/Tools/nuget/make_pkg.proj b/Tools/nuget/make_pkg.proj index 9843bc97ccdc..e093a6d0bd76 100644 --- a/Tools/nuget/make_pkg.proj +++ b/Tools/nuget/make_pkg.proj @@ -20,25 +20,28 @@ false $(OutputName).$(NuspecVersion) .nupkg - $(IntermediateOutputPath)\nuget_$(ArchName) + $(IntermediateOutputPath)\nuget_$(ArchName)\ - rmdir /q/s "$(IntermediateOutputPath)" + rmdir /q/s "$(IntermediateOutputPath.TrimEnd(`\`))" - "$(PythonExe)" "$(MSBuildThisFileDirectory)\..\msi\make_zip.py" - $(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))" + "$(PythonExe)" "$(PySourcePath)PC\layout" + $(PythonArguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))" + $(PythonArguments) -t "$(IntermediateOutputPath)obj" + $(PythonArguments) --copy "$(IntermediateOutputPath)pkg" + $(PythonArguments) --include-dev --include-tools --include-pip --include-stable --include-launcher --include-props - "$(IntermediateOutputPath)\python.exe" -B -c "import sys; sys.path.append(r'$(PySourcePath)\Lib'); import ensurepip; ensurepip._main()" - "$(IntermediateOutputPath)\python.exe" -B -m pip install -U $(Packages) + "$(IntermediateOutputPath)pkg\pip.exe" -B -m pip install -U $(Packages) - "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)" + "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)pkg" "$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))" $(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))" $(NugetArguments) -Version "$(NuspecVersion)" $(NugetArguments) -NoPackageAnalysis -NonInteractive - set DOC_FILENAME=python$(PythonVersion).chm $(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib - $(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform) + $(Environment)%0D%0Aset PYTHON_NUSPEC_VERSION=$(NuspecVersion) + $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=$(Platform) + $(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=Win32 $(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" >nul 2>nul @@ -48,22 +51,7 @@ - - - - - - <_PropsContents>$([System.IO.File]::ReadAllText('python.props')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TAG$$', '$(MajorVersionNumber).$(MinorVersionNumber)')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_VERSION$$', '$(NuspecVersion)')) - <_PropsContents Condition="$(Platform) == 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', 'Win32')) - <_PropsContents Condition="$(Platform) != 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', '$(Platform)')) - <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TARGET$$', '_GetPythonRuntimeFilesDependsOn$(MajorVersionNumber)$(MinorVersionNumber)_$(Platform)')) - <_ExistingContents Condition="Exists('$(IntermediateOutputPath)\python.props')">$([System.IO.File]::ReadAllText('$(IntermediateOutputPath)\python.props')) - - + From webhook-mailer at python.org Mon Dec 10 23:16:51 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 04:16:51 -0000 Subject: [Python-checkins] bpo-35401: Update Windows build to OpenSSL 1.1.0j (GH-11088) Message-ID: https://github.com/python/cpython/commit/d1fb21209bca0620ece849e05cca97bce861c996 commit: d1fb21209bca0620ece849e05cca97bce861c996 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T20:16:46-08:00 summary: bpo-35401: Update Windows build to OpenSSL 1.1.0j (GH-11088) (cherry picked from commit 4824385fec0a1de99b4183f995a3e4923771bf64) Co-authored-by: Steve Dower files: A Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst M PCbuild/get_externals.bat M PCbuild/prepare_ssl.bat M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst b/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst new file mode 100644 index 000000000000..5854388d067d --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst @@ -0,0 +1 @@ +Updates Windows build to OpenSSL 1.1.0j diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 9d4faee5f7cf..7a8de1e7612c 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -49,7 +49,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.6 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.0i +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.0j set libraries=%libraries% sqlite-3.21.0.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.8.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.8.0 @@ -72,7 +72,7 @@ for %%e in (%libraries%) do ( echo.Fetching external binaries... set binaries= -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.0i +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.0j if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.8.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/prepare_ssl.bat b/PCbuild/prepare_ssl.bat index bfdac5404423..bd4b548528c5 100644 --- a/PCbuild/prepare_ssl.bat +++ b/PCbuild/prepare_ssl.bat @@ -23,8 +23,6 @@ setlocal if "%PCBUILD%"=="" (set PCBUILD=%~dp0) if "%EXTERNALS_DIR%"=="" (set EXTERNALS_DIR=%PCBUILD%\..\externals) -set OUT= -set SRC= set ORG_SETTING= :CheckOpts @@ -32,19 +30,12 @@ if "%~1"=="-h" shift & goto Usage if "%~1"=="--certificate" (set SigningCertificate=%~2) && shift && shift & goto CheckOpts if "%~1"=="-c" (set SigningCertificate=%~2) && shift && shift & goto CheckOpts if "%~1"=="--organization" (set ORG_SETTING=--organization "%~2") && shift && shift && goto CheckOpts -if "%~1"=="-i" (SET SRC=$~2) && shift && shift && goto CheckOpts -if "%~1"=="--in" (SET SRC=$~2) && shift && shift && goto CheckOpts -if "%~1"=="-o" (set OUT=$~2) && shift && shift && goto CheckOpts -if "%~1"=="--out" (set OUT=$~2) && shift && shift && goto CheckOpts if "%~1"=="" goto Build echo Unrecognized option: %1 goto Usage :Build -if not defined SRC (echo --in directory is required & exit /b 1) -if not defined OUT (echo --out directory is required & exit /b 1) - call "%PCBUILD%\find_msbuild.bat" %MSBUILD% if ERRORLEVEL 1 (echo Cannot locate MSBuild.exe on PATH or as MSBUILD variable & exit /b 2) diff --git a/PCbuild/python.props b/PCbuild/python.props index f474e6f07e6d..f83d4df0d59f 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -49,8 +49,8 @@ $(ExternalsDir)sqlite-3.21.0.0\ $(ExternalsDir)bzip2-1.0.6\ $(ExternalsDir)xz-5.2.2\ - $(ExternalsDir)openssl-1.1.0i\ - $(ExternalsDir)openssl-bin-1.1.0i\$(ArchName)\ + $(ExternalsDir)openssl-1.1.0j\ + $(ExternalsDir)openssl-bin-1.1.0j\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.11\ From webhook-mailer at python.org Mon Dec 10 23:26:36 2018 From: webhook-mailer at python.org (Steve Dower) Date: Tue, 11 Dec 2018 04:26:36 -0000 Subject: [Python-checkins] bpo-35401: Updates Windows build to OpenSSL 1.0.2q (GH-11089) Message-ID: https://github.com/python/cpython/commit/3c8bd22b8f0d7f57261b9d3c90e56447cd5acf94 commit: 3c8bd22b8f0d7f57261b9d3c90e56447cd5acf94 branch: 2.7 author: Steve Dower committer: GitHub date: 2018-12-10T20:26:33-08:00 summary: bpo-35401: Updates Windows build to OpenSSL 1.0.2q (GH-11089) files: A Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst M PCbuild/get_externals.bat M PCbuild/libeay.vcxproj M PCbuild/python.props diff --git a/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst b/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst new file mode 100644 index 000000000000..a804473fd6db --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-12-10-15-01-13.bpo-35401.9L1onG.rst @@ -0,0 +1 @@ +Updates Windows build to OpenSSL 1.0.2q diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 8bc19e678ce7..bda243fe2e31 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -47,7 +47,7 @@ rem files in both this dir and PC\VS9.0 set libraries= set libraries=%libraries% bzip2-1.0.6 if NOT "%IncludeBsddb%"=="false" set libraries=%libraries% bsddb-4.7.25.0 -if NOT "%IncludeSSL%"=="false" set libraries=%libraries% openssl-1.0.2p +if NOT "%IncludeSSL%"=="false" set libraries=%libraries% openssl-1.0.2q set libraries=%libraries% sqlite-3.14.2.0 if NOT "%IncludeTkinter%"=="false" set libraries=%libraries% tcl-8.5.19.0 if NOT "%IncludeTkinter%"=="false" set libraries=%libraries% tk-8.5.19.0 diff --git a/PCbuild/libeay.vcxproj b/PCbuild/libeay.vcxproj index 68ef936db88b..b0994e12f0cd 100644 --- a/PCbuild/libeay.vcxproj +++ b/PCbuild/libeay.vcxproj @@ -533,6 +533,7 @@ + @@ -725,9 +726,7 @@ - diff --git a/PCbuild/python.props b/PCbuild/python.props index cebb86f147a5..6673ff31368e 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -35,7 +35,7 @@ $(ExternalsDir)sqlite-3.14.2.0\ $(ExternalsDir)bzip2-1.0.6\ $(ExternalsDir)bsddb-4.7.25.0 - $(ExternalsDir)openssl-1.0.2p\ + $(ExternalsDir)openssl-1.0.2q\ $(opensslDir)include32 $(opensslDir)include64 $(ExternalsDir)\nasm-2.11.06\ From webhook-mailer at python.org Mon Dec 10 23:56:12 2018 From: webhook-mailer at python.org (Steve Dower) Date: Tue, 11 Dec 2018 04:56:12 -0000 Subject: [Python-checkins] bpo-34977: Remove unused preprocessor definition (GH-11092) Message-ID: https://github.com/python/cpython/commit/d5a6a389d492c5e3d7933bafbd5252fd86ac4d49 commit: d5a6a389d492c5e3d7933bafbd5252fd86ac4d49 branch: master author: Steve Dower committer: GitHub date: 2018-12-10T20:56:09-08:00 summary: bpo-34977: Remove unused preprocessor definition (GH-11092) files: M PC/python_uwp.cpp diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp index b015abd59e66..5c8caa6666c4 100644 --- a/PC/python_uwp.cpp +++ b/PC/python_uwp.cpp @@ -7,12 +7,8 @@ #include #include -#ifdef PYTHON_UWP_SUPPORTED #include #include -#else -#include -#endif #ifdef PYTHONW #ifdef _DEBUG @@ -31,7 +27,6 @@ const wchar_t *PROGNAME = L"python.exe"; static void set_user_base() { -#ifdef PYTHON_UWP_SUPPORTED wchar_t envBuffer[2048]; try { const auto appData = winrt::Windows::Storage::ApplicationData::Current(); @@ -49,17 +44,12 @@ set_user_base() } } catch (...) { } -#endif } static const wchar_t * get_argv0(const wchar_t *argv0) { -#ifdef PYTHON_UWP_SUPPORTED winrt::hstring installPath; -#else - std::wstring installPath; -#endif const wchar_t *launcherPath; wchar_t *buffer; size_t len; @@ -79,7 +69,6 @@ get_argv0(const wchar_t *argv0) return buffer; } -#ifdef PYTHON_UWP_SUPPORTED try { const auto package = winrt::Windows::ApplicationModel::Package::Current(); if (package) { @@ -91,7 +80,6 @@ get_argv0(const wchar_t *argv0) } catch (...) { } -#endif if (!installPath.empty()) { len = installPath.size() + wcslen(PROGNAME) + 2; From webhook-mailer at python.org Tue Dec 11 00:15:02 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 05:15:02 -0000 Subject: [Python-checkins] bpo-34977: Remove unused preprocessor definition (GH-11092) Message-ID: https://github.com/python/cpython/commit/9bb306d586e3f1a48db40bd9519412de4fff3ee8 commit: 9bb306d586e3f1a48db40bd9519412de4fff3ee8 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T21:14:57-08:00 summary: bpo-34977: Remove unused preprocessor definition (GH-11092) (cherry picked from commit d5a6a389d492c5e3d7933bafbd5252fd86ac4d49) Co-authored-by: Steve Dower files: M PC/python_uwp.cpp diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp index b015abd59e66..5c8caa6666c4 100644 --- a/PC/python_uwp.cpp +++ b/PC/python_uwp.cpp @@ -7,12 +7,8 @@ #include #include -#ifdef PYTHON_UWP_SUPPORTED #include #include -#else -#include -#endif #ifdef PYTHONW #ifdef _DEBUG @@ -31,7 +27,6 @@ const wchar_t *PROGNAME = L"python.exe"; static void set_user_base() { -#ifdef PYTHON_UWP_SUPPORTED wchar_t envBuffer[2048]; try { const auto appData = winrt::Windows::Storage::ApplicationData::Current(); @@ -49,17 +44,12 @@ set_user_base() } } catch (...) { } -#endif } static const wchar_t * get_argv0(const wchar_t *argv0) { -#ifdef PYTHON_UWP_SUPPORTED winrt::hstring installPath; -#else - std::wstring installPath; -#endif const wchar_t *launcherPath; wchar_t *buffer; size_t len; @@ -79,7 +69,6 @@ get_argv0(const wchar_t *argv0) return buffer; } -#ifdef PYTHON_UWP_SUPPORTED try { const auto package = winrt::Windows::ApplicationModel::Package::Current(); if (package) { @@ -91,7 +80,6 @@ get_argv0(const wchar_t *argv0) } catch (...) { } -#endif if (!installPath.empty()) { len = installPath.size() + wcslen(PROGNAME) + 2; From webhook-mailer at python.org Tue Dec 11 00:24:09 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 05:24:09 -0000 Subject: [Python-checkins] bpo-35401: Update macOS installer to OpenSSL 1.1.0j (GH-11094) Message-ID: https://github.com/python/cpython/commit/3ec982640f89f6ce56dd2699a81e0bd834ae0c95 commit: 3ec982640f89f6ce56dd2699a81e0bd834ae0c95 branch: master author: Ned Deily committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-10T21:24:06-08:00 summary: bpo-35401: Update macOS installer to OpenSSL 1.1.0j (GH-11094) https://bugs.python.org/issue35401 files: A Misc/NEWS.d/next/macOS/2018-12-09-13-56-49.bpo-35401.n8B7X1.rst M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 15488780327e..d55020883928 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -215,9 +215,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 1.1.0i", - url="https://www.openssl.org/source/openssl-1.1.0i.tar.gz", - checksum='9495126aafd2659d357ea66a969c3fe1', + name="OpenSSL 1.1.0j", + url="https://www.openssl.org/source/openssl-1.1.0j.tar.gz", + checksum='b4ca5b78ae6ae79da80790b30dbedbdc', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/macOS/2018-12-09-13-56-49.bpo-35401.n8B7X1.rst b/Misc/NEWS.d/next/macOS/2018-12-09-13-56-49.bpo-35401.n8B7X1.rst new file mode 100644 index 000000000000..bfe41bd67ff6 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-09-13-56-49.bpo-35401.n8B7X1.rst @@ -0,0 +1 @@ +Update macOS installer to use OpenSSL 1.1.0j. From webhook-mailer at python.org Tue Dec 11 00:37:58 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 05:37:58 -0000 Subject: [Python-checkins] [3.6] bpo-35401: Update macOS installer to OpenSSL 1.0.2q (GH-11095) Message-ID: https://github.com/python/cpython/commit/419b5ffc2ca46d7adf0be6216ca3a6e40028e50f commit: 419b5ffc2ca46d7adf0be6216ca3a6e40028e50f branch: 3.6 author: Ned Deily committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-10T21:37:53-08:00 summary: [3.6] bpo-35401: Update macOS installer to OpenSSL 1.0.2q (GH-11095) https://bugs.python.org/issue35401 files: A Misc/NEWS.d/next/macOS/2018-12-10-02-37-11.bpo-35401.sFhD5z.rst M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index b97d55bb0306..2ef6ddc095e5 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -211,9 +211,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 1.0.2p", - url="https://www.openssl.org/source/openssl-1.0.2p.tar.gz", - checksum='ac5eb30bf5798aa14b1ae6d0e7da58df', + name="OpenSSL 1.0.2q", + url="https://www.openssl.org/source/openssl-1.0.2q.tar.gz", + checksum='7563e1ce046cb21948eeb6ba1a0eb71c', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/macOS/2018-12-10-02-37-11.bpo-35401.sFhD5z.rst b/Misc/NEWS.d/next/macOS/2018-12-10-02-37-11.bpo-35401.sFhD5z.rst new file mode 100644 index 000000000000..113329d0f231 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-10-02-37-11.bpo-35401.sFhD5z.rst @@ -0,0 +1 @@ +Update macOS installer to use OpenSSL 1.0.2q. From webhook-mailer at python.org Tue Dec 11 00:43:22 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 05:43:22 -0000 Subject: [Python-checkins] bpo-35401: Update macOS installer to OpenSSL 1.1.0j (GH-11094) Message-ID: https://github.com/python/cpython/commit/c37923ece75721c21b06f7e3645d2838c2452e18 commit: c37923ece75721c21b06f7e3645d2838c2452e18 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T21:43:17-08:00 summary: bpo-35401: Update macOS installer to OpenSSL 1.1.0j (GH-11094) https://bugs.python.org/issue35401 (cherry picked from commit 3ec982640f89f6ce56dd2699a81e0bd834ae0c95) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/macOS/2018-12-09-13-56-49.bpo-35401.n8B7X1.rst M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 15488780327e..d55020883928 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -215,9 +215,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 1.1.0i", - url="https://www.openssl.org/source/openssl-1.1.0i.tar.gz", - checksum='9495126aafd2659d357ea66a969c3fe1', + name="OpenSSL 1.1.0j", + url="https://www.openssl.org/source/openssl-1.1.0j.tar.gz", + checksum='b4ca5b78ae6ae79da80790b30dbedbdc', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/macOS/2018-12-09-13-56-49.bpo-35401.n8B7X1.rst b/Misc/NEWS.d/next/macOS/2018-12-09-13-56-49.bpo-35401.n8B7X1.rst new file mode 100644 index 000000000000..bfe41bd67ff6 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-09-13-56-49.bpo-35401.n8B7X1.rst @@ -0,0 +1 @@ +Update macOS installer to use OpenSSL 1.1.0j. From webhook-mailer at python.org Tue Dec 11 00:54:07 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 05:54:07 -0000 Subject: [Python-checkins] [3.6] bpo-35401: Update macOS installer to OpenSSL 1.0.2q (GH-11095) Message-ID: https://github.com/python/cpython/commit/55076cc0ffd1b7602d67f3a9a420d6261ffd5c89 commit: 55076cc0ffd1b7602d67f3a9a420d6261ffd5c89 branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T21:54:04-08:00 summary: [3.6] bpo-35401: Update macOS installer to OpenSSL 1.0.2q (GH-11095) https://bugs.python.org/issue35401 (cherry picked from commit 419b5ffc2ca46d7adf0be6216ca3a6e40028e50f) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/macOS/2018-12-10-02-37-11.bpo-35401.sFhD5z.rst M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index b97d55bb0306..2ef6ddc095e5 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -211,9 +211,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 1.0.2p", - url="https://www.openssl.org/source/openssl-1.0.2p.tar.gz", - checksum='ac5eb30bf5798aa14b1ae6d0e7da58df', + name="OpenSSL 1.0.2q", + url="https://www.openssl.org/source/openssl-1.0.2q.tar.gz", + checksum='7563e1ce046cb21948eeb6ba1a0eb71c', buildrecipe=build_universal_openssl, configure=None, install=None, diff --git a/Misc/NEWS.d/next/macOS/2018-12-10-02-37-11.bpo-35401.sFhD5z.rst b/Misc/NEWS.d/next/macOS/2018-12-10-02-37-11.bpo-35401.sFhD5z.rst new file mode 100644 index 000000000000..113329d0f231 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-10-02-37-11.bpo-35401.sFhD5z.rst @@ -0,0 +1 @@ +Update macOS installer to use OpenSSL 1.0.2q. From webhook-mailer at python.org Tue Dec 11 01:07:01 2018 From: webhook-mailer at python.org (Ned Deily) Date: Tue, 11 Dec 2018 06:07:01 -0000 Subject: [Python-checkins] bpo-35402: Update macOS installer to use Tcl 8.6.9 / Tk 8.6.9.1 (GH-11101) Message-ID: https://github.com/python/cpython/commit/7cf3d8e25174c8871883e42f3240fd7f01efd3a8 commit: 7cf3d8e25174c8871883e42f3240fd7f01efd3a8 branch: master author: Ned Deily committer: GitHub date: 2018-12-11T01:06:57-05:00 summary: bpo-35402: Update macOS installer to use Tcl 8.6.9 / Tk 8.6.9.1 (GH-11101) files: A Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst D Mac/BuildScript/tk868_on_10_8_10_9.patch M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index d55020883928..806f660bd644 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -227,9 +227,9 @@ def library_recipes(): if internalTk(): result.extend([ dict( - name="Tcl 8.6.8", - url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz", - checksum='81656d3367af032e0ae6157eff134f89', + name="Tcl 8.6.9", + url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.9-src.tar.gz", + checksum='aa0a121d95a0e7b73a036f26028538d4', buildDir="unix", configure_pre=[ '--enable-shared', @@ -243,12 +243,9 @@ def library_recipes(): }, ), dict( - name="Tk 8.6.8", - url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz", - checksum='5e0faecba458ee1386078fb228d008ba', - patches=[ - "tk868_on_10_8_10_9.patch", - ], + name="Tk 8.6.9.1", + url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.9.1-src.tar.gz", + checksum='9efe3976468352dc894dae0c4e785a8e', buildDir="unix", configure_pre=[ '--enable-aqua', @@ -709,6 +706,7 @@ def extractArchive(builddir, archiveName): work for current Tcl and Tk source releases where the basename of the archive ends with "-src" but the uncompressed directory does not. For now, just special case Tcl and Tk tar.gz downloads. + Another special case: the tk8.6.9.1 tarball extracts to tk8.6.9. """ curdir = os.getcwd() try: @@ -718,6 +716,8 @@ def extractArchive(builddir, archiveName): if ((retval.startswith('tcl') or retval.startswith('tk')) and retval.endswith('-src')): retval = retval[:-4] + if retval == 'tk8.6.9.1': + retval = 'tk8.6.9' if os.path.exists(retval): shutil.rmtree(retval) fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r') diff --git a/Mac/BuildScript/tk868_on_10_8_10_9.patch b/Mac/BuildScript/tk868_on_10_8_10_9.patch deleted file mode 100644 index 8fe10604a68c..000000000000 --- a/Mac/BuildScript/tk868_on_10_8_10_9.patch +++ /dev/null @@ -1,18 +0,0 @@ -Fix build failure with +quartz variant on OS X 10.8 and 10.9. -Even though Gestalt was deprecated in OS X 10.8, it should work fine -through OS X 10.9, and its replacement NSOperatingSystemVersion was -not introduced until OS X 10.10. - -Patch from MacPorts project and reported upstream: -https://trac.macports.org/ticket/55649 ---- tk8.6.8/macosx/tkMacOSXXStubs.c.orig 2017-12-06 09:25:08.000000000 -0600 -+++ tk8.6.8-patched/macosx/tkMacOSXXStubs.c 2018-01-06 19:34:17.000000000 -0600 -@@ -175,7 +175,7 @@ - { - int major, minor, patch; - --#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 -+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 - Gestalt(gestaltSystemVersionMajor, (SInt32*)&major); - Gestalt(gestaltSystemVersionMinor, (SInt32*)&minor); - Gestalt(gestaltSystemVersionBugFix, (SInt32*)&patch); diff --git a/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst b/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst new file mode 100644 index 000000000000..d6b503d10527 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst @@ -0,0 +1 @@ +Update macOS installer to use Tcl/Tk 8.6.9.1. From webhook-mailer at python.org Tue Dec 11 01:28:22 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 11 Dec 2018 06:28:22 -0000 Subject: [Python-checkins] bpo-35444: Unify and optimize the helper for getting a builtin object. (GH-11047) Message-ID: https://github.com/python/cpython/commit/bb86bf4c4eaa30b1f5192dab9f389ce0bb61114d commit: bb86bf4c4eaa30b1f5192dab9f389ce0bb61114d branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-11T08:28:18+02:00 summary: bpo-35444: Unify and optimize the helper for getting a builtin object. (GH-11047) This speeds up pickling of some iterators. This fixes also error handling in pickling methods when fail to look up builtin "getattr". files: A Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst M Include/ceval.h M Include/cpython/object.h M Modules/_pickle.c M Modules/arraymodule.c M Modules/itertoolsmodule.c M Objects/bytearrayobject.c M Objects/bytesobject.c M Objects/classobject.c M Objects/descrobject.c M Objects/dictobject.c M Objects/iterobject.c M Objects/listobject.c M Objects/methodobject.c M Objects/object.c M Objects/odictobject.c M Objects/rangeobject.c M Objects/setobject.c M Objects/tupleobject.c M Objects/unicodeobject.c M Python/ceval.c diff --git a/Include/ceval.h b/Include/ceval.h index bce8a0beed85..11283c0a570b 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -48,10 +48,12 @@ PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void); PyAPI_FUNC(PyObject *) PyEval_GetLocals(void); PyAPI_FUNC(struct _frame *) PyEval_GetFrame(void); +#ifndef Py_LIMITED_API +/* Helper to look up a builtin object */ +PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *); /* Look at the current frame's (if any) code's co_flags, and turn on the corresponding compiler flags in cf->cf_flags. Return 1 if any flag was set, else return 0. */ -#ifndef Py_LIMITED_API PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf); #endif diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 77184c95151a..64d196a722ee 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -330,9 +330,6 @@ PyAPI_FUNC(int) _PyObject_GenericSetAttrWithDict(PyObject *, PyObject *, PyObject *, PyObject *); -/* Helper to look up a builtin object */ -PyAPI_FUNC(PyObject *) _PyObject_GetBuiltin(const char *name); - #define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0) static inline void _Py_Dealloc_inline(PyObject *op) diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst new file mode 100644 index 000000000000..39591f9c32dd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst @@ -0,0 +1,2 @@ +Fixed error handling in pickling methods when fail to look up builtin +"getattr". Sped up pickling iterators. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index c4fe349187c4..58cd09ca61a6 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -209,19 +209,15 @@ _Pickle_ClearState(PickleState *st) static int _Pickle_InitState(PickleState *st) { - PyObject *builtins; PyObject *copyreg = NULL; PyObject *compat_pickle = NULL; PyObject *codecs = NULL; PyObject *functools = NULL; + _Py_IDENTIFIER(getattr); - builtins = PyEval_GetBuiltins(); - if (builtins == NULL) - goto error; - st->getattr = PyDict_GetItemString(builtins, "getattr"); + st->getattr = _PyEval_GetBuiltinId(&PyId_getattr); if (st->getattr == NULL) goto error; - Py_INCREF(st->getattr); copyreg = PyImport_ImportModule("copyreg"); if (!copyreg) diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index aa7a4fb23c68..1bfc4dd95bfd 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -2943,7 +2943,8 @@ static PyObject * array_arrayiterator___reduce___impl(arrayiterobject *self) /*[clinic end generated code: output=7898a52e8e66e016 input=a062ea1e9951417a]*/ { - PyObject *func = _PyObject_GetBuiltin("iter"); + _Py_IDENTIFIER(iter); + PyObject *func = _PyEval_GetBuiltinId(&PyId_iter); if (self->ao == NULL) { return Py_BuildValue("N(())", func); } diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 89c0280c9d35..581ae2c67e85 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -368,8 +368,9 @@ _grouper_next(_grouperobject *igo) static PyObject * _grouper_reduce(_grouperobject *lz, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); if (((groupbyobject *)lz->parent)->currgrouper != lz) { - return Py_BuildValue("N(())", _PyObject_GetBuiltin("iter")); + return Py_BuildValue("N(())", _PyEval_GetBuiltinId(&PyId_iter)); } return Py_BuildValue("O(OO)", Py_TYPE(lz), lz->parent, lz->tgtkey); } diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 144265381943..3926095a1812 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2358,11 +2358,12 @@ PyDoc_STRVAR(length_hint_doc, static PyObject * bytearrayiter_reduce(bytesiterobject *it, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); if (it->it_seq != NULL) { - return Py_BuildValue("N(O)n", _PyObject_GetBuiltin("iter"), + return Py_BuildValue("N(O)n", _PyEval_GetBuiltinId(&PyId_iter), it->it_seq, it->it_index); } else { - return Py_BuildValue("N(())", _PyObject_GetBuiltin("iter")); + return Py_BuildValue("N(())", _PyEval_GetBuiltinId(&PyId_iter)); } } diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index e4a49731aba6..adf0cff5f3b2 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3089,11 +3089,12 @@ PyDoc_STRVAR(length_hint_doc, static PyObject * striter_reduce(striterobject *it, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); if (it->it_seq != NULL) { - return Py_BuildValue("N(O)n", _PyObject_GetBuiltin("iter"), + return Py_BuildValue("N(O)n", _PyEval_GetBuiltinId(&PyId_iter), it->it_seq, it->it_index); } else { - return Py_BuildValue("N(())", _PyObject_GetBuiltin("iter")); + return Py_BuildValue("N(())", _PyEval_GetBuiltinId(&PyId_iter)); } } diff --git a/Objects/classobject.c b/Objects/classobject.c index 1eed5d36ab03..0d1cf7a95ccc 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -78,8 +78,6 @@ method_reduce(PyMethodObject *im, PyObject *Py_UNUSED(ignored)) { PyObject *self = PyMethod_GET_SELF(im); PyObject *func = PyMethod_GET_FUNCTION(im); - PyObject *builtins; - PyObject *getattr; PyObject *funcname; _Py_IDENTIFIER(getattr); @@ -87,9 +85,8 @@ method_reduce(PyMethodObject *im, PyObject *Py_UNUSED(ignored)) if (funcname == NULL) { return NULL; } - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(ON)", getattr, self, funcname); + return Py_BuildValue("N(ON)", _PyEval_GetBuiltinId(&PyId_getattr), + self, funcname); } static PyMethodDef method_methods[] = { diff --git a/Objects/descrobject.c b/Objects/descrobject.c index e129d0c48f27..22546a563a51 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -452,14 +452,9 @@ descr_get_qualname(PyDescrObject *descr, void *Py_UNUSED(ignored)) static PyObject * descr_reduce(PyDescrObject *descr, PyObject *Py_UNUSED(ignored)) { - PyObject *builtins; - PyObject *getattr; _Py_IDENTIFIER(getattr); - - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(OO)", getattr, PyDescr_TYPE(descr), - PyDescr_NAME(descr)); + return Py_BuildValue("N(OO)", _PyEval_GetBuiltinId(&PyId_getattr), + PyDescr_TYPE(descr), PyDescr_NAME(descr)); } static PyMethodDef descr_methods[] = { @@ -1087,13 +1082,9 @@ wrapper_repr(wrapperobject *wp) static PyObject * wrapper_reduce(wrapperobject *wp, PyObject *Py_UNUSED(ignored)) { - PyObject *builtins; - PyObject *getattr; _Py_IDENTIFIER(getattr); - - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(OO)", getattr, wp->self, PyDescr_NAME(wp->descr)); + return Py_BuildValue("N(OO)", _PyEval_GetBuiltinId(&PyId_getattr), + wp->self, PyDescr_NAME(wp->descr)); } static PyMethodDef wrapper_methods[] = { diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 72cb4c5d666f..a87163682377 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3810,6 +3810,7 @@ dict___reversed___impl(PyDictObject *self) static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); /* copy the iterator state */ dictiterobject tmp = *di; Py_XINCREF(tmp.di_dict); @@ -3819,7 +3820,7 @@ dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) if (list == NULL) { return NULL; } - return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), list); + return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } PyTypeObject PyDictRevIterItem_Type = { diff --git a/Objects/iterobject.c b/Objects/iterobject.c index ada1bdc7e87e..5bee1e21e65e 100644 --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -104,11 +104,12 @@ PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list( static PyObject * iter_reduce(seqiterobject *it, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); if (it->it_seq != NULL) - return Py_BuildValue("N(O)n", _PyObject_GetBuiltin("iter"), + return Py_BuildValue("N(O)n", _PyEval_GetBuiltinId(&PyId_iter), it->it_seq, it->it_index); else - return Py_BuildValue("N(())", _PyObject_GetBuiltin("iter")); + return Py_BuildValue("N(())", _PyEval_GetBuiltinId(&PyId_iter)); } PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); @@ -243,11 +244,12 @@ calliter_iternext(calliterobject *it) static PyObject * calliter_reduce(calliterobject *it, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); if (it->it_callable != NULL && it->it_sentinel != NULL) - return Py_BuildValue("N(OO)", _PyObject_GetBuiltin("iter"), + return Py_BuildValue("N(OO)", _PyEval_GetBuiltinId(&PyId_iter), it->it_callable, it->it_sentinel); else - return Py_BuildValue("N(())", _PyObject_GetBuiltin("iter")); + return Py_BuildValue("N(())", _PyEval_GetBuiltinId(&PyId_iter)); } static PyMethodDef calliter_methods[] = { diff --git a/Objects/listobject.c b/Objects/listobject.c index 6da8391fc275..b1f2b59db0a7 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3356,23 +3356,25 @@ listreviter_setstate(listreviterobject *it, PyObject *state) static PyObject * listiter_reduce_general(void *_it, int forward) { + _Py_IDENTIFIER(iter); + _Py_IDENTIFIER(reversed); PyObject *list; /* the objects are not the same, index is of different types! */ if (forward) { listiterobject *it = (listiterobject *)_it; if (it->it_seq) - return Py_BuildValue("N(O)n", _PyObject_GetBuiltin("iter"), + return Py_BuildValue("N(O)n", _PyEval_GetBuiltinId(&PyId_iter), it->it_seq, it->it_index); } else { listreviterobject *it = (listreviterobject *)_it; if (it->it_seq) - return Py_BuildValue("N(O)n", _PyObject_GetBuiltin("reversed"), + return Py_BuildValue("N(O)n", _PyEval_GetBuiltinId(&PyId_reversed), it->it_seq, it->it_index); } /* empty iterator, create an empty list */ list = PyList_New(0); if (list == NULL) return NULL; - return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), list); + return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 6dec64276de4..9fed3fca99be 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -104,16 +104,13 @@ meth_dealloc(PyCFunctionObject *m) static PyObject * meth_reduce(PyCFunctionObject *m, PyObject *Py_UNUSED(ignored)) { - PyObject *builtins; - PyObject *getattr; _Py_IDENTIFIER(getattr); if (m->m_self == NULL || PyModule_Check(m->m_self)) return PyUnicode_FromString(m->m_ml->ml_name); - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(Os)", getattr, m->m_self, m->m_ml->ml_name); + return Py_BuildValue("N(Os)", _PyEval_GetBuiltinId(&PyId_getattr), + m->m_self, m->m_ml->ml_name); } static PyMethodDef meth_methods[] = { diff --git a/Objects/object.c b/Objects/object.c index c2d78aa47e65..993342eae5d8 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1080,23 +1080,6 @@ PyObject_SelfIter(PyObject *obj) return obj; } -/* Convenience function to get a builtin from its name */ -PyObject * -_PyObject_GetBuiltin(const char *name) -{ - PyObject *mod_name, *mod, *attr; - - mod_name = _PyUnicode_FromId(&PyId_builtins); /* borrowed */ - if (mod_name == NULL) - return NULL; - mod = PyImport_Import(mod_name); - if (mod == NULL) - return NULL; - attr = PyObject_GetAttrString(mod, name); - Py_DECREF(mod); - return attr; -} - /* Helper used when the __next__ method is removed from a type: tp_iternext is never NULL and can be safely called without checking on every iteration. diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 689062c95ddc..767eb5f60612 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -1806,6 +1806,7 @@ PyDoc_STRVAR(reduce_doc, "Return state information for pickling"); static PyObject * odictiter_reduce(odictiterobject *di, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); /* copy the iterator state */ odictiterobject tmp = *di; Py_XINCREF(tmp.di_odict); @@ -1818,7 +1819,7 @@ odictiter_reduce(odictiterobject *di, PyObject *Py_UNUSED(ignored)) if (list == NULL) { return NULL; } - return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), list); + return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } static PyMethodDef odictiter_methods[] = { diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index e7b47bde6661..4b8e5ed4cfd4 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -742,6 +742,7 @@ PyDoc_STRVAR(length_hint_doc, static PyObject * rangeiter_reduce(rangeiterobject *r, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); PyObject *start=NULL, *stop=NULL, *step=NULL; PyObject *range; @@ -760,7 +761,8 @@ rangeiter_reduce(rangeiterobject *r, PyObject *Py_UNUSED(ignored)) if (range == NULL) goto err; /* return the result */ - return Py_BuildValue("N(N)i", _PyObject_GetBuiltin("iter"), range, r->index); + return Py_BuildValue("N(N)i", _PyEval_GetBuiltinId(&PyId_iter), + range, r->index); err: Py_XDECREF(start); Py_XDECREF(stop); @@ -898,6 +900,7 @@ longrangeiter_len(longrangeiterobject *r, PyObject *no_args) static PyObject * longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); PyObject *product, *stop=NULL; PyObject *range; @@ -921,7 +924,8 @@ longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored)) } /* return the result */ - return Py_BuildValue("N(N)O", _PyObject_GetBuiltin("iter"), range, r->index); + return Py_BuildValue("N(N)O", _PyEval_GetBuiltinId(&PyId_iter), + range, r->index); } static PyObject * diff --git a/Objects/setobject.c b/Objects/setobject.c index c2a1467ba61a..a43ecd52853a 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -842,6 +842,7 @@ static PyObject *setiter_iternext(setiterobject *si); static PyObject * setiter_reduce(setiterobject *si, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); /* copy the iterator state */ setiterobject tmp = *si; Py_XINCREF(tmp.si_set); @@ -852,7 +853,7 @@ setiter_reduce(setiterobject *si, PyObject *Py_UNUSED(ignored)) if (list == NULL) { return NULL; } - return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), list); + return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 83c63e089c3e..9cf3f3dd66ee 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -1024,11 +1024,12 @@ PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list( static PyObject * tupleiter_reduce(tupleiterobject *it, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); if (it->it_seq) - return Py_BuildValue("N(O)n", _PyObject_GetBuiltin("iter"), + return Py_BuildValue("N(O)n", _PyEval_GetBuiltinId(&PyId_iter), it->it_seq, it->it_index); else - return Py_BuildValue("N(())", _PyObject_GetBuiltin("iter")); + return Py_BuildValue("N(())", _PyEval_GetBuiltinId(&PyId_iter)); } static PyObject * diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index d0f0358cfc69..06338fac2b28 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15444,14 +15444,15 @@ PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list( static PyObject * unicodeiter_reduce(unicodeiterobject *it, PyObject *Py_UNUSED(ignored)) { + _Py_IDENTIFIER(iter); if (it->it_seq != NULL) { - return Py_BuildValue("N(O)n", _PyObject_GetBuiltin("iter"), + return Py_BuildValue("N(O)n", _PyEval_GetBuiltinId(&PyId_iter), it->it_seq, it->it_index); } else { PyObject *u = (PyObject *)_PyUnicode_New(0); if (u == NULL) return NULL; - return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), u); + return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), u); } } diff --git a/Python/ceval.c b/Python/ceval.c index 7ffc68a1e1fc..ebe1c50e7bad 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4437,6 +4437,20 @@ PyEval_GetBuiltins(void) return current_frame->f_builtins; } +/* Convenience function to get a builtin from its name */ +PyObject * +_PyEval_GetBuiltinId(_Py_Identifier *name) +{ + PyObject *attr = _PyDict_GetItemIdWithError(PyEval_GetBuiltins(), name); + if (attr) { + Py_INCREF(attr); + } + else if (!PyErr_Occurred()) { + PyErr_SetObject(PyExc_AttributeError, _PyUnicode_FromId(name)); + } + return attr; +} + PyObject * PyEval_GetLocals(void) { From webhook-mailer at python.org Tue Dec 11 01:28:52 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 06:28:52 -0000 Subject: [Python-checkins] bpo-35402: Update macOS installer to use Tcl 8.6.9 / Tk 8.6.9.1 (GH-11101) Message-ID: https://github.com/python/cpython/commit/3b9a0186c44d0c3e477c38fdc00203ec99aec912 commit: 3b9a0186c44d0c3e477c38fdc00203ec99aec912 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T22:28:49-08:00 summary: bpo-35402: Update macOS installer to use Tcl 8.6.9 / Tk 8.6.9.1 (GH-11101) (cherry picked from commit 7cf3d8e25174c8871883e42f3240fd7f01efd3a8) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst D Mac/BuildScript/tk868_on_10_8_10_9.patch M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index d55020883928..806f660bd644 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -227,9 +227,9 @@ def library_recipes(): if internalTk(): result.extend([ dict( - name="Tcl 8.6.8", - url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz", - checksum='81656d3367af032e0ae6157eff134f89', + name="Tcl 8.6.9", + url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.9-src.tar.gz", + checksum='aa0a121d95a0e7b73a036f26028538d4', buildDir="unix", configure_pre=[ '--enable-shared', @@ -243,12 +243,9 @@ def library_recipes(): }, ), dict( - name="Tk 8.6.8", - url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz", - checksum='5e0faecba458ee1386078fb228d008ba', - patches=[ - "tk868_on_10_8_10_9.patch", - ], + name="Tk 8.6.9.1", + url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.9.1-src.tar.gz", + checksum='9efe3976468352dc894dae0c4e785a8e', buildDir="unix", configure_pre=[ '--enable-aqua', @@ -709,6 +706,7 @@ def extractArchive(builddir, archiveName): work for current Tcl and Tk source releases where the basename of the archive ends with "-src" but the uncompressed directory does not. For now, just special case Tcl and Tk tar.gz downloads. + Another special case: the tk8.6.9.1 tarball extracts to tk8.6.9. """ curdir = os.getcwd() try: @@ -718,6 +716,8 @@ def extractArchive(builddir, archiveName): if ((retval.startswith('tcl') or retval.startswith('tk')) and retval.endswith('-src')): retval = retval[:-4] + if retval == 'tk8.6.9.1': + retval = 'tk8.6.9' if os.path.exists(retval): shutil.rmtree(retval) fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r') diff --git a/Mac/BuildScript/tk868_on_10_8_10_9.patch b/Mac/BuildScript/tk868_on_10_8_10_9.patch deleted file mode 100644 index 8fe10604a68c..000000000000 --- a/Mac/BuildScript/tk868_on_10_8_10_9.patch +++ /dev/null @@ -1,18 +0,0 @@ -Fix build failure with +quartz variant on OS X 10.8 and 10.9. -Even though Gestalt was deprecated in OS X 10.8, it should work fine -through OS X 10.9, and its replacement NSOperatingSystemVersion was -not introduced until OS X 10.10. - -Patch from MacPorts project and reported upstream: -https://trac.macports.org/ticket/55649 ---- tk8.6.8/macosx/tkMacOSXXStubs.c.orig 2017-12-06 09:25:08.000000000 -0600 -+++ tk8.6.8-patched/macosx/tkMacOSXXStubs.c 2018-01-06 19:34:17.000000000 -0600 -@@ -175,7 +175,7 @@ - { - int major, minor, patch; - --#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 -+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 - Gestalt(gestaltSystemVersionMajor, (SInt32*)&major); - Gestalt(gestaltSystemVersionMinor, (SInt32*)&minor); - Gestalt(gestaltSystemVersionBugFix, (SInt32*)&patch); diff --git a/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst b/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst new file mode 100644 index 000000000000..d6b503d10527 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst @@ -0,0 +1 @@ +Update macOS installer to use Tcl/Tk 8.6.9.1. From webhook-mailer at python.org Tue Dec 11 01:29:48 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 06:29:48 -0000 Subject: [Python-checkins] bpo-35402: Update macOS installer to use Tcl 8.6.9 / Tk 8.6.9.1 (GH-11101) Message-ID: https://github.com/python/cpython/commit/37607f26697351751165a042f91f04530ce333f7 commit: 37607f26697351751165a042f91f04530ce333f7 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T22:29:45-08:00 summary: bpo-35402: Update macOS installer to use Tcl 8.6.9 / Tk 8.6.9.1 (GH-11101) (cherry picked from commit 7cf3d8e25174c8871883e42f3240fd7f01efd3a8) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst D Mac/BuildScript/tk868_on_10_8_10_9.patch M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 2ef6ddc095e5..48c4e5cf9b34 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -223,9 +223,9 @@ def library_recipes(): if internalTk(): result.extend([ dict( - name="Tcl 8.6.8", - url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz", - checksum='81656d3367af032e0ae6157eff134f89', + name="Tcl 8.6.9", + url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.9-src.tar.gz", + checksum='aa0a121d95a0e7b73a036f26028538d4', buildDir="unix", configure_pre=[ '--enable-shared', @@ -239,12 +239,9 @@ def library_recipes(): }, ), dict( - name="Tk 8.6.8", - url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz", - checksum='5e0faecba458ee1386078fb228d008ba', - patches=[ - "tk868_on_10_8_10_9.patch", - ], + name="Tk 8.6.9.1", + url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.9.1-src.tar.gz", + checksum='9efe3976468352dc894dae0c4e785a8e', buildDir="unix", configure_pre=[ '--enable-aqua', @@ -706,6 +703,7 @@ def extractArchive(builddir, archiveName): work for current Tcl and Tk source releases where the basename of the archive ends with "-src" but the uncompressed directory does not. For now, just special case Tcl and Tk tar.gz downloads. + Another special case: the tk8.6.9.1 tarball extracts to tk8.6.9. """ curdir = os.getcwd() try: @@ -715,6 +713,8 @@ def extractArchive(builddir, archiveName): if ((retval.startswith('tcl') or retval.startswith('tk')) and retval.endswith('-src')): retval = retval[:-4] + if retval == 'tk8.6.9.1': + retval = 'tk8.6.9' if os.path.exists(retval): shutil.rmtree(retval) fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r') diff --git a/Mac/BuildScript/tk868_on_10_8_10_9.patch b/Mac/BuildScript/tk868_on_10_8_10_9.patch deleted file mode 100644 index 8fe10604a68c..000000000000 --- a/Mac/BuildScript/tk868_on_10_8_10_9.patch +++ /dev/null @@ -1,18 +0,0 @@ -Fix build failure with +quartz variant on OS X 10.8 and 10.9. -Even though Gestalt was deprecated in OS X 10.8, it should work fine -through OS X 10.9, and its replacement NSOperatingSystemVersion was -not introduced until OS X 10.10. - -Patch from MacPorts project and reported upstream: -https://trac.macports.org/ticket/55649 ---- tk8.6.8/macosx/tkMacOSXXStubs.c.orig 2017-12-06 09:25:08.000000000 -0600 -+++ tk8.6.8-patched/macosx/tkMacOSXXStubs.c 2018-01-06 19:34:17.000000000 -0600 -@@ -175,7 +175,7 @@ - { - int major, minor, patch; - --#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 -+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 - Gestalt(gestaltSystemVersionMajor, (SInt32*)&major); - Gestalt(gestaltSystemVersionMinor, (SInt32*)&minor); - Gestalt(gestaltSystemVersionBugFix, (SInt32*)&patch); diff --git a/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst b/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst new file mode 100644 index 000000000000..d6b503d10527 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst @@ -0,0 +1 @@ +Update macOS installer to use Tcl/Tk 8.6.9.1. From webhook-mailer at python.org Tue Dec 11 01:38:06 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 11 Dec 2018 06:38:06 -0000 Subject: [Python-checkins] bpo-35454: Fix miscellaneous minor issues in error handling. (#11077) Message-ID: https://github.com/python/cpython/commit/8905fcc85a6fc3ac394bc89b0bbf40897e9497a6 commit: 8905fcc85a6fc3ac394bc89b0bbf40897e9497a6 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-11T08:38:03+02:00 summary: bpo-35454: Fix miscellaneous minor issues in error handling. (#11077) * bpo-35454: Fix miscellaneous minor issues in error handling. * Fix a null pointer dereference. files: M Modules/_elementtree.c M Modules/_threadmodule.c M Modules/socketmodule.c M Objects/namespaceobject.c M Python/_warnings.c M Python/ceval.c M Python/codecs.c M Python/import.c M Python/pylifecycle.c diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 62374d887c76..12e418d85ed5 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -352,7 +352,10 @@ get_attrib_from_keywords(PyObject *kwds) return NULL; } attrib = PyDict_Copy(attrib); - PyDict_DelItem(kwds, attrib_str); + if (attrib && PyDict_DelItem(kwds, attrib_str) < 0) { + Py_DECREF(attrib); + attrib = NULL; + } } else { attrib = PyDict_New(); } diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index d075ef7455db..aacce698e4a4 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -777,9 +777,11 @@ local_clear(localobject *self) for(tstate = PyInterpreterState_ThreadHead(tstate->interp); tstate; tstate = PyThreadState_Next(tstate)) - if (tstate->dict && - PyDict_GetItem(tstate->dict, self->key)) - PyDict_DelItem(tstate->dict, self->key); + if (tstate->dict && PyDict_GetItem(tstate->dict, self->key)) { + if (PyDict_DelItem(tstate->dict, self->key)) { + PyErr_Clear(); + } + } } return 0; } diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 40f1ca64a4ac..73d3e1add3ef 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -362,10 +362,14 @@ remove_unusable_flags(PyObject *m) else { if (PyDict_GetItemString( dict, - win_runtime_flags[i].flag_name) != NULL) { - PyDict_DelItemString( - dict, - win_runtime_flags[i].flag_name); + win_runtime_flags[i].flag_name) != NULL) + { + if (PyDict_DelItemString( + dict, + win_runtime_flags[i].flag_name)) + { + PyErr_Clear(); + } } } } diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c index 0b902693849d..2acf809959da 100644 --- a/Objects/namespaceobject.c +++ b/Objects/namespaceobject.c @@ -103,15 +103,15 @@ namespace_repr(PyObject *ns) PyObject *value, *item; value = PyDict_GetItem(d, key); - assert(value != NULL); - - item = PyUnicode_FromFormat("%S=%R", key, value); - if (item == NULL) { - loop_error = 1; - } - else { - loop_error = PyList_Append(pairs, item); - Py_DECREF(item); + if (value != NULL) { + item = PyUnicode_FromFormat("%S=%R", key, value); + if (item == NULL) { + loop_error = 1; + } + else { + loop_error = PyList_Append(pairs, item); + Py_DECREF(item); + } } } diff --git a/Python/_warnings.c b/Python/_warnings.c index ccbc73f54e4f..7eedd1374cba 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -255,7 +255,11 @@ already_warned(PyObject *registry, PyObject *key, int should_set) version_obj = _PyDict_GetItemId(registry, &PyId_version); if (version_obj == NULL || !PyLong_CheckExact(version_obj) - || PyLong_AsLong(version_obj) != _PyRuntime.warnings.filters_version) { + || PyLong_AsLong(version_obj) != _PyRuntime.warnings.filters_version) + { + if (PyErr_Occurred()) { + return -1; + } PyDict_Clear(registry); version_obj = PyLong_FromLong(_PyRuntime.warnings.filters_version); if (version_obj == NULL) diff --git a/Python/ceval.c b/Python/ceval.c index ebe1c50e7bad..de6ff2994550 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5109,7 +5109,7 @@ unicode_concatenate(PyObject *v, PyObject *w, PyObject *names = f->f_code->co_names; PyObject *name = GETITEM(names, oparg); PyObject *locals = f->f_locals; - if (PyDict_CheckExact(locals) && + if (locals && PyDict_CheckExact(locals) && PyDict_GetItem(locals, name) == v) { if (PyDict_DelItem(locals, name) != 0) { PyErr_Clear(); diff --git a/Python/codecs.c b/Python/codecs.c index 002fad308038..ff2142df40cc 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -129,8 +129,10 @@ PyObject *_PyCodec_Lookup(const char *encoding) /* Next, scan the search functions in order of registration */ args = PyTuple_New(1); - if (args == NULL) - goto onError; + if (args == NULL) { + Py_DECREF(v); + return NULL; + } PyTuple_SET_ITEM(args,0,v); len = PyList_Size(interp->codec_search_path); diff --git a/Python/import.c b/Python/import.c index 15637c6a1f31..c4f087761cd6 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2143,10 +2143,10 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } mod = _PyImport_FindExtensionObject(name, path); - if (mod != NULL) { + if (mod != NULL || PyErr_Occurred()) { Py_DECREF(name); Py_DECREF(path); - Py_INCREF(mod); + Py_XINCREF(mod); return mod; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 6de32decc5ae..37ecc510c8fb 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1286,6 +1286,9 @@ new_interpreter(PyThreadState **tstate_p) PyDict_SetItemString(interp->sysdict, "modules", modules); _PySys_EndInit(interp->sysdict, interp); } + else if (PyErr_Occurred()) { + goto handle_error; + } bimod = _PyImport_FindBuiltin("builtins", modules); if (bimod != NULL) { @@ -1294,6 +1297,9 @@ new_interpreter(PyThreadState **tstate_p) goto handle_error; Py_INCREF(interp->builtins); } + else if (PyErr_Occurred()) { + goto handle_error; + } /* initialize builtin exceptions */ _PyExc_Init(bimod); From webhook-mailer at python.org Tue Dec 11 01:39:37 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 06:39:37 -0000 Subject: [Python-checkins] bpo-35402: Update macOS installer to use Tcl 8.6.9 / Tk 8.6.9.1 (GH-11101) Message-ID: https://github.com/python/cpython/commit/aa580508431d231677cfaa13ac9b6aa37538b9ef commit: aa580508431d231677cfaa13ac9b6aa37538b9ef branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T22:39:34-08:00 summary: bpo-35402: Update macOS installer to use Tcl 8.6.9 / Tk 8.6.9.1 (GH-11101) (cherry picked from commit 7cf3d8e25174c8871883e42f3240fd7f01efd3a8) Co-authored-by: Ned Deily files: A Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst D Mac/BuildScript/tk868_on_10_8_10_9.patch M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 2ef6ddc095e5..48c4e5cf9b34 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -223,9 +223,9 @@ def library_recipes(): if internalTk(): result.extend([ dict( - name="Tcl 8.6.8", - url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz", - checksum='81656d3367af032e0ae6157eff134f89', + name="Tcl 8.6.9", + url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.9-src.tar.gz", + checksum='aa0a121d95a0e7b73a036f26028538d4', buildDir="unix", configure_pre=[ '--enable-shared', @@ -239,12 +239,9 @@ def library_recipes(): }, ), dict( - name="Tk 8.6.8", - url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz", - checksum='5e0faecba458ee1386078fb228d008ba', - patches=[ - "tk868_on_10_8_10_9.patch", - ], + name="Tk 8.6.9.1", + url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.9.1-src.tar.gz", + checksum='9efe3976468352dc894dae0c4e785a8e', buildDir="unix", configure_pre=[ '--enable-aqua', @@ -706,6 +703,7 @@ def extractArchive(builddir, archiveName): work for current Tcl and Tk source releases where the basename of the archive ends with "-src" but the uncompressed directory does not. For now, just special case Tcl and Tk tar.gz downloads. + Another special case: the tk8.6.9.1 tarball extracts to tk8.6.9. """ curdir = os.getcwd() try: @@ -715,6 +713,8 @@ def extractArchive(builddir, archiveName): if ((retval.startswith('tcl') or retval.startswith('tk')) and retval.endswith('-src')): retval = retval[:-4] + if retval == 'tk8.6.9.1': + retval = 'tk8.6.9' if os.path.exists(retval): shutil.rmtree(retval) fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r') diff --git a/Mac/BuildScript/tk868_on_10_8_10_9.patch b/Mac/BuildScript/tk868_on_10_8_10_9.patch deleted file mode 100644 index 8fe10604a68c..000000000000 --- a/Mac/BuildScript/tk868_on_10_8_10_9.patch +++ /dev/null @@ -1,18 +0,0 @@ -Fix build failure with +quartz variant on OS X 10.8 and 10.9. -Even though Gestalt was deprecated in OS X 10.8, it should work fine -through OS X 10.9, and its replacement NSOperatingSystemVersion was -not introduced until OS X 10.10. - -Patch from MacPorts project and reported upstream: -https://trac.macports.org/ticket/55649 ---- tk8.6.8/macosx/tkMacOSXXStubs.c.orig 2017-12-06 09:25:08.000000000 -0600 -+++ tk8.6.8-patched/macosx/tkMacOSXXStubs.c 2018-01-06 19:34:17.000000000 -0600 -@@ -175,7 +175,7 @@ - { - int major, minor, patch; - --#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 -+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 - Gestalt(gestaltSystemVersionMajor, (SInt32*)&major); - Gestalt(gestaltSystemVersionMinor, (SInt32*)&minor); - Gestalt(gestaltSystemVersionBugFix, (SInt32*)&patch); diff --git a/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst b/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst new file mode 100644 index 000000000000..d6b503d10527 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-10-02-44-48.bpo-35402.xzn8qJ.rst @@ -0,0 +1 @@ +Update macOS installer to use Tcl/Tk 8.6.9.1. From webhook-mailer at python.org Tue Dec 11 02:05:17 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 07:05:17 -0000 Subject: [Python-checkins] bpo-35454: Fix miscellaneous minor issues in error handling. (GH-11077) Message-ID: https://github.com/python/cpython/commit/62674f3a36ec55f86a5f20ee028a37fbd549bd6c commit: 62674f3a36ec55f86a5f20ee028a37fbd549bd6c branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-10T23:05:13-08:00 summary: bpo-35454: Fix miscellaneous minor issues in error handling. (GH-11077) * bpo-35454: Fix miscellaneous minor issues in error handling. * Fix a null pointer dereference. (cherry picked from commit 8905fcc85a6fc3ac394bc89b0bbf40897e9497a6) Co-authored-by: Serhiy Storchaka files: M Modules/_elementtree.c M Modules/_threadmodule.c M Modules/socketmodule.c M Objects/namespaceobject.c M Python/_warnings.c M Python/ceval.c M Python/codecs.c M Python/import.c M Python/pylifecycle.c diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 3118b55c874b..ff3a240f2ec3 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -352,7 +352,10 @@ get_attrib_from_keywords(PyObject *kwds) return NULL; } attrib = PyDict_Copy(attrib); - PyDict_DelItem(kwds, attrib_str); + if (attrib && PyDict_DelItem(kwds, attrib_str) < 0) { + Py_DECREF(attrib); + attrib = NULL; + } } else { attrib = PyDict_New(); } diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 16d3191c621a..2a6b8931d70c 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -777,9 +777,11 @@ local_clear(localobject *self) for(tstate = PyInterpreterState_ThreadHead(tstate->interp); tstate; tstate = PyThreadState_Next(tstate)) - if (tstate->dict && - PyDict_GetItem(tstate->dict, self->key)) - PyDict_DelItem(tstate->dict, self->key); + if (tstate->dict && PyDict_GetItem(tstate->dict, self->key)) { + if (PyDict_DelItem(tstate->dict, self->key)) { + PyErr_Clear(); + } + } } return 0; } diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 53bf99687626..e6d3f8be6ede 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -360,10 +360,14 @@ remove_unusable_flags(PyObject *m) else { if (PyDict_GetItemString( dict, - win_runtime_flags[i].flag_name) != NULL) { - PyDict_DelItemString( - dict, - win_runtime_flags[i].flag_name); + win_runtime_flags[i].flag_name) != NULL) + { + if (PyDict_DelItemString( + dict, + win_runtime_flags[i].flag_name)) + { + PyErr_Clear(); + } } } } diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c index 7de102eb06a0..c2f9d30d2998 100644 --- a/Objects/namespaceobject.c +++ b/Objects/namespaceobject.c @@ -103,15 +103,15 @@ namespace_repr(PyObject *ns) PyObject *value, *item; value = PyDict_GetItem(d, key); - assert(value != NULL); - - item = PyUnicode_FromFormat("%S=%R", key, value); - if (item == NULL) { - loop_error = 1; - } - else { - loop_error = PyList_Append(pairs, item); - Py_DECREF(item); + if (value != NULL) { + item = PyUnicode_FromFormat("%S=%R", key, value); + if (item == NULL) { + loop_error = 1; + } + else { + loop_error = PyList_Append(pairs, item); + Py_DECREF(item); + } } } diff --git a/Python/_warnings.c b/Python/_warnings.c index 29e475d67d1f..80dca65fd353 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -256,7 +256,11 @@ already_warned(PyObject *registry, PyObject *key, int should_set) version_obj = _PyDict_GetItemId(registry, &PyId_version); if (version_obj == NULL || !PyLong_CheckExact(version_obj) - || PyLong_AsLong(version_obj) != _PyRuntime.warnings.filters_version) { + || PyLong_AsLong(version_obj) != _PyRuntime.warnings.filters_version) + { + if (PyErr_Occurred()) { + return -1; + } PyDict_Clear(registry); version_obj = PyLong_FromLong(_PyRuntime.warnings.filters_version); if (version_obj == NULL) diff --git a/Python/ceval.c b/Python/ceval.c index e371d345cc74..00b2e6d85f48 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5027,7 +5027,7 @@ unicode_concatenate(PyObject *v, PyObject *w, PyObject *names = f->f_code->co_names; PyObject *name = GETITEM(names, oparg); PyObject *locals = f->f_locals; - if (PyDict_CheckExact(locals) && + if (locals && PyDict_CheckExact(locals) && PyDict_GetItem(locals, name) == v) { if (PyDict_DelItem(locals, name) != 0) { PyErr_Clear(); diff --git a/Python/codecs.c b/Python/codecs.c index eb3cd35fb8e2..c315ae3ed7ee 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -130,8 +130,10 @@ PyObject *_PyCodec_Lookup(const char *encoding) /* Next, scan the search functions in order of registration */ args = PyTuple_New(1); - if (args == NULL) - goto onError; + if (args == NULL) { + Py_DECREF(v); + return NULL; + } PyTuple_SET_ITEM(args,0,v); len = PyList_Size(interp->codec_search_path); diff --git a/Python/import.c b/Python/import.c index 709fc4324e6f..5f5d135c7a2e 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2138,10 +2138,10 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } mod = _PyImport_FindExtensionObject(name, path); - if (mod != NULL) { + if (mod != NULL || PyErr_Occurred()) { Py_DECREF(name); Py_DECREF(path); - Py_INCREF(mod); + Py_XINCREF(mod); return mod; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 4b08c9c2b2ce..94b6d43c0e51 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1417,6 +1417,9 @@ new_interpreter(PyThreadState **tstate_p) PyDict_SetItemString(interp->sysdict, "modules", modules); _PySys_EndInit(interp->sysdict, &interp->config); } + else if (PyErr_Occurred()) { + goto handle_error; + } bimod = _PyImport_FindBuiltin("builtins", modules); if (bimod != NULL) { @@ -1425,6 +1428,9 @@ new_interpreter(PyThreadState **tstate_p) goto handle_error; Py_INCREF(interp->builtins); } + else if (PyErr_Occurred()) { + goto handle_error; + } /* initialize builtin exceptions */ _PyExc_Init(bimod); From webhook-mailer at python.org Tue Dec 11 02:27:56 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 11 Dec 2018 07:27:56 -0000 Subject: [Python-checkins] [3.6] bpo-35454: Fix miscellaneous minor issues in error handling. (GH-11077) (GH-11106) Message-ID: https://github.com/python/cpython/commit/8855d9339858683c9b4fcd50b02a7bca526d4726 commit: 8855d9339858683c9b4fcd50b02a7bca526d4726 branch: 3.6 author: Serhiy Storchaka committer: GitHub date: 2018-12-11T09:27:50+02:00 summary: [3.6] bpo-35454: Fix miscellaneous minor issues in error handling. (GH-11077) (GH-11106) (cherry picked from commit 8905fcc85a6fc3ac394bc89b0bbf40897e9497a6) files: M Modules/_elementtree.c M Modules/_threadmodule.c M Modules/socketmodule.c M Objects/namespaceobject.c M Python/_warnings.c M Python/ceval.c M Python/codecs.c M Python/import.c M Python/pylifecycle.c diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 5c6959662316..30e382dd6c5e 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -352,7 +352,10 @@ get_attrib_from_keywords(PyObject *kwds) return NULL; } attrib = PyDict_Copy(attrib); - PyDict_DelItem(kwds, attrib_str); + if (attrib && PyDict_DelItem(kwds, attrib_str) < 0) { + Py_DECREF(attrib); + attrib = NULL; + } } else { attrib = PyDict_New(); } diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index c504b57b064f..a13b2e07400c 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -785,9 +785,11 @@ local_clear(localobject *self) for(tstate = PyInterpreterState_ThreadHead(tstate->interp); tstate; tstate = PyThreadState_Next(tstate)) - if (tstate->dict && - PyDict_GetItem(tstate->dict, self->key)) - PyDict_DelItem(tstate->dict, self->key); + if (tstate->dict && PyDict_GetItem(tstate->dict, self->key)) { + if (PyDict_DelItem(tstate->dict, self->key)) { + PyErr_Clear(); + } + } } return 0; } diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 0daf98b6d238..520867458c1b 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -367,10 +367,14 @@ remove_unusable_flags(PyObject *m) else { if (PyDict_GetItemString( dict, - win_runtime_flags[i].flag_name) != NULL) { - PyDict_DelItemString( - dict, - win_runtime_flags[i].flag_name); + win_runtime_flags[i].flag_name) != NULL) + { + if (PyDict_DelItemString( + dict, + win_runtime_flags[i].flag_name)) + { + PyErr_Clear(); + } } } } diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c index 6307ee0423dc..a810effeb3d3 100644 --- a/Objects/namespaceobject.c +++ b/Objects/namespaceobject.c @@ -109,15 +109,15 @@ namespace_repr(PyObject *ns) PyObject *value, *item; value = PyDict_GetItem(d, key); - assert(value != NULL); - - item = PyUnicode_FromFormat("%S=%R", key, value); - if (item == NULL) { - loop_error = 1; - } - else { - loop_error = PyList_Append(pairs, item); - Py_DECREF(item); + if (value != NULL) { + item = PyUnicode_FromFormat("%S=%R", key, value); + if (item == NULL) { + loop_error = 1; + } + else { + loop_error = PyList_Append(pairs, item); + Py_DECREF(item); + } } } diff --git a/Python/_warnings.c b/Python/_warnings.c index e96e154b0d54..3ae68bb2613b 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -248,7 +248,11 @@ already_warned(PyObject *registry, PyObject *key, int should_set) version_obj = _PyDict_GetItemId(registry, &PyId_version); if (version_obj == NULL || !PyLong_CheckExact(version_obj) - || PyLong_AsLong(version_obj) != _filters_version) { + || PyLong_AsLong(version_obj) != _filters_version) + { + if (PyErr_Occurred()) { + return -1; + } PyDict_Clear(registry); version_obj = PyLong_FromLong(_filters_version); if (version_obj == NULL) diff --git a/Python/ceval.c b/Python/ceval.c index 38d1d73845fb..36e966470dba 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5471,7 +5471,7 @@ unicode_concatenate(PyObject *v, PyObject *w, PyObject *names = f->f_code->co_names; PyObject *name = GETITEM(names, oparg); PyObject *locals = f->f_locals; - if (PyDict_CheckExact(locals) && + if (locals && PyDict_CheckExact(locals) && PyDict_GetItem(locals, name) == v) { if (PyDict_DelItem(locals, name) != 0) { PyErr_Clear(); diff --git a/Python/codecs.c b/Python/codecs.c index 4ff83014d8bc..584c1ef430de 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -129,8 +129,10 @@ PyObject *_PyCodec_Lookup(const char *encoding) /* Next, scan the search functions in order of registration */ args = PyTuple_New(1); - if (args == NULL) - goto onError; + if (args == NULL) { + Py_DECREF(v); + return NULL; + } PyTuple_SET_ITEM(args,0,v); len = PyList_Size(interp->codec_search_path); diff --git a/Python/import.c b/Python/import.c index db1650a9ddd2..cb1927045582 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1973,10 +1973,10 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } mod = _PyImport_FindExtensionObject(name, path); - if (mod != NULL) { + if (mod != NULL || PyErr_Occurred()) { Py_DECREF(name); Py_DECREF(path); - Py_INCREF(mod); + Py_XINCREF(mod); return mod; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 56f04afc7701..0ebf9c702928 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -797,6 +797,9 @@ Py_NewInterpreter(void) goto handle_error; Py_INCREF(interp->builtins); } + else if (PyErr_Occurred()) { + goto handle_error; + } /* initialize builtin exceptions */ _PyExc_Init(bimod); From webhook-mailer at python.org Tue Dec 11 03:51:33 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 11 Dec 2018 08:51:33 -0000 Subject: [Python-checkins] bpo-35444: Fix error handling when fail to look up builtin "getattr". (GH-11047) (GH-11107) Message-ID: https://github.com/python/cpython/commit/3cae16d2e98ffaa89ddd311df70a857dfaff4020 commit: 3cae16d2e98ffaa89ddd311df70a857dfaff4020 branch: 3.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-11T10:51:27+02:00 summary: bpo-35444: Fix error handling when fail to look up builtin "getattr". (GH-11047) (GH-11107) (cherry picked from commit bb86bf4c4eaa30b1f5192dab9f389ce0bb61114d) files: A Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst M Include/ceval.h M Modules/_pickle.c M Objects/classobject.c M Objects/descrobject.c M Objects/methodobject.c M Python/ceval.c diff --git a/Include/ceval.h b/Include/ceval.h index bce8a0beed85..11283c0a570b 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -48,10 +48,12 @@ PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void); PyAPI_FUNC(PyObject *) PyEval_GetLocals(void); PyAPI_FUNC(struct _frame *) PyEval_GetFrame(void); +#ifndef Py_LIMITED_API +/* Helper to look up a builtin object */ +PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *); /* Look at the current frame's (if any) code's co_flags, and turn on the corresponding compiler flags in cf->cf_flags. Return 1 if any flag was set, else return 0. */ -#ifndef Py_LIMITED_API PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf); #endif diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst new file mode 100644 index 000000000000..22c3969f1276 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst @@ -0,0 +1,2 @@ +Fixed error handling in pickling methods when fail to look up builtin +"getattr". diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 57bb771addde..60ef921521ef 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -207,19 +207,15 @@ _Pickle_ClearState(PickleState *st) static int _Pickle_InitState(PickleState *st) { - PyObject *builtins; PyObject *copyreg = NULL; PyObject *compat_pickle = NULL; PyObject *codecs = NULL; PyObject *functools = NULL; + _Py_IDENTIFIER(getattr); - builtins = PyEval_GetBuiltins(); - if (builtins == NULL) - goto error; - st->getattr = PyDict_GetItemString(builtins, "getattr"); + st->getattr = _PyEval_GetBuiltinId(&PyId_getattr); if (st->getattr == NULL) goto error; - Py_INCREF(st->getattr); copyreg = PyImport_ImportModule("copyreg"); if (!copyreg) diff --git a/Objects/classobject.c b/Objects/classobject.c index 3dc23b796c2c..f45d6ae809bb 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -77,8 +77,6 @@ method_reduce(PyMethodObject *im) { PyObject *self = PyMethod_GET_SELF(im); PyObject *func = PyMethod_GET_FUNCTION(im); - PyObject *builtins; - PyObject *getattr; PyObject *funcname; _Py_IDENTIFIER(getattr); @@ -86,9 +84,8 @@ method_reduce(PyMethodObject *im) if (funcname == NULL) { return NULL; } - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(ON)", getattr, self, funcname); + return Py_BuildValue("N(ON)", _PyEval_GetBuiltinId(&PyId_getattr), + self, funcname); } static PyMethodDef method_methods[] = { diff --git a/Objects/descrobject.c b/Objects/descrobject.c index f19d07aa2568..277fed995615 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -450,14 +450,9 @@ descr_get_qualname(PyDescrObject *descr, void *Py_UNUSED(ignored)) static PyObject * descr_reduce(PyDescrObject *descr) { - PyObject *builtins; - PyObject *getattr; _Py_IDENTIFIER(getattr); - - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(OO)", getattr, PyDescr_TYPE(descr), - PyDescr_NAME(descr)); + return Py_BuildValue("N(OO)", _PyEval_GetBuiltinId(&PyId_getattr), + PyDescr_TYPE(descr), PyDescr_NAME(descr)); } static PyMethodDef descr_methods[] = { @@ -1088,13 +1083,9 @@ wrapper_repr(wrapperobject *wp) static PyObject * wrapper_reduce(wrapperobject *wp) { - PyObject *builtins; - PyObject *getattr; _Py_IDENTIFIER(getattr); - - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(OO)", getattr, wp->self, PyDescr_NAME(wp->descr)); + return Py_BuildValue("N(OO)", _PyEval_GetBuiltinId(&PyId_getattr), + wp->self, PyDescr_NAME(wp->descr)); } static PyMethodDef wrapper_methods[] = { diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 2cf314660d00..f9bac1922dc7 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -103,16 +103,13 @@ meth_dealloc(PyCFunctionObject *m) static PyObject * meth_reduce(PyCFunctionObject *m) { - PyObject *builtins; - PyObject *getattr; _Py_IDENTIFIER(getattr); if (m->m_self == NULL || PyModule_Check(m->m_self)) return PyUnicode_FromString(m->m_ml->ml_name); - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(Os)", getattr, m->m_self, m->m_ml->ml_name); + return Py_BuildValue("N(Os)", _PyEval_GetBuiltinId(&PyId_getattr), + m->m_self, m->m_ml->ml_name); } static PyMethodDef meth_methods[] = { diff --git a/Python/ceval.c b/Python/ceval.c index 00b2e6d85f48..c394b9b4827b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4419,6 +4419,20 @@ PyEval_GetBuiltins(void) return current_frame->f_builtins; } +/* Convenience function to get a builtin from its name */ +PyObject * +_PyEval_GetBuiltinId(_Py_Identifier *name) +{ + PyObject *attr = _PyDict_GetItemIdWithError(PyEval_GetBuiltins(), name); + if (attr) { + Py_INCREF(attr); + } + else if (!PyErr_Occurred()) { + PyErr_SetObject(PyExc_AttributeError, _PyUnicode_FromId(name)); + } + return attr; +} + PyObject * PyEval_GetLocals(void) { From solipsis at pitrou.net Tue Dec 11 04:07:50 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 11 Dec 2018 09:07:50 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=4 Message-ID: <20181211090750.1.34BD97F43C0342AB@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [7, -7, 1] memory blocks, sum=1 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_fork leaked [-2, 0, 1] memory blocks, sum=-1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogkxOTCt', '--timeout', '7200'] From webhook-mailer at python.org Tue Dec 11 04:28:36 2018 From: webhook-mailer at python.org (Ned Deily) Date: Tue, 11 Dec 2018 09:28:36 -0000 Subject: [Python-checkins] [3.6] bpo-15663: the 10.6+ macOS installers for 3.6/2.7 now provide a private Tcl/Tk 8.6 (GH-11109) Message-ID: https://github.com/python/cpython/commit/f74cabd9203cf3be97fdb3821a7fa0b74d7b2263 commit: f74cabd9203cf3be97fdb3821a7fa0b74d7b2263 branch: 3.6 author: Ned Deily committer: GitHub date: 2018-12-11T04:28:31-05:00 summary: [3.6] bpo-15663: the 10.6+ macOS installers for 3.6/2.7 now provide a private Tcl/Tk 8.6 (GH-11109) files: A Misc/NEWS.d/next/macOS/2018-12-11-02-50-35.bpo-15663.6tnyd2.rst M Mac/BuildScript/build-installer.py M Mac/BuildScript/resources/ReadMe.rtf M Mac/BuildScript/resources/Welcome.rtf diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 48c4e5cf9b34..b1dede492058 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -2,6 +2,8 @@ """ This script is used to build "official" universal installers on macOS. +NEW for 3.6.8 / 2.7.16: +- also build and use Tk 8.6 for 10.6+ installers NEW for 3.6.5: - support Intel 64-bit-only () and 32-bit-only installer builds - build and link with private Tcl/Tk 8.6 for 10.9+ builds @@ -20,8 +22,8 @@ so will fetch them from PyPI if necessary. Since python3 is now used for Sphinx, build-installer.py should also be converted to use python3! -For 10.9 or greater deployment targets, build-installer builds and links -with its own copy of Tcl/Tk 8.5 and the rest of this paragraph does not +For 10.6 or greater deployment targets, build-installer builds and links +with its own copy of Tcl/Tk 8.6 and the rest of this paragraph does not apply. Otherwise, build-installer requires an installed third-party version of Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets) or Tcl/TK 8.5 (for 10.6 or later) installed in /Library/Frameworks. When installed, @@ -188,9 +190,9 @@ def getTargetCompilers(): EXPECTED_SHARED_LIBS = {} # Are we building and linking with our own copy of Tcl/TK? -# For now, do so if deployment target is 10.9+. +# For now, do so if deployment target is 10.6+. def internalTk(): - return getDeptargetTuple() >= (10, 9) + return getDeptargetTuple() >= (10, 6) # List of names of third party software built with this installer. # The names will be inserted into the rtf version of the License. diff --git a/Mac/BuildScript/resources/ReadMe.rtf b/Mac/BuildScript/resources/ReadMe.rtf index e8deea1b54fe..35a17eda210a 100644 --- a/Mac/BuildScript/resources/ReadMe.rtf +++ b/Mac/BuildScript/resources/ReadMe.rtf @@ -1,5 +1,6 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf400 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fmodern\fcharset0 CourierNewPSMT;} +{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf100 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;\f2\fswiss\fcharset0 Helvetica-Oblique; +\f3\fmodern\fcharset0 CourierNewPSMT;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} \margl1440\margr1440\vieww13380\viewh14600\viewkind0 @@ -9,104 +10,98 @@ \ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 -\b \cf0 \ul \ulc0 Which installer variant should I use? [CHANGED in 3.6.6] -\b0 \ulnone \ +\f1\b \cf0 \ul \ulc0 Which installer variant should I use? [CHANGED in 3.6.6] +\f0\b0 \ulnone \ \ With Python 3.6.5, the python.org website now provides two installer variants for download: one that installs a -\i 64-bit-only -\i0 Python capable of running on -\i macOS 10.9 (Mavericks) -\i0 or later; and one that installs a -\i 64-bit/32-bit Intel -\i0 Python capable of running on -\i macOS 10.6 (Snow Leopard) -\i0 or later. (This ReadMe was installed with the -\i $MACOSX_DEPLOYMENT_TARGET -\i0 variant.) Previous Python 3.6.x releases only provided the 10.6 or later installer. If you are running on macOS 10.9 or later and if you have no need for compatibility with older systems, use the 10.9 variant. Use the 10.6 variant if you are running on macOS 10.6 through 10.8, if you need to maintain compatibility with previous 3.6.x releases, or if you want to produce standalone applications that can run on systems from 10.6. The Pythons installed by these installers are built with private copies of some third-party libraries not included with or newer than those in macOS itself. The list of these libraries varies by installer variant and is included at the end of the License.rtf file.\ +\f2\i 64-bit-only +\f0\i0 Python capable of running on +\f2\i macOS 10.9 (Mavericks) +\f0\i0 or later; and one that installs a +\f2\i 64-bit/32-bit Intel +\f0\i0 Python capable of running on +\f2\i macOS 10.6 (Snow Leopard) +\f0\i0 or later. (This ReadMe was installed with the +\f2\i $MACOSX_DEPLOYMENT_TARGET +\f0\i0 variant.) Previous Python 3.6.x releases only provided the 10.6 or later installer. If you are running on macOS 10.9 or later and if you have no need for compatibility with older systems, use the 10.9 variant. Use the 10.6 variant if you are running on macOS 10.6 through 10.8, if you need to maintain compatibility with previous 3.6.x releases, or if you want to produce standalone applications that can run on systems from 10.6. The Pythons installed by these installers are built with private copies of some third-party libraries not included with or newer than those in macOS itself. The list of these libraries varies by installer variant and is included at the end of the License.rtf file.\ \ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\partightenfactor0 -\b \cf0 CHANGED in 3.6.6: -\b0 the 10.9+ 64-bit-only installer variant is now the default download. The 10.6+ variant is available from the $FULL_VERSION release page.\ +\f1\b \cf0 CHANGED in 3.6.6: +\f0\b0 the 10.9+ 64-bit-only installer variant is now the default download. The 10.6+ variant is available from the $FULL_VERSION release page.\ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 -\b \cf0 \ul \ +\f1\b \cf0 \ul \ Certificate verification and OpenSSL\ -\b0 \ulnone \ +\f0\b0 \ulnone \ This variant of Python 3.6 now includes its own private copy of OpenSSL 1.0.2. Unlike previous releases, the deprecated Apple-supplied OpenSSL libraries are no longer used. This also means that the trust certificates in system and user keychains managed by the -\i Keychain Access -\i0 application and the -\i security -\i0 command line utility are no longer used as defaults by the Python -\f1 ssl +\f2\i Keychain Access +\f0\i0 application and the +\f2\i security +\f0\i0 command line utility are no longer used as defaults by the Python +\f3 ssl \f0 module. A sample command script is included in -\f1 /Applications/Python 3.6 +\f3 /Applications/Python 3.6 \f0 to install a curated bundle of default root certificates from the third-party -\f1 certifi +\f3 certifi \f0 package ({\field{\*\fldinst{HYPERLINK "https://pypi.org/project/certifi/"}}{\fldrslt https://pypi.org/project/certifi/}}). If you choose to use -\f1 certifi +\f3 certifi \f0 , you should consider subscribing to the{\field{\*\fldinst{HYPERLINK "https://certifi.io/en/latest/"}}{\fldrslt project's email update service}} to be notified when the certificate bundle is updated.\ \ The bundled -\f1 pip +\f3 pip \f0 included with the Python 3.6 installer has its own default certificate store for verifying download connections.\ \ -\b \ul Using IDLE or other Tk applications [NEW/CHANGED in 3.6.5] -\b0 \ulnone \ +\f1\b \ul Using IDLE or other Tk applications [NEW/CHANGED in 3.6.5] +\f0\b0 \ulnone \ \ -The 10.9+ installer variant comes with its own private version of Tcl/Tk 8.6. It does not use system-supplied or third-party supplied versions of Tcl/Tk.\ -\ -For the 10.6+ variant in 3.6.6, you continue to need to install a newer third-party version of the -\i Tcl/Tk -\i0 8.5 (not 8.6) frameworks to use IDLE or other programs that use the Tkinter graphical user interface toolkit. Visit {\field{\*\fldinst{HYPERLINK "https://www.python.org/download/mac/tcltk/"}}{\fldrslt https://www.python.org/download/mac/tcltk/}} for current information about supported and recommended versions of -\i Tcl/Tk -\i0 for this version of Python and of macOS.\ +As of 3.6.5, the 10.9+ installer variant comes with its own private version of Tcl/Tk 8.6. It does not use system-supplied or third-party supplied versions of Tcl/Tk.\ \ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 -\b \cf0 NOTE: -\b0 As of the next 3.6.x release, 3.6.7, the 10.6+ variant will also include Tcl/Tk 8.6.\ +\f1\b \cf0 CHANGED in 3.6.8: +\f0\b0 The 10.6+ variant now also uses a private version of Tcl/Tk 8.6.\ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 -\b \cf0 \ul \ +\f1\b \cf0 \ul \ Other changes\ -\b0 \ulnone \ +\f0\b0 \ulnone \ For other changes in this release, see the -\i What's new -\i0 section in the {\field{\*\fldinst{HYPERLINK "https://www.python.org/doc/"}}{\fldrslt Documentation Set}} for this release and its -\i Release Notes -\i0 link at {\field{\*\fldinst{HYPERLINK "https://www.python.org/downloads/"}}{\fldrslt https://www.python.org/downloads/}}.\ +\f2\i What's new +\f0\i0 section in the {\field{\*\fldinst{HYPERLINK "https://www.python.org/doc/"}}{\fldrslt Documentation Set}} for this release and its +\f2\i Release Notes +\f0\i0 link at {\field{\*\fldinst{HYPERLINK "https://www.python.org/downloads/"}}{\fldrslt https://www.python.org/downloads/}}.\ -\b \ul \ +\f1\b \ul \ Python 3 and Python 2 Co-existence\ -\b0 \ulnone \ +\f0\b0 \ulnone \ Python.org Python $VERSION and 2.7.x versions can both be installed on your system and will not conflict. Command names for Python 3 contain a 3 in them, -\f1 python3 +\f3 python3 \f0 (or -\f1 python$VERSION +\f3 python$VERSION \f0 ), -\f1 idle3 +\f3 idle3 \f0 (or i -\f1 dle$VERSION +\f3 dle$VERSION \f0 ), -\f1 pip3 +\f3 pip3 \f0 (or -\f1 pip$VERSION +\f3 pip$VERSION \f0 ), etc. Python 2.7 command names contain a 2 or no digit: -\f1 python2 +\f3 python2 \f0 (or -\f1 python2.7 +\f3 python2.7 \f0 or -\f1 python +\f3 python \f0 ), -\f1 idle2 +\f3 idle2 \f0 (or -\f1 idle2.7 +\f3 idle2.7 \f0 or -\f1 idle +\f3 idle \f0 ), etc.\ } \ No newline at end of file diff --git a/Mac/BuildScript/resources/Welcome.rtf b/Mac/BuildScript/resources/Welcome.rtf index 22a794a5a573..f55172ec606a 100644 --- a/Mac/BuildScript/resources/Welcome.rtf +++ b/Mac/BuildScript/resources/Welcome.rtf @@ -1,27 +1,32 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf400 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf100 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} \paperw11905\paperh16837\margl1440\margr1440\vieww12200\viewh10880\viewkind0 \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 \f0\fs24 \cf0 This package will install -\b Python $FULL_VERSION -\b0 for -\b macOS $MACOSX_DEPLOYMENT_TARGET -\b0 .\ +\f1\b Python $FULL_VERSION +\f0\b0 for +\f1\b macOS $MACOSX_DEPLOYMENT_TARGET +\f0\b0 .\ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\partightenfactor0 \cf0 \ -\b Python for macOS -\b0 consists of the Python programming language interpreter, plus a set of programs to allow easy access to it for macOS users including an integrated development environment -\b IDLE -\b0 .\ +\f1\b Python for macOS +\f0\b0 consists of the Python programming language interpreter, plus a set of programs to allow easy access to it for macOS users including an integrated development environment +\f1\b IDLE +\f0\b0 .\ \ -\b NEW in 3.6.5: -\b0 two installer variants (10.9+ 64-bit-only, 10.6+ 64-/32-bit), built-in Tcl/Tk 8.6 support in the 10.9+ variant (no additional third-party downloads!)\ +\f1\b NEW in 3.6.5: +\f0\b0 two installer variants (10.9+ 64-bit-only, 10.6+ 64-/32-bit), built-in Tcl/Tk 8.6 support in the 10.9+ variant (no additional third-party downloads!)\ \ -\b CHANGED in 3.6.6: -\b0 the 10.9+ 64-bit-only installer variant is now the default download} \ No newline at end of file +\f1\b CHANGED in 3.6.6: +\f0\b0 the 10.9+ 64-bit-only installer variant is now the default download\ +\ + +\f1\b CHANGED in 3.6.8: +\f0\b0 the 10.6+ variant now also uses a built-in Tcl/Tk 8.6\ +} \ No newline at end of file diff --git a/Misc/NEWS.d/next/macOS/2018-12-11-02-50-35.bpo-15663.6tnyd2.rst b/Misc/NEWS.d/next/macOS/2018-12-11-02-50-35.bpo-15663.6tnyd2.rst new file mode 100644 index 000000000000..1aa7f11a05ef --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-11-02-50-35.bpo-15663.6tnyd2.rst @@ -0,0 +1,2 @@ +The macOS 10.6+ installer now provides a private copy of Tcl/Tk 8.6, like +the 10.9+ installer does. From webhook-mailer at python.org Tue Dec 11 04:34:02 2018 From: webhook-mailer at python.org (Ned Deily) Date: Tue, 11 Dec 2018 09:34:02 -0000 Subject: [Python-checkins] [2.7] bpo-15663: the 10.6+ macOS installers for 3.6/2.7 now provide a private Tcl/Tk 8.6 (GH-11110) Message-ID: https://github.com/python/cpython/commit/d0d09b511d7a438fb18a9a4703480763119b4eac commit: d0d09b511d7a438fb18a9a4703480763119b4eac branch: 2.7 author: Ned Deily committer: GitHub date: 2018-12-11T04:33:56-05:00 summary: [2.7] bpo-15663: the 10.6+ macOS installers for 3.6/2.7 now provide a private Tcl/Tk 8.6 (GH-11110) files: A Misc/NEWS.d/next/macOS/2018-12-11-02-50-35.bpo-15663.6tnyd2.rst M Mac/BuildScript/build-installer.py M Mac/BuildScript/resources/ReadMe.rtf M Mac/BuildScript/resources/Welcome.rtf diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 48c4e5cf9b34..b1dede492058 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -2,6 +2,8 @@ """ This script is used to build "official" universal installers on macOS. +NEW for 3.6.8 / 2.7.16: +- also build and use Tk 8.6 for 10.6+ installers NEW for 3.6.5: - support Intel 64-bit-only () and 32-bit-only installer builds - build and link with private Tcl/Tk 8.6 for 10.9+ builds @@ -20,8 +22,8 @@ so will fetch them from PyPI if necessary. Since python3 is now used for Sphinx, build-installer.py should also be converted to use python3! -For 10.9 or greater deployment targets, build-installer builds and links -with its own copy of Tcl/Tk 8.5 and the rest of this paragraph does not +For 10.6 or greater deployment targets, build-installer builds and links +with its own copy of Tcl/Tk 8.6 and the rest of this paragraph does not apply. Otherwise, build-installer requires an installed third-party version of Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets) or Tcl/TK 8.5 (for 10.6 or later) installed in /Library/Frameworks. When installed, @@ -188,9 +190,9 @@ def getTargetCompilers(): EXPECTED_SHARED_LIBS = {} # Are we building and linking with our own copy of Tcl/TK? -# For now, do so if deployment target is 10.9+. +# For now, do so if deployment target is 10.6+. def internalTk(): - return getDeptargetTuple() >= (10, 9) + return getDeptargetTuple() >= (10, 6) # List of names of third party software built with this installer. # The names will be inserted into the rtf version of the License. diff --git a/Mac/BuildScript/resources/ReadMe.rtf b/Mac/BuildScript/resources/ReadMe.rtf index ebabb242241f..8133c35b2169 100644 --- a/Mac/BuildScript/resources/ReadMe.rtf +++ b/Mac/BuildScript/resources/ReadMe.rtf @@ -1,5 +1,6 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf400 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fmodern\fcharset0 CourierNewPSMT;} +{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf100 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;\f2\fswiss\fcharset0 Helvetica-Oblique; +\f3\fmodern\fcharset0 CourierNewPSMT;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} \margl1440\margr1440\vieww15240\viewh15540\viewkind0 @@ -9,145 +10,143 @@ \ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 -\b \cf0 \ul \ulc0 Which installer variant should I use? [CHANGED in 2.7.15] -\b0 \ulnone \ -\ - -\b **NEW** -\b0 With Python 2.7.15, the python.org website now provides two installer variants for download: one that installs a -\i 64-bit-only -\i0 Python capable of running on -\i macOS 10.9 (Mavericks) -\i0 or later; and one that installs a -\i 64-bit/32-bit Intel -\i0 Python capable of running on -\i macOS 10.6 (Snow Leopard) -\i0 or later. (This ReadMe was installed with the -\i $MACOSX_DEPLOYMENT_TARGET -\i0 variant.) Previous Python 2.7.x releases provided the 10.6 or later installer and a 10.5 or later 32-bit-only variant. If you are running on macOS 10.9 or later and if you have no need for compatibility with older systems, use the 10.9 variant. Use the 10.6 variant if you are running on macOS 10.6 through 10.8, if you need to maintain compatibility with previous 2.7.x releases, or if you want to produce standalone applications that can run on systems from 10.6. The Pythons installed by these installers are built with private copies of some third-party libraries not included with or newer than those in macOS itself. The list of these libraries varies by installer variant and is included at the end of the -\f1 License.rtf +\f1\b \cf0 \ul \ulc0 Which installer variant should I use? [CHANGED in 2.7.15] +\f0\b0 \ulnone \ +\ +With Python 2.7.15, the python.org website now provides two installer variants for download: one that installs a +\f2\i 64-bit-only +\f0\i0 Python capable of running on +\f2\i macOS 10.9 (Mavericks) +\f0\i0 or later; and one that installs a +\f2\i 64-bit/32-bit Intel +\f0\i0 Python capable of running on +\f2\i macOS 10.6 (Snow Leopard) +\f0\i0 or later. (This ReadMe was installed with the +\f2\i $MACOSX_DEPLOYMENT_TARGET +\f0\i0 variant.) Previous Python 2.7.x releases provided the 10.6 or later installer and a 10.5 or later 32-bit-only variant. If you are running on macOS 10.9 or later and if you have no need for compatibility with older systems, use the 10.9 variant. Use the 10.6 variant if you are running on macOS 10.6 through 10.8, if you need to maintain compatibility with previous 2.7.x releases, or if you want to produce standalone applications that can run on systems from 10.6. The Pythons installed by these installers are built with private copies of some third-party libraries not included with or newer than those in macOS itself. The list of these libraries varies by installer variant and is included at the end of the +\f3 License.rtf \f0 file.\ -\b \ul \ +\f1\b \ul \ Certificate verification and OpenSSL_[CHANGED in 2.7.15]\ -\b0 \ulnone \ +\f0\b0 \ulnone \ This variant of Python 2.7 now includes its own private copy of OpenSSL 1.0.2. Unlike previous releases, the deprecated Apple-supplied OpenSSL libraries are no longer used. This also means that the trust certificates in system and user keychains managed by the -\i Keychain Access -\i0 application and the -\i security -\i0 command line utility are no longer used as defaults by the Python -\f1 ssl +\f2\i Keychain Access +\f0\i0 application and the +\f2\i security +\f0\i0 command line utility are no longer used as defaults by the Python +\f3 ssl \f0 module. A sample command script is included in -\f1 /Applications/Python 2.7 +\f3 /Applications/Python 2.7 \f0 to install a curated bundle of default root certificates from the third-party -\f1 certifi +\f3 certifi \f0 package ({\field{\*\fldinst{HYPERLINK "https://pypi.python.org/pypi/certifi"}}{\fldrslt https://pypi.python.org/pypi/certifi}}). Click on -\f1 Install Certificates +\f3 Install Certificates \f0 to run it. If you choose to use -\f1 certifi +\f3 certifi \f0 , you should consider subscribing to the{\field{\*\fldinst{HYPERLINK "https://certifi.io/en/latest/"}}{\fldrslt project's email update service}} to be notified when the certificate bundle is updated.\ \ The bundled -\f1 pip +\f3 pip \f0 included with the Python 2.7 installer has its own default certificate store for verifying download connections.\ \ -\b \ul Using IDLE or other Tk applications [NEW/CHANGED in 2.7.15] -\b0 \ulnone \ +\f1\b \ul Using IDLE or other Tk applications [NEW/CHANGED in 2.7.15] +\f0\b0 \ulnone \ \ -The 10.9+ installer variant comes with its own private version of Tcl/Tk 8.6. It does not use system-supplied or third-party supplied versions of Tcl/Tk.\ +As of 2.7.15, the 10.9+ installer variant comes with its own private version of Tcl/Tk 8.6. It does not use system-supplied or third-party supplied versions of Tcl/Tk.\ \ -For the 10.6+ variant, you continue to need to install a newer third-party version of the -\i Tcl/Tk -\i0 8.5 (not 8.6) frameworks to use IDLE or other programs that use the Tkinter graphical user interface toolkit. Visit {\field{\*\fldinst{HYPERLINK "https://www.python.org/download/mac/tcltk/"}}{\fldrslt https://www.python.org/download/mac/tcltk/}} for current information about supported and recommended versions of -\i Tcl/Tk -\i0 for this version of Python and of macOS.\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 + +\f1\b \cf0 CHANGED in 2.7.16: +\f0\b0 The 10.6+ variant now also uses a private version of Tcl/Tk 8.6.\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 -\b \ul \ +\f1\b \cf0 \ul \ulc0 \ Binary installer support for Mac OS X 10.5 and earlier discontinued [CHANGED for Python 2.7.15] -\b0 \ulnone \ +\f0\b0 \ulnone \ \ As of Python 2.7.15, binary installers from python.org no longer support Mac OS X 10.5 (Leopard) systems. Binary installer support for Mac OS X 10.3.9 (Panther) and 10.4.x (Tiger) systems was previously dropped in Python 2.7.9. Mac OS X 10.5 was originally released by Apple in 2007 and last updated in 2009 and was the last OS X release for PPC machines (G4 and G5). If needed, it is still possible to build Python from source for 10.3.9, 10.4, or 10.5.\ \ -\b \ul Packages installed with the system Python 2.7 are no longer searched for [CHANGED for Python 2.7.13] -\b0 \ulnone \ +\f1\b \ul Packages installed with the system Python 2.7 are no longer searched for [CHANGED for Python 2.7.13] +\f0\b0 \ulnone \ \ As of Python 2.7.0, user-installed Python 2.7 versions from python.org installers added the system-wide site-packages directory for the Apple-supplied Python 2.7 to the end of their search path. This meant that packages installed with the system Python 2.7 could also be used by the user-installed Python 2.7. While sometimes convenient, this also often caused confusion with the implicit coupling between the two Python instances. Separately, as of macOS 10.12, Apple changed the layout of the system site-packages directory, -\f1 /Library/Python/2.7/site-packages +\f3 /Library/Python/2.7/site-packages \f0 , in a way that can now cause installation of -\f1 pip +\f3 pip \f0 components to fail. To avoid the confusion and the installation failures, as of 2.7.13 user-installed Pythons no longer add -\f1 /Library/Python/2.7/site-packages +\f3 /Library/Python/2.7/site-packages \f0 to -\f1 sys.path +\f3 sys.path \f0 . If you are using a package with both a user-installed Python 2.7 and the system Python 2.7, you will now need to ensure that separate copies of the package are installed for each instance.\ \ -\b \ul Installing on OS X 10.8 (Mountain Lion) or later systems [CHANGED for Python 2.7.9] -\b0 \ulnone \ +\f1\b \ul Installing on OS X 10.8 (Mountain Lion) or later systems [CHANGED for Python 2.7.9] +\f0\b0 \ulnone \ \ As of Python 2.7.9, installer packages from python.org are now compatible with the Gatekeeper security feature introduced in OS X 10.8. Downloaded packages can now be directly installed by double-clicking with the default system security settings. Python.org installer packages for macOS are signed with the Developer ID of the builder, as identified on {\field{\*\fldinst{HYPERLINK "https://www.python.org/downloads/"}}{\fldrslt the download page}} for this release. To inspect the digital signature of the package, click on the lock icon in the upper right corner of the -\i Install Python -\i0 installer window. Refer to Apple\'92s support pages for {\field{\*\fldinst{HYPERLINK "http://support.apple.com/kb/ht5290"}}{\fldrslt more information on Gatekeeper}}.\ +\f2\i Install Python +\f0\i0 installer window. Refer to Apple\'92s support pages for {\field{\*\fldinst{HYPERLINK "http://support.apple.com/kb/ht5290"}}{\fldrslt more information on Gatekeeper}}.\ \ -\b \ul Simplified web-based installs [NEW for Python 2.7.9] -\b0 \ulnone \ +\f1\b \ul Simplified web-based installs [NEW for Python 2.7.9] +\f0\b0 \ulnone \ \ With the change to the newer flat format installer package, the download file now has a -\f1 .pkg +\f3 .pkg \f0 extension as it is no longer necessary to embed the installer within a disk image ( -\f1 .dmg +\f3 .dmg \f0 ) container. If you download the Python installer through a web browser, the macOS installer application may open automatically to allow you to perform the install. If your browser settings do not allow automatic open, double click on the downloaded installer file.\ \ -\b \ul New Installation Options and Defaults [NEW for Python 2.7.9] -\b0 \ulnone \ +\f1\b \ul New Installation Options and Defaults [NEW for Python 2.7.9] +\f0\b0 \ulnone \ \ The Python installer now includes an option to automatically install or upgrade -\f1 pip +\f3 pip \f0 , a tool for installing and managing Python packages. This option is enabled by default and no Internet access is required. If you do not want the installer to do this, select the -\i Customize -\i0 option at the -\i Installation Type -\i0 step and uncheck the -\i Install or upgrade pip -\i0 option. For other changes in this release, see the -\i Release Notes -\i0 link for this release at {\field{\*\fldinst{HYPERLINK "https://www.python.org/downloads/"}}{\fldrslt https://www.python.org/downloads/}}.\ +\f2\i Customize +\f0\i0 option at the +\f2\i Installation Type +\f0\i0 step and uncheck the +\f2\i Install or upgrade pip +\f0\i0 option. For other changes in this release, see the +\f2\i Release Notes +\f0\i0 link for this release at {\field{\*\fldinst{HYPERLINK "https://www.python.org/downloads/"}}{\fldrslt https://www.python.org/downloads/}}.\ \ -\b \ul Python 3 and Python 2 Co-existence\ +\f1\b \ul Python 3 and Python 2 Co-existence\ -\b0 \ulnone \ +\f0\b0 \ulnone \ Python.org Python 2.7 and 3.x versions can both be installed on your system and will not conflict. Python 2.7 command names contain a 2 or no digit: -\f1 python2 +\f3 python2 \f0 (or -\f1 python2.7 +\f3 python2.7 \f0 or -\f1 python +\f3 python \f0 ), -\f1 idle2 +\f3 idle2 \f0 (or -\f1 idle2.7 +\f3 idle2.7 \f0 or -\f1 idle +\f3 idle \f0 ), -\f1 pip2 +\f3 pip2 \f0 (or -\f1 pip2.7 +\f3 pip2.7 \f0 or -\f1 pip +\f3 pip \f0 ), etc. Command names for Python 3 contain a 3 in them, -\f1 python3 +\f3 python3 \f0 , -\f1 idle3 +\f3 idle3 \f0 , -\f1 pip3 +\f3 pip3 \f0 , etc. Also, installing a python.org Python 2.7 does not alter or remove any Apple-supplied system Pythons, found in -\f1 /usr/bin +\f3 /usr/bin \f0 .\ \ \ diff --git a/Mac/BuildScript/resources/Welcome.rtf b/Mac/BuildScript/resources/Welcome.rtf index fcf9e042dc36..4810c334ebf1 100644 --- a/Mac/BuildScript/resources/Welcome.rtf +++ b/Mac/BuildScript/resources/Welcome.rtf @@ -1,28 +1,33 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf400 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fmodern\fcharset0 CourierNewPSMT;} +{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf100 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;\f2\fmodern\fcharset0 CourierNewPSMT; +} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} \paperw11905\paperh16837\margl1440\margr1440\vieww13360\viewh10920\viewkind0 \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 \f0\fs24 \cf0 This package will install -\b Python $FULL_VERSION -\b0 for -\b macOS $MACOSX_DEPLOYMENT_TARGET -\b0 .\ +\f1\b Python $FULL_VERSION +\f0\b0 for +\f1\b macOS $MACOSX_DEPLOYMENT_TARGET +\f0\b0 .\ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\partightenfactor0 \cf0 \ -\b Python for macOS -\b0 consists of the Python programming language interpreter, plus a set of programs to allow easy access to it for macOS users including an integrated development environment -\b IDLE -\b0 .\ +\f1\b Python for macOS +\f0\b0 consists of the Python programming language interpreter, plus a set of programs to allow easy access to it for macOS users including an integrated development environment +\f1\b IDLE +\f0\b0 .\ \ -\b NEW in 2.7.15: -\b0 two installer variants (10.9+ 64-bit-only, 10.6+ 64-/32-bit), built-in Tcl/Tk 8.6 support in the 10.9+ variant (no additional third-party downloads!), updated -\f1 pip, +\f1\b NEW in 2.7.15: +\f0\b0 two installer variants (10.9+ 64-bit-only, 10.6+ 64-/32-bit), built-in Tcl/Tk 8.6 support in the 10.9+ variant (no additional third-party downloads!), updated +\f2 pip, \f0 built-in OpenSSL 1.0.2 (click on -\f1 Install Certificates +\f2 Install Certificates \f0 for root certificates)\ +\ + +\f1\b CHANGED in 2.7.16: +\f0\b0 the 10.6+ variant now also uses a built-in Tcl/Tk 8.6\ } \ No newline at end of file diff --git a/Misc/NEWS.d/next/macOS/2018-12-11-02-50-35.bpo-15663.6tnyd2.rst b/Misc/NEWS.d/next/macOS/2018-12-11-02-50-35.bpo-15663.6tnyd2.rst new file mode 100644 index 000000000000..1aa7f11a05ef --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2018-12-11-02-50-35.bpo-15663.6tnyd2.rst @@ -0,0 +1,2 @@ +The macOS 10.6+ installer now provides a private copy of Tcl/Tk 8.6, like +the 10.9+ installer does. From webhook-mailer at python.org Tue Dec 11 05:13:22 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 11 Dec 2018 10:13:22 -0000 Subject: [Python-checkins] bpo-35444: Fix error handling when fail to look up builtin "getattr". (GH-11047) (GH-11107) (GH-11108) Message-ID: https://github.com/python/cpython/commit/be6ec444729f727f304ae10f3a7e2feda3cc3aaa commit: be6ec444729f727f304ae10f3a7e2feda3cc3aaa branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Serhiy Storchaka date: 2018-12-11T12:13:14+02:00 summary: bpo-35444: Fix error handling when fail to look up builtin "getattr". (GH-11047) (GH-11107) (GH-11108) (cherry picked from commit bb86bf4c4eaa30b1f5192dab9f389ce0bb61114d) (cherry picked from commit 3cae16d2e98ffaa89ddd311df70a857dfaff4020) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst M Include/ceval.h M Modules/_pickle.c M Objects/classobject.c M Objects/descrobject.c M Objects/methodobject.c M Python/ceval.c diff --git a/Include/ceval.h b/Include/ceval.h index 38d470999f73..f46aef18043f 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -38,10 +38,12 @@ PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void); PyAPI_FUNC(PyObject *) PyEval_GetLocals(void); PyAPI_FUNC(struct _frame *) PyEval_GetFrame(void); +#ifndef Py_LIMITED_API +/* Helper to look up a builtin object */ +PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *); /* Look at the current frame's (if any) code's co_flags, and turn on the corresponding compiler flags in cf->cf_flags. Return 1 if any flag was set, else return 0. */ -#ifndef Py_LIMITED_API PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf); #endif diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst new file mode 100644 index 000000000000..22c3969f1276 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-09-13-09-39.bpo-35444.9kYn4V.rst @@ -0,0 +1,2 @@ +Fixed error handling in pickling methods when fail to look up builtin +"getattr". diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 8c8163560f11..41b8fa7b3c29 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -200,19 +200,15 @@ _Pickle_ClearState(PickleState *st) static int _Pickle_InitState(PickleState *st) { - PyObject *builtins; PyObject *copyreg = NULL; PyObject *compat_pickle = NULL; PyObject *codecs = NULL; PyObject *functools = NULL; + _Py_IDENTIFIER(getattr); - builtins = PyEval_GetBuiltins(); - if (builtins == NULL) - goto error; - st->getattr = PyDict_GetItemString(builtins, "getattr"); + st->getattr = _PyEval_GetBuiltinId(&PyId_getattr); if (st->getattr == NULL) goto error; - Py_INCREF(st->getattr); copyreg = PyImport_ImportModule("copyreg"); if (!copyreg) diff --git a/Objects/classobject.c b/Objects/classobject.c index b0ed02305695..7071016d06b9 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -75,8 +75,6 @@ method_reduce(PyMethodObject *im) { PyObject *self = PyMethod_GET_SELF(im); PyObject *func = PyMethod_GET_FUNCTION(im); - PyObject *builtins; - PyObject *getattr; PyObject *funcname; _Py_IDENTIFIER(getattr); @@ -84,9 +82,8 @@ method_reduce(PyMethodObject *im) if (funcname == NULL) { return NULL; } - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(ON)", getattr, self, funcname); + return Py_BuildValue("N(ON)", _PyEval_GetBuiltinId(&PyId_getattr), + self, funcname); } static PyMethodDef method_methods[] = { diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 9d258cf111d1..e897b1092616 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -389,14 +389,9 @@ descr_get_qualname(PyDescrObject *descr, void *Py_UNUSED(ignored)) static PyObject * descr_reduce(PyDescrObject *descr) { - PyObject *builtins; - PyObject *getattr; _Py_IDENTIFIER(getattr); - - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(OO)", getattr, PyDescr_TYPE(descr), - PyDescr_NAME(descr)); + return Py_BuildValue("N(OO)", _PyEval_GetBuiltinId(&PyId_getattr), + PyDescr_TYPE(descr), PyDescr_NAME(descr)); } static PyMethodDef descr_methods[] = { @@ -1098,13 +1093,9 @@ wrapper_repr(wrapperobject *wp) static PyObject * wrapper_reduce(wrapperobject *wp) { - PyObject *builtins; - PyObject *getattr; _Py_IDENTIFIER(getattr); - - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(OO)", getattr, wp->self, PyDescr_NAME(wp->descr)); + return Py_BuildValue("N(OO)", _PyEval_GetBuiltinId(&PyId_getattr), + wp->self, PyDescr_NAME(wp->descr)); } static PyMethodDef wrapper_methods[] = { diff --git a/Objects/methodobject.c b/Objects/methodobject.c index fe52545667f2..6794bf21d997 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -320,16 +320,13 @@ meth_dealloc(PyCFunctionObject *m) static PyObject * meth_reduce(PyCFunctionObject *m) { - PyObject *builtins; - PyObject *getattr; _Py_IDENTIFIER(getattr); if (m->m_self == NULL || PyModule_Check(m->m_self)) return PyUnicode_FromString(m->m_ml->ml_name); - builtins = PyEval_GetBuiltins(); - getattr = _PyDict_GetItemId(builtins, &PyId_getattr); - return Py_BuildValue("O(Os)", getattr, m->m_self, m->m_ml->ml_name); + return Py_BuildValue("N(Os)", _PyEval_GetBuiltinId(&PyId_getattr), + m->m_self, m->m_ml->ml_name); } static PyMethodDef meth_methods[] = { diff --git a/Python/ceval.c b/Python/ceval.c index 36e966470dba..0b30cc1d48d1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4667,6 +4667,20 @@ PyEval_GetBuiltins(void) return current_frame->f_builtins; } +/* Convenience function to get a builtin from its name */ +PyObject * +_PyEval_GetBuiltinId(_Py_Identifier *name) +{ + PyObject *attr = _PyDict_GetItemIdWithError(PyEval_GetBuiltins(), name); + if (attr) { + Py_INCREF(attr); + } + else if (!PyErr_Occurred()) { + PyErr_SetObject(PyExc_AttributeError, _PyUnicode_FromId(name)); + } + return attr; +} + PyObject * PyEval_GetLocals(void) { From webhook-mailer at python.org Tue Dec 11 05:17:33 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 10:17:33 -0000 Subject: [Python-checkins] [3.7] bpo-33747: Avoid mutating the global sys.modules dict in unittest.mock tests (GH-8520) (GH-11031) Message-ID: https://github.com/python/cpython/commit/45a31a1ec11e75cd0ef487dd4cea7f9fc4f885c8 commit: 45a31a1ec11e75cd0ef487dd4cea7f9fc4f885c8 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-11T02:17:29-08:00 summary: [3.7] bpo-33747: Avoid mutating the global sys.modules dict in unittest.mock tests (GH-8520) (GH-11031) (cherry picked from commit 3cf74384b53b998fa846dc2590cedf9ad2a0d5fd) Co-authored-by: Anirudha Bose https://bugs.python.org/issue33747 files: M Lib/unittest/test/testmock/testpatch.py diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py index fe4ecefd4405..f05225730daf 100644 --- a/Lib/unittest/test/testmock/testpatch.py +++ b/Lib/unittest/test/testmock/testpatch.py @@ -9,6 +9,7 @@ from unittest.test.testmock import support from unittest.test.testmock.support import SomeClass, is_instance +from test.test_importlib.util import uncache from unittest.mock import ( NonCallableMock, CallableMixin, sentinel, MagicMock, Mock, NonCallableMagicMock, patch, _patch, @@ -1660,20 +1661,19 @@ def test_mock_calls_with_patch(self): def test_patch_imports_lazily(self): - sys.modules.pop('squizz', None) - p1 = patch('squizz.squozz') self.assertRaises(ImportError, p1.start) - squizz = Mock() - squizz.squozz = 6 - sys.modules['squizz'] = squizz - p1 = patch('squizz.squozz') - squizz.squozz = 3 - p1.start() - p1.stop() - self.assertEqual(squizz.squozz, 3) + with uncache('squizz'): + squizz = Mock() + sys.modules['squizz'] = squizz + squizz.squozz = 6 + p1 = patch('squizz.squozz') + squizz.squozz = 3 + p1.start() + p1.stop() + self.assertEqual(squizz.squozz, 3) def test_patch_propogrates_exc_on_exit(self): class holder: @@ -1696,7 +1696,12 @@ def with_custom_patch(target): def test(mock): raise RuntimeError - self.assertRaises(RuntimeError, test) + with uncache('squizz'): + squizz = Mock() + sys.modules['squizz'] = squizz + + self.assertRaises(RuntimeError, test) + self.assertIs(holder.exc_info[0], RuntimeError) self.assertIsNotNone(holder.exc_info[1], 'exception value not propgated') From webhook-mailer at python.org Tue Dec 11 05:17:42 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 10:17:42 -0000 Subject: [Python-checkins] [3.6] bpo-33747: Avoid mutating the global sys.modules dict in unittest.mock tests (GH-8520) (GH-11032) Message-ID: https://github.com/python/cpython/commit/7d9f21950927e7c7fe69e5edeb06023a963c6f68 commit: 7d9f21950927e7c7fe69e5edeb06023a963c6f68 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-11T02:17:37-08:00 summary: [3.6] bpo-33747: Avoid mutating the global sys.modules dict in unittest.mock tests (GH-8520) (GH-11032) (cherry picked from commit 3cf74384b53b998fa846dc2590cedf9ad2a0d5fd) Co-authored-by: Anirudha Bose https://bugs.python.org/issue33747 files: M Lib/unittest/test/testmock/testpatch.py diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py index fe4ecefd4405..f05225730daf 100644 --- a/Lib/unittest/test/testmock/testpatch.py +++ b/Lib/unittest/test/testmock/testpatch.py @@ -9,6 +9,7 @@ from unittest.test.testmock import support from unittest.test.testmock.support import SomeClass, is_instance +from test.test_importlib.util import uncache from unittest.mock import ( NonCallableMock, CallableMixin, sentinel, MagicMock, Mock, NonCallableMagicMock, patch, _patch, @@ -1660,20 +1661,19 @@ def test_mock_calls_with_patch(self): def test_patch_imports_lazily(self): - sys.modules.pop('squizz', None) - p1 = patch('squizz.squozz') self.assertRaises(ImportError, p1.start) - squizz = Mock() - squizz.squozz = 6 - sys.modules['squizz'] = squizz - p1 = patch('squizz.squozz') - squizz.squozz = 3 - p1.start() - p1.stop() - self.assertEqual(squizz.squozz, 3) + with uncache('squizz'): + squizz = Mock() + sys.modules['squizz'] = squizz + squizz.squozz = 6 + p1 = patch('squizz.squozz') + squizz.squozz = 3 + p1.start() + p1.stop() + self.assertEqual(squizz.squozz, 3) def test_patch_propogrates_exc_on_exit(self): class holder: @@ -1696,7 +1696,12 @@ def with_custom_patch(target): def test(mock): raise RuntimeError - self.assertRaises(RuntimeError, test) + with uncache('squizz'): + squizz = Mock() + sys.modules['squizz'] = squizz + + self.assertRaises(RuntimeError, test) + self.assertIs(holder.exc_info[0], RuntimeError) self.assertIsNotNone(holder.exc_info[1], 'exception value not propgated') From webhook-mailer at python.org Tue Dec 11 06:05:26 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 11 Dec 2018 11:05:26 -0000 Subject: [Python-checkins] bpo-35458: Fix test_shutil.test_disk_usage() (GH-11111) Message-ID: https://github.com/python/cpython/commit/dc525f4315cdbe84693396d3f7a65a00425743bb commit: dc525f4315cdbe84693396d3f7a65a00425743bb branch: master author: Victor Stinner committer: GitHub date: 2018-12-11T12:05:21+01:00 summary: bpo-35458: Fix test_shutil.test_disk_usage() (GH-11111) The following test fails if a different process creates or removes a file on the same disk partition between the two lines: usage = shutil.disk_usage(os.path.dirname(__file__)) self.assertEqual(usage, shutil.disk_usage(__file__)) Only test that disk_usage() succeed on a filename, but don't check the result. Add also tests on the fields type (must be int). files: M Lib/test/test_shutil.py diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 9db6aec19206..ec8fcc3eef01 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1363,13 +1363,17 @@ def _boo(filename, extract_dir, extra): "disk_usage not available on this platform") def test_disk_usage(self): usage = shutil.disk_usage(os.path.dirname(__file__)) - self.assertEqual(usage, shutil.disk_usage(__file__)) + for attr in ('total', 'used', 'free'): + self.assertIsInstance(getattr(usage, attr), int) self.assertGreater(usage.total, 0) self.assertGreater(usage.used, 0) self.assertGreaterEqual(usage.free, 0) self.assertGreaterEqual(usage.total, usage.used) self.assertGreater(usage.total, usage.free) + # bpo-32557: Check that disk_usage() also accepts a filename + shutil.disk_usage(__file__) + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown') def test_chown(self): From webhook-mailer at python.org Tue Dec 11 06:31:26 2018 From: webhook-mailer at python.org (Pablo Galindo) Date: Tue, 11 Dec 2018 11:31:26 -0000 Subject: [Python-checkins] bpo-35412: Skip test_multiprocessing_fork and test_multiprocessing_forkserver on Windows (GH-11086) Message-ID: https://github.com/python/cpython/commit/a932d0b496767b5aac14191cbc17093e502b6cb4 commit: a932d0b496767b5aac14191cbc17093e502b6cb4 branch: master author: Pablo Galindo committer: GitHub date: 2018-12-11T11:31:16Z summary: bpo-35412: Skip test_multiprocessing_fork and test_multiprocessing_forkserver on Windows (GH-11086) Forkserver and fork are not available on Windows and therefore these test must be skipped. files: M Lib/test/test_multiprocessing_fork.py M Lib/test/test_multiprocessing_forkserver.py diff --git a/Lib/test/test_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py index daadcd3cc312..5000edb7c5c2 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -7,6 +7,9 @@ if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") +if sys.platform == "win32": + raise unittest.SkipTest("fork is not available on Windows") + if sys.platform == 'darwin': raise unittest.SkipTest("test may crash on macOS (bpo-33725)") diff --git a/Lib/test/test_multiprocessing_forkserver.py b/Lib/test/test_multiprocessing_forkserver.py index 407bb3f9b694..6ad5faf9e8a3 100644 --- a/Lib/test/test_multiprocessing_forkserver.py +++ b/Lib/test/test_multiprocessing_forkserver.py @@ -1,11 +1,15 @@ import unittest import test._test_multiprocessing +import sys from test import support if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") +if sys.platform == "win32": + raise unittest.SkipTest("forkserver is not available on Windows") + test._test_multiprocessing.install_tests_in_module_dict(globals(), 'forkserver') if __name__ == '__main__': From webhook-mailer at python.org Tue Dec 11 06:32:15 2018 From: webhook-mailer at python.org (Pablo Galindo) Date: Tue, 11 Dec 2018 11:32:15 -0000 Subject: [Python-checkins] bpo-35426: Eliminate race condition in test_interprocess_signal (GH-11087) Message-ID: https://github.com/python/cpython/commit/2ab2afd387084ba38a37f5944fcb0675113b64dc commit: 2ab2afd387084ba38a37f5944fcb0675113b64dc branch: master author: Pablo Galindo committer: GitHub date: 2018-12-11T11:32:12Z summary: bpo-35426: Eliminate race condition in test_interprocess_signal (GH-11087) The test only except SIGUSR1Exception inside wait_signal(), but the signal can be sent during subprocess_send_signal() call. files: M Lib/test/signalinterproctester.py diff --git a/Lib/test/signalinterproctester.py b/Lib/test/signalinterproctester.py index 877be517c0ea..168b5da0f2c0 100644 --- a/Lib/test/signalinterproctester.py +++ b/Lib/test/signalinterproctester.py @@ -21,25 +21,19 @@ def sigusr1_handler(self, signum, frame): self.got_signals['SIGUSR1'] += 1 raise SIGUSR1Exception - def wait_signal(self, child, signame, exc_class=None): - try: - if child is not None: - # This wait should be interrupted by exc_class - # (if set) - child.wait() - - timeout = 10.0 - deadline = time.monotonic() + timeout - - while time.monotonic() < deadline: - if self.got_signals[signame]: - return - signal.pause() - except BaseException as exc: - if exc_class is not None and isinstance(exc, exc_class): - # got the expected exception + def wait_signal(self, child, signame): + if child is not None: + # This wait should be interrupted by exc_class + # (if set) + child.wait() + + timeout = 10.0 + deadline = time.monotonic() + timeout + + while time.monotonic() < deadline: + if self.got_signals[signame]: return - raise + signal.pause() self.fail('signal %s not received after %s seconds' % (signame, timeout)) @@ -65,8 +59,9 @@ def test_interprocess_signal(self): self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 0, 'SIGALRM': 0}) - with self.subprocess_send_signal(pid, "SIGUSR1") as child: - self.wait_signal(child, 'SIGUSR1', SIGUSR1Exception) + with self.assertRaises(SIGUSR1Exception): + with self.subprocess_send_signal(pid, "SIGUSR1") as child: + self.wait_signal(child, 'SIGUSR1') self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1, 'SIGALRM': 0}) @@ -75,8 +70,9 @@ def test_interprocess_signal(self): child.wait() try: - signal.alarm(1) - self.wait_signal(None, 'SIGALRM', KeyboardInterrupt) + with self.assertRaises(KeyboardInterrupt): + signal.alarm(1) + self.wait_signal(None, 'SIGALRM') self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1, 'SIGALRM': 0}) finally: From webhook-mailer at python.org Tue Dec 11 06:50:37 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 11:50:37 -0000 Subject: [Python-checkins] bpo-35426: Eliminate race condition in test_interprocess_signal (GH-11087) Message-ID: https://github.com/python/cpython/commit/f7404a5a08b70ec171279a277c1817e82430fa83 commit: f7404a5a08b70ec171279a277c1817e82430fa83 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-11T03:50:34-08:00 summary: bpo-35426: Eliminate race condition in test_interprocess_signal (GH-11087) The test only except SIGUSR1Exception inside wait_signal(), but the signal can be sent during subprocess_send_signal() call. (cherry picked from commit 2ab2afd387084ba38a37f5944fcb0675113b64dc) Co-authored-by: Pablo Galindo files: M Lib/test/signalinterproctester.py diff --git a/Lib/test/signalinterproctester.py b/Lib/test/signalinterproctester.py index 877be517c0ea..168b5da0f2c0 100644 --- a/Lib/test/signalinterproctester.py +++ b/Lib/test/signalinterproctester.py @@ -21,25 +21,19 @@ def sigusr1_handler(self, signum, frame): self.got_signals['SIGUSR1'] += 1 raise SIGUSR1Exception - def wait_signal(self, child, signame, exc_class=None): - try: - if child is not None: - # This wait should be interrupted by exc_class - # (if set) - child.wait() - - timeout = 10.0 - deadline = time.monotonic() + timeout - - while time.monotonic() < deadline: - if self.got_signals[signame]: - return - signal.pause() - except BaseException as exc: - if exc_class is not None and isinstance(exc, exc_class): - # got the expected exception + def wait_signal(self, child, signame): + if child is not None: + # This wait should be interrupted by exc_class + # (if set) + child.wait() + + timeout = 10.0 + deadline = time.monotonic() + timeout + + while time.monotonic() < deadline: + if self.got_signals[signame]: return - raise + signal.pause() self.fail('signal %s not received after %s seconds' % (signame, timeout)) @@ -65,8 +59,9 @@ def test_interprocess_signal(self): self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 0, 'SIGALRM': 0}) - with self.subprocess_send_signal(pid, "SIGUSR1") as child: - self.wait_signal(child, 'SIGUSR1', SIGUSR1Exception) + with self.assertRaises(SIGUSR1Exception): + with self.subprocess_send_signal(pid, "SIGUSR1") as child: + self.wait_signal(child, 'SIGUSR1') self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1, 'SIGALRM': 0}) @@ -75,8 +70,9 @@ def test_interprocess_signal(self): child.wait() try: - signal.alarm(1) - self.wait_signal(None, 'SIGALRM', KeyboardInterrupt) + with self.assertRaises(KeyboardInterrupt): + signal.alarm(1) + self.wait_signal(None, 'SIGALRM') self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1, 'SIGALRM': 0}) finally: From webhook-mailer at python.org Tue Dec 11 06:56:54 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 11:56:54 -0000 Subject: [Python-checkins] bpo-35426: Eliminate race condition in test_interprocess_signal (GH-11087) Message-ID: https://github.com/python/cpython/commit/869e23e0af806ed3a10d0484827cb1b5f5cd5e5f commit: 869e23e0af806ed3a10d0484827cb1b5f5cd5e5f branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-11T03:56:50-08:00 summary: bpo-35426: Eliminate race condition in test_interprocess_signal (GH-11087) The test only except SIGUSR1Exception inside wait_signal(), but the signal can be sent during subprocess_send_signal() call. (cherry picked from commit 2ab2afd387084ba38a37f5944fcb0675113b64dc) Co-authored-by: Pablo Galindo files: M Lib/test/signalinterproctester.py diff --git a/Lib/test/signalinterproctester.py b/Lib/test/signalinterproctester.py index 877be517c0ea..168b5da0f2c0 100644 --- a/Lib/test/signalinterproctester.py +++ b/Lib/test/signalinterproctester.py @@ -21,25 +21,19 @@ def sigusr1_handler(self, signum, frame): self.got_signals['SIGUSR1'] += 1 raise SIGUSR1Exception - def wait_signal(self, child, signame, exc_class=None): - try: - if child is not None: - # This wait should be interrupted by exc_class - # (if set) - child.wait() - - timeout = 10.0 - deadline = time.monotonic() + timeout - - while time.monotonic() < deadline: - if self.got_signals[signame]: - return - signal.pause() - except BaseException as exc: - if exc_class is not None and isinstance(exc, exc_class): - # got the expected exception + def wait_signal(self, child, signame): + if child is not None: + # This wait should be interrupted by exc_class + # (if set) + child.wait() + + timeout = 10.0 + deadline = time.monotonic() + timeout + + while time.monotonic() < deadline: + if self.got_signals[signame]: return - raise + signal.pause() self.fail('signal %s not received after %s seconds' % (signame, timeout)) @@ -65,8 +59,9 @@ def test_interprocess_signal(self): self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 0, 'SIGALRM': 0}) - with self.subprocess_send_signal(pid, "SIGUSR1") as child: - self.wait_signal(child, 'SIGUSR1', SIGUSR1Exception) + with self.assertRaises(SIGUSR1Exception): + with self.subprocess_send_signal(pid, "SIGUSR1") as child: + self.wait_signal(child, 'SIGUSR1') self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1, 'SIGALRM': 0}) @@ -75,8 +70,9 @@ def test_interprocess_signal(self): child.wait() try: - signal.alarm(1) - self.wait_signal(None, 'SIGALRM', KeyboardInterrupt) + with self.assertRaises(KeyboardInterrupt): + signal.alarm(1) + self.wait_signal(None, 'SIGALRM') self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1, 'SIGALRM': 0}) finally: From webhook-mailer at python.org Tue Dec 11 07:14:29 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 12:14:29 -0000 Subject: [Python-checkins] bpo-35412: Skip test_multiprocessing_fork and test_multiprocessing_forkserver on Windows (GH-11086) Message-ID: https://github.com/python/cpython/commit/d55a896cd63e72e2848c48226d031d612539ea2a commit: d55a896cd63e72e2848c48226d031d612539ea2a branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-11T04:14:23-08:00 summary: bpo-35412: Skip test_multiprocessing_fork and test_multiprocessing_forkserver on Windows (GH-11086) Forkserver and fork are not available on Windows and therefore these test must be skipped. (cherry picked from commit a932d0b496767b5aac14191cbc17093e502b6cb4) Co-authored-by: Pablo Galindo files: M Lib/test/test_multiprocessing_fork.py M Lib/test/test_multiprocessing_forkserver.py diff --git a/Lib/test/test_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py index daadcd3cc312..5000edb7c5c2 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -7,6 +7,9 @@ if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") +if sys.platform == "win32": + raise unittest.SkipTest("fork is not available on Windows") + if sys.platform == 'darwin': raise unittest.SkipTest("test may crash on macOS (bpo-33725)") diff --git a/Lib/test/test_multiprocessing_forkserver.py b/Lib/test/test_multiprocessing_forkserver.py index 407bb3f9b694..6ad5faf9e8a3 100644 --- a/Lib/test/test_multiprocessing_forkserver.py +++ b/Lib/test/test_multiprocessing_forkserver.py @@ -1,11 +1,15 @@ import unittest import test._test_multiprocessing +import sys from test import support if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") +if sys.platform == "win32": + raise unittest.SkipTest("forkserver is not available on Windows") + test._test_multiprocessing.install_tests_in_module_dict(globals(), 'forkserver') if __name__ == '__main__': From webhook-mailer at python.org Tue Dec 11 07:22:58 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 11 Dec 2018 12:22:58 -0000 Subject: [Python-checkins] bpo-35412: Skip test_multiprocessing_fork and test_multiprocessing_forkserver on Windows (GH-11086) Message-ID: https://github.com/python/cpython/commit/97568761a6adb64d750fb0ea68484f67ae5e54f7 commit: 97568761a6adb64d750fb0ea68484f67ae5e54f7 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-11T04:22:53-08:00 summary: bpo-35412: Skip test_multiprocessing_fork and test_multiprocessing_forkserver on Windows (GH-11086) Forkserver and fork are not available on Windows and therefore these test must be skipped. (cherry picked from commit a932d0b496767b5aac14191cbc17093e502b6cb4) Co-authored-by: Pablo Galindo files: M Lib/test/test_multiprocessing_fork.py M Lib/test/test_multiprocessing_forkserver.py diff --git a/Lib/test/test_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py index daadcd3cc312..5000edb7c5c2 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -7,6 +7,9 @@ if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") +if sys.platform == "win32": + raise unittest.SkipTest("fork is not available on Windows") + if sys.platform == 'darwin': raise unittest.SkipTest("test may crash on macOS (bpo-33725)") diff --git a/Lib/test/test_multiprocessing_forkserver.py b/Lib/test/test_multiprocessing_forkserver.py index 407bb3f9b694..6ad5faf9e8a3 100644 --- a/Lib/test/test_multiprocessing_forkserver.py +++ b/Lib/test/test_multiprocessing_forkserver.py @@ -1,11 +1,15 @@ import unittest import test._test_multiprocessing +import sys from test import support if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") +if sys.platform == "win32": + raise unittest.SkipTest("forkserver is not available on Windows") + test._test_multiprocessing.install_tests_in_module_dict(globals(), 'forkserver') if __name__ == '__main__': From webhook-mailer at python.org Tue Dec 11 08:14:22 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 11 Dec 2018 13:14:22 -0000 Subject: [Python-checkins] Remove an unused variable after bpo-35444. (GH-11117) Message-ID: https://github.com/python/cpython/commit/7211d306d4c2f73732540759e20dd17bd18b3361 commit: 7211d306d4c2f73732540759e20dd17bd18b3361 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-11T15:14:12+02:00 summary: Remove an unused variable after bpo-35444. (GH-11117) files: M Objects/object.c diff --git a/Objects/object.c b/Objects/object.c index 993342eae5d8..6c2bd7717c01 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -17,7 +17,6 @@ _Py_IDENTIFIER(Py_Repr); _Py_IDENTIFIER(__bytes__); _Py_IDENTIFIER(__dir__); _Py_IDENTIFIER(__isabstractmethod__); -_Py_IDENTIFIER(builtins); #ifdef Py_REF_DEBUG Py_ssize_t _Py_RefTotal; From webhook-mailer at python.org Tue Dec 11 11:08:49 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 11 Dec 2018 16:08:49 -0000 Subject: [Python-checkins] bpo-11617: Try to strengthen test_httpservers (GH-11121) Message-ID: https://github.com/python/cpython/commit/d336b1c8a40d14054145393fafb54b782cc1a549 commit: d336b1c8a40d14054145393fafb54b782cc1a549 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-12-11T17:08:02+01:00 summary: bpo-11617: Try to strengthen test_httpservers (GH-11121) (cherry picked from commit cb342182ee47c3e2c743de50d81aad60f4eebefd) files: M Lib/test/test_httpservers.py diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 93807c1959bb..25236c6ef41a 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -55,8 +55,8 @@ def __init__(self, test_object, request_handler): self.test_object = test_object def run(self): - self.server = HTTPServer(('', 0), self.request_handler) - self.test_object.PORT = self.server.socket.getsockname()[1] + self.server = HTTPServer(('localhost', 0), self.request_handler) + self.test_object.HOST, self.test_object.PORT = self.server.socket.getsockname() self.test_object.server_started.set() self.test_object = None try: @@ -84,7 +84,7 @@ def tearDown(self): test_support.threading_cleanup(*self._threads) def request(self, uri, method='GET', body=None, headers={}): - self.connection = httplib.HTTPConnection('localhost', self.PORT) + self.connection = httplib.HTTPConnection(self.HOST, self.PORT) self.connection.request(method, uri, body, headers) return self.connection.getresponse() @@ -187,7 +187,7 @@ def do_HEAD(self): def setUp(self): BaseTestCase.setUp(self) - self.con = httplib.HTTPConnection('localhost', self.PORT) + self.con = httplib.HTTPConnection(self.HOST, self.PORT) self.con.connect() def test_command(self): From webhook-mailer at python.org Tue Dec 11 12:07:11 2018 From: webhook-mailer at python.org (Andrew Svetlov) Date: Tue, 11 Dec 2018 17:07:11 -0000 Subject: [Python-checkins] bpo-35394: Add empty slots to abstract asyncio protocols (#10889) Message-ID: https://github.com/python/cpython/commit/5344501ad166c1380be452644a863a4679c4291b commit: 5344501ad166c1380be452644a863a4679c4291b branch: master author: Andrew Svetlov committer: GitHub date: 2018-12-11T19:07:05+02:00 summary: bpo-35394: Add empty slots to abstract asyncio protocols (#10889) * bpo-35394: Add empty slots to abstract asyncio protocols * Add missing test file files: A Lib/test/test_asyncio/test_protocols.py A Misc/NEWS.d/next/Library/2018-12-04-12-17-08.bpo-35394.fuTVDk.rst M Lib/asyncio/protocols.py M Lib/test/test_asyncio/test_events.py diff --git a/Lib/asyncio/protocols.py b/Lib/asyncio/protocols.py index a35ea822f33e..69fa43e8b651 100644 --- a/Lib/asyncio/protocols.py +++ b/Lib/asyncio/protocols.py @@ -16,6 +16,8 @@ class BaseProtocol: write-only transport like write pipe """ + __slots__ = () + def connection_made(self, transport): """Called when a connection is made. @@ -87,6 +89,8 @@ class Protocol(BaseProtocol): * CL: connection_lost() """ + __slots__ = () + def data_received(self, data): """Called when some data is received. @@ -130,6 +134,8 @@ class BufferedProtocol(BaseProtocol): * CL: connection_lost() """ + __slots__ = () + def get_buffer(self, sizehint): """Called to allocate a new receive buffer. @@ -160,6 +166,8 @@ def eof_received(self): class DatagramProtocol(BaseProtocol): """Interface for datagram protocol.""" + __slots__ = () + def datagram_received(self, data, addr): """Called when some datagram is received.""" @@ -173,6 +181,8 @@ def error_received(self, exc): class SubprocessProtocol(BaseProtocol): """Interface for protocol for subprocess calls.""" + __slots__ = () + def pipe_data_received(self, fd, data): """Called when the subprocess writes data into stdout/stderr pipe. diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index b76cfb75cce2..9311a209f23e 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -2486,30 +2486,6 @@ def test_not_implemented_async(self): loop.close() -class ProtocolsAbsTests(unittest.TestCase): - - def test_empty(self): - f = mock.Mock() - p = asyncio.Protocol() - self.assertIsNone(p.connection_made(f)) - self.assertIsNone(p.connection_lost(f)) - self.assertIsNone(p.data_received(f)) - self.assertIsNone(p.eof_received()) - - dp = asyncio.DatagramProtocol() - self.assertIsNone(dp.connection_made(f)) - self.assertIsNone(dp.connection_lost(f)) - self.assertIsNone(dp.error_received(f)) - self.assertIsNone(dp.datagram_received(f, f)) - - sp = asyncio.SubprocessProtocol() - self.assertIsNone(sp.connection_made(f)) - self.assertIsNone(sp.connection_lost(f)) - self.assertIsNone(sp.pipe_data_received(1, f)) - self.assertIsNone(sp.pipe_connection_lost(1, f)) - self.assertIsNone(sp.process_exited()) - - class PolicyTests(unittest.TestCase): def test_event_loop_policy(self): diff --git a/Lib/test/test_asyncio/test_protocols.py b/Lib/test/test_asyncio/test_protocols.py new file mode 100644 index 000000000000..438111cccd34 --- /dev/null +++ b/Lib/test/test_asyncio/test_protocols.py @@ -0,0 +1,57 @@ +import unittest +from unittest import mock + +import asyncio + + +class ProtocolsAbsTests(unittest.TestCase): + + def test_base_protocol(self): + f = mock.Mock() + p = asyncio.BaseProtocol() + self.assertIsNone(p.connection_made(f)) + self.assertIsNone(p.connection_lost(f)) + self.assertIsNone(p.pause_writing()) + self.assertIsNone(p.resume_writing()) + self.assertFalse(hasattr(p, '__dict__')) + + def test_protocol(self): + f = mock.Mock() + p = asyncio.Protocol() + self.assertIsNone(p.connection_made(f)) + self.assertIsNone(p.connection_lost(f)) + self.assertIsNone(p.data_received(f)) + self.assertIsNone(p.eof_received()) + self.assertIsNone(p.pause_writing()) + self.assertIsNone(p.resume_writing()) + self.assertFalse(hasattr(p, '__dict__')) + + def test_buffered_protocol(self): + f = mock.Mock() + p = asyncio.BufferedProtocol() + self.assertIsNone(p.connection_made(f)) + self.assertIsNone(p.connection_lost(f)) + self.assertIsNone(p.get_buffer(100)) + self.assertIsNone(p.buffer_updated(150)) + self.assertIsNone(p.pause_writing()) + self.assertIsNone(p.resume_writing()) + self.assertFalse(hasattr(p, '__dict__')) + + def test_datagram_protocol(self): + f = mock.Mock() + dp = asyncio.DatagramProtocol() + self.assertIsNone(dp.connection_made(f)) + self.assertIsNone(dp.connection_lost(f)) + self.assertIsNone(dp.error_received(f)) + self.assertIsNone(dp.datagram_received(f, f)) + self.assertFalse(hasattr(dp, '__dict__')) + + def test_subprocess_protocol(self): + f = mock.Mock() + sp = asyncio.SubprocessProtocol() + self.assertIsNone(sp.connection_made(f)) + self.assertIsNone(sp.connection_lost(f)) + self.assertIsNone(sp.pipe_data_received(1, f)) + self.assertIsNone(sp.pipe_connection_lost(1, f)) + self.assertIsNone(sp.process_exited()) + self.assertFalse(hasattr(sp, '__dict__')) diff --git a/Misc/NEWS.d/next/Library/2018-12-04-12-17-08.bpo-35394.fuTVDk.rst b/Misc/NEWS.d/next/Library/2018-12-04-12-17-08.bpo-35394.fuTVDk.rst new file mode 100644 index 000000000000..ab630c0f67fc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-04-12-17-08.bpo-35394.fuTVDk.rst @@ -0,0 +1 @@ +Add empty slots to asyncio abstract protocols. From webhook-mailer at python.org Tue Dec 11 21:11:33 2018 From: webhook-mailer at python.org (Ned Deily) Date: Wed, 12 Dec 2018 02:11:33 -0000 Subject: [Python-checkins] macOS installer build: mitigate hdiutil resource busy bug Message-ID: https://github.com/python/cpython/commit/56f86fcc44f7d74c68b6fa508328d61ce973eebc commit: 56f86fcc44f7d74c68b6fa508328d61ce973eebc branch: 3.6 author: Ned Deily committer: Ned Deily date: 2018-12-11T15:56:50-05:00 summary: macOS installer build: mitigate hdiutil resource busy bug files: M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index b1dede492058..6fc07ad3d3d1 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -1533,16 +1533,27 @@ def buildDMG(): imagepath = imagepath + '.dmg' os.mkdir(outdir) + + # Try to mitigate race condition in certain versions of macOS, e.g. 10.9, + # when hdiutil create fails with "Resource busy". For now, just retry + # the create a few times and hope that it eventually works. + volname='Python %s'%(getFullVersion()) - runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%( + cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%( shellQuote(volname), shellQuote(os.path.join(WORKDIR, 'installer')), shellQuote(imagepath + ".tmp.dmg" ))) - - # Try to mitigate race condition in certain versions of macOS, e.g. 10.9, - # when hdiutil fails with "Resource busy" - - time.sleep(10) + for i in range(5): + fd = os.popen(cmd, 'r') + data = fd.read() + xit = fd.close() + if not xit: + break + sys.stdout.write(data) + print(" -- retrying hdiutil create") + time.sleep(5) + else: + raise RuntimeError("command failed: %s"%(commandline,)) if not os.path.exists(os.path.join(WORKDIR, "mnt")): os.mkdir(os.path.join(WORKDIR, "mnt")) From webhook-mailer at python.org Tue Dec 11 21:17:18 2018 From: webhook-mailer at python.org (Ned Deily) Date: Wed, 12 Dec 2018 02:17:18 -0000 Subject: [Python-checkins] macOS installer build: mitigate hdiutil resource busy bug Message-ID: https://github.com/python/cpython/commit/8aa057aaabf710eda53cf90a77e23d2f127346a2 commit: 8aa057aaabf710eda53cf90a77e23d2f127346a2 branch: 3.7 author: Ned Deily committer: Ned Deily date: 2018-12-11T15:47:04-05:00 summary: macOS installer build: mitigate hdiutil resource busy bug files: M Mac/BuildScript/build-installer.py diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index 806f660bd644..5d11bbb10e60 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -1518,16 +1518,27 @@ def buildDMG(): imagepath = imagepath + '.dmg' os.mkdir(outdir) + + # Try to mitigate race condition in certain versions of macOS, e.g. 10.9, + # when hdiutil create fails with "Resource busy". For now, just retry + # the create a few times and hope that it eventually works. + volname='Python %s'%(getFullVersion()) - runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%( + cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%( shellQuote(volname), shellQuote(os.path.join(WORKDIR, 'installer')), shellQuote(imagepath + ".tmp.dmg" ))) - - # Try to mitigate race condition in certain versions of macOS, e.g. 10.9, - # when hdiutil fails with "Resource busy" - - time.sleep(10) + for i in range(5): + fd = os.popen(cmd, 'r') + data = fd.read() + xit = fd.close() + if not xit: + break + sys.stdout.write(data) + print(" -- retrying hdiutil create") + time.sleep(5) + else: + raise RuntimeError("command failed: %s"%(commandline,)) if not os.path.exists(os.path.join(WORKDIR, "mnt")): os.mkdir(os.path.join(WORKDIR, "mnt")) From webhook-mailer at python.org Wed Dec 12 02:54:59 2018 From: webhook-mailer at python.org (Chris Withers) Date: Wed, 12 Dec 2018 07:54:59 -0000 Subject: [Python-checkins] bpo-17185: Add __signature__ to mock that can be used by inspect for signature (GH11048) Message-ID: https://github.com/python/cpython/commit/f7fa62ef4422c9deee050a794fd8504640d9f8f4 commit: f7fa62ef4422c9deee050a794fd8504640d9f8f4 branch: master author: Xtreak committer: Chris Withers date: 2018-12-12T07:54:54Z summary: bpo-17185: Add __signature__ to mock that can be used by inspect for signature (GH11048) * Fix partial and partial method signatures in mock * Add more calls * Add NEWS entry * Use assertEquals and fix markup in NEWS * Refactor branching and add markup reference for functools * Revert partial object related changes and fix pr comments files: A Misc/NEWS.d/next/Library/2018-12-09-17-04-15.bpo-17185.SfSCJF.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testhelpers.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 61ed80b976a7..38189c9aec2e 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -103,6 +103,7 @@ def checksig(_mock_self, *args, **kwargs): sig.bind(*args, **kwargs) _copy_func_details(func, checksig) type(mock)._mock_check_sig = checksig + type(mock).__signature__ = sig def _copy_func_details(func, funcopy): @@ -172,11 +173,11 @@ def checksig(*args, **kwargs): return mock(*args, **kwargs)""" % name exec (src, context) funcopy = context[name] - _setup_func(funcopy, mock) + _setup_func(funcopy, mock, sig) return funcopy -def _setup_func(funcopy, mock): +def _setup_func(funcopy, mock, sig): funcopy.mock = mock # can't use isinstance with mocks @@ -224,6 +225,7 @@ def reset_mock(): funcopy.assert_called = assert_called funcopy.assert_not_called = assert_not_called funcopy.assert_called_once = assert_called_once + funcopy.__signature__ = sig mock._mock_delegate = funcopy diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 9388311e3e25..745580ef79db 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -1,3 +1,4 @@ +import inspect import time import types import unittest @@ -901,6 +902,35 @@ def __getattr__(self, attribute): self.assertFalse(hasattr(autospec, '__name__')) + def test_spec_inspect_signature(self): + + def myfunc(x, y): + pass + + mock = create_autospec(myfunc) + mock(1, 2) + mock(x=1, y=2) + + self.assertEqual(inspect.getfullargspec(mock), inspect.getfullargspec(myfunc)) + self.assertEqual(mock.mock_calls, [call(1, 2), call(x=1, y=2)]) + self.assertRaises(TypeError, mock, 1) + + + def test_spec_inspect_signature_annotations(self): + + def foo(a: int, b: int=10, *, c:int) -> int: + return a + b + c + + mock = create_autospec(foo) + mock(1, 2, c=3) + mock(1, c=3) + + self.assertEqual(inspect.getfullargspec(mock), inspect.getfullargspec(foo)) + self.assertEqual(mock.mock_calls, [call(1, 2, c=3), call(1, c=3)]) + self.assertRaises(TypeError, mock, 1) + self.assertRaises(TypeError, mock, 1, 2, 3, c=4) + + class TestCallList(unittest.TestCase): def test_args_list_contains_call_list(self): diff --git a/Misc/NEWS.d/next/Library/2018-12-09-17-04-15.bpo-17185.SfSCJF.rst b/Misc/NEWS.d/next/Library/2018-12-09-17-04-15.bpo-17185.SfSCJF.rst new file mode 100644 index 000000000000..311c6d2b0e4e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-09-17-04-15.bpo-17185.SfSCJF.rst @@ -0,0 +1,2 @@ +Set ``__signature__`` on mock for :mod:`inspect` to get signature. +Patch by Karthikeyan Singaravelan. From webhook-mailer at python.org Wed Dec 12 02:56:38 2018 From: webhook-mailer at python.org (Chris Withers) Date: Wed, 12 Dec 2018 07:56:38 -0000 Subject: [Python-checkins] Add test for double patching instance methods (#11085) Message-ID: https://github.com/python/cpython/commit/5a718e918db6211b633a7afb2bf537eb5b56cb1b commit: 5a718e918db6211b633a7afb2bf537eb5b56cb1b branch: master author: Anthony Sottile committer: Chris Withers date: 2018-12-12T07:56:35Z summary: Add test for double patching instance methods (#11085) files: A Misc/NEWS.d/next/Tests/2018-12-10-13-18-37.bpo-26704.DBAN4c.rst M Lib/unittest/test/testmock/testwith.py diff --git a/Lib/unittest/test/testmock/testwith.py b/Lib/unittest/test/testmock/testwith.py index 43b36a119952..ec4e540dcfd9 100644 --- a/Lib/unittest/test/testmock/testwith.py +++ b/Lib/unittest/test/testmock/testwith.py @@ -126,6 +126,20 @@ def test_dict_context_manager(self): self.assertEqual(foo, {}) + def test_double_patch_instance_method(self): + class C: + def f(self): + pass + + c = C() + + with patch.object(c, 'f', autospec=True) as patch1: + with patch.object(c, 'f', autospec=True) as patch2: + c.f() + self.assertEqual(patch2.call_count, 1) + self.assertEqual(patch1.call_count, 0) + c.f() + self.assertEqual(patch1.call_count, 1) class TestMockOpen(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Tests/2018-12-10-13-18-37.bpo-26704.DBAN4c.rst b/Misc/NEWS.d/next/Tests/2018-12-10-13-18-37.bpo-26704.DBAN4c.rst new file mode 100644 index 000000000000..458f495be483 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-10-13-18-37.bpo-26704.DBAN4c.rst @@ -0,0 +1,2 @@ +Added test demonstrating double-patching of an instance method. Patch by +Anthony Sottile. From webhook-mailer at python.org Wed Dec 12 03:58:41 2018 From: webhook-mailer at python.org (Chris Withers) Date: Wed, 12 Dec 2018 08:58:41 -0000 Subject: [Python-checkins] bpo-17185: Add __signature__ to mock that can be used by inspect for signature (GH11125) Message-ID: https://github.com/python/cpython/commit/6a12931c9cb5d472fe6370dbcd2bde72f34dddb4 commit: 6a12931c9cb5d472fe6370dbcd2bde72f34dddb4 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-12T08:58:36Z summary: bpo-17185: Add __signature__ to mock that can be used by inspect for signature (GH11125) * Fix partial and partial method signatures in mock * Add more calls * Add NEWS entry * Use assertEquals and fix markup in NEWS * Refactor branching and add markup reference for functools * Revert partial object related changes and fix pr comments (cherry picked from commit f7fa62ef4422c9deee050a794fd8504640d9f8f4) Co-authored-by: Xtreak files: A Misc/NEWS.d/next/Library/2018-12-09-17-04-15.bpo-17185.SfSCJF.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testhelpers.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index f641e38dc54b..5907e5c240f5 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -102,6 +102,7 @@ def checksig(_mock_self, *args, **kwargs): sig.bind(*args, **kwargs) _copy_func_details(func, checksig) type(mock)._mock_check_sig = checksig + type(mock).__signature__ = sig def _copy_func_details(func, funcopy): @@ -171,11 +172,11 @@ def checksig(*args, **kwargs): return mock(*args, **kwargs)""" % name exec (src, context) funcopy = context[name] - _setup_func(funcopy, mock) + _setup_func(funcopy, mock, sig) return funcopy -def _setup_func(funcopy, mock): +def _setup_func(funcopy, mock, sig): funcopy.mock = mock # can't use isinstance with mocks @@ -223,6 +224,7 @@ def reset_mock(): funcopy.assert_called = assert_called funcopy.assert_not_called = assert_not_called funcopy.assert_called_once = assert_called_once + funcopy.__signature__ = sig mock._mock_delegate = funcopy diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 9388311e3e25..745580ef79db 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -1,3 +1,4 @@ +import inspect import time import types import unittest @@ -901,6 +902,35 @@ def __getattr__(self, attribute): self.assertFalse(hasattr(autospec, '__name__')) + def test_spec_inspect_signature(self): + + def myfunc(x, y): + pass + + mock = create_autospec(myfunc) + mock(1, 2) + mock(x=1, y=2) + + self.assertEqual(inspect.getfullargspec(mock), inspect.getfullargspec(myfunc)) + self.assertEqual(mock.mock_calls, [call(1, 2), call(x=1, y=2)]) + self.assertRaises(TypeError, mock, 1) + + + def test_spec_inspect_signature_annotations(self): + + def foo(a: int, b: int=10, *, c:int) -> int: + return a + b + c + + mock = create_autospec(foo) + mock(1, 2, c=3) + mock(1, c=3) + + self.assertEqual(inspect.getfullargspec(mock), inspect.getfullargspec(foo)) + self.assertEqual(mock.mock_calls, [call(1, 2, c=3), call(1, c=3)]) + self.assertRaises(TypeError, mock, 1) + self.assertRaises(TypeError, mock, 1, 2, 3, c=4) + + class TestCallList(unittest.TestCase): def test_args_list_contains_call_list(self): diff --git a/Misc/NEWS.d/next/Library/2018-12-09-17-04-15.bpo-17185.SfSCJF.rst b/Misc/NEWS.d/next/Library/2018-12-09-17-04-15.bpo-17185.SfSCJF.rst new file mode 100644 index 000000000000..311c6d2b0e4e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-09-17-04-15.bpo-17185.SfSCJF.rst @@ -0,0 +1,2 @@ +Set ``__signature__`` on mock for :mod:`inspect` to get signature. +Patch by Karthikeyan Singaravelan. From webhook-mailer at python.org Wed Dec 12 04:00:47 2018 From: webhook-mailer at python.org (Chris Withers) Date: Wed, 12 Dec 2018 09:00:47 -0000 Subject: [Python-checkins] Add test for double patching instance methods (GH11126) Message-ID: https://github.com/python/cpython/commit/f27f0d2be4887f1a2c276d05246ab005964031d4 commit: f27f0d2be4887f1a2c276d05246ab005964031d4 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-12T09:00:44Z summary: Add test for double patching instance methods (GH11126) (cherry picked from commit 5a718e918db6211b633a7afb2bf537eb5b56cb1b) Co-authored-by: Anthony Sottile files: A Misc/NEWS.d/next/Tests/2018-12-10-13-18-37.bpo-26704.DBAN4c.rst M Lib/unittest/test/testmock/testwith.py diff --git a/Lib/unittest/test/testmock/testwith.py b/Lib/unittest/test/testmock/testwith.py index 43b36a119952..ec4e540dcfd9 100644 --- a/Lib/unittest/test/testmock/testwith.py +++ b/Lib/unittest/test/testmock/testwith.py @@ -126,6 +126,20 @@ def test_dict_context_manager(self): self.assertEqual(foo, {}) + def test_double_patch_instance_method(self): + class C: + def f(self): + pass + + c = C() + + with patch.object(c, 'f', autospec=True) as patch1: + with patch.object(c, 'f', autospec=True) as patch2: + c.f() + self.assertEqual(patch2.call_count, 1) + self.assertEqual(patch1.call_count, 0) + c.f() + self.assertEqual(patch1.call_count, 1) class TestMockOpen(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Tests/2018-12-10-13-18-37.bpo-26704.DBAN4c.rst b/Misc/NEWS.d/next/Tests/2018-12-10-13-18-37.bpo-26704.DBAN4c.rst new file mode 100644 index 000000000000..458f495be483 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-10-13-18-37.bpo-26704.DBAN4c.rst @@ -0,0 +1,2 @@ +Added test demonstrating double-patching of an instance method. Patch by +Anthony Sottile. From solipsis at pitrou.net Wed Dec 12 04:11:57 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 12 Dec 2018 09:11:57 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=66 Message-ID: <20181212091157.1.F97827037CC81F6B@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_asyncio leaked [0, 3, 0] memory blocks, sum=3 test_collections leaked [-7, 1, 0] memory blocks, sum=-6 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_forkserver leaked [0, 0, 44] references, sum=44 test_multiprocessing_forkserver leaked [-1, 1, 19] memory blocks, sum=19 test_multiprocessing_forkserver leaked [0, 0, 2] file descriptors, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/refloguD5AhE', '--timeout', '7200'] From webhook-mailer at python.org Wed Dec 12 06:06:13 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 12 Dec 2018 11:06:13 -0000 Subject: [Python-checkins] bpo-16039: CVE-2013-1752: Limit imaplib.IMAP4_SSL.readline() (GH-11120) Message-ID: https://github.com/python/cpython/commit/16d63202af35dadd652a5e3eae687ea709e95b11 commit: 16d63202af35dadd652a5e3eae687ea709e95b11 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-12-12T12:05:59+01:00 summary: bpo-16039: CVE-2013-1752: Limit imaplib.IMAP4_SSL.readline() (GH-11120) * bpo-16039: CVE-2013-1752: Change use of readline() in imaplib.IMAP4_SSL to limit line length. Remove IMAP4_SSL.readline() and IMAP4_SSL.read() to inherit safe IMAP4 implementation. * bpo-20118: reenable test_linetoolong() of test_imaplib on ThreadedNetworkedTests and ThreadedNetworkedTestsSSL. The test now sets the _MAXLINE limit to 10 characters. files: A Misc/NEWS.d/next/Security/2018-12-11-16-00-57.bpo-16039.PCj2n4.rst M Lib/imaplib.py M Lib/test/test_imaplib.py diff --git a/Lib/imaplib.py b/Lib/imaplib.py index 2e5511e02416..679c468251be 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -1182,16 +1182,6 @@ def open(self, host = '', port = IMAP4_SSL_PORT): self.file = self.sslobj.makefile('rb') - def read(self, size): - """Read 'size' bytes from remote.""" - return self.file.read(size) - - - def readline(self): - """Read line from remote.""" - return self.file.readline() - - def send(self, data): """Send data to remote.""" bytes = len(data) diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 405b7ea8dd70..acaad63b6a3a 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -166,14 +166,18 @@ def handle(self): def test_linetoolong(self): + maxline = 10 + class TooLongHandler(SimpleIMAPHandler): def handle(self): # Send a very long response line - self.wfile.write('* OK ' + imaplib._MAXLINE*'x' + '\r\n') + self.wfile.write('* OK ' + maxline * 'x' + '\r\n') - with self.reaped_server(TooLongHandler) as server: - self.assertRaises(imaplib.IMAP4.error, - self.imap_class, *server.server_address) + with self.reaped_server(TooLongHandler) as server, \ + support.swap_attr(imaplib, '_MAXLINE', maxline): + with self.assertRaisesRegexp(imaplib.IMAP4.error, + 'got more than 10 bytes'): + self.imap_class(*server.server_address) class ThreadedNetworkedTests(BaseThreadedNetworkedTests): @@ -187,9 +191,6 @@ class ThreadedNetworkedTestsSSL(BaseThreadedNetworkedTests): server_class = SecureTCPServer imap_class = IMAP4_SSL - def test_linetoolong(self): - raise unittest.SkipTest("test is not reliable on 2.7; see issue 20118") - class RemoteIMAPTest(unittest.TestCase): host = 'cyrus.andrew.cmu.edu' diff --git a/Misc/NEWS.d/next/Security/2018-12-11-16-00-57.bpo-16039.PCj2n4.rst b/Misc/NEWS.d/next/Security/2018-12-11-16-00-57.bpo-16039.PCj2n4.rst new file mode 100644 index 000000000000..ff9ff47e0805 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2018-12-11-16-00-57.bpo-16039.PCj2n4.rst @@ -0,0 +1,2 @@ +CVE-2013-1752: Change use of ``readline()`` in :class:`imaplib.IMAP4_SSL` to +limit line length. From webhook-mailer at python.org Wed Dec 12 07:46:59 2018 From: webhook-mailer at python.org (Xiang Zhang) Date: Wed, 12 Dec 2018 12:46:59 -0000 Subject: [Python-checkins] bpo-33106: change dbm key deletion error for readonly file from KeyError to dbm.error (#6295) Message-ID: https://github.com/python/cpython/commit/4fb0b8bc25c52aae8dcb4353e69c1c88999a9a53 commit: 4fb0b8bc25c52aae8dcb4353e69c1c88999a9a53 branch: master author: Xiang Zhang committer: GitHub date: 2018-12-12T20:46:55+08:00 summary: bpo-33106: change dbm key deletion error for readonly file from KeyError to dbm.error (#6295) files: A Misc/NEWS.d/next/Library/2018-03-30-01-20-35.bpo-33106.zncfvW.rst M Doc/library/dbm.rst M Doc/whatsnew/3.8.rst M Lib/dbm/dumb.py M Lib/test/test_dbm_dumb.py M Lib/test/test_dbm_gnu.py M Lib/test/test_dbm_ndbm.py M Modules/_dbmmodule.c M Modules/_gdbmmodule.c diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index ab45cac830e2..57ae547b833c 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -73,6 +73,10 @@ available, as well as :meth:`get` and :meth:`setdefault`. .. versionchanged:: 3.2 :meth:`get` and :meth:`setdefault` are now available in all database modules. +.. versionchanged:: 3.8 + Deleting a key from a read-only database raises database module specific error + instead of :exc:`KeyError`. + Key and values are always stored as bytes. This means that when strings are used they are implicitly converted to the default encoding before being stored. diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 0d0d1ca70e6c..b38a1a767c68 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -483,6 +483,12 @@ Changes in the Python API external entities by default. (Contributed by Christian Heimes in :issue:`17239`.) +* Deleting a key from a read-only :mod:`dbm` database (:mod:`dbm.dumb`, + :mod:`dbm.gnu` or :mod:`dbm.ndbm`) raises :attr:`error` (:exc:`dbm.dumb.error`, + :exc:`dbm.gnu.error` or :exc:`dbm.ndbm.error`) instead of :exc:`KeyError`. + (Contributed by Xiang Zhang in :issue:`33106`.) + + CPython bytecode changes ------------------------ diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py index e5c17f5ae2ed..6cef72a49acc 100644 --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -185,7 +185,7 @@ def _addkey(self, key, pos_and_siz_pair): def __setitem__(self, key, val): if self._readonly: - raise ValueError('The database is opened for reading only') + raise error('The database is opened for reading only') if isinstance(key, str): key = key.encode('utf-8') elif not isinstance(key, (bytes, bytearray)): @@ -222,7 +222,7 @@ def __setitem__(self, key, val): def __delitem__(self, key): if self._readonly: - raise ValueError('The database is opened for reading only') + raise error('The database is opened for reading only') if isinstance(key, str): key = key.encode('utf-8') self._verify_open() diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index 58b9d1746568..e8e827e5f5bc 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -82,10 +82,10 @@ def test_dumbdbm_read(self): self.init_db() f = dumbdbm.open(_fname, 'r') self.read_helper(f) - with self.assertRaisesRegex(ValueError, + with self.assertRaisesRegex(dumbdbm.error, 'The database is opened for reading only'): f[b'g'] = b'x' - with self.assertRaisesRegex(ValueError, + with self.assertRaisesRegex(dumbdbm.error, 'The database is opened for reading only'): del f[b'a'] # get() works as in the dict interface diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py index 16b7fe6fac17..f1c7d34085c5 100644 --- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -131,6 +131,17 @@ def test_unicode(self): self.assertEqual(db['Unicode key \U0001f40d'], 'Unicode value \U0001f40d'.encode()) + def test_write_readonly_file(self): + with gdbm.open(filename, 'c') as db: + db[b'bytes key'] = b'bytes value' + with gdbm.open(filename, 'r') as db: + with self.assertRaises(gdbm.error): + del db[b'not exist key'] + with self.assertRaises(gdbm.error): + del db[b'bytes key'] + with self.assertRaises(gdbm.error): + db[b'not exist key'] = b'not exist value' + @unittest.skipUnless(TESTFN_NONASCII, 'requires OS support of non-ASCII encodings') def test_nonascii_filename(self): diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py index bd411da652e6..7ac75c5fea1b 100644 --- a/Lib/test/test_dbm_ndbm.py +++ b/Lib/test/test_dbm_ndbm.py @@ -90,6 +90,17 @@ def test_unicode(self): self.assertEqual(db['Unicode key \U0001f40d'], 'Unicode value \U0001f40d'.encode()) + def test_write_readonly_file(self): + with dbm.ndbm.open(self.filename, 'c') as db: + db[b'bytes key'] = b'bytes value' + with dbm.ndbm.open(self.filename, 'r') as db: + with self.assertRaises(error): + del db[b'not exist key'] + with self.assertRaises(error): + del db[b'bytes key'] + with self.assertRaises(error): + db[b'not exist key'] = b'not exist value' + @unittest.skipUnless(support.TESTFN_NONASCII, 'requires OS support of non-ASCII encodings') def test_nonascii_filename(self): diff --git a/Misc/NEWS.d/next/Library/2018-03-30-01-20-35.bpo-33106.zncfvW.rst b/Misc/NEWS.d/next/Library/2018-03-30-01-20-35.bpo-33106.zncfvW.rst new file mode 100644 index 000000000000..859c09cd58f3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-03-30-01-20-35.bpo-33106.zncfvW.rst @@ -0,0 +1,2 @@ +Deleting a key from a read-only dbm database raises module specfic error +instead of KeyError. diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 081184a0b322..21784ae2ce4f 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -36,6 +36,7 @@ class _dbm.dbm "dbmobject *" "&Dbmtype" typedef struct { PyObject_HEAD + int flags; int di_size; /* -1 means recompute */ DBM *di_dbm; } dbmobject; @@ -60,6 +61,7 @@ newdbmobject(const char *file, int flags, int mode) if (dp == NULL) return NULL; dp->di_size = -1; + dp->flags = flags; /* See issue #19296 */ if ( (dp->di_dbm = dbm_open((char *)file, flags, mode)) == 0 ) { PyErr_SetFromErrnoWithFilename(DbmError, file); @@ -143,13 +145,20 @@ dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) if (w == NULL) { if ( dbm_delete(dp->di_dbm, krec) < 0 ) { dbm_clearerr(dp->di_dbm); - PyErr_SetObject(PyExc_KeyError, v); + /* we might get a failure for reasons like file corrupted, + but we are not able to distinguish it */ + if (dp->flags & O_RDWR) { + PyErr_SetObject(PyExc_KeyError, v); + } + else { + PyErr_SetString(DbmError, "cannot delete item from database"); + } return -1; } } else { if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { PyErr_SetString(PyExc_TypeError, - "dbm mappings have byte or string elements only"); + "dbm mappings have bytes or string elements only"); return -1; } drec.dsize = tmp_size; @@ -335,7 +344,7 @@ _dbm_dbm_setdefault_impl(dbmobject *self, const char *key, else { if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) { PyErr_SetString(PyExc_TypeError, - "dbm mappings have byte string elements only"); + "dbm mappings have bytes or string elements only"); return NULL; } val.dsize = tmp_size; diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 46bb59ac9521..cc94e60745c8 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -186,14 +186,19 @@ dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) dp->di_size = -1; if (w == NULL) { if (gdbm_delete(dp->di_dbm, krec) < 0) { - PyErr_SetObject(PyExc_KeyError, v); + if (gdbm_errno == GDBM_ITEM_NOT_FOUND) { + PyErr_SetObject(PyExc_KeyError, v); + } + else { + PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); + } return -1; } } else { if (!PyArg_Parse(w, "s#", &drec.dptr, &drec.dsize)) { PyErr_SetString(PyExc_TypeError, - "gdbm mappings have byte or string elements only"); + "gdbm mappings have bytes or string elements only"); return -1; } errno = 0; From webhook-mailer at python.org Wed Dec 12 11:48:13 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 12 Dec 2018 16:48:13 -0000 Subject: [Python-checkins] bpo-35346: Drop Mac OS 9 support from platform (GH-10959) Message-ID: https://github.com/python/cpython/commit/b0e0877629e3df4bc3042fd424e96f197b2e9fa4 commit: b0e0877629e3df4bc3042fd424e96f197b2e9fa4 branch: master author: Victor Stinner committer: GitHub date: 2018-12-12T17:48:08+01:00 summary: bpo-35346: Drop Mac OS 9 support from platform (GH-10959) Drop Mac OS 9 and Rhapsody support from the platform module: * Rhapsody: last release in 2000 * Mac OS 9: last release in 2001 files: A Misc/NEWS.d/next/Library/2018-12-05-22-52-21.bpo-35346.Okm9-S.rst M Lib/platform.py diff --git a/Lib/platform.py b/Lib/platform.py index 5f9491819169..ab0cf254bc69 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -406,7 +406,7 @@ def _mac_ver_xml(): def mac_ver(release='', versioninfo=('', '', ''), machine=''): - """ Get MacOS version information and return it as tuple (release, + """ Get macOS version information and return it as tuple (release, versioninfo, machine) with versioninfo being a tuple (version, dev_stage, non_release_version). @@ -478,12 +478,7 @@ def system_alias(system, release, version): where it would otherwise cause confusion. """ - if system == 'Rhapsody': - # Apple's BSD derivative - # XXX How can we determine the marketing release number ? - return 'MacOS X Server', system+release, version - - elif system == 'SunOS': + if system == 'SunOS': # Sun's OS if release < '5': # These releases use the old name SunOS @@ -1166,7 +1161,6 @@ def platform(aliased=0, terse=0): # macOS (darwin kernel) macos_release = mac_ver()[0] if macos_release: - # note: 'macOS' is different than 'MacOS' used below system = 'macOS' release = macos_release @@ -1194,13 +1188,6 @@ def platform(aliased=0, terse=0): 'on', os_name, os_version, os_arch) - elif system == 'MacOS': - # MacOS platforms - if terse: - platform = _platform(system, release) - else: - platform = _platform(system, release, machine) - else: # Generic handler if terse: diff --git a/Misc/NEWS.d/next/Library/2018-12-05-22-52-21.bpo-35346.Okm9-S.rst b/Misc/NEWS.d/next/Library/2018-12-05-22-52-21.bpo-35346.Okm9-S.rst new file mode 100644 index 000000000000..047a1c82b8f2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-05-22-52-21.bpo-35346.Okm9-S.rst @@ -0,0 +1,2 @@ +Drop Mac OS 9 and Rhapsody support from the :mod:`platform` module. Rhapsody +last release was in 2000. Mac OS 9 last release was in 2001. From webhook-mailer at python.org Wed Dec 12 12:38:39 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 12 Dec 2018 17:38:39 -0000 Subject: [Python-checkins] bpo-35412: Add testcase to test_future4 (GH-11131) Message-ID: https://github.com/python/cpython/commit/502fe19b10f66235fcf8f13fc1c0308190845def commit: 502fe19b10f66235fcf8f13fc1c0308190845def branch: master author: Victor Stinner committer: GitHub date: 2018-12-12T18:38:34+01:00 summary: bpo-35412: Add testcase to test_future4 (GH-11131) Add testcase to test_future4: check unicode literal. files: A Misc/NEWS.d/next/Tests/2018-12-12-18-07-58.bpo-35412.kbuJor.rst M Lib/test/test_future4.py diff --git a/Lib/test/test_future4.py b/Lib/test/test_future4.py index 413dd4d96b03..b27ca40d2eae 100644 --- a/Lib/test/test_future4.py +++ b/Lib/test/test_future4.py @@ -1,6 +1,11 @@ from __future__ import unicode_literals - import unittest + +class Tests(unittest.TestCase): + def test_unicode_literals(self): + self.assertIsInstance("literal", str) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2018-12-12-18-07-58.bpo-35412.kbuJor.rst b/Misc/NEWS.d/next/Tests/2018-12-12-18-07-58.bpo-35412.kbuJor.rst new file mode 100644 index 000000000000..d696074fb734 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-12-18-07-58.bpo-35412.kbuJor.rst @@ -0,0 +1 @@ +Add testcase to ``test_future4``: check unicode literal. From webhook-mailer at python.org Wed Dec 12 20:15:35 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 13 Dec 2018 01:15:35 -0000 Subject: [Python-checkins] bpo-35477: multiprocessing.Pool.__enter__() fails if called twice (GH-11134) Message-ID: https://github.com/python/cpython/commit/08c2ba0717089662132af69bf5948d82277a8a69 commit: 08c2ba0717089662132af69bf5948d82277a8a69 branch: master author: Victor Stinner committer: GitHub date: 2018-12-13T02:15:30+01:00 summary: bpo-35477: multiprocessing.Pool.__enter__() fails if called twice (GH-11134) multiprocessing.Pool.__enter__() now fails if the pool is not running: "with pool:" fails if used more than once. files: A Misc/NEWS.d/next/Library/2018-12-13-00-10-51.bpo-35477.hHyy06.rst M Lib/multiprocessing/pool.py M Lib/test/_test_multiprocessing.py diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py index 2b3cc59a9ff8..c0775418852d 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -261,6 +261,10 @@ def _setup_queues(self): self._quick_put = self._inqueue._writer.send self._quick_get = self._outqueue._reader.recv + def _check_running(self): + if self._state != RUN: + raise ValueError("Pool not running") + def apply(self, func, args=(), kwds={}): ''' Equivalent of `func(*args, **kwds)`. @@ -306,8 +310,7 @@ def imap(self, func, iterable, chunksize=1): ''' Equivalent of `map()` -- can be MUCH slower than `Pool.map()`. ''' - if self._state != RUN: - raise ValueError("Pool not running") + self._check_running() if chunksize == 1: result = IMapIterator(self._cache) self._taskqueue.put( @@ -336,8 +339,7 @@ def imap_unordered(self, func, iterable, chunksize=1): ''' Like `imap()` method but ordering of results is arbitrary. ''' - if self._state != RUN: - raise ValueError("Pool not running") + self._check_running() if chunksize == 1: result = IMapUnorderedIterator(self._cache) self._taskqueue.put( @@ -366,8 +368,7 @@ def apply_async(self, func, args=(), kwds={}, callback=None, ''' Asynchronous version of `apply()` method. ''' - if self._state != RUN: - raise ValueError("Pool not running") + self._check_running() result = ApplyResult(self._cache, callback, error_callback) self._taskqueue.put(([(result._job, 0, func, args, kwds)], None)) return result @@ -385,8 +386,7 @@ def _map_async(self, func, iterable, mapper, chunksize=None, callback=None, ''' Helper function to implement map, starmap and their async counterparts. ''' - if self._state != RUN: - raise ValueError("Pool not running") + self._check_running() if not hasattr(iterable, '__len__'): iterable = list(iterable) @@ -625,6 +625,7 @@ def _terminate_pool(cls, taskqueue, inqueue, outqueue, pool, p.join() def __enter__(self): + self._check_running() return self def __exit__(self, exc_type, exc_val, exc_tb): diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 0b0fe7c9b298..a2dc53c8e4e6 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2561,6 +2561,22 @@ def test_release_task_refs(self): # they were released too. self.assertEqual(CountedObject.n_instances, 0) + def test_enter(self): + if self.TYPE == 'manager': + self.skipTest("test not applicable to manager") + + pool = self.Pool(1) + with pool: + pass + # call pool.terminate() + # pool is no longer running + + with self.assertRaises(ValueError): + # bpo-35477: pool.__enter__() fails if the pool is not running + with pool: + pass + pool.join() + def raising(): raise KeyError("key") diff --git a/Misc/NEWS.d/next/Library/2018-12-13-00-10-51.bpo-35477.hHyy06.rst b/Misc/NEWS.d/next/Library/2018-12-13-00-10-51.bpo-35477.hHyy06.rst new file mode 100644 index 000000000000..524df71ed951 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-13-00-10-51.bpo-35477.hHyy06.rst @@ -0,0 +1,2 @@ +:meth:`multiprocessing.Pool.__enter__` now fails if the pool is not running: +``with pool:`` fails if used more than once. From solipsis at pitrou.net Thu Dec 13 04:08:51 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 13 Dec 2018 09:08:51 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=7 Message-ID: <20181213090851.1.F409402FD25AAC6E@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_asyncio leaked [3, 0, 0] memory blocks, sum=3 test_collections leaked [7, 0, -7] memory blocks, sum=0 test_functools leaked [0, 3, 1] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogETjzok', '--timeout', '7200'] From webhook-mailer at python.org Fri Dec 14 03:30:58 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 14 Dec 2018 08:30:58 -0000 Subject: [Python-checkins] bpo-31446: Copy command line that should be passed to CreateProcessW(). (GH-11141) Message-ID: https://github.com/python/cpython/commit/7b36016a15aeed0d76a4c05a66203e6d7723aace commit: 7b36016a15aeed0d76a4c05a66203e6d7723aace branch: master author: Vladimir Matveev committer: Serhiy Storchaka date: 2018-12-14T10:30:51+02:00 summary: bpo-31446: Copy command line that should be passed to CreateProcessW(). (GH-11141) files: A Misc/NEWS.d/next/Library/2018-12-12-22-52-34.bpo-31446.l--Fjz.rst M Modules/_winapi.c M Modules/clinic/_winapi.c.h diff --git a/Misc/NEWS.d/next/Library/2018-12-12-22-52-34.bpo-31446.l--Fjz.rst b/Misc/NEWS.d/next/Library/2018-12-12-22-52-34.bpo-31446.l--Fjz.rst new file mode 100644 index 000000000000..741263f16bb4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-12-22-52-34.bpo-31446.l--Fjz.rst @@ -0,0 +1,2 @@ +Copy command line that was passed to CreateProcessW since this function can +change the content of the input buffer. diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 44788e5992a0..852e0a7d7b48 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -975,7 +975,8 @@ getattributelist(PyObject *obj, const char *name, AttributeList *attribute_list) _winapi.CreateProcess application_name: Py_UNICODE(accept={str, NoneType}) - command_line: Py_UNICODE(accept={str, NoneType}) + command_line: object + Can be str or None proc_attrs: object Ignored internally, can be None. thread_attrs: object @@ -995,12 +996,12 @@ process ID, and thread ID. static PyObject * _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, - Py_UNICODE *command_line, PyObject *proc_attrs, + PyObject *command_line, PyObject *proc_attrs, PyObject *thread_attrs, BOOL inherit_handles, DWORD creation_flags, PyObject *env_mapping, Py_UNICODE *current_directory, PyObject *startup_info) -/*[clinic end generated code: output=4652a33aff4b0ae1 input=4a43b05038d639bb]*/ +/*[clinic end generated code: output=2ecaab46a05e3123 input=42ac293eaea03fc4]*/ { PyObject *ret = NULL; BOOL result; @@ -1008,6 +1009,7 @@ _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, STARTUPINFOEXW si; PyObject *environment = NULL; wchar_t *wenvironment; + wchar_t *command_line_copy = NULL; AttributeList attribute_list = {0}; ZeroMemory(&si, sizeof(si)); @@ -1042,10 +1044,23 @@ _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, goto cleanup; si.lpAttributeList = attribute_list.attribute_list; + if (PyUnicode_Check(command_line)) { + command_line_copy = PyUnicode_AsWideCharString(command_line, NULL); + if (command_line_copy == NULL) { + goto cleanup; + } + } + else if (command_line != Py_None) { + PyErr_Format(PyExc_TypeError, + "CreateProcess() argument 2 must be str or None, not %s", + Py_TYPE(command_line)->tp_name); + goto cleanup; + } + Py_BEGIN_ALLOW_THREADS result = CreateProcessW(application_name, - command_line, + command_line_copy, NULL, NULL, inherit_handles, @@ -1069,6 +1084,7 @@ _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, pi.dwThreadId); cleanup: + PyMem_Free(command_line_copy); Py_XDECREF(environment); freeattributelist(&attribute_list); diff --git a/Modules/clinic/_winapi.c.h b/Modules/clinic/_winapi.c.h index f82b46d7381c..241c18e5a252 100644 --- a/Modules/clinic/_winapi.c.h +++ b/Modules/clinic/_winapi.c.h @@ -286,6 +286,8 @@ PyDoc_STRVAR(_winapi_CreateProcess__doc__, "\n" "Create a new process and its primary thread.\n" "\n" +" command_line\n" +" Can be str or None\n" " proc_attrs\n" " Ignored internally, can be None.\n" " thread_attrs\n" @@ -299,7 +301,7 @@ PyDoc_STRVAR(_winapi_CreateProcess__doc__, static PyObject * _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, - Py_UNICODE *command_line, PyObject *proc_attrs, + PyObject *command_line, PyObject *proc_attrs, PyObject *thread_attrs, BOOL inherit_handles, DWORD creation_flags, PyObject *env_mapping, Py_UNICODE *current_directory, @@ -310,7 +312,7 @@ _winapi_CreateProcess(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; Py_UNICODE *application_name; - Py_UNICODE *command_line; + PyObject *command_line; PyObject *proc_attrs; PyObject *thread_attrs; BOOL inherit_handles; @@ -319,7 +321,7 @@ _winapi_CreateProcess(PyObject *module, PyObject *const *args, Py_ssize_t nargs) Py_UNICODE *current_directory; PyObject *startup_info; - if (!_PyArg_ParseStack(args, nargs, "ZZOOikOZO:CreateProcess", + if (!_PyArg_ParseStack(args, nargs, "ZOOOikOZO:CreateProcess", &application_name, &command_line, &proc_attrs, &thread_attrs, &inherit_handles, &creation_flags, &env_mapping, ¤t_directory, &startup_info)) { goto exit; } @@ -941,4 +943,4 @@ _winapi_GetFileType(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P exit: return return_value; } -/*[clinic end generated code: output=915dd640329de0c0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1568ad4bd625f2af input=a9049054013a1b77]*/ From solipsis at pitrou.net Fri Dec 14 04:11:20 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 14 Dec 2018 09:11:20 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=6 Message-ID: <20181214091120.1.DEC2C129BBDAFBD4@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_asyncio leaked [0, 0, 3] memory blocks, sum=3 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_spawn leaked [-2, 2, -1] memory blocks, sum=-1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogF9jXqY', '--timeout', '7200'] From webhook-mailer at python.org Fri Dec 14 04:18:22 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 14 Dec 2018 09:18:22 -0000 Subject: [Python-checkins] [3.7] bpo-31446: Copy command line that should be passed to CreateProcessW(). (GH-11141). (GH-11149) Message-ID: https://github.com/python/cpython/commit/922b2a0d0d9928af420ea4d5c104f8be72517aa2 commit: 922b2a0d0d9928af420ea4d5c104f8be72517aa2 branch: 3.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-14T11:18:13+02:00 summary: [3.7] bpo-31446: Copy command line that should be passed to CreateProcessW(). (GH-11141). (GH-11149) (cherry picked from commit 7b36016a15aeed0d76a4c05a66203e6d7723aace) Co-authored-by: Vladimir Matveev files: A Misc/NEWS.d/next/Library/2018-12-12-22-52-34.bpo-31446.l--Fjz.rst M Modules/_winapi.c M Modules/clinic/_winapi.c.h diff --git a/Misc/NEWS.d/next/Library/2018-12-12-22-52-34.bpo-31446.l--Fjz.rst b/Misc/NEWS.d/next/Library/2018-12-12-22-52-34.bpo-31446.l--Fjz.rst new file mode 100644 index 000000000000..741263f16bb4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-12-22-52-34.bpo-31446.l--Fjz.rst @@ -0,0 +1,2 @@ +Copy command line that was passed to CreateProcessW since this function can +change the content of the input buffer. diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 9ea5a9266295..127f8886e210 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -974,7 +974,8 @@ getattributelist(PyObject *obj, const char *name, AttributeList *attribute_list) _winapi.CreateProcess application_name: Py_UNICODE(accept={str, NoneType}) - command_line: Py_UNICODE(accept={str, NoneType}) + command_line: object + Can be str or None proc_attrs: object Ignored internally, can be None. thread_attrs: object @@ -994,12 +995,12 @@ process ID, and thread ID. static PyObject * _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, - Py_UNICODE *command_line, PyObject *proc_attrs, + PyObject *command_line, PyObject *proc_attrs, PyObject *thread_attrs, BOOL inherit_handles, DWORD creation_flags, PyObject *env_mapping, Py_UNICODE *current_directory, PyObject *startup_info) -/*[clinic end generated code: output=4652a33aff4b0ae1 input=4a43b05038d639bb]*/ +/*[clinic end generated code: output=2ecaab46a05e3123 input=42ac293eaea03fc4]*/ { PyObject *ret = NULL; BOOL result; @@ -1007,6 +1008,7 @@ _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, STARTUPINFOEXW si; PyObject *environment = NULL; wchar_t *wenvironment; + wchar_t *command_line_copy = NULL; AttributeList attribute_list = {0}; ZeroMemory(&si, sizeof(si)); @@ -1041,10 +1043,23 @@ _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, goto cleanup; si.lpAttributeList = attribute_list.attribute_list; + if (PyUnicode_Check(command_line)) { + command_line_copy = PyUnicode_AsWideCharString(command_line, NULL); + if (command_line_copy == NULL) { + goto cleanup; + } + } + else if (command_line != Py_None) { + PyErr_Format(PyExc_TypeError, + "CreateProcess() argument 2 must be str or None, not %s", + Py_TYPE(command_line)->tp_name); + goto cleanup; + } + Py_BEGIN_ALLOW_THREADS result = CreateProcessW(application_name, - command_line, + command_line_copy, NULL, NULL, inherit_handles, @@ -1068,6 +1083,7 @@ _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, pi.dwThreadId); cleanup: + PyMem_Free(command_line_copy); Py_XDECREF(environment); freeattributelist(&attribute_list); diff --git a/Modules/clinic/_winapi.c.h b/Modules/clinic/_winapi.c.h index c66522ebab6d..436fbe54947a 100644 --- a/Modules/clinic/_winapi.c.h +++ b/Modules/clinic/_winapi.c.h @@ -286,6 +286,8 @@ PyDoc_STRVAR(_winapi_CreateProcess__doc__, "\n" "Create a new process and its primary thread.\n" "\n" +" command_line\n" +" Can be str or None\n" " proc_attrs\n" " Ignored internally, can be None.\n" " thread_attrs\n" @@ -299,7 +301,7 @@ PyDoc_STRVAR(_winapi_CreateProcess__doc__, static PyObject * _winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, - Py_UNICODE *command_line, PyObject *proc_attrs, + PyObject *command_line, PyObject *proc_attrs, PyObject *thread_attrs, BOOL inherit_handles, DWORD creation_flags, PyObject *env_mapping, Py_UNICODE *current_directory, @@ -310,7 +312,7 @@ _winapi_CreateProcess(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; Py_UNICODE *application_name; - Py_UNICODE *command_line; + PyObject *command_line; PyObject *proc_attrs; PyObject *thread_attrs; BOOL inherit_handles; @@ -319,7 +321,7 @@ _winapi_CreateProcess(PyObject *module, PyObject *const *args, Py_ssize_t nargs) Py_UNICODE *current_directory; PyObject *startup_info; - if (!_PyArg_ParseStack(args, nargs, "ZZOOikOZO:CreateProcess", + if (!_PyArg_ParseStack(args, nargs, "ZOOOikOZO:CreateProcess", &application_name, &command_line, &proc_attrs, &thread_attrs, &inherit_handles, &creation_flags, &env_mapping, ¤t_directory, &startup_info)) { goto exit; } @@ -941,4 +943,4 @@ _winapi_GetFileType(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P exit: return return_value; } -/*[clinic end generated code: output=baaf3d379b91be0a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3a6492395b11b5a5 input=a9049054013a1b77]*/ From webhook-mailer at python.org Fri Dec 14 04:19:57 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 14 Dec 2018 09:19:57 -0000 Subject: [Python-checkins] bpo-35489: Use "const Py_UNICODE *" for the Py_UNICODE converter in AC. (GH-11150) Message-ID: https://github.com/python/cpython/commit/afb3e71a1710c444fbe789b51df43ee16ee9ede7 commit: afb3e71a1710c444fbe789b51df43ee16ee9ede7 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-14T11:19:51+02:00 summary: bpo-35489: Use "const Py_UNICODE *" for the Py_UNICODE converter in AC. (GH-11150) files: M Modules/_winapi.c M Modules/arraymodule.c M Modules/clinic/_winapi.c.h M Modules/clinic/arraymodule.c.h M Modules/clinic/posixmodule.c.h M Modules/posixmodule.c M PC/clinic/winreg.c.h M PC/winreg.c M Tools/clinic/clinic.py diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 852e0a7d7b48..cdb45c23e787 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -995,13 +995,14 @@ process ID, and thread ID. [clinic start generated code]*/ static PyObject * -_winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, +_winapi_CreateProcess_impl(PyObject *module, + const Py_UNICODE *application_name, PyObject *command_line, PyObject *proc_attrs, PyObject *thread_attrs, BOOL inherit_handles, DWORD creation_flags, PyObject *env_mapping, - Py_UNICODE *current_directory, + const Py_UNICODE *current_directory, PyObject *startup_info) -/*[clinic end generated code: output=2ecaab46a05e3123 input=42ac293eaea03fc4]*/ +/*[clinic end generated code: output=9b2423a609230132 input=42ac293eaea03fc4]*/ { PyObject *ret = NULL; BOOL result; diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 1bfc4dd95bfd..f5f461b75648 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1712,9 +1712,9 @@ some other type. [clinic start generated code]*/ static PyObject * -array_array_fromunicode_impl(arrayobject *self, Py_UNICODE *ustr, +array_array_fromunicode_impl(arrayobject *self, const Py_UNICODE *ustr, Py_ssize_clean_t ustr_length) -/*[clinic end generated code: output=ebb72fc16975e06d input=150f00566ffbca6e]*/ +/*[clinic end generated code: output=cf2f662908e2befc input=150f00566ffbca6e]*/ { char typecode; diff --git a/Modules/clinic/_winapi.c.h b/Modules/clinic/_winapi.c.h index 241c18e5a252..79d85ff6bb0b 100644 --- a/Modules/clinic/_winapi.c.h +++ b/Modules/clinic/_winapi.c.h @@ -300,25 +300,26 @@ PyDoc_STRVAR(_winapi_CreateProcess__doc__, {"CreateProcess", (PyCFunction)(void(*)(void))_winapi_CreateProcess, METH_FASTCALL, _winapi_CreateProcess__doc__}, static PyObject * -_winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, +_winapi_CreateProcess_impl(PyObject *module, + const Py_UNICODE *application_name, PyObject *command_line, PyObject *proc_attrs, PyObject *thread_attrs, BOOL inherit_handles, DWORD creation_flags, PyObject *env_mapping, - Py_UNICODE *current_directory, + const Py_UNICODE *current_directory, PyObject *startup_info); static PyObject * _winapi_CreateProcess(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_UNICODE *application_name; + const Py_UNICODE *application_name; PyObject *command_line; PyObject *proc_attrs; PyObject *thread_attrs; BOOL inherit_handles; DWORD creation_flags; PyObject *env_mapping; - Py_UNICODE *current_directory; + const Py_UNICODE *current_directory; PyObject *startup_info; if (!_PyArg_ParseStack(args, nargs, "ZOOOikOZO:CreateProcess", @@ -943,4 +944,4 @@ _winapi_GetFileType(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P exit: return return_value; } -/*[clinic end generated code: output=1568ad4bd625f2af input=a9049054013a1b77]*/ +/*[clinic end generated code: output=145d0d362167c1b1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/arraymodule.c.h b/Modules/clinic/arraymodule.c.h index aa9938868f41..0d1008743e12 100644 --- a/Modules/clinic/arraymodule.c.h +++ b/Modules/clinic/arraymodule.c.h @@ -376,14 +376,14 @@ PyDoc_STRVAR(array_array_fromunicode__doc__, {"fromunicode", (PyCFunction)array_array_fromunicode, METH_O, array_array_fromunicode__doc__}, static PyObject * -array_array_fromunicode_impl(arrayobject *self, Py_UNICODE *ustr, +array_array_fromunicode_impl(arrayobject *self, const Py_UNICODE *ustr, Py_ssize_clean_t ustr_length); static PyObject * array_array_fromunicode(arrayobject *self, PyObject *arg) { PyObject *return_value = NULL; - Py_UNICODE *ustr; + const Py_UNICODE *ustr; Py_ssize_clean_t ustr_length; if (!PyArg_Parse(arg, "u#:fromunicode", &ustr, &ustr_length)) { @@ -505,4 +505,4 @@ PyDoc_STRVAR(array_arrayiterator___setstate____doc__, #define ARRAY_ARRAYITERATOR___SETSTATE___METHODDEF \ {"__setstate__", (PyCFunction)array_arrayiterator___setstate__, METH_O, array_arrayiterator___setstate____doc__}, -/*[clinic end generated code: output=3a4c6f3deb597bfd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3d2bb1aa81541cbd input=a9049054013a1b77]*/ diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 80690703ee2c..6d523bcc9ff4 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1329,7 +1329,7 @@ PyDoc_STRVAR(os_system__doc__, {"system", (PyCFunction)(void(*)(void))os_system, METH_FASTCALL|METH_KEYWORDS, os_system__doc__}, static long -os_system_impl(PyObject *module, Py_UNICODE *command); +os_system_impl(PyObject *module, const Py_UNICODE *command); static PyObject * os_system(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -1337,7 +1337,7 @@ os_system(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyObject *return_value = NULL; static const char * const _keywords[] = {"command", NULL}; static _PyArg_Parser _parser = {"u:system", _keywords, 0}; - Py_UNICODE *command; + const Py_UNICODE *command; long _return_value; if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, @@ -5327,7 +5327,8 @@ PyDoc_STRVAR(os_startfile__doc__, {"startfile", (PyCFunction)(void(*)(void))os_startfile, METH_FASTCALL|METH_KEYWORDS, os_startfile__doc__}, static PyObject * -os_startfile_impl(PyObject *module, path_t *filepath, Py_UNICODE *operation); +os_startfile_impl(PyObject *module, path_t *filepath, + const Py_UNICODE *operation); static PyObject * os_startfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -5336,7 +5337,7 @@ os_startfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject static const char * const _keywords[] = {"filepath", "operation", NULL}; static _PyArg_Parser _parser = {"O&|u:startfile", _keywords, 0}; path_t filepath = PATH_T_INITIALIZE("startfile", "filepath", 0, 0); - Py_UNICODE *operation = NULL; + const Py_UNICODE *operation = NULL; if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, path_converter, &filepath, &operation)) { @@ -6757,4 +6758,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject #ifndef OS_GETRANDOM_METHODDEF #define OS_GETRANDOM_METHODDEF #endif /* !defined(OS_GETRANDOM_METHODDEF) */ -/*[clinic end generated code: output=d62c0bb988141e70 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7ebb53d872bab149 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d05724a50d1c..0ea391e799b9 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4215,8 +4215,8 @@ Execute the command in a subshell. [clinic start generated code]*/ static long -os_system_impl(PyObject *module, Py_UNICODE *command) -/*[clinic end generated code: output=96c4dffee36dfb48 input=303f5ce97df606b0]*/ +os_system_impl(PyObject *module, const Py_UNICODE *command) +/*[clinic end generated code: output=5b7c3599c068ca42 input=303f5ce97df606b0]*/ { long result; Py_BEGIN_ALLOW_THREADS @@ -11224,8 +11224,9 @@ the underlying Win32 ShellExecute function doesn't work if it is. [clinic start generated code]*/ static PyObject * -os_startfile_impl(PyObject *module, path_t *filepath, Py_UNICODE *operation) -/*[clinic end generated code: output=912ceba79acfa1c9 input=63950bf2986380d0]*/ +os_startfile_impl(PyObject *module, path_t *filepath, + const Py_UNICODE *operation) +/*[clinic end generated code: output=66dc311c94d50797 input=63950bf2986380d0]*/ { HINSTANCE rc; diff --git a/PC/clinic/winreg.c.h b/PC/clinic/winreg.c.h index 1a5356335521..a7385a6c693c 100644 --- a/PC/clinic/winreg.c.h +++ b/PC/clinic/winreg.c.h @@ -137,14 +137,14 @@ PyDoc_STRVAR(winreg_ConnectRegistry__doc__, {"ConnectRegistry", (PyCFunction)(void(*)(void))winreg_ConnectRegistry, METH_FASTCALL, winreg_ConnectRegistry__doc__}, static HKEY -winreg_ConnectRegistry_impl(PyObject *module, Py_UNICODE *computer_name, - HKEY key); +winreg_ConnectRegistry_impl(PyObject *module, + const Py_UNICODE *computer_name, HKEY key); static PyObject * winreg_ConnectRegistry(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_UNICODE *computer_name; + const Py_UNICODE *computer_name; HKEY key; HKEY _return_value; @@ -185,14 +185,14 @@ PyDoc_STRVAR(winreg_CreateKey__doc__, {"CreateKey", (PyCFunction)(void(*)(void))winreg_CreateKey, METH_FASTCALL, winreg_CreateKey__doc__}, static HKEY -winreg_CreateKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key); +winreg_CreateKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key); static PyObject * winreg_CreateKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; HKEY _return_value; if (!_PyArg_ParseStack(args, nargs, "O&Z:CreateKey", @@ -238,8 +238,9 @@ PyDoc_STRVAR(winreg_CreateKeyEx__doc__, {"CreateKeyEx", (PyCFunction)(void(*)(void))winreg_CreateKeyEx, METH_FASTCALL|METH_KEYWORDS, winreg_CreateKeyEx__doc__}, static HKEY -winreg_CreateKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - int reserved, REGSAM access); +winreg_CreateKeyEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *sub_key, int reserved, + REGSAM access); static PyObject * winreg_CreateKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -248,7 +249,7 @@ winreg_CreateKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py static const char * const _keywords[] = {"key", "sub_key", "reserved", "access", NULL}; static _PyArg_Parser _parser = {"O&Z|ii:CreateKeyEx", _keywords, 0}; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; int reserved = 0; REGSAM access = KEY_WRITE; HKEY _return_value; @@ -289,14 +290,14 @@ PyDoc_STRVAR(winreg_DeleteKey__doc__, {"DeleteKey", (PyCFunction)(void(*)(void))winreg_DeleteKey, METH_FASTCALL, winreg_DeleteKey__doc__}, static PyObject * -winreg_DeleteKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key); +winreg_DeleteKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key); static PyObject * winreg_DeleteKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; if (!_PyArg_ParseStack(args, nargs, "O&u:DeleteKey", clinic_HKEY_converter, &key, &sub_key)) { @@ -337,8 +338,9 @@ PyDoc_STRVAR(winreg_DeleteKeyEx__doc__, {"DeleteKeyEx", (PyCFunction)(void(*)(void))winreg_DeleteKeyEx, METH_FASTCALL|METH_KEYWORDS, winreg_DeleteKeyEx__doc__}, static PyObject * -winreg_DeleteKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - REGSAM access, int reserved); +winreg_DeleteKeyEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *sub_key, REGSAM access, + int reserved); static PyObject * winreg_DeleteKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -347,7 +349,7 @@ winreg_DeleteKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py static const char * const _keywords[] = {"key", "sub_key", "access", "reserved", NULL}; static _PyArg_Parser _parser = {"O&u|ii:DeleteKeyEx", _keywords, 0}; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; REGSAM access = KEY_WOW64_64KEY; int reserved = 0; @@ -376,14 +378,14 @@ PyDoc_STRVAR(winreg_DeleteValue__doc__, {"DeleteValue", (PyCFunction)(void(*)(void))winreg_DeleteValue, METH_FASTCALL, winreg_DeleteValue__doc__}, static PyObject * -winreg_DeleteValue_impl(PyObject *module, HKEY key, Py_UNICODE *value); +winreg_DeleteValue_impl(PyObject *module, HKEY key, const Py_UNICODE *value); static PyObject * winreg_DeleteValue(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *value; + const Py_UNICODE *value; if (!_PyArg_ParseStack(args, nargs, "O&Z:DeleteValue", clinic_HKEY_converter, &key, &value)) { @@ -490,13 +492,14 @@ PyDoc_STRVAR(winreg_ExpandEnvironmentStrings__doc__, {"ExpandEnvironmentStrings", (PyCFunction)winreg_ExpandEnvironmentStrings, METH_O, winreg_ExpandEnvironmentStrings__doc__}, static PyObject * -winreg_ExpandEnvironmentStrings_impl(PyObject *module, Py_UNICODE *string); +winreg_ExpandEnvironmentStrings_impl(PyObject *module, + const Py_UNICODE *string); static PyObject * winreg_ExpandEnvironmentStrings(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - Py_UNICODE *string; + const Py_UNICODE *string; if (!PyArg_Parse(arg, "u:ExpandEnvironmentStrings", &string)) { goto exit; @@ -579,16 +582,16 @@ PyDoc_STRVAR(winreg_LoadKey__doc__, {"LoadKey", (PyCFunction)(void(*)(void))winreg_LoadKey, METH_FASTCALL, winreg_LoadKey__doc__}, static PyObject * -winreg_LoadKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - Py_UNICODE *file_name); +winreg_LoadKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, + const Py_UNICODE *file_name); static PyObject * winreg_LoadKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; - Py_UNICODE *file_name; + const Py_UNICODE *sub_key; + const Py_UNICODE *file_name; if (!_PyArg_ParseStack(args, nargs, "O&uu:LoadKey", clinic_HKEY_converter, &key, &sub_key, &file_name)) { @@ -623,7 +626,7 @@ PyDoc_STRVAR(winreg_OpenKey__doc__, {"OpenKey", (PyCFunction)(void(*)(void))winreg_OpenKey, METH_FASTCALL|METH_KEYWORDS, winreg_OpenKey__doc__}, static HKEY -winreg_OpenKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, +winreg_OpenKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, int reserved, REGSAM access); static PyObject * @@ -633,7 +636,7 @@ winreg_OpenKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje static const char * const _keywords[] = {"key", "sub_key", "reserved", "access", NULL}; static _PyArg_Parser _parser = {"O&Z|ii:OpenKey", _keywords, 0}; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; int reserved = 0; REGSAM access = KEY_READ; HKEY _return_value; @@ -675,7 +678,7 @@ PyDoc_STRVAR(winreg_OpenKeyEx__doc__, {"OpenKeyEx", (PyCFunction)(void(*)(void))winreg_OpenKeyEx, METH_FASTCALL|METH_KEYWORDS, winreg_OpenKeyEx__doc__}, static HKEY -winreg_OpenKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, +winreg_OpenKeyEx_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, int reserved, REGSAM access); static PyObject * @@ -685,7 +688,7 @@ winreg_OpenKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb static const char * const _keywords[] = {"key", "sub_key", "reserved", "access", NULL}; static _PyArg_Parser _parser = {"O&Z|ii:OpenKeyEx", _keywords, 0}; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; int reserved = 0; REGSAM access = KEY_READ; HKEY _return_value; @@ -764,14 +767,14 @@ PyDoc_STRVAR(winreg_QueryValue__doc__, {"QueryValue", (PyCFunction)(void(*)(void))winreg_QueryValue, METH_FASTCALL, winreg_QueryValue__doc__}, static PyObject * -winreg_QueryValue_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key); +winreg_QueryValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key); static PyObject * winreg_QueryValue(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; if (!_PyArg_ParseStack(args, nargs, "O&Z:QueryValue", clinic_HKEY_converter, &key, &sub_key)) { @@ -803,14 +806,14 @@ PyDoc_STRVAR(winreg_QueryValueEx__doc__, {"QueryValueEx", (PyCFunction)(void(*)(void))winreg_QueryValueEx, METH_FASTCALL, winreg_QueryValueEx__doc__}, static PyObject * -winreg_QueryValueEx_impl(PyObject *module, HKEY key, Py_UNICODE *name); +winreg_QueryValueEx_impl(PyObject *module, HKEY key, const Py_UNICODE *name); static PyObject * winreg_QueryValueEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *name; + const Py_UNICODE *name; if (!_PyArg_ParseStack(args, nargs, "O&Z:QueryValueEx", clinic_HKEY_converter, &key, &name)) { @@ -847,14 +850,14 @@ PyDoc_STRVAR(winreg_SaveKey__doc__, {"SaveKey", (PyCFunction)(void(*)(void))winreg_SaveKey, METH_FASTCALL, winreg_SaveKey__doc__}, static PyObject * -winreg_SaveKey_impl(PyObject *module, HKEY key, Py_UNICODE *file_name); +winreg_SaveKey_impl(PyObject *module, HKEY key, const Py_UNICODE *file_name); static PyObject * winreg_SaveKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *file_name; + const Py_UNICODE *file_name; if (!_PyArg_ParseStack(args, nargs, "O&u:SaveKey", clinic_HKEY_converter, &key, &file_name)) { @@ -896,8 +899,8 @@ PyDoc_STRVAR(winreg_SetValue__doc__, {"SetValue", (PyCFunction)(void(*)(void))winreg_SetValue, METH_FASTCALL, winreg_SetValue__doc__}, static PyObject * -winreg_SetValue_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - DWORD type, Py_UNICODE *value, +winreg_SetValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, + DWORD type, const Py_UNICODE *value, Py_ssize_clean_t value_length); static PyObject * @@ -905,9 +908,9 @@ winreg_SetValue(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; DWORD type; - Py_UNICODE *value; + const Py_UNICODE *value; Py_ssize_clean_t value_length; if (!_PyArg_ParseStack(args, nargs, "O&Zku#:SetValue", @@ -967,15 +970,16 @@ PyDoc_STRVAR(winreg_SetValueEx__doc__, {"SetValueEx", (PyCFunction)(void(*)(void))winreg_SetValueEx, METH_FASTCALL, winreg_SetValueEx__doc__}, static PyObject * -winreg_SetValueEx_impl(PyObject *module, HKEY key, Py_UNICODE *value_name, - PyObject *reserved, DWORD type, PyObject *value); +winreg_SetValueEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *value_name, PyObject *reserved, + DWORD type, PyObject *value); static PyObject * winreg_SetValueEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *value_name; + const Py_UNICODE *value_name; PyObject *reserved; DWORD type; PyObject *value; @@ -1091,4 +1095,4 @@ winreg_QueryReflectionKey(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=45a9aec9f9258c0a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ff2cc1951ab1a56c input=a9049054013a1b77]*/ diff --git a/PC/winreg.c b/PC/winreg.c index 4505c314d7b4..8ed6be48f15b 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -825,9 +825,9 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_ConnectRegistry_impl(PyObject *module, Py_UNICODE *computer_name, - HKEY key) -/*[clinic end generated code: output=5ab79d02aa3167b4 input=5f98a891a347e68e]*/ +winreg_ConnectRegistry_impl(PyObject *module, + const Py_UNICODE *computer_name, HKEY key) +/*[clinic end generated code: output=cd4f70fb9ec901fb input=5f98a891a347e68e]*/ { HKEY retKey; long rc; @@ -862,8 +862,8 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_CreateKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key) -/*[clinic end generated code: output=9c81d4095527c927 input=3cdd1622488acea2]*/ +winreg_CreateKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key) +/*[clinic end generated code: output=2af13910d56eae26 input=3cdd1622488acea2]*/ { HKEY retKey; long rc; @@ -901,9 +901,10 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_CreateKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - int reserved, REGSAM access) -/*[clinic end generated code: output=b9fce6dc5c4e39b1 input=42c2b03f98406b66]*/ +winreg_CreateKeyEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *sub_key, int reserved, + REGSAM access) +/*[clinic end generated code: output=643a70ad6a361a97 input=42c2b03f98406b66]*/ { HKEY retKey; long rc; @@ -936,8 +937,8 @@ is removed. If the function fails, an OSError exception is raised. [clinic start generated code]*/ static PyObject * -winreg_DeleteKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key) -/*[clinic end generated code: output=7734b1e431991ae4 input=b31d225b935e4211]*/ +winreg_DeleteKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key) +/*[clinic end generated code: output=d2652a84f70e0862 input=b31d225b935e4211]*/ { long rc; rc = RegDeleteKeyW(key, sub_key ); @@ -971,9 +972,10 @@ On unsupported Windows versions, NotImplementedError is raised. [clinic start generated code]*/ static PyObject * -winreg_DeleteKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - REGSAM access, int reserved) -/*[clinic end generated code: output=01378d86ad3eb936 input=711d9d89e7ecbed7]*/ +winreg_DeleteKeyEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *sub_key, REGSAM access, + int reserved) +/*[clinic end generated code: output=52a1c8b374ebc003 input=711d9d89e7ecbed7]*/ { HMODULE hMod; typedef LONG (WINAPI *RDKEFunc)(HKEY, const wchar_t*, REGSAM, int); @@ -1013,8 +1015,8 @@ Removes a named value from a registry key. [clinic start generated code]*/ static PyObject * -winreg_DeleteValue_impl(PyObject *module, HKEY key, Py_UNICODE *value) -/*[clinic end generated code: output=67e7e9a514f84951 input=a78d3407a4197b21]*/ +winreg_DeleteValue_impl(PyObject *module, HKEY key, const Py_UNICODE *value) +/*[clinic end generated code: output=56fa9d21f3a54371 input=a78d3407a4197b21]*/ { long rc; Py_BEGIN_ALLOW_THREADS @@ -1181,8 +1183,9 @@ Expand environment vars. [clinic start generated code]*/ static PyObject * -winreg_ExpandEnvironmentStrings_impl(PyObject *module, Py_UNICODE *string) -/*[clinic end generated code: output=cba46ac293a8af1a input=b2a9714d2b751aa6]*/ +winreg_ExpandEnvironmentStrings_impl(PyObject *module, + const Py_UNICODE *string) +/*[clinic end generated code: output=8fa4e959747a7312 input=b2a9714d2b751aa6]*/ { wchar_t *retValue = NULL; DWORD retValueSize; @@ -1274,9 +1277,9 @@ tree. [clinic start generated code]*/ static PyObject * -winreg_LoadKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - Py_UNICODE *file_name) -/*[clinic end generated code: output=87344005c5905cde input=e3b5b45ade311582]*/ +winreg_LoadKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, + const Py_UNICODE *file_name) +/*[clinic end generated code: output=65f89f2548cb27c7 input=e3b5b45ade311582]*/ { long rc; @@ -1308,9 +1311,9 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_OpenKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, +winreg_OpenKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, int reserved, REGSAM access) -/*[clinic end generated code: output=a905f1b947f3ce85 input=098505ac36a9ae28]*/ +/*[clinic end generated code: output=8849bff2c30104ad input=098505ac36a9ae28]*/ { HKEY retKey; long rc; @@ -1335,9 +1338,9 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_OpenKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, +winreg_OpenKeyEx_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, int reserved, REGSAM access) -/*[clinic end generated code: output=226042593b37e940 input=c6c4972af8622959]*/ +/*[clinic end generated code: output=81bc2bd684bc77ae input=c6c4972af8622959]*/ { return winreg_OpenKey_impl(module, key, sub_key, reserved, access); } @@ -1405,8 +1408,8 @@ completeness. [clinic start generated code]*/ static PyObject * -winreg_QueryValue_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key) -/*[clinic end generated code: output=2bb8d1e02c10d0b6 input=41cafbbf423b21d6]*/ +winreg_QueryValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key) +/*[clinic end generated code: output=c655810ae50c63a9 input=41cafbbf423b21d6]*/ { long rc; PyObject *retStr; @@ -1472,8 +1475,8 @@ The return value is a tuple of the value and the type_id. [clinic start generated code]*/ static PyObject * -winreg_QueryValueEx_impl(PyObject *module, HKEY key, Py_UNICODE *name) -/*[clinic end generated code: output=5b4fa3e33d6d3e8f input=cf366cada4836891]*/ +winreg_QueryValueEx_impl(PyObject *module, HKEY key, const Py_UNICODE *name) +/*[clinic end generated code: output=f1b85b1c3d887ec7 input=cf366cada4836891]*/ { long rc; BYTE *retBuf, *tmp; @@ -1545,8 +1548,8 @@ to the API. [clinic start generated code]*/ static PyObject * -winreg_SaveKey_impl(PyObject *module, HKEY key, Py_UNICODE *file_name) -/*[clinic end generated code: output=1dda1502bd4c30d8 input=da735241f91ac7a2]*/ +winreg_SaveKey_impl(PyObject *module, HKEY key, const Py_UNICODE *file_name) +/*[clinic end generated code: output=ca94b835c88f112b input=da735241f91ac7a2]*/ { LPSECURITY_ATTRIBUTES pSA = NULL; @@ -1591,10 +1594,10 @@ KEY_SET_VALUE access. [clinic start generated code]*/ static PyObject * -winreg_SetValue_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - DWORD type, Py_UNICODE *value, +winreg_SetValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, + DWORD type, const Py_UNICODE *value, Py_ssize_clean_t value_length) -/*[clinic end generated code: output=1e31931174820631 input=2cd2adab79339c53]*/ +/*[clinic end generated code: output=686bedb1cbb4367b input=2cd2adab79339c53]*/ { long rc; @@ -1657,9 +1660,10 @@ the configuration registry to help the registry perform efficiently. [clinic start generated code]*/ static PyObject * -winreg_SetValueEx_impl(PyObject *module, HKEY key, Py_UNICODE *value_name, - PyObject *reserved, DWORD type, PyObject *value) -/*[clinic end generated code: output=c88c8426b6c00ec7 input=900a9e3990bfb196]*/ +winreg_SetValueEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *value_name, PyObject *reserved, + DWORD type, PyObject *value) +/*[clinic end generated code: output=811b769a66ae11b7 input=900a9e3990bfb196]*/ { BYTE *data; DWORD len; diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 2df807144073..1adabb95cc96 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -2858,7 +2858,7 @@ class unicode_converter(CConverter): @add_legacy_c_converter('Z', accept={str, NoneType}) @add_legacy_c_converter('Z#', accept={str, NoneType}, zeroes=True) class Py_UNICODE_converter(CConverter): - type = 'Py_UNICODE *' + type = 'const Py_UNICODE *' default_type = (str, Null, NoneType) format_unit = 'u' From webhook-mailer at python.org Fri Dec 14 04:57:01 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 14 Dec 2018 09:57:01 -0000 Subject: [Python-checkins] [3.7] bpo-35489: Use "const Py_UNICODE *" for the Py_UNICODE converter in AC. (GH-11150). (GH-11151) Message-ID: https://github.com/python/cpython/commit/45a7b7617e67bd1a8491f5e10ea9d24fe418b52d commit: 45a7b7617e67bd1a8491f5e10ea9d24fe418b52d branch: 3.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-14T11:56:48+02:00 summary: [3.7] bpo-35489: Use "const Py_UNICODE *" for the Py_UNICODE converter in AC. (GH-11150). (GH-11151) (cherry picked from commit afb3e71a1710c444fbe789b51df43ee16ee9ede7) files: M Modules/_winapi.c M Modules/arraymodule.c M Modules/clinic/_winapi.c.h M Modules/clinic/arraymodule.c.h M Modules/clinic/posixmodule.c.h M Modules/posixmodule.c M PC/clinic/winreg.c.h M PC/winreg.c M Tools/clinic/clinic.py diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 127f8886e210..036464d9ab4a 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -994,13 +994,14 @@ process ID, and thread ID. [clinic start generated code]*/ static PyObject * -_winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, +_winapi_CreateProcess_impl(PyObject *module, + const Py_UNICODE *application_name, PyObject *command_line, PyObject *proc_attrs, PyObject *thread_attrs, BOOL inherit_handles, DWORD creation_flags, PyObject *env_mapping, - Py_UNICODE *current_directory, + const Py_UNICODE *current_directory, PyObject *startup_info) -/*[clinic end generated code: output=2ecaab46a05e3123 input=42ac293eaea03fc4]*/ +/*[clinic end generated code: output=9b2423a609230132 input=42ac293eaea03fc4]*/ { PyObject *ret = NULL; BOOL result; diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 7b4a4a3ad216..ee7ae54661b3 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1712,9 +1712,9 @@ some other type. [clinic start generated code]*/ static PyObject * -array_array_fromunicode_impl(arrayobject *self, Py_UNICODE *ustr, +array_array_fromunicode_impl(arrayobject *self, const Py_UNICODE *ustr, Py_ssize_clean_t ustr_length) -/*[clinic end generated code: output=ebb72fc16975e06d input=150f00566ffbca6e]*/ +/*[clinic end generated code: output=cf2f662908e2befc input=150f00566ffbca6e]*/ { char typecode; diff --git a/Modules/clinic/_winapi.c.h b/Modules/clinic/_winapi.c.h index 436fbe54947a..f4d884237b3f 100644 --- a/Modules/clinic/_winapi.c.h +++ b/Modules/clinic/_winapi.c.h @@ -300,25 +300,26 @@ PyDoc_STRVAR(_winapi_CreateProcess__doc__, {"CreateProcess", (PyCFunction)_winapi_CreateProcess, METH_FASTCALL, _winapi_CreateProcess__doc__}, static PyObject * -_winapi_CreateProcess_impl(PyObject *module, Py_UNICODE *application_name, +_winapi_CreateProcess_impl(PyObject *module, + const Py_UNICODE *application_name, PyObject *command_line, PyObject *proc_attrs, PyObject *thread_attrs, BOOL inherit_handles, DWORD creation_flags, PyObject *env_mapping, - Py_UNICODE *current_directory, + const Py_UNICODE *current_directory, PyObject *startup_info); static PyObject * _winapi_CreateProcess(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_UNICODE *application_name; + const Py_UNICODE *application_name; PyObject *command_line; PyObject *proc_attrs; PyObject *thread_attrs; BOOL inherit_handles; DWORD creation_flags; PyObject *env_mapping; - Py_UNICODE *current_directory; + const Py_UNICODE *current_directory; PyObject *startup_info; if (!_PyArg_ParseStack(args, nargs, "ZOOOikOZO:CreateProcess", @@ -943,4 +944,4 @@ _winapi_GetFileType(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P exit: return return_value; } -/*[clinic end generated code: output=3a6492395b11b5a5 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=896d06ce2290aa86 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/arraymodule.c.h b/Modules/clinic/arraymodule.c.h index b03a507914a8..c85d0c6262e6 100644 --- a/Modules/clinic/arraymodule.c.h +++ b/Modules/clinic/arraymodule.c.h @@ -376,14 +376,14 @@ PyDoc_STRVAR(array_array_fromunicode__doc__, {"fromunicode", (PyCFunction)array_array_fromunicode, METH_O, array_array_fromunicode__doc__}, static PyObject * -array_array_fromunicode_impl(arrayobject *self, Py_UNICODE *ustr, +array_array_fromunicode_impl(arrayobject *self, const Py_UNICODE *ustr, Py_ssize_clean_t ustr_length); static PyObject * array_array_fromunicode(arrayobject *self, PyObject *arg) { PyObject *return_value = NULL; - Py_UNICODE *ustr; + const Py_UNICODE *ustr; Py_ssize_clean_t ustr_length; if (!PyArg_Parse(arg, "u#:fromunicode", &ustr, &ustr_length)) { @@ -505,4 +505,4 @@ PyDoc_STRVAR(array_arrayiterator___setstate____doc__, #define ARRAY_ARRAYITERATOR___SETSTATE___METHODDEF \ {"__setstate__", (PyCFunction)array_arrayiterator___setstate__, METH_O, array_arrayiterator___setstate____doc__}, -/*[clinic end generated code: output=1289bde2a095a712 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2d0fb1937dea02c2 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 3230cd700972..a722e93be275 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1350,7 +1350,7 @@ PyDoc_STRVAR(os_system__doc__, {"system", (PyCFunction)os_system, METH_FASTCALL|METH_KEYWORDS, os_system__doc__}, static long -os_system_impl(PyObject *module, Py_UNICODE *command); +os_system_impl(PyObject *module, const Py_UNICODE *command); static PyObject * os_system(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -1358,7 +1358,7 @@ os_system(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyObject *return_value = NULL; static const char * const _keywords[] = {"command", NULL}; static _PyArg_Parser _parser = {"u:system", _keywords, 0}; - Py_UNICODE *command; + const Py_UNICODE *command; long _return_value; if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, @@ -5201,7 +5201,8 @@ PyDoc_STRVAR(os_startfile__doc__, {"startfile", (PyCFunction)os_startfile, METH_FASTCALL|METH_KEYWORDS, os_startfile__doc__}, static PyObject * -os_startfile_impl(PyObject *module, path_t *filepath, Py_UNICODE *operation); +os_startfile_impl(PyObject *module, path_t *filepath, + const Py_UNICODE *operation); static PyObject * os_startfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -5210,7 +5211,7 @@ os_startfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject static const char * const _keywords[] = {"filepath", "operation", NULL}; static _PyArg_Parser _parser = {"O&|u:startfile", _keywords, 0}; path_t filepath = PATH_T_INITIALIZE("startfile", "filepath", 0, 0); - Py_UNICODE *operation = NULL; + const Py_UNICODE *operation = NULL; if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, path_converter, &filepath, &operation)) { @@ -6537,4 +6538,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject #ifndef OS_GETRANDOM_METHODDEF #define OS_GETRANDOM_METHODDEF #endif /* !defined(OS_GETRANDOM_METHODDEF) */ -/*[clinic end generated code: output=c6ca6ad4afa64454 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=df73bd7805978a25 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 5403660ba45e..383999ea8492 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4181,8 +4181,8 @@ Execute the command in a subshell. [clinic start generated code]*/ static long -os_system_impl(PyObject *module, Py_UNICODE *command) -/*[clinic end generated code: output=96c4dffee36dfb48 input=303f5ce97df606b0]*/ +os_system_impl(PyObject *module, const Py_UNICODE *command) +/*[clinic end generated code: output=5b7c3599c068ca42 input=303f5ce97df606b0]*/ { long result; Py_BEGIN_ALLOW_THREADS @@ -10792,8 +10792,9 @@ the underlying Win32 ShellExecute function doesn't work if it is. [clinic start generated code]*/ static PyObject * -os_startfile_impl(PyObject *module, path_t *filepath, Py_UNICODE *operation) -/*[clinic end generated code: output=912ceba79acfa1c9 input=63950bf2986380d0]*/ +os_startfile_impl(PyObject *module, path_t *filepath, + const Py_UNICODE *operation) +/*[clinic end generated code: output=66dc311c94d50797 input=63950bf2986380d0]*/ { HINSTANCE rc; diff --git a/PC/clinic/winreg.c.h b/PC/clinic/winreg.c.h index 69781a969285..6a5f613808c2 100644 --- a/PC/clinic/winreg.c.h +++ b/PC/clinic/winreg.c.h @@ -137,14 +137,14 @@ PyDoc_STRVAR(winreg_ConnectRegistry__doc__, {"ConnectRegistry", (PyCFunction)winreg_ConnectRegistry, METH_FASTCALL, winreg_ConnectRegistry__doc__}, static HKEY -winreg_ConnectRegistry_impl(PyObject *module, Py_UNICODE *computer_name, - HKEY key); +winreg_ConnectRegistry_impl(PyObject *module, + const Py_UNICODE *computer_name, HKEY key); static PyObject * winreg_ConnectRegistry(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_UNICODE *computer_name; + const Py_UNICODE *computer_name; HKEY key; HKEY _return_value; @@ -185,14 +185,14 @@ PyDoc_STRVAR(winreg_CreateKey__doc__, {"CreateKey", (PyCFunction)winreg_CreateKey, METH_FASTCALL, winreg_CreateKey__doc__}, static HKEY -winreg_CreateKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key); +winreg_CreateKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key); static PyObject * winreg_CreateKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; HKEY _return_value; if (!_PyArg_ParseStack(args, nargs, "O&Z:CreateKey", @@ -238,8 +238,9 @@ PyDoc_STRVAR(winreg_CreateKeyEx__doc__, {"CreateKeyEx", (PyCFunction)winreg_CreateKeyEx, METH_FASTCALL|METH_KEYWORDS, winreg_CreateKeyEx__doc__}, static HKEY -winreg_CreateKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - int reserved, REGSAM access); +winreg_CreateKeyEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *sub_key, int reserved, + REGSAM access); static PyObject * winreg_CreateKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -248,7 +249,7 @@ winreg_CreateKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py static const char * const _keywords[] = {"key", "sub_key", "reserved", "access", NULL}; static _PyArg_Parser _parser = {"O&Z|ii:CreateKeyEx", _keywords, 0}; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; int reserved = 0; REGSAM access = KEY_WRITE; HKEY _return_value; @@ -289,14 +290,14 @@ PyDoc_STRVAR(winreg_DeleteKey__doc__, {"DeleteKey", (PyCFunction)winreg_DeleteKey, METH_FASTCALL, winreg_DeleteKey__doc__}, static PyObject * -winreg_DeleteKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key); +winreg_DeleteKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key); static PyObject * winreg_DeleteKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; if (!_PyArg_ParseStack(args, nargs, "O&u:DeleteKey", clinic_HKEY_converter, &key, &sub_key)) { @@ -337,8 +338,9 @@ PyDoc_STRVAR(winreg_DeleteKeyEx__doc__, {"DeleteKeyEx", (PyCFunction)winreg_DeleteKeyEx, METH_FASTCALL|METH_KEYWORDS, winreg_DeleteKeyEx__doc__}, static PyObject * -winreg_DeleteKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - REGSAM access, int reserved); +winreg_DeleteKeyEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *sub_key, REGSAM access, + int reserved); static PyObject * winreg_DeleteKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -347,7 +349,7 @@ winreg_DeleteKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py static const char * const _keywords[] = {"key", "sub_key", "access", "reserved", NULL}; static _PyArg_Parser _parser = {"O&u|ii:DeleteKeyEx", _keywords, 0}; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; REGSAM access = KEY_WOW64_64KEY; int reserved = 0; @@ -376,14 +378,14 @@ PyDoc_STRVAR(winreg_DeleteValue__doc__, {"DeleteValue", (PyCFunction)winreg_DeleteValue, METH_FASTCALL, winreg_DeleteValue__doc__}, static PyObject * -winreg_DeleteValue_impl(PyObject *module, HKEY key, Py_UNICODE *value); +winreg_DeleteValue_impl(PyObject *module, HKEY key, const Py_UNICODE *value); static PyObject * winreg_DeleteValue(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *value; + const Py_UNICODE *value; if (!_PyArg_ParseStack(args, nargs, "O&Z:DeleteValue", clinic_HKEY_converter, &key, &value)) { @@ -490,13 +492,14 @@ PyDoc_STRVAR(winreg_ExpandEnvironmentStrings__doc__, {"ExpandEnvironmentStrings", (PyCFunction)winreg_ExpandEnvironmentStrings, METH_O, winreg_ExpandEnvironmentStrings__doc__}, static PyObject * -winreg_ExpandEnvironmentStrings_impl(PyObject *module, Py_UNICODE *string); +winreg_ExpandEnvironmentStrings_impl(PyObject *module, + const Py_UNICODE *string); static PyObject * winreg_ExpandEnvironmentStrings(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - Py_UNICODE *string; + const Py_UNICODE *string; if (!PyArg_Parse(arg, "u:ExpandEnvironmentStrings", &string)) { goto exit; @@ -579,16 +582,16 @@ PyDoc_STRVAR(winreg_LoadKey__doc__, {"LoadKey", (PyCFunction)winreg_LoadKey, METH_FASTCALL, winreg_LoadKey__doc__}, static PyObject * -winreg_LoadKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - Py_UNICODE *file_name); +winreg_LoadKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, + const Py_UNICODE *file_name); static PyObject * winreg_LoadKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; - Py_UNICODE *file_name; + const Py_UNICODE *sub_key; + const Py_UNICODE *file_name; if (!_PyArg_ParseStack(args, nargs, "O&uu:LoadKey", clinic_HKEY_converter, &key, &sub_key, &file_name)) { @@ -623,7 +626,7 @@ PyDoc_STRVAR(winreg_OpenKey__doc__, {"OpenKey", (PyCFunction)winreg_OpenKey, METH_FASTCALL|METH_KEYWORDS, winreg_OpenKey__doc__}, static HKEY -winreg_OpenKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, +winreg_OpenKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, int reserved, REGSAM access); static PyObject * @@ -633,7 +636,7 @@ winreg_OpenKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje static const char * const _keywords[] = {"key", "sub_key", "reserved", "access", NULL}; static _PyArg_Parser _parser = {"O&Z|ii:OpenKey", _keywords, 0}; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; int reserved = 0; REGSAM access = KEY_READ; HKEY _return_value; @@ -675,7 +678,7 @@ PyDoc_STRVAR(winreg_OpenKeyEx__doc__, {"OpenKeyEx", (PyCFunction)winreg_OpenKeyEx, METH_FASTCALL|METH_KEYWORDS, winreg_OpenKeyEx__doc__}, static HKEY -winreg_OpenKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, +winreg_OpenKeyEx_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, int reserved, REGSAM access); static PyObject * @@ -685,7 +688,7 @@ winreg_OpenKeyEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb static const char * const _keywords[] = {"key", "sub_key", "reserved", "access", NULL}; static _PyArg_Parser _parser = {"O&Z|ii:OpenKeyEx", _keywords, 0}; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; int reserved = 0; REGSAM access = KEY_READ; HKEY _return_value; @@ -764,14 +767,14 @@ PyDoc_STRVAR(winreg_QueryValue__doc__, {"QueryValue", (PyCFunction)winreg_QueryValue, METH_FASTCALL, winreg_QueryValue__doc__}, static PyObject * -winreg_QueryValue_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key); +winreg_QueryValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key); static PyObject * winreg_QueryValue(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; if (!_PyArg_ParseStack(args, nargs, "O&Z:QueryValue", clinic_HKEY_converter, &key, &sub_key)) { @@ -803,14 +806,14 @@ PyDoc_STRVAR(winreg_QueryValueEx__doc__, {"QueryValueEx", (PyCFunction)winreg_QueryValueEx, METH_FASTCALL, winreg_QueryValueEx__doc__}, static PyObject * -winreg_QueryValueEx_impl(PyObject *module, HKEY key, Py_UNICODE *name); +winreg_QueryValueEx_impl(PyObject *module, HKEY key, const Py_UNICODE *name); static PyObject * winreg_QueryValueEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *name; + const Py_UNICODE *name; if (!_PyArg_ParseStack(args, nargs, "O&Z:QueryValueEx", clinic_HKEY_converter, &key, &name)) { @@ -847,14 +850,14 @@ PyDoc_STRVAR(winreg_SaveKey__doc__, {"SaveKey", (PyCFunction)winreg_SaveKey, METH_FASTCALL, winreg_SaveKey__doc__}, static PyObject * -winreg_SaveKey_impl(PyObject *module, HKEY key, Py_UNICODE *file_name); +winreg_SaveKey_impl(PyObject *module, HKEY key, const Py_UNICODE *file_name); static PyObject * winreg_SaveKey(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *file_name; + const Py_UNICODE *file_name; if (!_PyArg_ParseStack(args, nargs, "O&u:SaveKey", clinic_HKEY_converter, &key, &file_name)) { @@ -896,8 +899,8 @@ PyDoc_STRVAR(winreg_SetValue__doc__, {"SetValue", (PyCFunction)winreg_SetValue, METH_FASTCALL, winreg_SetValue__doc__}, static PyObject * -winreg_SetValue_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - DWORD type, Py_UNICODE *value, +winreg_SetValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, + DWORD type, const Py_UNICODE *value, Py_ssize_clean_t value_length); static PyObject * @@ -905,9 +908,9 @@ winreg_SetValue(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *sub_key; + const Py_UNICODE *sub_key; DWORD type; - Py_UNICODE *value; + const Py_UNICODE *value; Py_ssize_clean_t value_length; if (!_PyArg_ParseStack(args, nargs, "O&Zku#:SetValue", @@ -967,15 +970,16 @@ PyDoc_STRVAR(winreg_SetValueEx__doc__, {"SetValueEx", (PyCFunction)winreg_SetValueEx, METH_FASTCALL, winreg_SetValueEx__doc__}, static PyObject * -winreg_SetValueEx_impl(PyObject *module, HKEY key, Py_UNICODE *value_name, - PyObject *reserved, DWORD type, PyObject *value); +winreg_SetValueEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *value_name, PyObject *reserved, + DWORD type, PyObject *value); static PyObject * winreg_SetValueEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; HKEY key; - Py_UNICODE *value_name; + const Py_UNICODE *value_name; PyObject *reserved; DWORD type; PyObject *value; @@ -1091,4 +1095,4 @@ winreg_QueryReflectionKey(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=d1c8e2678015dd7d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=60c92ffc7438f8cf input=a9049054013a1b77]*/ diff --git a/PC/winreg.c b/PC/winreg.c index 6f3106644b2c..e3801b257b07 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -826,9 +826,9 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_ConnectRegistry_impl(PyObject *module, Py_UNICODE *computer_name, - HKEY key) -/*[clinic end generated code: output=5ab79d02aa3167b4 input=5f98a891a347e68e]*/ +winreg_ConnectRegistry_impl(PyObject *module, + const Py_UNICODE *computer_name, HKEY key) +/*[clinic end generated code: output=cd4f70fb9ec901fb input=5f98a891a347e68e]*/ { HKEY retKey; long rc; @@ -863,8 +863,8 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_CreateKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key) -/*[clinic end generated code: output=9c81d4095527c927 input=3cdd1622488acea2]*/ +winreg_CreateKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key) +/*[clinic end generated code: output=2af13910d56eae26 input=3cdd1622488acea2]*/ { HKEY retKey; long rc; @@ -902,9 +902,10 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_CreateKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - int reserved, REGSAM access) -/*[clinic end generated code: output=b9fce6dc5c4e39b1 input=42c2b03f98406b66]*/ +winreg_CreateKeyEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *sub_key, int reserved, + REGSAM access) +/*[clinic end generated code: output=643a70ad6a361a97 input=42c2b03f98406b66]*/ { HKEY retKey; long rc; @@ -937,8 +938,8 @@ is removed. If the function fails, an OSError exception is raised. [clinic start generated code]*/ static PyObject * -winreg_DeleteKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key) -/*[clinic end generated code: output=7734b1e431991ae4 input=b31d225b935e4211]*/ +winreg_DeleteKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key) +/*[clinic end generated code: output=d2652a84f70e0862 input=b31d225b935e4211]*/ { long rc; rc = RegDeleteKeyW(key, sub_key ); @@ -972,9 +973,10 @@ On unsupported Windows versions, NotImplementedError is raised. [clinic start generated code]*/ static PyObject * -winreg_DeleteKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - REGSAM access, int reserved) -/*[clinic end generated code: output=01378d86ad3eb936 input=711d9d89e7ecbed7]*/ +winreg_DeleteKeyEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *sub_key, REGSAM access, + int reserved) +/*[clinic end generated code: output=52a1c8b374ebc003 input=711d9d89e7ecbed7]*/ { HMODULE hMod; typedef LONG (WINAPI *RDKEFunc)(HKEY, const wchar_t*, REGSAM, int); @@ -1014,8 +1016,8 @@ Removes a named value from a registry key. [clinic start generated code]*/ static PyObject * -winreg_DeleteValue_impl(PyObject *module, HKEY key, Py_UNICODE *value) -/*[clinic end generated code: output=67e7e9a514f84951 input=a78d3407a4197b21]*/ +winreg_DeleteValue_impl(PyObject *module, HKEY key, const Py_UNICODE *value) +/*[clinic end generated code: output=56fa9d21f3a54371 input=a78d3407a4197b21]*/ { long rc; Py_BEGIN_ALLOW_THREADS @@ -1182,8 +1184,9 @@ Expand environment vars. [clinic start generated code]*/ static PyObject * -winreg_ExpandEnvironmentStrings_impl(PyObject *module, Py_UNICODE *string) -/*[clinic end generated code: output=cba46ac293a8af1a input=b2a9714d2b751aa6]*/ +winreg_ExpandEnvironmentStrings_impl(PyObject *module, + const Py_UNICODE *string) +/*[clinic end generated code: output=8fa4e959747a7312 input=b2a9714d2b751aa6]*/ { wchar_t *retValue = NULL; DWORD retValueSize; @@ -1275,9 +1278,9 @@ tree. [clinic start generated code]*/ static PyObject * -winreg_LoadKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - Py_UNICODE *file_name) -/*[clinic end generated code: output=87344005c5905cde input=e3b5b45ade311582]*/ +winreg_LoadKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, + const Py_UNICODE *file_name) +/*[clinic end generated code: output=65f89f2548cb27c7 input=e3b5b45ade311582]*/ { long rc; @@ -1309,9 +1312,9 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_OpenKey_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, +winreg_OpenKey_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, int reserved, REGSAM access) -/*[clinic end generated code: output=a905f1b947f3ce85 input=098505ac36a9ae28]*/ +/*[clinic end generated code: output=8849bff2c30104ad input=098505ac36a9ae28]*/ { HKEY retKey; long rc; @@ -1336,9 +1339,9 @@ If the function fails, an OSError exception is raised. [clinic start generated code]*/ static HKEY -winreg_OpenKeyEx_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, +winreg_OpenKeyEx_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, int reserved, REGSAM access) -/*[clinic end generated code: output=226042593b37e940 input=c6c4972af8622959]*/ +/*[clinic end generated code: output=81bc2bd684bc77ae input=c6c4972af8622959]*/ { return winreg_OpenKey_impl(module, key, sub_key, reserved, access); } @@ -1406,8 +1409,8 @@ completeness. [clinic start generated code]*/ static PyObject * -winreg_QueryValue_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key) -/*[clinic end generated code: output=2bb8d1e02c10d0b6 input=41cafbbf423b21d6]*/ +winreg_QueryValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key) +/*[clinic end generated code: output=c655810ae50c63a9 input=41cafbbf423b21d6]*/ { long rc; PyObject *retStr; @@ -1473,8 +1476,8 @@ The return value is a tuple of the value and the type_id. [clinic start generated code]*/ static PyObject * -winreg_QueryValueEx_impl(PyObject *module, HKEY key, Py_UNICODE *name) -/*[clinic end generated code: output=5b4fa3e33d6d3e8f input=cf366cada4836891]*/ +winreg_QueryValueEx_impl(PyObject *module, HKEY key, const Py_UNICODE *name) +/*[clinic end generated code: output=f1b85b1c3d887ec7 input=cf366cada4836891]*/ { long rc; BYTE *retBuf, *tmp; @@ -1546,8 +1549,8 @@ to the API. [clinic start generated code]*/ static PyObject * -winreg_SaveKey_impl(PyObject *module, HKEY key, Py_UNICODE *file_name) -/*[clinic end generated code: output=1dda1502bd4c30d8 input=da735241f91ac7a2]*/ +winreg_SaveKey_impl(PyObject *module, HKEY key, const Py_UNICODE *file_name) +/*[clinic end generated code: output=ca94b835c88f112b input=da735241f91ac7a2]*/ { LPSECURITY_ATTRIBUTES pSA = NULL; @@ -1592,10 +1595,10 @@ KEY_SET_VALUE access. [clinic start generated code]*/ static PyObject * -winreg_SetValue_impl(PyObject *module, HKEY key, Py_UNICODE *sub_key, - DWORD type, Py_UNICODE *value, +winreg_SetValue_impl(PyObject *module, HKEY key, const Py_UNICODE *sub_key, + DWORD type, const Py_UNICODE *value, Py_ssize_clean_t value_length) -/*[clinic end generated code: output=1e31931174820631 input=2cd2adab79339c53]*/ +/*[clinic end generated code: output=686bedb1cbb4367b input=2cd2adab79339c53]*/ { long rc; @@ -1658,9 +1661,10 @@ the configuration registry to help the registry perform efficiently. [clinic start generated code]*/ static PyObject * -winreg_SetValueEx_impl(PyObject *module, HKEY key, Py_UNICODE *value_name, - PyObject *reserved, DWORD type, PyObject *value) -/*[clinic end generated code: output=c88c8426b6c00ec7 input=900a9e3990bfb196]*/ +winreg_SetValueEx_impl(PyObject *module, HKEY key, + const Py_UNICODE *value_name, PyObject *reserved, + DWORD type, PyObject *value) +/*[clinic end generated code: output=811b769a66ae11b7 input=900a9e3990bfb196]*/ { BYTE *data; DWORD len; diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index b704b5a669fb..0ba8caba9e79 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -2824,7 +2824,7 @@ class unicode_converter(CConverter): @add_legacy_c_converter('Z', accept={str, NoneType}) @add_legacy_c_converter('Z#', accept={str, NoneType}, zeroes=True) class Py_UNICODE_converter(CConverter): - type = 'Py_UNICODE *' + type = 'const Py_UNICODE *' default_type = (str, Null, NoneType) format_unit = 'u' From webhook-mailer at python.org Fri Dec 14 05:13:24 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 14 Dec 2018 10:13:24 -0000 Subject: [Python-checkins] Add multiprocessing.Pool.__repr__() (GH-11137) Message-ID: https://github.com/python/cpython/commit/2b417fba25f036c2d6139875e389d80e4286ad75 commit: 2b417fba25f036c2d6139875e389d80e4286ad75 branch: master author: Victor Stinner committer: GitHub date: 2018-12-14T11:13:18+01:00 summary: Add multiprocessing.Pool.__repr__() (GH-11137) * Add multiprocessing.Pool.__repr__() to ease debug * RUN, CLOSE and TERMINATE constants values are now strings rather than integer to ease debug files: M Lib/multiprocessing/pool.py diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py index c0775418852d..cede9bbd5d40 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -30,9 +30,9 @@ # Constants representing the state of a pool # -RUN = 0 -CLOSE = 1 -TERMINATE = 2 +RUN = "RUN" +CLOSE = "CLOSE" +TERMINATE = "TERMINATE" # # Miscellaneous @@ -217,6 +217,12 @@ def __init__(self, processes=None, initializer=None, initargs=(), exitpriority=15 ) + def __repr__(self): + cls = self.__class__ + return (f'<{cls.__module__}.{cls.__qualname__} ' + f'state={self._state} ' + f'pool_size={len(self._pool)}>') + def _join_exited_workers(self): """Cleanup after any worker processes which have exited due to reaching their specified lifetime. Returns True if any workers were cleaned up. @@ -432,7 +438,7 @@ def _handle_tasks(taskqueue, put, outqueue, pool, cache): try: # iterating taskseq cannot fail for task in taskseq: - if thread._state: + if thread._state != RUN: util.debug('task handler found thread._state != RUN') break try: @@ -480,7 +486,7 @@ def _handle_results(outqueue, get, cache): util.debug('result handler got EOFError/OSError -- exiting') return - if thread._state: + if thread._state != "RUN": assert thread._state == TERMINATE, "Thread not in TERMINATE" util.debug('result handler found thread._state=TERMINATE') break From webhook-mailer at python.org Fri Dec 14 06:28:47 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 14 Dec 2018 11:28:47 -0000 Subject: [Python-checkins] Fixed missing colun in library/sys.po (GH-11153) Message-ID: https://github.com/python/cpython/commit/cb0f5e29e37c081e9bba91a9858370e2504e9e8e commit: cb0f5e29e37c081e9bba91a9858370e2504e9e8e branch: master author: Jules Lasne (jlasne) committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-14T03:28:44-08:00 summary: Fixed missing colun in library/sys.po (GH-11153) # Fixed missing colun in library/sys.po [bpo-35492](https://bugs.python.org/issue35492): Fixed missing colun in library/sys.po files: M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index c7d26866fd80..9002f94545b6 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -703,7 +703,7 @@ always available. .. function:: get_coroutine_origin_tracking_depth() Get the current coroutine origin tracking depth, as set by - func:`set_coroutine_origin_tracking_depth`. + :func:`set_coroutine_origin_tracking_depth`. .. versionadded:: 3.7 From webhook-mailer at python.org Fri Dec 14 06:35:58 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 14 Dec 2018 11:35:58 -0000 Subject: [Python-checkins] Fixed missing colun in library/sys.po (GH-11153) Message-ID: https://github.com/python/cpython/commit/527008599dca9377aa3e71da5e5068433ae2222e commit: 527008599dca9377aa3e71da5e5068433ae2222e branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-14T03:35:55-08:00 summary: Fixed missing colun in library/sys.po (GH-11153) GH- Fixed missing colun in library/sys.po [bpo-35492](https://bugs.python.org/issue35492): Fixed missing colun in library/sys.po (cherry picked from commit cb0f5e29e37c081e9bba91a9858370e2504e9e8e) Co-authored-by: Jules Lasne (jlasne) files: M Doc/library/sys.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 358b11820c62..63861abb431a 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -683,7 +683,7 @@ always available. .. function:: get_coroutine_origin_tracking_depth() Get the current coroutine origin tracking depth, as set by - func:`set_coroutine_origin_tracking_depth`. + :func:`set_coroutine_origin_tracking_depth`. .. versionadded:: 3.7 From webhook-mailer at python.org Fri Dec 14 06:58:57 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 14 Dec 2018 11:58:57 -0000 Subject: [Python-checkins] bpo-35491: Enhance multiprocessing.BaseProcess.__repr__() (GH-11138) Message-ID: https://github.com/python/cpython/commit/7acd50ad8b2a4fe132f7b26980ed3cd209b7ea12 commit: 7acd50ad8b2a4fe132f7b26980ed3cd209b7ea12 branch: master author: Victor Stinner committer: GitHub date: 2018-12-14T12:58:52+01:00 summary: bpo-35491: Enhance multiprocessing.BaseProcess.__repr__() (GH-11138) * Add the pid and parent pid to multiprocessing.BaseProcess.__repr__(). * Add negative sign (ex: "-SIGTERM") to exitcode (process killed by a signal) * Only call _popen.poll() once. Example: becomes: Example: becomes: files: A Misc/NEWS.d/next/Library/2018-12-14-12-12-15.bpo-35491.jHsNOU.rst M Doc/library/multiprocessing.rst M Lib/multiprocessing/process.py diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index 578b5483286a..1d0920aa608b 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -626,14 +626,14 @@ The :mod:`multiprocessing` package mostly replicates the API of the >>> import multiprocessing, time, signal >>> p = multiprocessing.Process(target=time.sleep, args=(1000,)) >>> print(p, p.is_alive()) - False + False >>> p.start() >>> print(p, p.is_alive()) - True + True >>> p.terminate() >>> time.sleep(0.1) >>> print(p, p.is_alive()) - False + False >>> p.exitcode == -signal.SIGTERM True diff --git a/Lib/multiprocessing/process.py b/Lib/multiprocessing/process.py index cd592d0bdf09..780f2d0c2734 100644 --- a/Lib/multiprocessing/process.py +++ b/Lib/multiprocessing/process.py @@ -248,6 +248,7 @@ def sentinel(self): raise ValueError("process not started") from None def __repr__(self): + exitcode = None if self is _current_process: status = 'started' elif self._closed: @@ -257,19 +258,23 @@ def __repr__(self): elif self._popen is None: status = 'initial' else: - if self._popen.poll() is not None: - status = self.exitcode - else: - status = 'started' - - if type(status) is int: - if status == 0: + exitcode = self._popen.poll() + if exitcode is not None: status = 'stopped' else: - status = 'stopped[%s]' % _exitcode_to_name.get(status, status) + status = 'started' - return '<%s(%s, %s%s)>' % (type(self).__name__, self._name, - status, self.daemon and ' daemon' or '') + info = [type(self).__name__, 'name=%r' % self._name] + if self._popen is not None: + info.append('pid=%s' % self._popen.pid) + info.append('parent=%s' % self._parent_pid) + info.append(status) + if exitcode is not None: + exitcode = _exitcode_to_name.get(exitcode, exitcode) + info.append('exitcode=%s' % exitcode) + if self.daemon: + info.append('daemon') + return '<%s>' % ' '.join(info) ## @@ -373,7 +378,7 @@ def close(self): for name, signum in list(signal.__dict__.items()): if name[:3]=='SIG' and '_' not in name: - _exitcode_to_name[-signum] = name + _exitcode_to_name[-signum] = f'-{name}' # For debug and leak testing _dangling = WeakSet() diff --git a/Misc/NEWS.d/next/Library/2018-12-14-12-12-15.bpo-35491.jHsNOU.rst b/Misc/NEWS.d/next/Library/2018-12-14-12-12-15.bpo-35491.jHsNOU.rst new file mode 100644 index 000000000000..7bb650ad7349 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-14-12-12-15.bpo-35491.jHsNOU.rst @@ -0,0 +1,4 @@ +:mod:`multiprocessing`: Add ``Pool.__repr__()`` and enhance +``BaseProcess.__repr__()`` (add pid and parent pid) to ease debugging. Pool +state constant values are now strings instead of integers, for example ``RUN`` +value becomes ``'RUN'`` instead of ``0``. From webhook-mailer at python.org Fri Dec 14 07:06:53 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 14 Dec 2018 12:06:53 -0000 Subject: [Python-checkins] bpo-34279: regrtest consider that skipped tests are ran (GH-11132) Message-ID: https://github.com/python/cpython/commit/3a8f4fef4a4dd0e4a800545468eef9542e126181 commit: 3a8f4fef4a4dd0e4a800545468eef9542e126181 branch: master author: Victor Stinner committer: GitHub date: 2018-12-14T13:06:50+01:00 summary: bpo-34279: regrtest consider that skipped tests are ran (GH-11132) bpo-34279, bpo-35412: support.run_unittest() no longer raises TestDidNotRun if a test result contains skipped tests. The exception is now only raised if no test have been run and no test have been skipped. files: A Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst M Lib/test/support/__init__.py M Lib/test/test_regrtest.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 05e8593f9825..9b6c338a5666 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1937,7 +1937,7 @@ def _run_suite(suite): if junit_xml_list is not None: junit_xml_list.append(result.get_xml_element()) - if not result.testsRun: + if not result.testsRun and not result.skipped: raise TestDidNotRun if not result.wasSuccessful(): if len(result.errors) == 1 and not result.failures: diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index db9bd6dfc043..a67458313add 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -1004,6 +1004,7 @@ def test_bug(self): output = self.run_tests("-w", testname, exitcode=2) self.check_executed_tests(output, [testname], failed=testname, rerun=testname) + def test_no_tests_ran(self): code = textwrap.dedent(""" import unittest @@ -1017,6 +1018,19 @@ def test_bug(self): output = self.run_tests(testname, "-m", "nosuchtest", exitcode=0) self.check_executed_tests(output, [testname], no_test_ran=testname) + def test_no_tests_ran_skip(self): + code = textwrap.dedent(""" + import unittest + + class Tests(unittest.TestCase): + def test_skipped(self): + self.skipTest("because") + """) + testname = self.create_test(code=code) + + output = self.run_tests(testname, exitcode=0) + self.check_executed_tests(output, [testname]) + def test_no_tests_ran_multiple_tests_nonexistent(self): code = textwrap.dedent(""" import unittest diff --git a/Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst b/Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst new file mode 100644 index 000000000000..5a70cc5308ef --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst @@ -0,0 +1,3 @@ +:func:`test.support.run_unittest` no longer raise :exc:`TestDidNotRun` if +the test result contains skipped tests. The exception is now only raised if +no test have been run and no test have been skipped. From webhook-mailer at python.org Fri Dec 14 07:14:14 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 14 Dec 2018 12:14:14 -0000 Subject: [Python-checkins] bpo-35346: Cleanup platform.architecture() (GH-11130) Message-ID: https://github.com/python/cpython/commit/4aa917c5feaec07a6f6db87b34185ab6180e20ee commit: 4aa917c5feaec07a6f6db87b34185ab6180e20ee branch: master author: Victor Stinner committer: GitHub date: 2018-12-14T13:14:10+01:00 summary: bpo-35346: Cleanup platform.architecture() (GH-11130) struct.calcsize('P') now always works. files: M Lib/platform.py diff --git a/Lib/platform.py b/Lib/platform.py index ab0cf254bc69..0fe841c71ce6 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -651,12 +651,8 @@ def architecture(executable=sys.executable, bits='', linkage=''): # else is given as default. if not bits: import struct - try: - size = struct.calcsize('P') - except struct.error: - # Older installations can only query longs - size = struct.calcsize('l') - bits = str(size*8) + 'bit' + size = struct.calcsize('P') + bits = str(size * 8) + 'bit' # Get data from the 'file' system command if executable: From webhook-mailer at python.org Fri Dec 14 07:27:03 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 14 Dec 2018 12:27:03 -0000 Subject: [Python-checkins] bpo-34279: regrtest consider that skipped tests are ran (GH-11132) Message-ID: https://github.com/python/cpython/commit/5f252e1ebc098fff7f88fbf89d203b1dd15fe7fa commit: 5f252e1ebc098fff7f88fbf89d203b1dd15fe7fa branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-14T04:26:58-08:00 summary: bpo-34279: regrtest consider that skipped tests are ran (GH-11132) bpo-34279, bpo-35412: support.run_unittest() no longer raises TestDidNotRun if a test result contains skipped tests. The exception is now only raised if no test have been run and no test have been skipped. (cherry picked from commit 3a8f4fef4a4dd0e4a800545468eef9542e126181) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst M Lib/test/support/__init__.py M Lib/test/test_regrtest.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 512e354fabc8..5bea5474f282 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1897,7 +1897,7 @@ def _run_suite(suite): if junit_xml_list is not None: junit_xml_list.append(result.get_xml_element()) - if not result.testsRun: + if not result.testsRun and not result.skipped: raise TestDidNotRun if not result.wasSuccessful(): if len(result.errors) == 1 and not result.failures: diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index db9bd6dfc043..a67458313add 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -1004,6 +1004,7 @@ def test_bug(self): output = self.run_tests("-w", testname, exitcode=2) self.check_executed_tests(output, [testname], failed=testname, rerun=testname) + def test_no_tests_ran(self): code = textwrap.dedent(""" import unittest @@ -1017,6 +1018,19 @@ def test_bug(self): output = self.run_tests(testname, "-m", "nosuchtest", exitcode=0) self.check_executed_tests(output, [testname], no_test_ran=testname) + def test_no_tests_ran_skip(self): + code = textwrap.dedent(""" + import unittest + + class Tests(unittest.TestCase): + def test_skipped(self): + self.skipTest("because") + """) + testname = self.create_test(code=code) + + output = self.run_tests(testname, exitcode=0) + self.check_executed_tests(output, [testname]) + def test_no_tests_ran_multiple_tests_nonexistent(self): code = textwrap.dedent(""" import unittest diff --git a/Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst b/Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst new file mode 100644 index 000000000000..5a70cc5308ef --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst @@ -0,0 +1,3 @@ +:func:`test.support.run_unittest` no longer raise :exc:`TestDidNotRun` if +the test result contains skipped tests. The exception is now only raised if +no test have been run and no test have been skipped. From webhook-mailer at python.org Fri Dec 14 07:37:29 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 14 Dec 2018 12:37:29 -0000 Subject: [Python-checkins] bpo-35471: Remove the macpath module (GH-11129) Message-ID: https://github.com/python/cpython/commit/d7538dd5e3e04a8db22e1470cb2ed696bf3be160 commit: d7538dd5e3e04a8db22e1470cb2ed696bf3be160 branch: master author: Victor Stinner committer: GitHub date: 2018-12-14T13:37:26+01:00 summary: bpo-35471: Remove the macpath module (GH-11129) Python 2.4 dropped MacOS 9 support. The macpath module was deprecated in Python 3.7. This change removes it. files: A Misc/NEWS.d/next/Library/2018-12-12-16-25-21.bpo-35471.SK8jFC.rst D Doc/library/macpath.rst D Lib/macpath.py D Lib/test/test_macpath.py M Doc/library/filesys.rst M Doc/library/os.path.rst M Doc/whatsnew/3.8.rst M Lib/posixpath.py M Lib/test/test_genericpath.py M PCbuild/lib.pyproj M Tools/freeze/freeze.py diff --git a/Doc/library/filesys.rst b/Doc/library/filesys.rst index 03e085b45422..0ccf2b7bf59a 100644 --- a/Doc/library/filesys.rst +++ b/Doc/library/filesys.rst @@ -22,7 +22,6 @@ in this chapter is: fnmatch.rst linecache.rst shutil.rst - macpath.rst .. seealso:: diff --git a/Doc/library/macpath.rst b/Doc/library/macpath.rst deleted file mode 100644 index 2af51c66c50a..000000000000 --- a/Doc/library/macpath.rst +++ /dev/null @@ -1,21 +0,0 @@ -:mod:`macpath` --- Mac OS 9 path manipulation functions -======================================================= - -.. module:: macpath - :synopsis: Mac OS 9 path manipulation functions. - -**Source code:** :source:`Lib/macpath.py` - -.. deprecated-removed:: 3.7 3.8 - --------------- - -This module is the Mac OS 9 (and earlier) implementation of the :mod:`os.path` -module. It can be used to manipulate old-style Macintosh pathnames on Mac OS X -(or any other platform). - -The following functions are available in this module: :func:`normcase`, -:func:`normpath`, :func:`isabs`, :func:`join`, :func:`split`, :func:`isdir`, -:func:`isfile`, :func:`walk`, :func:`exists`. For other functions available in -:mod:`os.path` dummy counterparts are available. - diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 469b26267223..23194aee66d5 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -4,9 +4,8 @@ .. module:: os.path :synopsis: Operations on pathnames. -**Source code:** :source:`Lib/posixpath.py` (for POSIX), -:source:`Lib/ntpath.py` (for Windows NT), -and :source:`Lib/macpath.py` (for Macintosh) +**Source code:** :source:`Lib/posixpath.py` (for POSIX) and +:source:`Lib/ntpath.py` (for Windows NT). .. index:: single: path; operations @@ -52,7 +51,6 @@ the :mod:`glob` module.) * :mod:`posixpath` for UNIX-style paths * :mod:`ntpath` for Windows paths - * :mod:`macpath` for old-style MacOS paths .. versionchanged:: 3.8 diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index b38a1a767c68..f4e6f646327e 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -378,6 +378,9 @@ API and Feature Removals The following features and APIs have been removed from Python 3.8: +* The :mod:`macpath` module, deprecated in Python 3.7, has been removed. + (Contributed by Victor Stinner in :issue:`35471`.) + * The function :func:`platform.popen` has been removed, it was deprecated since Python 3.3: use :func:`os.popen` instead. diff --git a/Lib/macpath.py b/Lib/macpath.py deleted file mode 100644 index 9a12d2feee35..000000000000 --- a/Lib/macpath.py +++ /dev/null @@ -1,216 +0,0 @@ -"""Pathname and path-related operations for the Macintosh.""" - -# strings representing various path-related bits and pieces -# These are primarily for export; internally, they are hardcoded. -# Should be set before imports for resolving cyclic dependency. -curdir = ':' -pardir = '::' -extsep = '.' -sep = ':' -pathsep = '\n' -defpath = ':' -altsep = None -devnull = 'Dev:Null' - -import os -from stat import * -import genericpath -from genericpath import * -import warnings - -warnings.warn('the macpath module is deprecated in 3.7 and will be removed ' - 'in 3.8', DeprecationWarning, stacklevel=2) - -__all__ = ["normcase","isabs","join","splitdrive","split","splitext", - "basename","dirname","commonprefix","getsize","getmtime", - "getatime","getctime", "islink","exists","lexists","isdir","isfile", - "expanduser","expandvars","normpath","abspath", - "curdir","pardir","sep","pathsep","defpath","altsep","extsep", - "devnull","realpath","supports_unicode_filenames"] - -def _get_colon(path): - if isinstance(path, bytes): - return b':' - else: - return ':' - -# Normalize the case of a pathname. Dummy in Posix, but .lower() here. - -def normcase(path): - if not isinstance(path, (bytes, str)): - raise TypeError("normcase() argument must be str or bytes, " - "not '{}'".format(path.__class__.__name__)) - return path.lower() - - -def isabs(s): - """Return true if a path is absolute. - On the Mac, relative paths begin with a colon, - but as a special case, paths with no colons at all are also relative. - Anything else is absolute (the string up to the first colon is the - volume name).""" - - colon = _get_colon(s) - return colon in s and s[:1] != colon - - -def join(s, *p): - try: - colon = _get_colon(s) - path = s - if not p: - path[:0] + colon #23780: Ensure compatible data type even if p is null. - for t in p: - if (not path) or isabs(t): - path = t - continue - if t[:1] == colon: - t = t[1:] - if colon not in path: - path = colon + path - if path[-1:] != colon: - path = path + colon - path = path + t - return path - except (TypeError, AttributeError, BytesWarning): - genericpath._check_arg_types('join', s, *p) - raise - - -def split(s): - """Split a pathname into two parts: the directory leading up to the final - bit, and the basename (the filename, without colons, in that directory). - The result (s, t) is such that join(s, t) yields the original argument.""" - - colon = _get_colon(s) - if colon not in s: return s[:0], s - col = 0 - for i in range(len(s)): - if s[i:i+1] == colon: col = i + 1 - path, file = s[:col-1], s[col:] - if path and not colon in path: - path = path + colon - return path, file - - -def splitext(p): - if isinstance(p, bytes): - return genericpath._splitext(p, b':', altsep, b'.') - else: - return genericpath._splitext(p, sep, altsep, extsep) -splitext.__doc__ = genericpath._splitext.__doc__ - -def splitdrive(p): - """Split a pathname into a drive specification and the rest of the - path. Useful on DOS/Windows/NT; on the Mac, the drive is always - empty (don't use the volume name -- it doesn't have the same - syntactic and semantic oddities as DOS drive letters, such as there - being a separate current directory per drive).""" - - return p[:0], p - - -# Short interfaces to split() - -def dirname(s): return split(s)[0] -def basename(s): return split(s)[1] - -def ismount(s): - if not isabs(s): - return False - components = split(s) - return len(components) == 2 and not components[1] - -def islink(s): - """Return true if the pathname refers to a symbolic link.""" - - try: - import Carbon.File - return Carbon.File.ResolveAliasFile(s, 0)[2] - except: - return False - -# Is `stat`/`lstat` a meaningful difference on the Mac? This is safe in any -# case. - -def lexists(path): - """Test whether a path exists. Returns True for broken symbolic links""" - - try: - st = os.lstat(path) - except (OSError, ValueError): - return False - return True - -def expandvars(path): - """Dummy to retain interface-compatibility with other operating systems.""" - return path - - -def expanduser(path): - """Dummy to retain interface-compatibility with other operating systems.""" - return path - -class norm_error(Exception): - """Path cannot be normalized""" - -def normpath(s): - """Normalize a pathname. Will return the same result for - equivalent paths.""" - - colon = _get_colon(s) - - if colon not in s: - return colon + s - - comps = s.split(colon) - i = 1 - while i < len(comps)-1: - if not comps[i] and comps[i-1]: - if i > 1: - del comps[i-1:i+1] - i = i - 1 - else: - # best way to handle this is to raise an exception - raise norm_error('Cannot use :: immediately after volume name') - else: - i = i + 1 - - s = colon.join(comps) - - # remove trailing ":" except for ":" and "Volume:" - if s[-1:] == colon and len(comps) > 2 and s != colon*len(s): - s = s[:-1] - return s - -def abspath(path): - """Return an absolute path.""" - if not isabs(path): - if isinstance(path, bytes): - cwd = os.getcwdb() - else: - cwd = os.getcwd() - path = join(cwd, path) - return normpath(path) - -# realpath is a no-op on systems without islink support -def realpath(path): - path = abspath(path) - try: - import Carbon.File - except ImportError: - return path - if not path: - return path - colon = _get_colon(path) - components = path.split(colon) - path = components[0] + colon - for c in components[1:]: - path = join(path, c) - try: - path = Carbon.File.FSResolveAliasFile(path, 1)[0].as_pathname() - except Carbon.File.Error: - pass - return path - -supports_unicode_filenames = True diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 4914a1728ab5..12ab2eadbf9b 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -2,9 +2,9 @@ Instead of importing this module directly, import os and refer to this module as os.path. The "os.path" name is an alias for this -module on Posix systems; on other systems (e.g. Mac, Windows), +module on Posix systems; on other systems (e.g. Windows), os.path provides the same operations in a manner specific to that -platform, and is an alias to another module (e.g. macpath, ntpath). +platform, and is an alias to another module (e.g. ntpath). Some of this can actually be useful on non-Posix systems too, e.g. for manipulation of the pathname component of URLs. diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index 08e1c12101c7..9d5ac44b6d06 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -1,5 +1,5 @@ """ -Tests common to genericpath, macpath, ntpath and posixpath +Tests common to genericpath, ntpath and posixpath """ import genericpath @@ -334,7 +334,7 @@ def test_invalid_paths(self): func(b'/tmp\x00abcds') # Following TestCase is not supposed to be run from test_genericpath. -# It is inherited by other test modules (macpath, ntpath, posixpath). +# It is inherited by other test modules (ntpath, posixpath). class CommonTest(GenericTest): common_attributes = GenericTest.common_attributes + [ @@ -373,8 +373,6 @@ def test_splitdrive(self): self.assertEqual(splitdrive(b":foo:bar"), (b"", b":foo:bar")) def test_expandvars(self): - if self.pathmodule.__name__ == 'macpath': - self.skipTest('macpath.expandvars is a stub') expandvars = self.pathmodule.expandvars with support.EnvironmentVarGuard() as env: env.clear() @@ -407,8 +405,6 @@ def test_expandvars(self): @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII') def test_expandvars_nonascii(self): - if self.pathmodule.__name__ == 'macpath': - self.skipTest('macpath.expandvars is a stub') expandvars = self.pathmodule.expandvars def check(value, expected): self.assertEqual(expandvars(value), expected) diff --git a/Lib/test/test_macpath.py b/Lib/test/test_macpath.py deleted file mode 100644 index 540bf2200c19..000000000000 --- a/Lib/test/test_macpath.py +++ /dev/null @@ -1,155 +0,0 @@ -from test import test_genericpath -import unittest -import warnings - - -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", "the macpath module is deprecated", - DeprecationWarning) - import macpath - - -class MacPathTestCase(unittest.TestCase): - - def test_abspath(self): - self.assertEqual(macpath.abspath("xx:yy"), "xx:yy") - - def test_isabs(self): - isabs = macpath.isabs - self.assertTrue(isabs("xx:yy")) - self.assertTrue(isabs("xx:yy:")) - self.assertTrue(isabs("xx:")) - self.assertFalse(isabs("foo")) - self.assertFalse(isabs(":foo")) - self.assertFalse(isabs(":foo:bar")) - self.assertFalse(isabs(":foo:bar:")) - - self.assertTrue(isabs(b"xx:yy")) - self.assertTrue(isabs(b"xx:yy:")) - self.assertTrue(isabs(b"xx:")) - self.assertFalse(isabs(b"foo")) - self.assertFalse(isabs(b":foo")) - self.assertFalse(isabs(b":foo:bar")) - self.assertFalse(isabs(b":foo:bar:")) - - def test_split(self): - split = macpath.split - self.assertEqual(split("foo:bar"), - ('foo:', 'bar')) - self.assertEqual(split("conky:mountpoint:foo:bar"), - ('conky:mountpoint:foo', 'bar')) - - self.assertEqual(split(":"), ('', '')) - self.assertEqual(split(":conky:mountpoint:"), - (':conky:mountpoint', '')) - - self.assertEqual(split(b"foo:bar"), - (b'foo:', b'bar')) - self.assertEqual(split(b"conky:mountpoint:foo:bar"), - (b'conky:mountpoint:foo', b'bar')) - - self.assertEqual(split(b":"), (b'', b'')) - self.assertEqual(split(b":conky:mountpoint:"), - (b':conky:mountpoint', b'')) - - def test_join(self): - join = macpath.join - self.assertEqual(join('a', 'b'), ':a:b') - self.assertEqual(join(':a', 'b'), ':a:b') - self.assertEqual(join(':a:', 'b'), ':a:b') - self.assertEqual(join(':a::', 'b'), ':a::b') - self.assertEqual(join(':a', '::b'), ':a::b') - self.assertEqual(join('a', ':'), ':a:') - self.assertEqual(join('a:', ':'), 'a:') - self.assertEqual(join('a', ''), ':a:') - self.assertEqual(join('a:', ''), 'a:') - self.assertEqual(join('', ''), '') - self.assertEqual(join('', 'a:b'), 'a:b') - self.assertEqual(join('', 'a', 'b'), ':a:b') - self.assertEqual(join('a:b', 'c'), 'a:b:c') - self.assertEqual(join('a:b', ':c'), 'a:b:c') - self.assertEqual(join('a', ':b', ':c'), ':a:b:c') - self.assertEqual(join('a', 'b:'), 'b:') - self.assertEqual(join('a:', 'b:'), 'b:') - - self.assertEqual(join(b'a', b'b'), b':a:b') - self.assertEqual(join(b':a', b'b'), b':a:b') - self.assertEqual(join(b':a:', b'b'), b':a:b') - self.assertEqual(join(b':a::', b'b'), b':a::b') - self.assertEqual(join(b':a', b'::b'), b':a::b') - self.assertEqual(join(b'a', b':'), b':a:') - self.assertEqual(join(b'a:', b':'), b'a:') - self.assertEqual(join(b'a', b''), b':a:') - self.assertEqual(join(b'a:', b''), b'a:') - self.assertEqual(join(b'', b''), b'') - self.assertEqual(join(b'', b'a:b'), b'a:b') - self.assertEqual(join(b'', b'a', b'b'), b':a:b') - self.assertEqual(join(b'a:b', b'c'), b'a:b:c') - self.assertEqual(join(b'a:b', b':c'), b'a:b:c') - self.assertEqual(join(b'a', b':b', b':c'), b':a:b:c') - self.assertEqual(join(b'a', b'b:'), b'b:') - self.assertEqual(join(b'a:', b'b:'), b'b:') - - def test_splitext(self): - splitext = macpath.splitext - self.assertEqual(splitext(":foo.ext"), (':foo', '.ext')) - self.assertEqual(splitext("foo:foo.ext"), ('foo:foo', '.ext')) - self.assertEqual(splitext(".ext"), ('.ext', '')) - self.assertEqual(splitext("foo.ext:foo"), ('foo.ext:foo', '')) - self.assertEqual(splitext(":foo.ext:"), (':foo.ext:', '')) - self.assertEqual(splitext(""), ('', '')) - self.assertEqual(splitext("foo.bar.ext"), ('foo.bar', '.ext')) - - self.assertEqual(splitext(b":foo.ext"), (b':foo', b'.ext')) - self.assertEqual(splitext(b"foo:foo.ext"), (b'foo:foo', b'.ext')) - self.assertEqual(splitext(b".ext"), (b'.ext', b'')) - self.assertEqual(splitext(b"foo.ext:foo"), (b'foo.ext:foo', b'')) - self.assertEqual(splitext(b":foo.ext:"), (b':foo.ext:', b'')) - self.assertEqual(splitext(b""), (b'', b'')) - self.assertEqual(splitext(b"foo.bar.ext"), (b'foo.bar', b'.ext')) - - def test_ismount(self): - ismount = macpath.ismount - self.assertEqual(ismount("a:"), True) - self.assertEqual(ismount("a:b"), False) - self.assertEqual(ismount("a:b:"), True) - self.assertEqual(ismount(""), False) - self.assertEqual(ismount(":"), False) - - self.assertEqual(ismount(b"a:"), True) - self.assertEqual(ismount(b"a:b"), False) - self.assertEqual(ismount(b"a:b:"), True) - self.assertEqual(ismount(b""), False) - self.assertEqual(ismount(b":"), False) - - def test_normpath(self): - normpath = macpath.normpath - self.assertEqual(normpath("a:b"), "a:b") - self.assertEqual(normpath("a"), ":a") - self.assertEqual(normpath("a:b::c"), "a:c") - self.assertEqual(normpath("a:b:c:::d"), "a:d") - self.assertRaises(macpath.norm_error, normpath, "a::b") - self.assertRaises(macpath.norm_error, normpath, "a:b:::c") - self.assertEqual(normpath(":"), ":") - self.assertEqual(normpath("a:"), "a:") - self.assertEqual(normpath("a:b:"), "a:b") - - self.assertEqual(normpath(b"a:b"), b"a:b") - self.assertEqual(normpath(b"a"), b":a") - self.assertEqual(normpath(b"a:b::c"), b"a:c") - self.assertEqual(normpath(b"a:b:c:::d"), b"a:d") - self.assertRaises(macpath.norm_error, normpath, b"a::b") - self.assertRaises(macpath.norm_error, normpath, b"a:b:::c") - self.assertEqual(normpath(b":"), b":") - self.assertEqual(normpath(b"a:"), b"a:") - self.assertEqual(normpath(b"a:b:"), b"a:b") - - -class MacCommonTest(test_genericpath.CommonTest, unittest.TestCase): - pathmodule = macpath - - test_relpath_errors = None - - -if __name__ == "__main__": - unittest.main() diff --git a/Misc/NEWS.d/next/Library/2018-12-12-16-25-21.bpo-35471.SK8jFC.rst b/Misc/NEWS.d/next/Library/2018-12-12-16-25-21.bpo-35471.SK8jFC.rst new file mode 100644 index 000000000000..fb4b9ff5b7d3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-12-16-25-21.bpo-35471.SK8jFC.rst @@ -0,0 +1,2 @@ +Python 2.4 dropped MacOS 9 support. The macpath module was deprecated in +Python 3.7. The module is now removed. diff --git a/PCbuild/lib.pyproj b/PCbuild/lib.pyproj index 273d5ef7529e..701b55f7d111 100644 --- a/PCbuild/lib.pyproj +++ b/PCbuild/lib.pyproj @@ -655,7 +655,6 @@ - @@ -1176,7 +1175,6 @@ - @@ -1816,4 +1814,4 @@ - \ No newline at end of file + diff --git a/Tools/freeze/freeze.py b/Tools/freeze/freeze.py index d602f5853975..08d09e706205 100755 --- a/Tools/freeze/freeze.py +++ b/Tools/freeze/freeze.py @@ -124,7 +124,7 @@ def main(): # default the exclude list for each platform if win: exclude = exclude + [ - 'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix', ] + 'dos', 'dospath', 'mac', 'macfs', 'MACFS', 'posix', ] fail_import = exclude[:] From webhook-mailer at python.org Fri Dec 14 07:44:13 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 14 Dec 2018 12:44:13 -0000 Subject: [Python-checkins] bpo-34279: regrtest consider that skipped tests are ran (GH-11132) (GH-11158) Message-ID: https://github.com/python/cpython/commit/34b7c438b8dc0a1e7e23c9b2d7ce7f8a7c31b4f4 commit: 34b7c438b8dc0a1e7e23c9b2d7ce7f8a7c31b4f4 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-12-14T13:44:08+01:00 summary: bpo-34279: regrtest consider that skipped tests are ran (GH-11132) (GH-11158) bpo-34279, bpo-35412: support.run_unittest() no longer raises TestDidNotRun if a test result contains skipped tests. The exception is now only raised if no test have been run and no test have been skipped. (cherry picked from commit 3a8f4fef4a4dd0e4a800545468eef9542e126181) files: A Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst M Lib/test/support/__init__.py M Lib/test/test_regrtest.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index aaf028632a59..9effdddd27dc 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1543,7 +1543,7 @@ def _run_suite(suite): runner = BasicTestRunner() result = runner.run(suite) - if not result.testsRun: + if not result.testsRun and not result.skipped: raise TestDidNotRun if not result.wasSuccessful(): if len(result.errors) == 1 and not result.failures: diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 015adc3fa06c..872ba6461107 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -734,6 +734,19 @@ def test_main(): output = self.run_tests("-m", "nosuchtest", testname, exitcode=0) self.check_executed_tests(output, [testname], no_test_ran=testname) + def test_no_tests_ran_skip(self): + code = textwrap.dedent(""" + import unittest + + class Tests(unittest.TestCase): + def test_skipped(self): + self.skipTest("because") + """) + testname = self.create_test(code=code) + + output = self.run_tests(testname, exitcode=0) + self.check_executed_tests(output, [testname]) + def test_no_tests_ran_multiple_tests_nonexistent(self): code = textwrap.dedent(""" import unittest diff --git a/Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst b/Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst new file mode 100644 index 000000000000..5a70cc5308ef --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-12-18-20-18.bpo-34279.DhKcuP.rst @@ -0,0 +1,3 @@ +:func:`test.support.run_unittest` no longer raise :exc:`TestDidNotRun` if +the test result contains skipped tests. The exception is now only raised if +no test have been run and no test have been skipped. From webhook-mailer at python.org Fri Dec 14 12:13:19 2018 From: webhook-mailer at python.org (Steve Dower) Date: Fri, 14 Dec 2018 17:13:19 -0000 Subject: [Python-checkins] bpo-35402: Update Windows build to use Tcl and Tk 8.6.9 (GH-11146) Message-ID: https://github.com/python/cpython/commit/f8e9bd568adf85c1e4aea1dda542a96b027797e2 commit: f8e9bd568adf85c1e4aea1dda542a96b027797e2 branch: master author: Steve Dower committer: GitHub date: 2018-12-14T09:13:15-08:00 summary: bpo-35402: Update Windows build to use Tcl and Tk 8.6.9 (GH-11146) files: A Misc/NEWS.d/next/Windows/2018-12-13-13-30-04.bpo-35402.n_mXb2.rst M PCbuild/get_externals.bat M PCbuild/tcltk.props diff --git a/Misc/NEWS.d/next/Windows/2018-12-13-13-30-04.bpo-35402.n_mXb2.rst b/Misc/NEWS.d/next/Windows/2018-12-13-13-30-04.bpo-35402.n_mXb2.rst new file mode 100644 index 000000000000..56cf17c09dcf --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-12-13-13-30-04.bpo-35402.n_mXb2.rst @@ -0,0 +1 @@ +Update Windows build to use Tcl and Tk 8.6.9 diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 7a8de1e7612c..175a0513e77c 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -51,8 +51,8 @@ set libraries= set libraries=%libraries% bzip2-1.0.6 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.0j set libraries=%libraries% sqlite-3.21.0.0 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.8.0 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.8.0 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.9.0 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.9.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 set libraries=%libraries% xz-5.2.2 set libraries=%libraries% zlib-1.2.11 @@ -73,7 +73,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.0j -if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.8.0 +if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.9.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 for %%b in (%binaries%) do ( diff --git a/PCbuild/tcltk.props b/PCbuild/tcltk.props index 0a59c0894d74..b185cb7b1e28 100644 --- a/PCbuild/tcltk.props +++ b/PCbuild/tcltk.props @@ -4,7 +4,7 @@ 8 6 - 8 + 9 0 $(TclMajorVersion) $(TclMinorVersion) From webhook-mailer at python.org Fri Dec 14 12:49:15 2018 From: webhook-mailer at python.org (Steve Dower) Date: Fri, 14 Dec 2018 17:49:15 -0000 Subject: [Python-checkins] bpo-35402: Update Windows build to use Tcl and Tk 8.6.9 (GH-11146) Message-ID: https://github.com/python/cpython/commit/77824ef6e50e8a47a0b57df2d9f3b48bffd414ac commit: 77824ef6e50e8a47a0b57df2d9f3b48bffd414ac branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Steve Dower date: 2018-12-14T09:49:10-08:00 summary: bpo-35402: Update Windows build to use Tcl and Tk 8.6.9 (GH-11146) (cherry picked from commit f8e9bd568adf85c1e4aea1dda542a96b027797e2) Co-authored-by: Steve Dower files: A Misc/NEWS.d/next/Windows/2018-12-13-13-30-04.bpo-35402.n_mXb2.rst M PCbuild/get_externals.bat M PCbuild/tcltk.props diff --git a/Misc/NEWS.d/next/Windows/2018-12-13-13-30-04.bpo-35402.n_mXb2.rst b/Misc/NEWS.d/next/Windows/2018-12-13-13-30-04.bpo-35402.n_mXb2.rst new file mode 100644 index 000000000000..56cf17c09dcf --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-12-13-13-30-04.bpo-35402.n_mXb2.rst @@ -0,0 +1 @@ +Update Windows build to use Tcl and Tk 8.6.9 diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 7a8de1e7612c..175a0513e77c 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -51,8 +51,8 @@ set libraries= set libraries=%libraries% bzip2-1.0.6 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.0j set libraries=%libraries% sqlite-3.21.0.0 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.8.0 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.8.0 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.9.0 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.9.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 set libraries=%libraries% xz-5.2.2 set libraries=%libraries% zlib-1.2.11 @@ -73,7 +73,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.0j -if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.8.0 +if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.9.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 for %%b in (%binaries%) do ( diff --git a/PCbuild/tcltk.props b/PCbuild/tcltk.props index 0a59c0894d74..b185cb7b1e28 100644 --- a/PCbuild/tcltk.props +++ b/PCbuild/tcltk.props @@ -4,7 +4,7 @@ 8 6 - 8 + 9 0 $(TclMajorVersion) $(TclMinorVersion) From webhook-mailer at python.org Fri Dec 14 15:28:57 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 14 Dec 2018 20:28:57 -0000 Subject: [Python-checkins] =?utf-8?q?bpo-35450=3A_reflect_in_docs_that_ve?= =?utf-8?q?nv_module_is_not_always_creating_a_=E2=80=A6_=28GH-11144=29?= Message-ID: https://github.com/python/cpython/commit/f5107dfd42121ef40b13eb678705802f0ff02cf9 commit: f5107dfd42121ef40b13eb678705802f0ff02cf9 branch: master author: mkkot committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-14T12:28:52-08:00 summary: bpo-35450: reflect in docs that venv module is not always creating a ? (GH-11144) ?copy of python binary https://bugs.python.org/issue35450 files: M Doc/using/venv-create.inc diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc index 45abd59d1868..ba5096abd370 100644 --- a/Doc/using/venv-create.inc +++ b/Doc/using/venv-create.inc @@ -7,9 +7,10 @@ Running this command creates the target directory (creating any parent directories that don't exist already) and places a ``pyvenv.cfg`` file in it with a ``home`` key pointing to the Python installation from which the command was run. It also creates a ``bin`` (or ``Scripts`` on Windows) subdirectory -containing a copy of the ``python`` binary (or binaries, in the case of -Windows). It also creates an (initially empty) ``lib/pythonX.Y/site-packages`` -subdirectory (on Windows, this is ``Lib\site-packages``). If an existing +containing a copy/symlink of the Python binary/binaries (as appropriate for the +platform or arguments used at environment creation time). It also creates an +(initially empty) ``lib/pythonX.Y/site-packages`` subdirectory +(on Windows, this is ``Lib\site-packages``). If an existing directory is specified, it will be re-used. .. deprecated:: 3.6 From webhook-mailer at python.org Fri Dec 14 15:37:48 2018 From: webhook-mailer at python.org (Brett Cannon) Date: Fri, 14 Dec 2018 20:37:48 -0000 Subject: [Python-checkins] bpo-35450: reflect in docs that venv module is not always creating a copy of the Python binary (GH-11144) (GH-11168) Message-ID: https://github.com/python/cpython/commit/1fb312ce1f147ea84ecb6f5993a20d1a85c53dc3 commit: 1fb312ce1f147ea84ecb6f5993a20d1a85c53dc3 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Brett Cannon date: 2018-12-14T12:37:45-08:00 summary: bpo-35450: reflect in docs that venv module is not always creating a copy of the Python binary (GH-11144) (GH-11168) files: M Doc/using/venv-create.inc diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc index 7e8ae4a85f8a..d2f76afc2789 100644 --- a/Doc/using/venv-create.inc +++ b/Doc/using/venv-create.inc @@ -7,9 +7,10 @@ Running this command creates the target directory (creating any parent directories that don't exist already) and places a ``pyvenv.cfg`` file in it with a ``home`` key pointing to the Python installation from which the command was run. It also creates a ``bin`` (or ``Scripts`` on Windows) subdirectory -containing a copy of the ``python`` binary (or binaries, in the case of -Windows). It also creates an (initially empty) ``lib/pythonX.Y/site-packages`` -subdirectory (on Windows, this is ``Lib\site-packages``). If an existing +containing a copy/symlink of the Python binary/binaries (as appropriate for the +platform or arguments used at environment creation time). It also creates an +(initially empty) ``lib/pythonX.Y/site-packages`` subdirectory +(on Windows, this is ``Lib\site-packages``). If an existing directory is specified, it will be re-used. .. deprecated:: 3.6 From webhook-mailer at python.org Fri Dec 14 15:38:26 2018 From: webhook-mailer at python.org (Brett Cannon) Date: Fri, 14 Dec 2018 20:38:26 -0000 Subject: [Python-checkins] bpo-35450: reflect in docs that venv module is not always creating a copy of the Python binary (GH-11144) (GH-11167) Message-ID: https://github.com/python/cpython/commit/d5176fe2bcd35dc8d70d13220b58fa7ccd05b47a commit: d5176fe2bcd35dc8d70d13220b58fa7ccd05b47a branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Brett Cannon date: 2018-12-14T12:38:23-08:00 summary: bpo-35450: reflect in docs that venv module is not always creating a copy of the Python binary (GH-11144) (GH-11167) https://bugs.python.org/issue35450 (cherry picked from commit f5107dfd42121ef40b13eb678705802f0ff02cf9) Co-authored-by: mkkot files: M Doc/using/venv-create.inc diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc index 45abd59d1868..ba5096abd370 100644 --- a/Doc/using/venv-create.inc +++ b/Doc/using/venv-create.inc @@ -7,9 +7,10 @@ Running this command creates the target directory (creating any parent directories that don't exist already) and places a ``pyvenv.cfg`` file in it with a ``home`` key pointing to the Python installation from which the command was run. It also creates a ``bin`` (or ``Scripts`` on Windows) subdirectory -containing a copy of the ``python`` binary (or binaries, in the case of -Windows). It also creates an (initially empty) ``lib/pythonX.Y/site-packages`` -subdirectory (on Windows, this is ``Lib\site-packages``). If an existing +containing a copy/symlink of the Python binary/binaries (as appropriate for the +platform or arguments used at environment creation time). It also creates an +(initially empty) ``lib/pythonX.Y/site-packages`` subdirectory +(on Windows, this is ``Lib\site-packages``). If an existing directory is specified, it will be re-used. .. deprecated:: 3.6 From solipsis at pitrou.net Sat Dec 15 04:10:33 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 15 Dec 2018 09:10:33 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=-2 Message-ID: <20181215091033.1.2209E36236CDCF11@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [0, -7, 1] memory blocks, sum=-6 test_functools leaked [0, 3, 1] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogngtRYV', '--timeout', '7200'] From solipsis at pitrou.net Sun Dec 16 04:07:09 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 16 Dec 2018 09:07:09 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=2 Message-ID: <20181216090709.1.39A3579E5B650BE5@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_asyncio leaked [3, 0, 0] memory blocks, sum=3 test_collections leaked [0, -7, 1] memory blocks, sum=-6 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_fork leaked [2, 0, -2] memory blocks, sum=0 test_multiprocessing_forkserver leaked [1, -2, 2] memory blocks, sum=1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogrl4OFE', '--timeout', '7200'] From webhook-mailer at python.org Sun Dec 16 12:00:47 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Sun, 16 Dec 2018 17:00:47 -0000 Subject: [Python-checkins] bpo-35499: make profile-opt don't override CFLAGS_NODIST (GH-11164) Message-ID: https://github.com/python/cpython/commit/640ed520dd6a43a8bf470b79542f58b5d57af9de commit: 640ed520dd6a43a8bf470b79542f58b5d57af9de branch: master author: Victor Stinner committer: GitHub date: 2018-12-16T18:00:42+01:00 summary: bpo-35499: make profile-opt don't override CFLAGS_NODIST (GH-11164) "make profile-opt" no longer replaces CFLAGS_NODIST with CFLAGS. It now adds profile-guided optimization (PGO) flags to CFLAGS_NODIST, existing CFLAGS_NODIST flags are kept. files: A Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst M Makefile.pre.in diff --git a/Makefile.pre.in b/Makefile.pre.in index f16eb96418e9..9c790f1bc60b 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -496,7 +496,7 @@ profile-run-stamp: touch $@ build_all_generate_profile: - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" run_profile_task: @ # FIXME: can't run for a cross build @@ -510,7 +510,7 @@ build_all_merge_profile: profile-opt: profile-run-stamp @echo "Rebuilding with profile guided optimizations:" -rm -f profile-clean-stamp - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)" # Compile and run with gcov .PHONY=coverage coverage-lcov coverage-report diff --git a/Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst b/Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst new file mode 100644 index 000000000000..ed730b9d9b4a --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst @@ -0,0 +1,3 @@ +``make profile-opt`` no longer replaces ``CFLAGS_NODIST`` with ``CFLAGS``. It +now adds profile-guided optimization (PGO) flags to ``CFLAGS_NODIST``: existing +``CFLAGS_NODIST`` flags are kept. From webhook-mailer at python.org Sun Dec 16 14:34:13 2018 From: webhook-mailer at python.org (Andrew Svetlov) Date: Sun, 16 Dec 2018 19:34:13 -0000 Subject: [Python-checkins] bpo-35511: Trivial docs updates for profile and resource library modules. (GH-11124) Message-ID: https://github.com/python/cpython/commit/b912f9342e7a37d170ba659c13c959115c11545a commit: b912f9342e7a37d170ba659c13c959115c11545a branch: master author: Beomsoo Kim committer: Andrew Svetlov date: 2018-12-16T21:34:08+02:00 summary: bpo-35511: Trivial docs updates for profile and resource library modules. (GH-11124) polish documentation for profile and resource modules files: A Misc/NEWS.d/next/Documentation/2018-12-16-16-14-44.bpo-35511.iVcyav.rst M Doc/library/profile.rst M Doc/library/resource.rst diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst index 9ceb81603c95..d8039fd28e0d 100644 --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -265,8 +265,8 @@ functions: ps.print_stats() print(s.getvalue()) - The :class:`Profile` class can also be used as a context manager (see - :ref:`typecontextmanager`):: + The :class:`Profile` class can also be used as a context manager (supported + only in :mod:`cProfile` module. see :ref:`typecontextmanager`):: import cProfile @@ -280,11 +280,11 @@ functions: .. method:: enable() - Start collecting profiling data. + Start collecting profiling data. Only in :mod:`cProfile`. .. method:: disable() - Stop collecting profiling data. + Stop collecting profiling data. Only in :mod:`cProfile`. .. method:: create_stats() @@ -540,9 +540,9 @@ less overhead (as the code does not need to be instrumented), but provides only relative indications of where time is being spent. In Python, since there is an interpreter active during execution, the presence -of instrumented code is not required to do deterministic profiling. Python -automatically provides a :dfn:`hook` (optional callback) for each event. In -addition, the interpreted nature of Python tends to add so much overhead to +of instrumented code is not required in order to do deterministic profiling. +Python automatically provides a :dfn:`hook` (optional callback) for each event. +In addition, the interpreted nature of Python tends to add so much overhead to execution, that deterministic profiling tends to only add small processing overhead in typical applications. The result is that deterministic profiling is not that expensive, yet provides extensive run time statistics about the diff --git a/Doc/library/resource.rst b/Doc/library/resource.rst index bd49c87f1d78..2ed15c136736 100644 --- a/Doc/library/resource.rst +++ b/Doc/library/resource.rst @@ -261,6 +261,20 @@ These functions are used to retrieve resource usage information: *who* parameter should be specified using one of the :const:`RUSAGE_\*` constants described below. + A simple example:: + + from resource import * + import time + + # a non CPU-bound task + time.sleep(3) + print(getrusage(RUSAGE_SELF)) + + # a CPU-bound task + for i in range(10 ** 8): + _ = 1 + 1 + print(getrusage(RUSAGE_SELF)) + The fields of the return value each describe how a particular system resource has been used, e.g. amount of time spent running is user mode or number of times the process was swapped out of main memory. Some values are dependent on the @@ -275,41 +289,41 @@ These functions are used to retrieve resource usage information: remaining values are integers. Consult the :manpage:`getrusage(2)` man page for detailed information about these values. A brief summary is presented here: - +--------+---------------------+-------------------------------+ - | Index | Field | Resource | - +========+=====================+===============================+ - | ``0`` | :attr:`ru_utime` | time in user mode (float) | - +--------+---------------------+-------------------------------+ - | ``1`` | :attr:`ru_stime` | time in system mode (float) | - +--------+---------------------+-------------------------------+ - | ``2`` | :attr:`ru_maxrss` | maximum resident set size | - +--------+---------------------+-------------------------------+ - | ``3`` | :attr:`ru_ixrss` | shared memory size | - +--------+---------------------+-------------------------------+ - | ``4`` | :attr:`ru_idrss` | unshared memory size | - +--------+---------------------+-------------------------------+ - | ``5`` | :attr:`ru_isrss` | unshared stack size | - +--------+---------------------+-------------------------------+ - | ``6`` | :attr:`ru_minflt` | page faults not requiring I/O | - +--------+---------------------+-------------------------------+ - | ``7`` | :attr:`ru_majflt` | page faults requiring I/O | - +--------+---------------------+-------------------------------+ - | ``8`` | :attr:`ru_nswap` | number of swap outs | - +--------+---------------------+-------------------------------+ - | ``9`` | :attr:`ru_inblock` | block input operations | - +--------+---------------------+-------------------------------+ - | ``10`` | :attr:`ru_oublock` | block output operations | - +--------+---------------------+-------------------------------+ - | ``11`` | :attr:`ru_msgsnd` | messages sent | - +--------+---------------------+-------------------------------+ - | ``12`` | :attr:`ru_msgrcv` | messages received | - +--------+---------------------+-------------------------------+ - | ``13`` | :attr:`ru_nsignals` | signals received | - +--------+---------------------+-------------------------------+ - | ``14`` | :attr:`ru_nvcsw` | voluntary context switches | - +--------+---------------------+-------------------------------+ - | ``15`` | :attr:`ru_nivcsw` | involuntary context switches | - +--------+---------------------+-------------------------------+ + +--------+---------------------+---------------------------------------+ + | Index | Field | Resource | + +========+=====================+=======================================+ + | ``0`` | :attr:`ru_utime` | time in user mode (float seconds) | + +--------+---------------------+---------------------------------------+ + | ``1`` | :attr:`ru_stime` | time in system mode (float seconds) | + +--------+---------------------+---------------------------------------+ + | ``2`` | :attr:`ru_maxrss` | maximum resident set size | + +--------+---------------------+---------------------------------------+ + | ``3`` | :attr:`ru_ixrss` | shared memory size | + +--------+---------------------+---------------------------------------+ + | ``4`` | :attr:`ru_idrss` | unshared memory size | + +--------+---------------------+---------------------------------------+ + | ``5`` | :attr:`ru_isrss` | unshared stack size | + +--------+---------------------+---------------------------------------+ + | ``6`` | :attr:`ru_minflt` | page faults not requiring I/O | + +--------+---------------------+---------------------------------------+ + | ``7`` | :attr:`ru_majflt` | page faults requiring I/O | + +--------+---------------------+---------------------------------------+ + | ``8`` | :attr:`ru_nswap` | number of swap outs | + +--------+---------------------+---------------------------------------+ + | ``9`` | :attr:`ru_inblock` | block input operations | + +--------+---------------------+---------------------------------------+ + | ``10`` | :attr:`ru_oublock` | block output operations | + +--------+---------------------+---------------------------------------+ + | ``11`` | :attr:`ru_msgsnd` | messages sent | + +--------+---------------------+---------------------------------------+ + | ``12`` | :attr:`ru_msgrcv` | messages received | + +--------+---------------------+---------------------------------------+ + | ``13`` | :attr:`ru_nsignals` | signals received | + +--------+---------------------+---------------------------------------+ + | ``14`` | :attr:`ru_nvcsw` | voluntary context switches | + +--------+---------------------+---------------------------------------+ + | ``15`` | :attr:`ru_nivcsw` | involuntary context switches | + +--------+---------------------+---------------------------------------+ This function will raise a :exc:`ValueError` if an invalid *who* parameter is specified. It may also raise :exc:`error` exception in unusual circumstances. diff --git a/Misc/NEWS.d/next/Documentation/2018-12-16-16-14-44.bpo-35511.iVcyav.rst b/Misc/NEWS.d/next/Documentation/2018-12-16-16-14-44.bpo-35511.iVcyav.rst new file mode 100644 index 000000000000..69c9d9fc56e6 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2018-12-16-16-14-44.bpo-35511.iVcyav.rst @@ -0,0 +1,3 @@ +Specified that profile.Profile class doesn't not support enable or disable +methods. Also, elaborated that Profile object as a context manager is only +supported in cProfile module. From webhook-mailer at python.org Sun Dec 16 17:24:10 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Sun, 16 Dec 2018 22:24:10 -0000 Subject: [Python-checkins] bpo-35499: make profile-opt don't override CFLAGS_NODIST (GH-11164) (GH-11179) Message-ID: https://github.com/python/cpython/commit/9a4758550d96030ee7e7f7c7c68b435db1a2a825 commit: 9a4758550d96030ee7e7f7c7c68b435db1a2a825 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Victor Stinner date: 2018-12-16T23:24:04+01:00 summary: bpo-35499: make profile-opt don't override CFLAGS_NODIST (GH-11164) (GH-11179) "make profile-opt" no longer replaces CFLAGS_NODIST with CFLAGS. It now adds profile-guided optimization (PGO) flags to CFLAGS_NODIST, existing CFLAGS_NODIST flags are kept. (cherry picked from commit 640ed520dd6a43a8bf470b79542f58b5d57af9de) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst M Makefile.pre.in diff --git a/Makefile.pre.in b/Makefile.pre.in index 7d9cbd5c8bf3..4ec2d3511fc4 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -497,7 +497,7 @@ profile-run-stamp: touch $@ build_all_generate_profile: - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" run_profile_task: @ # FIXME: can't run for a cross build @@ -511,7 +511,7 @@ build_all_merge_profile: profile-opt: profile-run-stamp @echo "Rebuilding with profile guided optimizations:" -rm -f profile-clean-stamp - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)" # Compile and run with gcov .PHONY=coverage coverage-lcov coverage-report diff --git a/Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst b/Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst new file mode 100644 index 000000000000..ed730b9d9b4a --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst @@ -0,0 +1,3 @@ +``make profile-opt`` no longer replaces ``CFLAGS_NODIST`` with ``CFLAGS``. It +now adds profile-guided optimization (PGO) flags to ``CFLAGS_NODIST``: existing +``CFLAGS_NODIST`` flags are kept. From webhook-mailer at python.org Sun Dec 16 17:40:53 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Sun, 16 Dec 2018 22:40:53 -0000 Subject: [Python-checkins] bpo-35491, multiprocessing: replace "RUN" with RUN (GH-11178) Message-ID: https://github.com/python/cpython/commit/2dfe3511fe310c559d5571c52dcac381f33fd3a6 commit: 2dfe3511fe310c559d5571c52dcac381f33fd3a6 branch: master author: Victor Stinner committer: GitHub date: 2018-12-16T23:40:49+01:00 summary: bpo-35491, multiprocessing: replace "RUN" with RUN (GH-11178) files: M Lib/multiprocessing/pool.py diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py index cede9bbd5d40..1e26a9b56f0c 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -486,7 +486,7 @@ def _handle_results(outqueue, get, cache): util.debug('result handler got EOFError/OSError -- exiting') return - if thread._state != "RUN": + if thread._state != RUN: assert thread._state == TERMINATE, "Thread not in TERMINATE" util.debug('result handler found thread._state=TERMINATE') break From webhook-mailer at python.org Mon Dec 17 02:59:13 2018 From: webhook-mailer at python.org (Petr Viktorin) Date: Mon, 17 Dec 2018 07:59:13 -0000 Subject: [Python-checkins] bpo-35186: Remove "built with" comment in setup.py upload (GH-10414) Message-ID: https://github.com/python/cpython/commit/4e80f5cbeaee87a26e49bc9623c92a10e28dbbd9 commit: 4e80f5cbeaee87a26e49bc9623c92a10e28dbbd9 branch: master author: Paul Ganssle committer: Petr Viktorin date: 2018-12-17T08:59:02+01:00 summary: bpo-35186: Remove "built with" comment in setup.py upload (GH-10414) platform.dist() is deprecated and slated for removal in Python 3.8. The upload command itself should also not be used to upload to PyPI, but while it continues to exist it should not use deprecated functions. files: A Misc/NEWS.d/next/Library/2018-11-08-14-22-29.bpo-35186.5m22Mj.rst M Lib/distutils/command/upload.py diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py index 32dda359badb..613ea711296c 100644 --- a/Lib/distutils/command/upload.py +++ b/Lib/distutils/command/upload.py @@ -121,14 +121,8 @@ def upload_file(self, command, pyversion, filename): 'requires': meta.get_requires(), 'obsoletes': meta.get_obsoletes(), } - comment = '' - if command == 'bdist_rpm': - dist, version, id = platform.dist() - if dist: - comment = 'built for %s %s' % (dist, version) - elif command == 'bdist_dumb': - comment = 'built for %s' % platform.platform(terse=1) - data['comment'] = comment + + data['comment'] = '' if self.sign: data['gpg_signature'] = (os.path.basename(filename) + ".asc", diff --git a/Misc/NEWS.d/next/Library/2018-11-08-14-22-29.bpo-35186.5m22Mj.rst b/Misc/NEWS.d/next/Library/2018-11-08-14-22-29.bpo-35186.5m22Mj.rst new file mode 100644 index 000000000000..2e8cff982907 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-11-08-14-22-29.bpo-35186.5m22Mj.rst @@ -0,0 +1,2 @@ +Removed the "built with" comment added when ``setup.py upload`` is used with +either ``bdist_rpm`` or ``bdist_dumb``. From webhook-mailer at python.org Mon Dec 17 03:34:14 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 17 Dec 2018 08:34:14 -0000 Subject: [Python-checkins] bpo-35412: Add testcase to test_future4 (GH-11131) (GH-11183) Message-ID: https://github.com/python/cpython/commit/2d91a1325f7def1cc3762cadf5f5a99a55dac78a commit: 2d91a1325f7def1cc3762cadf5f5a99a55dac78a branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Victor Stinner date: 2018-12-17T09:34:06+01:00 summary: bpo-35412: Add testcase to test_future4 (GH-11131) (GH-11183) Add testcase to test_future4: check unicode literal. (cherry picked from commit 502fe19b10f66235fcf8f13fc1c0308190845def) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2018-12-12-18-07-58.bpo-35412.kbuJor.rst M Lib/test/test_future4.py diff --git a/Lib/test/test_future4.py b/Lib/test/test_future4.py index 413dd4d96b03..b27ca40d2eae 100644 --- a/Lib/test/test_future4.py +++ b/Lib/test/test_future4.py @@ -1,6 +1,11 @@ from __future__ import unicode_literals - import unittest + +class Tests(unittest.TestCase): + def test_unicode_literals(self): + self.assertIsInstance("literal", str) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2018-12-12-18-07-58.bpo-35412.kbuJor.rst b/Misc/NEWS.d/next/Tests/2018-12-12-18-07-58.bpo-35412.kbuJor.rst new file mode 100644 index 000000000000..d696074fb734 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-12-18-07-58.bpo-35412.kbuJor.rst @@ -0,0 +1 @@ +Add testcase to ``test_future4``: check unicode literal. From webhook-mailer at python.org Mon Dec 17 03:36:43 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 17 Dec 2018 08:36:43 -0000 Subject: [Python-checkins] bpo-35513: Replace time.time() with time.monotonic() in tests (GH-11182) Message-ID: https://github.com/python/cpython/commit/2cf4c202ffeb30787c944365ba54013688b854c2 commit: 2cf4c202ffeb30787c944365ba54013688b854c2 branch: master author: Victor Stinner committer: GitHub date: 2018-12-17T09:36:36+01:00 summary: bpo-35513: Replace time.time() with time.monotonic() in tests (GH-11182) Replace time.time() with time.monotonic() in tests to measure time delta. test_zipfile64: display progress every minute (60 secs) rather than every 5 minutes (5*60 seconds). files: A Misc/NEWS.d/next/Tests/2018-12-16-23-36-47.bpo-35513.k4WHlA.rst M Lib/pydoc.py M Lib/test/_test_multiprocessing.py M Lib/test/lock_tests.py M Lib/test/support/__init__.py M Lib/test/test_asyncio/utils.py M Lib/test/test_asyncore.py M Lib/test/test_dummy_thread.py M Lib/test/test_ossaudiodev.py M Lib/test/test_pydoc.py M Lib/test/test_signal.py M Lib/test/test_threadsignals.py M Lib/test/test_timeout.py M Lib/test/test_zipfile64.py diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 54e6ce8b25bc..1d84b847cf9f 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -2231,14 +2231,14 @@ def _start_server(urlhandler, hostname, port): Let the server do its thing. We just need to monitor its status. Use time.sleep so the loop doesn't hog the CPU. - >>> starttime = time.time() + >>> starttime = time.monotonic() >>> timeout = 1 #seconds This is a short timeout for testing purposes. >>> while serverthread.serving: ... time.sleep(.01) - ... if serverthread.serving and time.time() - starttime > timeout: + ... if serverthread.serving and time.monotonic() - starttime > timeout: ... serverthread.stop() ... break diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index a2dc53c8e4e6..b5597d5fb129 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -155,11 +155,11 @@ def __init__(self, func): self.elapsed = None def __call__(self, *args, **kwds): - t = time.time() + t = time.monotonic() try: return self.func(*args, **kwds) finally: - self.elapsed = time.time() - t + self.elapsed = time.monotonic() - t # # Base class for test cases @@ -1034,9 +1034,9 @@ def test_no_import_lock_contention(self): def test_timeout(self): q = multiprocessing.Queue() - start = time.time() + start = time.monotonic() self.assertRaises(pyqueue.Empty, q.get, True, 0.200) - delta = time.time() - start + delta = time.monotonic() - start # bpo-30317: Tolerate a delta of 100 ms because of the bad clock # resolution on Windows (usually 15.6 ms). x86 Windows7 3.x once # failed because the delta was only 135.8 ms. @@ -1440,9 +1440,9 @@ def _test_waitfor_timeout_f(cls, cond, state, success, sem): sem.release() with cond: expected = 0.1 - dt = time.time() + dt = time.monotonic() result = cond.wait_for(lambda : state.value==4, timeout=expected) - dt = time.time() - dt + dt = time.monotonic() - dt # borrow logic in assertTimeout() from test/lock_tests.py if not result and expected * 0.6 < dt < expected * 10.0: success.value = True @@ -2533,7 +2533,7 @@ def test_map_no_failfast(self): # process would fill the result queue (after the result handler thread # terminated, hence not draining it anymore). - t_start = time.time() + t_start = time.monotonic() with self.assertRaises(ValueError): with self.Pool(2) as p: @@ -2545,7 +2545,7 @@ def test_map_no_failfast(self): p.join() # check that we indeed waited for all jobs - self.assertGreater(time.time() - t_start, 0.9) + self.assertGreater(time.monotonic() - t_start, 0.9) def test_release_task_refs(self): # Issue #29861: task arguments and results should not be kept @@ -4108,9 +4108,9 @@ def test_wait_timeout(self): expected = 5 a, b = multiprocessing.Pipe() - start = time.time() + start = time.monotonic() res = wait([a, b], expected) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(res, []) self.assertLess(delta, expected * 2) @@ -4118,9 +4118,9 @@ def test_wait_timeout(self): b.send(None) - start = time.time() + start = time.monotonic() res = wait([a, b], 20) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(res, [a]) self.assertLess(delta, 0.4) @@ -4144,9 +4144,9 @@ def test_wait_integer(self): self.assertIsInstance(p.sentinel, int) self.assertTrue(sem.acquire(timeout=20)) - start = time.time() + start = time.monotonic() res = wait([a, p.sentinel, b], expected + 20) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(res, [p.sentinel]) self.assertLess(delta, expected + 2) @@ -4154,18 +4154,18 @@ def test_wait_integer(self): a.send(None) - start = time.time() + start = time.monotonic() res = wait([a, p.sentinel, b], 20) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(sorted_(res), sorted_([p.sentinel, b])) self.assertLess(delta, 0.4) b.send(None) - start = time.time() + start = time.monotonic() res = wait([a, p.sentinel, b], 20) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(sorted_(res), sorted_([a, p.sentinel, b])) self.assertLess(delta, 0.4) @@ -4176,9 +4176,9 @@ def test_wait_integer(self): def test_neg_timeout(self): from multiprocessing.connection import wait a, b = multiprocessing.Pipe() - t = time.time() + t = time.monotonic() res = wait([a], timeout=-1) - t = time.time() - t + t = time.monotonic() - t self.assertEqual(res, []) self.assertLess(t, 1) a.close() diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index 65fa4d87d4ce..23a02e0b4eb2 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -74,7 +74,7 @@ def tearDown(self): support.reap_children() def assertTimeout(self, actual, expected): - # The waiting and/or time.time() can be imprecise, which + # The waiting and/or time.monotonic() can be imprecise, which # is why comparing to the expected value would sometimes fail # (especially under Windows). self.assertGreaterEqual(actual, expected * 0.6) @@ -190,16 +190,16 @@ def test_timeout(self): # TIMEOUT_MAX is ok lock.acquire(timeout=TIMEOUT_MAX) lock.release() - t1 = time.time() + t1 = time.monotonic() self.assertTrue(lock.acquire(timeout=5)) - t2 = time.time() + t2 = time.monotonic() # Just a sanity test that it didn't actually wait for the timeout. self.assertLess(t2 - t1, 5) results = [] def f(): - t1 = time.time() + t1 = time.monotonic() results.append(lock.acquire(timeout=0.5)) - t2 = time.time() + t2 = time.monotonic() results.append(t2 - t1) Bunch(f, 1).wait_for_finished() self.assertFalse(results[0]) @@ -382,9 +382,9 @@ def test_timeout(self): N = 5 def f(): results1.append(evt.wait(0.0)) - t1 = time.time() + t1 = time.monotonic() r = evt.wait(0.5) - t2 = time.time() + t2 = time.monotonic() results2.append((r, t2 - t1)) Bunch(f, N).wait_for_finished() self.assertEqual(results1, [False] * N) @@ -545,9 +545,9 @@ def test_timeout(self): N = 5 def f(): cond.acquire() - t1 = time.time() + t1 = time.monotonic() result = cond.wait(0.5) - t2 = time.time() + t2 = time.monotonic() cond.release() results.append((t2 - t1, result)) Bunch(f, N).wait_for_finished() @@ -584,9 +584,9 @@ def test_waitfor_timeout(self): success = [] def f(): with cond: - dt = time.time() + dt = time.monotonic() result = cond.wait_for(lambda : state==4, timeout=0.1) - dt = time.time() - dt + dt = time.monotonic() - dt self.assertFalse(result) self.assertTimeout(dt, 0.1) success.append(None) @@ -692,9 +692,9 @@ def test_acquire_timeout(self): self.assertFalse(sem.acquire(timeout=0.005)) sem.release() self.assertTrue(sem.acquire(timeout=0.005)) - t = time.time() + t = time.monotonic() self.assertFalse(sem.acquire(timeout=0.5)) - dt = time.time() - t + dt = time.monotonic() - t self.assertTimeout(dt, 0.5) def test_default_value(self): diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 9b6c338a5666..9ffb04de9bc1 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2259,11 +2259,11 @@ def start_threads(threads, unlock=None): try: if unlock: unlock() - endtime = starttime = time.time() + endtime = starttime = time.monotonic() for timeout in range(1, 16): endtime += 60 for t in started: - t.join(max(endtime - time.time(), 0.01)) + t.join(max(endtime - time.monotonic(), 0.01)) started = [t for t in started if t.isAlive()] if not started: break diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index 8b23b0175076..cb373d544f41 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -107,10 +107,10 @@ def run_briefly(loop): def run_until(loop, pred, timeout=30): - deadline = time.time() + timeout + deadline = time.monotonic() + timeout while not pred(): if timeout is not None: - timeout = deadline - time.time() + timeout = deadline - time.monotonic() if timeout <= 0: raise futures.TimeoutError() loop.run_until_complete(tasks.sleep(0.001)) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index 694ddffd6879..3fcedb58ec18 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -70,8 +70,8 @@ def capture_server(evt, buf, serv): pass else: n = 200 - start = time.time() - while n > 0 and time.time() - start < 3.0: + start = time.monotonic() + while n > 0 and time.monotonic() - start < 3.0: r, w, e = select.select([conn], [], [], 0.1) if r: n -= 1 diff --git a/Lib/test/test_dummy_thread.py b/Lib/test/test_dummy_thread.py index 0840be67d562..da512167834f 100644 --- a/Lib/test/test_dummy_thread.py +++ b/Lib/test/test_dummy_thread.py @@ -70,14 +70,14 @@ def delay_unlock(to_unlock, delay): to_unlock.release() self.lock.acquire() - start_time = int(time.time()) + start_time = int(time.monotonic()) _thread.start_new_thread(delay_unlock,(self.lock, DELAY)) if support.verbose: print() print("*** Waiting for thread to release the lock "\ "(approx. %s sec.) ***" % DELAY) self.lock.acquire() - end_time = int(time.time()) + end_time = int(time.monotonic()) if support.verbose: print("done") self.assertGreaterEqual(end_time - start_time, DELAY, diff --git a/Lib/test/test_ossaudiodev.py b/Lib/test/test_ossaudiodev.py index c9e2a2476779..624fbf21ba7d 100644 --- a/Lib/test/test_ossaudiodev.py +++ b/Lib/test/test_ossaudiodev.py @@ -77,10 +77,10 @@ def play_sound_file(self, data, rate, ssize, nchannels): # set parameters based on .au file headers dsp.setparameters(AFMT_S16_NE, nchannels, rate) self.assertTrue(abs(expected_time - 3.51) < 1e-2, expected_time) - t1 = time.time() + t1 = time.monotonic() dsp.write(data) dsp.close() - t2 = time.time() + t2 = time.monotonic() elapsed_time = t2 - t1 percent_diff = (abs(elapsed_time - expected_time) / expected_time) * 100 diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index d521deda1760..409fea4a5e94 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -1131,12 +1131,12 @@ def my_url_handler(url, content_type): serverthread = pydoc._start_server(my_url_handler, hostname='0.0.0.0', port=0) self.assertIn('0.0.0.0', serverthread.docserver.address) - starttime = time.time() + starttime = time.monotonic() timeout = 1 #seconds while serverthread.serving: time.sleep(.01) - if serverthread.serving and time.time() - starttime > timeout: + if serverthread.serving and time.monotonic() - starttime > timeout: serverthread.stop() break diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index d30a2d612905..b10faa010b2b 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -1163,18 +1163,18 @@ def second_handler(signum=None, frame=None): self.setsig(signal.SIGALRM, second_handler) # for ITIMER_REAL expected_sigs = 0 - deadline = time.time() + 15.0 + deadline = time.monotonic() + 15.0 while expected_sigs < N: os.kill(os.getpid(), signal.SIGPROF) expected_sigs += 1 # Wait for handlers to run to avoid signal coalescing - while len(sigs) < expected_sigs and time.time() < deadline: + while len(sigs) < expected_sigs and time.monotonic() < deadline: time.sleep(1e-5) os.kill(os.getpid(), signal.SIGUSR1) expected_sigs += 1 - while len(sigs) < expected_sigs and time.time() < deadline: + while len(sigs) < expected_sigs and time.monotonic() < deadline: time.sleep(1e-5) # All ITIMER_REAL signals should have been delivered to the @@ -1197,7 +1197,7 @@ def handler(signum, frame): self.setsig(signal.SIGALRM, handler) # for ITIMER_REAL expected_sigs = 0 - deadline = time.time() + 15.0 + deadline = time.monotonic() + 15.0 while expected_sigs < N: # Hopefully the SIGALRM will be received somewhere during @@ -1207,7 +1207,7 @@ def handler(signum, frame): expected_sigs += 2 # Wait for handlers to run to avoid signal coalescing - while len(sigs) < expected_sigs and time.time() < deadline: + while len(sigs) < expected_sigs and time.monotonic() < deadline: time.sleep(1e-5) # All ITIMER_REAL signals should have been delivered to the diff --git a/Lib/test/test_threadsignals.py b/Lib/test/test_threadsignals.py index 7e13b1720f91..eeacd3698cb1 100644 --- a/Lib/test/test_threadsignals.py +++ b/Lib/test/test_threadsignals.py @@ -95,9 +95,9 @@ def test_lock_acquire_interruption(self): lock = thread.allocate_lock() lock.acquire() signal.alarm(1) - t1 = time.time() + t1 = time.monotonic() self.assertRaises(KeyboardInterrupt, lock.acquire, timeout=5) - dt = time.time() - t1 + dt = time.monotonic() - t1 # Checking that KeyboardInterrupt was raised is not sufficient. # We want to assert that lock.acquire() was interrupted because # of the signal, not that the signal handler was called immediately @@ -136,9 +136,9 @@ def other_thread(): rlock.release() time.sleep(0.01) signal.alarm(1) - t1 = time.time() + t1 = time.monotonic() self.assertRaises(KeyboardInterrupt, rlock.acquire, timeout=5) - dt = time.time() - t1 + dt = time.monotonic() - t1 # See rationale above in test_lock_acquire_interruption self.assertLess(dt, 3.0) finally: @@ -203,9 +203,9 @@ def my_handler(signum, frame): old_handler = signal.signal(signal.SIGUSR1, my_handler) try: def timed_acquire(): - self.start = time.time() + self.start = time.monotonic() lock.acquire(timeout=0.5) - self.end = time.time() + self.end = time.monotonic() def send_signals(): for _ in range(40): time.sleep(0.02) diff --git a/Lib/test/test_timeout.py b/Lib/test/test_timeout.py index 3c75dcc6f922..b54fc826ae0a 100644 --- a/Lib/test/test_timeout.py +++ b/Lib/test/test_timeout.py @@ -127,11 +127,11 @@ def _sock_operation(self, count, timeout, method, *args): self.sock.settimeout(timeout) method = getattr(self.sock, method) for i in range(count): - t1 = time.time() + t1 = time.monotonic() try: method(*args) except socket.timeout as e: - delta = time.time() - t1 + delta = time.monotonic() - t1 break else: self.fail('socket.timeout was not raised') diff --git a/Lib/test/test_zipfile64.py b/Lib/test/test_zipfile64.py index cba909f6bce2..524dcf021324 100644 --- a/Lib/test/test_zipfile64.py +++ b/Lib/test/test_zipfile64.py @@ -22,7 +22,7 @@ TESTFN2 = TESTFN + "2" # How much time in seconds can pass before we print a 'Still working' message. -_PRINT_WORKING_MSG_INTERVAL = 5 * 60 +_PRINT_WORKING_MSG_INTERVAL = 60 class TestsWithSourceFile(unittest.TestCase): def setUp(self): @@ -43,12 +43,12 @@ def zipTest(self, f, compression): # raw data to store. filecount = 6*1024**3 // len(self.data) - next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL + next_time = time.monotonic() + _PRINT_WORKING_MSG_INTERVAL for num in range(filecount): zipfp.writestr("testfn%d" % num, self.data) # Print still working message since this test can be really slow - if next_time <= time.time(): - next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL + if next_time <= time.monotonic(): + next_time = time.monotonic() + _PRINT_WORKING_MSG_INTERVAL print(( ' zipTest still writing %d of %d, be patient...' % (num, filecount)), file=sys.__stdout__) @@ -60,8 +60,8 @@ def zipTest(self, f, compression): for num in range(filecount): self.assertEqual(zipfp.read("testfn%d" % num), self.data) # Print still working message since this test can be really slow - if next_time <= time.time(): - next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL + if next_time <= time.monotonic(): + next_time = time.monotonic() + _PRINT_WORKING_MSG_INTERVAL print(( ' zipTest still reading %d of %d, be patient...' % (num, filecount)), file=sys.__stdout__) diff --git a/Misc/NEWS.d/next/Tests/2018-12-16-23-36-47.bpo-35513.k4WHlA.rst b/Misc/NEWS.d/next/Tests/2018-12-16-23-36-47.bpo-35513.k4WHlA.rst new file mode 100644 index 000000000000..33ca3a8846e2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-16-23-36-47.bpo-35513.k4WHlA.rst @@ -0,0 +1,2 @@ +Replace :func:`time.time` with :func:`time.monotonic` in tests to measure +time delta. From webhook-mailer at python.org Mon Dec 17 04:03:09 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 17 Dec 2018 09:03:09 -0000 Subject: [Python-checkins] bpo-35513: Replace time.time() with time.monotonic() in tests (GH-11182) Message-ID: https://github.com/python/cpython/commit/be69ff232df23b6ee165d7c34df5435d497cb79b commit: be69ff232df23b6ee165d7c34df5435d497cb79b branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-17T01:03:04-08:00 summary: bpo-35513: Replace time.time() with time.monotonic() in tests (GH-11182) Replace time.time() with time.monotonic() in tests to measure time delta. test_zipfile64: display progress every minute (60 secs) rather than every 5 minutes (5*60 seconds). (cherry picked from commit 2cf4c202ffeb30787c944365ba54013688b854c2) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2018-12-16-23-36-47.bpo-35513.k4WHlA.rst M Lib/pydoc.py M Lib/test/_test_multiprocessing.py M Lib/test/lock_tests.py M Lib/test/support/__init__.py M Lib/test/test_asyncio/utils.py M Lib/test/test_asyncore.py M Lib/test/test_dummy_thread.py M Lib/test/test_ossaudiodev.py M Lib/test/test_pydoc.py M Lib/test/test_signal.py M Lib/test/test_threadsignals.py M Lib/test/test_timeout.py M Lib/test/test_zipfile64.py diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 09992cd08245..44df8c854ae9 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -2213,14 +2213,14 @@ def _start_server(urlhandler, hostname, port): Let the server do its thing. We just need to monitor its status. Use time.sleep so the loop doesn't hog the CPU. - >>> starttime = time.time() + >>> starttime = time.monotonic() >>> timeout = 1 #seconds This is a short timeout for testing purposes. >>> while serverthread.serving: ... time.sleep(.01) - ... if serverthread.serving and time.time() - starttime > timeout: + ... if serverthread.serving and time.monotonic() - starttime > timeout: ... serverthread.stop() ... break diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index abcb5d45e7ab..1bf83ffb9b4a 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -157,11 +157,11 @@ def __init__(self, func): self.elapsed = None def __call__(self, *args, **kwds): - t = time.time() + t = time.monotonic() try: return self.func(*args, **kwds) finally: - self.elapsed = time.time() - t + self.elapsed = time.monotonic() - t # # Base class for test cases @@ -1036,9 +1036,9 @@ def test_no_import_lock_contention(self): def test_timeout(self): q = multiprocessing.Queue() - start = time.time() + start = time.monotonic() self.assertRaises(pyqueue.Empty, q.get, True, 0.200) - delta = time.time() - start + delta = time.monotonic() - start # bpo-30317: Tolerate a delta of 100 ms because of the bad clock # resolution on Windows (usually 15.6 ms). x86 Windows7 3.x once # failed because the delta was only 135.8 ms. @@ -1434,9 +1434,9 @@ def _test_waitfor_timeout_f(cls, cond, state, success, sem): sem.release() with cond: expected = 0.1 - dt = time.time() + dt = time.monotonic() result = cond.wait_for(lambda : state.value==4, timeout=expected) - dt = time.time() - dt + dt = time.monotonic() - dt # borrow logic in assertTimeout() from test/lock_tests.py if not result and expected * 0.6 < dt < expected * 10.0: success.value = True @@ -2527,7 +2527,7 @@ def test_map_no_failfast(self): # process would fill the result queue (after the result handler thread # terminated, hence not draining it anymore). - t_start = time.time() + t_start = time.monotonic() with self.assertRaises(ValueError): with self.Pool(2) as p: @@ -2539,7 +2539,7 @@ def test_map_no_failfast(self): p.join() # check that we indeed waited for all jobs - self.assertGreater(time.time() - t_start, 0.9) + self.assertGreater(time.monotonic() - t_start, 0.9) def test_release_task_refs(self): # Issue #29861: task arguments and results should not be kept @@ -4051,9 +4051,9 @@ def test_wait_timeout(self): expected = 5 a, b = multiprocessing.Pipe() - start = time.time() + start = time.monotonic() res = wait([a, b], expected) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(res, []) self.assertLess(delta, expected * 2) @@ -4061,9 +4061,9 @@ def test_wait_timeout(self): b.send(None) - start = time.time() + start = time.monotonic() res = wait([a, b], 20) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(res, [a]) self.assertLess(delta, 0.4) @@ -4087,9 +4087,9 @@ def test_wait_integer(self): self.assertIsInstance(p.sentinel, int) self.assertTrue(sem.acquire(timeout=20)) - start = time.time() + start = time.monotonic() res = wait([a, p.sentinel, b], expected + 20) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(res, [p.sentinel]) self.assertLess(delta, expected + 2) @@ -4097,18 +4097,18 @@ def test_wait_integer(self): a.send(None) - start = time.time() + start = time.monotonic() res = wait([a, p.sentinel, b], 20) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(sorted_(res), sorted_([p.sentinel, b])) self.assertLess(delta, 0.4) b.send(None) - start = time.time() + start = time.monotonic() res = wait([a, p.sentinel, b], 20) - delta = time.time() - start + delta = time.monotonic() - start self.assertEqual(sorted_(res), sorted_([a, p.sentinel, b])) self.assertLess(delta, 0.4) @@ -4119,9 +4119,9 @@ def test_wait_integer(self): def test_neg_timeout(self): from multiprocessing.connection import wait a, b = multiprocessing.Pipe() - t = time.time() + t = time.monotonic() res = wait([a], timeout=-1) - t = time.time() - t + t = time.monotonic() - t self.assertEqual(res, []) self.assertLess(t, 1) a.close() diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index 65fa4d87d4ce..23a02e0b4eb2 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -74,7 +74,7 @@ def tearDown(self): support.reap_children() def assertTimeout(self, actual, expected): - # The waiting and/or time.time() can be imprecise, which + # The waiting and/or time.monotonic() can be imprecise, which # is why comparing to the expected value would sometimes fail # (especially under Windows). self.assertGreaterEqual(actual, expected * 0.6) @@ -190,16 +190,16 @@ def test_timeout(self): # TIMEOUT_MAX is ok lock.acquire(timeout=TIMEOUT_MAX) lock.release() - t1 = time.time() + t1 = time.monotonic() self.assertTrue(lock.acquire(timeout=5)) - t2 = time.time() + t2 = time.monotonic() # Just a sanity test that it didn't actually wait for the timeout. self.assertLess(t2 - t1, 5) results = [] def f(): - t1 = time.time() + t1 = time.monotonic() results.append(lock.acquire(timeout=0.5)) - t2 = time.time() + t2 = time.monotonic() results.append(t2 - t1) Bunch(f, 1).wait_for_finished() self.assertFalse(results[0]) @@ -382,9 +382,9 @@ def test_timeout(self): N = 5 def f(): results1.append(evt.wait(0.0)) - t1 = time.time() + t1 = time.monotonic() r = evt.wait(0.5) - t2 = time.time() + t2 = time.monotonic() results2.append((r, t2 - t1)) Bunch(f, N).wait_for_finished() self.assertEqual(results1, [False] * N) @@ -545,9 +545,9 @@ def test_timeout(self): N = 5 def f(): cond.acquire() - t1 = time.time() + t1 = time.monotonic() result = cond.wait(0.5) - t2 = time.time() + t2 = time.monotonic() cond.release() results.append((t2 - t1, result)) Bunch(f, N).wait_for_finished() @@ -584,9 +584,9 @@ def test_waitfor_timeout(self): success = [] def f(): with cond: - dt = time.time() + dt = time.monotonic() result = cond.wait_for(lambda : state==4, timeout=0.1) - dt = time.time() - dt + dt = time.monotonic() - dt self.assertFalse(result) self.assertTimeout(dt, 0.1) success.append(None) @@ -692,9 +692,9 @@ def test_acquire_timeout(self): self.assertFalse(sem.acquire(timeout=0.005)) sem.release() self.assertTrue(sem.acquire(timeout=0.005)) - t = time.time() + t = time.monotonic() self.assertFalse(sem.acquire(timeout=0.5)) - dt = time.time() - t + dt = time.monotonic() - t self.assertTimeout(dt, 0.5) def test_default_value(self): diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 5bea5474f282..fd7fb2cc1702 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2219,11 +2219,11 @@ def start_threads(threads, unlock=None): try: if unlock: unlock() - endtime = starttime = time.time() + endtime = starttime = time.monotonic() for timeout in range(1, 16): endtime += 60 for t in started: - t.join(max(endtime - time.time(), 0.01)) + t.join(max(endtime - time.monotonic(), 0.01)) started = [t for t in started if t.isAlive()] if not started: break diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index a148f1639ecc..d25d2cd5405c 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -107,10 +107,10 @@ def run_briefly(loop): def run_until(loop, pred, timeout=30): - deadline = time.time() + timeout + deadline = time.monotonic() + timeout while not pred(): if timeout is not None: - timeout = deadline - time.time() + timeout = deadline - time.monotonic() if timeout <= 0: raise futures.TimeoutError() loop.run_until_complete(tasks.sleep(0.001, loop=loop)) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index 694ddffd6879..3fcedb58ec18 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -70,8 +70,8 @@ def capture_server(evt, buf, serv): pass else: n = 200 - start = time.time() - while n > 0 and time.time() - start < 3.0: + start = time.monotonic() + while n > 0 and time.monotonic() - start < 3.0: r, w, e = select.select([conn], [], [], 0.1) if r: n -= 1 diff --git a/Lib/test/test_dummy_thread.py b/Lib/test/test_dummy_thread.py index 0840be67d562..da512167834f 100644 --- a/Lib/test/test_dummy_thread.py +++ b/Lib/test/test_dummy_thread.py @@ -70,14 +70,14 @@ def delay_unlock(to_unlock, delay): to_unlock.release() self.lock.acquire() - start_time = int(time.time()) + start_time = int(time.monotonic()) _thread.start_new_thread(delay_unlock,(self.lock, DELAY)) if support.verbose: print() print("*** Waiting for thread to release the lock "\ "(approx. %s sec.) ***" % DELAY) self.lock.acquire() - end_time = int(time.time()) + end_time = int(time.monotonic()) if support.verbose: print("done") self.assertGreaterEqual(end_time - start_time, DELAY, diff --git a/Lib/test/test_ossaudiodev.py b/Lib/test/test_ossaudiodev.py index c9e2a2476779..624fbf21ba7d 100644 --- a/Lib/test/test_ossaudiodev.py +++ b/Lib/test/test_ossaudiodev.py @@ -77,10 +77,10 @@ def play_sound_file(self, data, rate, ssize, nchannels): # set parameters based on .au file headers dsp.setparameters(AFMT_S16_NE, nchannels, rate) self.assertTrue(abs(expected_time - 3.51) < 1e-2, expected_time) - t1 = time.time() + t1 = time.monotonic() dsp.write(data) dsp.close() - t2 = time.time() + t2 = time.monotonic() elapsed_time = t2 - t1 percent_diff = (abs(elapsed_time - expected_time) / expected_time) * 100 diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 9bf365ed4b95..198cea93eb52 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -1013,12 +1013,12 @@ def my_url_handler(url, content_type): serverthread = pydoc._start_server(my_url_handler, hostname='0.0.0.0', port=0) self.assertIn('0.0.0.0', serverthread.docserver.address) - starttime = time.time() + starttime = time.monotonic() timeout = 1 #seconds while serverthread.serving: time.sleep(.01) - if serverthread.serving and time.time() - starttime > timeout: + if serverthread.serving and time.monotonic() - starttime > timeout: serverthread.stop() break diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 24cab0f89ee7..406684bdbec7 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -1122,18 +1122,18 @@ def second_handler(signum=None, frame=None): self.setsig(signal.SIGALRM, second_handler) # for ITIMER_REAL expected_sigs = 0 - deadline = time.time() + 15.0 + deadline = time.monotonic() + 15.0 while expected_sigs < N: os.kill(os.getpid(), signal.SIGPROF) expected_sigs += 1 # Wait for handlers to run to avoid signal coalescing - while len(sigs) < expected_sigs and time.time() < deadline: + while len(sigs) < expected_sigs and time.monotonic() < deadline: time.sleep(1e-5) os.kill(os.getpid(), signal.SIGUSR1) expected_sigs += 1 - while len(sigs) < expected_sigs and time.time() < deadline: + while len(sigs) < expected_sigs and time.monotonic() < deadline: time.sleep(1e-5) # All ITIMER_REAL signals should have been delivered to the @@ -1156,7 +1156,7 @@ def handler(signum, frame): self.setsig(signal.SIGALRM, handler) # for ITIMER_REAL expected_sigs = 0 - deadline = time.time() + 15.0 + deadline = time.monotonic() + 15.0 while expected_sigs < N: # Hopefully the SIGALRM will be received somewhere during @@ -1166,7 +1166,7 @@ def handler(signum, frame): expected_sigs += 2 # Wait for handlers to run to avoid signal coalescing - while len(sigs) < expected_sigs and time.time() < deadline: + while len(sigs) < expected_sigs and time.monotonic() < deadline: time.sleep(1e-5) # All ITIMER_REAL signals should have been delivered to the diff --git a/Lib/test/test_threadsignals.py b/Lib/test/test_threadsignals.py index 7e13b1720f91..eeacd3698cb1 100644 --- a/Lib/test/test_threadsignals.py +++ b/Lib/test/test_threadsignals.py @@ -95,9 +95,9 @@ def test_lock_acquire_interruption(self): lock = thread.allocate_lock() lock.acquire() signal.alarm(1) - t1 = time.time() + t1 = time.monotonic() self.assertRaises(KeyboardInterrupt, lock.acquire, timeout=5) - dt = time.time() - t1 + dt = time.monotonic() - t1 # Checking that KeyboardInterrupt was raised is not sufficient. # We want to assert that lock.acquire() was interrupted because # of the signal, not that the signal handler was called immediately @@ -136,9 +136,9 @@ def other_thread(): rlock.release() time.sleep(0.01) signal.alarm(1) - t1 = time.time() + t1 = time.monotonic() self.assertRaises(KeyboardInterrupt, rlock.acquire, timeout=5) - dt = time.time() - t1 + dt = time.monotonic() - t1 # See rationale above in test_lock_acquire_interruption self.assertLess(dt, 3.0) finally: @@ -203,9 +203,9 @@ def my_handler(signum, frame): old_handler = signal.signal(signal.SIGUSR1, my_handler) try: def timed_acquire(): - self.start = time.time() + self.start = time.monotonic() lock.acquire(timeout=0.5) - self.end = time.time() + self.end = time.monotonic() def send_signals(): for _ in range(40): time.sleep(0.02) diff --git a/Lib/test/test_timeout.py b/Lib/test/test_timeout.py index 3c75dcc6f922..b54fc826ae0a 100644 --- a/Lib/test/test_timeout.py +++ b/Lib/test/test_timeout.py @@ -127,11 +127,11 @@ def _sock_operation(self, count, timeout, method, *args): self.sock.settimeout(timeout) method = getattr(self.sock, method) for i in range(count): - t1 = time.time() + t1 = time.monotonic() try: method(*args) except socket.timeout as e: - delta = time.time() - t1 + delta = time.monotonic() - t1 break else: self.fail('socket.timeout was not raised') diff --git a/Lib/test/test_zipfile64.py b/Lib/test/test_zipfile64.py index cba909f6bce2..524dcf021324 100644 --- a/Lib/test/test_zipfile64.py +++ b/Lib/test/test_zipfile64.py @@ -22,7 +22,7 @@ TESTFN2 = TESTFN + "2" # How much time in seconds can pass before we print a 'Still working' message. -_PRINT_WORKING_MSG_INTERVAL = 5 * 60 +_PRINT_WORKING_MSG_INTERVAL = 60 class TestsWithSourceFile(unittest.TestCase): def setUp(self): @@ -43,12 +43,12 @@ def zipTest(self, f, compression): # raw data to store. filecount = 6*1024**3 // len(self.data) - next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL + next_time = time.monotonic() + _PRINT_WORKING_MSG_INTERVAL for num in range(filecount): zipfp.writestr("testfn%d" % num, self.data) # Print still working message since this test can be really slow - if next_time <= time.time(): - next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL + if next_time <= time.monotonic(): + next_time = time.monotonic() + _PRINT_WORKING_MSG_INTERVAL print(( ' zipTest still writing %d of %d, be patient...' % (num, filecount)), file=sys.__stdout__) @@ -60,8 +60,8 @@ def zipTest(self, f, compression): for num in range(filecount): self.assertEqual(zipfp.read("testfn%d" % num), self.data) # Print still working message since this test can be really slow - if next_time <= time.time(): - next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL + if next_time <= time.monotonic(): + next_time = time.monotonic() + _PRINT_WORKING_MSG_INTERVAL print(( ' zipTest still reading %d of %d, be patient...' % (num, filecount)), file=sys.__stdout__) diff --git a/Misc/NEWS.d/next/Tests/2018-12-16-23-36-47.bpo-35513.k4WHlA.rst b/Misc/NEWS.d/next/Tests/2018-12-16-23-36-47.bpo-35513.k4WHlA.rst new file mode 100644 index 000000000000..33ca3a8846e2 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-16-23-36-47.bpo-35513.k4WHlA.rst @@ -0,0 +1,2 @@ +Replace :func:`time.time` with :func:`time.monotonic` in tests to measure +time delta. From solipsis at pitrou.net Mon Dec 17 04:09:55 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 17 Dec 2018 09:09:55 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=10 Message-ID: <20181217090955.1.C0EB1744D2A50DC3@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [7, -7, 8] memory blocks, sum=8 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_forkserver leaked [-2, 2, -2] memory blocks, sum=-2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogyReBZE', '--timeout', '7200'] From webhook-mailer at python.org Mon Dec 17 05:30:44 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 17 Dec 2018 10:30:44 -0000 Subject: [Python-checkins] bpo-35513, unittest: TextTestRunner uses time.perf_counter() (GH-11180) Message-ID: https://github.com/python/cpython/commit/8db5b54463118e5eb60cb3582e3108623f01f5df commit: 8db5b54463118e5eb60cb3582e3108623f01f5df branch: master author: Victor Stinner committer: GitHub date: 2018-12-17T11:30:34+01:00 summary: bpo-35513, unittest: TextTestRunner uses time.perf_counter() (GH-11180) TextTestRunner of unittest.runner now uses time.perf_counter() rather than time.time() to measure the execution time of a test: time.time() can go backwards, whereas time.perf_counter() is monotonic. Similar change made in libregrtest, pprint and random. files: A Misc/NEWS.d/next/Library/2018-12-16-23-28-49.bpo-35513.pn-Zh3.rst M Lib/pprint.py M Lib/random.py M Lib/test/libregrtest/runtest.py M Lib/test/time_hashlib.py M Lib/unittest/runner.py diff --git a/Lib/pprint.py b/Lib/pprint.py index bcf2eedebe6b..f2a117864e5e 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -568,11 +568,11 @@ def _perfcheck(object=None): if object is None: object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000 p = PrettyPrinter() - t1 = time.time() + t1 = time.perf_counter() _safe_repr(object, {}, None, 0) - t2 = time.time() + t2 = time.perf_counter() p.pformat(object) - t3 = time.time() + t3 = time.perf_counter() print("_safe_repr:", t2 - t1) print("pformat:", t3 - t2) diff --git a/Lib/random.py b/Lib/random.py index 03c058a39d6e..9c2904cfd2f9 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -740,14 +740,14 @@ def _test_generator(n, func, args): sqsum = 0.0 smallest = 1e10 largest = -1e10 - t0 = time.time() + t0 = time.perf_counter() for i in range(n): x = func(*args) total += x sqsum = sqsum + x*x smallest = min(x, smallest) largest = max(x, largest) - t1 = time.time() + t1 = time.perf_counter() print(round(t1-t0, 3), 'sec,', end=' ') avg = total/n stddev = _sqrt(sqsum/n - avg*avg) diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index 466b522db9ed..dc2abf237bc0 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -162,7 +162,7 @@ def runtest_inner(ns, test, display_failure=True): abstest = get_abs_module(ns, test) clear_caches() with saved_test_environment(test, ns.verbose, ns.quiet, pgo=ns.pgo) as environment: - start_time = time.time() + start_time = time.perf_counter() the_module = importlib.import_module(abstest) # If the test has a test_main, that will run the appropriate # tests. If not, use normal unittest test loading. @@ -180,7 +180,7 @@ def test_runner(): refleak = dash_R(the_module, test, test_runner, ns.huntrleaks) else: test_runner() - test_time = time.time() - start_time + test_time = time.perf_counter() - start_time post_test_cleanup() except support.ResourceDenied as msg: if not ns.quiet and not ns.pgo: diff --git a/Lib/test/time_hashlib.py b/Lib/test/time_hashlib.py index 2585ecb78207..55ebac62912f 100644 --- a/Lib/test/time_hashlib.py +++ b/Lib/test/time_hashlib.py @@ -14,26 +14,26 @@ def test_scaled_msg(scale, name): longStr = b'Z'*scale localCF = creatorFunc - start = time.time() + start = time.perf_counter() for f in range(iterations): x = localCF(longStr).digest() - end = time.time() + end = time.perf_counter() print(('%2.2f' % (end-start)), "seconds", iterations, "x", len(longStr), "bytes", name) def test_create(): - start = time.time() + start = time.perf_counter() for f in range(20000): d = creatorFunc() - end = time.time() + end = time.perf_counter() print(('%2.2f' % (end-start)), "seconds", '[20000 creations]') def test_zero(): - start = time.time() + start = time.perf_counter() for f in range(20000): x = creatorFunc().digest() - end = time.time() + end = time.perf_counter() print(('%2.2f' % (end-start)), "seconds", '[20000 "" digests]') diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py index 2c5ea4ab079d..45e7e4c0458d 100644 --- a/Lib/unittest/runner.py +++ b/Lib/unittest/runner.py @@ -168,7 +168,7 @@ def run(self, test): warnings.filterwarnings('module', category=DeprecationWarning, message=r'Please use assert\w+ instead.') - startTime = time.time() + startTime = time.perf_counter() startTestRun = getattr(result, 'startTestRun', None) if startTestRun is not None: startTestRun() @@ -178,7 +178,7 @@ def run(self, test): stopTestRun = getattr(result, 'stopTestRun', None) if stopTestRun is not None: stopTestRun() - stopTime = time.time() + stopTime = time.perf_counter() timeTaken = stopTime - startTime result.printErrors() if hasattr(result, 'separator2'): diff --git a/Misc/NEWS.d/next/Library/2018-12-16-23-28-49.bpo-35513.pn-Zh3.rst b/Misc/NEWS.d/next/Library/2018-12-16-23-28-49.bpo-35513.pn-Zh3.rst new file mode 100644 index 000000000000..f1436a718de2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-16-23-28-49.bpo-35513.pn-Zh3.rst @@ -0,0 +1,4 @@ +:class:`~unittest.runner.TextTestRunner` of :mod:`unittest.runner` now uses +:func:`time.perf_counter` rather than :func:`time.time` to measure the +execution time of a test: :func:`time.time` can go backwards, whereas +:func:`time.perf_counter` is monotonic. From webhook-mailer at python.org Mon Dec 17 05:49:25 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 17 Dec 2018 10:49:25 -0000 Subject: [Python-checkins] bpo-35513, unittest: TextTestRunner uses time.perf_counter() (GH-11180) Message-ID: https://github.com/python/cpython/commit/9ade4cbc0f54fc0e2970e4e202f09ab83f5e3b77 commit: 9ade4cbc0f54fc0e2970e4e202f09ab83f5e3b77 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-17T02:49:22-08:00 summary: bpo-35513, unittest: TextTestRunner uses time.perf_counter() (GH-11180) TextTestRunner of unittest.runner now uses time.perf_counter() rather than time.time() to measure the execution time of a test: time.time() can go backwards, whereas time.perf_counter() is monotonic. Similar change made in libregrtest, pprint and random. (cherry picked from commit 8db5b54463118e5eb60cb3582e3108623f01f5df) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Library/2018-12-16-23-28-49.bpo-35513.pn-Zh3.rst M Lib/pprint.py M Lib/random.py M Lib/test/libregrtest/runtest.py M Lib/test/time_hashlib.py M Lib/unittest/runner.py diff --git a/Lib/pprint.py b/Lib/pprint.py index bcf2eedebe6b..f2a117864e5e 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -568,11 +568,11 @@ def _perfcheck(object=None): if object is None: object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000 p = PrettyPrinter() - t1 = time.time() + t1 = time.perf_counter() _safe_repr(object, {}, None, 0) - t2 = time.time() + t2 = time.perf_counter() p.pformat(object) - t3 = time.time() + t3 = time.perf_counter() print("_safe_repr:", t2 - t1) print("pformat:", t3 - t2) diff --git a/Lib/random.py b/Lib/random.py index 8e94064c9c61..bb6a05f26af6 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -706,14 +706,14 @@ def _test_generator(n, func, args): sqsum = 0.0 smallest = 1e10 largest = -1e10 - t0 = time.time() + t0 = time.perf_counter() for i in range(n): x = func(*args) total += x sqsum = sqsum + x*x smallest = min(x, smallest) largest = max(x, largest) - t1 = time.time() + t1 = time.perf_counter() print(round(t1-t0, 3), 'sec,', end=' ') avg = total/n stddev = _sqrt(sqsum/n - avg*avg) diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index 466b522db9ed..dc2abf237bc0 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -162,7 +162,7 @@ def runtest_inner(ns, test, display_failure=True): abstest = get_abs_module(ns, test) clear_caches() with saved_test_environment(test, ns.verbose, ns.quiet, pgo=ns.pgo) as environment: - start_time = time.time() + start_time = time.perf_counter() the_module = importlib.import_module(abstest) # If the test has a test_main, that will run the appropriate # tests. If not, use normal unittest test loading. @@ -180,7 +180,7 @@ def test_runner(): refleak = dash_R(the_module, test, test_runner, ns.huntrleaks) else: test_runner() - test_time = time.time() - start_time + test_time = time.perf_counter() - start_time post_test_cleanup() except support.ResourceDenied as msg: if not ns.quiet and not ns.pgo: diff --git a/Lib/test/time_hashlib.py b/Lib/test/time_hashlib.py index 2585ecb78207..55ebac62912f 100644 --- a/Lib/test/time_hashlib.py +++ b/Lib/test/time_hashlib.py @@ -14,26 +14,26 @@ def test_scaled_msg(scale, name): longStr = b'Z'*scale localCF = creatorFunc - start = time.time() + start = time.perf_counter() for f in range(iterations): x = localCF(longStr).digest() - end = time.time() + end = time.perf_counter() print(('%2.2f' % (end-start)), "seconds", iterations, "x", len(longStr), "bytes", name) def test_create(): - start = time.time() + start = time.perf_counter() for f in range(20000): d = creatorFunc() - end = time.time() + end = time.perf_counter() print(('%2.2f' % (end-start)), "seconds", '[20000 creations]') def test_zero(): - start = time.time() + start = time.perf_counter() for f in range(20000): x = creatorFunc().digest() - end = time.time() + end = time.perf_counter() print(('%2.2f' % (end-start)), "seconds", '[20000 "" digests]') diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py index 2c5ea4ab079d..45e7e4c0458d 100644 --- a/Lib/unittest/runner.py +++ b/Lib/unittest/runner.py @@ -168,7 +168,7 @@ def run(self, test): warnings.filterwarnings('module', category=DeprecationWarning, message=r'Please use assert\w+ instead.') - startTime = time.time() + startTime = time.perf_counter() startTestRun = getattr(result, 'startTestRun', None) if startTestRun is not None: startTestRun() @@ -178,7 +178,7 @@ def run(self, test): stopTestRun = getattr(result, 'stopTestRun', None) if stopTestRun is not None: stopTestRun() - stopTime = time.time() + stopTime = time.perf_counter() timeTaken = stopTime - startTime result.printErrors() if hasattr(result, 'separator2'): diff --git a/Misc/NEWS.d/next/Library/2018-12-16-23-28-49.bpo-35513.pn-Zh3.rst b/Misc/NEWS.d/next/Library/2018-12-16-23-28-49.bpo-35513.pn-Zh3.rst new file mode 100644 index 000000000000..f1436a718de2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-16-23-28-49.bpo-35513.pn-Zh3.rst @@ -0,0 +1,4 @@ +:class:`~unittest.runner.TextTestRunner` of :mod:`unittest.runner` now uses +:func:`time.perf_counter` rather than :func:`time.time` to measure the +execution time of a test: :func:`time.time` can go backwards, whereas +:func:`time.perf_counter` is monotonic. From webhook-mailer at python.org Mon Dec 17 06:12:37 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 17 Dec 2018 11:12:37 -0000 Subject: [Python-checkins] bpo-23451: Update time.monotonic() documentation (GH-11190) Message-ID: https://github.com/python/cpython/commit/3ab064e80a9be1e6e9c62437fffb92bde9c5e1fb commit: 3ab064e80a9be1e6e9c62437fffb92bde9c5e1fb branch: master author: Victor Stinner committer: GitHub date: 2018-12-17T12:12:34+01:00 summary: bpo-23451: Update time.monotonic() documentation (GH-11190) bpo-23451, bpo-22117: Python 3.5 requires Windows Vista or newer, time.monotonic() is now always system-wide. files: M Doc/library/time.rst diff --git a/Doc/library/time.rst b/Doc/library/time.rst index c6a1f337d695..0ffce475a368 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -293,17 +293,9 @@ Functions The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid. - On Windows versions older than Vista, :func:`monotonic` detects - :c:func:`GetTickCount` integer overflow (32 bits, roll-over after 49.7 days). - It increases an internal epoch (reference time) by 2\ :sup:`32` each time - that an overflow is detected. The epoch is stored in the process-local state - and so the value of :func:`monotonic` may be different in two Python - processes running for more than 49 days. On more recent versions of Windows - and on other operating systems, :func:`monotonic` is system-wide. - .. versionadded:: 3.3 .. versionchanged:: 3.5 - The function is now always available. + The function is now always available and always system-wide. .. function:: monotonic_ns() -> int From webhook-mailer at python.org Mon Dec 17 06:31:07 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 17 Dec 2018 11:31:07 -0000 Subject: [Python-checkins] bpo-23451: Update time.monotonic() documentation (GH-11190) Message-ID: https://github.com/python/cpython/commit/c367d52a74781b2c9ffd9e29722fbdfc0234408c commit: c367d52a74781b2c9ffd9e29722fbdfc0234408c branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-17T03:31:03-08:00 summary: bpo-23451: Update time.monotonic() documentation (GH-11190) bpo-23451, bpo-22117: Python 3.5 requires Windows Vista or newer, time.monotonic() is now always system-wide. (cherry picked from commit 3ab064e80a9be1e6e9c62437fffb92bde9c5e1fb) Co-authored-by: Victor Stinner files: M Doc/library/time.rst diff --git a/Doc/library/time.rst b/Doc/library/time.rst index c6a1f337d695..0ffce475a368 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -293,17 +293,9 @@ Functions The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid. - On Windows versions older than Vista, :func:`monotonic` detects - :c:func:`GetTickCount` integer overflow (32 bits, roll-over after 49.7 days). - It increases an internal epoch (reference time) by 2\ :sup:`32` each time - that an overflow is detected. The epoch is stored in the process-local state - and so the value of :func:`monotonic` may be different in two Python - processes running for more than 49 days. On more recent versions of Windows - and on other operating systems, :func:`monotonic` is system-wide. - .. versionadded:: 3.3 .. versionchanged:: 3.5 - The function is now always available. + The function is now always available and always system-wide. .. function:: monotonic_ns() -> int From webhook-mailer at python.org Mon Dec 17 07:57:09 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 17 Dec 2018 12:57:09 -0000 Subject: [Python-checkins] Fixed a few obvious mistakes in c-api docs (GH-11184) Message-ID: https://github.com/python/cpython/commit/05c1b387f1cad3d3d005bb98ad42b1e31eb9e379 commit: 05c1b387f1cad3d3d005bb98ad42b1e31eb9e379 branch: master author: Beomsoo Kim committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-17T04:57:03-08:00 summary: Fixed a few obvious mistakes in c-api docs (GH-11184) I thought these simple changes doesn't need bpo number(Am I right..?). Please refer to the commit message for detail. files: M Doc/c-api/intro.rst M Doc/c-api/refcounting.rst diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index 15006100c736..6bb2356f694e 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -234,7 +234,7 @@ duration of the call. However, a common pitfall is to extract an object from a list and hold on to it for a while without incrementing its reference count. Some other operation might conceivably remove the object from the list, decrementing its reference count -and possible deallocating it. The real danger is that innocent-looking +and possibly deallocating it. The real danger is that innocent-looking operations may invoke arbitrary Python code which could do this; there is a code path which allows control to flow back to the user from a :c:func:`Py_DECREF`, so almost any operation is potentially dangerous. diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 4f512ecdbe9f..225a1feb2506 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -58,8 +58,8 @@ objects. the macro carefully uses a temporary variable and sets the argument to *NULL* before decrementing its reference count. - It is a good idea to use this macro whenever decrementing the value of a - variable that might be traversed during garbage collection. + It is a good idea to use this macro whenever decrementing the reference + count of an object that might be traversed during garbage collection. The following functions are for runtime dynamic embedding of Python: From webhook-mailer at python.org Mon Dec 17 08:08:00 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 17 Dec 2018 13:08:00 -0000 Subject: [Python-checkins] bpo-35415: validate fileno argument to socket.socket (GH-10917) Message-ID: https://github.com/python/cpython/commit/e991270363435da12049ecfe70bb69bd9c14b535 commit: e991270363435da12049ecfe70bb69bd9c14b535 branch: master author: Dima Tisnek committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> date: 2018-12-17T05:07:55-08:00 summary: bpo-35415: validate fileno argument to socket.socket (GH-10917) https://bugs.python.org/issue35415 files: A Misc/NEWS.d/next/Library/2018-12-06-14-44-21.bpo-35415.-HoK3d.rst M Lib/test/test_socket.py M Modules/socketmodule.c diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 626a07797358..bfbd1cc2a18a 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1700,7 +1700,6 @@ def test_socket_consistent_sock_type(self): s.setblocking(False) self.assertEqual(s.type, socket.SOCK_STREAM) - @unittest.skipIf(os.name == 'nt', 'Will not work on Windows') def test_unknown_socket_family_repr(self): # Test that when created with a family that's not one of the known # AF_*/SOCK_* constants, socket.family just returns the number. @@ -1708,10 +1707,8 @@ def test_unknown_socket_family_repr(self): # To do this we fool socket.socket into believing it already has an # open fd because on this path it doesn't actually verify the family and # type and populates the socket object. - # - # On Windows this trick won't work, so the test is skipped. - fd, path = tempfile.mkstemp() - self.addCleanup(os.unlink, path) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + fd = sock.detach() unknown_family = max(socket.AddressFamily.__members__.values()) + 1 unknown_type = max( @@ -1785,6 +1782,48 @@ def test_socket_fileno(self): s.bind(os.path.join(tmpdir, 'socket')) self._test_socket_fileno(s, socket.AF_UNIX, socket.SOCK_STREAM) + def test_socket_fileno_rejects_float(self): + with self.assertRaisesRegex(TypeError, "integer argument expected"): + socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=42.5) + + def test_socket_fileno_rejects_other_types(self): + with self.assertRaisesRegex(TypeError, "integer is required"): + socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno="foo") + + def test_socket_fileno_rejects_invalid_socket(self): + with self.assertRaisesRegex(ValueError, "negative file descriptor"): + socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=-1) + + @unittest.skipIf(os.name == "nt", "Windows disallows -1 only") + def test_socket_fileno_rejects_negative(self): + with self.assertRaisesRegex(ValueError, "negative file descriptor"): + socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=-42) + + def test_socket_fileno_requires_valid_fd(self): + WSAENOTSOCK = 10038 + with self.assertRaises(OSError) as cm: + socket.socket(fileno=support.make_bad_fd()) + self.assertIn(cm.exception.errno, (errno.EBADF, WSAENOTSOCK)) + + with self.assertRaises(OSError) as cm: + socket.socket( + socket.AF_INET, + socket.SOCK_STREAM, + fileno=support.make_bad_fd()) + self.assertIn(cm.exception.errno, (errno.EBADF, WSAENOTSOCK)) + + def test_socket_fileno_requires_socket_fd(self): + with tempfile.NamedTemporaryFile() as afile: + with self.assertRaises(OSError): + socket.socket(fileno=afile.fileno()) + + with self.assertRaises(OSError) as cm: + socket.socket( + socket.AF_INET, + socket.SOCK_STREAM, + fileno=afile.fileno()) + self.assertEqual(cm.exception.errno, errno.ENOTSOCK) + @unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.') class BasicCANTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2018-12-06-14-44-21.bpo-35415.-HoK3d.rst b/Misc/NEWS.d/next/Library/2018-12-06-14-44-21.bpo-35415.-HoK3d.rst new file mode 100644 index 000000000000..ab053df4f74c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-06-14-44-21.bpo-35415.-HoK3d.rst @@ -0,0 +1 @@ +Validate fileno= argument to socket.socket(). diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 73d3e1add3ef..66e52f84eb91 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5018,28 +5018,45 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) else #endif { + + if (PyFloat_Check(fdobj)) { + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float"); + return -1; + } + fd = PyLong_AsSocket_t(fdobj); if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) return -1; +#ifdef MS_WINDOWS if (fd == INVALID_SOCKET) { - PyErr_SetString(PyExc_ValueError, - "can't use invalid socket value"); +#else + if (fd < 0) { +#endif + PyErr_SetString(PyExc_ValueError, "negative file descriptor"); return -1; } - if (family == -1) { - sock_addr_t addrbuf; - socklen_t addrlen = sizeof(sock_addr_t); + /* validate that passed file descriptor is valid and a socket. */ + sock_addr_t addrbuf; + socklen_t addrlen = sizeof(sock_addr_t); - memset(&addrbuf, 0, addrlen); - if (getsockname(fd, SAS2SA(&addrbuf), &addrlen) == 0) { + memset(&addrbuf, 0, addrlen); + if (getsockname(fd, SAS2SA(&addrbuf), &addrlen) == 0) { + if (family == -1) { family = SAS2SA(&addrbuf)->sa_family; - } else { + } + } else { #ifdef MS_WINDOWS - PyErr_SetFromWindowsErrWithFilename(0, "family"); + /* getsockname() on an unbound socket is an error on Windows. + Invalid descriptor and not a socket is same error code. + Error out if family must be resolved, or bad descriptor. */ + if (family == -1 || CHECK_ERRNO(ENOTSOCK)) { #else - PyErr_SetFromErrnoWithFilename(PyExc_OSError, "family"); + /* getsockname() is not supported for SOL_ALG on Linux. */ + if (family == -1 || CHECK_ERRNO(EBADF) || CHECK_ERRNO(ENOTSOCK)) { #endif + set_error(); return -1; } } @@ -5052,11 +5069,7 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) { type = tmp; } else { -#ifdef MS_WINDOWS - PyErr_SetFromWindowsErrWithFilename(0, "type"); -#else - PyErr_SetFromErrnoWithFilename(PyExc_OSError, "type"); -#endif + set_error(); return -1; } } @@ -5072,11 +5085,7 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) { proto = tmp; } else { -#ifdef MS_WINDOWS - PyErr_SetFromWindowsErrWithFilename(0, "protocol"); -#else - PyErr_SetFromErrnoWithFilename(PyExc_OSError, "protocol"); -#endif + set_error(); return -1; } } From webhook-mailer at python.org Mon Dec 17 09:16:29 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Mon, 17 Dec 2018 14:16:29 -0000 Subject: [Python-checkins] bpo-18799: Resurrect test_404 in test_xmlrpc. (GH-11196) Message-ID: https://github.com/python/cpython/commit/fae95874b729dfe62a7a40625f8717aed20b0fca commit: fae95874b729dfe62a7a40625f8717aed20b0fca branch: master author: Vajrasky Kok committer: Serhiy Storchaka date: 2018-12-17T16:16:24+02:00 summary: bpo-18799: Resurrect test_404 in test_xmlrpc. (GH-11196) files: M Lib/test/test_xmlrpc.py diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 5f780d88002e..32263f7f0b3b 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -818,11 +818,10 @@ def test_nonascii_methodname(self): # protocol error; provide additional information in test output self.fail("%s\n%s" % (e, getattr(e, "headers", ""))) - # [ch] The test 404 is causing lots of false alarms. - def XXXtest_404(self): + def test_404(self): # send POST with http.client, it should return 404 header and # 'Not Found' message. - conn = httplib.client.HTTPConnection(ADDR, PORT) + conn = http.client.HTTPConnection(ADDR, PORT) conn.request('POST', '/this-is-not-valid') response = conn.getresponse() conn.close() From webhook-mailer at python.org Mon Dec 17 09:43:21 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Mon, 17 Dec 2018 14:43:21 -0000 Subject: [Python-checkins] bpo-35504: Fix a SystemError when delete the characters_written attribute of an OSError. (GH-11172) Message-ID: https://github.com/python/cpython/commit/e2af34fcf84b41189b54e1f2912faded5daabaca commit: e2af34fcf84b41189b54e1f2912faded5daabaca branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-17T16:43:14+02:00 summary: bpo-35504: Fix a SystemError when delete the characters_written attribute of an OSError. (GH-11172) files: A Misc/NEWS.d/next/Core and Builtins/2018-12-15-00-47-41.bpo-35504.9gVuen.rst M Lib/test/test_exception_hierarchy.py M Objects/exceptions.c diff --git a/Lib/test/test_exception_hierarchy.py b/Lib/test/test_exception_hierarchy.py index 864959679094..43b4af84039c 100644 --- a/Lib/test/test_exception_hierarchy.py +++ b/Lib/test/test_exception_hierarchy.py @@ -150,10 +150,15 @@ def test_blockingioerror(self): e = BlockingIOError(*args[:n]) with self.assertRaises(AttributeError): e.characters_written + with self.assertRaises(AttributeError): + del e.characters_written e = BlockingIOError("a", "b", 3) self.assertEqual(e.characters_written, 3) e.characters_written = 5 self.assertEqual(e.characters_written, 5) + del e.characters_written + with self.assertRaises(AttributeError): + e.characters_written class ExplicitSubclassingTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-15-00-47-41.bpo-35504.9gVuen.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-15-00-47-41.bpo-35504.9gVuen.rst new file mode 100644 index 000000000000..622b50ca62da --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-15-00-47-41.bpo-35504.9gVuen.rst @@ -0,0 +1 @@ +Fixed a SystemError when delete the characters_written attribute of an OSError. diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 05578d4a6aad..002a602373d7 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1196,6 +1196,14 @@ OSError_written_get(PyOSErrorObject *self, void *context) static int OSError_written_set(PyOSErrorObject *self, PyObject *arg, void *context) { + if (arg == NULL) { + if (self->written == -1) { + PyErr_SetString(PyExc_AttributeError, "characters_written"); + return -1; + } + self->written = -1; + return 0; + } Py_ssize_t n; n = PyNumber_AsSsize_t(arg, PyExc_ValueError); if (n == -1 && PyErr_Occurred()) From webhook-mailer at python.org Mon Dec 17 09:47:49 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Mon, 17 Dec 2018 14:47:49 -0000 Subject: [Python-checkins] bpo-35490: Remove the DecodeFSDefault return converter in AC. (#11152) Message-ID: https://github.com/python/cpython/commit/4db62e115891425db2a974142a72d8eaaf95eecb commit: 4db62e115891425db2a974142a72d8eaaf95eecb branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-17T16:47:45+02:00 summary: bpo-35490: Remove the DecodeFSDefault return converter in AC. (#11152) files: M Modules/clinic/posixmodule.c.h M Modules/posixmodule.c M Tools/clinic/clinic.py diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 6d523bcc9ff4..eabfcf7fc83c 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -176,7 +176,7 @@ PyDoc_STRVAR(os_ttyname__doc__, #define OS_TTYNAME_METHODDEF \ {"ttyname", (PyCFunction)os_ttyname, METH_O, os_ttyname__doc__}, -static char * +static PyObject * os_ttyname_impl(PyObject *module, int fd); static PyObject * @@ -184,16 +184,11 @@ os_ttyname(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; int fd; - char *_return_value; if (!PyArg_Parse(arg, "i:ttyname", &fd)) { goto exit; } - _return_value = os_ttyname_impl(module, fd); - if (_return_value == NULL) { - goto exit; - } - return_value = PyUnicode_DecodeFSDefault(_return_value); + return_value = os_ttyname_impl(module, fd); exit: return return_value; @@ -6758,4 +6753,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject #ifndef OS_GETRANDOM_METHODDEF #define OS_GETRANDOM_METHODDEF #endif /* !defined(OS_GETRANDOM_METHODDEF) */ -/*[clinic end generated code: output=7ebb53d872bab149 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=87a3ebadb91bc46b input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 0ea391e799b9..41fedb097ed0 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -2647,7 +2647,7 @@ os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd, #ifdef HAVE_TTYNAME /*[clinic input] -os.ttyname -> DecodeFSDefault +os.ttyname fd: int Integer file descriptor handle. @@ -2657,16 +2657,17 @@ os.ttyname -> DecodeFSDefault Return the name of the terminal device connected to 'fd'. [clinic start generated code]*/ -static char * +static PyObject * os_ttyname_impl(PyObject *module, int fd) -/*[clinic end generated code: output=ed16ad216d813591 input=5f72ca83e76b3b45]*/ +/*[clinic end generated code: output=c424d2e9d1cd636a input=9ff5a58b08115c55]*/ { char *ret; ret = ttyname(fd); - if (ret == NULL) - posix_error(); - return ret; + if (ret == NULL) { + return posix_error(); + } + return PyUnicode_DecodeFSDefault(ret); } #endif diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 1adabb95cc96..cd492b47a249 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -3166,16 +3166,6 @@ class float_return_converter(double_return_converter): cast = '(double)' -class DecodeFSDefault_return_converter(CReturnConverter): - type = 'char *' - - def render(self, function, data): - self.declare(data) - self.err_occurred_if_null_pointer("_return_value", data) - data.return_conversion.append( - 'return_value = PyUnicode_DecodeFSDefault(_return_value);\n') - - def eval_ast_expr(node, globals, *, filename='-'): """ Takes an ast.Expr node. Compiles and evaluates it. From webhook-mailer at python.org Mon Dec 17 09:52:49 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Mon, 17 Dec 2018 14:52:49 -0000 Subject: [Python-checkins] bpo-35504: Fix segfaults and SystemErrors when deleting certain attrs. (GH-11175) Message-ID: https://github.com/python/cpython/commit/842acaab1376c5c84fd5966bb6070e289880e1ca commit: 842acaab1376c5c84fd5966bb6070e289880e1ca branch: master author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-17T16:52:45+02:00 summary: bpo-35504: Fix segfaults and SystemErrors when deleting certain attrs. (GH-11175) files: A Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst M Lib/ctypes/test/test_strings.py M Lib/sqlite3/test/regression.py M Lib/test/multibytecodec_support.py M Lib/test/test_asyncio/test_futures.py M Lib/test/test_asyncio/test_tasks.py M Lib/test/test_frame.py M Lib/test/test_io.py M Modules/_asynciomodule.c M Modules/_ctypes/_ctypes.c M Modules/_io/textio.c M Modules/_sqlite/connection.c M Modules/_ssl.c M Modules/cjkcodecs/multibytecodec.c M Objects/frameobject.c diff --git a/Lib/ctypes/test/test_strings.py b/Lib/ctypes/test/test_strings.py index e28e141394de..5434efda10c0 100644 --- a/Lib/ctypes/test/test_strings.py +++ b/Lib/ctypes/test/test_strings.py @@ -54,6 +54,13 @@ def test_param_2(self): ## print BUF.from_param(c_char_p("python")) ## print BUF.from_param(BUF(*"pyth")) + def test_del_segfault(self): + BUF = c_char * 4 + buf = BUF() + with self.assertRaises(AttributeError): + del buf.raw + + @need_symbol('c_wchar') class WStringArrayTestCase(unittest.TestCase): def test(self): diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 1c59a3cd31c6..865bd88f74f1 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -379,6 +379,10 @@ def callback(*args): del ref support.gc_collect() + def CheckDelIsolation_levelSegfault(self): + with self.assertRaises(AttributeError): + del self.con.isolation_level + class UnhashableFunc: __hash__ = None diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index 813b7aa1bd2d..cca8af67d6d1 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -277,6 +277,11 @@ def test_streamwriter_reset_no_pending(self): writer = self.writer(stream) writer.reset() + def test_incrementalencoder_del_segfault(self): + e = self.incrementalencoder() + with self.assertRaises(AttributeError): + del e.errors + class TestBase_Mapping(unittest.TestCase): pass_enctest = [] diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 333935620909..2e4583d12450 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -570,6 +570,13 @@ class CFutureTests(BaseFutureTests, test_utils.TestCase): except AttributeError: cls = None + def test_future_del_segfault(self): + fut = self._new_future(loop=self.loop) + with self.assertRaises(AttributeError): + del fut._asyncio_future_blocking + with self.assertRaises(AttributeError): + del fut._log_traceback + @unittest.skipUnless(hasattr(futures, '_CFuture'), 'requires the C _asyncio module') diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index c65d1f2440d2..22f14f87624e 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2546,6 +2546,15 @@ def coro(): self.loop.run_until_complete(task) self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) + def test_del__log_destroy_pending_segfault(self): + @asyncio.coroutine + def coro(): + pass + task = self.new_task(self.loop, coro()) + self.loop.run_until_complete(task) + with self.assertRaises(AttributeError): + del task._log_destroy_pending + @unittest.skipUnless(hasattr(futures, '_CFuture') and hasattr(tasks, '_CTask'), diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index fd795085a5cd..d6aa2834cbc2 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -109,10 +109,7 @@ class C: self.assertIs(None, wr()) -class FrameLocalsTest(unittest.TestCase): - """ - Tests for the .f_locals attribute. - """ +class FrameAttrsTest(unittest.TestCase): def make_frames(self): def outer(): @@ -159,6 +156,11 @@ def test_locals_clear_locals(self): self.assertEqual(outer.f_locals, {}) self.assertEqual(inner.f_locals, {}) + def test_f_lineno_del_segfault(self): + f, _, _ = self.make_frames() + with self.assertRaises(AttributeError): + del f.f_lineno + class ReprTest(unittest.TestCase): """ diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 14352ff84fff..dc353c159fb0 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3663,6 +3663,11 @@ def test_rwpair_cleared_before_textio(self): t2.buddy = t1 support.gc_collect() + def test_del__CHUNK_SIZE_SystemError(self): + t = self.TextIOWrapper(self.BytesIO(), encoding='ascii') + with self.assertRaises(AttributeError): + del t._CHUNK_SIZE + class PyTextIOWrapperTest(TextIOWrapperTest): io = pyio diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst new file mode 100644 index 000000000000..2a4f0f694fee --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst @@ -0,0 +1,2 @@ +Fix segfaults and :exc:`SystemError`\ s when deleting certain attributes. +Patch by Zackery Spytz. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 0998cc103880..7637cb75d63f 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1113,6 +1113,10 @@ FutureObj_set_blocking(FutureObj *fut, PyObject *val, void *Py_UNUSED(ignored)) if (future_ensure_alive(fut)) { return -1; } + if (val == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } int is_true = PyObject_IsTrue(val); if (is_true < 0) { @@ -1137,6 +1141,10 @@ FutureObj_get_log_traceback(FutureObj *fut, void *Py_UNUSED(ignored)) static int FutureObj_set_log_traceback(FutureObj *fut, PyObject *val, void *Py_UNUSED(ignored)) { + if (val == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } int is_true = PyObject_IsTrue(val); if (is_true < 0) { return -1; @@ -2015,6 +2023,10 @@ TaskObj_get_log_destroy_pending(TaskObj *task, void *Py_UNUSED(ignored)) static int TaskObj_set_log_destroy_pending(TaskObj *task, PyObject *val, void *Py_UNUSED(ignored)) { + if (val == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } int is_true = PyObject_IsTrue(val); if (is_true < 0) { return -1; diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 163b3e3b6c78..637be4222d98 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1191,6 +1191,10 @@ CharArray_set_raw(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored)) Py_ssize_t size; Py_buffer view; + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } if (PyObject_GetBuffer(value, &view, PyBUF_SIMPLE) < 0) return -1; size = view.len; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 645d7123324c..14f94885b5f8 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -3042,6 +3042,10 @@ textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) { Py_ssize_t n; CHECK_ATTACHED_INT(self); + if (arg == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } n = PyNumber_AsSsize_t(arg, PyExc_ValueError); if (n == -1 && PyErr_Occurred()) return -1; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index aab4b1afd220..e3340bf19f7b 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1161,6 +1161,10 @@ static PyObject* pysqlite_connection_get_in_transaction(pysqlite_Connection* sel static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level, void *Py_UNUSED(ignored)) { + if (isolation_level == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } if (isolation_level == Py_None) { PyObject *res = pysqlite_connection_commit(self, NULL); if (!res) { diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 269f003e0df9..4e3352d9e661 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -3625,6 +3625,10 @@ 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; + } int pha = PyObject_IsTrue(arg); if (pha == -1) { diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index 938a774f05cb..f266e5f33a60 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -133,6 +133,10 @@ codecctx_errors_set(MultibyteStatefulCodecContext *self, PyObject *value, PyObject *cb; const char *str; + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, "errors must be a string"); return -1; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 400f99f6c963..8488b96a9aab 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -105,6 +105,10 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore int blockstack[CO_MAXBLOCKS]; /* Walking the 'finally' blocks */ int blockstack_top = 0; /* (ditto) */ + if (p_new_lineno == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } /* f_lineno must be an integer. */ if (!PyLong_CheckExact(p_new_lineno)) { PyErr_SetString(PyExc_ValueError, From webhook-mailer at python.org Mon Dec 17 10:10:25 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 17 Dec 2018 15:10:25 -0000 Subject: [Python-checkins] bpo-35504: Fix segfaults and SystemErrors when deleting certain attrs. (GH-11175) Message-ID: https://github.com/python/cpython/commit/cb272843f2d5dfc4ef996ba952b99a3e30c88bbc commit: cb272843f2d5dfc4ef996ba952b99a3e30c88bbc branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-17T07:10:20-08:00 summary: bpo-35504: Fix segfaults and SystemErrors when deleting certain attrs. (GH-11175) (cherry picked from commit 842acaab1376c5c84fd5966bb6070e289880e1ca) Co-authored-by: Zackery Spytz files: A Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst M Lib/ctypes/test/test_strings.py M Lib/sqlite3/test/regression.py M Lib/test/multibytecodec_support.py M Lib/test/test_asyncio/test_futures.py M Lib/test/test_asyncio/test_tasks.py M Lib/test/test_frame.py M Lib/test/test_io.py M Modules/_asynciomodule.c M Modules/_ctypes/_ctypes.c M Modules/_io/textio.c M Modules/_sqlite/connection.c M Modules/_ssl.c M Modules/cjkcodecs/multibytecodec.c M Objects/frameobject.c diff --git a/Lib/ctypes/test/test_strings.py b/Lib/ctypes/test/test_strings.py index e28e141394de..5434efda10c0 100644 --- a/Lib/ctypes/test/test_strings.py +++ b/Lib/ctypes/test/test_strings.py @@ -54,6 +54,13 @@ def test_param_2(self): ## print BUF.from_param(c_char_p("python")) ## print BUF.from_param(BUF(*"pyth")) + def test_del_segfault(self): + BUF = c_char * 4 + buf = BUF() + with self.assertRaises(AttributeError): + del buf.raw + + @need_symbol('c_wchar') class WStringArrayTestCase(unittest.TestCase): def test(self): diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 1c59a3cd31c6..865bd88f74f1 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -379,6 +379,10 @@ def callback(*args): del ref support.gc_collect() + def CheckDelIsolation_levelSegfault(self): + with self.assertRaises(AttributeError): + del self.con.isolation_level + class UnhashableFunc: __hash__ = None diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index 813b7aa1bd2d..cca8af67d6d1 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -277,6 +277,11 @@ def test_streamwriter_reset_no_pending(self): writer = self.writer(stream) writer.reset() + def test_incrementalencoder_del_segfault(self): + e = self.incrementalencoder() + with self.assertRaises(AttributeError): + del e.errors + class TestBase_Mapping(unittest.TestCase): pass_enctest = [] diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 8c837ad6b620..9608a3a81cc3 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -566,6 +566,13 @@ class CFutureTests(BaseFutureTests, test_utils.TestCase): except AttributeError: cls = None + def test_future_del_segfault(self): + fut = self._new_future(loop=self.loop) + with self.assertRaises(AttributeError): + del fut._asyncio_future_blocking + with self.assertRaises(AttributeError): + del fut._log_traceback + @unittest.skipUnless(hasattr(futures, '_CFuture'), 'requires the C _asyncio module') diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 12c6ef4f619e..d92ed32bc99f 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2512,6 +2512,15 @@ def coro(): self.loop.run_until_complete(task) self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) + def test_del__log_destroy_pending_segfault(self): + @asyncio.coroutine + def coro(): + pass + task = self.new_task(self.loop, coro()) + self.loop.run_until_complete(task) + with self.assertRaises(AttributeError): + del task._log_destroy_pending + @unittest.skipUnless(hasattr(futures, '_CFuture') and hasattr(tasks, '_CTask'), diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index fd795085a5cd..d6aa2834cbc2 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -109,10 +109,7 @@ class C: self.assertIs(None, wr()) -class FrameLocalsTest(unittest.TestCase): - """ - Tests for the .f_locals attribute. - """ +class FrameAttrsTest(unittest.TestCase): def make_frames(self): def outer(): @@ -159,6 +156,11 @@ def test_locals_clear_locals(self): self.assertEqual(outer.f_locals, {}) self.assertEqual(inner.f_locals, {}) + def test_f_lineno_del_segfault(self): + f, _, _ = self.make_frames() + with self.assertRaises(AttributeError): + del f.f_lineno + class ReprTest(unittest.TestCase): """ diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index b41141e28adb..4c206d287f27 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3635,6 +3635,11 @@ def test_rwpair_cleared_before_textio(self): t2.buddy = t1 support.gc_collect() + def test_del__CHUNK_SIZE_SystemError(self): + t = self.TextIOWrapper(self.BytesIO(), encoding='ascii') + with self.assertRaises(AttributeError): + del t._CHUNK_SIZE + class PyTextIOWrapperTest(TextIOWrapperTest): io = pyio diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst new file mode 100644 index 000000000000..2a4f0f694fee --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst @@ -0,0 +1,2 @@ +Fix segfaults and :exc:`SystemError`\ s when deleting certain attributes. +Patch by Zackery Spytz. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 5816a6748f66..35264f5815c3 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1110,6 +1110,10 @@ FutureObj_set_blocking(FutureObj *fut, PyObject *val, void *Py_UNUSED(ignored)) if (future_ensure_alive(fut)) { return -1; } + if (val == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } int is_true = PyObject_IsTrue(val); if (is_true < 0) { @@ -1134,6 +1138,10 @@ FutureObj_get_log_traceback(FutureObj *fut, void *Py_UNUSED(ignored)) static int FutureObj_set_log_traceback(FutureObj *fut, PyObject *val, void *Py_UNUSED(ignored)) { + if (val == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } int is_true = PyObject_IsTrue(val); if (is_true < 0) { return -1; @@ -2008,6 +2016,10 @@ TaskObj_get_log_destroy_pending(TaskObj *task, void *Py_UNUSED(ignored)) static int TaskObj_set_log_destroy_pending(TaskObj *task, PyObject *val, void *Py_UNUSED(ignored)) { + if (val == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } int is_true = PyObject_IsTrue(val); if (is_true < 0) { return -1; diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index c5fc811ad98e..937375a4ea44 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1171,6 +1171,10 @@ CharArray_set_raw(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored)) Py_ssize_t size; Py_buffer view; + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } if (PyObject_GetBuffer(value, &view, PyBUF_SIMPLE) < 0) return -1; size = view.len; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 9d0d9cac40db..49b545c4787f 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -3049,6 +3049,10 @@ textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) { Py_ssize_t n; CHECK_ATTACHED_INT(self); + if (arg == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } n = PyNumber_AsSsize_t(arg, PyExc_ValueError); if (n == -1 && PyErr_Occurred()) return -1; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 351317e78db4..d43286a2d34d 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1138,6 +1138,10 @@ static PyObject* pysqlite_connection_get_in_transaction(pysqlite_Connection* sel static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level, void *Py_UNUSED(ignored)) { + if (isolation_level == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } if (isolation_level == Py_None) { PyObject *res = pysqlite_connection_commit(self, NULL); if (!res) { diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 310b38bf11f4..9894ad821d63 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -3626,6 +3626,10 @@ 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; + } int pha = PyObject_IsTrue(arg); if (pha == -1) { diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index 4d0aaf3a336f..5c91ada103d5 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -133,6 +133,10 @@ codecctx_errors_set(MultibyteStatefulCodecContext *self, PyObject *value, PyObject *cb; const char *str; + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, "errors must be a string"); return -1; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 4ef10d0eb7bc..4362615cb089 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -90,6 +90,10 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore int blockstack_top = 0; /* (ditto) */ unsigned char setup_op = 0; /* (ditto) */ + if (p_new_lineno == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } /* f_lineno must be an integer. */ if (!PyLong_CheckExact(p_new_lineno)) { PyErr_SetString(PyExc_ValueError, From webhook-mailer at python.org Mon Dec 17 10:30:07 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Mon, 17 Dec 2018 15:30:07 -0000 Subject: [Python-checkins] bpo-35475: Add more PyImport* functions in refcounts.dat. (GH-11142) Message-ID: https://github.com/python/cpython/commit/bdabb0737c631835b246c9823852d20331243315 commit: bdabb0737c631835b246c9823852d20331243315 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-17T17:30:03+02:00 summary: bpo-35475: Add more PyImport* functions in refcounts.dat. (GH-11142) files: M Doc/data/refcounts.dat diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 62cc93832783..cedcbfe3b834 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -569,6 +569,9 @@ Py_InitModule4:int:apiver::usually provided by Py_InitModule or Py_InitModule3 PyImport_AddModule:PyObject*::0:reference borrowed from sys.modules PyImport_AddModule:const char*:name:: +PyImport_AddModuleObject:PyObject*::0:reference borrowed from sys.modules +PyImport_AddModuleObject:PyObject*:name:0: + PyImport_Cleanup:void::: PyImport_ExecCodeModule:PyObject*::+1: @@ -580,6 +583,21 @@ PyImport_ExecCodeModuleEx:const char*:name:: PyImport_ExecCodeModuleEx:PyObject*:co:0: PyImport_ExecCodeModuleEx:const char*:pathname:: +PyImport_ExecCodeModuleObject:PyObject*::+1: +PyImport_ExecCodeModuleObject:const char*:name:: +PyImport_ExecCodeModuleObject:PyObject*:co:0: +PyImport_ExecCodeModuleObject:PyObject*:pathname:0: +PyImport_ExecCodeModuleObject:PyObject*:cpathname:0: + +PyImport_ExecCodeModuleWithPathnames:PyObject*::+1: +PyImport_ExecCodeModuleWithPathnames:const char*:name:: +PyImport_ExecCodeModuleWithPathnames:PyObject*:co:0: +PyImport_ExecCodeModuleWithPathnames:const char*:pathname:: +PyImport_ExecCodeModuleWithPathnames:const char*:cpathname:: + +PyImport_GetImporter:PyObject*::+1: +PyImport_GetImporter:PyObject*:path:0: + PyImport_GetMagicNumber:long::: PyImport_GetModule:PyObject*::+1: @@ -593,6 +611,9 @@ PyImport_Import:PyObject*:name:0: PyImport_ImportFrozenModule:int::: PyImport_ImportFrozenModule:const char*::: +PyImport_ImportFrozenModuleObject:int::: +PyImport_ImportFrozenModuleObject:PyObject*::+1: + PyImport_ImportModule:PyObject*::+1: PyImport_ImportModule:const char*:name:: @@ -609,6 +630,13 @@ PyImport_ImportModuleLevel:PyObject*:locals:0:??? PyImport_ImportModuleLevel:PyObject*:fromlist:0:??? PyImport_ImportModuleLevel:int:level:: +PyImport_ImportModuleLevelObject:PyObject*::+1: +PyImport_ImportModuleLevelObject:PyObject*:name:0: +PyImport_ImportModuleLevelObject:PyObject*:globals:0:??? +PyImport_ImportModuleLevelObject:PyObject*:locals:0:??? +PyImport_ImportModuleLevelObject:PyObject*:fromlist:0:??? +PyImport_ImportModuleLevelObject:int:level:: + PyImport_ReloadModule:PyObject*::+1: PyImport_ReloadModule:PyObject*:m:0: From webhook-mailer at python.org Mon Dec 17 10:34:18 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Mon, 17 Dec 2018 15:34:18 -0000 Subject: [Python-checkins] bpo-33306: Improve SyntaxError messages for unbalanced parentheses. (GH-6516) Message-ID: https://github.com/python/cpython/commit/94cf308ee231bfbfaa9ddc50b9764545a1318773 commit: 94cf308ee231bfbfaa9ddc50b9764545a1318773 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-17T17:34:14+02:00 summary: bpo-33306: Improve SyntaxError messages for unbalanced parentheses. (GH-6516) files: A Misc/NEWS.d/next/Core and Builtins/2018-04-18-12-23-30.bpo-33306.tSM3cp.rst M Lib/test/test_fstring.py M Lib/test/test_site.py M Parser/tokenizer.c M Parser/tokenizer.h diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 09b5ae1fdaee..fe3804b2215d 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -1004,10 +1004,14 @@ def test_str_format_differences(self): self.assertEqual('{d[0]}'.format(d=d), 'integer') def test_invalid_expressions(self): - self.assertAllRaise(SyntaxError, 'invalid syntax', - [r"f'{a[4)}'", - r"f'{a(4]}'", - ]) + self.assertAllRaise(SyntaxError, + r"closing parenthesis '\)' does not match " + r"opening parenthesis '\[' \(, line 1\)", + [r"f'{a[4)}'"]) + self.assertAllRaise(SyntaxError, + r"closing parenthesis '\]' does not match " + r"opening parenthesis '\(' \(, line 1\)", + [r"f'{a(4]}'"]) def test_errors(self): # see issue 26287 diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index f38e8d853ada..735651ec7d75 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -133,7 +133,7 @@ def make_pth(self, contents, pth_dir='.', pth_name=TESTFN): def test_addpackage_import_bad_syntax(self): # Issue 10642 - pth_dir, pth_fn = self.make_pth("import bad)syntax\n") + pth_dir, pth_fn = self.make_pth("import bad-syntax\n") with captured_stderr() as err_out: site.addpackage(pth_dir, pth_fn, set()) self.assertRegex(err_out.getvalue(), "line 1") @@ -143,7 +143,7 @@ def test_addpackage_import_bad_syntax(self): # order doesn't matter. The next three could be a single check # but my regex foo isn't good enough to write it. self.assertRegex(err_out.getvalue(), 'Traceback') - self.assertRegex(err_out.getvalue(), r'import bad\)syntax') + self.assertRegex(err_out.getvalue(), r'import bad-syntax') self.assertRegex(err_out.getvalue(), 'SyntaxError') def test_addpackage_import_bad_exec(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-04-18-12-23-30.bpo-33306.tSM3cp.rst b/Misc/NEWS.d/next/Core and Builtins/2018-04-18-12-23-30.bpo-33306.tSM3cp.rst new file mode 100644 index 000000000000..2d891062607c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-04-18-12-23-30.bpo-33306.tSM3cp.rst @@ -0,0 +1 @@ +Improved syntax error messages for unbalanced parentheses. diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index d319a4c90a9e..c246ee204c5d 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1842,12 +1842,44 @@ tok_get(struct tok_state *tok, char **p_start, char **p_end) case '(': case '[': case '{': +#ifndef PGEN + if (tok->level >= MAXLEVEL) { + return syntaxerror(tok, "too many nested parentheses"); + } + tok->parenstack[tok->level] = c; + tok->parenlinenostack[tok->level] = tok->lineno; +#endif tok->level++; break; case ')': case ']': case '}': +#ifndef PGEN + if (!tok->level) { + return syntaxerror(tok, "unmatched '%c'", c); + } +#endif tok->level--; +#ifndef PGEN + int opening = tok->parenstack[tok->level]; + if (!((opening == '(' && c == ')') || + (opening == '[' && c == ']') || + (opening == '{' && c == '}'))) + { + if (tok->parenlinenostack[tok->level] != tok->lineno) { + return syntaxerror(tok, + "closing parenthesis '%c' does not match " + "opening parenthesis '%c' on line %d", + c, opening, tok->parenlinenostack[tok->level]); + } + else { + return syntaxerror(tok, + "closing parenthesis '%c' does not match " + "opening parenthesis '%c'", + c, opening); + } + } +#endif break; } diff --git a/Parser/tokenizer.h b/Parser/tokenizer.h index 2e31d8624da7..cd18d25dc192 100644 --- a/Parser/tokenizer.h +++ b/Parser/tokenizer.h @@ -11,6 +11,7 @@ extern "C" { #include "token.h" /* For token types */ #define MAXINDENT 100 /* Max indentation level */ +#define MAXLEVEL 200 /* Max parentheses level */ enum decoding_state { STATE_INIT, @@ -39,14 +40,16 @@ struct tok_state { int lineno; /* Current line number */ int level; /* () [] {} Parentheses nesting level */ /* Used to allow free continuations inside them */ - /* Stuff for checking on different tab sizes */ #ifndef PGEN + char parenstack[MAXLEVEL]; + int parenlinenostack[MAXLEVEL]; /* pgen doesn't have access to Python codecs, it cannot decode the input filename. The bytes filename might be kept, but it is only used by indenterror() and it is not really needed: pgen only compiles one file (Grammar/Grammar). */ PyObject *filename; #endif + /* Stuff for checking on different tab sizes */ int altindstack[MAXINDENT]; /* Stack of alternate indents */ /* Stuff for PEP 0263 */ enum decoding_state decoding_state; From webhook-mailer at python.org Mon Dec 17 10:48:32 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 17 Dec 2018 15:48:32 -0000 Subject: [Python-checkins] bpo-35475: Add more PyImport* functions in refcounts.dat. (GH-11142) Message-ID: https://github.com/python/cpython/commit/605ef6e534f05925ff826f65518abf163ed3900a commit: 605ef6e534f05925ff826f65518abf163ed3900a branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-17T07:48:29-08:00 summary: bpo-35475: Add more PyImport* functions in refcounts.dat. (GH-11142) (cherry picked from commit bdabb0737c631835b246c9823852d20331243315) Co-authored-by: Serhiy Storchaka files: M Doc/data/refcounts.dat diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 62cc93832783..cedcbfe3b834 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -569,6 +569,9 @@ Py_InitModule4:int:apiver::usually provided by Py_InitModule or Py_InitModule3 PyImport_AddModule:PyObject*::0:reference borrowed from sys.modules PyImport_AddModule:const char*:name:: +PyImport_AddModuleObject:PyObject*::0:reference borrowed from sys.modules +PyImport_AddModuleObject:PyObject*:name:0: + PyImport_Cleanup:void::: PyImport_ExecCodeModule:PyObject*::+1: @@ -580,6 +583,21 @@ PyImport_ExecCodeModuleEx:const char*:name:: PyImport_ExecCodeModuleEx:PyObject*:co:0: PyImport_ExecCodeModuleEx:const char*:pathname:: +PyImport_ExecCodeModuleObject:PyObject*::+1: +PyImport_ExecCodeModuleObject:const char*:name:: +PyImport_ExecCodeModuleObject:PyObject*:co:0: +PyImport_ExecCodeModuleObject:PyObject*:pathname:0: +PyImport_ExecCodeModuleObject:PyObject*:cpathname:0: + +PyImport_ExecCodeModuleWithPathnames:PyObject*::+1: +PyImport_ExecCodeModuleWithPathnames:const char*:name:: +PyImport_ExecCodeModuleWithPathnames:PyObject*:co:0: +PyImport_ExecCodeModuleWithPathnames:const char*:pathname:: +PyImport_ExecCodeModuleWithPathnames:const char*:cpathname:: + +PyImport_GetImporter:PyObject*::+1: +PyImport_GetImporter:PyObject*:path:0: + PyImport_GetMagicNumber:long::: PyImport_GetModule:PyObject*::+1: @@ -593,6 +611,9 @@ PyImport_Import:PyObject*:name:0: PyImport_ImportFrozenModule:int::: PyImport_ImportFrozenModule:const char*::: +PyImport_ImportFrozenModuleObject:int::: +PyImport_ImportFrozenModuleObject:PyObject*::+1: + PyImport_ImportModule:PyObject*::+1: PyImport_ImportModule:const char*:name:: @@ -609,6 +630,13 @@ PyImport_ImportModuleLevel:PyObject*:locals:0:??? PyImport_ImportModuleLevel:PyObject*:fromlist:0:??? PyImport_ImportModuleLevel:int:level:: +PyImport_ImportModuleLevelObject:PyObject*::+1: +PyImport_ImportModuleLevelObject:PyObject*:name:0: +PyImport_ImportModuleLevelObject:PyObject*:globals:0:??? +PyImport_ImportModuleLevelObject:PyObject*:locals:0:??? +PyImport_ImportModuleLevelObject:PyObject*:fromlist:0:??? +PyImport_ImportModuleLevelObject:int:level:: + PyImport_ReloadModule:PyObject*::+1: PyImport_ReloadModule:PyObject*:m:0: From webhook-mailer at python.org Mon Dec 17 12:47:30 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 17 Dec 2018 17:47:30 -0000 Subject: [Python-checkins] bpo-35348: Fix platform.architecture() (GH-11159) Message-ID: https://github.com/python/cpython/commit/0af9c33262fb43f39a6558e3f155689e83e10706 commit: 0af9c33262fb43f39a6558e3f155689e83e10706 branch: master author: Victor Stinner committer: GitHub date: 2018-12-17T18:47:24+01:00 summary: bpo-35348: Fix platform.architecture() (GH-11159) Make platform.architecture() parsing of "file" command output more reliable: * Add the "-b" option to the "file" command to omit the filename; * Force the usage of the C locale; * Search also the "shared object" pattern. Co-Authored-By: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2018-12-14-13-27-45.bpo-35348.u3Y2an.rst M Lib/platform.py diff --git a/Lib/platform.py b/Lib/platform.py index 0fe841c71ce6..9dd3f47075bf 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -608,13 +608,21 @@ def _syscmd_file(target, default=''): import subprocess target = _follow_symlinks(target) + # "file" output is locale dependent: force the usage of the C locale + # to get deterministic behavior. + env = dict(os.environ, LC_ALL='C') try: - output = subprocess.check_output(['file', target], + # -b: do not prepend filenames to output lines (brief mode) + output = subprocess.check_output(['file', '-b', target], stderr=subprocess.DEVNULL, - encoding='latin-1') + env=env) except (OSError, subprocess.CalledProcessError): return default - return (output or default) + if not output: + return default + # With the C locale, the output should be mostly ASCII-compatible. + # Decode from Latin-1 to prevent Unicode decode error. + return output.decode('latin-1') ### Information about the used architecture @@ -672,7 +680,7 @@ def architecture(executable=sys.executable, bits='', linkage=''): linkage = l return bits, linkage - if 'executable' not in fileout: + if 'executable' not in fileout and 'shared object' not in fileout: # Format not supported return bits, linkage diff --git a/Misc/NEWS.d/next/Library/2018-12-14-13-27-45.bpo-35348.u3Y2an.rst b/Misc/NEWS.d/next/Library/2018-12-14-13-27-45.bpo-35348.u3Y2an.rst new file mode 100644 index 000000000000..190db31cfd68 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-14-13-27-45.bpo-35348.u3Y2an.rst @@ -0,0 +1,3 @@ +Make :func:`platform.architecture` parsing of ``file`` command output more +reliable: add the ``-b`` option to the ``file`` command to omit the filename, +force the usage of the C locale, and search also the "shared object" pattern. From webhook-mailer at python.org Mon Dec 17 16:06:14 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Mon, 17 Dec 2018 21:06:14 -0000 Subject: [Python-checkins] bpo-35519: Rename test.bisect to test.bisect_cmd (GH-11200) Message-ID: https://github.com/python/cpython/commit/1dd035954bb03c41b954ebbd63969b4bcb0e106e commit: 1dd035954bb03c41b954ebbd63969b4bcb0e106e branch: master author: Victor Stinner committer: GitHub date: 2018-12-17T22:06:10+01:00 summary: bpo-35519: Rename test.bisect to test.bisect_cmd (GH-11200) Rename test.bisect module to test.bisect_cmd to avoid conflict with bisect module when running directly a test like "./python Lib/test/test_xmlrpc.py". files: A Lib/test/bisect_cmd.py A Misc/NEWS.d/next/Tests/2018-12-17-16-41-45.bpo-35519.RR3L_w.rst D Lib/test/bisect.py M Lib/test/support/__init__.py diff --git a/Lib/test/bisect.py b/Lib/test/bisect_cmd.py similarity index 100% rename from Lib/test/bisect.py rename to Lib/test/bisect_cmd.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 9ffb04de9bc1..53119e138cff 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1986,7 +1986,7 @@ def set_match_tests(patterns): patterns = () elif all(map(_is_full_match_test, patterns)): # Simple case: all patterns are full test identifier. - # The test.bisect utility only uses such full test identifiers. + # The test.bisect_cmd utility only uses such full test identifiers. func = set(patterns).__contains__ else: regex = '|'.join(map(fnmatch.translate, patterns)) diff --git a/Misc/NEWS.d/next/Tests/2018-12-17-16-41-45.bpo-35519.RR3L_w.rst b/Misc/NEWS.d/next/Tests/2018-12-17-16-41-45.bpo-35519.RR3L_w.rst new file mode 100644 index 000000000000..e108dd877e1b --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-17-16-41-45.bpo-35519.RR3L_w.rst @@ -0,0 +1,3 @@ +Rename :mod:`test.bisect` module to :mod:`test.bisect_cmd` to avoid conflict +with :mod:`bisect` module when running directly a test like +``./python Lib/test/test_xmlrpc.py``. From webhook-mailer at python.org Mon Dec 17 16:24:57 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Mon, 17 Dec 2018 21:24:57 -0000 Subject: [Python-checkins] bpo-35519: Rename test.bisect to test.bisect_cmd (GH-11200) Message-ID: https://github.com/python/cpython/commit/05dfa0cc96f6b72b1e72f57b1b5f4b37764a382d commit: 05dfa0cc96f6b72b1e72f57b1b5f4b37764a382d branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-17T13:24:52-08:00 summary: bpo-35519: Rename test.bisect to test.bisect_cmd (GH-11200) Rename test.bisect module to test.bisect_cmd to avoid conflict with bisect module when running directly a test like "./python Lib/test/test_xmlrpc.py". (cherry picked from commit 1dd035954bb03c41b954ebbd63969b4bcb0e106e) Co-authored-by: Victor Stinner files: A Lib/test/bisect_cmd.py A Misc/NEWS.d/next/Tests/2018-12-17-16-41-45.bpo-35519.RR3L_w.rst D Lib/test/bisect.py M Lib/test/support/__init__.py diff --git a/Lib/test/bisect.py b/Lib/test/bisect_cmd.py similarity index 100% rename from Lib/test/bisect.py rename to Lib/test/bisect_cmd.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index fd7fb2cc1702..25c05edad340 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1946,7 +1946,7 @@ def set_match_tests(patterns): patterns = () elif all(map(_is_full_match_test, patterns)): # Simple case: all patterns are full test identifier. - # The test.bisect utility only uses such full test identifiers. + # The test.bisect_cmd utility only uses such full test identifiers. func = set(patterns).__contains__ else: regex = '|'.join(map(fnmatch.translate, patterns)) diff --git a/Misc/NEWS.d/next/Tests/2018-12-17-16-41-45.bpo-35519.RR3L_w.rst b/Misc/NEWS.d/next/Tests/2018-12-17-16-41-45.bpo-35519.RR3L_w.rst new file mode 100644 index 000000000000..e108dd877e1b --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-17-16-41-45.bpo-35519.RR3L_w.rst @@ -0,0 +1,3 @@ +Rename :mod:`test.bisect` module to :mod:`test.bisect_cmd` to avoid conflict +with :mod:`bisect` module when running directly a test like +``./python Lib/test/test_xmlrpc.py``. From webhook-mailer at python.org Tue Dec 18 05:45:18 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 18 Dec 2018 10:45:18 -0000 Subject: [Python-checkins] bpo-31784: Use time.time_ns() in uuid.uuid1() (GH-11189) Message-ID: https://github.com/python/cpython/commit/62a68b762a479a72c3defba9ace5f72a0063c5c6 commit: 62a68b762a479a72c3defba9ace5f72a0063c5c6 branch: master author: Victor Stinner committer: GitHub date: 2018-12-18T11:45:13+01:00 summary: bpo-31784: Use time.time_ns() in uuid.uuid1() (GH-11189) uuid.uuid1() now calls time.time_ns() rather than int(time.time() * 1e9). Replace also int(nanoseconds/100) with nanoseconds // 100. Add an unit test. files: A Misc/NEWS.d/next/Library/2018-12-17-11-43-11.bpo-31784.W0gDjC.rst M Lib/test/test_uuid.py M Lib/uuid.py diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index dc502b97ca4e..757bf3cc4193 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -9,6 +9,7 @@ import shutil import subprocess import sys +from unittest import mock py_uuid = support.import_fresh_module('uuid', blocked=['_uuid']) c_uuid = support.import_fresh_module('uuid', fresh=['_uuid']) @@ -567,6 +568,23 @@ def test_uuid1_bogus_return_value(self): u = self.uuid.uuid1() self.assertEqual(u.is_safe, self.uuid.SafeUUID.unknown) + def test_uuid1_time(self): + with mock.patch.object(self.uuid, '_has_uuid_generate_time_safe', False), \ + mock.patch.object(self.uuid, '_generate_time_safe', None), \ + mock.patch.object(self.uuid, '_last_timestamp', None), \ + mock.patch.object(self.uuid, 'getnode', return_value=93328246233727), \ + mock.patch('time.time_ns', return_value=1545052026752910643), \ + mock.patch('random.getrandbits', return_value=5317): # guaranteed to be random + u = self.uuid.uuid1() + self.assertEqual(u, self.uuid.UUID('a7a55b92-01fc-11e9-94c5-54e1acf6da7f')) + + with mock.patch.object(self.uuid, '_has_uuid_generate_time_safe', False), \ + mock.patch.object(self.uuid, '_generate_time_safe', None), \ + mock.patch.object(self.uuid, '_last_timestamp', None), \ + mock.patch('time.time_ns', return_value=1545052026752910643): + u = self.uuid.uuid1(node=93328246233727, clock_seq=5317) + self.assertEqual(u, self.uuid.UUID('a7a55b92-01fc-11e9-94c5-54e1acf6da7f')) + def test_uuid3(self): equal = self.assertEqual diff --git a/Lib/uuid.py b/Lib/uuid.py index 073ca711ab42..4468d4a6c1f9 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -728,10 +728,10 @@ def uuid1(node=None, clock_seq=None): global _last_timestamp import time - nanoseconds = int(time.time() * 1e9) + nanoseconds = time.time_ns() # 0x01b21dd213814000 is the number of 100-ns intervals between the # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. - timestamp = int(nanoseconds/100) + 0x01b21dd213814000 + timestamp = nanoseconds // 100 + 0x01b21dd213814000 if _last_timestamp is not None and timestamp <= _last_timestamp: timestamp = _last_timestamp + 1 _last_timestamp = timestamp diff --git a/Misc/NEWS.d/next/Library/2018-12-17-11-43-11.bpo-31784.W0gDjC.rst b/Misc/NEWS.d/next/Library/2018-12-17-11-43-11.bpo-31784.W0gDjC.rst new file mode 100644 index 000000000000..6f0cb8ff4faf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-17-11-43-11.bpo-31784.W0gDjC.rst @@ -0,0 +1,2 @@ +:func:`uuid.uuid1` now calls :func:`time.time_ns` rather than +``int(time.time() * 1e9)``. From webhook-mailer at python.org Tue Dec 18 06:57:20 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 18 Dec 2018 11:57:20 -0000 Subject: [Python-checkins] bpo-35461: Document C API functions which suppress exceptions. (GH-11119) Message-ID: https://github.com/python/cpython/commit/3fcc1e08db6fb7e17acc4a8f63be3e42f52f094b commit: 3fcc1e08db6fb7e17acc4a8f63be3e42f52f094b branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-18T13:57:17+02:00 summary: bpo-35461: Document C API functions which suppress exceptions. (GH-11119) files: M Doc/c-api/buffer.rst M Doc/c-api/codec.rst M Doc/c-api/dict.rst M Doc/c-api/mapping.rst M Doc/c-api/number.rst M Doc/c-api/objbuffer.rst M Doc/c-api/object.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 33abb5bb94d9..c7c1e3cc745a 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -429,7 +429,7 @@ Buffer-related functions Return ``1`` if *obj* supports the buffer interface otherwise ``0``. When ``1`` is returned, it doesn't guarantee that :c:func:`PyObject_GetBuffer` will - succeed. + succeed. This function always succeeds. .. c:function:: int PyObject_GetBuffer(PyObject *exporter, Py_buffer *view, int flags) @@ -470,7 +470,7 @@ Buffer-related functions Return ``1`` if the memory defined by the *view* is C-style (*order* is ``'C'``) or Fortran-style (*order* is ``'F'``) :term:`contiguous` or either one - (*order* is ``'A'``). Return ``0`` otherwise. + (*order* is ``'A'``). Return ``0`` otherwise. This function always succeeds. .. c:function:: int PyBuffer_ToContiguous(void *buf, Py_buffer *src, Py_ssize_t len, char order) diff --git a/Doc/c-api/codec.rst b/Doc/c-api/codec.rst index dfe3d436e5f4..c55f19970e12 100644 --- a/Doc/c-api/codec.rst +++ b/Doc/c-api/codec.rst @@ -13,7 +13,7 @@ Codec registry and support functions .. c:function:: int PyCodec_KnownEncoding(const char *encoding) Return ``1`` or ``0`` depending on whether there is a registered codec for - the given *encoding*. + the given *encoding*. This function always succeeds. .. c:function:: PyObject* PyCodec_Encode(PyObject *object, const char *encoding, const char *errors) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index b7225faf408c..4e55c1a977ce 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -95,6 +95,10 @@ Dictionary Objects Return the object from dictionary *p* which has a key *key*. Return *NULL* if the key *key* is not present, but *without* setting an exception. + Note that exceptions which occur while calling :meth:`__hash__` and + :meth:`__eq__` methods will get suppressed. + To get error reporting use :c:func:`PyDict_GetItemWithError()` instead. + .. c:function:: PyObject* PyDict_GetItemWithError(PyObject *p, PyObject *key) @@ -109,6 +113,11 @@ Dictionary Objects This is the same as :c:func:`PyDict_GetItem`, but *key* is specified as a :c:type:`const char\*`, rather than a :c:type:`PyObject\*`. + Note that exceptions which occur while calling :meth:`__hash__` and + :meth:`__eq__` methods and creating a temporary string object + will get suppressed. + To get error reporting use :c:func:`PyDict_GetItemWithError()` instead. + .. c:function:: PyObject* PyDict_SetDefault(PyObject *p, PyObject *key, PyObject *default) diff --git a/Doc/c-api/mapping.rst b/Doc/c-api/mapping.rst index b8eaadbd702c..e37dec9949ab 100644 --- a/Doc/c-api/mapping.rst +++ b/Doc/c-api/mapping.rst @@ -60,6 +60,10 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and This is equivalent to the Python expression ``key in o``. This function always succeeds. + Note that exceptions which occur while calling the :meth:`__getitem__` + method will get suppressed. + To get error reporting use :c:func:`PyObject_GetItem()` instead. + .. c:function:: int PyMapping_HasKeyString(PyObject *o, const char *key) @@ -67,6 +71,10 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and This is equivalent to the Python expression ``key in o``. This function always succeeds. + Note that exceptions which occur while calling the :meth:`__getitem__` + method and creating a temporary string object will get suppressed. + To get error reporting use :c:func:`PyMapping_GetItemString()` instead. + .. c:function:: PyObject* PyMapping_Keys(PyObject *o) diff --git a/Doc/c-api/number.rst b/Doc/c-api/number.rst index 3c7605a67fa2..296b21c132b7 100644 --- a/Doc/c-api/number.rst +++ b/Doc/c-api/number.rst @@ -280,3 +280,4 @@ Number Protocol Returns ``1`` if *o* is an index integer (has the nb_index slot of the tp_as_number structure filled in), and ``0`` otherwise. + This function always succeeds. diff --git a/Doc/c-api/objbuffer.rst b/Doc/c-api/objbuffer.rst index e7f4fde00256..9ad7c571c4d8 100644 --- a/Doc/c-api/objbuffer.rst +++ b/Doc/c-api/objbuffer.rst @@ -39,7 +39,11 @@ an object, and :c:func:`PyBuffer_Release` when the buffer view can be released. .. c:function:: int PyObject_CheckReadBuffer(PyObject *o) Returns ``1`` if *o* supports the single-segment readable buffer interface. - Otherwise returns ``0``. + Otherwise returns ``0``. This function always succeeds. + + Note that this function tries to get and release a buffer, and exceptions + which occur while calling correspoding functions will get suppressed. + To get error reporting use :c:func:`PyObject_GetBuffer()` instead. .. c:function:: int PyObject_AsWriteBuffer(PyObject *obj, void **buffer, Py_ssize_t *buffer_len) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index f0b2005f2d12..a64ff2e6b58b 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -33,6 +33,10 @@ Object Protocol is equivalent to the Python expression ``hasattr(o, attr_name)``. This function always succeeds. + Note that exceptions which occur while calling :meth:`__getattr__` and + :meth:`__getattribute__` methods will get suppressed. + To get error reporting use :c:func:`PyObject_GetAttr()` instead. + .. c:function:: int PyObject_HasAttrString(PyObject *o, const char *attr_name) @@ -40,6 +44,11 @@ Object Protocol is equivalent to the Python expression ``hasattr(o, attr_name)``. This function always succeeds. + Note that exceptions which occur while calling :meth:`__getattr__` and + :meth:`__getattribute__` methods and creating a temporary string object + will get suppressed. + To get error reporting use :c:func:`PyObject_GetAttrString()` instead. + .. c:function:: PyObject* PyObject_GetAttr(PyObject *o, PyObject *attr_name) From webhook-mailer at python.org Tue Dec 18 08:47:31 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 18 Dec 2018 13:47:31 -0000 Subject: [Python-checkins] bpo-35523: Remove ctypes callback workaround (GH-11211) Message-ID: https://github.com/python/cpython/commit/e6b247c8e524dbe5fc03b3492f628d0d5348bc49 commit: e6b247c8e524dbe5fc03b3492f628d0d5348bc49 branch: master author: Victor Stinner committer: GitHub date: 2018-12-18T14:47:21+01:00 summary: bpo-35523: Remove ctypes callback workaround (GH-11211) Remove ctypes callback workaround: no longer create a callback at startup. Avoid SELinux alert on "import ctypes" and "import uuid". files: A Misc/NEWS.d/next/Library/2018-12-18-13-52-13.bpo-35523.SkoMno.rst M Lib/ctypes/__init__.py diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 614677398864..5f78beda5866 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -266,11 +266,6 @@ def _reset_cache(): # _SimpleCData.c_char_p_from_param POINTER(c_char).from_param = c_char_p.from_param _pointer_type_cache[None] = c_void_p - # XXX for whatever reasons, creating the first instance of a callback - # function is needed for the unittests on Win64 to succeed. This MAY - # be a compiler bug, since the problem occurs only when _ctypes is - # compiled with the MS SDK compiler. Or an uninitialized variable? - CFUNCTYPE(c_int)(lambda: None) def create_unicode_buffer(init, size=None): """create_unicode_buffer(aString) -> character array diff --git a/Misc/NEWS.d/next/Library/2018-12-18-13-52-13.bpo-35523.SkoMno.rst b/Misc/NEWS.d/next/Library/2018-12-18-13-52-13.bpo-35523.SkoMno.rst new file mode 100644 index 000000000000..94a9fd257383 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-18-13-52-13.bpo-35523.SkoMno.rst @@ -0,0 +1,2 @@ +Remove :mod:`ctypes` callback workaround: no longer create a callback at +startup. Avoid SELinux alert on ``import ctypes`` and ``import uuid``. From webhook-mailer at python.org Tue Dec 18 10:18:10 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 18 Dec 2018 15:18:10 -0000 Subject: [Python-checkins] bpo-10496: distutils check_environ() handles getpwuid() error (GH-10931) Message-ID: https://github.com/python/cpython/commit/17d0c0595e101c4ce76b58e55de37e6b5083e6cd commit: 17d0c0595e101c4ce76b58e55de37e6b5083e6cd branch: master author: Victor Stinner committer: GitHub date: 2018-12-18T16:17:56+01:00 summary: bpo-10496: distutils check_environ() handles getpwuid() error (GH-10931) check_environ() of distutils.utils now catchs KeyError on calling pwd.getpwuid(): don't create the HOME environment variable in this case. files: A Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst M Lib/distutils/tests/test_util.py M Lib/distutils/util.py diff --git a/Lib/distutils/tests/test_util.py b/Lib/distutils/tests/test_util.py index e2fc3809587f..bf0d4333f9ae 100644 --- a/Lib/distutils/tests/test_util.py +++ b/Lib/distutils/tests/test_util.py @@ -4,6 +4,7 @@ import unittest from copy import copy from test.support import run_unittest +from unittest import mock from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, @@ -234,20 +235,35 @@ def _join(*path): def test_check_environ(self): util._environ_checked = 0 - if 'HOME' in os.environ: - del os.environ['HOME'] + os.environ.pop('HOME', None) - # posix without HOME - if os.name == 'posix': # this test won't run on windows - check_environ() - import pwd - self.assertEqual(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) - else: - check_environ() + check_environ() self.assertEqual(os.environ['PLAT'], get_platform()) self.assertEqual(util._environ_checked, 1) + @unittest.skipUnless(os.name == 'posix', 'specific to posix') + def test_check_environ_getpwuid(self): + util._environ_checked = 0 + os.environ.pop('HOME', None) + + import pwd + + # only set pw_dir field, other fields are not used + result = pwd.struct_passwd((None, None, None, None, None, + '/home/distutils', None)) + with mock.patch.object(pwd, 'getpwuid', return_value=result): + check_environ() + self.assertEqual(os.environ['HOME'], '/home/distutils') + + util._environ_checked = 0 + os.environ.pop('HOME', None) + + # bpo-10496: Catch pwd.getpwuid() error + with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError): + check_environ() + self.assertNotIn('HOME', os.environ) + def test_split_quoted(self): self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), ['one', 'two', 'three', 'four']) diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py index 83682628ba68..30a21e4afa1f 100644 --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -157,8 +157,13 @@ def check_environ (): return if os.name == 'posix' and 'HOME' not in os.environ: - import pwd - os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + try: + import pwd + os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + except (ImportError, KeyError): + # bpo-10496: if the current user identifier doesn't exist in the + # password database, do nothing + pass if 'PLAT' not in os.environ: os.environ['PLAT'] = get_platform() diff --git a/Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst b/Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst new file mode 100644 index 000000000000..cbfe5eb11668 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst @@ -0,0 +1,3 @@ +:func:`~distutils.utils.check_environ` of :mod:`distutils.utils` now catchs +:exc:`KeyError` on calling :func:`pwd.getpwuid`: don't create the ``HOME`` +environment variable in this case. From webhook-mailer at python.org Tue Dec 18 10:34:58 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 18 Dec 2018 15:34:58 -0000 Subject: [Python-checkins] bpo-10496: distutils check_environ() handles getpwuid() error (GH-10931) Message-ID: https://github.com/python/cpython/commit/6e96fb44f12c2e9d7ab0d14a21f2aa85ecaa2f83 commit: 6e96fb44f12c2e9d7ab0d14a21f2aa85ecaa2f83 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-18T07:34:54-08:00 summary: bpo-10496: distutils check_environ() handles getpwuid() error (GH-10931) check_environ() of distutils.utils now catchs KeyError on calling pwd.getpwuid(): don't create the HOME environment variable in this case. (cherry picked from commit 17d0c0595e101c4ce76b58e55de37e6b5083e6cd) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst M Lib/distutils/tests/test_util.py M Lib/distutils/util.py diff --git a/Lib/distutils/tests/test_util.py b/Lib/distutils/tests/test_util.py index e2fc3809587f..bf0d4333f9ae 100644 --- a/Lib/distutils/tests/test_util.py +++ b/Lib/distutils/tests/test_util.py @@ -4,6 +4,7 @@ import unittest from copy import copy from test.support import run_unittest +from unittest import mock from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, @@ -234,20 +235,35 @@ def _join(*path): def test_check_environ(self): util._environ_checked = 0 - if 'HOME' in os.environ: - del os.environ['HOME'] + os.environ.pop('HOME', None) - # posix without HOME - if os.name == 'posix': # this test won't run on windows - check_environ() - import pwd - self.assertEqual(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) - else: - check_environ() + check_environ() self.assertEqual(os.environ['PLAT'], get_platform()) self.assertEqual(util._environ_checked, 1) + @unittest.skipUnless(os.name == 'posix', 'specific to posix') + def test_check_environ_getpwuid(self): + util._environ_checked = 0 + os.environ.pop('HOME', None) + + import pwd + + # only set pw_dir field, other fields are not used + result = pwd.struct_passwd((None, None, None, None, None, + '/home/distutils', None)) + with mock.patch.object(pwd, 'getpwuid', return_value=result): + check_environ() + self.assertEqual(os.environ['HOME'], '/home/distutils') + + util._environ_checked = 0 + os.environ.pop('HOME', None) + + # bpo-10496: Catch pwd.getpwuid() error + with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError): + check_environ() + self.assertNotIn('HOME', os.environ) + def test_split_quoted(self): self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), ['one', 'two', 'three', 'four']) diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py index 83682628ba68..30a21e4afa1f 100644 --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -157,8 +157,13 @@ def check_environ (): return if os.name == 'posix' and 'HOME' not in os.environ: - import pwd - os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + try: + import pwd + os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + except (ImportError, KeyError): + # bpo-10496: if the current user identifier doesn't exist in the + # password database, do nothing + pass if 'PLAT' not in os.environ: os.environ['PLAT'] = get_platform() diff --git a/Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst b/Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst new file mode 100644 index 000000000000..cbfe5eb11668 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst @@ -0,0 +1,3 @@ +:func:`~distutils.utils.check_environ` of :mod:`distutils.utils` now catchs +:exc:`KeyError` on calling :func:`pwd.getpwuid`: don't create the ``HOME`` +environment variable in this case. From webhook-mailer at python.org Tue Dec 18 11:35:01 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 18 Dec 2018 16:35:01 -0000 Subject: [Python-checkins] bpo-10496: distutils check_environ() handles getpwuid() error (GH-10931) (GH-11213) Message-ID: https://github.com/python/cpython/commit/ea6b322829c62951362f267d7afdd262aa2b3e2c commit: ea6b322829c62951362f267d7afdd262aa2b3e2c branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-12-18T17:34:51+01:00 summary: bpo-10496: distutils check_environ() handles getpwuid() error (GH-10931) (GH-11213) check_environ() of distutils.utils now catchs KeyError on calling pwd.getpwuid(): don't create the HOME environment variable in this case. (cherry picked from commit 17d0c0595e101c4ce76b58e55de37e6b5083e6cd) files: A Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst M Lib/distutils/tests/test_util.py M Lib/distutils/util.py diff --git a/Lib/distutils/tests/test_util.py b/Lib/distutils/tests/test_util.py index 7898e07b2517..e0817097296d 100644 --- a/Lib/distutils/tests/test_util.py +++ b/Lib/distutils/tests/test_util.py @@ -1,11 +1,14 @@ """Tests for distutils.util.""" +import os import sys import unittest -from test.test_support import run_unittest +from test.test_support import run_unittest, swap_attr from distutils.errors import DistutilsByteCompileError from distutils.tests import support -from distutils.util import byte_compile, grok_environment_error +from distutils import util # used to patch _environ_checked +from distutils.util import (byte_compile, grok_environment_error, + check_environ, get_platform) class UtilTestCase(support.EnvironGuard, unittest.TestCase): @@ -26,6 +29,41 @@ def test_grok_environment_error(self): msg = grok_environment_error(exc) self.assertEqual(msg, "error: Unable to find batch file") + def test_check_environ(self): + util._environ_checked = 0 + os.environ.pop('HOME', None) + + check_environ() + + self.assertEqual(os.environ['PLAT'], get_platform()) + self.assertEqual(util._environ_checked, 1) + + @unittest.skipUnless(os.name == 'posix', 'specific to posix') + def test_check_environ_getpwuid(self): + util._environ_checked = 0 + os.environ.pop('HOME', None) + + import pwd + + # only set pw_dir field, other fields are not used + def mock_getpwuid(uid): + return pwd.struct_passwd((None, None, None, None, None, + '/home/distutils', None)) + + with swap_attr(pwd, 'getpwuid', mock_getpwuid): + check_environ() + self.assertEqual(os.environ['HOME'], '/home/distutils') + + util._environ_checked = 0 + os.environ.pop('HOME', None) + + # bpo-10496: Catch pwd.getpwuid() error + def getpwuid_err(uid): + raise KeyError + with swap_attr(pwd, 'getpwuid', getpwuid_err): + check_environ() + self.assertNotIn('HOME', os.environ) + def test_suite(): return unittest.makeSuite(UtilTestCase) diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py index 2b4d7849bdcc..5a06b8597c6e 100644 --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -178,8 +178,13 @@ def check_environ (): return if os.name == 'posix' and 'HOME' not in os.environ: - import pwd - os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + try: + import pwd + os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + except (ImportError, KeyError): + # bpo-10496: if the current user identifier doesn't exist in the + # password database, do nothing + pass if 'PLAT' not in os.environ: os.environ['PLAT'] = get_platform() diff --git a/Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst b/Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst new file mode 100644 index 000000000000..cbfe5eb11668 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-05-17-42-49.bpo-10496.laV_IE.rst @@ -0,0 +1,3 @@ +:func:`~distutils.utils.check_environ` of :mod:`distutils.utils` now catchs +:exc:`KeyError` on calling :func:`pwd.getpwuid`: don't create the ``HOME`` +environment variable in this case. From webhook-mailer at python.org Tue Dec 18 13:51:43 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 18 Dec 2018 18:51:43 -0000 Subject: [Python-checkins] bpo-35516: platform.system_alias() don't replace Darwin (GH-11207) Message-ID: https://github.com/python/cpython/commit/60875db2f67815d7d181c552bfac59e8c97619e3 commit: 60875db2f67815d7d181c552bfac59e8c97619e3 branch: master author: Victor Stinner committer: GitHub date: 2018-12-18T19:51:35+01:00 summary: bpo-35516: platform.system_alias() don't replace Darwin (GH-11207) Add a comment explaining why system_alias() doesn't alias Darwin to macOS. files: M Lib/platform.py diff --git a/Lib/platform.py b/Lib/platform.py index 9dd3f47075bf..2ab68aed7861 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -514,6 +514,9 @@ def system_alias(system, release, version): # In case one of the other tricks system = 'Windows' + # bpo-35516: Don't replace Darwin with macOS since input release and + # version arguments can be different than the currently running version. + return system, release, version ### Various internal helpers From webhook-mailer at python.org Tue Dec 18 15:24:44 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 18 Dec 2018 20:24:44 -0000 Subject: [Python-checkins] bpo-35461: Document C API functions which suppress exceptions. (GH-11119) Message-ID: https://github.com/python/cpython/commit/f265afec1c2a5acb8cb9c9ddb6cd45f7465c6eb5 commit: f265afec1c2a5acb8cb9c9ddb6cd45f7465c6eb5 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-18T12:24:39-08:00 summary: bpo-35461: Document C API functions which suppress exceptions. (GH-11119) (cherry picked from commit 3fcc1e08db6fb7e17acc4a8f63be3e42f52f094b) Co-authored-by: Serhiy Storchaka files: M Doc/c-api/buffer.rst M Doc/c-api/codec.rst M Doc/c-api/dict.rst M Doc/c-api/mapping.rst M Doc/c-api/number.rst M Doc/c-api/objbuffer.rst M Doc/c-api/object.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 33abb5bb94d9..c7c1e3cc745a 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -429,7 +429,7 @@ Buffer-related functions Return ``1`` if *obj* supports the buffer interface otherwise ``0``. When ``1`` is returned, it doesn't guarantee that :c:func:`PyObject_GetBuffer` will - succeed. + succeed. This function always succeeds. .. c:function:: int PyObject_GetBuffer(PyObject *exporter, Py_buffer *view, int flags) @@ -470,7 +470,7 @@ Buffer-related functions Return ``1`` if the memory defined by the *view* is C-style (*order* is ``'C'``) or Fortran-style (*order* is ``'F'``) :term:`contiguous` or either one - (*order* is ``'A'``). Return ``0`` otherwise. + (*order* is ``'A'``). Return ``0`` otherwise. This function always succeeds. .. c:function:: int PyBuffer_ToContiguous(void *buf, Py_buffer *src, Py_ssize_t len, char order) diff --git a/Doc/c-api/codec.rst b/Doc/c-api/codec.rst index dfe3d436e5f4..c55f19970e12 100644 --- a/Doc/c-api/codec.rst +++ b/Doc/c-api/codec.rst @@ -13,7 +13,7 @@ Codec registry and support functions .. c:function:: int PyCodec_KnownEncoding(const char *encoding) Return ``1`` or ``0`` depending on whether there is a registered codec for - the given *encoding*. + the given *encoding*. This function always succeeds. .. c:function:: PyObject* PyCodec_Encode(PyObject *object, const char *encoding, const char *errors) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index b7225faf408c..4e55c1a977ce 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -95,6 +95,10 @@ Dictionary Objects Return the object from dictionary *p* which has a key *key*. Return *NULL* if the key *key* is not present, but *without* setting an exception. + Note that exceptions which occur while calling :meth:`__hash__` and + :meth:`__eq__` methods will get suppressed. + To get error reporting use :c:func:`PyDict_GetItemWithError()` instead. + .. c:function:: PyObject* PyDict_GetItemWithError(PyObject *p, PyObject *key) @@ -109,6 +113,11 @@ Dictionary Objects This is the same as :c:func:`PyDict_GetItem`, but *key* is specified as a :c:type:`const char\*`, rather than a :c:type:`PyObject\*`. + Note that exceptions which occur while calling :meth:`__hash__` and + :meth:`__eq__` methods and creating a temporary string object + will get suppressed. + To get error reporting use :c:func:`PyDict_GetItemWithError()` instead. + .. c:function:: PyObject* PyDict_SetDefault(PyObject *p, PyObject *key, PyObject *default) diff --git a/Doc/c-api/mapping.rst b/Doc/c-api/mapping.rst index b8eaadbd702c..e37dec9949ab 100644 --- a/Doc/c-api/mapping.rst +++ b/Doc/c-api/mapping.rst @@ -60,6 +60,10 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and This is equivalent to the Python expression ``key in o``. This function always succeeds. + Note that exceptions which occur while calling the :meth:`__getitem__` + method will get suppressed. + To get error reporting use :c:func:`PyObject_GetItem()` instead. + .. c:function:: int PyMapping_HasKeyString(PyObject *o, const char *key) @@ -67,6 +71,10 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and This is equivalent to the Python expression ``key in o``. This function always succeeds. + Note that exceptions which occur while calling the :meth:`__getitem__` + method and creating a temporary string object will get suppressed. + To get error reporting use :c:func:`PyMapping_GetItemString()` instead. + .. c:function:: PyObject* PyMapping_Keys(PyObject *o) diff --git a/Doc/c-api/number.rst b/Doc/c-api/number.rst index 3c7605a67fa2..296b21c132b7 100644 --- a/Doc/c-api/number.rst +++ b/Doc/c-api/number.rst @@ -280,3 +280,4 @@ Number Protocol Returns ``1`` if *o* is an index integer (has the nb_index slot of the tp_as_number structure filled in), and ``0`` otherwise. + This function always succeeds. diff --git a/Doc/c-api/objbuffer.rst b/Doc/c-api/objbuffer.rst index e7f4fde00256..9ad7c571c4d8 100644 --- a/Doc/c-api/objbuffer.rst +++ b/Doc/c-api/objbuffer.rst @@ -39,7 +39,11 @@ an object, and :c:func:`PyBuffer_Release` when the buffer view can be released. .. c:function:: int PyObject_CheckReadBuffer(PyObject *o) Returns ``1`` if *o* supports the single-segment readable buffer interface. - Otherwise returns ``0``. + Otherwise returns ``0``. This function always succeeds. + + Note that this function tries to get and release a buffer, and exceptions + which occur while calling correspoding functions will get suppressed. + To get error reporting use :c:func:`PyObject_GetBuffer()` instead. .. c:function:: int PyObject_AsWriteBuffer(PyObject *obj, void **buffer, Py_ssize_t *buffer_len) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index f0b2005f2d12..a64ff2e6b58b 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -33,6 +33,10 @@ Object Protocol is equivalent to the Python expression ``hasattr(o, attr_name)``. This function always succeeds. + Note that exceptions which occur while calling :meth:`__getattr__` and + :meth:`__getattribute__` methods will get suppressed. + To get error reporting use :c:func:`PyObject_GetAttr()` instead. + .. c:function:: int PyObject_HasAttrString(PyObject *o, const char *attr_name) @@ -40,6 +44,11 @@ Object Protocol is equivalent to the Python expression ``hasattr(o, attr_name)``. This function always succeeds. + Note that exceptions which occur while calling :meth:`__getattr__` and + :meth:`__getattribute__` methods and creating a temporary string object + will get suppressed. + To get error reporting use :c:func:`PyObject_GetAttrString()` instead. + .. c:function:: PyObject* PyObject_GetAttr(PyObject *o, PyObject *attr_name) From webhook-mailer at python.org Tue Dec 18 15:29:18 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Tue, 18 Dec 2018 20:29:18 -0000 Subject: [Python-checkins] bpo-35502: Fix reference leaks in ElementTree.TreeBuilder. (GH-11170) Message-ID: https://github.com/python/cpython/commit/d2a75c67830d7c9f59e4e9b60f36974234c829ef commit: d2a75c67830d7c9f59e4e9b60f36974234c829ef branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-18T22:29:14+02:00 summary: bpo-35502: Fix reference leaks in ElementTree.TreeBuilder. (GH-11170) files: A Misc/NEWS.d/next/Library/2018-12-14-23-56-48.bpo-35502.gLHuFS.rst M Lib/test/test_xml_etree_c.py M Modules/_elementtree.c diff --git a/Lib/test/test_xml_etree_c.py b/Lib/test/test_xml_etree_c.py index e87de6094441..2144d203e1e9 100644 --- a/Lib/test/test_xml_etree_c.py +++ b/Lib/test/test_xml_etree_c.py @@ -1,4 +1,5 @@ # xml.etree test for cElementTree +import io import struct from test import support from test.support import import_fresh_module @@ -133,6 +134,26 @@ def test_setstate_leaks(self): self.assertEqual(len(elem), 1) self.assertEqual(elem[0].tag, 'child') + def test_iterparse_leaks(self): + # Test reference leaks in TreeBuilder (issue #35502). + # The test is written to be executed in the hunting reference leaks + # mode. + XML = '' + parser = cET.iterparse(io.StringIO(XML)) + next(parser) + del parser + support.gc_collect() + + def test_xmlpullparser_leaks(self): + # Test reference leaks in TreeBuilder (issue #35502). + # The test is written to be executed in the hunting reference leaks + # mode. + XML = '' + parser = cET.XMLPullParser() + parser.feed(XML) + del parser + support.gc_collect() + @unittest.skipUnless(cET, 'requires _elementtree') class TestAliasWorking(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2018-12-14-23-56-48.bpo-35502.gLHuFS.rst b/Misc/NEWS.d/next/Library/2018-12-14-23-56-48.bpo-35502.gLHuFS.rst new file mode 100644 index 000000000000..0fcea8d5a41d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-14-23-56-48.bpo-35502.gLHuFS.rst @@ -0,0 +1,3 @@ +Fixed reference leaks in :class:`xml.etree.ElementTree.TreeBuilder` in case +of unfinished building of the tree (in particular when an error was raised +during parsing XML). diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 12e418d85ed5..b1a96299f24f 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2447,6 +2447,11 @@ _elementtree_TreeBuilder___init___impl(TreeBuilderObject *self, static int treebuilder_gc_traverse(TreeBuilderObject *self, visitproc visit, void *arg) { + Py_VISIT(self->end_ns_event_obj); + Py_VISIT(self->start_ns_event_obj); + Py_VISIT(self->end_event_obj); + Py_VISIT(self->start_event_obj); + Py_VISIT(self->events_append); Py_VISIT(self->root); Py_VISIT(self->this); Py_VISIT(self->last); From webhook-mailer at python.org Tue Dec 18 16:31:38 2018 From: webhook-mailer at python.org (Yury Selivanov) Date: Tue, 18 Dec 2018 21:31:38 -0000 Subject: [Python-checkins] bpo-35465: Document _UnixSelectorEventLoop.add_signal_handler. (GH-11145) Message-ID: https://github.com/python/cpython/commit/e3666fc8effb05b555121f4ab7388df59e21f8b4 commit: e3666fc8effb05b555121f4ab7388df59e21f8b4 branch: master author: Hrvoje Nik?i? committer: Yury Selivanov date: 2018-12-18T16:31:29-05:00 summary: bpo-35465: Document _UnixSelectorEventLoop.add_signal_handler. (GH-11145) files: M Doc/library/asyncio-eventloop.rst diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index da2339132a40..acf9477f723d 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -966,12 +966,20 @@ Unix signals Set *callback* as the handler for the *signum* signal. + The callback will be invoked by *loop*, along with other queued callbacks + and runnable coroutines of that event loop. Unlike signal handlers + registered using :func:`signal.signal`, a callback registered with this + function is allowed to interact with the event loop. + Raise :exc:`ValueError` if the signal number is invalid or uncatchable. Raise :exc:`RuntimeError` if there is a problem setting up the handler. Use :func:`functools.partial` :ref:`to pass keyword arguments ` to *callback*. + Like :func:`signal.signal`, this function must be invoked in the main + thread. + .. method:: loop.remove_signal_handler(sig) Remove the handler for the *sig* signal. From webhook-mailer at python.org Tue Dec 18 16:40:29 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 18 Dec 2018 21:40:29 -0000 Subject: [Python-checkins] bpo-35502: Fix reference leaks in ElementTree.TreeBuilder. (GH-11170) Message-ID: https://github.com/python/cpython/commit/60c919b58bd3cf8730947a00ddc6a527d6922ff1 commit: 60c919b58bd3cf8730947a00ddc6a527d6922ff1 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-18T13:40:23-08:00 summary: bpo-35502: Fix reference leaks in ElementTree.TreeBuilder. (GH-11170) (cherry picked from commit d2a75c67830d7c9f59e4e9b60f36974234c829ef) Co-authored-by: Serhiy Storchaka files: A Misc/NEWS.d/next/Library/2018-12-14-23-56-48.bpo-35502.gLHuFS.rst M Lib/test/test_xml_etree_c.py M Modules/_elementtree.c diff --git a/Lib/test/test_xml_etree_c.py b/Lib/test/test_xml_etree_c.py index e87de6094441..2144d203e1e9 100644 --- a/Lib/test/test_xml_etree_c.py +++ b/Lib/test/test_xml_etree_c.py @@ -1,4 +1,5 @@ # xml.etree test for cElementTree +import io import struct from test import support from test.support import import_fresh_module @@ -133,6 +134,26 @@ def test_setstate_leaks(self): self.assertEqual(len(elem), 1) self.assertEqual(elem[0].tag, 'child') + def test_iterparse_leaks(self): + # Test reference leaks in TreeBuilder (issue #35502). + # The test is written to be executed in the hunting reference leaks + # mode. + XML = '' + parser = cET.iterparse(io.StringIO(XML)) + next(parser) + del parser + support.gc_collect() + + def test_xmlpullparser_leaks(self): + # Test reference leaks in TreeBuilder (issue #35502). + # The test is written to be executed in the hunting reference leaks + # mode. + XML = '' + parser = cET.XMLPullParser() + parser.feed(XML) + del parser + support.gc_collect() + @unittest.skipUnless(cET, 'requires _elementtree') class TestAliasWorking(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2018-12-14-23-56-48.bpo-35502.gLHuFS.rst b/Misc/NEWS.d/next/Library/2018-12-14-23-56-48.bpo-35502.gLHuFS.rst new file mode 100644 index 000000000000..0fcea8d5a41d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-14-23-56-48.bpo-35502.gLHuFS.rst @@ -0,0 +1,3 @@ +Fixed reference leaks in :class:`xml.etree.ElementTree.TreeBuilder` in case +of unfinished building of the tree (in particular when an error was raised +during parsing XML). diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index ff3a240f2ec3..79f1ccd68565 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2429,6 +2429,11 @@ _elementtree_TreeBuilder___init___impl(TreeBuilderObject *self, static int treebuilder_gc_traverse(TreeBuilderObject *self, visitproc visit, void *arg) { + Py_VISIT(self->end_ns_event_obj); + Py_VISIT(self->start_ns_event_obj); + Py_VISIT(self->end_event_obj); + Py_VISIT(self->start_event_obj); + Py_VISIT(self->events_append); Py_VISIT(self->root); Py_VISIT(self->this); Py_VISIT(self->last); From webhook-mailer at python.org Tue Dec 18 16:52:41 2018 From: webhook-mailer at python.org (Yury Selivanov) Date: Tue, 18 Dec 2018 21:52:41 -0000 Subject: [Python-checkins] bpo-35465: Document _UnixSelectorEventLoop.add_signal_handler. (GH-11145) (GH-11221) Message-ID: https://github.com/python/cpython/commit/12f3979b3807b448ca070b44bbc1597cf800f8a4 commit: 12f3979b3807b448ca070b44bbc1597cf800f8a4 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Yury Selivanov date: 2018-12-18T16:52:37-05:00 summary: bpo-35465: Document _UnixSelectorEventLoop.add_signal_handler. (GH-11145) (GH-11221) (cherry picked from commit e3666fc8effb05b555121f4ab7388df59e21f8b4) Co-authored-by: Hrvoje Nik?i? files: M Doc/library/asyncio-eventloop.rst diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 647b7fc5e7a5..d59cf055b614 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -960,12 +960,20 @@ Unix signals Set *callback* as the handler for the *signum* signal. + The callback will be invoked by *loop*, along with other queued callbacks + and runnable coroutines of that event loop. Unlike signal handlers + registered using :func:`signal.signal`, a callback registered with this + function is allowed to interact with the event loop. + Raise :exc:`ValueError` if the signal number is invalid or uncatchable. Raise :exc:`RuntimeError` if there is a problem setting up the handler. Use :func:`functools.partial` :ref:`to pass keyword arguments ` to *callback*. + Like :func:`signal.signal`, this function must be invoked in the main + thread. + .. method:: loop.remove_signal_handler(sig) Remove the handler for the *sig* signal. From webhook-mailer at python.org Tue Dec 18 16:56:20 2018 From: webhook-mailer at python.org (Andrew Svetlov) Date: Tue, 18 Dec 2018 21:56:20 -0000 Subject: [Python-checkins] bpo-23057: add loop self socket as wakeup fd for signals (#11135) Message-ID: https://github.com/python/cpython/commit/b5c8cfa1da17c6f3acac80a0afca7f7104fb9589 commit: b5c8cfa1da17c6f3acac80a0afca7f7104fb9589 branch: master author: Vladimir Matveev committer: Andrew Svetlov date: 2018-12-18T23:56:17+02:00 summary: bpo-23057: add loop self socket as wakeup fd for signals (#11135) files: A Lib/test/test_asyncio/test_ctrl_c_in_proactor_loop_helper.py A Misc/NEWS.d/next/Library/2018-12-12-16-24-55.bpo-23057.OB4Z1Y.rst M Lib/asyncio/proactor_events.py M Lib/asyncio/windows_events.py M Lib/test/test_asyncio/test_proactor_events.py M Lib/test/test_asyncio/test_windows_events.py diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 69d96a81035e..9b9e0aa7c7f1 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -10,6 +10,7 @@ import os import socket import warnings +import signal from . import base_events from . import constants @@ -489,6 +490,8 @@ def __init__(self, proactor): self._accept_futures = {} # socket file descriptor => Future proactor.set_loop(self) self._make_self_pipe() + self_no = self._csock.fileno() + signal.set_wakeup_fd(self_no) def _make_socket_transport(self, sock, protocol, waiter=None, extra=None, server=None): @@ -529,6 +532,7 @@ def close(self): if self.is_closed(): return + signal.set_wakeup_fd(-1) # Call these methods before closing the event loop (before calling # BaseEventLoop.close), because they can schedule callbacks with # call_soon(), which is forbidden when the event loop is closed. @@ -613,7 +617,6 @@ def _make_self_pipe(self): self._ssock.setblocking(False) self._csock.setblocking(False) self._internal_fds += 1 - self.call_soon(self._loop_self_reading) def _loop_self_reading(self, f=None): try: diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index 772ddf4dfebd..33ffaf971770 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -308,6 +308,16 @@ def __init__(self, proactor=None): proactor = IocpProactor() super().__init__(proactor) + def run_forever(self): + try: + assert self._self_reading_future is None + self.call_soon(self._loop_self_reading) + super().run_forever() + finally: + if self._self_reading_future is not None: + self._self_reading_future.cancel() + self._self_reading_future = None + async def create_pipe_connection(self, protocol_factory, address): f = self._proactor.connect_pipe(address) pipe = await f diff --git a/Lib/test/test_asyncio/test_ctrl_c_in_proactor_loop_helper.py b/Lib/test/test_asyncio/test_ctrl_c_in_proactor_loop_helper.py new file mode 100644 index 000000000000..9aeb58aae212 --- /dev/null +++ b/Lib/test/test_asyncio/test_ctrl_c_in_proactor_loop_helper.py @@ -0,0 +1,63 @@ +import sys + + +def do_in_child_process(): + import asyncio + + asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) + l = asyncio.get_event_loop() + + def step(n): + try: + print(n) + sys.stdout.flush() + l.run_forever() + sys.exit(100) + except KeyboardInterrupt: + # ok + pass + except: + # error - use default exit code + sys.exit(200) + + step(1) + step(2) + sys.exit(255) + + +def do_in_main_process(): + import os + import signal + import subprocess + import time + from test.support.script_helper import spawn_python + + ok = False + + def step(p, expected): + s = p.stdout.readline() + if s != expected: + raise Exception(f"Unexpected line: got {s}, expected '{expected}'") + # ensure that child process gets to run_forever + time.sleep(0.5) + os.kill(p.pid, signal.CTRL_C_EVENT) + + with spawn_python(__file__, "--child") as p: + try: + # ignore ctrl-c in current process + signal.signal(signal.SIGINT, signal.SIG_IGN) + step(p, b"1\r\n") + step(p, b"2\r\n") + exit_code = p.wait(timeout=5) + ok = exit_code = 255 + except Exception as e: + sys.stderr.write(repr(e)) + p.kill() + sys.exit(255 if ok else 1) + + +if __name__ == "__main__": + if len(sys.argv) == 1: + do_in_main_process() + else: + do_in_child_process() diff --git a/Lib/test/test_asyncio/test_proactor_events.py b/Lib/test/test_asyncio/test_proactor_events.py index afc4c19a96b0..5952ccccce0e 100644 --- a/Lib/test/test_asyncio/test_proactor_events.py +++ b/Lib/test/test_asyncio/test_proactor_events.py @@ -737,19 +737,19 @@ def setUp(self): with mock.patch('asyncio.proactor_events.socket.socketpair', return_value=(self.ssock, self.csock)): - self.loop = BaseProactorEventLoop(self.proactor) + with mock.patch('signal.set_wakeup_fd'): + self.loop = BaseProactorEventLoop(self.proactor) self.set_event_loop(self.loop) - @mock.patch.object(BaseProactorEventLoop, 'call_soon') @mock.patch('asyncio.proactor_events.socket.socketpair') - def test_ctor(self, socketpair, call_soon): + def test_ctor(self, socketpair): ssock, csock = socketpair.return_value = ( mock.Mock(), mock.Mock()) - loop = BaseProactorEventLoop(self.proactor) + with mock.patch('signal.set_wakeup_fd'): + loop = BaseProactorEventLoop(self.proactor) self.assertIs(loop._ssock, ssock) self.assertIs(loop._csock, csock) self.assertEqual(loop._internal_fds, 1) - call_soon.assert_called_with(loop._loop_self_reading) loop.close() def test_close_self_pipe(self): diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 8f4c50e2c92b..05d875ac3c85 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -1,6 +1,9 @@ import os +import signal import socket import sys +import subprocess +import time import unittest from unittest import mock @@ -13,6 +16,7 @@ import asyncio from asyncio import windows_events from test.test_asyncio import utils as test_utils +from test.support.script_helper import spawn_python def tearDownModule(): @@ -33,6 +37,23 @@ def data_received(self, data): self.trans.close() +class ProactorLoopCtrlC(test_utils.TestCase): + def test_ctrl_c(self): + from .test_ctrl_c_in_proactor_loop_helper import __file__ as f + + # ctrl-c will be sent to all processes that share the same console + # in order to isolate the effect of raising ctrl-c we'll create + # a process with a new console + flags = subprocess.CREATE_NEW_CONSOLE + with spawn_python(f, creationflags=flags) as p: + try: + exit_code = p.wait(timeout=5) + self.assertEqual(exit_code, 255) + except: + p.kill() + raise + + class ProactorTests(test_utils.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2018-12-12-16-24-55.bpo-23057.OB4Z1Y.rst b/Misc/NEWS.d/next/Library/2018-12-12-16-24-55.bpo-23057.OB4Z1Y.rst new file mode 100644 index 000000000000..51b727db3042 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-12-16-24-55.bpo-23057.OB4Z1Y.rst @@ -0,0 +1 @@ +Unblock Proactor event loop when keyboard interrupt is received on Windows From webhook-mailer at python.org Tue Dec 18 17:52:44 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 18 Dec 2018 22:52:44 -0000 Subject: [Python-checkins] bpo-31731: Fix test_io.check_interrupted_write() (GH-11225) Message-ID: https://github.com/python/cpython/commit/05c9d31eb62cc45dc3c55a5cdb7cbc713d0421db commit: 05c9d31eb62cc45dc3c55a5cdb7cbc713d0421db branch: master author: Victor Stinner committer: GitHub date: 2018-12-18T23:52:39+01:00 summary: bpo-31731: Fix test_io.check_interrupted_write() (GH-11225) Fix a race condition in check_interrupted_write() of test_io: create directly the thread with SIGALRM signal blocked, rather than blocking the signal later from the thread. Previously, it was possible that the thread gets the signal before the signal is blocked. files: A Misc/NEWS.d/next/Tests/2018-12-18-23-20-39.bpo-31731.tcv85C.rst M Lib/test/test_io.py diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index dc353c159fb0..c3644875103d 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -4149,10 +4149,9 @@ def check_interrupted_write(self, item, bytes, **fdopen_kwargs): in the latter.""" read_results = [] def _read(): - if hasattr(signal, 'pthread_sigmask'): - signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGALRM]) s = os.read(r, 1) read_results.append(s) + t = threading.Thread(target=_read) t.daemon = True r, w = os.pipe() @@ -4160,7 +4159,14 @@ def _read(): large_data = item * (support.PIPE_MAX_SIZE // len(item) + 1) try: wio = self.io.open(w, **fdopen_kwargs) - t.start() + if hasattr(signal, 'pthread_sigmask'): + # create the thread with SIGALRM signal blocked + signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGALRM]) + t.start() + signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGALRM]) + else: + t.start() + # Fill the pipe enough that the write will be blocking. # It will be interrupted by the timer armed above. Since the # other thread has read one byte, the low-level write will diff --git a/Misc/NEWS.d/next/Tests/2018-12-18-23-20-39.bpo-31731.tcv85C.rst b/Misc/NEWS.d/next/Tests/2018-12-18-23-20-39.bpo-31731.tcv85C.rst new file mode 100644 index 000000000000..530977c6e87e --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-18-23-20-39.bpo-31731.tcv85C.rst @@ -0,0 +1,4 @@ +Fix a race condition in ``check_interrupted_write()`` of test_io: create +directly the thread with SIGALRM signal blocked, rather than blocking the +signal later from the thread. Previously, it was possible that the thread gets +the signal before the signal is blocked. From webhook-mailer at python.org Tue Dec 18 17:54:36 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 18 Dec 2018 22:54:36 -0000 Subject: [Python-checkins] bpo-35424: Fix test_multiprocessing_main_handling (GH-11223) Message-ID: https://github.com/python/cpython/commit/6cdce3ddef805e11d75142f3e20e23c3fe21fdf4 commit: 6cdce3ddef805e11d75142f3e20e23c3fe21fdf4 branch: master author: Victor Stinner committer: GitHub date: 2018-12-18T23:54:33+01:00 summary: bpo-35424: Fix test_multiprocessing_main_handling (GH-11223) Fix test_multiprocessing_main_handling: use multiprocessing.Pool with a context manager and then explicitly join the pool. files: A Misc/NEWS.d/next/Tests/2018-12-18-22-36-53.bpo-35424.1Pz4IS.rst M Lib/test/test_multiprocessing_main_handling.py diff --git a/Lib/test/test_multiprocessing_main_handling.py b/Lib/test/test_multiprocessing_main_handling.py index 9fd5c9fcd91f..b6abfcc7e283 100644 --- a/Lib/test/test_multiprocessing_main_handling.py +++ b/Lib/test/test_multiprocessing_main_handling.py @@ -54,18 +54,21 @@ def f(x): if __name__ == '__main__': start_method = sys.argv[1] set_start_method(start_method) - p = Pool(5) results = [] - p.map_async(f, [1, 2, 3], callback=results.extend) - start_time = time.monotonic() - while not results: - time.sleep(0.05) - # up to 1 min to report the results - dt = time.monotonic() - start_time - if dt > 60.0: - raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) + with Pool(5) as pool: + pool.map_async(f, [1, 2, 3], callback=results.extend) + start_time = time.monotonic() + while not results: + time.sleep(0.05) + # up to 1 min to report the results + dt = time.monotonic() - start_time + if dt > 60.0: + raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) + results.sort() print(start_method, "->", results) + + pool.join() """ test_source_main_skipped_in_children = """\ @@ -84,18 +87,21 @@ def f(x): start_method = sys.argv[1] set_start_method(start_method) -p = Pool(5) results = [] -p.map_async(int, [1, 4, 9], callback=results.extend) -start_time = time.monotonic() -while not results: - time.sleep(0.05) - # up to 1 min to report the results - dt = time.monotonic() - start_time - if dt > 60.0: - raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) +with Pool(5) as pool: + pool.map_async(int, [1, 4, 9], callback=results.extend) + start_time = time.monotonic() + while not results: + time.sleep(0.05) + # up to 1 min to report the results + dt = time.monotonic() - start_time + if dt > 60.0: + raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) + results.sort() print(start_method, "->", results) + +pool.join() """ # These helpers were copied from test_cmd_line_script & tweaked a bit... diff --git a/Misc/NEWS.d/next/Tests/2018-12-18-22-36-53.bpo-35424.1Pz4IS.rst b/Misc/NEWS.d/next/Tests/2018-12-18-22-36-53.bpo-35424.1Pz4IS.rst new file mode 100644 index 000000000000..461f636981b1 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-18-22-36-53.bpo-35424.1Pz4IS.rst @@ -0,0 +1,2 @@ +Fix test_multiprocessing_main_handling: use :class:`multiprocessing.Pool` with +a context manager and then explicitly join the pool. From webhook-mailer at python.org Tue Dec 18 18:10:51 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 18 Dec 2018 23:10:51 -0000 Subject: [Python-checkins] bpo-31731: Fix test_io.check_interrupted_write() (GH-11225) Message-ID: https://github.com/python/cpython/commit/729fc5d2acab9eb672c4804092236af5362a4c66 commit: 729fc5d2acab9eb672c4804092236af5362a4c66 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-18T15:10:47-08:00 summary: bpo-31731: Fix test_io.check_interrupted_write() (GH-11225) Fix a race condition in check_interrupted_write() of test_io: create directly the thread with SIGALRM signal blocked, rather than blocking the signal later from the thread. Previously, it was possible that the thread gets the signal before the signal is blocked. (cherry picked from commit 05c9d31eb62cc45dc3c55a5cdb7cbc713d0421db) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2018-12-18-23-20-39.bpo-31731.tcv85C.rst M Lib/test/test_io.py diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 4c206d287f27..405f2d38b494 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -4121,10 +4121,9 @@ def check_interrupted_write(self, item, bytes, **fdopen_kwargs): in the latter.""" read_results = [] def _read(): - if hasattr(signal, 'pthread_sigmask'): - signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGALRM]) s = os.read(r, 1) read_results.append(s) + t = threading.Thread(target=_read) t.daemon = True r, w = os.pipe() @@ -4132,7 +4131,14 @@ def _read(): large_data = item * (support.PIPE_MAX_SIZE // len(item) + 1) try: wio = self.io.open(w, **fdopen_kwargs) - t.start() + if hasattr(signal, 'pthread_sigmask'): + # create the thread with SIGALRM signal blocked + signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGALRM]) + t.start() + signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGALRM]) + else: + t.start() + # Fill the pipe enough that the write will be blocking. # It will be interrupted by the timer armed above. Since the # other thread has read one byte, the low-level write will diff --git a/Misc/NEWS.d/next/Tests/2018-12-18-23-20-39.bpo-31731.tcv85C.rst b/Misc/NEWS.d/next/Tests/2018-12-18-23-20-39.bpo-31731.tcv85C.rst new file mode 100644 index 000000000000..530977c6e87e --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-18-23-20-39.bpo-31731.tcv85C.rst @@ -0,0 +1,4 @@ +Fix a race condition in ``check_interrupted_write()`` of test_io: create +directly the thread with SIGALRM signal blocked, rather than blocking the +signal later from the thread. Previously, it was possible that the thread gets +the signal before the signal is blocked. From webhook-mailer at python.org Tue Dec 18 18:43:32 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Tue, 18 Dec 2018 23:43:32 -0000 Subject: [Python-checkins] bpo-35424: Fix test_multiprocessing_main_handling (GH-11223) (GH-11227) Message-ID: https://github.com/python/cpython/commit/c74e7c48ba4f8437d4c8b402098b8cefc33a28a9 commit: c74e7c48ba4f8437d4c8b402098b8cefc33a28a9 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Victor Stinner date: 2018-12-19T00:43:27+01:00 summary: bpo-35424: Fix test_multiprocessing_main_handling (GH-11223) (GH-11227) Fix test_multiprocessing_main_handling: use multiprocessing.Pool with a context manager and then explicitly join the pool. (cherry picked from commit 6cdce3ddef805e11d75142f3e20e23c3fe21fdf4) Co-authored-by: Victor Stinner files: A Misc/NEWS.d/next/Tests/2018-12-18-22-36-53.bpo-35424.1Pz4IS.rst M Lib/test/test_multiprocessing_main_handling.py diff --git a/Lib/test/test_multiprocessing_main_handling.py b/Lib/test/test_multiprocessing_main_handling.py index 9fd5c9fcd91f..b6abfcc7e283 100644 --- a/Lib/test/test_multiprocessing_main_handling.py +++ b/Lib/test/test_multiprocessing_main_handling.py @@ -54,18 +54,21 @@ def f(x): if __name__ == '__main__': start_method = sys.argv[1] set_start_method(start_method) - p = Pool(5) results = [] - p.map_async(f, [1, 2, 3], callback=results.extend) - start_time = time.monotonic() - while not results: - time.sleep(0.05) - # up to 1 min to report the results - dt = time.monotonic() - start_time - if dt > 60.0: - raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) + with Pool(5) as pool: + pool.map_async(f, [1, 2, 3], callback=results.extend) + start_time = time.monotonic() + while not results: + time.sleep(0.05) + # up to 1 min to report the results + dt = time.monotonic() - start_time + if dt > 60.0: + raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) + results.sort() print(start_method, "->", results) + + pool.join() """ test_source_main_skipped_in_children = """\ @@ -84,18 +87,21 @@ def f(x): start_method = sys.argv[1] set_start_method(start_method) -p = Pool(5) results = [] -p.map_async(int, [1, 4, 9], callback=results.extend) -start_time = time.monotonic() -while not results: - time.sleep(0.05) - # up to 1 min to report the results - dt = time.monotonic() - start_time - if dt > 60.0: - raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) +with Pool(5) as pool: + pool.map_async(int, [1, 4, 9], callback=results.extend) + start_time = time.monotonic() + while not results: + time.sleep(0.05) + # up to 1 min to report the results + dt = time.monotonic() - start_time + if dt > 60.0: + raise RuntimeError("Timed out waiting for results (%.1f sec)" % dt) + results.sort() print(start_method, "->", results) + +pool.join() """ # These helpers were copied from test_cmd_line_script & tweaked a bit... diff --git a/Misc/NEWS.d/next/Tests/2018-12-18-22-36-53.bpo-35424.1Pz4IS.rst b/Misc/NEWS.d/next/Tests/2018-12-18-22-36-53.bpo-35424.1Pz4IS.rst new file mode 100644 index 000000000000..461f636981b1 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-12-18-22-36-53.bpo-35424.1Pz4IS.rst @@ -0,0 +1,2 @@ +Fix test_multiprocessing_main_handling: use :class:`multiprocessing.Pool` with +a context manager and then explicitly join the pool. From webhook-mailer at python.org Tue Dec 18 18:51:07 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Tue, 18 Dec 2018 23:51:07 -0000 Subject: [Python-checkins] bpo-10320: Replace nonstandard sprintf() length modifier in ctypes' PyCArg_repr(). (GH-10853) Message-ID: https://github.com/python/cpython/commit/53e2248a94cd89e65326c5cfd400f74a88552d8c commit: 53e2248a94cd89e65326c5cfd400f74a88552d8c branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-18T15:51:04-08:00 summary: bpo-10320: Replace nonstandard sprintf() length modifier in ctypes' PyCArg_repr(). (GH-10853) Use "ll" instead of the nonstandard "q". (cherry picked from commit 062cbb67726f26794b1b461853e40696b4a0b220) Co-authored-by: Zackery Spytz files: M Modules/_ctypes/callproc.c diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 63f3c21784d5..ff2a7da4246f 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -488,7 +488,7 @@ PyCArg_repr(PyCArgObject *self) #ifdef MS_WIN32 "", #else - "", + "", #endif self->tag, self->value.q); break; From webhook-mailer at python.org Wed Dec 19 01:01:46 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 19 Dec 2018 06:01:46 -0000 Subject: [Python-checkins] bpo-10320: Use PY_FORMAT_LONG_LONG in ctypes' PyCArg_repr(). (GH-11230) Message-ID: https://github.com/python/cpython/commit/838645dc4191c4109e2b300cf9ed9d481b55509f commit: 838645dc4191c4109e2b300cf9ed9d481b55509f branch: 2.7 author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-19T08:01:38+02:00 summary: bpo-10320: Use PY_FORMAT_LONG_LONG in ctypes' PyCArg_repr(). (GH-11230) files: M Modules/_ctypes/callproc.c diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index ff2a7da4246f..209734208578 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -485,11 +485,7 @@ PyCArg_repr(PyCArgObject *self) case 'q': case 'Q': sprintf(buffer, -#ifdef MS_WIN32 - "", -#else - "", -#endif + "", self->tag, self->value.q); break; #endif From webhook-mailer at python.org Wed Dec 19 01:05:18 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 19 Dec 2018 06:05:18 -0000 Subject: [Python-checkins] Removed dangling `since Python` at the end of library/xml.rst. (GH-11201) Message-ID: https://github.com/python/cpython/commit/82d73554e4764350bfd8f13957c5e024ac95c4af commit: 82d73554e4764350bfd8f13957c5e024ac95c4af branch: master author: Jules Lasne (jlasne) committer: Serhiy Storchaka date: 2018-12-19T08:05:14+02:00 summary: Removed dangling `since Python` at the end of library/xml.rst. (GH-11201) files: M Doc/library/xml.rst diff --git a/Doc/library/xml.rst b/Doc/library/xml.rst index 9b8ba6b17c85..be1272940b8e 100644 --- a/Doc/library/xml.rst +++ b/Doc/library/xml.rst @@ -75,8 +75,8 @@ decompression bomb Safe Safe Safe S 2. :mod:`xml.dom.minidom` doesn't expand external entities and simply returns the unexpanded entity verbatim. 3. :mod:`xmlrpclib` doesn't expand external entities and omits them. -4. Since Python 3.8.0, external general entities are no longer processed by - default since Python. +4. Since Python 3.8, external general entities are no longer processed by + default. billion laughs / exponential entity expansion From webhook-mailer at python.org Wed Dec 19 01:09:50 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 19 Dec 2018 06:09:50 -0000 Subject: [Python-checkins] bpo-35506: Remove redundant and incorrect links from keywords. (GH-11174) Message-ID: https://github.com/python/cpython/commit/2b57c43f21f891df4c6f2294a3b9e1b9029a16b6 commit: 2b57c43f21f891df4c6f2294a3b9e1b9029a16b6 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-19T08:09:46+02:00 summary: bpo-35506: Remove redundant and incorrect links from keywords. (GH-11174) files: M Doc/faq/programming.rst M Doc/glossary.rst M Doc/howto/functional.rst M Doc/library/aifc.rst M Doc/library/contextlib.rst M Doc/library/fileinput.rst M Doc/library/functions.rst M Doc/library/imaplib.rst M Doc/library/imp.rst M Doc/library/importlib.rst M Doc/library/io.rst M Doc/library/parser.rst M Doc/library/pickle.rst M Doc/library/smtplib.rst M Doc/library/socketserver.rst M Doc/library/stdtypes.rst M Doc/library/telnetlib.rst M Doc/library/tempfile.rst M Doc/library/threading.rst M Doc/library/wave.rst M Doc/library/xml.dom.minidom.rst M Doc/library/zipfile.rst M Doc/reference/compound_stmts.rst M Doc/reference/datamodel.rst M Doc/reference/executionmodel.rst M Doc/reference/expressions.rst M Doc/reference/import.rst M Doc/reference/simple_stmts.rst M Doc/tutorial/classes.rst M Doc/tutorial/controlflow.rst M Doc/tutorial/datastructures.rst M Doc/tutorial/errors.rst M Doc/tutorial/inputoutput.rst M Doc/tutorial/modules.rst M Doc/whatsnew/2.0.rst M Doc/whatsnew/2.1.rst M Doc/whatsnew/2.2.rst M Doc/whatsnew/2.3.rst M Doc/whatsnew/2.5.rst M Doc/whatsnew/2.6.rst M Doc/whatsnew/2.7.rst M Doc/whatsnew/3.0.rst M Doc/whatsnew/3.4.rst M Doc/whatsnew/3.7.rst M Doc/whatsnew/3.8.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index fd720c1a304b..047812e6e036 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -738,7 +738,7 @@ Is it possible to write obfuscated one-liners in Python? -------------------------------------------------------- Yes. Usually this is done by nesting :keyword:`lambda` within -:keyword:`lambda`. See the following three examples, due to Ulf Bartelt:: +:keyword:`!lambda`. See the following three examples, due to Ulf Bartelt:: from functools import reduce diff --git a/Doc/glossary.rst b/Doc/glossary.rst index daa98a5f910e..31cdba2444be 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -336,7 +336,7 @@ Glossary names, attribute access, operators or function calls which all return a value. In contrast to many other languages, not all language constructs are expressions. There are also :term:`statement`\s which cannot be used - as expressions, such as :keyword:`if`. Assignments are also statements, + as expressions, such as :keyword:`while`. Assignments are also statements, not expressions. extension module @@ -448,8 +448,8 @@ Glossary generator expression An expression that returns an iterator. It looks like a normal expression - followed by a :keyword:`for` expression defining a loop variable, range, - and an optional :keyword:`if` expression. The combined expression + followed by a :keyword:`!for` clause defining a loop variable, range, + and an optional :keyword:`!if` clause. The combined expression generates values for an enclosing function:: >>> sum(i*i for i in range(10)) # sum of squares 0, 1, 4, ... 81 diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst index 2efe4537e288..f8f2aac70f9b 100644 --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -1108,7 +1108,7 @@ need to define a new function at all:: existing_files = filter(os.path.exists, file_list) If the function you need doesn't exist, you need to write it. One way to write -small functions is to use the :keyword:`lambda` statement. ``lambda`` takes a +small functions is to use the :keyword:`lambda` expression. ``lambda`` takes a number of parameters and an expression combining these parameters, and creates an anonymous function that returns the value of the expression:: diff --git a/Doc/library/aifc.rst b/Doc/library/aifc.rst index 970a7aeb98a7..7328907730fb 100644 --- a/Doc/library/aifc.rst +++ b/Doc/library/aifc.rst @@ -45,7 +45,7 @@ Module :mod:`aifc` defines the following function: time how many samples you are going to write in total and use :meth:`writeframesraw` and :meth:`setnframes`. The :func:`.open` function may be used in a :keyword:`with` statement. When - the :keyword:`with` block completes, the :meth:`~aifc.close` method is called. + the :keyword:`!with` block completes, the :meth:`~aifc.close` method is called. .. versionchanged:: 3.4 Support for the :keyword:`with` statement was added. diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index 930c97358e08..017a87a5648c 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -1,5 +1,5 @@ -:mod:`contextlib` --- Utilities for :keyword:`with`\ -statement contexts -======================================================================== +:mod:`!contextlib` --- Utilities for :keyword:`!with`\ -statement contexts +========================================================================== .. module:: contextlib :synopsis: Utilities for with-statement contexts. @@ -72,7 +72,7 @@ Functions and classes provided: The function being decorated must return a :term:`generator`-iterator when called. This iterator must yield exactly one value, which will be bound to - the targets in the :keyword:`with` statement's :keyword:`as` clause, if any. + the targets in the :keyword:`with` statement's :keyword:`!as` clause, if any. At the point where the generator yields, the block nested in the :keyword:`with` statement is executed. The generator is then resumed after the block is exited. @@ -82,9 +82,9 @@ Functions and classes provided: the error (if any), or ensure that some cleanup takes place. If an exception is trapped merely in order to log it or to perform some action (rather than to suppress it entirely), the generator must reraise that exception. Otherwise the - generator context manager will indicate to the :keyword:`with` statement that + generator context manager will indicate to the :keyword:`!with` statement that the exception has been handled, and execution will resume with the statement - immediately following the :keyword:`with` statement. + immediately following the :keyword:`!with` statement. :func:`contextmanager` uses :class:`ContextDecorator` so the context managers it creates can be used as decorators as well as in :keyword:`with` statements. @@ -346,7 +346,7 @@ Functions and classes provided: As the decorated function must be able to be called multiple times, the underlying context manager must support use in multiple :keyword:`with` statements. If this is not the case, then the original construct with the - explicit :keyword:`with` statement inside the function should be used. + explicit :keyword:`!with` statement inside the function should be used. .. versionadded:: 3.2 @@ -771,7 +771,7 @@ Reentrant context managers More sophisticated context managers may be "reentrant". These context managers can not only be used in multiple :keyword:`with` statements, -but may also be used *inside* a :keyword:`with` statement that is already +but may also be used *inside* a :keyword:`!with` statement that is already using the same context manager. :class:`threading.RLock` is an example of a reentrant context manager, as are diff --git a/Doc/library/fileinput.rst b/Doc/library/fileinput.rst index f1e29a8a7d85..af9dff34a808 100644 --- a/Doc/library/fileinput.rst +++ b/Doc/library/fileinput.rst @@ -63,7 +63,7 @@ The following function is the primary interface of this module: The :class:`FileInput` instance can be used as a context manager in the :keyword:`with` statement. In this example, *input* is closed after the - :keyword:`with` statement is exited, even if an exception occurs:: + :keyword:`!with` statement is exited, even if an exception occurs:: with fileinput.input(files=('spam.txt', 'eggs.txt')) as f: for line in f: @@ -155,7 +155,7 @@ available for subclassing as well: A :class:`FileInput` instance can be used as a context manager in the :keyword:`with` statement. In this example, *input* is closed after the - :keyword:`with` statement is exited, even if an exception occurs:: + :keyword:`!with` statement is exited, even if an exception occurs:: with FileInput(files=('spam.txt', 'eggs.txt')) as input: process(input) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index e7b98eb44b2e..24a158fd5a85 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1665,7 +1665,7 @@ are always available. They are listed here in alphabetical order. This function is invoked by the :keyword:`import` statement. It can be replaced (by importing the :mod:`builtins` module and assigning to ``builtins.__import__``) in order to change semantics of the - :keyword:`import` statement, but doing so is **strongly** discouraged as it + :keyword:`!import` statement, but doing so is **strongly** discouraged as it is usually simpler to use import hooks (see :pep:`302`) to attain the same goals and does not cause issues with code which assumes the default import implementation is in use. Direct use of :func:`__import__` is also diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst index 040dab6e9f7a..d0709f8b678e 100644 --- a/Doc/library/imaplib.rst +++ b/Doc/library/imaplib.rst @@ -39,7 +39,7 @@ base class: The :class:`IMAP4` class supports the :keyword:`with` statement. When used like this, the IMAP4 ``LOGOUT`` command is issued automatically when the - :keyword:`with` statement exits. E.g.:: + :keyword:`!with` statement exits. E.g.:: >>> from imaplib import IMAP4 >>> with IMAP4("domain.org") as M: diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst index 1bd6f12b915f..04f207f58d38 100644 --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -179,7 +179,7 @@ This module provides an interface to the mechanisms used to implement the If a module imports objects from another module using :keyword:`from` ... :keyword:`import` ..., calling :func:`reload` for the other module does not redefine the objects imported from it --- one way around this is to re-execute - the :keyword:`from` statement, another is to use :keyword:`import` and qualified + the :keyword:`!from` statement, another is to use :keyword:`!import` and qualified names (*module*.*name*) instead. If a module instantiates instances of a class, reloading the module that defines diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 0bcfbb1c7263..3c9a99abf9a2 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1,5 +1,5 @@ -:mod:`importlib` --- The implementation of :keyword:`import` -============================================================ +:mod:`!importlib` --- The implementation of :keyword:`!import` +============================================================== .. module:: importlib :synopsis: The implementation of the import machinery. @@ -19,7 +19,7 @@ Introduction The purpose of the :mod:`importlib` package is two-fold. One is to provide the implementation of the :keyword:`import` statement (and thus, by extension, the :func:`__import__` function) in Python source code. This provides an -implementation of :keyword:`import` which is portable to any Python +implementation of :keyword:`!import` which is portable to any Python interpreter. This also provides an implementation which is easier to comprehend than one implemented in a programming language other than Python. @@ -197,7 +197,7 @@ Functions If a module imports objects from another module using :keyword:`from` ... :keyword:`import` ..., calling :func:`reload` for the other module does not redefine the objects imported from it --- one way around this is to - re-execute the :keyword:`from` statement, another is to use :keyword:`import` + re-execute the :keyword:`!from` statement, another is to use :keyword:`!import` and qualified names (*module.name*) instead. If a module instantiates instances of a class, reloading the module that diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 7068e68ad90f..473e8c8f82a1 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -249,7 +249,7 @@ I/O Base Classes :class:`IOBase` is also a context manager and therefore supports the :keyword:`with` statement. In this example, *file* is closed after the - :keyword:`with` statement's suite is finished---even if an exception occurs:: + :keyword:`!with` statement's suite is finished---even if an exception occurs:: with open('spam.txt', 'w') as file: file.write('Spam and eggs!') diff --git a/Doc/library/parser.rst b/Doc/library/parser.rst index c3b699a36024..a302681eca05 100644 --- a/Doc/library/parser.rst +++ b/Doc/library/parser.rst @@ -63,7 +63,7 @@ of the production as recognized in the input string: these are always sequences which have the same form as the parent. An important aspect of this structure which should be noted is that keywords used to identify the parent node type, such as the keyword :keyword:`if` in an :const:`if_stmt`, are included in the -node tree without any special treatment. For example, the :keyword:`if` keyword +node tree without any special treatment. For example, the :keyword:`!if` keyword is represented by the tuple ``(1, 'if')``, where ``1`` is the numeric value associated with all :const:`NAME` tokens, including variable and function names defined by the user. In an alternate form returned when line number information diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 100a6a134508..53eb5d39ef94 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -931,7 +931,7 @@ The following example reads the resulting pickled data. :: .. [#] Don't confuse this with the :mod:`marshal` module .. [#] This is why :keyword:`lambda` functions cannot be pickled: all - :keyword:`lambda` functions share the same name: ````. + :keyword:`!lambda` functions share the same name: ````. .. [#] The exception raised will likely be an :exc:`ImportError` or an :exc:`AttributeError` but it could be something else. diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst index f59c18409411..2c3a5f0c6f72 100644 --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -46,7 +46,7 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). The :class:`SMTP` class supports the :keyword:`with` statement. When used like this, the SMTP ``QUIT`` command is issued automatically when the - :keyword:`with` statement exits. E.g.:: + :keyword:`!with` statement exits. E.g.:: >>> from smtplib import SMTP >>> with SMTP("domain.org") as smtp: diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst index 1b3062da6df9..7c8c8d52e03d 100644 --- a/Doc/library/socketserver.rst +++ b/Doc/library/socketserver.rst @@ -57,7 +57,7 @@ the server in a :keyword:`with` statement. Then call the :meth:`~BaseServer.handle_request` or :meth:`~BaseServer.serve_forever` method of the server object to process one or many requests. Finally, call :meth:`~BaseServer.server_close` -to close the socket (unless you used a :keyword:`with` statement). +to close the socket (unless you used a :keyword:`!with` statement). When inheriting from :class:`ThreadingMixIn` for threaded connection behavior, you should explicitly declare how you want your threads to behave on an abrupt diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 86e7d68dd2d6..0262c0292f52 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -74,8 +74,8 @@ one of their operands.) .. _boolean: -Boolean Operations --- :keyword:`and`, :keyword:`or`, :keyword:`not` -==================================================================== +Boolean Operations --- :keyword:`!and`, :keyword:`!or`, :keyword:`!not` +======================================================================= .. index:: pair: Boolean; operations @@ -4460,7 +4460,7 @@ before the statement body is executed and exited when the statement ends: Enter the runtime context and return either this object or another object related to the runtime context. The value returned by this method is bound to - the identifier in the :keyword:`as` clause of :keyword:`with` statements using + the identifier in the :keyword:`!as` clause of :keyword:`with` statements using this context manager. An example of a context manager that returns itself is a :term:`file object`. @@ -4472,7 +4472,7 @@ before the statement body is executed and exited when the statement ends: decimal context to a copy of the original decimal context and then return the copy. This allows changes to be made to the current decimal context in the body of the :keyword:`with` statement without affecting code outside the - :keyword:`with` statement. + :keyword:`!with` statement. .. method:: contextmanager.__exit__(exc_type, exc_val, exc_tb) @@ -4484,10 +4484,10 @@ before the statement body is executed and exited when the statement ends: Returning a true value from this method will cause the :keyword:`with` statement to suppress the exception and continue execution with the statement immediately - following the :keyword:`with` statement. Otherwise the exception continues + following the :keyword:`!with` statement. Otherwise the exception continues propagating after this method has finished executing. Exceptions that occur during execution of this method will replace any exception that occurred in the - body of the :keyword:`with` statement. + body of the :keyword:`!with` statement. The exception passed in should never be reraised explicitly - instead, this method should return a false value to indicate that the method completed diff --git a/Doc/library/telnetlib.rst b/Doc/library/telnetlib.rst index f9c5153e3f58..4ba426425277 100644 --- a/Doc/library/telnetlib.rst +++ b/Doc/library/telnetlib.rst @@ -44,7 +44,7 @@ Character), EL (Erase Line), GA (Go Ahead), SB (Subnegotiation Begin). an empty string for other reasons. See the individual descriptions below. A :class:`Telnet` object is a context manager and can be used in a - :keyword:`with` statement. When the :keyword:`with` block ends, the + :keyword:`with` statement. When the :keyword:`!with` block ends, the :meth:`close` method is called:: >>> from telnetlib import Telnet diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst index 20046ee0fb4e..daa6f621f137 100644 --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -124,7 +124,7 @@ The module defines the following user-callable items: The directory name can be retrieved from the :attr:`name` attribute of the returned object. When the returned object is used as a context manager, the - :attr:`name` will be assigned to the target of the :keyword:`as` clause in + :attr:`name` will be assigned to the target of the :keyword:`!as` clause in the :keyword:`with` statement, if there is one. The directory can be explicitly cleaned up by calling the diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index a9d5268dd2b8..d7dbcb107dda 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -973,8 +973,8 @@ As an example, here is a simple way to synchronize a client and server thread:: .. _with-locks: -Using locks, conditions, and semaphores in the :keyword:`with` statement ------------------------------------------------------------------------- +Using locks, conditions, and semaphores in the :keyword:`!with` statement +------------------------------------------------------------------------- All of the objects provided by this module that have :meth:`acquire` and :meth:`release` methods can be used as context managers for a :keyword:`with` diff --git a/Doc/library/wave.rst b/Doc/library/wave.rst index 5c315c516175..60d19a8d5f7d 100644 --- a/Doc/library/wave.rst +++ b/Doc/library/wave.rst @@ -40,7 +40,7 @@ The :mod:`wave` module defines the following function and exception: the file object. The :func:`.open` function may be used in a :keyword:`with` statement. When - the :keyword:`with` block completes, the :meth:`Wave_read.close() + the :keyword:`!with` block completes, the :meth:`Wave_read.close() ` or :meth:`Wave_write.close() ` method is called. diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index fb6fe6e66143..af09ea98c818 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -126,7 +126,7 @@ module documentation. This section lists the differences between the API and You can avoid calling this method explicitly by using the :keyword:`with` statement. The following code will automatically unlink *dom* when the - :keyword:`with` block is exited:: + :keyword:`!with` block is exited:: with xml.dom.minidom.parse(datasource) as dom: ... # Work with dom. diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index 1437730087a7..4e9edff27014 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -181,7 +181,7 @@ ZipFile Objects ZipFile is also a context manager and therefore supports the :keyword:`with` statement. In the example, *myzip* is closed after the - :keyword:`with` statement's suite is finished---even if an exception occurs:: + :keyword:`!with` statement's suite is finished---even if an exception occurs:: with ZipFile('spam.zip', 'w') as myzip: myzip.write('eggs.txt') diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 474ec884a69f..a83b561c13fa 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -78,11 +78,11 @@ on a separate line for clarity. .. _elif: .. _else: -The :keyword:`if` statement -=========================== +The :keyword:`!if` statement +============================ .. index:: - statement: if + ! statement: if keyword: elif keyword: else single: : (colon); compound statement @@ -103,14 +103,13 @@ false, the suite of the :keyword:`else` clause, if present, is executed. .. _while: -The :keyword:`while` statement -============================== +The :keyword:`!while` statement +=============================== .. index:: - statement: while + ! statement: while keyword: else pair: loop; statement - keyword: else single: : (colon); compound statement The :keyword:`while` statement is used for repeated execution as long as an @@ -122,7 +121,7 @@ expression is true: This repeatedly tests the expression and, if it is true, executes the first suite; if the expression is false (which may be the first time it is tested) the -suite of the :keyword:`else` clause, if present, is executed and the loop +suite of the :keyword:`!else` clause, if present, is executed and the loop terminates. .. index:: @@ -130,25 +129,22 @@ terminates. statement: continue A :keyword:`break` statement executed in the first suite terminates the loop -without executing the :keyword:`else` clause's suite. A :keyword:`continue` +without executing the :keyword:`!else` clause's suite. A :keyword:`continue` statement executed in the first suite skips the rest of the suite and goes back to testing the expression. .. _for: -The :keyword:`for` statement -============================ +The :keyword:`!for` statement +============================= .. index:: - statement: for + ! statement: for keyword: in keyword: else pair: target; list pair: loop; statement - keyword: in - keyword: else - pair: target; list object: sequence single: : (colon); compound statement @@ -166,16 +162,16 @@ by the iterator. Each item in turn is assigned to the target list using the standard rules for assignments (see :ref:`assignment`), and then the suite is executed. When the items are exhausted (which is immediately when the sequence is empty or an iterator raises a :exc:`StopIteration` exception), the suite in -the :keyword:`else` clause, if present, is executed, and the loop terminates. +the :keyword:`!else` clause, if present, is executed, and the loop terminates. .. index:: statement: break statement: continue A :keyword:`break` statement executed in the first suite terminates the loop -without executing the :keyword:`else` clause's suite. A :keyword:`continue` +without executing the :keyword:`!else` clause's suite. A :keyword:`continue` statement executed in the first suite skips the rest of the suite and continues -with the next item, or with the :keyword:`else` clause if there is no next +with the next item, or with the :keyword:`!else` clause if there is no next item. The for-loop makes assignments to the variables in the target list. @@ -224,11 +220,11 @@ returns the list ``[0, 1, 2]``. .. _except: .. _finally: -The :keyword:`try` statement -============================ +The :keyword:`!try` statement +============================= .. index:: - statement: try + ! statement: try keyword: except keyword: finally keyword: else @@ -250,7 +246,7 @@ for a group of statements: The :keyword:`except` clause(s) specify one or more exception handlers. When no exception occurs in the :keyword:`try` clause, no exception handler is executed. -When an exception occurs in the :keyword:`try` suite, a search for an exception +When an exception occurs in the :keyword:`!try` suite, a search for an exception handler is started. This search inspects the except clauses in turn until one is found that matches the exception. An expression-less except clause, if present, must be last; it matches any exception. For an except clause with an @@ -270,7 +266,7 @@ as if the entire :keyword:`try` statement raised the exception). .. index:: single: as; except clause When a matching except clause is found, the exception is assigned to the target -specified after the :keyword:`as` keyword in that except clause, if present, and +specified after the :keyword:`!as` keyword in that except clause, if present, and the except clause's suite is executed. All except clauses must have an executable block. When the end of this block is reached, execution continues normally after the entire try statement. (This means that if two nested @@ -314,22 +310,22 @@ from a function that handled an exception. statement: break statement: continue -The optional :keyword:`else` clause is executed if the control flow leaves the +The optional :keyword:`!else` clause is executed if the control flow leaves the :keyword:`try` suite, no exception was raised, and no :keyword:`return`, :keyword:`continue`, or :keyword:`break` statement was executed. Exceptions in -the :keyword:`else` clause are not handled by the preceding :keyword:`except` +the :keyword:`!else` clause are not handled by the preceding :keyword:`except` clauses. .. index:: keyword: finally If :keyword:`finally` is present, it specifies a 'cleanup' handler. The :keyword:`try` clause is executed, including any :keyword:`except` and -:keyword:`else` clauses. If an exception occurs in any of the clauses and is -not handled, the exception is temporarily saved. The :keyword:`finally` clause +:keyword:`!else` clauses. If an exception occurs in any of the clauses and is +not handled, the exception is temporarily saved. The :keyword:`!finally` clause is executed. If there is a saved exception it is re-raised at the end of the -:keyword:`finally` clause. If the :keyword:`finally` clause raises another +:keyword:`!finally` clause. If the :keyword:`!finally` clause raises another exception, the saved exception is set as the context of the new exception. -If the :keyword:`finally` clause executes a :keyword:`return`, :keyword:`break` +If the :keyword:`!finally` clause executes a :keyword:`return`, :keyword:`break` or :keyword:`continue` statement, the saved exception is discarded:: >>> def f(): @@ -350,12 +346,12 @@ the :keyword:`finally` clause. statement: continue When a :keyword:`return`, :keyword:`break` or :keyword:`continue` statement is -executed in the :keyword:`try` suite of a :keyword:`try`...\ :keyword:`finally` +executed in the :keyword:`try` suite of a :keyword:`!try`...\ :keyword:`!finally` statement, the :keyword:`finally` clause is also executed 'on the way out.' The return value of a function is determined by the last :keyword:`return` statement executed. Since the :keyword:`finally` clause always executes, a -:keyword:`return` statement executed in the :keyword:`finally` clause will +:keyword:`!return` statement executed in the :keyword:`!finally` clause will always be the last one executed:: >>> def foo(): @@ -379,11 +375,11 @@ may be found in section :ref:`raise`. .. _with: .. _as: -The :keyword:`with` statement -============================= +The :keyword:`!with` statement +============================== .. index:: - statement: with + ! statement: with keyword: as single: as; with statement single: , (comma); with statement @@ -595,7 +591,7 @@ name), for immediate use in expressions. This uses lambda expressions, describe section :ref:`lambda`. Note that the lambda expression is merely a shorthand for a simplified function definition; a function defined in a ":keyword:`def`" statement can be passed around or assigned to another name just like a function -defined by a lambda expression. The ":keyword:`def`" form is actually more powerful +defined by a lambda expression. The ":keyword:`!def`" form is actually more powerful since it allows the execution of multiple statements and annotations. **Programmer's note:** Functions are first-class objects. A "``def``" statement @@ -758,8 +754,8 @@ An example of a coroutine function:: .. index:: statement: async for .. _`async for`: -The :keyword:`async for` statement ----------------------------------- +The :keyword:`!async for` statement +----------------------------------- .. productionlist:: async_for_stmt: "async" `for_stmt` @@ -802,8 +798,8 @@ body of a coroutine function. .. index:: statement: async with .. _`async with`: -The :keyword:`async with` statement ------------------------------------ +The :keyword:`!async with` statement +------------------------------------ .. productionlist:: async_with_stmt: "async" `with_stmt` diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index b20b708e9575..83e1d239b34a 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -620,7 +620,7 @@ Callable types called, always returns an iterator object which can be used to execute the body of the function: calling the iterator's :meth:`iterator.__next__` method will cause the function to execute until it provides a value - using the :keyword:`yield` statement. When the function executes a + using the :keyword:`!yield` statement. When the function executes a :keyword:`return` statement or falls off the end, a :exc:`StopIteration` exception is raised and the iterator will have reached the end of the set of values to be returned. @@ -700,7 +700,7 @@ Modules Modules are a basic organizational unit of Python code, and are created by the :ref:`import system ` as invoked either by the - :keyword:`import` statement (see :keyword:`import`), or by calling + :keyword:`import` statement, or by calling functions such as :func:`importlib.import_module` and built-in :func:`__import__`. A module object has a namespace implemented by a dictionary object (this is the dictionary referenced by the ``__globals__`` @@ -2423,7 +2423,7 @@ A :dfn:`context manager` is an object that defines the runtime context to be established when executing a :keyword:`with` statement. The context manager handles the entry into, and the exit from, the desired runtime context for the execution of the block of code. Context managers are normally invoked using the -:keyword:`with` statement (described in section :ref:`with`), but can also be +:keyword:`!with` statement (described in section :ref:`with`), but can also be used by directly invoking their methods. .. index:: @@ -2440,7 +2440,7 @@ For more information on context managers, see :ref:`typecontextmanager`. Enter the runtime context related to this object. The :keyword:`with` statement will bind this method's return value to the target(s) specified in the - :keyword:`as` clause of the statement, if any. + :keyword:`!as` clause of the statement, if any. .. method:: object.__exit__(self, exc_type, exc_value, traceback) diff --git a/Doc/reference/executionmodel.rst b/Doc/reference/executionmodel.rst index 1a69e972f2cb..ba7130d63621 100644 --- a/Doc/reference/executionmodel.rst +++ b/Doc/reference/executionmodel.rst @@ -58,8 +58,8 @@ The following constructs bind names: formal parameters to functions, :keyword:`import` statements, class and function definitions (these bind the class or function name in the defining block), and targets that are identifiers if occurring in an assignment, :keyword:`for` loop header, or after -:keyword:`as` in a :keyword:`with` statement or :keyword:`except` clause. -The :keyword:`import` statement +:keyword:`!as` in a :keyword:`with` statement or :keyword:`except` clause. +The :keyword:`!import` statement of the form ``from ... import *`` binds all names defined in the imported module, except those beginning with an underscore. This form may only be used at the module level. @@ -123,7 +123,7 @@ namespace. Names are resolved in the top-level namespace by searching the global namespace, i.e. the namespace of the module containing the code block, and the builtins namespace, the namespace of the module :mod:`builtins`. The global namespace is searched first. If the name is not found there, the -builtins namespace is searched. The :keyword:`global` statement must precede +builtins namespace is searched. The :keyword:`!global` statement must precede all uses of the name. The :keyword:`global` statement has the same scope as a name binding operation diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 571e6fa191c8..cf7d05eef674 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -185,20 +185,20 @@ Common syntax elements for comprehensions are: comp_if: "if" `expression_nocond` [`comp_iter`] The comprehension consists of a single expression followed by at least one -:keyword:`for` clause and zero or more :keyword:`for` or :keyword:`if` clauses. +:keyword:`!for` clause and zero or more :keyword:`!for` or :keyword:`!if` clauses. In this case, the elements of the new container are those that would be produced -by considering each of the :keyword:`for` or :keyword:`if` clauses a block, +by considering each of the :keyword:`!for` or :keyword:`!if` clauses a block, nesting from left to right, and evaluating the expression to produce an element each time the innermost block is reached. -However, aside from the iterable expression in the leftmost :keyword:`for` clause, +However, aside from the iterable expression in the leftmost :keyword:`!for` clause, the comprehension is executed in a separate implicitly nested scope. This ensures that names assigned to in the target list don't "leak" into the enclosing scope. -The iterable expression in the leftmost :keyword:`for` clause is evaluated +The iterable expression in the leftmost :keyword:`!for` clause is evaluated directly in the enclosing scope and then passed as an argument to the implictly -nested scope. Subsequent :keyword:`for` clauses and any filter condition in the -leftmost :keyword:`for` clause cannot be evaluated in the enclosing scope as +nested scope. Subsequent :keyword:`!for` clauses and any filter condition in the +leftmost :keyword:`!for` clause cannot be evaluated in the enclosing scope as they may depend on the values obtained from the leftmost iterable. For example: ``[x*y for x in range(10) for y in range(x, x+10)]``. @@ -209,14 +209,14 @@ nested scope. .. index:: single: await; in comprehensions -Since Python 3.6, in an :keyword:`async def` function, an :keyword:`async for` +Since Python 3.6, in an :keyword:`async def` function, an :keyword:`!async for` clause may be used to iterate over a :term:`asynchronous iterator`. -A comprehension in an :keyword:`async def` function may consist of either a -:keyword:`for` or :keyword:`async for` clause following the leading -expression, may contain additional :keyword:`for` or :keyword:`async for` +A comprehension in an :keyword:`!async def` function may consist of either a +:keyword:`!for` or :keyword:`!async for` clause following the leading +expression, may contain additional :keyword:`!for` or :keyword:`!async for` clauses, and may also use :keyword:`await` expressions. -If a comprehension contains either :keyword:`async for` clauses -or :keyword:`await` expressions it is called an +If a comprehension contains either :keyword:`!async for` clauses +or :keyword:`!await` expressions it is called an :dfn:`asynchronous comprehension`. An asynchronous comprehension may suspend the execution of the coroutine function in which it appears. See also :pep:`530`. @@ -360,11 +360,11 @@ brackets or curly braces. Variables used in the generator expression are evaluated lazily when the :meth:`~generator.__next__` method is called for the generator object (in the same fashion as normal generators). However, the iterable expression in the -leftmost :keyword:`for` clause is immediately evaluated, so that an error +leftmost :keyword:`!for` clause is immediately evaluated, so that an error produced by it will be emitted at the point where the generator expression is defined, rather than at the point where the first value is retrieved. -Subsequent :keyword:`for` clauses and any filter condition in the leftmost -:keyword:`for` clause cannot be evaluated in the enclosing scope as they may +Subsequent :keyword:`!for` clauses and any filter condition in the leftmost +:keyword:`!for` clause cannot be evaluated in the enclosing scope as they may depend on the values obtained from the leftmost iterable. For example: ``(x*y for x in range(10) for y in range(x, x+10))``. @@ -375,7 +375,7 @@ To avoid interfering with the expected operation of the generator expression itself, ``yield`` and ``yield from`` expressions are prohibited in the implicitly defined generator. -If a generator expression contains either :keyword:`async for` +If a generator expression contains either :keyword:`!async for` clauses or :keyword:`await` expressions it is called an :dfn:`asynchronous generator expression`. An asynchronous generator expression returns a new asynchronous generator object, @@ -637,12 +637,12 @@ that method. In an asynchronous generator function, yield expressions are allowed anywhere in a :keyword:`try` construct. However, if an asynchronous generator is not resumed before it is finalized (by reaching a zero reference count or by -being garbage collected), then a yield expression within a :keyword:`try` +being garbage collected), then a yield expression within a :keyword:`!try` construct could result in a failure to execute pending :keyword:`finally` clauses. In this case, it is the responsibility of the event loop or scheduler running the asynchronous generator to call the asynchronous generator-iterator's :meth:`~agen.aclose` method and run the resulting -coroutine object, thus allowing any pending :keyword:`finally` clauses +coroutine object, thus allowing any pending :keyword:`!finally` clauses to execute. To take care of finalization, an event loop should define @@ -1548,7 +1548,7 @@ Membership test operations The operators :keyword:`in` and :keyword:`not in` test for membership. ``x in s`` evaluates to ``True`` if *x* is a member of *s*, and ``False`` otherwise. ``x not in s`` returns the negation of ``x in s``. All built-in sequences and -set types support this as well as dictionary, for which :keyword:`in` tests +set types support this as well as dictionary, for which :keyword:`!in` tests whether the dictionary has a given key. For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression ``x in y`` is equivalent to ``any(x is e or x == e for e in y)``. @@ -1648,6 +1648,8 @@ returns a boolean value regardless of the type of its argument (for example, ``not 'foo'`` produces ``False`` rather than ``''``.) +.. _if_expr: + Conditional expressions ======================= @@ -1790,7 +1792,7 @@ precedence and have a left-to-right chaining feature as described in the +===============================================+=====================================+ | :keyword:`lambda` | Lambda expression | +-----------------------------------------------+-------------------------------------+ -| :keyword:`if` -- :keyword:`else` | Conditional expression | +| :keyword:`if ` -- :keyword:`!else` | Conditional expression | +-----------------------------------------------+-------------------------------------+ | :keyword:`or` | Boolean OR | +-----------------------------------------------+-------------------------------------+ diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index d36d7d6a707e..9a0ab39d3b4a 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -15,11 +15,11 @@ way. Functions such as :func:`importlib.import_module` and built-in The :keyword:`import` statement combines two operations; it searches for the named module, then it binds the results of that search to a name in the local -scope. The search operation of the :keyword:`import` statement is defined as +scope. The search operation of the :keyword:`!import` statement is defined as a call to the :func:`__import__` function, with the appropriate arguments. The return value of :func:`__import__` is used to perform the name -binding operation of the :keyword:`import` statement. See the -:keyword:`import` statement for the exact details of that name binding +binding operation of the :keyword:`!import` statement. See the +:keyword:`!import` statement for the exact details of that name binding operation. A direct call to :func:`__import__` performs only the module search and, if diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index b96297d591f1..0efbe1ce5d57 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -369,11 +369,11 @@ target, then the interpreter evaluates the target except for the last .. _assert: -The :keyword:`assert` statement -=============================== +The :keyword:`!assert` statement +================================ .. index:: - statement: assert + ! statement: assert pair: debugging; assertions single: , (comma); expression list @@ -412,8 +412,8 @@ is determined when the interpreter starts. .. _pass: -The :keyword:`pass` statement -============================= +The :keyword:`!pass` statement +============================== .. index:: statement: pass @@ -434,11 +434,11 @@ code needs to be executed, for example:: .. _del: -The :keyword:`del` statement -============================ +The :keyword:`!del` statement +============================= .. index:: - statement: del + ! statement: del pair: deletion; target triple: deletion; target; list @@ -473,11 +473,11 @@ the sliced object). .. _return: -The :keyword:`return` statement -=============================== +The :keyword:`!return` statement +================================ .. index:: - statement: return + ! statement: return pair: function; definition pair: class; definition @@ -495,7 +495,7 @@ If an expression list is present, it is evaluated, else ``None`` is substituted. .. index:: keyword: finally When :keyword:`return` passes control out of a :keyword:`try` statement with a -:keyword:`finally` clause, that :keyword:`finally` clause is executed before +:keyword:`finally` clause, that :keyword:`!finally` clause is executed before really leaving the function. In a generator function, the :keyword:`return` statement indicates that the @@ -505,13 +505,13 @@ becomes the :attr:`StopIteration.value` attribute. In an asynchronous generator function, an empty :keyword:`return` statement indicates that the asynchronous generator is done and will cause -:exc:`StopAsyncIteration` to be raised. A non-empty :keyword:`return` +:exc:`StopAsyncIteration` to be raised. A non-empty :keyword:`!return` statement is a syntax error in an asynchronous generator function. .. _yield: -The :keyword:`yield` statement -============================== +The :keyword:`!yield` statement +=============================== .. index:: statement: yield @@ -546,11 +546,11 @@ For full details of :keyword:`yield` semantics, refer to the .. _raise: -The :keyword:`raise` statement -============================== +The :keyword:`!raise` statement +=============================== .. index:: - statement: raise + ! statement: raise single: exception pair: raising; exception single: __traceback__ (exception attribute) @@ -649,11 +649,11 @@ and information about handling exceptions is in section :ref:`try`. .. _break: -The :keyword:`break` statement -============================== +The :keyword:`!break` statement +=============================== .. index:: - statement: break + ! statement: break statement: for statement: while pair: loop; statement @@ -668,7 +668,7 @@ that loop. .. index:: keyword: else pair: loop control; target -It terminates the nearest enclosing loop, skipping the optional :keyword:`else` +It terminates the nearest enclosing loop, skipping the optional :keyword:`!else` clause if the loop has one. If a :keyword:`for` loop is terminated by :keyword:`break`, the loop control @@ -677,17 +677,17 @@ target keeps its current value. .. index:: keyword: finally When :keyword:`break` passes control out of a :keyword:`try` statement with a -:keyword:`finally` clause, that :keyword:`finally` clause is executed before +:keyword:`finally` clause, that :keyword:`!finally` clause is executed before really leaving the loop. .. _continue: -The :keyword:`continue` statement -================================= +The :keyword:`!continue` statement +================================== .. index:: - statement: continue + ! statement: continue statement: for statement: while pair: loop; statement @@ -701,18 +701,18 @@ The :keyword:`continue` statement that loop. It continues with the next cycle of the nearest enclosing loop. When :keyword:`continue` passes control out of a :keyword:`try` statement with a -:keyword:`finally` clause, that :keyword:`finally` clause is executed before +:keyword:`finally` clause, that :keyword:`!finally` clause is executed before really starting the next loop cycle. .. _import: .. _from: -The :keyword:`import` statement -=============================== +The :keyword:`!import` statement +================================ .. index:: - statement: import + ! statement: import single: module; importing pair: name; binding keyword: from @@ -755,8 +755,8 @@ available in the local namespace in one of three ways: .. index:: single: as; import statement -* If the module name is followed by :keyword:`as`, then the name - following :keyword:`as` is bound directly to the imported module. +* If the module name is followed by :keyword:`!as`, then the name + following :keyword:`!as` is bound directly to the imported module. * If no other name is specified, and the module being imported is a top level module, the module's name is bound in the local namespace as a reference to the imported module @@ -781,7 +781,7 @@ The :keyword:`from` form uses a slightly more complex process: check the imported module again for that attribute #. if the attribute is not found, :exc:`ImportError` is raised. #. otherwise, a reference to that value is stored in the local namespace, - using the name in the :keyword:`as` clause if it is present, + using the name in the :keyword:`!as` clause if it is present, otherwise using the attribute name Examples:: @@ -922,11 +922,11 @@ after the script is executed. .. _global: -The :keyword:`global` statement -=============================== +The :keyword:`!global` statement +================================ .. index:: - statement: global + ! statement: global triple: global; name; binding single: , (comma); identifier list @@ -936,11 +936,11 @@ The :keyword:`global` statement The :keyword:`global` statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals. It would be impossible to assign to a global variable without -:keyword:`global`, although free variables may refer to globals without being +:keyword:`!global`, although free variables may refer to globals without being declared global. Names listed in a :keyword:`global` statement must not be used in the same code -block textually preceding that :keyword:`global` statement. +block textually preceding that :keyword:`!global` statement. Names listed in a :keyword:`global` statement must not be defined as formal parameters or in a :keyword:`for` loop control target, :keyword:`class` @@ -959,18 +959,18 @@ annotation. builtin: compile **Programmer's note:** :keyword:`global` is a directive to the parser. It -applies only to code parsed at the same time as the :keyword:`global` statement. -In particular, a :keyword:`global` statement contained in a string or code +applies only to code parsed at the same time as the :keyword:`!global` statement. +In particular, a :keyword:`!global` statement contained in a string or code object supplied to the built-in :func:`exec` function does not affect the code block *containing* the function call, and code contained in such a string is -unaffected by :keyword:`global` statements in the code containing the function +unaffected by :keyword:`!global` statements in the code containing the function call. The same applies to the :func:`eval` and :func:`compile` functions. .. _nonlocal: -The :keyword:`nonlocal` statement -================================= +The :keyword:`!nonlocal` statement +================================== .. index:: statement: nonlocal single: , (comma); identifier list diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index edbc43fb1b5d..7619ccbc1f36 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -783,7 +783,7 @@ calls :func:`iter` on the container object. The function returns an iterator object that defines the method :meth:`~iterator.__next__` which accesses elements in the container one at a time. When there are no more elements, :meth:`~iterator.__next__` raises a :exc:`StopIteration` exception which tells the -:keyword:`for` loop to terminate. You can call the :meth:`~iterator.__next__` method +:keyword:`!for` loop to terminate. You can call the :meth:`~iterator.__next__` method using the :func:`next` built-in function; this example shows how it all works:: >>> s = 'abc' diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index bf6fbe21a7f7..08eaa66a4069 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -10,8 +10,8 @@ control flow statements known from other languages, with some twists. .. _tut-if: -:keyword:`if` Statements -======================== +:keyword:`!if` Statements +========================= Perhaps the most well-known statement type is the :keyword:`if` statement. For example:: @@ -31,16 +31,16 @@ example:: More There can be zero or more :keyword:`elif` parts, and the :keyword:`else` part is -optional. The keyword ':keyword:`elif`' is short for 'else if', and is useful -to avoid excessive indentation. An :keyword:`if` ... :keyword:`elif` ... -:keyword:`elif` ... sequence is a substitute for the ``switch`` or +optional. The keyword ':keyword:`!elif`' is short for 'else if', and is useful +to avoid excessive indentation. An :keyword:`!if` ... :keyword:`!elif` ... +:keyword:`!elif` ... sequence is a substitute for the ``switch`` or ``case`` statements found in other languages. .. _tut-for: -:keyword:`for` Statements -========================= +:keyword:`!for` Statements +========================== .. index:: statement: for @@ -48,7 +48,7 @@ to avoid excessive indentation. An :keyword:`if` ... :keyword:`elif` ... The :keyword:`for` statement in Python differs a bit from what you may be used to in C or Pascal. Rather than always iterating over an arithmetic progression of numbers (like in Pascal), or giving the user the ability to define both the -iteration step and halting condition (as C), Python's :keyword:`for` statement +iteration step and halting condition (as C), Python's :keyword:`!for` statement iterates over the items of any sequence (a list or a string), in the order that they appear in the sequence. For example (no pun intended): @@ -154,13 +154,13 @@ Later we will see more functions that return iterables and take iterables as arg .. _tut-break: -:keyword:`break` and :keyword:`continue` Statements, and :keyword:`else` Clauses on Loops -========================================================================================= +:keyword:`!break` and :keyword:`!continue` Statements, and :keyword:`!else` Clauses on Loops +============================================================================================ The :keyword:`break` statement, like in C, breaks out of the innermost enclosing :keyword:`for` or :keyword:`while` loop. -Loop statements may have an ``else`` clause; it is executed when the loop +Loop statements may have an :keyword:`!else` clause; it is executed when the loop terminates through exhaustion of the list (with :keyword:`for`) or when the condition becomes false (with :keyword:`while`), but not when the loop is terminated by a :keyword:`break` statement. This is exemplified by the @@ -189,9 +189,9 @@ the :keyword:`for` loop, **not** the :keyword:`if` statement.) When used with a loop, the ``else`` clause has more in common with the ``else`` clause of a :keyword:`try` statement than it does that of -:keyword:`if` statements: a :keyword:`try` statement's ``else`` clause runs +:keyword:`if` statements: a :keyword:`!try` statement's ``else`` clause runs when no exception occurs, and a loop's ``else`` clause runs when no ``break`` -occurs. For more on the :keyword:`try` statement and exceptions, see +occurs. For more on the :keyword:`!try` statement and exceptions, see :ref:`tut-handling`. The :keyword:`continue` statement, also borrowed from C, continues with the next @@ -213,8 +213,8 @@ iteration of the loop:: .. _tut-pass: -:keyword:`pass` Statements -========================== +:keyword:`!pass` Statements +=========================== The :keyword:`pass` statement does nothing. It can be used when a statement is required syntactically but the program requires no action. For example:: @@ -231,7 +231,7 @@ This is commonly used for creating minimal classes:: Another place :keyword:`pass` can be used is as a place-holder for a function or conditional body when you are working on new code, allowing you to keep thinking -at a more abstract level. The :keyword:`pass` is silently ignored:: +at a more abstract level. The :keyword:`!pass` is silently ignored:: >>> def initlog(*args): ... pass # Remember to implement this! @@ -331,7 +331,7 @@ Fibonacci series, instead of printing it:: This example, as usual, demonstrates some new Python features: * The :keyword:`return` statement returns with a value from a function. - :keyword:`return` without an expression argument returns ``None``. Falling off + :keyword:`!return` without an expression argument returns ``None``. Falling off the end of a function also returns ``None``. * The statement ``result.append(a)`` calls a *method* of the list object diff --git a/Doc/tutorial/datastructures.rst b/Doc/tutorial/datastructures.rst index b291d11595a8..b4db3f015912 100644 --- a/Doc/tutorial/datastructures.rst +++ b/Doc/tutorial/datastructures.rst @@ -216,9 +216,9 @@ or, equivalently:: which is more concise and readable. A list comprehension consists of brackets containing an expression followed -by a :keyword:`for` clause, then zero or more :keyword:`for` or :keyword:`if` +by a :keyword:`!for` clause, then zero or more :keyword:`!for` or :keyword:`!if` clauses. The result will be a new list resulting from evaluating the expression -in the context of the :keyword:`for` and :keyword:`if` clauses which follow it. +in the context of the :keyword:`!for` and :keyword:`!if` clauses which follow it. For example, this listcomp combines the elements of two lists if they are not equal:: @@ -330,12 +330,12 @@ See :ref:`tut-unpacking-arguments` for details on the asterisk in this line. .. _tut-del: -The :keyword:`del` statement -============================ +The :keyword:`!del` statement +============================= There is a way to remove an item from a list given its index instead of its value: the :keyword:`del` statement. This differs from the :meth:`pop` method -which returns a value. The :keyword:`del` statement can also be used to remove +which returns a value. The :keyword:`!del` statement can also be used to remove slices from a list or clear the entire list (which we did earlier by assignment of an empty list to the slice). For example:: diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst index 957cbf962b20..4e287bbd8d29 100644 --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -114,7 +114,7 @@ The :keyword:`try` statement works as follows. A :keyword:`try` statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not -in other handlers of the same :keyword:`try` statement. An except clause may +in other handlers of the same :keyword:`!try` statement. An except clause may name multiple exceptions as a parenthesized tuple, for example:: ... except (RuntimeError, TypeError, NameError): @@ -180,10 +180,10 @@ example:: print(arg, 'has', len(f.readlines()), 'lines') f.close() -The use of the :keyword:`else` clause is better than adding additional code to +The use of the :keyword:`!else` clause is better than adding additional code to the :keyword:`try` clause because it avoids accidentally catching an exception -that wasn't raised by the code being protected by the :keyword:`try` ... -:keyword:`except` statement. +that wasn't raised by the code being protected by the :keyword:`!try` ... +:keyword:`!except` statement. When an exception occurs, it may have an associated value, also known as the exception's *argument*. The presence and type of the argument depend on the @@ -343,11 +343,11 @@ example:: A *finally clause* is always executed before leaving the :keyword:`try` statement, whether an exception has occurred or not. When an exception has -occurred in the :keyword:`try` clause and has not been handled by an -:keyword:`except` clause (or it has occurred in an :keyword:`except` or -:keyword:`else` clause), it is re-raised after the :keyword:`finally` clause has -been executed. The :keyword:`finally` clause is also executed "on the way out" -when any other clause of the :keyword:`try` statement is left via a +occurred in the :keyword:`!try` clause and has not been handled by an +:keyword:`except` clause (or it has occurred in an :keyword:`!except` or +:keyword:`!else` clause), it is re-raised after the :keyword:`finally` clause has +been executed. The :keyword:`!finally` clause is also executed "on the way out" +when any other clause of the :keyword:`!try` statement is left via a :keyword:`break`, :keyword:`continue` or :keyword:`return` statement. A more complicated example:: @@ -376,7 +376,7 @@ complicated example:: As you can see, the :keyword:`finally` clause is executed in any event. The :exc:`TypeError` raised by dividing two strings is not handled by the -:keyword:`except` clause and therefore re-raised after the :keyword:`finally` +:keyword:`except` clause and therefore re-raised after the :keyword:`!finally` clause has been executed. In real world applications, the :keyword:`finally` clause is useful for diff --git a/Doc/tutorial/inputoutput.rst b/Doc/tutorial/inputoutput.rst index 785de29ac9d8..79427860f518 100644 --- a/Doc/tutorial/inputoutput.rst +++ b/Doc/tutorial/inputoutput.rst @@ -317,7 +317,7 @@ reading and writing such files. It is good practice to use the :keyword:`with` keyword when dealing with file objects. The advantage is that the file is properly closed after its suite finishes, even if an exception is raised at some -point. Using :keyword:`with` is also much shorter than writing +point. Using :keyword:`!with` is also much shorter than writing equivalent :keyword:`try`\ -\ :keyword:`finally` blocks:: >>> with open('workfile') as f: diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst index 0aadad309a82..accc30649f24 100644 --- a/Doc/tutorial/modules.rst +++ b/Doc/tutorial/modules.rst @@ -112,8 +112,8 @@ Note that in general the practice of importing ``*`` from a module or package is frowned upon, since it often causes poorly readable code. However, it is okay to use it to save typing in interactive sessions. -If the module name is followed by :keyword:`as`, then the name -following :keyword:`as` is bound directly to the imported module. +If the module name is followed by :keyword:`!as`, then the name +following :keyword:`!as` is bound directly to the imported module. :: diff --git a/Doc/whatsnew/2.0.rst b/Doc/whatsnew/2.0.rst index 8c740eefb098..ace396b9d846 100644 --- a/Doc/whatsnew/2.0.rst +++ b/Doc/whatsnew/2.0.rst @@ -284,7 +284,7 @@ write the following to do it:: L) Because of Python's scoping rules, a default argument is used so that the -anonymous function created by the :keyword:`lambda` statement knows what +anonymous function created by the :keyword:`lambda` expression knows what substring is being searched for. List comprehensions make this cleaner:: sublist = [ s for s in L if string.find(s, S) != -1 ] @@ -296,11 +296,11 @@ List comprehensions have the form:: for exprN in sequenceN if condition ] -The :keyword:`for`...\ :keyword:`in` clauses contain the sequences to be +The :keyword:`!for`...\ :keyword:`!in` clauses contain the sequences to be iterated over. The sequences do not have to be the same length, because they are *not* iterated over in parallel, but from left to right; this is explained more clearly in the following paragraphs. The elements of the generated list -will be the successive values of *expression*. The final :keyword:`if` clause +will be the successive values of *expression*. The final :keyword:`!if` clause is optional; if present, *expression* is only evaluated and added to the result if *condition* is true. @@ -316,7 +316,7 @@ following Python code:: # the expression to the # resulting list. -This means that when there are multiple :keyword:`for`...\ :keyword:`in` +This means that when there are multiple :keyword:`!for`...\ :keyword:`!in` clauses, the resulting list will be equal to the product of the lengths of all the sequences. If you have two lists of length 3, the output list is 9 elements long:: @@ -541,8 +541,8 @@ true if *obj* is present in the sequence *seq*; Python computes this by simply trying every index of the sequence until either *obj* is found or an :exc:`IndexError` is encountered. Moshe Zadka contributed a patch which adds a :meth:`__contains__` magic method for providing a custom implementation for -:keyword:`in`. Additionally, new built-in objects written in C can define what -:keyword:`in` means for them via a new slot in the sequence protocol. +:keyword:`!in`. Additionally, new built-in objects written in C can define what +:keyword:`!in` means for them via a new slot in the sequence protocol. Earlier versions of Python used a recursive algorithm for deleting objects. Deeply nested data structures could cause the interpreter to fill up the C stack diff --git a/Doc/whatsnew/2.1.rst b/Doc/whatsnew/2.1.rst index 12c028f24180..8b1eac968e2b 100644 --- a/Doc/whatsnew/2.1.rst +++ b/Doc/whatsnew/2.1.rst @@ -52,7 +52,7 @@ The function :func:`g` will always raise a :exc:`NameError` exception, because the binding of the name ``g`` isn't in either its local namespace or in the module-level namespace. This isn't much of a problem in practice (how often do you recursively define interior functions like this?), but this also made using -the :keyword:`lambda` statement clumsier, and this was a problem in practice. +the :keyword:`lambda` expression clumsier, and this was a problem in practice. In code which uses :keyword:`lambda` you can often find local variables being copied by passing them as the default values of arguments. :: @@ -143,7 +143,7 @@ The syntax uses a ``from...import`` statement using the reserved module name While it looks like a normal :keyword:`import` statement, it's not; there are strict rules on where such a future statement can be put. They can only be at the top of a module, and must precede any Python code or regular -:keyword:`import` statements. This is because such statements can affect how +:keyword:`!import` statements. This is because such statements can affect how the Python bytecode compiler parses code and generates bytecode, so they must precede any statement that will result in bytecodes being produced. diff --git a/Doc/whatsnew/2.2.rst b/Doc/whatsnew/2.2.rst index c2ae866b73b8..b4cd4341f4be 100644 --- a/Doc/whatsnew/2.2.rst +++ b/Doc/whatsnew/2.2.rst @@ -121,7 +121,7 @@ added so if no built-in type is suitable, you can just subclass This means that :keyword:`class` statements that don't have any base classes are always classic classes in Python 2.2. (Actually you can also change this by setting a module-level variable named :attr:`__metaclass__` --- see :pep:`253` -for the details --- but it's easier to just subclass :keyword:`object`.) +for the details --- but it's easier to just subclass :class:`object`.) The type objects for the built-in types are available as built-ins, named using a clever trick. Python has always had built-in functions named :func:`int`, @@ -560,7 +560,7 @@ Here's the simplest example of a generator function:: yield i A new keyword, :keyword:`yield`, was introduced for generators. Any function -containing a :keyword:`yield` statement is a generator function; this is +containing a :keyword:`!yield` statement is a generator function; this is detected by Python's bytecode compiler which compiles the function specially as a result. Because a new keyword was introduced, generators must be explicitly enabled in a module by including a ``from __future__ import generators`` @@ -571,14 +571,14 @@ When you call a generator function, it doesn't return a single value; instead it returns a generator object that supports the iterator protocol. On executing the :keyword:`yield` statement, the generator outputs the value of ``i``, similar to a :keyword:`return` statement. The big difference between -:keyword:`yield` and a :keyword:`return` statement is that on reaching a -:keyword:`yield` the generator's state of execution is suspended and local +:keyword:`!yield` and a :keyword:`!return` statement is that on reaching a +:keyword:`!yield` the generator's state of execution is suspended and local variables are preserved. On the next call to the generator's ``next()`` method, -the function will resume executing immediately after the :keyword:`yield` -statement. (For complicated reasons, the :keyword:`yield` statement isn't -allowed inside the :keyword:`try` block of a +the function will resume executing immediately after the :keyword:`!yield` +statement. (For complicated reasons, the :keyword:`!yield` statement isn't +allowed inside the :keyword:`!try` block of a :keyword:`try`...\ :keyword:`finally` statement; read :pep:`255` for a full -explanation of the interaction between :keyword:`yield` and exceptions.) +explanation of the interaction between :keyword:`!yield` and exceptions.) Here's a sample usage of the :func:`generate_ints` generator:: @@ -602,7 +602,7 @@ generate_ints(3)``. Inside a generator function, the :keyword:`return` statement can only be used without a value, and signals the end of the procession of values; afterwards the -generator cannot return any further values. :keyword:`return` with a value, such +generator cannot return any further values. :keyword:`!return` with a value, such as ``return 5``, is a syntax error inside a generator function. The end of the generator's results can also be indicated by raising :exc:`StopIteration` manually, or by just letting the flow of execution fall off the bottom of the @@ -863,8 +863,8 @@ The function :func:`g` will always raise a :exc:`NameError` exception, because the binding of the name ``g`` isn't in either its local namespace or in the module-level namespace. This isn't much of a problem in practice (how often do you recursively define interior functions like this?), but this also made using -the :keyword:`lambda` statement clumsier, and this was a problem in practice. -In code which uses :keyword:`lambda` you can often find local variables being +the :keyword:`lambda` expression clumsier, and this was a problem in practice. +In code which uses :keyword:`!lambda` you can often find local variables being copied by passing them as the default values of arguments. :: def find(self, name): diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index 37ba7c09c917..dac0e6364928 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -149,7 +149,7 @@ Here's the simplest example of a generator function:: yield i A new keyword, :keyword:`yield`, was introduced for generators. Any function -containing a :keyword:`yield` statement is a generator function; this is +containing a :keyword:`!yield` statement is a generator function; this is detected by Python's bytecode compiler which compiles the function specially as a result. @@ -157,14 +157,14 @@ When you call a generator function, it doesn't return a single value; instead it returns a generator object that supports the iterator protocol. On executing the :keyword:`yield` statement, the generator outputs the value of ``i``, similar to a :keyword:`return` statement. The big difference between -:keyword:`yield` and a :keyword:`return` statement is that on reaching a -:keyword:`yield` the generator's state of execution is suspended and local +:keyword:`!yield` and a :keyword:`!return` statement is that on reaching a +:keyword:`!yield` the generator's state of execution is suspended and local variables are preserved. On the next call to the generator's ``.next()`` method, the function will resume executing immediately after the -:keyword:`yield` statement. (For complicated reasons, the :keyword:`yield` +:keyword:`!yield` statement. (For complicated reasons, the :keyword:`!yield` statement isn't allowed inside the :keyword:`try` block of a -:keyword:`try`...\ :keyword:`finally` statement; read :pep:`255` for a full -explanation of the interaction between :keyword:`yield` and exceptions.) +:keyword:`!try`...\ :keyword:`!finally` statement; read :pep:`255` for a full +explanation of the interaction between :keyword:`!yield` and exceptions.) Here's a sample usage of the :func:`generate_ints` generator:: @@ -188,7 +188,7 @@ generate_ints(3)``. Inside a generator function, the :keyword:`return` statement can only be used without a value, and signals the end of the procession of values; afterwards the -generator cannot return any further values. :keyword:`return` with a value, such +generator cannot return any further values. :keyword:`!return` with a value, such as ``return 5``, is a syntax error inside a generator function. The end of the generator's results can also be indicated by raising :exc:`StopIteration` manually, or by just letting the flow of execution fall off the bottom of the @@ -589,7 +589,7 @@ strict language such as Pascal would also prevent you performing arithmetic with Booleans, and would require that the expression in an :keyword:`if` statement always evaluate to a Boolean result. Python is not this strict and never will be, as :pep:`285` explicitly says. This means you can still use any expression -in an :keyword:`if` statement, even ones that evaluate to a list or tuple or +in an :keyword:`!if` statement, even ones that evaluate to a list or tuple or some random object. The Boolean type is a subclass of the :class:`int` class so that arithmetic using a Boolean still works. :: diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index f803a29ccefd..4e85abaea755 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -370,7 +370,7 @@ PEP 341: Unified try/except/finally Until Python 2.5, the :keyword:`try` statement came in two flavours. You could use a :keyword:`finally` block to ensure that code is always executed, or one or more :keyword:`except` blocks to catch specific exceptions. You couldn't -combine both :keyword:`except` blocks and a :keyword:`finally` block, because +combine both :keyword:`!except` blocks and a :keyword:`!finally` block, because generating the right bytecode for the combined version was complicated and it wasn't clear what the semantics of the combined statement should be. @@ -435,10 +435,10 @@ When you call ``counter(10)``, the result is an iterator that returns the values from 0 up to 9. On encountering the :keyword:`yield` statement, the iterator returns the provided value and suspends the function's execution, preserving the local variables. Execution resumes on the following call to the iterator's -:meth:`next` method, picking up after the :keyword:`yield` statement. +:meth:`next` method, picking up after the :keyword:`!yield` statement. In Python 2.3, :keyword:`yield` was a statement; it didn't return any value. In -2.5, :keyword:`yield` is now an expression, returning a value that can be +2.5, :keyword:`!yield` is now an expression, returning a value that can be assigned to a variable or otherwise operated on:: val = (yield i) @@ -458,7 +458,7 @@ expression on the right-hand side of an assignment. This means you can write Values are sent into a generator by calling its ``send(value)`` method. The generator's code is then resumed and the :keyword:`yield` expression returns the specified *value*. If the regular :meth:`next` method is called, the -:keyword:`yield` returns :const:`None`. +:keyword:`!yield` returns :const:`None`. Here's the previous example, modified to allow changing the value of the internal counter. :: @@ -644,7 +644,7 @@ Writing Context Managers ------------------------ Under the hood, the ':keyword:`with`' statement is fairly complicated. Most -people will only use ':keyword:`with`' in company with existing objects and +people will only use ':keyword:`!with`' in company with existing objects and don't need to know these details, so you can skip the rest of this section if you like. Authors of new objects will need to understand the details of the underlying implementation and should keep reading. @@ -750,9 +750,9 @@ generator function instead of defining a new class. The generator should yield exactly one value. The code up to the :keyword:`yield` will be executed as the :meth:`__enter__` method, and the value yielded will be the method's return value that will get bound to the variable in the ':keyword:`with`' statement's -:keyword:`as` clause, if any. The code after the :keyword:`yield` will be +:keyword:`!as` clause, if any. The code after the :keyword:`yield` will be executed in the :meth:`__exit__` method. Any exception raised in the block will -be raised by the :keyword:`yield` statement. +be raised by the :keyword:`!yield` statement. Our database example from the previous section could be written using this decorator as:: @@ -776,7 +776,7 @@ decorator as:: The :mod:`contextlib` module also has a ``nested(mgr1, mgr2, ...)`` function that combines a number of context managers so you don't need to write nested -':keyword:`with`' statements. In this example, the single ':keyword:`with`' +':keyword:`with`' statements. In this example, the single ':keyword:`!with`' statement both starts a database transaction and acquires a thread lock:: lock = threading.Lock() diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index ccfdbdce03e9..512b8edb357a 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -250,10 +250,10 @@ PEP 343: The 'with' statement The previous version, Python 2.5, added the ':keyword:`with`' statement as an optional feature, to be enabled by a ``from __future__ import with_statement`` directive. In 2.6 the statement no longer needs to -be specially enabled; this means that :keyword:`with` is now always a +be specially enabled; this means that :keyword:`!with` is now always a keyword. The rest of this section is a copy of the corresponding section from the "What's New in Python 2.5" document; if you're -familiar with the ':keyword:`with`' statement +familiar with the ':keyword:`!with`' statement from Python 2.5, you can skip this section. The ':keyword:`with`' statement clarifies code that previously would use @@ -331,7 +331,7 @@ Writing Context Managers ------------------------ Under the hood, the ':keyword:`with`' statement is fairly complicated. Most -people will only use ':keyword:`with`' in company with existing objects and +people will only use ':keyword:`!with`' in company with existing objects and don't need to know these details, so you can skip the rest of this section if you like. Authors of new objects will need to understand the details of the underlying implementation and should keep reading. @@ -438,9 +438,9 @@ generator function instead of defining a new class. The generator should yield exactly one value. The code up to the :keyword:`yield` will be executed as the :meth:`__enter__` method, and the value yielded will be the method's return value that will get bound to the variable in the ':keyword:`with`' statement's -:keyword:`as` clause, if any. The code after the :keyword:`yield` will be +:keyword:`!as` clause, if any. The code after the :keyword:`!yield` will be executed in the :meth:`__exit__` method. Any exception raised in the block will -be raised by the :keyword:`yield` statement. +be raised by the :keyword:`!yield` statement. Using this decorator, our database example from the previous section could be written as:: @@ -464,7 +464,7 @@ could be written as:: The :mod:`contextlib` module also has a ``nested(mgr1, mgr2, ...)`` function that combines a number of context managers so you don't need to write nested -':keyword:`with`' statements. In this example, the single ':keyword:`with`' +':keyword:`with`' statements. In this example, the single ':keyword:`!with`' statement both starts a database transaction and acquires a thread lock:: lock = threading.Lock() @@ -1684,7 +1684,7 @@ Some smaller changes made to the core Python language are: * An obscure change: when you use the :func:`locals` function inside a :keyword:`class` statement, the resulting dictionary no longer returns free variables. (Free variables, in this case, are variables referenced in the - :keyword:`class` statement that aren't attributes of the class.) + :keyword:`!class` statement that aren't attributes of the class.) .. ====================================================================== diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index fd59c1611fcd..9f8d9f202c43 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -708,7 +708,7 @@ Some smaller changes made to the core Python language are: * The :keyword:`with` statement can now use multiple context managers in one statement. Context managers are processed from left to right - and each one is treated as beginning a new :keyword:`with` statement. + and each one is treated as beginning a new :keyword:`!with` statement. This means that:: with A() as a, B() as b: @@ -844,7 +844,7 @@ Some smaller changes made to the core Python language are: * The :keyword:`import` statement will no longer try an absolute import if a relative import (e.g. ``from .os import sep``) fails. This - fixes a bug, but could possibly break certain :keyword:`import` + fixes a bug, but could possibly break certain :keyword:`!import` statements that were only working by accident. (Fixed by Meador Inge; :issue:`7902`.) @@ -1158,7 +1158,7 @@ changes, or look through the Subversion logs for all the details. * Deprecated function: :func:`contextlib.nested`, which allows handling more than one context manager with a single :keyword:`with` - statement, has been deprecated, because the :keyword:`with` statement + statement, has been deprecated, because the :keyword:`!with` statement now supports multiple context managers. * The :mod:`cookielib` module now ignores cookies that have an invalid diff --git a/Doc/whatsnew/3.0.rst b/Doc/whatsnew/3.0.rst index 5ecf2ebfe79c..880958d3edb9 100644 --- a/Doc/whatsnew/3.0.rst +++ b/Doc/whatsnew/3.0.rst @@ -373,7 +373,7 @@ New Syntax * :pep:`3104`: :keyword:`nonlocal` statement. Using ``nonlocal x`` you can now assign directly to a variable in an outer (but - non-global) scope. :keyword:`nonlocal` is a new reserved word. + non-global) scope. :keyword:`!nonlocal` is a new reserved word. * :pep:`3132`: Extended Iterable Unpacking. You can now write things like ``a, b, *rest = some_sequence``. And even ``*rest, a = @@ -408,14 +408,14 @@ Changed Syntax * :pep:`3109` and :pep:`3134`: new :keyword:`raise` statement syntax: :samp:`raise [{expr} [from {expr}]]`. See below. -* :keyword:`as` and :keyword:`with` are now reserved words. (Since +* :keyword:`!as` and :keyword:`with` are now reserved words. (Since 2.6, actually.) * ``True``, ``False``, and ``None`` are reserved words. (2.6 partially enforced the restrictions on ``None`` already.) * Change from :keyword:`except` *exc*, *var* to - :keyword:`except` *exc* :keyword:`as` *var*. See :pep:`3110`. + :keyword:`!except` *exc* :keyword:`!as` *var*. See :pep:`3110`. * :pep:`3115`: New Metaclass Syntax. Instead of:: @@ -507,9 +507,9 @@ consulted for longer descriptions. * :ref:`pep-3105`. This is now a standard feature and no longer needs to be imported from :mod:`__future__`. More details were given above. -* :ref:`pep-3110`. The :keyword:`except` *exc* :keyword:`as` *var* - syntax is now standard and :keyword:`except` *exc*, *var* is no - longer supported. (Of course, the :keyword:`as` *var* part is still +* :ref:`pep-3110`. The :keyword:`except` *exc* :keyword:`!as` *var* + syntax is now standard and :keyword:`!except` *exc*, *var* is no + longer supported. (Of course, the :keyword:`!as` *var* part is still optional.) * :ref:`pep-3112`. The ``b"..."`` string literal notation (and its diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index 10996233ff6e..845e327cabc8 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -1352,7 +1352,7 @@ shelve ------ :class:`~shelve.Shelf` instances may now be used in :keyword:`with` statements, -and will be automatically closed at the end of the :keyword:`with` block. +and will be automatically closed at the end of the :keyword:`!with` block. (Contributed by Filip Gruszczy?ski in :issue:`13896`.) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 3e94a619b45f..93d3e62b75d2 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1896,7 +1896,7 @@ Deprecated Python Behavior Yield expressions (both ``yield`` and ``yield from`` clauses) are now deprecated in comprehensions and generator expressions (aside from the iterable expression -in the leftmost :keyword:`for` clause). This ensures that comprehensions +in the leftmost :keyword:`!for` clause). This ensures that comprehensions always immediately return a container of the appropriate type (rather than potentially returning a :term:`generator iterator` object), while generator expressions won't attempt to interleave their implicit output with the output diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index f4e6f646327e..2d45e7e94deb 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -415,7 +415,7 @@ Changes in Python behavior * Yield expressions (both ``yield`` and ``yield from`` clauses) are now disallowed in comprehensions and generator expressions (aside from the iterable expression - in the leftmost :keyword:`for` clause). + in the leftmost :keyword:`!for` clause). (Contributed by Serhiy Storchaka in :issue:`10544`.) From webhook-mailer at python.org Wed Dec 19 02:28:17 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 19 Dec 2018 07:28:17 -0000 Subject: [Python-checkins] [3.7] bpo-35506: Remove redundant and incorrect links from keywords. (GH-11174). (GH-11232) Message-ID: https://github.com/python/cpython/commit/1e47fbcf78c4d66fbe1fc7b4ea91a6b147ff83d2 commit: 1e47fbcf78c4d66fbe1fc7b4ea91a6b147ff83d2 branch: 3.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-19T09:28:12+02:00 summary: [3.7] bpo-35506: Remove redundant and incorrect links from keywords. (GH-11174). (GH-11232) (cherry picked from commit 2b57c43f21f891df4c6f2294a3b9e1b9029a16b6) files: M Doc/faq/programming.rst M Doc/glossary.rst M Doc/howto/functional.rst M Doc/library/aifc.rst M Doc/library/contextlib.rst M Doc/library/fileinput.rst M Doc/library/functions.rst M Doc/library/imaplib.rst M Doc/library/imp.rst M Doc/library/importlib.rst M Doc/library/io.rst M Doc/library/parser.rst M Doc/library/pickle.rst M Doc/library/smtplib.rst M Doc/library/socketserver.rst M Doc/library/stdtypes.rst M Doc/library/telnetlib.rst M Doc/library/tempfile.rst M Doc/library/threading.rst M Doc/library/wave.rst M Doc/library/xml.dom.minidom.rst M Doc/library/zipfile.rst M Doc/reference/compound_stmts.rst M Doc/reference/datamodel.rst M Doc/reference/executionmodel.rst M Doc/reference/expressions.rst M Doc/reference/import.rst M Doc/reference/simple_stmts.rst M Doc/tutorial/classes.rst M Doc/tutorial/controlflow.rst M Doc/tutorial/datastructures.rst M Doc/tutorial/errors.rst M Doc/tutorial/inputoutput.rst M Doc/tutorial/modules.rst M Doc/whatsnew/2.0.rst M Doc/whatsnew/2.1.rst M Doc/whatsnew/2.2.rst M Doc/whatsnew/2.3.rst M Doc/whatsnew/2.5.rst M Doc/whatsnew/2.6.rst M Doc/whatsnew/2.7.rst M Doc/whatsnew/3.0.rst M Doc/whatsnew/3.4.rst M Doc/whatsnew/3.7.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index fd720c1a304b..047812e6e036 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -738,7 +738,7 @@ Is it possible to write obfuscated one-liners in Python? -------------------------------------------------------- Yes. Usually this is done by nesting :keyword:`lambda` within -:keyword:`lambda`. See the following three examples, due to Ulf Bartelt:: +:keyword:`!lambda`. See the following three examples, due to Ulf Bartelt:: from functools import reduce diff --git a/Doc/glossary.rst b/Doc/glossary.rst index b8d98dd34c13..fb8ff2a7c656 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -332,7 +332,7 @@ Glossary names, attribute access, operators or function calls which all return a value. In contrast to many other languages, not all language constructs are expressions. There are also :term:`statement`\s which cannot be used - as expressions, such as :keyword:`if`. Assignments are also statements, + as expressions, such as :keyword:`while`. Assignments are also statements, not expressions. extension module @@ -444,8 +444,8 @@ Glossary generator expression An expression that returns an iterator. It looks like a normal expression - followed by a :keyword:`for` expression defining a loop variable, range, - and an optional :keyword:`if` expression. The combined expression + followed by a :keyword:`!for` clause defining a loop variable, range, + and an optional :keyword:`!if` clause. The combined expression generates values for an enclosing function:: >>> sum(i*i for i in range(10)) # sum of squares 0, 1, 4, ... 81 diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst index 2efe4537e288..f8f2aac70f9b 100644 --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -1108,7 +1108,7 @@ need to define a new function at all:: existing_files = filter(os.path.exists, file_list) If the function you need doesn't exist, you need to write it. One way to write -small functions is to use the :keyword:`lambda` statement. ``lambda`` takes a +small functions is to use the :keyword:`lambda` expression. ``lambda`` takes a number of parameters and an expression combining these parameters, and creates an anonymous function that returns the value of the expression:: diff --git a/Doc/library/aifc.rst b/Doc/library/aifc.rst index 970a7aeb98a7..7328907730fb 100644 --- a/Doc/library/aifc.rst +++ b/Doc/library/aifc.rst @@ -45,7 +45,7 @@ Module :mod:`aifc` defines the following function: time how many samples you are going to write in total and use :meth:`writeframesraw` and :meth:`setnframes`. The :func:`.open` function may be used in a :keyword:`with` statement. When - the :keyword:`with` block completes, the :meth:`~aifc.close` method is called. + the :keyword:`!with` block completes, the :meth:`~aifc.close` method is called. .. versionchanged:: 3.4 Support for the :keyword:`with` statement was added. diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index 930c97358e08..017a87a5648c 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -1,5 +1,5 @@ -:mod:`contextlib` --- Utilities for :keyword:`with`\ -statement contexts -======================================================================== +:mod:`!contextlib` --- Utilities for :keyword:`!with`\ -statement contexts +========================================================================== .. module:: contextlib :synopsis: Utilities for with-statement contexts. @@ -72,7 +72,7 @@ Functions and classes provided: The function being decorated must return a :term:`generator`-iterator when called. This iterator must yield exactly one value, which will be bound to - the targets in the :keyword:`with` statement's :keyword:`as` clause, if any. + the targets in the :keyword:`with` statement's :keyword:`!as` clause, if any. At the point where the generator yields, the block nested in the :keyword:`with` statement is executed. The generator is then resumed after the block is exited. @@ -82,9 +82,9 @@ Functions and classes provided: the error (if any), or ensure that some cleanup takes place. If an exception is trapped merely in order to log it or to perform some action (rather than to suppress it entirely), the generator must reraise that exception. Otherwise the - generator context manager will indicate to the :keyword:`with` statement that + generator context manager will indicate to the :keyword:`!with` statement that the exception has been handled, and execution will resume with the statement - immediately following the :keyword:`with` statement. + immediately following the :keyword:`!with` statement. :func:`contextmanager` uses :class:`ContextDecorator` so the context managers it creates can be used as decorators as well as in :keyword:`with` statements. @@ -346,7 +346,7 @@ Functions and classes provided: As the decorated function must be able to be called multiple times, the underlying context manager must support use in multiple :keyword:`with` statements. If this is not the case, then the original construct with the - explicit :keyword:`with` statement inside the function should be used. + explicit :keyword:`!with` statement inside the function should be used. .. versionadded:: 3.2 @@ -771,7 +771,7 @@ Reentrant context managers More sophisticated context managers may be "reentrant". These context managers can not only be used in multiple :keyword:`with` statements, -but may also be used *inside* a :keyword:`with` statement that is already +but may also be used *inside* a :keyword:`!with` statement that is already using the same context manager. :class:`threading.RLock` is an example of a reentrant context manager, as are diff --git a/Doc/library/fileinput.rst b/Doc/library/fileinput.rst index 5881fefe2932..1fc11ffce25e 100644 --- a/Doc/library/fileinput.rst +++ b/Doc/library/fileinput.rst @@ -63,7 +63,7 @@ The following function is the primary interface of this module: The :class:`FileInput` instance can be used as a context manager in the :keyword:`with` statement. In this example, *input* is closed after the - :keyword:`with` statement is exited, even if an exception occurs:: + :keyword:`!with` statement is exited, even if an exception occurs:: with fileinput.input(files=('spam.txt', 'eggs.txt')) as f: for line in f: @@ -155,7 +155,7 @@ available for subclassing as well: A :class:`FileInput` instance can be used as a context manager in the :keyword:`with` statement. In this example, *input* is closed after the - :keyword:`with` statement is exited, even if an exception occurs:: + :keyword:`!with` statement is exited, even if an exception occurs:: with FileInput(files=('spam.txt', 'eggs.txt')) as input: process(input) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index c0f4ffd2cb2a..5e37204a2446 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1655,7 +1655,7 @@ are always available. They are listed here in alphabetical order. This function is invoked by the :keyword:`import` statement. It can be replaced (by importing the :mod:`builtins` module and assigning to ``builtins.__import__``) in order to change semantics of the - :keyword:`import` statement, but doing so is **strongly** discouraged as it + :keyword:`!import` statement, but doing so is **strongly** discouraged as it is usually simpler to use import hooks (see :pep:`302`) to attain the same goals and does not cause issues with code which assumes the default import implementation is in use. Direct use of :func:`__import__` is also diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst index 040dab6e9f7a..d0709f8b678e 100644 --- a/Doc/library/imaplib.rst +++ b/Doc/library/imaplib.rst @@ -39,7 +39,7 @@ base class: The :class:`IMAP4` class supports the :keyword:`with` statement. When used like this, the IMAP4 ``LOGOUT`` command is issued automatically when the - :keyword:`with` statement exits. E.g.:: + :keyword:`!with` statement exits. E.g.:: >>> from imaplib import IMAP4 >>> with IMAP4("domain.org") as M: diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst index 1bd6f12b915f..04f207f58d38 100644 --- a/Doc/library/imp.rst +++ b/Doc/library/imp.rst @@ -179,7 +179,7 @@ This module provides an interface to the mechanisms used to implement the If a module imports objects from another module using :keyword:`from` ... :keyword:`import` ..., calling :func:`reload` for the other module does not redefine the objects imported from it --- one way around this is to re-execute - the :keyword:`from` statement, another is to use :keyword:`import` and qualified + the :keyword:`!from` statement, another is to use :keyword:`!import` and qualified names (*module*.*name*) instead. If a module instantiates instances of a class, reloading the module that defines diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 0bcfbb1c7263..3c9a99abf9a2 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1,5 +1,5 @@ -:mod:`importlib` --- The implementation of :keyword:`import` -============================================================ +:mod:`!importlib` --- The implementation of :keyword:`!import` +============================================================== .. module:: importlib :synopsis: The implementation of the import machinery. @@ -19,7 +19,7 @@ Introduction The purpose of the :mod:`importlib` package is two-fold. One is to provide the implementation of the :keyword:`import` statement (and thus, by extension, the :func:`__import__` function) in Python source code. This provides an -implementation of :keyword:`import` which is portable to any Python +implementation of :keyword:`!import` which is portable to any Python interpreter. This also provides an implementation which is easier to comprehend than one implemented in a programming language other than Python. @@ -197,7 +197,7 @@ Functions If a module imports objects from another module using :keyword:`from` ... :keyword:`import` ..., calling :func:`reload` for the other module does not redefine the objects imported from it --- one way around this is to - re-execute the :keyword:`from` statement, another is to use :keyword:`import` + re-execute the :keyword:`!from` statement, another is to use :keyword:`!import` and qualified names (*module.name*) instead. If a module instantiates instances of a class, reloading the module that diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 7068e68ad90f..473e8c8f82a1 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -249,7 +249,7 @@ I/O Base Classes :class:`IOBase` is also a context manager and therefore supports the :keyword:`with` statement. In this example, *file* is closed after the - :keyword:`with` statement's suite is finished---even if an exception occurs:: + :keyword:`!with` statement's suite is finished---even if an exception occurs:: with open('spam.txt', 'w') as file: file.write('Spam and eggs!') diff --git a/Doc/library/parser.rst b/Doc/library/parser.rst index c3b699a36024..a302681eca05 100644 --- a/Doc/library/parser.rst +++ b/Doc/library/parser.rst @@ -63,7 +63,7 @@ of the production as recognized in the input string: these are always sequences which have the same form as the parent. An important aspect of this structure which should be noted is that keywords used to identify the parent node type, such as the keyword :keyword:`if` in an :const:`if_stmt`, are included in the -node tree without any special treatment. For example, the :keyword:`if` keyword +node tree without any special treatment. For example, the :keyword:`!if` keyword is represented by the tuple ``(1, 'if')``, where ``1`` is the numeric value associated with all :const:`NAME` tokens, including variable and function names defined by the user. In an alternate form returned when line number information diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 36f66a159bf2..2d01d8b7ab25 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -923,7 +923,7 @@ The following example reads the resulting pickled data. :: .. [#] Don't confuse this with the :mod:`marshal` module .. [#] This is why :keyword:`lambda` functions cannot be pickled: all - :keyword:`lambda` functions share the same name: ````. + :keyword:`!lambda` functions share the same name: ````. .. [#] The exception raised will likely be an :exc:`ImportError` or an :exc:`AttributeError` but it could be something else. diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst index f59c18409411..2c3a5f0c6f72 100644 --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -46,7 +46,7 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). The :class:`SMTP` class supports the :keyword:`with` statement. When used like this, the SMTP ``QUIT`` command is issued automatically when the - :keyword:`with` statement exits. E.g.:: + :keyword:`!with` statement exits. E.g.:: >>> from smtplib import SMTP >>> with SMTP("domain.org") as smtp: diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst index 1b3062da6df9..7c8c8d52e03d 100644 --- a/Doc/library/socketserver.rst +++ b/Doc/library/socketserver.rst @@ -57,7 +57,7 @@ the server in a :keyword:`with` statement. Then call the :meth:`~BaseServer.handle_request` or :meth:`~BaseServer.serve_forever` method of the server object to process one or many requests. Finally, call :meth:`~BaseServer.server_close` -to close the socket (unless you used a :keyword:`with` statement). +to close the socket (unless you used a :keyword:`!with` statement). When inheriting from :class:`ThreadingMixIn` for threaded connection behavior, you should explicitly declare how you want your threads to behave on an abrupt diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 42c92824b25e..dd5a8ed35bf9 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -74,8 +74,8 @@ one of their operands.) .. _boolean: -Boolean Operations --- :keyword:`and`, :keyword:`or`, :keyword:`not` -==================================================================== +Boolean Operations --- :keyword:`!and`, :keyword:`!or`, :keyword:`!not` +======================================================================= .. index:: pair: Boolean; operations @@ -4393,7 +4393,7 @@ before the statement body is executed and exited when the statement ends: Enter the runtime context and return either this object or another object related to the runtime context. The value returned by this method is bound to - the identifier in the :keyword:`as` clause of :keyword:`with` statements using + the identifier in the :keyword:`!as` clause of :keyword:`with` statements using this context manager. An example of a context manager that returns itself is a :term:`file object`. @@ -4405,7 +4405,7 @@ before the statement body is executed and exited when the statement ends: decimal context to a copy of the original decimal context and then return the copy. This allows changes to be made to the current decimal context in the body of the :keyword:`with` statement without affecting code outside the - :keyword:`with` statement. + :keyword:`!with` statement. .. method:: contextmanager.__exit__(exc_type, exc_val, exc_tb) @@ -4417,10 +4417,10 @@ before the statement body is executed and exited when the statement ends: Returning a true value from this method will cause the :keyword:`with` statement to suppress the exception and continue execution with the statement immediately - following the :keyword:`with` statement. Otherwise the exception continues + following the :keyword:`!with` statement. Otherwise the exception continues propagating after this method has finished executing. Exceptions that occur during execution of this method will replace any exception that occurred in the - body of the :keyword:`with` statement. + body of the :keyword:`!with` statement. The exception passed in should never be reraised explicitly - instead, this method should return a false value to indicate that the method completed diff --git a/Doc/library/telnetlib.rst b/Doc/library/telnetlib.rst index f9c5153e3f58..4ba426425277 100644 --- a/Doc/library/telnetlib.rst +++ b/Doc/library/telnetlib.rst @@ -44,7 +44,7 @@ Character), EL (Erase Line), GA (Go Ahead), SB (Subnegotiation Begin). an empty string for other reasons. See the individual descriptions below. A :class:`Telnet` object is a context manager and can be used in a - :keyword:`with` statement. When the :keyword:`with` block ends, the + :keyword:`with` statement. When the :keyword:`!with` block ends, the :meth:`close` method is called:: >>> from telnetlib import Telnet diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst index 0d0da4d62e47..746adb1eee48 100644 --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -115,7 +115,7 @@ The module defines the following user-callable items: The directory name can be retrieved from the :attr:`name` attribute of the returned object. When the returned object is used as a context manager, the - :attr:`name` will be assigned to the target of the :keyword:`as` clause in + :attr:`name` will be assigned to the target of the :keyword:`!as` clause in the :keyword:`with` statement, if there is one. The directory can be explicitly cleaned up by calling the diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index a9d5268dd2b8..d7dbcb107dda 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -973,8 +973,8 @@ As an example, here is a simple way to synchronize a client and server thread:: .. _with-locks: -Using locks, conditions, and semaphores in the :keyword:`with` statement ------------------------------------------------------------------------- +Using locks, conditions, and semaphores in the :keyword:`!with` statement +------------------------------------------------------------------------- All of the objects provided by this module that have :meth:`acquire` and :meth:`release` methods can be used as context managers for a :keyword:`with` diff --git a/Doc/library/wave.rst b/Doc/library/wave.rst index 5c315c516175..60d19a8d5f7d 100644 --- a/Doc/library/wave.rst +++ b/Doc/library/wave.rst @@ -40,7 +40,7 @@ The :mod:`wave` module defines the following function and exception: the file object. The :func:`.open` function may be used in a :keyword:`with` statement. When - the :keyword:`with` block completes, the :meth:`Wave_read.close() + the :keyword:`!with` block completes, the :meth:`Wave_read.close() ` or :meth:`Wave_write.close() ` method is called. diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index 15b1cb0cbf78..a37caf62a374 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -126,7 +126,7 @@ module documentation. This section lists the differences between the API and You can avoid calling this method explicitly by using the :keyword:`with` statement. The following code will automatically unlink *dom* when the - :keyword:`with` block is exited:: + :keyword:`!with` block is exited:: with xml.dom.minidom.parse(datasource) as dom: ... # Work with dom. diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index e1f8b9a4a32a..6fb03a0e3145 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -175,7 +175,7 @@ ZipFile Objects ZipFile is also a context manager and therefore supports the :keyword:`with` statement. In the example, *myzip* is closed after the - :keyword:`with` statement's suite is finished---even if an exception occurs:: + :keyword:`!with` statement's suite is finished---even if an exception occurs:: with ZipFile('spam.zip', 'w') as myzip: myzip.write('eggs.txt') diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index f98785a5780d..db453fcdd1e0 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -78,11 +78,11 @@ on a separate line for clarity. .. _elif: .. _else: -The :keyword:`if` statement -=========================== +The :keyword:`!if` statement +============================ .. index:: - statement: if + ! statement: if keyword: elif keyword: else single: : (colon); compound statement @@ -103,14 +103,13 @@ false, the suite of the :keyword:`else` clause, if present, is executed. .. _while: -The :keyword:`while` statement -============================== +The :keyword:`!while` statement +=============================== .. index:: - statement: while + ! statement: while keyword: else pair: loop; statement - keyword: else single: : (colon); compound statement The :keyword:`while` statement is used for repeated execution as long as an @@ -122,7 +121,7 @@ expression is true: This repeatedly tests the expression and, if it is true, executes the first suite; if the expression is false (which may be the first time it is tested) the -suite of the :keyword:`else` clause, if present, is executed and the loop +suite of the :keyword:`!else` clause, if present, is executed and the loop terminates. .. index:: @@ -130,25 +129,22 @@ terminates. statement: continue A :keyword:`break` statement executed in the first suite terminates the loop -without executing the :keyword:`else` clause's suite. A :keyword:`continue` +without executing the :keyword:`!else` clause's suite. A :keyword:`continue` statement executed in the first suite skips the rest of the suite and goes back to testing the expression. .. _for: -The :keyword:`for` statement -============================ +The :keyword:`!for` statement +============================= .. index:: - statement: for + ! statement: for keyword: in keyword: else pair: target; list pair: loop; statement - keyword: in - keyword: else - pair: target; list object: sequence single: : (colon); compound statement @@ -166,16 +162,16 @@ by the iterator. Each item in turn is assigned to the target list using the standard rules for assignments (see :ref:`assignment`), and then the suite is executed. When the items are exhausted (which is immediately when the sequence is empty or an iterator raises a :exc:`StopIteration` exception), the suite in -the :keyword:`else` clause, if present, is executed, and the loop terminates. +the :keyword:`!else` clause, if present, is executed, and the loop terminates. .. index:: statement: break statement: continue A :keyword:`break` statement executed in the first suite terminates the loop -without executing the :keyword:`else` clause's suite. A :keyword:`continue` +without executing the :keyword:`!else` clause's suite. A :keyword:`continue` statement executed in the first suite skips the rest of the suite and continues -with the next item, or with the :keyword:`else` clause if there is no next +with the next item, or with the :keyword:`!else` clause if there is no next item. The for-loop makes assignments to the variables(s) in the target list. @@ -224,11 +220,11 @@ returns the list ``[0, 1, 2]``. .. _except: .. _finally: -The :keyword:`try` statement -============================ +The :keyword:`!try` statement +============================= .. index:: - statement: try + ! statement: try keyword: except keyword: finally keyword: else @@ -250,7 +246,7 @@ for a group of statements: The :keyword:`except` clause(s) specify one or more exception handlers. When no exception occurs in the :keyword:`try` clause, no exception handler is executed. -When an exception occurs in the :keyword:`try` suite, a search for an exception +When an exception occurs in the :keyword:`!try` suite, a search for an exception handler is started. This search inspects the except clauses in turn until one is found that matches the exception. An expression-less except clause, if present, must be last; it matches any exception. For an except clause with an @@ -270,7 +266,7 @@ as if the entire :keyword:`try` statement raised the exception). .. index:: single: as; except clause When a matching except clause is found, the exception is assigned to the target -specified after the :keyword:`as` keyword in that except clause, if present, and +specified after the :keyword:`!as` keyword in that except clause, if present, and the except clause's suite is executed. All except clauses must have an executable block. When the end of this block is reached, execution continues normally after the entire try statement. (This means that if two nested @@ -314,22 +310,22 @@ from a function that handled an exception. statement: break statement: continue -The optional :keyword:`else` clause is executed if the control flow leaves the +The optional :keyword:`!else` clause is executed if the control flow leaves the :keyword:`try` suite, no exception was raised, and no :keyword:`return`, :keyword:`continue`, or :keyword:`break` statement was executed. Exceptions in -the :keyword:`else` clause are not handled by the preceding :keyword:`except` +the :keyword:`!else` clause are not handled by the preceding :keyword:`except` clauses. .. index:: keyword: finally If :keyword:`finally` is present, it specifies a 'cleanup' handler. The :keyword:`try` clause is executed, including any :keyword:`except` and -:keyword:`else` clauses. If an exception occurs in any of the clauses and is -not handled, the exception is temporarily saved. The :keyword:`finally` clause +:keyword:`!else` clauses. If an exception occurs in any of the clauses and is +not handled, the exception is temporarily saved. The :keyword:`!finally` clause is executed. If there is a saved exception it is re-raised at the end of the -:keyword:`finally` clause. If the :keyword:`finally` clause raises another +:keyword:`!finally` clause. If the :keyword:`!finally` clause raises another exception, the saved exception is set as the context of the new exception. -If the :keyword:`finally` clause executes a :keyword:`return` or :keyword:`break` +If the :keyword:`!finally` clause executes a :keyword:`return` or :keyword:`break` statement, the saved exception is discarded:: >>> def f(): @@ -350,15 +346,15 @@ the :keyword:`finally` clause. statement: continue When a :keyword:`return`, :keyword:`break` or :keyword:`continue` statement is -executed in the :keyword:`try` suite of a :keyword:`try`...\ :keyword:`finally` +executed in the :keyword:`try` suite of a :keyword:`!try`...\ :keyword:`!finally` statement, the :keyword:`finally` clause is also executed 'on the way out.' A -:keyword:`continue` statement is illegal in the :keyword:`finally` clause. (The +:keyword:`continue` statement is illegal in the :keyword:`!finally` clause. (The reason is a problem with the current implementation --- this restriction may be lifted in the future). The return value of a function is determined by the last :keyword:`return` statement executed. Since the :keyword:`finally` clause always executes, a -:keyword:`return` statement executed in the :keyword:`finally` clause will +:keyword:`!return` statement executed in the :keyword:`!finally` clause will always be the last one executed:: >>> def foo(): @@ -378,11 +374,11 @@ may be found in section :ref:`raise`. .. _with: .. _as: -The :keyword:`with` statement -============================= +The :keyword:`!with` statement +============================== .. index:: - statement: with + ! statement: with keyword: as single: as; with statement single: , (comma); with statement @@ -594,7 +590,7 @@ name), for immediate use in expressions. This uses lambda expressions, describe section :ref:`lambda`. Note that the lambda expression is merely a shorthand for a simplified function definition; a function defined in a ":keyword:`def`" statement can be passed around or assigned to another name just like a function -defined by a lambda expression. The ":keyword:`def`" form is actually more powerful +defined by a lambda expression. The ":keyword:`!def`" form is actually more powerful since it allows the execution of multiple statements and annotations. **Programmer's note:** Functions are first-class objects. A "``def``" statement @@ -757,8 +753,8 @@ An example of a coroutine function:: .. index:: statement: async for .. _`async for`: -The :keyword:`async for` statement ----------------------------------- +The :keyword:`!async for` statement +----------------------------------- .. productionlist:: async_for_stmt: "async" `for_stmt` @@ -801,8 +797,8 @@ body of a coroutine function. .. index:: statement: async with .. _`async with`: -The :keyword:`async with` statement ------------------------------------ +The :keyword:`!async with` statement +------------------------------------ .. productionlist:: async_with_stmt: "async" `with_stmt` diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index e6fa8a0d2a6f..f31ddb15e5cb 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -620,7 +620,7 @@ Callable types called, always returns an iterator object which can be used to execute the body of the function: calling the iterator's :meth:`iterator.__next__` method will cause the function to execute until it provides a value - using the :keyword:`yield` statement. When the function executes a + using the :keyword:`!yield` statement. When the function executes a :keyword:`return` statement or falls off the end, a :exc:`StopIteration` exception is raised and the iterator will have reached the end of the set of values to be returned. @@ -700,7 +700,7 @@ Modules Modules are a basic organizational unit of Python code, and are created by the :ref:`import system ` as invoked either by the - :keyword:`import` statement (see :keyword:`import`), or by calling + :keyword:`import` statement, or by calling functions such as :func:`importlib.import_module` and built-in :func:`__import__`. A module object has a namespace implemented by a dictionary object (this is the dictionary referenced by the ``__globals__`` @@ -2424,7 +2424,7 @@ A :dfn:`context manager` is an object that defines the runtime context to be established when executing a :keyword:`with` statement. The context manager handles the entry into, and the exit from, the desired runtime context for the execution of the block of code. Context managers are normally invoked using the -:keyword:`with` statement (described in section :ref:`with`), but can also be +:keyword:`!with` statement (described in section :ref:`with`), but can also be used by directly invoking their methods. .. index:: @@ -2441,7 +2441,7 @@ For more information on context managers, see :ref:`typecontextmanager`. Enter the runtime context related to this object. The :keyword:`with` statement will bind this method's return value to the target(s) specified in the - :keyword:`as` clause of the statement, if any. + :keyword:`!as` clause of the statement, if any. .. method:: object.__exit__(self, exc_type, exc_value, traceback) diff --git a/Doc/reference/executionmodel.rst b/Doc/reference/executionmodel.rst index 1a69e972f2cb..ba7130d63621 100644 --- a/Doc/reference/executionmodel.rst +++ b/Doc/reference/executionmodel.rst @@ -58,8 +58,8 @@ The following constructs bind names: formal parameters to functions, :keyword:`import` statements, class and function definitions (these bind the class or function name in the defining block), and targets that are identifiers if occurring in an assignment, :keyword:`for` loop header, or after -:keyword:`as` in a :keyword:`with` statement or :keyword:`except` clause. -The :keyword:`import` statement +:keyword:`!as` in a :keyword:`with` statement or :keyword:`except` clause. +The :keyword:`!import` statement of the form ``from ... import *`` binds all names defined in the imported module, except those beginning with an underscore. This form may only be used at the module level. @@ -123,7 +123,7 @@ namespace. Names are resolved in the top-level namespace by searching the global namespace, i.e. the namespace of the module containing the code block, and the builtins namespace, the namespace of the module :mod:`builtins`. The global namespace is searched first. If the name is not found there, the -builtins namespace is searched. The :keyword:`global` statement must precede +builtins namespace is searched. The :keyword:`!global` statement must precede all uses of the name. The :keyword:`global` statement has the same scope as a name binding operation diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 3e7db901b849..d7da7ad4b8b2 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -185,20 +185,20 @@ Common syntax elements for comprehensions are: comp_if: "if" `expression_nocond` [`comp_iter`] The comprehension consists of a single expression followed by at least one -:keyword:`for` clause and zero or more :keyword:`for` or :keyword:`if` clauses. +:keyword:`!for` clause and zero or more :keyword:`!for` or :keyword:`!if` clauses. In this case, the elements of the new container are those that would be produced -by considering each of the :keyword:`for` or :keyword:`if` clauses a block, +by considering each of the :keyword:`!for` or :keyword:`!if` clauses a block, nesting from left to right, and evaluating the expression to produce an element each time the innermost block is reached. -However, aside from the iterable expression in the leftmost :keyword:`for` clause, +However, aside from the iterable expression in the leftmost :keyword:`!for` clause, the comprehension is executed in a separate implicitly nested scope. This ensures that names assigned to in the target list don't "leak" into the enclosing scope. -The iterable expression in the leftmost :keyword:`for` clause is evaluated +The iterable expression in the leftmost :keyword:`!for` clause is evaluated directly in the enclosing scope and then passed as an argument to the implictly -nested scope. Subsequent :keyword:`for` clauses and any filter condition in the -leftmost :keyword:`for` clause cannot be evaluated in the enclosing scope as +nested scope. Subsequent :keyword:`!for` clauses and any filter condition in the +leftmost :keyword:`!for` clause cannot be evaluated in the enclosing scope as they may depend on the values obtained from the leftmost iterable. For example: ``[x*y for x in range(10) for y in range(x, x+10)]``. @@ -210,14 +210,14 @@ when compiled, in Python 3.8+ they will emit :exc:`SyntaxError`). .. index:: single: await; in comprehensions -Since Python 3.6, in an :keyword:`async def` function, an :keyword:`async for` +Since Python 3.6, in an :keyword:`async def` function, an :keyword:`!async for` clause may be used to iterate over a :term:`asynchronous iterator`. -A comprehension in an :keyword:`async def` function may consist of either a -:keyword:`for` or :keyword:`async for` clause following the leading -expression, may contain additional :keyword:`for` or :keyword:`async for` +A comprehension in an :keyword:`!async def` function may consist of either a +:keyword:`!for` or :keyword:`!async for` clause following the leading +expression, may contain additional :keyword:`!for` or :keyword:`!async for` clauses, and may also use :keyword:`await` expressions. -If a comprehension contains either :keyword:`async for` clauses -or :keyword:`await` expressions it is called an +If a comprehension contains either :keyword:`!async for` clauses +or :keyword:`!await` expressions it is called an :dfn:`asynchronous comprehension`. An asynchronous comprehension may suspend the execution of the coroutine function in which it appears. See also :pep:`530`. @@ -361,11 +361,11 @@ brackets or curly braces. Variables used in the generator expression are evaluated lazily when the :meth:`~generator.__next__` method is called for the generator object (in the same fashion as normal generators). However, the iterable expression in the -leftmost :keyword:`for` clause is immediately evaluated, so that an error +leftmost :keyword:`!for` clause is immediately evaluated, so that an error produced by it will be emitted at the point where the generator expression is defined, rather than at the point where the first value is retrieved. -Subsequent :keyword:`for` clauses and any filter condition in the leftmost -:keyword:`for` clause cannot be evaluated in the enclosing scope as they may +Subsequent :keyword:`!for` clauses and any filter condition in the leftmost +:keyword:`!for` clause cannot be evaluated in the enclosing scope as they may depend on the values obtained from the leftmost iterable. For example: ``(x*y for x in range(10) for y in range(x, x+10))``. @@ -378,7 +378,7 @@ implicitly defined generator (in Python 3.7, such expressions emit :exc:`DeprecationWarning` when compiled, in Python 3.8+ they will emit :exc:`SyntaxError`). -If a generator expression contains either :keyword:`async for` +If a generator expression contains either :keyword:`!async for` clauses or :keyword:`await` expressions it is called an :dfn:`asynchronous generator expression`. An asynchronous generator expression returns a new asynchronous generator object, @@ -642,12 +642,12 @@ that method. In an asynchronous generator function, yield expressions are allowed anywhere in a :keyword:`try` construct. However, if an asynchronous generator is not resumed before it is finalized (by reaching a zero reference count or by -being garbage collected), then a yield expression within a :keyword:`try` +being garbage collected), then a yield expression within a :keyword:`!try` construct could result in a failure to execute pending :keyword:`finally` clauses. In this case, it is the responsibility of the event loop or scheduler running the asynchronous generator to call the asynchronous generator-iterator's :meth:`~agen.aclose` method and run the resulting -coroutine object, thus allowing any pending :keyword:`finally` clauses +coroutine object, thus allowing any pending :keyword:`!finally` clauses to execute. To take care of finalization, an event loop should define @@ -1553,7 +1553,7 @@ Membership test operations The operators :keyword:`in` and :keyword:`not in` test for membership. ``x in s`` evaluates to ``True`` if *x* is a member of *s*, and ``False`` otherwise. ``x not in s`` returns the negation of ``x in s``. All built-in sequences and -set types support this as well as dictionary, for which :keyword:`in` tests +set types support this as well as dictionary, for which :keyword:`!in` tests whether the dictionary has a given key. For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression ``x in y`` is equivalent to ``any(x is e or x == e for e in y)``. @@ -1653,6 +1653,8 @@ returns a boolean value regardless of the type of its argument (for example, ``not 'foo'`` produces ``False`` rather than ``''``.) +.. _if_expr: + Conditional expressions ======================= @@ -1795,7 +1797,7 @@ precedence and have a left-to-right chaining feature as described in the +===============================================+=====================================+ | :keyword:`lambda` | Lambda expression | +-----------------------------------------------+-------------------------------------+ -| :keyword:`if` -- :keyword:`else` | Conditional expression | +| :keyword:`if ` -- :keyword:`!else` | Conditional expression | +-----------------------------------------------+-------------------------------------+ | :keyword:`or` | Boolean OR | +-----------------------------------------------+-------------------------------------+ diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index d36d7d6a707e..9a0ab39d3b4a 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -15,11 +15,11 @@ way. Functions such as :func:`importlib.import_module` and built-in The :keyword:`import` statement combines two operations; it searches for the named module, then it binds the results of that search to a name in the local -scope. The search operation of the :keyword:`import` statement is defined as +scope. The search operation of the :keyword:`!import` statement is defined as a call to the :func:`__import__` function, with the appropriate arguments. The return value of :func:`__import__` is used to perform the name -binding operation of the :keyword:`import` statement. See the -:keyword:`import` statement for the exact details of that name binding +binding operation of the :keyword:`!import` statement. See the +:keyword:`!import` statement for the exact details of that name binding operation. A direct call to :func:`__import__` performs only the module search and, if diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index 33ae0512830d..dcda309b58de 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -369,11 +369,11 @@ target, then the interpreter evaluates the target except for the last .. _assert: -The :keyword:`assert` statement -=============================== +The :keyword:`!assert` statement +================================ .. index:: - statement: assert + ! statement: assert pair: debugging; assertions single: , (comma); expression list @@ -412,8 +412,8 @@ is determined when the interpreter starts. .. _pass: -The :keyword:`pass` statement -============================= +The :keyword:`!pass` statement +============================== .. index:: statement: pass @@ -434,11 +434,11 @@ code needs to be executed, for example:: .. _del: -The :keyword:`del` statement -============================ +The :keyword:`!del` statement +============================= .. index:: - statement: del + ! statement: del pair: deletion; target triple: deletion; target; list @@ -473,11 +473,11 @@ the sliced object). .. _return: -The :keyword:`return` statement -=============================== +The :keyword:`!return` statement +================================ .. index:: - statement: return + ! statement: return pair: function; definition pair: class; definition @@ -495,7 +495,7 @@ If an expression list is present, it is evaluated, else ``None`` is substituted. .. index:: keyword: finally When :keyword:`return` passes control out of a :keyword:`try` statement with a -:keyword:`finally` clause, that :keyword:`finally` clause is executed before +:keyword:`finally` clause, that :keyword:`!finally` clause is executed before really leaving the function. In a generator function, the :keyword:`return` statement indicates that the @@ -505,13 +505,13 @@ becomes the :attr:`StopIteration.value` attribute. In an asynchronous generator function, an empty :keyword:`return` statement indicates that the asynchronous generator is done and will cause -:exc:`StopAsyncIteration` to be raised. A non-empty :keyword:`return` +:exc:`StopAsyncIteration` to be raised. A non-empty :keyword:`!return` statement is a syntax error in an asynchronous generator function. .. _yield: -The :keyword:`yield` statement -============================== +The :keyword:`!yield` statement +=============================== .. index:: statement: yield @@ -546,11 +546,11 @@ For full details of :keyword:`yield` semantics, refer to the .. _raise: -The :keyword:`raise` statement -============================== +The :keyword:`!raise` statement +=============================== .. index:: - statement: raise + ! statement: raise single: exception pair: raising; exception single: __traceback__ (exception attribute) @@ -649,11 +649,11 @@ and information about handling exceptions is in section :ref:`try`. .. _break: -The :keyword:`break` statement -============================== +The :keyword:`!break` statement +=============================== .. index:: - statement: break + ! statement: break statement: for statement: while pair: loop; statement @@ -668,7 +668,7 @@ that loop. .. index:: keyword: else pair: loop control; target -It terminates the nearest enclosing loop, skipping the optional :keyword:`else` +It terminates the nearest enclosing loop, skipping the optional :keyword:`!else` clause if the loop has one. If a :keyword:`for` loop is terminated by :keyword:`break`, the loop control @@ -677,17 +677,17 @@ target keeps its current value. .. index:: keyword: finally When :keyword:`break` passes control out of a :keyword:`try` statement with a -:keyword:`finally` clause, that :keyword:`finally` clause is executed before +:keyword:`finally` clause, that :keyword:`!finally` clause is executed before really leaving the loop. .. _continue: -The :keyword:`continue` statement -================================= +The :keyword:`!continue` statement +================================== .. index:: - statement: continue + ! statement: continue statement: for statement: while pair: loop; statement @@ -702,18 +702,18 @@ The :keyword:`continue` statement cycle of the nearest enclosing loop. When :keyword:`continue` passes control out of a :keyword:`try` statement with a -:keyword:`finally` clause, that :keyword:`finally` clause is executed before +:keyword:`finally` clause, that :keyword:`!finally` clause is executed before really starting the next loop cycle. .. _import: .. _from: -The :keyword:`import` statement -=============================== +The :keyword:`!import` statement +================================ .. index:: - statement: import + ! statement: import single: module; importing pair: name; binding keyword: from @@ -756,8 +756,8 @@ available in the local namespace in one of three ways: .. index:: single: as; import statement -* If the module name is followed by :keyword:`as`, then the name - following :keyword:`as` is bound directly to the imported module. +* If the module name is followed by :keyword:`!as`, then the name + following :keyword:`!as` is bound directly to the imported module. * If no other name is specified, and the module being imported is a top level module, the module's name is bound in the local namespace as a reference to the imported module @@ -782,7 +782,7 @@ The :keyword:`from` form uses a slightly more complex process: check the imported module again for that attribute #. if the attribute is not found, :exc:`ImportError` is raised. #. otherwise, a reference to that value is stored in the local namespace, - using the name in the :keyword:`as` clause if it is present, + using the name in the :keyword:`!as` clause if it is present, otherwise using the attribute name Examples:: @@ -923,11 +923,11 @@ after the script is executed. .. _global: -The :keyword:`global` statement -=============================== +The :keyword:`!global` statement +================================ .. index:: - statement: global + ! statement: global triple: global; name; binding single: , (comma); identifier list @@ -937,11 +937,11 @@ The :keyword:`global` statement The :keyword:`global` statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals. It would be impossible to assign to a global variable without -:keyword:`global`, although free variables may refer to globals without being +:keyword:`!global`, although free variables may refer to globals without being declared global. Names listed in a :keyword:`global` statement must not be used in the same code -block textually preceding that :keyword:`global` statement. +block textually preceding that :keyword:`!global` statement. Names listed in a :keyword:`global` statement must not be defined as formal parameters or in a :keyword:`for` loop control target, :keyword:`class` @@ -960,18 +960,18 @@ annotation. builtin: compile **Programmer's note:** :keyword:`global` is a directive to the parser. It -applies only to code parsed at the same time as the :keyword:`global` statement. -In particular, a :keyword:`global` statement contained in a string or code +applies only to code parsed at the same time as the :keyword:`!global` statement. +In particular, a :keyword:`!global` statement contained in a string or code object supplied to the built-in :func:`exec` function does not affect the code block *containing* the function call, and code contained in such a string is -unaffected by :keyword:`global` statements in the code containing the function +unaffected by :keyword:`!global` statements in the code containing the function call. The same applies to the :func:`eval` and :func:`compile` functions. .. _nonlocal: -The :keyword:`nonlocal` statement -================================= +The :keyword:`!nonlocal` statement +================================== .. index:: statement: nonlocal single: , (comma); identifier list diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index 914da426df36..2538c3106187 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -783,7 +783,7 @@ calls :func:`iter` on the container object. The function returns an iterator object that defines the method :meth:`~iterator.__next__` which accesses elements in the container one at a time. When there are no more elements, :meth:`~iterator.__next__` raises a :exc:`StopIteration` exception which tells the -:keyword:`for` loop to terminate. You can call the :meth:`~iterator.__next__` method +:keyword:`!for` loop to terminate. You can call the :meth:`~iterator.__next__` method using the :func:`next` built-in function; this example shows how it all works:: >>> s = 'abc' diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index bf6fbe21a7f7..08eaa66a4069 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -10,8 +10,8 @@ control flow statements known from other languages, with some twists. .. _tut-if: -:keyword:`if` Statements -======================== +:keyword:`!if` Statements +========================= Perhaps the most well-known statement type is the :keyword:`if` statement. For example:: @@ -31,16 +31,16 @@ example:: More There can be zero or more :keyword:`elif` parts, and the :keyword:`else` part is -optional. The keyword ':keyword:`elif`' is short for 'else if', and is useful -to avoid excessive indentation. An :keyword:`if` ... :keyword:`elif` ... -:keyword:`elif` ... sequence is a substitute for the ``switch`` or +optional. The keyword ':keyword:`!elif`' is short for 'else if', and is useful +to avoid excessive indentation. An :keyword:`!if` ... :keyword:`!elif` ... +:keyword:`!elif` ... sequence is a substitute for the ``switch`` or ``case`` statements found in other languages. .. _tut-for: -:keyword:`for` Statements -========================= +:keyword:`!for` Statements +========================== .. index:: statement: for @@ -48,7 +48,7 @@ to avoid excessive indentation. An :keyword:`if` ... :keyword:`elif` ... The :keyword:`for` statement in Python differs a bit from what you may be used to in C or Pascal. Rather than always iterating over an arithmetic progression of numbers (like in Pascal), or giving the user the ability to define both the -iteration step and halting condition (as C), Python's :keyword:`for` statement +iteration step and halting condition (as C), Python's :keyword:`!for` statement iterates over the items of any sequence (a list or a string), in the order that they appear in the sequence. For example (no pun intended): @@ -154,13 +154,13 @@ Later we will see more functions that return iterables and take iterables as arg .. _tut-break: -:keyword:`break` and :keyword:`continue` Statements, and :keyword:`else` Clauses on Loops -========================================================================================= +:keyword:`!break` and :keyword:`!continue` Statements, and :keyword:`!else` Clauses on Loops +============================================================================================ The :keyword:`break` statement, like in C, breaks out of the innermost enclosing :keyword:`for` or :keyword:`while` loop. -Loop statements may have an ``else`` clause; it is executed when the loop +Loop statements may have an :keyword:`!else` clause; it is executed when the loop terminates through exhaustion of the list (with :keyword:`for`) or when the condition becomes false (with :keyword:`while`), but not when the loop is terminated by a :keyword:`break` statement. This is exemplified by the @@ -189,9 +189,9 @@ the :keyword:`for` loop, **not** the :keyword:`if` statement.) When used with a loop, the ``else`` clause has more in common with the ``else`` clause of a :keyword:`try` statement than it does that of -:keyword:`if` statements: a :keyword:`try` statement's ``else`` clause runs +:keyword:`if` statements: a :keyword:`!try` statement's ``else`` clause runs when no exception occurs, and a loop's ``else`` clause runs when no ``break`` -occurs. For more on the :keyword:`try` statement and exceptions, see +occurs. For more on the :keyword:`!try` statement and exceptions, see :ref:`tut-handling`. The :keyword:`continue` statement, also borrowed from C, continues with the next @@ -213,8 +213,8 @@ iteration of the loop:: .. _tut-pass: -:keyword:`pass` Statements -========================== +:keyword:`!pass` Statements +=========================== The :keyword:`pass` statement does nothing. It can be used when a statement is required syntactically but the program requires no action. For example:: @@ -231,7 +231,7 @@ This is commonly used for creating minimal classes:: Another place :keyword:`pass` can be used is as a place-holder for a function or conditional body when you are working on new code, allowing you to keep thinking -at a more abstract level. The :keyword:`pass` is silently ignored:: +at a more abstract level. The :keyword:`!pass` is silently ignored:: >>> def initlog(*args): ... pass # Remember to implement this! @@ -331,7 +331,7 @@ Fibonacci series, instead of printing it:: This example, as usual, demonstrates some new Python features: * The :keyword:`return` statement returns with a value from a function. - :keyword:`return` without an expression argument returns ``None``. Falling off + :keyword:`!return` without an expression argument returns ``None``. Falling off the end of a function also returns ``None``. * The statement ``result.append(a)`` calls a *method* of the list object diff --git a/Doc/tutorial/datastructures.rst b/Doc/tutorial/datastructures.rst index b291d11595a8..b4db3f015912 100644 --- a/Doc/tutorial/datastructures.rst +++ b/Doc/tutorial/datastructures.rst @@ -216,9 +216,9 @@ or, equivalently:: which is more concise and readable. A list comprehension consists of brackets containing an expression followed -by a :keyword:`for` clause, then zero or more :keyword:`for` or :keyword:`if` +by a :keyword:`!for` clause, then zero or more :keyword:`!for` or :keyword:`!if` clauses. The result will be a new list resulting from evaluating the expression -in the context of the :keyword:`for` and :keyword:`if` clauses which follow it. +in the context of the :keyword:`!for` and :keyword:`!if` clauses which follow it. For example, this listcomp combines the elements of two lists if they are not equal:: @@ -330,12 +330,12 @@ See :ref:`tut-unpacking-arguments` for details on the asterisk in this line. .. _tut-del: -The :keyword:`del` statement -============================ +The :keyword:`!del` statement +============================= There is a way to remove an item from a list given its index instead of its value: the :keyword:`del` statement. This differs from the :meth:`pop` method -which returns a value. The :keyword:`del` statement can also be used to remove +which returns a value. The :keyword:`!del` statement can also be used to remove slices from a list or clear the entire list (which we did earlier by assignment of an empty list to the slice). For example:: diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst index 957cbf962b20..4e287bbd8d29 100644 --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -114,7 +114,7 @@ The :keyword:`try` statement works as follows. A :keyword:`try` statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not -in other handlers of the same :keyword:`try` statement. An except clause may +in other handlers of the same :keyword:`!try` statement. An except clause may name multiple exceptions as a parenthesized tuple, for example:: ... except (RuntimeError, TypeError, NameError): @@ -180,10 +180,10 @@ example:: print(arg, 'has', len(f.readlines()), 'lines') f.close() -The use of the :keyword:`else` clause is better than adding additional code to +The use of the :keyword:`!else` clause is better than adding additional code to the :keyword:`try` clause because it avoids accidentally catching an exception -that wasn't raised by the code being protected by the :keyword:`try` ... -:keyword:`except` statement. +that wasn't raised by the code being protected by the :keyword:`!try` ... +:keyword:`!except` statement. When an exception occurs, it may have an associated value, also known as the exception's *argument*. The presence and type of the argument depend on the @@ -343,11 +343,11 @@ example:: A *finally clause* is always executed before leaving the :keyword:`try` statement, whether an exception has occurred or not. When an exception has -occurred in the :keyword:`try` clause and has not been handled by an -:keyword:`except` clause (or it has occurred in an :keyword:`except` or -:keyword:`else` clause), it is re-raised after the :keyword:`finally` clause has -been executed. The :keyword:`finally` clause is also executed "on the way out" -when any other clause of the :keyword:`try` statement is left via a +occurred in the :keyword:`!try` clause and has not been handled by an +:keyword:`except` clause (or it has occurred in an :keyword:`!except` or +:keyword:`!else` clause), it is re-raised after the :keyword:`finally` clause has +been executed. The :keyword:`!finally` clause is also executed "on the way out" +when any other clause of the :keyword:`!try` statement is left via a :keyword:`break`, :keyword:`continue` or :keyword:`return` statement. A more complicated example:: @@ -376,7 +376,7 @@ complicated example:: As you can see, the :keyword:`finally` clause is executed in any event. The :exc:`TypeError` raised by dividing two strings is not handled by the -:keyword:`except` clause and therefore re-raised after the :keyword:`finally` +:keyword:`except` clause and therefore re-raised after the :keyword:`!finally` clause has been executed. In real world applications, the :keyword:`finally` clause is useful for diff --git a/Doc/tutorial/inputoutput.rst b/Doc/tutorial/inputoutput.rst index 785de29ac9d8..79427860f518 100644 --- a/Doc/tutorial/inputoutput.rst +++ b/Doc/tutorial/inputoutput.rst @@ -317,7 +317,7 @@ reading and writing such files. It is good practice to use the :keyword:`with` keyword when dealing with file objects. The advantage is that the file is properly closed after its suite finishes, even if an exception is raised at some -point. Using :keyword:`with` is also much shorter than writing +point. Using :keyword:`!with` is also much shorter than writing equivalent :keyword:`try`\ -\ :keyword:`finally` blocks:: >>> with open('workfile') as f: diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst index 0aadad309a82..accc30649f24 100644 --- a/Doc/tutorial/modules.rst +++ b/Doc/tutorial/modules.rst @@ -112,8 +112,8 @@ Note that in general the practice of importing ``*`` from a module or package is frowned upon, since it often causes poorly readable code. However, it is okay to use it to save typing in interactive sessions. -If the module name is followed by :keyword:`as`, then the name -following :keyword:`as` is bound directly to the imported module. +If the module name is followed by :keyword:`!as`, then the name +following :keyword:`!as` is bound directly to the imported module. :: diff --git a/Doc/whatsnew/2.0.rst b/Doc/whatsnew/2.0.rst index 5cbf50112385..e6a39e24c95e 100644 --- a/Doc/whatsnew/2.0.rst +++ b/Doc/whatsnew/2.0.rst @@ -284,7 +284,7 @@ write the following to do it:: L) Because of Python's scoping rules, a default argument is used so that the -anonymous function created by the :keyword:`lambda` statement knows what +anonymous function created by the :keyword:`lambda` expression knows what substring is being searched for. List comprehensions make this cleaner:: sublist = [ s for s in L if string.find(s, S) != -1 ] @@ -296,11 +296,11 @@ List comprehensions have the form:: for exprN in sequenceN if condition ] -The :keyword:`for`...\ :keyword:`in` clauses contain the sequences to be +The :keyword:`!for`...\ :keyword:`!in` clauses contain the sequences to be iterated over. The sequences do not have to be the same length, because they are *not* iterated over in parallel, but from left to right; this is explained more clearly in the following paragraphs. The elements of the generated list -will be the successive values of *expression*. The final :keyword:`if` clause +will be the successive values of *expression*. The final :keyword:`!if` clause is optional; if present, *expression* is only evaluated and added to the result if *condition* is true. @@ -316,7 +316,7 @@ following Python code:: # the expression to the # resulting list. -This means that when there are multiple :keyword:`for`...\ :keyword:`in` +This means that when there are multiple :keyword:`!for`...\ :keyword:`!in` clauses, the resulting list will be equal to the product of the lengths of all the sequences. If you have two lists of length 3, the output list is 9 elements long:: @@ -541,8 +541,8 @@ true if *obj* is present in the sequence *seq*; Python computes this by simply trying every index of the sequence until either *obj* is found or an :exc:`IndexError` is encountered. Moshe Zadka contributed a patch which adds a :meth:`__contains__` magic method for providing a custom implementation for -:keyword:`in`. Additionally, new built-in objects written in C can define what -:keyword:`in` means for them via a new slot in the sequence protocol. +:keyword:`!in`. Additionally, new built-in objects written in C can define what +:keyword:`!in` means for them via a new slot in the sequence protocol. Earlier versions of Python used a recursive algorithm for deleting objects. Deeply nested data structures could cause the interpreter to fill up the C stack diff --git a/Doc/whatsnew/2.1.rst b/Doc/whatsnew/2.1.rst index 3486cddc05e9..9537361fe29e 100644 --- a/Doc/whatsnew/2.1.rst +++ b/Doc/whatsnew/2.1.rst @@ -52,7 +52,7 @@ The function :func:`g` will always raise a :exc:`NameError` exception, because the binding of the name ``g`` isn't in either its local namespace or in the module-level namespace. This isn't much of a problem in practice (how often do you recursively define interior functions like this?), but this also made using -the :keyword:`lambda` statement clumsier, and this was a problem in practice. +the :keyword:`lambda` expression clumsier, and this was a problem in practice. In code which uses :keyword:`lambda` you can often find local variables being copied by passing them as the default values of arguments. :: @@ -143,7 +143,7 @@ The syntax uses a ``from...import`` statement using the reserved module name While it looks like a normal :keyword:`import` statement, it's not; there are strict rules on where such a future statement can be put. They can only be at the top of a module, and must precede any Python code or regular -:keyword:`import` statements. This is because such statements can affect how +:keyword:`!import` statements. This is because such statements can affect how the Python bytecode compiler parses code and generates bytecode, so they must precede any statement that will result in bytecodes being produced. diff --git a/Doc/whatsnew/2.2.rst b/Doc/whatsnew/2.2.rst index c2ae866b73b8..b4cd4341f4be 100644 --- a/Doc/whatsnew/2.2.rst +++ b/Doc/whatsnew/2.2.rst @@ -121,7 +121,7 @@ added so if no built-in type is suitable, you can just subclass This means that :keyword:`class` statements that don't have any base classes are always classic classes in Python 2.2. (Actually you can also change this by setting a module-level variable named :attr:`__metaclass__` --- see :pep:`253` -for the details --- but it's easier to just subclass :keyword:`object`.) +for the details --- but it's easier to just subclass :class:`object`.) The type objects for the built-in types are available as built-ins, named using a clever trick. Python has always had built-in functions named :func:`int`, @@ -560,7 +560,7 @@ Here's the simplest example of a generator function:: yield i A new keyword, :keyword:`yield`, was introduced for generators. Any function -containing a :keyword:`yield` statement is a generator function; this is +containing a :keyword:`!yield` statement is a generator function; this is detected by Python's bytecode compiler which compiles the function specially as a result. Because a new keyword was introduced, generators must be explicitly enabled in a module by including a ``from __future__ import generators`` @@ -571,14 +571,14 @@ When you call a generator function, it doesn't return a single value; instead it returns a generator object that supports the iterator protocol. On executing the :keyword:`yield` statement, the generator outputs the value of ``i``, similar to a :keyword:`return` statement. The big difference between -:keyword:`yield` and a :keyword:`return` statement is that on reaching a -:keyword:`yield` the generator's state of execution is suspended and local +:keyword:`!yield` and a :keyword:`!return` statement is that on reaching a +:keyword:`!yield` the generator's state of execution is suspended and local variables are preserved. On the next call to the generator's ``next()`` method, -the function will resume executing immediately after the :keyword:`yield` -statement. (For complicated reasons, the :keyword:`yield` statement isn't -allowed inside the :keyword:`try` block of a +the function will resume executing immediately after the :keyword:`!yield` +statement. (For complicated reasons, the :keyword:`!yield` statement isn't +allowed inside the :keyword:`!try` block of a :keyword:`try`...\ :keyword:`finally` statement; read :pep:`255` for a full -explanation of the interaction between :keyword:`yield` and exceptions.) +explanation of the interaction between :keyword:`!yield` and exceptions.) Here's a sample usage of the :func:`generate_ints` generator:: @@ -602,7 +602,7 @@ generate_ints(3)``. Inside a generator function, the :keyword:`return` statement can only be used without a value, and signals the end of the procession of values; afterwards the -generator cannot return any further values. :keyword:`return` with a value, such +generator cannot return any further values. :keyword:`!return` with a value, such as ``return 5``, is a syntax error inside a generator function. The end of the generator's results can also be indicated by raising :exc:`StopIteration` manually, or by just letting the flow of execution fall off the bottom of the @@ -863,8 +863,8 @@ The function :func:`g` will always raise a :exc:`NameError` exception, because the binding of the name ``g`` isn't in either its local namespace or in the module-level namespace. This isn't much of a problem in practice (how often do you recursively define interior functions like this?), but this also made using -the :keyword:`lambda` statement clumsier, and this was a problem in practice. -In code which uses :keyword:`lambda` you can often find local variables being +the :keyword:`lambda` expression clumsier, and this was a problem in practice. +In code which uses :keyword:`!lambda` you can often find local variables being copied by passing them as the default values of arguments. :: def find(self, name): diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index 37ba7c09c917..dac0e6364928 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -149,7 +149,7 @@ Here's the simplest example of a generator function:: yield i A new keyword, :keyword:`yield`, was introduced for generators. Any function -containing a :keyword:`yield` statement is a generator function; this is +containing a :keyword:`!yield` statement is a generator function; this is detected by Python's bytecode compiler which compiles the function specially as a result. @@ -157,14 +157,14 @@ When you call a generator function, it doesn't return a single value; instead it returns a generator object that supports the iterator protocol. On executing the :keyword:`yield` statement, the generator outputs the value of ``i``, similar to a :keyword:`return` statement. The big difference between -:keyword:`yield` and a :keyword:`return` statement is that on reaching a -:keyword:`yield` the generator's state of execution is suspended and local +:keyword:`!yield` and a :keyword:`!return` statement is that on reaching a +:keyword:`!yield` the generator's state of execution is suspended and local variables are preserved. On the next call to the generator's ``.next()`` method, the function will resume executing immediately after the -:keyword:`yield` statement. (For complicated reasons, the :keyword:`yield` +:keyword:`!yield` statement. (For complicated reasons, the :keyword:`!yield` statement isn't allowed inside the :keyword:`try` block of a -:keyword:`try`...\ :keyword:`finally` statement; read :pep:`255` for a full -explanation of the interaction between :keyword:`yield` and exceptions.) +:keyword:`!try`...\ :keyword:`!finally` statement; read :pep:`255` for a full +explanation of the interaction between :keyword:`!yield` and exceptions.) Here's a sample usage of the :func:`generate_ints` generator:: @@ -188,7 +188,7 @@ generate_ints(3)``. Inside a generator function, the :keyword:`return` statement can only be used without a value, and signals the end of the procession of values; afterwards the -generator cannot return any further values. :keyword:`return` with a value, such +generator cannot return any further values. :keyword:`!return` with a value, such as ``return 5``, is a syntax error inside a generator function. The end of the generator's results can also be indicated by raising :exc:`StopIteration` manually, or by just letting the flow of execution fall off the bottom of the @@ -589,7 +589,7 @@ strict language such as Pascal would also prevent you performing arithmetic with Booleans, and would require that the expression in an :keyword:`if` statement always evaluate to a Boolean result. Python is not this strict and never will be, as :pep:`285` explicitly says. This means you can still use any expression -in an :keyword:`if` statement, even ones that evaluate to a list or tuple or +in an :keyword:`!if` statement, even ones that evaluate to a list or tuple or some random object. The Boolean type is a subclass of the :class:`int` class so that arithmetic using a Boolean still works. :: diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index 7a3384cbaa5d..4caacae02556 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -370,7 +370,7 @@ PEP 341: Unified try/except/finally Until Python 2.5, the :keyword:`try` statement came in two flavours. You could use a :keyword:`finally` block to ensure that code is always executed, or one or more :keyword:`except` blocks to catch specific exceptions. You couldn't -combine both :keyword:`except` blocks and a :keyword:`finally` block, because +combine both :keyword:`!except` blocks and a :keyword:`!finally` block, because generating the right bytecode for the combined version was complicated and it wasn't clear what the semantics of the combined statement should be. @@ -435,10 +435,10 @@ When you call ``counter(10)``, the result is an iterator that returns the values from 0 up to 9. On encountering the :keyword:`yield` statement, the iterator returns the provided value and suspends the function's execution, preserving the local variables. Execution resumes on the following call to the iterator's -:meth:`next` method, picking up after the :keyword:`yield` statement. +:meth:`next` method, picking up after the :keyword:`!yield` statement. In Python 2.3, :keyword:`yield` was a statement; it didn't return any value. In -2.5, :keyword:`yield` is now an expression, returning a value that can be +2.5, :keyword:`!yield` is now an expression, returning a value that can be assigned to a variable or otherwise operated on:: val = (yield i) @@ -458,7 +458,7 @@ expression on the right-hand side of an assignment. This means you can write Values are sent into a generator by calling its ``send(value)`` method. The generator's code is then resumed and the :keyword:`yield` expression returns the specified *value*. If the regular :meth:`next` method is called, the -:keyword:`yield` returns :const:`None`. +:keyword:`!yield` returns :const:`None`. Here's the previous example, modified to allow changing the value of the internal counter. :: @@ -644,7 +644,7 @@ Writing Context Managers ------------------------ Under the hood, the ':keyword:`with`' statement is fairly complicated. Most -people will only use ':keyword:`with`' in company with existing objects and +people will only use ':keyword:`!with`' in company with existing objects and don't need to know these details, so you can skip the rest of this section if you like. Authors of new objects will need to understand the details of the underlying implementation and should keep reading. @@ -750,9 +750,9 @@ generator function instead of defining a new class. The generator should yield exactly one value. The code up to the :keyword:`yield` will be executed as the :meth:`__enter__` method, and the value yielded will be the method's return value that will get bound to the variable in the ':keyword:`with`' statement's -:keyword:`as` clause, if any. The code after the :keyword:`yield` will be +:keyword:`!as` clause, if any. The code after the :keyword:`yield` will be executed in the :meth:`__exit__` method. Any exception raised in the block will -be raised by the :keyword:`yield` statement. +be raised by the :keyword:`!yield` statement. Our database example from the previous section could be written using this decorator as:: @@ -776,7 +776,7 @@ decorator as:: The :mod:`contextlib` module also has a ``nested(mgr1, mgr2, ...)`` function that combines a number of context managers so you don't need to write nested -':keyword:`with`' statements. In this example, the single ':keyword:`with`' +':keyword:`with`' statements. In this example, the single ':keyword:`!with`' statement both starts a database transaction and acquires a thread lock:: lock = threading.Lock() diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index ccfdbdce03e9..512b8edb357a 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -250,10 +250,10 @@ PEP 343: The 'with' statement The previous version, Python 2.5, added the ':keyword:`with`' statement as an optional feature, to be enabled by a ``from __future__ import with_statement`` directive. In 2.6 the statement no longer needs to -be specially enabled; this means that :keyword:`with` is now always a +be specially enabled; this means that :keyword:`!with` is now always a keyword. The rest of this section is a copy of the corresponding section from the "What's New in Python 2.5" document; if you're -familiar with the ':keyword:`with`' statement +familiar with the ':keyword:`!with`' statement from Python 2.5, you can skip this section. The ':keyword:`with`' statement clarifies code that previously would use @@ -331,7 +331,7 @@ Writing Context Managers ------------------------ Under the hood, the ':keyword:`with`' statement is fairly complicated. Most -people will only use ':keyword:`with`' in company with existing objects and +people will only use ':keyword:`!with`' in company with existing objects and don't need to know these details, so you can skip the rest of this section if you like. Authors of new objects will need to understand the details of the underlying implementation and should keep reading. @@ -438,9 +438,9 @@ generator function instead of defining a new class. The generator should yield exactly one value. The code up to the :keyword:`yield` will be executed as the :meth:`__enter__` method, and the value yielded will be the method's return value that will get bound to the variable in the ':keyword:`with`' statement's -:keyword:`as` clause, if any. The code after the :keyword:`yield` will be +:keyword:`!as` clause, if any. The code after the :keyword:`!yield` will be executed in the :meth:`__exit__` method. Any exception raised in the block will -be raised by the :keyword:`yield` statement. +be raised by the :keyword:`!yield` statement. Using this decorator, our database example from the previous section could be written as:: @@ -464,7 +464,7 @@ could be written as:: The :mod:`contextlib` module also has a ``nested(mgr1, mgr2, ...)`` function that combines a number of context managers so you don't need to write nested -':keyword:`with`' statements. In this example, the single ':keyword:`with`' +':keyword:`with`' statements. In this example, the single ':keyword:`!with`' statement both starts a database transaction and acquires a thread lock:: lock = threading.Lock() @@ -1684,7 +1684,7 @@ Some smaller changes made to the core Python language are: * An obscure change: when you use the :func:`locals` function inside a :keyword:`class` statement, the resulting dictionary no longer returns free variables. (Free variables, in this case, are variables referenced in the - :keyword:`class` statement that aren't attributes of the class.) + :keyword:`!class` statement that aren't attributes of the class.) .. ====================================================================== diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index fd59c1611fcd..9f8d9f202c43 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -708,7 +708,7 @@ Some smaller changes made to the core Python language are: * The :keyword:`with` statement can now use multiple context managers in one statement. Context managers are processed from left to right - and each one is treated as beginning a new :keyword:`with` statement. + and each one is treated as beginning a new :keyword:`!with` statement. This means that:: with A() as a, B() as b: @@ -844,7 +844,7 @@ Some smaller changes made to the core Python language are: * The :keyword:`import` statement will no longer try an absolute import if a relative import (e.g. ``from .os import sep``) fails. This - fixes a bug, but could possibly break certain :keyword:`import` + fixes a bug, but could possibly break certain :keyword:`!import` statements that were only working by accident. (Fixed by Meador Inge; :issue:`7902`.) @@ -1158,7 +1158,7 @@ changes, or look through the Subversion logs for all the details. * Deprecated function: :func:`contextlib.nested`, which allows handling more than one context manager with a single :keyword:`with` - statement, has been deprecated, because the :keyword:`with` statement + statement, has been deprecated, because the :keyword:`!with` statement now supports multiple context managers. * The :mod:`cookielib` module now ignores cookies that have an invalid diff --git a/Doc/whatsnew/3.0.rst b/Doc/whatsnew/3.0.rst index 5ecf2ebfe79c..880958d3edb9 100644 --- a/Doc/whatsnew/3.0.rst +++ b/Doc/whatsnew/3.0.rst @@ -373,7 +373,7 @@ New Syntax * :pep:`3104`: :keyword:`nonlocal` statement. Using ``nonlocal x`` you can now assign directly to a variable in an outer (but - non-global) scope. :keyword:`nonlocal` is a new reserved word. + non-global) scope. :keyword:`!nonlocal` is a new reserved word. * :pep:`3132`: Extended Iterable Unpacking. You can now write things like ``a, b, *rest = some_sequence``. And even ``*rest, a = @@ -408,14 +408,14 @@ Changed Syntax * :pep:`3109` and :pep:`3134`: new :keyword:`raise` statement syntax: :samp:`raise [{expr} [from {expr}]]`. See below. -* :keyword:`as` and :keyword:`with` are now reserved words. (Since +* :keyword:`!as` and :keyword:`with` are now reserved words. (Since 2.6, actually.) * ``True``, ``False``, and ``None`` are reserved words. (2.6 partially enforced the restrictions on ``None`` already.) * Change from :keyword:`except` *exc*, *var* to - :keyword:`except` *exc* :keyword:`as` *var*. See :pep:`3110`. + :keyword:`!except` *exc* :keyword:`!as` *var*. See :pep:`3110`. * :pep:`3115`: New Metaclass Syntax. Instead of:: @@ -507,9 +507,9 @@ consulted for longer descriptions. * :ref:`pep-3105`. This is now a standard feature and no longer needs to be imported from :mod:`__future__`. More details were given above. -* :ref:`pep-3110`. The :keyword:`except` *exc* :keyword:`as` *var* - syntax is now standard and :keyword:`except` *exc*, *var* is no - longer supported. (Of course, the :keyword:`as` *var* part is still +* :ref:`pep-3110`. The :keyword:`except` *exc* :keyword:`!as` *var* + syntax is now standard and :keyword:`!except` *exc*, *var* is no + longer supported. (Of course, the :keyword:`!as` *var* part is still optional.) * :ref:`pep-3112`. The ``b"..."`` string literal notation (and its diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index 10996233ff6e..845e327cabc8 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -1352,7 +1352,7 @@ shelve ------ :class:`~shelve.Shelf` instances may now be used in :keyword:`with` statements, -and will be automatically closed at the end of the :keyword:`with` block. +and will be automatically closed at the end of the :keyword:`!with` block. (Contributed by Filip Gruszczy?ski in :issue:`13896`.) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 7d4c4f91993b..e8572ec0b492 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -1909,7 +1909,7 @@ Deprecated Python Behavior Yield expressions (both ``yield`` and ``yield from`` clauses) are now deprecated in comprehensions and generator expressions (aside from the iterable expression -in the leftmost :keyword:`for` clause). This ensures that comprehensions +in the leftmost :keyword:`!for` clause). This ensures that comprehensions always immediately return a container of the appropriate type (rather than potentially returning a :term:`generator iterator` object), while generator expressions won't attempt to interleave their implicit output with the output From solipsis at pitrou.net Wed Dec 19 04:14:10 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 19 Dec 2018 09:14:10 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=2 Message-ID: <20181219091410.1.B5DE0B730565946A@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [0, 7, -7] memory blocks, sum=0 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_forkserver leaked [-2, 0, 1] memory blocks, sum=-1 test_multiprocessing_spawn leaked [0, -2, 1] memory blocks, sum=-1 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogiNJoud', '--timeout', '7200'] From webhook-mailer at python.org Wed Dec 19 04:46:28 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 19 Dec 2018 09:46:28 -0000 Subject: [Python-checkins] bpo-18799: Resurrect test_404 in test_xmlrpc. (GH-11196) Message-ID: https://github.com/python/cpython/commit/9e1c7ed9aaa4dea413f1b3ed92bb79cccb2f50eb commit: 9e1c7ed9aaa4dea413f1b3ed92bb79cccb2f50eb branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-19T01:46:23-08:00 summary: bpo-18799: Resurrect test_404 in test_xmlrpc. (GH-11196) (cherry picked from commit fae95874b729dfe62a7a40625f8717aed20b0fca) Co-authored-by: Vajrasky Kok files: M Lib/test/test_xmlrpc.py diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 5f780d88002e..32263f7f0b3b 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -818,11 +818,10 @@ def test_nonascii_methodname(self): # protocol error; provide additional information in test output self.fail("%s\n%s" % (e, getattr(e, "headers", ""))) - # [ch] The test 404 is causing lots of false alarms. - def XXXtest_404(self): + def test_404(self): # send POST with http.client, it should return 404 header and # 'Not Found' message. - conn = httplib.client.HTTPConnection(ADDR, PORT) + conn = http.client.HTTPConnection(ADDR, PORT) conn.request('POST', '/this-is-not-valid') response = conn.getresponse() conn.close() From webhook-mailer at python.org Wed Dec 19 05:15:00 2018 From: webhook-mailer at python.org (Julien Palard) Date: Wed, 19 Dec 2018 10:15:00 -0000 Subject: [Python-checkins] bpo-35472: Doc: For Python 3.7 Sphinx 1.6.6 is enough. (GH-11192) Message-ID: https://github.com/python/cpython/commit/a9ed8fcdbaeccdb82593525b170fb0544202eeda commit: a9ed8fcdbaeccdb82593525b170fb0544202eeda branch: 3.7 author: Julien Palard committer: GitHub date: 2018-12-19T11:14:55+01:00 summary: bpo-35472: Doc: For Python 3.7 Sphinx 1.6.6 is enough. (GH-11192) files: M Doc/conf.py diff --git a/Doc/conf.py b/Doc/conf.py index 124cb7aff6b3..eab3c39cfb7c 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -34,8 +34,8 @@ # By default, highlight as Python 3. highlight_language = 'python3' -# Require Sphinx 1.7 for build. -needs_sphinx = '1.7' +# Require Sphinx 1.6.6 for build. +needs_sphinx = "1.6.6" # Ignore any .rst files in the venv/ directory. venvdir = os.getenv('VENVDIR', 'venv') From webhook-mailer at python.org Wed Dec 19 06:59:57 2018 From: webhook-mailer at python.org (Xiang Zhang) Date: Wed, 19 Dec 2018 11:59:57 -0000 Subject: [Python-checkins] bpo-35497: add versionadded tag for EPOLLEXCLUSIVE (GH-11162) Message-ID: https://github.com/python/cpython/commit/92330c0b6d6c253c41a133cc50caea4853c7e311 commit: 92330c0b6d6c253c41a133cc50caea4853c7e311 branch: master author: Manjusaka committer: Xiang Zhang date: 2018-12-19T19:59:52+08:00 summary: bpo-35497: add versionadded tag for EPOLLEXCLUSIVE (GH-11162) files: M Doc/library/select.rst M Misc/ACKS diff --git a/Doc/library/select.rst b/Doc/library/select.rst index f5b25db774f6..733a91e20b68 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -317,6 +317,9 @@ Edge and Level Trigger Polling (epoll) Objects | :const:`EPOLLMSG` | Ignored. | +-------------------------+-----------------------------------------------+ + .. versionadded:: 3.6 + :const:`EPOLLEXCLUSIVE` was added. It's only supported by Linux Kernel 4.5 + or later. .. method:: epoll.close() diff --git a/Misc/ACKS b/Misc/ACKS index 3e5fa0a2facc..63df5df4cbef 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1843,3 +1843,4 @@ Jelle Zijlstra Gennadiy Zlobin Doug Zongker Peter ?strand +Zheao Li From webhook-mailer at python.org Wed Dec 19 07:05:53 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 19 Dec 2018 12:05:53 -0000 Subject: [Python-checkins] bpo-35497: add versionadded tag for EPOLLEXCLUSIVE (GH-11162) Message-ID: https://github.com/python/cpython/commit/a11d44056e4f9b64d28efec295e1c1c45d4cb9e1 commit: a11d44056e4f9b64d28efec295e1c1c45d4cb9e1 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-19T04:05:48-08:00 summary: bpo-35497: add versionadded tag for EPOLLEXCLUSIVE (GH-11162) (cherry picked from commit 92330c0b6d6c253c41a133cc50caea4853c7e311) Co-authored-by: Manjusaka files: M Doc/library/select.rst M Misc/ACKS diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 955ac2a8ef42..5b8fd7ee805c 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -317,6 +317,9 @@ Edge and Level Trigger Polling (epoll) Objects | :const:`EPOLLMSG` | Ignored. | +-------------------------+-----------------------------------------------+ + .. versionadded:: 3.6 + :const:`EPOLLEXCLUSIVE` was added. It's only supported by Linux Kernel 4.5 + or later. .. method:: epoll.close() diff --git a/Misc/ACKS b/Misc/ACKS index c35ef1c0fa35..f38408491288 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1821,3 +1821,4 @@ Jelle Zijlstra Gennadiy Zlobin Doug Zongker Peter ?strand +Zheao Li From webhook-mailer at python.org Wed Dec 19 08:29:07 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 19 Dec 2018 13:29:07 -0000 Subject: [Python-checkins] Fix Python version since which external enities are not resolved by default. (GH-11237) Message-ID: https://github.com/python/cpython/commit/bf99bcf56c8b4e97b2737f9e751a563b625430ec commit: bf99bcf56c8b4e97b2737f9e751a563b625430ec branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-19T15:29:04+02:00 summary: Fix Python version since which external enities are not resolved by default. (GH-11237) files: M Doc/library/xml.dom.pulldom.rst M Doc/library/xml.rst M Doc/library/xml.sax.rst diff --git a/Doc/library/xml.dom.pulldom.rst b/Doc/library/xml.dom.pulldom.rst index eb2b16bd6c0b..660c75c1a1b3 100644 --- a/Doc/library/xml.dom.pulldom.rst +++ b/Doc/library/xml.dom.pulldom.rst @@ -25,7 +25,7 @@ events until either processing is finished or an error condition occurs. maliciously constructed data. If you need to parse untrusted or unauthenticated data see :ref:`xml-vulnerabilities`. -.. versionchanged:: 3.8 +.. versionchanged:: 3.7.1 The SAX parser no longer processes general external entities by default to increase security by default. To enable processing of external entities, diff --git a/Doc/library/xml.rst b/Doc/library/xml.rst index be1272940b8e..fb86b6f5564d 100644 --- a/Doc/library/xml.rst +++ b/Doc/library/xml.rst @@ -75,7 +75,7 @@ decompression bomb Safe Safe Safe S 2. :mod:`xml.dom.minidom` doesn't expand external entities and simply returns the unexpanded entity verbatim. 3. :mod:`xmlrpclib` doesn't expand external entities and omits them. -4. Since Python 3.8, external general entities are no longer processed by +4. Since Python 3.7.1, external general entities are no longer processed by default. diff --git a/Doc/library/xml.sax.rst b/Doc/library/xml.sax.rst index 0b6973b8c8a8..6d351dfb4d70 100644 --- a/Doc/library/xml.sax.rst +++ b/Doc/library/xml.sax.rst @@ -24,7 +24,7 @@ the SAX API. constructed data. If you need to parse untrusted or unauthenticated data see :ref:`xml-vulnerabilities`. -.. versionchanged:: 3.8 +.. versionchanged:: 3.7.1 The SAX parser no longer processes general external entities by default to increase security. Before, the parser created network connections From webhook-mailer at python.org Wed Dec 19 08:31:44 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 19 Dec 2018 13:31:44 -0000 Subject: [Python-checkins] Fix documented signatures for C API functions. (GH-11236) Message-ID: https://github.com/python/cpython/commit/57dd79e6f7f33bb4e6817ac58c9cb91de99675e0 commit: 57dd79e6f7f33bb4e6817ac58c9cb91de99675e0 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-19T15:31:40+02:00 summary: Fix documented signatures for C API functions. (GH-11236) files: M Doc/c-api/tuple.rst M Doc/c-api/unicode.rst diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index a66832cfa434..20bf9f0b0804 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -209,7 +209,7 @@ type. This function "steals" a reference to *o*. -.. c:function:: PyObject* PyStructSequence_SET_ITEM(PyObject *p, Py_ssize_t *pos, PyObject *o) +.. c:function:: void PyStructSequence_SET_ITEM(PyObject *p, Py_ssize_t *pos, PyObject *o) Macro equivalent of :c:func:`PyStructSequence_SetItem`. diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 92e22b16a4ef..39c067d439cc 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -935,7 +935,7 @@ wchar_t Support Return *NULL* on failure. -.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyUnicodeObject *unicode, wchar_t *w, Py_ssize_t size) +.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyObject *unicode, wchar_t *w, Py_ssize_t size) Copy the Unicode object contents into the :c:type:`wchar_t` buffer *w*. At most *size* :c:type:`wchar_t` characters are copied (excluding a possibly trailing @@ -1346,7 +1346,7 @@ These are the "Raw Unicode Escape" codec APIs: .. c:function:: PyObject* PyUnicode_EncodeRawUnicodeEscape(const Py_UNICODE *s, \ - Py_ssize_t size, const char *errors) + Py_ssize_t size) Encode the :c:type:`Py_UNICODE` buffer of the given *size* using Raw-Unicode-Escape and return a bytes object. Return *NULL* if an exception was raised by the codec. @@ -1515,8 +1515,8 @@ the user settings on the machine running the codec. Return *NULL* if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, int size, \ - const char *errors, int *consumed) +.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, Py_ssize_t size, \ + const char *errors, Py_ssize_t *consumed) If *consumed* is *NULL*, behave like :c:func:`PyUnicode_DecodeMBCS`. If *consumed* is not *NULL*, :c:func:`PyUnicode_DecodeMBCSStateful` will not decode From webhook-mailer at python.org Wed Dec 19 08:43:32 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 19 Dec 2018 13:43:32 -0000 Subject: [Python-checkins] Fix documented signatures for C API functions. (GH-11236) Message-ID: https://github.com/python/cpython/commit/e2e6f907fdd3f62284d0e318c51ced752d1d263f commit: e2e6f907fdd3f62284d0e318c51ced752d1d263f branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-19T05:43:29-08:00 summary: Fix documented signatures for C API functions. (GH-11236) (cherry picked from commit 57dd79e6f7f33bb4e6817ac58c9cb91de99675e0) Co-authored-by: Serhiy Storchaka files: M Doc/c-api/tuple.rst M Doc/c-api/unicode.rst diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index a66832cfa434..20bf9f0b0804 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -209,7 +209,7 @@ type. This function "steals" a reference to *o*. -.. c:function:: PyObject* PyStructSequence_SET_ITEM(PyObject *p, Py_ssize_t *pos, PyObject *o) +.. c:function:: void PyStructSequence_SET_ITEM(PyObject *p, Py_ssize_t *pos, PyObject *o) Macro equivalent of :c:func:`PyStructSequence_SetItem`. diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 92e22b16a4ef..39c067d439cc 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -935,7 +935,7 @@ wchar_t Support Return *NULL* on failure. -.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyUnicodeObject *unicode, wchar_t *w, Py_ssize_t size) +.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyObject *unicode, wchar_t *w, Py_ssize_t size) Copy the Unicode object contents into the :c:type:`wchar_t` buffer *w*. At most *size* :c:type:`wchar_t` characters are copied (excluding a possibly trailing @@ -1346,7 +1346,7 @@ These are the "Raw Unicode Escape" codec APIs: .. c:function:: PyObject* PyUnicode_EncodeRawUnicodeEscape(const Py_UNICODE *s, \ - Py_ssize_t size, const char *errors) + Py_ssize_t size) Encode the :c:type:`Py_UNICODE` buffer of the given *size* using Raw-Unicode-Escape and return a bytes object. Return *NULL* if an exception was raised by the codec. @@ -1515,8 +1515,8 @@ the user settings on the machine running the codec. Return *NULL* if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, int size, \ - const char *errors, int *consumed) +.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, Py_ssize_t size, \ + const char *errors, Py_ssize_t *consumed) If *consumed* is *NULL*, behave like :c:func:`PyUnicode_DecodeMBCS`. If *consumed* is not *NULL*, :c:func:`PyUnicode_DecodeMBCSStateful` will not decode From webhook-mailer at python.org Wed Dec 19 10:11:07 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 19 Dec 2018 15:11:07 -0000 Subject: [Python-checkins] [2.7] bpo-35441: Remove dead and buggy code related to PyList_SetItem(). (GH-11033) (GH-11234) Message-ID: https://github.com/python/cpython/commit/89b5ea297d67f5efeb8fca0b63fa3d9f7030b2f0 commit: 89b5ea297d67f5efeb8fca0b63fa3d9f7030b2f0 branch: 2.7 author: Serhiy Storchaka committer: GitHub date: 2018-12-19T17:11:02+02:00 summary: [2.7] bpo-35441: Remove dead and buggy code related to PyList_SetItem(). (GH-11033) (GH-11234) In _localemodule.c and selectmodule.c, remove dead code that would cause double decrefs if run. In addition, replace PyList_SetItem() with PyList_SET_ITEM() in cases where a new list is populated and there is no possibility of an error. In addition, check if the list changed size in the loop in array_array_fromlist(). (cherry picked from commit 99d56b53560b3867844472ae381fb3f858760621) Co-authored-by: Zackery Spytz files: M Modules/_localemodule.c M Modules/arraymodule.c M Modules/readline.c M Modules/selectmodule.c M Python/ceval.c M Python/sysmodule.c diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index 7e2f1a997be6..c55bd0d846eb 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -73,20 +73,13 @@ copy_grouping(char* s) do { i++; val = PyInt_FromLong(s[i]); - if (!val) - break; - if (PyList_SetItem(result, i, val)) { - Py_DECREF(val); - val = NULL; - break; + if (val == NULL) { + Py_DECREF(result); + return NULL; } + PyList_SET_ITEM(result, i, val); } while (s[i] != '\0' && s[i] != CHAR_MAX); - if (!val) { - Py_DECREF(result); - return NULL; - } - return result; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 5bd3a42f009d..8d4eb0932df5 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1344,7 +1344,7 @@ array_fromlist(arrayobject *self, PyObject *list) Py_SIZE(self) += n; self->allocated = Py_SIZE(self); for (i = 0; i < n; i++) { - PyObject *v = PyList_GetItem(list, i); + PyObject *v = PyList_GET_ITEM(list, i); if ((*self->ob_descr->setitem)(self, Py_SIZE(self) - n + i, v) != 0) { Py_SIZE(self) -= n; @@ -1357,6 +1357,19 @@ array_fromlist(arrayobject *self, PyObject *list) self->allocated = Py_SIZE(self); return NULL; } + if (n != PyList_GET_SIZE(list)) { + PyErr_SetString(PyExc_RuntimeError, + "list changed size during iteration"); + Py_SIZE(self) -= n; + if (itemsize && (Py_SIZE(self) > PY_SSIZE_T_MAX / itemsize)) { + return PyErr_NoMemory(); + } + PyMem_RESIZE(item, char, + Py_SIZE(self) * itemsize); + self->ob_item = item; + self->allocated = Py_SIZE(self); + return NULL; + } } } Py_INCREF(Py_None); @@ -1383,7 +1396,7 @@ array_tolist(arrayobject *self, PyObject *unused) Py_DECREF(list); return NULL; } - PyList_SetItem(list, i, v); + PyList_SET_ITEM(list, i, v); } return list; } diff --git a/Modules/readline.c b/Modules/readline.c index 1e10dd708b99..02621358238e 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -792,8 +792,7 @@ on_completion_display_matches_hook(char **matches, s = PyString_FromString(matches[i+1]); if (s == NULL) goto error; - if (PyList_SetItem(m, i, s) == -1) - goto error; + PyList_SET_ITEM(m, i, s); } r = PyObject_CallFunction(completion_display_matches_hook, diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 1dec6a120d05..dc22f105fd71 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -601,10 +601,7 @@ poll_poll(pollObject *self, PyObject *args) goto error; } PyTuple_SET_ITEM(value, 1, num); - if ((PyList_SetItem(result_list, j, value)) == -1) { - Py_DECREF(value); - goto error; - } + PyList_SET_ITEM(result_list, j, value); i++; } } diff --git a/Python/ceval.c b/Python/ceval.c index b561cd0f3039..e1140a8e4012 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5247,7 +5247,7 @@ getarray(long a[256]) Py_DECREF(l); return NULL; } - PyList_SetItem(l, i, x); + PyList_SET_ITEM(l, i, x); } for (i = 0; i < 256; i++) a[i] = 0; @@ -5269,7 +5269,7 @@ _Py_GetDXProfile(PyObject *self, PyObject *args) Py_DECREF(l); return NULL; } - PyList_SetItem(l, i, x); + PyList_SET_ITEM(l, i, x); } return l; #endif diff --git a/Python/sysmodule.c b/Python/sysmodule.c index b153ef63381b..fdb7af2f5f67 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1555,7 +1555,7 @@ makepathobject(char *path, int delim) Py_DECREF(v); return NULL; } - PyList_SetItem(v, i, w); + PyList_SET_ITEM(v, i, w); if (*p == '\0') break; path = p+1; From webhook-mailer at python.org Wed Dec 19 11:19:43 2018 From: webhook-mailer at python.org (Barry Warsaw) Date: Wed, 19 Dec 2018 16:19:43 -0000 Subject: [Python-checkins] bpo-35526: make __future__.barry_as_FLUFL mandatory for Python 4.0 (#11218) Message-ID: https://github.com/python/cpython/commit/55cc34500e5abbfedb89adc95e3f94d53c544933 commit: 55cc34500e5abbfedb89adc95e3f94d53c544933 branch: master author: Chris Rands committer: Barry Warsaw date: 2018-12-19T08:19:39-08:00 summary: bpo-35526: make __future__.barry_as_FLUFL mandatory for Python 4.0 (#11218) * extending the joke! * ?? Added by blurb_it. files: A Misc/NEWS.d/next/Library/2018-12-18-21-12-25.bpo-35526.fYvo6H.rst M Lib/__future__.py diff --git a/Lib/__future__.py b/Lib/__future__.py index ce8bed7a643c..e1135685d846 100644 --- a/Lib/__future__.py +++ b/Lib/__future__.py @@ -134,7 +134,7 @@ def __repr__(self): CO_FUTURE_UNICODE_LITERALS) barry_as_FLUFL = _Feature((3, 1, 0, "alpha", 2), - (3, 9, 0, "alpha", 0), + (4, 0, 0, "alpha", 0), CO_FUTURE_BARRY_AS_BDFL) generator_stop = _Feature((3, 5, 0, "beta", 1), diff --git a/Misc/NEWS.d/next/Library/2018-12-18-21-12-25.bpo-35526.fYvo6H.rst b/Misc/NEWS.d/next/Library/2018-12-18-21-12-25.bpo-35526.fYvo6H.rst new file mode 100644 index 000000000000..ea1096393d21 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-18-21-12-25.bpo-35526.fYvo6H.rst @@ -0,0 +1 @@ +Delaying the 'joke' of barry_as_FLUFL.mandatory to Python version 4.0 \ No newline at end of file From webhook-mailer at python.org Wed Dec 19 12:19:07 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 19 Dec 2018 17:19:07 -0000 Subject: [Python-checkins] bpo-35257: Avoid leaking LTO linker flags into distutils (GH-10900) Message-ID: https://github.com/python/cpython/commit/cf10a750f4b50b6775719cfb17bee00bc3a9c60b commit: cf10a750f4b50b6775719cfb17bee00bc3a9c60b branch: master author: stratakis committer: Victor Stinner date: 2018-12-19T18:19:01+01:00 summary: bpo-35257: Avoid leaking LTO linker flags into distutils (GH-10900) When compiling 3rd party C extensions, the linker flags used by the compiler for the interpreter and the stdlib modules, will get leaked into distutils. In order to avoid that, the PY_CORE_LDFLAGS and PY_LDFLAGS_NODIST are introduced to keep those flags separated. files: A Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst M Lib/_osx_support.py M Lib/test/pythoninfo.py M Lib/test/test__osx_support.py M Makefile.pre.in M configure M configure.ac M setup.py diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py index e37852e2536c..db6674ea293f 100644 --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -17,7 +17,7 @@ _UNIVERSAL_CONFIG_VARS = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', - 'PY_CORE_CFLAGS') + 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS') # configuration variables that may contain compiler calls _COMPILER_CONFIG_VARS = ('BLDSHARED', 'LDSHARED', 'CC', 'CXX') diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 81fca10eaeb1..26bcf5f12d66 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -411,7 +411,10 @@ def collect_sysconfig(info_add): 'OPT', 'PY_CFLAGS', 'PY_CFLAGS_NODIST', + 'PY_CORE_LDFLAGS', 'PY_LDFLAGS', + 'PY_LDFLAGS_NODIST', + 'PY_STDMODULE_CFLAGS', 'Py_DEBUG', 'Py_ENABLE_SHARED', 'SHELL', diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py index bcba8caa2964..388a2b1a84b1 100644 --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -24,7 +24,7 @@ def setUp(self): for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', - 'PY_CORE_CFLAGS'): + 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS'): if cv in self.env: self.env.unset(cv) diff --git a/Makefile.pre.in b/Makefile.pre.in index 9c790f1bc60b..518602b1a22f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -84,6 +84,10 @@ CONFIGURE_CFLAGS= @CFLAGS@ # Use it when a compiler flag should _not_ be part of the distutils CFLAGS # once Python is installed (Issue #21121). CONFIGURE_CFLAGS_NODIST=@CFLAGS_NODIST@ +# LDFLAGS_NODIST is used in the same manner as CFLAGS_NODIST. +# Use it when a linker flag should _not_ be part of the distutils LDFLAGS +# once Python is installed (bpo-35257) +CONFIGURE_LDFLAGS_NODIST=@LDFLAGS_NODIST@ CONFIGURE_CPPFLAGS= @CPPFLAGS@ CONFIGURE_LDFLAGS= @LDFLAGS@ # Avoid assigning CFLAGS, LDFLAGS, etc. so users can use them on the @@ -96,6 +100,7 @@ PY_CFLAGS_NODIST=$(CONFIGURE_CFLAGS_NODIST) $(CFLAGS_NODIST) -I$(srcdir)/Include # environment variables PY_CPPFLAGS= $(BASECPPFLAGS) -I. -I$(srcdir)/Include $(CONFIGURE_CPPFLAGS) $(CPPFLAGS) PY_LDFLAGS= $(CONFIGURE_LDFLAGS) $(LDFLAGS) +PY_LDFLAGS_NODIST=$(CONFIGURE_LDFLAGS_NODIST) $(LDFLAGS_NODIST) NO_AS_NEEDED= @NO_AS_NEEDED@ SGI_ABI= @SGI_ABI@ CCSHARED= @CCSHARED@ @@ -107,6 +112,8 @@ CFLAGSFORSHARED=@CFLAGSFORSHARED@ PY_STDMODULE_CFLAGS= $(PY_CFLAGS) $(PY_CFLAGS_NODIST) $(PY_CPPFLAGS) $(CFLAGSFORSHARED) PY_BUILTIN_MODULE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE_BUILTIN PY_CORE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE +# Linker flags used for building the interpreter object files +PY_CORE_LDFLAGS=$(PY_LDFLAGS) $(PY_LDFLAGS_NODIST) # Strict or non-strict aliasing flags used to compile dtoa.c, see above CFLAGS_ALIASING=@CFLAGS_ALIASING@ @@ -146,7 +153,7 @@ CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(LDVERSION) SHLIB_SUFFIX= @SHLIB_SUFFIX@ EXT_SUFFIX= @EXT_SUFFIX@ LDSHARED= @LDSHARED@ $(PY_LDFLAGS) -BLDSHARED= @BLDSHARED@ $(PY_LDFLAGS) +BLDSHARED= @BLDSHARED@ $(PY_LDFLAGS_NODIST) LDCXXSHARED= @LDCXXSHARED@ DESTSHARED= $(BINLIBDEST)/lib-dynload @@ -496,7 +503,7 @@ profile-run-stamp: touch $@ build_all_generate_profile: - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" run_profile_task: @ # FIXME: can't run for a cross build @@ -510,7 +517,7 @@ build_all_merge_profile: profile-opt: profile-run-stamp @echo "Rebuilding with profile guided optimizations:" -rm -f profile-clean-stamp - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST)" # Compile and run with gcov .PHONY=coverage coverage-lcov coverage-report @@ -567,7 +574,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c # Build the interpreter $(BUILDPYTHON): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) - $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) + $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) platform: $(BUILDPYTHON) pybuilddir.txt $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform @@ -632,7 +639,7 @@ libpython3.so: libpython$(LDVERSION).so $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) - $(CC) -dynamiclib -Wl,-single_module $(PY_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ + $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ libpython$(VERSION).sl: $(LIBRARY_OBJS) @@ -657,7 +664,7 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ $(LIBRARY) \ $(RESSRCDIR)/Info.plist $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION) - $(CC) -o $(LDLIBRARY) $(PY_LDFLAGS) -dynamiclib \ + $(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \ -all_load $(LIBRARY) -Wl,-single_module \ -install_name $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \ -compatibility_version $(VERSION) \ @@ -698,7 +705,7 @@ Makefile Modules/config.c: Makefile.pre \ Programs/_testembed: Programs/_testembed.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) - $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) + $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) ############################################################################ # Importlib @@ -706,7 +713,7 @@ Programs/_testembed: Programs/_testembed.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) Programs/_freeze_importlib.o: Programs/_freeze_importlib.c Makefile Programs/_freeze_importlib: Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) - $(LINKCC) $(PY_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) + $(LINKCC) $(PY_CORE_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) .PHONY: regen-importlib regen-importlib: Programs/_freeze_importlib @@ -794,7 +801,7 @@ Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile $(IO_OBJS): $(IO_H) $(PGEN): $(PGENOBJS) - $(CC) $(OPT) $(PY_LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN) + $(CC) $(OPT) $(PY_CORE_LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN) .PHONY: regen-grammar regen-grammar: $(PGEN) diff --git a/Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst b/Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst new file mode 100644 index 000000000000..fad252578299 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst @@ -0,0 +1,2 @@ +Avoid leaking the linker flags from Link Time Optimizations (LTO) +into distutils when compiling C extensions. \ No newline at end of file diff --git a/configure b/configure index 8d3336387c34..edb85b524f54 100755 --- a/configure +++ b/configure @@ -666,6 +666,7 @@ SHLIB_SUFFIX LIBTOOL_CRUFT OTHER_LIBTOOL_OPT UNIVERSAL_ARCH_FLAGS +LDFLAGS_NODIST CFLAGS_NODIST BASECFLAGS CFLAGS_ALIASING @@ -6627,7 +6628,7 @@ $as_echo "$as_me: llvm-ar found via xcrun: ${LLVM_AR}" >&6;} fi CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" - LDFLAGS="$LDFLAGS $LTOFLAGS" + LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS" fi # Enable PGO flags. @@ -6879,6 +6880,7 @@ fi + # The -arch flags for universal builds on OSX UNIVERSAL_ARCH_FLAGS= diff --git a/configure.ac b/configure.ac index 5c1c02192087..349c927484cd 100644 --- a/configure.ac +++ b/configure.ac @@ -1358,7 +1358,7 @@ if test "$Py_LTO" = 'true' ; then fi CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" - LDFLAGS="$LDFLAGS $LTOFLAGS" + LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS" fi # Enable PGO flags. @@ -1518,6 +1518,7 @@ fi AC_SUBST(BASECFLAGS) AC_SUBST(CFLAGS_NODIST) +AC_SUBST(LDFLAGS_NODIST) # The -arch flags for universal builds on OSX UNIVERSAL_ARCH_FLAGS= diff --git a/setup.py b/setup.py index b87d05d2143f..44a563bce459 100644 --- a/setup.py +++ b/setup.py @@ -18,11 +18,16 @@ cross_compiling = "_PYTHON_HOST_PLATFORM" in os.environ -# Add special CFLAGS reserved for building the interpreter and the stdlib -# modules (Issue #21121). -cflags = sysconfig.get_config_var('CFLAGS') -py_cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') -sysconfig.get_config_vars()['CFLAGS'] = cflags + ' ' + py_cflags_nodist +# Set common compiler and linker flags derived from the Makefile, +# reserved for building the interpreter and the stdlib modules. +# See bpo-21121 and bpo-35257 +def set_compiler_flags(compiler_flags, compiler_py_flags_nodist): + flags = sysconfig.get_config_var(compiler_flags) + py_flags_nodist = sysconfig.get_config_var(compiler_py_flags_nodist) + sysconfig.get_config_vars()[compiler_flags] = flags + ' ' + py_flags_nodist + +set_compiler_flags('CFLAGS', 'PY_CFLAGS_NODIST') +set_compiler_flags('LDFLAGS', 'PY_LDFLAGS_NODIST') class Dummy: """Hack for parallel build""" From webhook-mailer at python.org Wed Dec 19 14:13:26 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Wed, 19 Dec 2018 19:13:26 -0000 Subject: [Python-checkins] bpo-32077: Update refcounts.dat for Unicode object functions. (GH-11243) Message-ID: https://github.com/python/cpython/commit/b2f642ccd2f65d2f3bf77bbaa103dd2bc2733734 commit: b2f642ccd2f65d2f3bf77bbaa103dd2bc2733734 branch: master author: Mat M committer: Serhiy Storchaka date: 2018-12-19T21:13:15+02:00 summary: bpo-32077: Update refcounts.dat for Unicode object functions. (GH-11243) Makes the documentation more comprehensive in terms of indicating whether or not a function returns a new reference. Also fixes some errors and adds missing functions. files: M Doc/data/refcounts.dat diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index cedcbfe3b834..add0600676f1 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -1618,13 +1618,24 @@ Py_UNICODE_TONUMERIC:Py_UNICODE:ch:: PyUnicode_FromUnicode:PyObject*::+1: PyUnicode_FromUnicode:const Py_UNICODE*:u:: -PyUnicode_FromUnicode:int:size:: +PyUnicode_FromUnicode:Py_ssize_t:size:: PyUnicode_AsUnicode:Py_UNICODE*::: -PyUnicode_AsUnicode:PyObject :*unicode:0: +PyUnicode_AsUnicode:PyObject*:unicode:0: -PyUnicode_GetSize:int::: -PyUnicode_GetSize:PyObject :*unicode:0: +PyUnicode_TransformDecimalToASCII:PyObject*::+1: +PyUnicode_TransformDecimalToASCII:Py_UNICODE*:s:: +PyUnicode_TransformDecimalToASCII:Py_ssize_t:size:: + +PyUnicode_AsUnicodeAndSize:Py_UNICODE*::: +PyUnicode_AsUnicodeAndSize:PyObject*:unicode:0: +PyUnicode_AsUnicodeAndSize:Py_ssize_t*:size:: + +PyUnicode_AsUnicodeCopy:Py_UNICODE*::: +PyUnicode_AsUnicodeCopy:PyObject*:unicode:0: + +PyUnicode_GetSize:Py_ssize_t::: +PyUnicode_GetSize:PyObject*:unicode:0: PyUnicode_FromObject:PyObject*::+1: PyUnicode_FromObject:PyObject*:*obj:0: @@ -1636,35 +1647,39 @@ PyUnicode_FromEncodedObject:const char*:errors:: PyUnicode_FromWideChar:PyObject*::+1: PyUnicode_FromWideChar:const wchar_t*:w:: -PyUnicode_FromWideChar:int:size:: +PyUnicode_FromWideChar:Py_ssize_t:size:: -PyUnicode_AsWideChar:int::: +PyUnicode_AsWideChar:Py_ssize_t::: PyUnicode_AsWideChar:PyObject*:*unicode:0: PyUnicode_AsWideChar:wchar_t*:w:: -PyUnicode_AsWideChar:int:size:: +PyUnicode_AsWideChar:Pyssize_t:size:: + +PyUnicode_AsWideCharString:wchar_t*::: +PyUnicode_AsWideCharString:PyObject*:unicode:0: +PyUnicode_AsWideCharString:Py_ssize_t*:size:: PyUnicode_Decode:PyObject*::+1: PyUnicode_Decode:const char*:s:: -PyUnicode_Decode:int:size:: +PyUnicode_Decode:Py_ssize_t:size:: PyUnicode_Decode:const char*:encoding:: PyUnicode_Decode:const char*:errors:: PyUnicode_DecodeUTF16Stateful:PyObject*::+1: PyUnicode_DecodeUTF16Stateful:const char*:s:: -PyUnicode_DecodeUTF16Stateful:int:size:: +PyUnicode_DecodeUTF16Stateful:Py_ssize_t:size:: PyUnicode_DecodeUTF16Stateful:const char*:errors:: PyUnicode_DecodeUTF16Stateful:int*:byteorder:: -PyUnicode_DecodeUTF16Stateful:int*:consumed:: +PyUnicode_DecodeUTF16Stateful:Py_ssize_t*:consumed:: PyUnicode_DecodeUTF8Stateful:PyObject*::+1: PyUnicode_DecodeUTF8Stateful:const char*:s:: -PyUnicode_DecodeUTF8Stateful:int:size:: +PyUnicode_DecodeUTF8Stateful:Py_ssize_t:size:: PyUnicode_DecodeUTF8Stateful:const char*:errors:: -PyUnicode_DecodeUTF8Stateful:int*:consumed:: +PyUnicode_DecodeUTF8Stateful:Py_ssize_t*:consumed:: PyUnicode_Encode:PyObject*::+1: PyUnicode_Encode:const Py_UNICODE*:s:: -PyUnicode_Encode:int:size:: +PyUnicode_Encode:Py_ssize_t:size:: PyUnicode_Encode:const char*:encoding:: PyUnicode_Encode:const char*:errors:: @@ -1673,68 +1688,113 @@ PyUnicode_AsEncodedString:PyObject*:unicode:: PyUnicode_AsEncodedString:const char*:encoding:: PyUnicode_AsEncodedString:const char*:errors:: +PyUnicode_DecodeUTF7:PyObject*::+1: +PyUnicode_DecodeUTF7:const char*:s:: +PyUnicode_DecodeUTF7:Py_ssize_t:size:: +PyUnicode_DecodeUTF7:const char*:errors:: + +PyUnicode_DecodeUTF7Stateful:PyObject*::+1: +PyUnicode_DecodeUTF7Stateful:const char*:s:: +PyUnicode_DecodeUTF7Stateful:Py_ssize_t:size:: +PyUnicode_DecodeUTF7Stateful:const char*:errors:: +PyUnicode_DecodeUTF7Stateful:Py_ssize_t*:consumed:: + +PyUnicode_EncodeUTF7:PyObject*::+1: +PyUnicode_EncodeUTF7:const Py_UNICODE*:s:: +PyUnicode_EncodeUTF7:Py_ssize_t:size:: +PyUnicode_EncodeUTF7:int:base64SetO:: +PyUnicode_EncodeUTF7:int:base64WhiteSpace:: +PyUnicode_EncodeUTF7:const char*:errors:: + PyUnicode_DecodeUTF8:PyObject*::+1: PyUnicode_DecodeUTF8:const char*:s:: -PyUnicode_DecodeUTF8:int:size:: +PyUnicode_DecodeUTF8:Py_ssize_t:size:: PyUnicode_DecodeUTF8:const char*:errors:: PyUnicode_EncodeUTF8:PyObject*::+1: PyUnicode_EncodeUTF8:const Py_UNICODE*:s:: -PyUnicode_EncodeUTF8:int:size:: +PyUnicode_EncodeUTF8:Py_ssize_t:size:: PyUnicode_EncodeUTF8:const char*:errors:: PyUnicode_AsUTF8String:PyObject*::+1: PyUnicode_AsUTF8String:PyObject*:unicode:: +PyUnicode_AsUTF8AndSize:const char*::: +PyUnicode_AsUTF8AndSize:PyObject*:unicode:0: +PyUnicode_AsUTF8AndSize:Py_ssize_t*:size:0: + +PyUnicode_AsUTF8:const char*::: +PyUnicode_AsUTF8:PyObject*:unicode:0: + PyUnicode_DecodeUTF16:PyObject*::+1: PyUnicode_DecodeUTF16:const char*:s:: -PyUnicode_DecodeUTF16:int:size:: +PyUnicode_DecodeUTF16:Py_ssize_t:size:: PyUnicode_DecodeUTF16:const char*:errors:: PyUnicode_DecodeUTF16:int*:byteorder:: PyUnicode_EncodeUTF16:PyObject*::+1: PyUnicode_EncodeUTF16:const Py_UNICODE*:s:: -PyUnicode_EncodeUTF16:int:size:: +PyUnicode_EncodeUTF16:Py_ssize_t:size:: PyUnicode_EncodeUTF16:const char*:errors:: PyUnicode_EncodeUTF16:int:byteorder:: PyUnicode_AsUTF16String:PyObject*::+1: PyUnicode_AsUTF16String:PyObject*:unicode:: +PyUnicode_DecodeUTF32:PyObject*::+1: +PyUnicode_DecodeUTF32:const char*:s:: +PyUnicode_DecodeUTF32:Py_ssize_t:size:: +PyUnicode_DecodeUTF32:const char*:errors:: +PyUnicode_DecodeUTF32:int*:byteorder:: + +PyUnicode_DecodeUTF32Stateful:PyObject*::+1: +PyUnicode_DecodeUTF32Stateful:const char*:s:: +PyUnicode_DecodeUTF32Stateful:Py_ssize_t:size:: +PyUnicode_DecodeUTF32Stateful:const char*:errors:: +PyUnicode_DecodeUTF32Stateful:int*:byteorder:: +PyUnicode_DecodeUTF32Stateful:Py_ssize_t*:consumed:: + +PyUnicode_AsUTF32String:PyObject*::+1: +PyUnicode_AsUTF32String:PyObject*:unicode:0: + +PyUnicode_EncodeUTF32:PyObject*::+1: +PyUnicode_EncodeUTF32:const Py_UNICODE*:s:: +PyUnicode_EncodeUTF32:Py_ssize_t:size:: +PyUnicode_EncodeUTF32:const char*:errors:: +PyUnicode_EncodeUTF32:int:byteorder:: + PyUnicode_DecodeUnicodeEscape:PyObject*::+1: PyUnicode_DecodeUnicodeEscape:const char*:s:: -PyUnicode_DecodeUnicodeEscape:int:size:: +PyUnicode_DecodeUnicodeEscape:Py_ssize_t:size:: PyUnicode_DecodeUnicodeEscape:const char*:errors:: PyUnicode_EncodeUnicodeEscape:PyObject*::+1: PyUnicode_EncodeUnicodeEscape:const Py_UNICODE*:s:: -PyUnicode_EncodeUnicodeEscape:int:size:: -PyUnicode_EncodeUnicodeEscape:const char*:errors:: +PyUnicode_EncodeUnicodeEscape:Py_ssize_t:size:: PyUnicode_AsUnicodeEscapeString:PyObject*::+1: PyUnicode_AsUnicodeEscapeString:PyObject*:unicode:: PyUnicode_DecodeRawUnicodeEscape:PyObject*::+1: PyUnicode_DecodeRawUnicodeEscape:const char*:s:: -PyUnicode_DecodeRawUnicodeEscape:int:size:: +PyUnicode_DecodeRawUnicodeEscape:Py_ssize_t:size:: PyUnicode_DecodeRawUnicodeEscape:const char*:errors:: PyUnicode_EncodeRawUnicodeEscape:PyObject*::+1: PyUnicode_EncodeRawUnicodeEscape:const Py_UNICODE*:s:: -PyUnicode_EncodeRawUnicodeEscape:int:size:: -PyUnicode_EncodeRawUnicodeEscape:const char*:errors:: +PyUnicode_EncodeRawUnicodeEscape:Py_ssize_t:size:: PyUnicode_AsRawUnicodeEscapeString:PyObject*::+1: PyUnicode_AsRawUnicodeEscapeString:PyObject*:unicode:: PyUnicode_DecodeLatin1:PyObject*::+1: PyUnicode_DecodeLatin1:const char*:s:: -PyUnicode_DecodeLatin1:int:size:: +PyUnicode_DecodeLatin1:Py_ssize_t:size:: PyUnicode_DecodeLatin1:const char*:errors:: PyUnicode_EncodeLatin1:PyObject*::+1: PyUnicode_EncodeLatin1:const Py_UNICODE*:s:: -PyUnicode_EncodeLatin1:int:size:: +PyUnicode_EncodeLatin1:Py_ssize_t:size:: PyUnicode_EncodeLatin1:const char*:errors:: PyUnicode_AsLatin1String:PyObject*::+1: @@ -1742,12 +1802,12 @@ PyUnicode_AsLatin1String:PyObject*:unicode:: PyUnicode_DecodeASCII:PyObject*::+1: PyUnicode_DecodeASCII:const char*:s:: -PyUnicode_DecodeASCII:int:size:: +PyUnicode_DecodeASCII:Py_ssize_t:size:: PyUnicode_DecodeASCII:const char*:errors:: PyUnicode_EncodeASCII:PyObject*::+1: PyUnicode_EncodeASCII:const Py_UNICODE*:s:: -PyUnicode_EncodeASCII:int:size:: +PyUnicode_EncodeASCII:Py_ssize_t:size:: PyUnicode_EncodeASCII:const char*:errors:: PyUnicode_AsASCIIString:PyObject*::+1: @@ -1755,13 +1815,13 @@ PyUnicode_AsASCIIString:PyObject*:unicode:: PyUnicode_DecodeCharmap:PyObject*::+1: PyUnicode_DecodeCharmap:const char*:s:: -PyUnicode_DecodeCharmap:int:size:: +PyUnicode_DecodeCharmap:Py_ssize_t:size:: PyUnicode_DecodeCharmap:PyObject*:mapping:0: PyUnicode_DecodeCharmap:const char*:errors:: PyUnicode_EncodeCharmap:PyObject*::+1: PyUnicode_EncodeCharmap:const Py_UNICODE*:s:: -PyUnicode_EncodeCharmap:int:size:: +PyUnicode_EncodeCharmap:Py_ssize_t:size:: PyUnicode_EncodeCharmap:PyObject*:mapping:0: PyUnicode_EncodeCharmap:const char*:errors:: @@ -1771,18 +1831,29 @@ PyUnicode_AsCharmapString:PyObject*:mapping:0: PyUnicode_TranslateCharmap:PyObject*::+1: PyUnicode_TranslateCharmap:const Py_UNICODE*:s:: -PyUnicode_TranslateCharmap:int:size:: +PyUnicode_TranslateCharmap:Py_ssize_t:size:: PyUnicode_TranslateCharmap:PyObject*:table:0: PyUnicode_TranslateCharmap:const char*:errors:: PyUnicode_DecodeMBCS:PyObject*::+1: PyUnicode_DecodeMBCS:const char*:s:: -PyUnicode_DecodeMBCS:int:size:: +PyUnicode_DecodeMBCS:Py_ssize_t:size:: PyUnicode_DecodeMBCS:const char*:errors:: +PyUnicode_DecodeMBCSStateful:PyObject::+1: +PyUnicode_DecodeMBCSStateful:const char*:s:: +PyUnicode_DecodeMBCSStateful:Py_ssize_t:size:: +PyUnicode_DecodeMBCSStateful:const char*:errors:: +PyUnicode_DecodeMBCSStateful:Py_ssize_t*:consumed:: + +PyUnicode_EncodeCodePage:PyObject*::+1: +PyUnicode_EncodeCodePage:int:code_page:: +PyUnicode_EncodeCodePage:PyObject*:unicode:0: +PyUnicode_EncodeCodePage:const char*:errors:: + PyUnicode_EncodeMBCS:PyObject*::+1: PyUnicode_EncodeMBCS:const Py_UNICODE*:s:: -PyUnicode_EncodeMBCS:int:size:: +PyUnicode_EncodeMBCS:Py_ssize_t:size:: PyUnicode_EncodeMBCS:const char*:errors:: PyUnicode_AsMBCSString:PyObject*::+1: @@ -1795,11 +1866,11 @@ PyUnicode_Concat:PyObject*:right:0: PyUnicode_Split:PyObject*::+1: PyUnicode_Split:PyObject*:left:0: PyUnicode_Split:PyObject*:right:0: -PyUnicode_Split:int:maxsplit:: +PyUnicode_Split:Py_ssize_t:maxsplit:: PyUnicode_Splitlines:PyObject*::+1: PyUnicode_Splitlines:PyObject*:s:0: -PyUnicode_Splitlines:int:maxsplit:: +PyUnicode_Splitlines:int:keepend:: PyUnicode_Translate:PyObject*::+1: PyUnicode_Translate:PyObject*:str:0: @@ -1810,36 +1881,52 @@ PyUnicode_Join:PyObject*::+1: PyUnicode_Join:PyObject*:separator:0: PyUnicode_Join:PyObject*:seq:0: -PyUnicode_Tailmatch:int::: +PyUnicode_Tailmatch:Py_ssize_t::: PyUnicode_Tailmatch:PyObject*:str:0: PyUnicode_Tailmatch:PyObject*:substr:0: -PyUnicode_Tailmatch:int:start:: -PyUnicode_Tailmatch:int:end:: +PyUnicode_Tailmatch:Py_ssize_t:start:: +PyUnicode_Tailmatch:Py_ssize_t:end:: PyUnicode_Tailmatch:int:direction:: -PyUnicode_Find:int::: +PyUnicode_Find:Py_ssize_t::: PyUnicode_Find:PyObject*:str:0: PyUnicode_Find:PyObject*:substr:0: -PyUnicode_Find:int:start:: -PyUnicode_Find:int:end:: +PyUnicode_Find:Py_ssize_t:start:: +PyUnicode_Find:Py_ssize_t:end:: PyUnicode_Find:int:direction:: -PyUnicode_Count:int::: +PyUnicode_FindChar:Py_ssize_t::: +PyUnicode_FindChar:PyObject*:str:0: +PyUnicode_FindChar:Py_UCS4:ch:: +PyUnicode_FindChar:Py_ssize_t:start:: +PyUnicode_FindChar:Py_ssize_t:end:: +PyUnicode_FindChar:int:direction:: + +PyUnicode_Count:Py_ssize_t::: PyUnicode_Count:PyObject*:str:0: PyUnicode_Count:PyObject*:substr:0: -PyUnicode_Count:int:start:: -PyUnicode_Count:int:end:: +PyUnicode_Count:Py_ssize_t:start:: +PyUnicode_Count:Py_ssize_t:end:: PyUnicode_Replace:PyObject*::+1: PyUnicode_Replace:PyObject*:str:0: PyUnicode_Replace:PyObject*:substr:0: PyUnicode_Replace:PyObject*:replstr:0: -PyUnicode_Replace:int:maxcount:: +PyUnicode_Replace:Py_ssize_t:maxcount:: PyUnicode_Compare:int::: PyUnicode_Compare:PyObject*:left:0: PyUnicode_Compare:PyObject*:right:0: +PyUnicode_CompareWithASCIIString:int::: +PyUnicode_CompareWithASCIIString:PyObject*:uni:0: +PyUnicode_CompareWithASCIIString:const char*:string:: + +PyUnicode_RichCompare:PyObject::+1: +PyUnicode_RichCompare:PyObject*:left:0: +PyUnicode_RichCompare:PyObject*:right:0: +PyUnicode_RichCompare:int:op:: + PyUnicode_Format:PyObject*::+1: PyUnicode_Format:PyObject*:format:0: PyUnicode_Format:PyObject*:args:0: @@ -1848,6 +1935,106 @@ PyUnicode_Contains:int::: PyUnicode_Contains:PyObject*:container:0: PyUnicode_Contains:PyObject*:element:0: +PyUnicode_InternInPlace:void::: +PyUnicode_InternInPlace:PyObject**:string:+1: + +PyUnicode_InternFromString:PyObject*::+1: +PyUnicode_InternFromString:const char*:cp:: + +PyUnicode_New:PyObject*::+1: +PyUnicode_New:Py_ssize_t:size:: +PyUnicode_New:Py_UCS4:maxchar:: + +PyUnicode_FromKindAndData:PyObject*::+1: +PyUnicode_FromKindAndData:int:kind:: +PyUnicode_FromKindAndData:const void*:buffer:: +PyUnicode_FromKindAndData:Py_ssize_t:size:: + +PyUnicode_FromStringAndSize:PyObject*::+1: +PyUnicode_FromStringAndSize:const char*:u:: +PyUnicode_FromStringAndSize:Py_ssize_t:size:: + +PyUnicode_FromString:PyObject*::+1: +PyUnicode_FromString:const char*:u:: + +PyUnicode_FromFormat:PyObject*::+1: +PyUnicode_FromFormat:const char*:format:: +PyUnicode_FromFormat::...:: + +PyUnicode_FromFormatV:PyObject*::+1: +PyUnicode_FromFormatV:const char*:format:: +PyUnicode_FromFormatV:va_list:args:: + +PyUnicode_GetLength:Py_ssize_t::: +PyUnicode_GetLength:PyObject*:unicode:0: + +PyUnicode_CopyCharacters:Py_ssize_t::: +PyUnicode_CopyCharacters:PyObject*:to:0: +PyUnicode_CopyCharacters:Py_ssize_t:to_start:: +PyUnicode_CopyCharacters:PyObject*:from:0: +PyUnicode_CopyCharacters:Py_ssize_t:from_start:: +PyUnicode_CopyCharacters:Py_ssize_t:how_many:: + +PyUnicode_Fill:Py_ssize_t::: +PyUnicode_Fill:PyObject*:unicode:0: +PyUnicode_Fill:Py_ssize_t:index:: +PyUnicode_Fill:Py_ssize_t:length:: +PyUnicode_Fill:Py_UCS4:fill_char:: + +PyUnicode_ReadChar:Py_UCS4::: +PyUnicode_ReadChar:PyObject*:unicode:0: +PyUnicode_ReadChar:Py_ssize_t:index:: + +PyUnicode_WriteChar:int::: +PyUnicode_WriteChar:PyObject*:unicode:0: +PyUnicode_WriteChar:Py_ssize_t:index:: +PyUnicode_WriteChar:Py_UCS4:character:: + +PyUnicode_Substring:PyObject*::+1: +PyUnicode_Substring:PyObject*:str:0: +PyUnicode_Substring:Py_ssize_t:start:: +PyUnicode_Substring:Py_ssize_t:end:: + +PyUnicode_AsUCS4:Py_UCS4*::: +PyUnicode_AsUCS4:PyObject*:u:0: +PyUnicode_AsUCS4:Py_UCS4*:buffer:: +PyUnicode_AsUCS4:Py_ssize_t:buflen:: +PyUnicode_AsUCS4:int:copy_null:: + +PyUnicode_AsUCS4Copy:Py_UCS4*::: +PyUnicode_AsUCS4Copy:PyObject*:u:0: + +PyUnicode_DecodeLocaleAndSize:PyObject*::+1: +PyUnicode_DecodeLocaleAndSize:const char*:str:: +PyUnicode_DecodeLocaleAndSize:Py_ssize_t:len:: +PyUnicode_DecodeLocaleAndSize:const char*:errors:: + +PyUnicode_DecodeLocale:PyObject*::+1: +PyUnicode_DecodeLocale:const char*:str:: +PyUnicode_DecodeLocale:const char*:errors:: + +PyUnicode_EncodeLocale:PyObject*::+1: +PyUnicode_EncodeLocale:PyObject*:unicode:0: +PyUnicode_EncodeLocale:const char*:errors:: + +PyUnicode_FSConverter:int::: +PyUnicode_FSConverter:PyObject*:obj:0: +PyUnicode_FSConverter:void*:result:: + +PyUnicode_FSDecoder:int::: +PyUnicode_FSDecoder:PyObject*:obj:0: +PyUnicode_FSDecoder:void*:result:: + +PyUnicode_DecodeFSDefaultAndSize:PyObject*::+1: +PyUnicode_DecodeFSDefaultAndSize:const char*:s:: +PyUnicode_DecodeFSDefaultAndSize:Py_ssize_t:size:: + +PyUnicode_DecodeFSDefault:PyObject::+1: +PyUnicode_DecodeFSDefault:const char*:s:: + +PyUnicode_EncodeFSDefault:PyObject::+1: +PyUnicode_EncodeFSDefault:PyObject:unicode:0: + PyWeakref_GET_OBJECT:PyObject*::0: PyWeakref_GET_OBJECT:PyObject*:ref:0: From webhook-mailer at python.org Wed Dec 19 15:03:27 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Wed, 19 Dec 2018 20:03:27 -0000 Subject: [Python-checkins] bpo-32077: Update refcounts.dat for Unicode object functions. (GH-11243) Message-ID: https://github.com/python/cpython/commit/29d4e309b1b4dfb26d65d83c683002911c066dec commit: 29d4e309b1b4dfb26d65d83c683002911c066dec branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-19T12:03:20-08:00 summary: bpo-32077: Update refcounts.dat for Unicode object functions. (GH-11243) Makes the documentation more comprehensive in terms of indicating whether or not a function returns a new reference. Also fixes some errors and adds missing functions. (cherry picked from commit b2f642ccd2f65d2f3bf77bbaa103dd2bc2733734) Co-authored-by: Mat M files: M Doc/data/refcounts.dat diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index cedcbfe3b834..add0600676f1 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -1618,13 +1618,24 @@ Py_UNICODE_TONUMERIC:Py_UNICODE:ch:: PyUnicode_FromUnicode:PyObject*::+1: PyUnicode_FromUnicode:const Py_UNICODE*:u:: -PyUnicode_FromUnicode:int:size:: +PyUnicode_FromUnicode:Py_ssize_t:size:: PyUnicode_AsUnicode:Py_UNICODE*::: -PyUnicode_AsUnicode:PyObject :*unicode:0: +PyUnicode_AsUnicode:PyObject*:unicode:0: -PyUnicode_GetSize:int::: -PyUnicode_GetSize:PyObject :*unicode:0: +PyUnicode_TransformDecimalToASCII:PyObject*::+1: +PyUnicode_TransformDecimalToASCII:Py_UNICODE*:s:: +PyUnicode_TransformDecimalToASCII:Py_ssize_t:size:: + +PyUnicode_AsUnicodeAndSize:Py_UNICODE*::: +PyUnicode_AsUnicodeAndSize:PyObject*:unicode:0: +PyUnicode_AsUnicodeAndSize:Py_ssize_t*:size:: + +PyUnicode_AsUnicodeCopy:Py_UNICODE*::: +PyUnicode_AsUnicodeCopy:PyObject*:unicode:0: + +PyUnicode_GetSize:Py_ssize_t::: +PyUnicode_GetSize:PyObject*:unicode:0: PyUnicode_FromObject:PyObject*::+1: PyUnicode_FromObject:PyObject*:*obj:0: @@ -1636,35 +1647,39 @@ PyUnicode_FromEncodedObject:const char*:errors:: PyUnicode_FromWideChar:PyObject*::+1: PyUnicode_FromWideChar:const wchar_t*:w:: -PyUnicode_FromWideChar:int:size:: +PyUnicode_FromWideChar:Py_ssize_t:size:: -PyUnicode_AsWideChar:int::: +PyUnicode_AsWideChar:Py_ssize_t::: PyUnicode_AsWideChar:PyObject*:*unicode:0: PyUnicode_AsWideChar:wchar_t*:w:: -PyUnicode_AsWideChar:int:size:: +PyUnicode_AsWideChar:Pyssize_t:size:: + +PyUnicode_AsWideCharString:wchar_t*::: +PyUnicode_AsWideCharString:PyObject*:unicode:0: +PyUnicode_AsWideCharString:Py_ssize_t*:size:: PyUnicode_Decode:PyObject*::+1: PyUnicode_Decode:const char*:s:: -PyUnicode_Decode:int:size:: +PyUnicode_Decode:Py_ssize_t:size:: PyUnicode_Decode:const char*:encoding:: PyUnicode_Decode:const char*:errors:: PyUnicode_DecodeUTF16Stateful:PyObject*::+1: PyUnicode_DecodeUTF16Stateful:const char*:s:: -PyUnicode_DecodeUTF16Stateful:int:size:: +PyUnicode_DecodeUTF16Stateful:Py_ssize_t:size:: PyUnicode_DecodeUTF16Stateful:const char*:errors:: PyUnicode_DecodeUTF16Stateful:int*:byteorder:: -PyUnicode_DecodeUTF16Stateful:int*:consumed:: +PyUnicode_DecodeUTF16Stateful:Py_ssize_t*:consumed:: PyUnicode_DecodeUTF8Stateful:PyObject*::+1: PyUnicode_DecodeUTF8Stateful:const char*:s:: -PyUnicode_DecodeUTF8Stateful:int:size:: +PyUnicode_DecodeUTF8Stateful:Py_ssize_t:size:: PyUnicode_DecodeUTF8Stateful:const char*:errors:: -PyUnicode_DecodeUTF8Stateful:int*:consumed:: +PyUnicode_DecodeUTF8Stateful:Py_ssize_t*:consumed:: PyUnicode_Encode:PyObject*::+1: PyUnicode_Encode:const Py_UNICODE*:s:: -PyUnicode_Encode:int:size:: +PyUnicode_Encode:Py_ssize_t:size:: PyUnicode_Encode:const char*:encoding:: PyUnicode_Encode:const char*:errors:: @@ -1673,68 +1688,113 @@ PyUnicode_AsEncodedString:PyObject*:unicode:: PyUnicode_AsEncodedString:const char*:encoding:: PyUnicode_AsEncodedString:const char*:errors:: +PyUnicode_DecodeUTF7:PyObject*::+1: +PyUnicode_DecodeUTF7:const char*:s:: +PyUnicode_DecodeUTF7:Py_ssize_t:size:: +PyUnicode_DecodeUTF7:const char*:errors:: + +PyUnicode_DecodeUTF7Stateful:PyObject*::+1: +PyUnicode_DecodeUTF7Stateful:const char*:s:: +PyUnicode_DecodeUTF7Stateful:Py_ssize_t:size:: +PyUnicode_DecodeUTF7Stateful:const char*:errors:: +PyUnicode_DecodeUTF7Stateful:Py_ssize_t*:consumed:: + +PyUnicode_EncodeUTF7:PyObject*::+1: +PyUnicode_EncodeUTF7:const Py_UNICODE*:s:: +PyUnicode_EncodeUTF7:Py_ssize_t:size:: +PyUnicode_EncodeUTF7:int:base64SetO:: +PyUnicode_EncodeUTF7:int:base64WhiteSpace:: +PyUnicode_EncodeUTF7:const char*:errors:: + PyUnicode_DecodeUTF8:PyObject*::+1: PyUnicode_DecodeUTF8:const char*:s:: -PyUnicode_DecodeUTF8:int:size:: +PyUnicode_DecodeUTF8:Py_ssize_t:size:: PyUnicode_DecodeUTF8:const char*:errors:: PyUnicode_EncodeUTF8:PyObject*::+1: PyUnicode_EncodeUTF8:const Py_UNICODE*:s:: -PyUnicode_EncodeUTF8:int:size:: +PyUnicode_EncodeUTF8:Py_ssize_t:size:: PyUnicode_EncodeUTF8:const char*:errors:: PyUnicode_AsUTF8String:PyObject*::+1: PyUnicode_AsUTF8String:PyObject*:unicode:: +PyUnicode_AsUTF8AndSize:const char*::: +PyUnicode_AsUTF8AndSize:PyObject*:unicode:0: +PyUnicode_AsUTF8AndSize:Py_ssize_t*:size:0: + +PyUnicode_AsUTF8:const char*::: +PyUnicode_AsUTF8:PyObject*:unicode:0: + PyUnicode_DecodeUTF16:PyObject*::+1: PyUnicode_DecodeUTF16:const char*:s:: -PyUnicode_DecodeUTF16:int:size:: +PyUnicode_DecodeUTF16:Py_ssize_t:size:: PyUnicode_DecodeUTF16:const char*:errors:: PyUnicode_DecodeUTF16:int*:byteorder:: PyUnicode_EncodeUTF16:PyObject*::+1: PyUnicode_EncodeUTF16:const Py_UNICODE*:s:: -PyUnicode_EncodeUTF16:int:size:: +PyUnicode_EncodeUTF16:Py_ssize_t:size:: PyUnicode_EncodeUTF16:const char*:errors:: PyUnicode_EncodeUTF16:int:byteorder:: PyUnicode_AsUTF16String:PyObject*::+1: PyUnicode_AsUTF16String:PyObject*:unicode:: +PyUnicode_DecodeUTF32:PyObject*::+1: +PyUnicode_DecodeUTF32:const char*:s:: +PyUnicode_DecodeUTF32:Py_ssize_t:size:: +PyUnicode_DecodeUTF32:const char*:errors:: +PyUnicode_DecodeUTF32:int*:byteorder:: + +PyUnicode_DecodeUTF32Stateful:PyObject*::+1: +PyUnicode_DecodeUTF32Stateful:const char*:s:: +PyUnicode_DecodeUTF32Stateful:Py_ssize_t:size:: +PyUnicode_DecodeUTF32Stateful:const char*:errors:: +PyUnicode_DecodeUTF32Stateful:int*:byteorder:: +PyUnicode_DecodeUTF32Stateful:Py_ssize_t*:consumed:: + +PyUnicode_AsUTF32String:PyObject*::+1: +PyUnicode_AsUTF32String:PyObject*:unicode:0: + +PyUnicode_EncodeUTF32:PyObject*::+1: +PyUnicode_EncodeUTF32:const Py_UNICODE*:s:: +PyUnicode_EncodeUTF32:Py_ssize_t:size:: +PyUnicode_EncodeUTF32:const char*:errors:: +PyUnicode_EncodeUTF32:int:byteorder:: + PyUnicode_DecodeUnicodeEscape:PyObject*::+1: PyUnicode_DecodeUnicodeEscape:const char*:s:: -PyUnicode_DecodeUnicodeEscape:int:size:: +PyUnicode_DecodeUnicodeEscape:Py_ssize_t:size:: PyUnicode_DecodeUnicodeEscape:const char*:errors:: PyUnicode_EncodeUnicodeEscape:PyObject*::+1: PyUnicode_EncodeUnicodeEscape:const Py_UNICODE*:s:: -PyUnicode_EncodeUnicodeEscape:int:size:: -PyUnicode_EncodeUnicodeEscape:const char*:errors:: +PyUnicode_EncodeUnicodeEscape:Py_ssize_t:size:: PyUnicode_AsUnicodeEscapeString:PyObject*::+1: PyUnicode_AsUnicodeEscapeString:PyObject*:unicode:: PyUnicode_DecodeRawUnicodeEscape:PyObject*::+1: PyUnicode_DecodeRawUnicodeEscape:const char*:s:: -PyUnicode_DecodeRawUnicodeEscape:int:size:: +PyUnicode_DecodeRawUnicodeEscape:Py_ssize_t:size:: PyUnicode_DecodeRawUnicodeEscape:const char*:errors:: PyUnicode_EncodeRawUnicodeEscape:PyObject*::+1: PyUnicode_EncodeRawUnicodeEscape:const Py_UNICODE*:s:: -PyUnicode_EncodeRawUnicodeEscape:int:size:: -PyUnicode_EncodeRawUnicodeEscape:const char*:errors:: +PyUnicode_EncodeRawUnicodeEscape:Py_ssize_t:size:: PyUnicode_AsRawUnicodeEscapeString:PyObject*::+1: PyUnicode_AsRawUnicodeEscapeString:PyObject*:unicode:: PyUnicode_DecodeLatin1:PyObject*::+1: PyUnicode_DecodeLatin1:const char*:s:: -PyUnicode_DecodeLatin1:int:size:: +PyUnicode_DecodeLatin1:Py_ssize_t:size:: PyUnicode_DecodeLatin1:const char*:errors:: PyUnicode_EncodeLatin1:PyObject*::+1: PyUnicode_EncodeLatin1:const Py_UNICODE*:s:: -PyUnicode_EncodeLatin1:int:size:: +PyUnicode_EncodeLatin1:Py_ssize_t:size:: PyUnicode_EncodeLatin1:const char*:errors:: PyUnicode_AsLatin1String:PyObject*::+1: @@ -1742,12 +1802,12 @@ PyUnicode_AsLatin1String:PyObject*:unicode:: PyUnicode_DecodeASCII:PyObject*::+1: PyUnicode_DecodeASCII:const char*:s:: -PyUnicode_DecodeASCII:int:size:: +PyUnicode_DecodeASCII:Py_ssize_t:size:: PyUnicode_DecodeASCII:const char*:errors:: PyUnicode_EncodeASCII:PyObject*::+1: PyUnicode_EncodeASCII:const Py_UNICODE*:s:: -PyUnicode_EncodeASCII:int:size:: +PyUnicode_EncodeASCII:Py_ssize_t:size:: PyUnicode_EncodeASCII:const char*:errors:: PyUnicode_AsASCIIString:PyObject*::+1: @@ -1755,13 +1815,13 @@ PyUnicode_AsASCIIString:PyObject*:unicode:: PyUnicode_DecodeCharmap:PyObject*::+1: PyUnicode_DecodeCharmap:const char*:s:: -PyUnicode_DecodeCharmap:int:size:: +PyUnicode_DecodeCharmap:Py_ssize_t:size:: PyUnicode_DecodeCharmap:PyObject*:mapping:0: PyUnicode_DecodeCharmap:const char*:errors:: PyUnicode_EncodeCharmap:PyObject*::+1: PyUnicode_EncodeCharmap:const Py_UNICODE*:s:: -PyUnicode_EncodeCharmap:int:size:: +PyUnicode_EncodeCharmap:Py_ssize_t:size:: PyUnicode_EncodeCharmap:PyObject*:mapping:0: PyUnicode_EncodeCharmap:const char*:errors:: @@ -1771,18 +1831,29 @@ PyUnicode_AsCharmapString:PyObject*:mapping:0: PyUnicode_TranslateCharmap:PyObject*::+1: PyUnicode_TranslateCharmap:const Py_UNICODE*:s:: -PyUnicode_TranslateCharmap:int:size:: +PyUnicode_TranslateCharmap:Py_ssize_t:size:: PyUnicode_TranslateCharmap:PyObject*:table:0: PyUnicode_TranslateCharmap:const char*:errors:: PyUnicode_DecodeMBCS:PyObject*::+1: PyUnicode_DecodeMBCS:const char*:s:: -PyUnicode_DecodeMBCS:int:size:: +PyUnicode_DecodeMBCS:Py_ssize_t:size:: PyUnicode_DecodeMBCS:const char*:errors:: +PyUnicode_DecodeMBCSStateful:PyObject::+1: +PyUnicode_DecodeMBCSStateful:const char*:s:: +PyUnicode_DecodeMBCSStateful:Py_ssize_t:size:: +PyUnicode_DecodeMBCSStateful:const char*:errors:: +PyUnicode_DecodeMBCSStateful:Py_ssize_t*:consumed:: + +PyUnicode_EncodeCodePage:PyObject*::+1: +PyUnicode_EncodeCodePage:int:code_page:: +PyUnicode_EncodeCodePage:PyObject*:unicode:0: +PyUnicode_EncodeCodePage:const char*:errors:: + PyUnicode_EncodeMBCS:PyObject*::+1: PyUnicode_EncodeMBCS:const Py_UNICODE*:s:: -PyUnicode_EncodeMBCS:int:size:: +PyUnicode_EncodeMBCS:Py_ssize_t:size:: PyUnicode_EncodeMBCS:const char*:errors:: PyUnicode_AsMBCSString:PyObject*::+1: @@ -1795,11 +1866,11 @@ PyUnicode_Concat:PyObject*:right:0: PyUnicode_Split:PyObject*::+1: PyUnicode_Split:PyObject*:left:0: PyUnicode_Split:PyObject*:right:0: -PyUnicode_Split:int:maxsplit:: +PyUnicode_Split:Py_ssize_t:maxsplit:: PyUnicode_Splitlines:PyObject*::+1: PyUnicode_Splitlines:PyObject*:s:0: -PyUnicode_Splitlines:int:maxsplit:: +PyUnicode_Splitlines:int:keepend:: PyUnicode_Translate:PyObject*::+1: PyUnicode_Translate:PyObject*:str:0: @@ -1810,36 +1881,52 @@ PyUnicode_Join:PyObject*::+1: PyUnicode_Join:PyObject*:separator:0: PyUnicode_Join:PyObject*:seq:0: -PyUnicode_Tailmatch:int::: +PyUnicode_Tailmatch:Py_ssize_t::: PyUnicode_Tailmatch:PyObject*:str:0: PyUnicode_Tailmatch:PyObject*:substr:0: -PyUnicode_Tailmatch:int:start:: -PyUnicode_Tailmatch:int:end:: +PyUnicode_Tailmatch:Py_ssize_t:start:: +PyUnicode_Tailmatch:Py_ssize_t:end:: PyUnicode_Tailmatch:int:direction:: -PyUnicode_Find:int::: +PyUnicode_Find:Py_ssize_t::: PyUnicode_Find:PyObject*:str:0: PyUnicode_Find:PyObject*:substr:0: -PyUnicode_Find:int:start:: -PyUnicode_Find:int:end:: +PyUnicode_Find:Py_ssize_t:start:: +PyUnicode_Find:Py_ssize_t:end:: PyUnicode_Find:int:direction:: -PyUnicode_Count:int::: +PyUnicode_FindChar:Py_ssize_t::: +PyUnicode_FindChar:PyObject*:str:0: +PyUnicode_FindChar:Py_UCS4:ch:: +PyUnicode_FindChar:Py_ssize_t:start:: +PyUnicode_FindChar:Py_ssize_t:end:: +PyUnicode_FindChar:int:direction:: + +PyUnicode_Count:Py_ssize_t::: PyUnicode_Count:PyObject*:str:0: PyUnicode_Count:PyObject*:substr:0: -PyUnicode_Count:int:start:: -PyUnicode_Count:int:end:: +PyUnicode_Count:Py_ssize_t:start:: +PyUnicode_Count:Py_ssize_t:end:: PyUnicode_Replace:PyObject*::+1: PyUnicode_Replace:PyObject*:str:0: PyUnicode_Replace:PyObject*:substr:0: PyUnicode_Replace:PyObject*:replstr:0: -PyUnicode_Replace:int:maxcount:: +PyUnicode_Replace:Py_ssize_t:maxcount:: PyUnicode_Compare:int::: PyUnicode_Compare:PyObject*:left:0: PyUnicode_Compare:PyObject*:right:0: +PyUnicode_CompareWithASCIIString:int::: +PyUnicode_CompareWithASCIIString:PyObject*:uni:0: +PyUnicode_CompareWithASCIIString:const char*:string:: + +PyUnicode_RichCompare:PyObject::+1: +PyUnicode_RichCompare:PyObject*:left:0: +PyUnicode_RichCompare:PyObject*:right:0: +PyUnicode_RichCompare:int:op:: + PyUnicode_Format:PyObject*::+1: PyUnicode_Format:PyObject*:format:0: PyUnicode_Format:PyObject*:args:0: @@ -1848,6 +1935,106 @@ PyUnicode_Contains:int::: PyUnicode_Contains:PyObject*:container:0: PyUnicode_Contains:PyObject*:element:0: +PyUnicode_InternInPlace:void::: +PyUnicode_InternInPlace:PyObject**:string:+1: + +PyUnicode_InternFromString:PyObject*::+1: +PyUnicode_InternFromString:const char*:cp:: + +PyUnicode_New:PyObject*::+1: +PyUnicode_New:Py_ssize_t:size:: +PyUnicode_New:Py_UCS4:maxchar:: + +PyUnicode_FromKindAndData:PyObject*::+1: +PyUnicode_FromKindAndData:int:kind:: +PyUnicode_FromKindAndData:const void*:buffer:: +PyUnicode_FromKindAndData:Py_ssize_t:size:: + +PyUnicode_FromStringAndSize:PyObject*::+1: +PyUnicode_FromStringAndSize:const char*:u:: +PyUnicode_FromStringAndSize:Py_ssize_t:size:: + +PyUnicode_FromString:PyObject*::+1: +PyUnicode_FromString:const char*:u:: + +PyUnicode_FromFormat:PyObject*::+1: +PyUnicode_FromFormat:const char*:format:: +PyUnicode_FromFormat::...:: + +PyUnicode_FromFormatV:PyObject*::+1: +PyUnicode_FromFormatV:const char*:format:: +PyUnicode_FromFormatV:va_list:args:: + +PyUnicode_GetLength:Py_ssize_t::: +PyUnicode_GetLength:PyObject*:unicode:0: + +PyUnicode_CopyCharacters:Py_ssize_t::: +PyUnicode_CopyCharacters:PyObject*:to:0: +PyUnicode_CopyCharacters:Py_ssize_t:to_start:: +PyUnicode_CopyCharacters:PyObject*:from:0: +PyUnicode_CopyCharacters:Py_ssize_t:from_start:: +PyUnicode_CopyCharacters:Py_ssize_t:how_many:: + +PyUnicode_Fill:Py_ssize_t::: +PyUnicode_Fill:PyObject*:unicode:0: +PyUnicode_Fill:Py_ssize_t:index:: +PyUnicode_Fill:Py_ssize_t:length:: +PyUnicode_Fill:Py_UCS4:fill_char:: + +PyUnicode_ReadChar:Py_UCS4::: +PyUnicode_ReadChar:PyObject*:unicode:0: +PyUnicode_ReadChar:Py_ssize_t:index:: + +PyUnicode_WriteChar:int::: +PyUnicode_WriteChar:PyObject*:unicode:0: +PyUnicode_WriteChar:Py_ssize_t:index:: +PyUnicode_WriteChar:Py_UCS4:character:: + +PyUnicode_Substring:PyObject*::+1: +PyUnicode_Substring:PyObject*:str:0: +PyUnicode_Substring:Py_ssize_t:start:: +PyUnicode_Substring:Py_ssize_t:end:: + +PyUnicode_AsUCS4:Py_UCS4*::: +PyUnicode_AsUCS4:PyObject*:u:0: +PyUnicode_AsUCS4:Py_UCS4*:buffer:: +PyUnicode_AsUCS4:Py_ssize_t:buflen:: +PyUnicode_AsUCS4:int:copy_null:: + +PyUnicode_AsUCS4Copy:Py_UCS4*::: +PyUnicode_AsUCS4Copy:PyObject*:u:0: + +PyUnicode_DecodeLocaleAndSize:PyObject*::+1: +PyUnicode_DecodeLocaleAndSize:const char*:str:: +PyUnicode_DecodeLocaleAndSize:Py_ssize_t:len:: +PyUnicode_DecodeLocaleAndSize:const char*:errors:: + +PyUnicode_DecodeLocale:PyObject*::+1: +PyUnicode_DecodeLocale:const char*:str:: +PyUnicode_DecodeLocale:const char*:errors:: + +PyUnicode_EncodeLocale:PyObject*::+1: +PyUnicode_EncodeLocale:PyObject*:unicode:0: +PyUnicode_EncodeLocale:const char*:errors:: + +PyUnicode_FSConverter:int::: +PyUnicode_FSConverter:PyObject*:obj:0: +PyUnicode_FSConverter:void*:result:: + +PyUnicode_FSDecoder:int::: +PyUnicode_FSDecoder:PyObject*:obj:0: +PyUnicode_FSDecoder:void*:result:: + +PyUnicode_DecodeFSDefaultAndSize:PyObject*::+1: +PyUnicode_DecodeFSDefaultAndSize:const char*:s:: +PyUnicode_DecodeFSDefaultAndSize:Py_ssize_t:size:: + +PyUnicode_DecodeFSDefault:PyObject::+1: +PyUnicode_DecodeFSDefault:const char*:s:: + +PyUnicode_EncodeFSDefault:PyObject::+1: +PyUnicode_EncodeFSDefault:PyObject:unicode:0: + PyWeakref_GET_OBJECT:PyObject*::0: PyWeakref_GET_OBJECT:PyObject*:ref:0: From webhook-mailer at python.org Wed Dec 19 21:20:18 2018 From: webhook-mailer at python.org (Steve Dower) Date: Thu, 20 Dec 2018 02:20:18 -0000 Subject: [Python-checkins] bpo-35482: Fixes HTML escaping in CHM index and build location of NEWS file (GH-11224) Message-ID: https://github.com/python/cpython/commit/afe17a7bee1bcb39dc56f2949431204918568ac1 commit: afe17a7bee1bcb39dc56f2949431204918568ac1 branch: master author: Steve Dower committer: GitHub date: 2018-12-19T18:20:06-08:00 summary: bpo-35482: Fixes HTML escaping in CHM index and build location of NEWS file (GH-11224) files: M Doc/make.bat M Doc/tools/extensions/escape4chm.py M Doc/tools/extensions/pyspecific.py diff --git a/Doc/make.bat b/Doc/make.bat index 077a1bc74069..461c35c5a114 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -16,7 +16,7 @@ if not defined SPHINXBUILD ( %PYTHON% -m pip install sphinx if errorlevel 1 exit /B ) - set SPHINXBUILD=%PYTHON% -c "import sphinx, sys; sys.argv[0] = 'sphinx-build'; sys.exit(sphinx.main())" + set SPHINXBUILD=%PYTHON% -c "import sphinx.cmd.build, sys; sys.exit(sphinx.cmd.build.main())" ) %PYTHON% -c "import python_docs_theme" > nul 2> nul @@ -115,17 +115,16 @@ goto end :build if not exist "%BUILDDIR%" mkdir "%BUILDDIR%" -rem We ought to move NEWS to %BUILDDIR%\NEWS and point -rem Sphinx at the right location. +rem PY_MISC_NEWS_DIR is also used by our Sphinx extension in tools/extensions/pyspecific.py +if not defined PY_MISC_NEWS_DIR set PY_MISC_NEWS_DIR=%BUILDDIR%\%1 if exist ..\Misc\NEWS ( - echo.Copying Misc\NEWS to build\NEWS - if not exist build mkdir build - copy ..\Misc\NEWS build\NEWS > nul + echo.Copying Misc\NEWS to %PY_MISC_NEWS_DIR%\NEWS + copy ..\Misc\NEWS "%PY_MISC_NEWS_DIR%\NEWS" > nul ) else if exist ..\Misc\NEWS.D ( if defined BLURB ( echo.Merging Misc/NEWS with %BLURB% if not exist build mkdir build - %BLURB% merge -f build\NEWS + %BLURB% merge -f "%PY_MISC_NEWS_DIR%\NEWS" ) else ( echo.No Misc/NEWS file and Blurb is not available. exit /B 1 diff --git a/Doc/tools/extensions/escape4chm.py b/Doc/tools/extensions/escape4chm.py index 6f2e35725b37..e99997162517 100644 --- a/Doc/tools/extensions/escape4chm.py +++ b/Doc/tools/extensions/escape4chm.py @@ -8,6 +8,8 @@ import re from html.entities import codepoint2name +from sphinx.util.logging import getLogger + # escape the characters which codepoint > 0x7F def _process(string): def escape(matchobj): @@ -23,7 +25,7 @@ def escape(matchobj): def escape_for_chm(app, pagename, templatename, context, doctree): # only works for .chm output - if not hasattr(app.builder, 'name') or app.builder.name != 'htmlhelp': + if getattr(app.builder, 'name', '') != 'htmlhelp': return # escape the `body` part to 7-bit ASCII @@ -31,9 +33,25 @@ def escape_for_chm(app, pagename, templatename, context, doctree): if body is not None: context['body'] = _process(body) +def fixup_keywords(app, exception): + # only works for .chm output + if getattr(app.builder, 'name', '') != 'htmlhelp' or exception: + return + + getLogger(__name__).info('fixing HTML escapes in keywords file...') + outdir = app.builder.outdir + outname = app.builder.config.htmlhelp_basename + with app.builder.open_file(outdir, outname + '.hhk', 'r') as f: + index = f.read() + with app.builder.open_file(outdir, outname + '.hhk', 'w') as f: + f.write(index.replace(''', ''')) + def setup(app): # `html-page-context` event emitted when the HTML builder has # created a context dictionary to render a template with. app.connect('html-page-context', escape_for_chm) + # `build-finished` event emitted when all the files have been + # output. + app.connect('build-finished', fixup_keywords) return {'version': '1.0', 'parallel_read_safe': True} diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index 10e4a4d11671..66317430881b 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -11,7 +11,7 @@ import re import io -from os import path +from os import getenv, path from time import asctime from pprint import pformat from docutils.io import StringOutput @@ -292,7 +292,9 @@ def run(self): fname = self.arguments[0] source = self.state_machine.input_lines.source( self.lineno - self.state_machine.input_offset - 1) - source_dir = path.dirname(path.abspath(source)) + source_dir = getenv('PY_MISC_NEWS_DIR') + if not source_dir: + source_dir = path.dirname(path.abspath(source)) fpath = path.join(source_dir, fname) self.state.document.settings.record_dependencies.add(fpath) try: From webhook-mailer at python.org Thu Dec 20 00:38:59 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 20 Dec 2018 05:38:59 -0000 Subject: [Python-checkins] bpo-35521: IDLE: Add code context section to docs (#11205) Message-ID: https://github.com/python/cpython/commit/01421bec1e0d25df17599cfa1160adbbcd08e949 commit: 01421bec1e0d25df17599cfa1160adbbcd08e949 branch: master author: Cheryl Sabella committer: Terry Jan Reedy date: 2018-12-20T00:38:54-05:00 summary: bpo-35521: IDLE: Add code context section to docs (#11205) Also add some internal cross-references. files: A Misc/NEWS.d/next/IDLE/2018-12-20-00-14-15.bpo-35521.x32BRn.rst M Doc/library/idle.rst M Lib/idlelib/help.html diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 384d2bf57dc5..449e54f067ec 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -146,7 +146,7 @@ Go to Line Show Completions Open a scrollable list allowing selection of keywords and attributes. See - Completions in the Tips sections below. + :ref:`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; @@ -154,7 +154,8 @@ Expand Word Show call tip After an unclosed parenthesis for a function, open a small window with - function parameter hints. + function parameter hints. See :ref:`Calltips ` in the + Editing and navigation section below. Show surrounding parens Highlight the surrounding parenthesis. @@ -278,8 +279,8 @@ Configure IDLE Code Context (toggle)(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. Clicking a - line in this pane exposes that line at the top of the editor. + of the code which has scrolled above the top of the window. See + :ref:`Code Context ` in the Editing and Navigation section below. Window menu (Shell and Editor) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -310,8 +311,8 @@ 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 for more -on Help menu choices. +the General tab. See the :ref:`Help sources ` subsection below +for more on Help menu choices. .. index:: single: Cut @@ -359,6 +360,8 @@ Squeeze the code above and the prompt below down to a 'Squeezed text' label. +.. _editing-and-navigation: + Editing and navigation ---------------------- @@ -431,6 +434,9 @@ are restricted to four spaces due to Tcl/Tk limitations. See also the indent/dedent region commands in the edit menu. + +.. _completions: + Completions ^^^^^^^^^^^ @@ -475,6 +481,8 @@ much can be found by default, e.g. the re module. If you don't like the ACW popping up unbidden, simply make the delay longer or disable the extension. +.. _calltips: + Calltips ^^^^^^^^ @@ -503,6 +511,25 @@ In an editor, import statements have no effect until one runs the file. One might want to run a file after writing the import statements at the top, or immediately run an existing file before editing. +.. _code-context: + +Code Context +^^^^^^^^^^^^ + +Within an editor window containing Python code, code context can be toggled +in order to show or hide a pane at the top of the window. When shown, this +pane freezes the opening lines for block code, such as those beginning with +``class``, ``def``, or ``if`` keywords, that would have otherwise scrolled +out of view. The size of the pane will be expanded and contracted as needed +to show the all current levels of context, up to the maximum number of +lines defined in the Configure IDLE dialog (which defaults to 15). If there +are no current context lines and the feature is toggled on, a single blank +line will display. Clicking on a line in the context pane will move that +line to the top of the editor. + +The text and background colors for the context pane can be configured under +the Highlights tab in the Configure IDLE dialog. + Python Shell window ^^^^^^^^^^^^^^^^^^^ @@ -768,6 +795,8 @@ with the default subprocess if at all possible. Help and preferences -------------------- +.. _help-sources: + Help sources ^^^^^^^^^^^^ diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index e2bf773478e8..b5f12d1a6f65 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -202,13 +202,14 @@

Edit menu (Shell and Editor)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; repeat to get a different expansion.
Show call tip
After an unclosed parenthesis for a function, open a small window with -function parameter hints.
+function parameter hints. See Calltips in the +Editing and navigation section below.
Show surrounding parens
Highlight the surrounding parenthesis.
@@ -314,8 +315,8 @@

Options menu (Shell and Editor)Code Context in the Editing and Navigation section below.
@@ -344,8 +345,8 @@

Help menu (Shell and Editor)Help sources subsection below +for more on Help menu choices.

Context Menus?

@@ -383,7 +384,7 @@

Help menu (Shell and Editor) -

Editing and navigation?

+

Editing and navigation?

Editor windows?

IDLE may open editor windows when it starts, depending on settings @@ -445,7 +446,7 @@

Automatic indentationSee also the indent/dedent region commands in the edit menu.

-

Completions?

+

Completions?

Completions are supplied for functions, classes, and attributes of classes, both built-in and user-defined. Completions are also provided for filenames.

@@ -480,7 +481,7 @@

Completions -

Calltips?

+

Calltips?

A calltip is shown when one types ( after the name of an accessible function. A name expression may include dots and subscripts. A calltip remains until it is clicked, the cursor is moved out of the argument area, @@ -502,6 +503,21 @@

Calltips +

Code Context?

+

Within an editor window containing Python code, code context can be toggled +in order to show or hide a pane at the top of the window. When shown, this +pane freezes the opening lines for block code, such as those beginning with +class, def, or if keywords, that would have otherwise scrolled +out of view. The size of the pane will be expanded and contracted as needed +to show the all current levels of context, up to the maximum number of +lines defined in the Configure IDLE dialog (which defaults to 15). If there +are no current context lines and the feature is toggled on, a single blank +line will display. Clicking on a line in the context pane will move that +line to the top of the editor.

+

The text and background colors for the context pane can be configured under +the Highlights tab in the Configure IDLE dialog.

+

Python Shell window?

With IDLE?s Shell, one enters, edits, and recalls complete statements. @@ -733,7 +749,7 @@

Running without a subprocess

Help and preferences?

-

Help sources?

+

Help sources?

Help menu entry ?IDLE Help? displays a formatted html version of the IDLE chapter of the Library Reference. The result, in a read-only tkinter text window, is close to what one sees in a web browser. @@ -801,6 +817,7 @@

Table of Contents

  • Automatic indentation
  • Completions
  • Calltips
  • +
  • Code Context
  • Python Shell window
  • Text colors
  • @@ -899,7 +916,7 @@

    Navigation



    - Last updated on Nov 12, 2018. + Last updated on Dec 19, 2018. Found a bug?
    diff --git a/Misc/NEWS.d/next/IDLE/2018-12-20-00-14-15.bpo-35521.x32BRn.rst b/Misc/NEWS.d/next/IDLE/2018-12-20-00-14-15.bpo-35521.x32BRn.rst new file mode 100644 index 000000000000..120de7f6c844 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-12-20-00-14-15.bpo-35521.x32BRn.rst @@ -0,0 +1,2 @@ +Document the IDLE editor code context feature. Add some internal references +within the IDLE doc. From webhook-mailer at python.org Thu Dec 20 00:59:30 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 20 Dec 2018 05:59:30 -0000 Subject: [Python-checkins] bpo-35521: IDLE: Add code context section to docs (GH-11205) Message-ID: https://github.com/python/cpython/commit/3f9338312738b4b6d909fa7d5e7bb02f2efc08a5 commit: 3f9338312738b4b6d909fa7d5e7bb02f2efc08a5 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-19T21:59:22-08:00 summary: bpo-35521: IDLE: Add code context section to docs (GH-11205) Also add some internal cross-references. (cherry picked from commit 01421bec1e0d25df17599cfa1160adbbcd08e949) Co-authored-by: Cheryl Sabella files: A Misc/NEWS.d/next/IDLE/2018-12-20-00-14-15.bpo-35521.x32BRn.rst M Doc/library/idle.rst M Lib/idlelib/help.html diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 384d2bf57dc5..449e54f067ec 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -146,7 +146,7 @@ Go to Line Show Completions Open a scrollable list allowing selection of keywords and attributes. See - Completions in the Tips sections below. + :ref:`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; @@ -154,7 +154,8 @@ Expand Word Show call tip After an unclosed parenthesis for a function, open a small window with - function parameter hints. + function parameter hints. See :ref:`Calltips ` in the + Editing and navigation section below. Show surrounding parens Highlight the surrounding parenthesis. @@ -278,8 +279,8 @@ Configure IDLE Code Context (toggle)(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. Clicking a - line in this pane exposes that line at the top of the editor. + of the code which has scrolled above the top of the window. See + :ref:`Code Context ` in the Editing and Navigation section below. Window menu (Shell and Editor) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -310,8 +311,8 @@ 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 for more -on Help menu choices. +the General tab. See the :ref:`Help sources ` subsection below +for more on Help menu choices. .. index:: single: Cut @@ -359,6 +360,8 @@ Squeeze the code above and the prompt below down to a 'Squeezed text' label. +.. _editing-and-navigation: + Editing and navigation ---------------------- @@ -431,6 +434,9 @@ are restricted to four spaces due to Tcl/Tk limitations. See also the indent/dedent region commands in the edit menu. + +.. _completions: + Completions ^^^^^^^^^^^ @@ -475,6 +481,8 @@ much can be found by default, e.g. the re module. If you don't like the ACW popping up unbidden, simply make the delay longer or disable the extension. +.. _calltips: + Calltips ^^^^^^^^ @@ -503,6 +511,25 @@ In an editor, import statements have no effect until one runs the file. One might want to run a file after writing the import statements at the top, or immediately run an existing file before editing. +.. _code-context: + +Code Context +^^^^^^^^^^^^ + +Within an editor window containing Python code, code context can be toggled +in order to show or hide a pane at the top of the window. When shown, this +pane freezes the opening lines for block code, such as those beginning with +``class``, ``def``, or ``if`` keywords, that would have otherwise scrolled +out of view. The size of the pane will be expanded and contracted as needed +to show the all current levels of context, up to the maximum number of +lines defined in the Configure IDLE dialog (which defaults to 15). If there +are no current context lines and the feature is toggled on, a single blank +line will display. Clicking on a line in the context pane will move that +line to the top of the editor. + +The text and background colors for the context pane can be configured under +the Highlights tab in the Configure IDLE dialog. + Python Shell window ^^^^^^^^^^^^^^^^^^^ @@ -768,6 +795,8 @@ with the default subprocess if at all possible. Help and preferences -------------------- +.. _help-sources: + Help sources ^^^^^^^^^^^^ diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index e2bf773478e8..b5f12d1a6f65 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -202,13 +202,14 @@

    Edit menu (Shell and Editor)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; repeat to get a different expansion.
    Show call tip
    After an unclosed parenthesis for a function, open a small window with -function parameter hints.
    +function parameter hints. See Calltips in the +Editing and navigation section below.
    Show surrounding parens
    Highlight the surrounding parenthesis.
    @@ -314,8 +315,8 @@

    Options menu (Shell and Editor)Code Context in the Editing and Navigation section below.

    @@ -344,8 +345,8 @@

    Help menu (Shell and Editor)Help sources subsection below +for more on Help menu choices.

    Context Menus?

    @@ -383,7 +384,7 @@

    Help menu (Shell and Editor) -

    Editing and navigation?

    +

    Editing and navigation?

    Editor windows?

    IDLE may open editor windows when it starts, depending on settings @@ -445,7 +446,7 @@

    Automatic indentationSee also the indent/dedent region commands in the edit menu.

    -

    Completions?

    +

    Completions?

    Completions are supplied for functions, classes, and attributes of classes, both built-in and user-defined. Completions are also provided for filenames.

    @@ -480,7 +481,7 @@

    Completions -

    Calltips?

    +

    Calltips?

    A calltip is shown when one types ( after the name of an accessible function. A name expression may include dots and subscripts. A calltip remains until it is clicked, the cursor is moved out of the argument area, @@ -502,6 +503,21 @@

    Calltips +

    Code Context?

    +

    Within an editor window containing Python code, code context can be toggled +in order to show or hide a pane at the top of the window. When shown, this +pane freezes the opening lines for block code, such as those beginning with +class, def, or if keywords, that would have otherwise scrolled +out of view. The size of the pane will be expanded and contracted as needed +to show the all current levels of context, up to the maximum number of +lines defined in the Configure IDLE dialog (which defaults to 15). If there +are no current context lines and the feature is toggled on, a single blank +line will display. Clicking on a line in the context pane will move that +line to the top of the editor.

    +

    The text and background colors for the context pane can be configured under +the Highlights tab in the Configure IDLE dialog.

    +

    Python Shell window?

    With IDLE?s Shell, one enters, edits, and recalls complete statements. @@ -733,7 +749,7 @@

    Running without a subprocess

    Help and preferences?

    -

    Help sources?

    +

    Help sources?

    Help menu entry ?IDLE Help? displays a formatted html version of the IDLE chapter of the Library Reference. The result, in a read-only tkinter text window, is close to what one sees in a web browser. @@ -801,6 +817,7 @@

    Table of Contents

  • Automatic indentation
  • Completions
  • Calltips
  • +
  • Code Context
  • Python Shell window
  • Text colors
  • @@ -899,7 +916,7 @@

    Navigation



    - Last updated on Nov 12, 2018. + Last updated on Dec 19, 2018. Found a bug?
    diff --git a/Misc/NEWS.d/next/IDLE/2018-12-20-00-14-15.bpo-35521.x32BRn.rst b/Misc/NEWS.d/next/IDLE/2018-12-20-00-14-15.bpo-35521.x32BRn.rst new file mode 100644 index 000000000000..120de7f6c844 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-12-20-00-14-15.bpo-35521.x32BRn.rst @@ -0,0 +1,2 @@ +Document the IDLE editor code context feature. Add some internal references +within the IDLE doc. From webhook-mailer at python.org Thu Dec 20 01:07:13 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 20 Dec 2018 06:07:13 -0000 Subject: [Python-checkins] bpo-34162: Update idlelib/NEWS.txt to 2018-12-20. (#11255) Message-ID: https://github.com/python/cpython/commit/87ec1104b369865c3c41d2d91ac7aee29cfb632e commit: 87ec1104b369865c3c41d2d91ac7aee29cfb632e branch: master author: Terry Jan Reedy committer: GitHub date: 2018-12-20T01:07:10-05:00 summary: bpo-34162: Update idlelib/NEWS.txt to 2018-12-20. (#11255) files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 6e11cab58600..b02b96bf476d 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,9 @@ Released on 2019-10-20? ====================================== +bpo-35521: Document the editor code context feature. +Add some internal references within the IDLE doc. + bpo-34864: When starting IDLE on MacOS, warn if the system setting "Prefer tabs when opening documents" is "Always". As previous documented for this issue, running IDLE with this setting causes From webhook-mailer at python.org Thu Dec 20 02:12:13 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 20 Dec 2018 07:12:13 -0000 Subject: [Python-checkins] [3.7] bpo-34162: Update idlelib/NEWS.txt to 2018-12-20 (GH-11255) (#11256) Message-ID: https://github.com/python/cpython/commit/93f5694bb0c147ae48da492d6eb6244447562f8d commit: 93f5694bb0c147ae48da492d6eb6244447562f8d branch: 3.7 author: Terry Jan Reedy committer: GitHub date: 2018-12-20T02:12:08-05:00 summary: [3.7] bpo-34162: Update idlelib/NEWS.txt to 2018-12-20 (GH-11255) (#11256) Cherry-picked from 87ec110. files: M Lib/idlelib/NEWS.txt diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index b93769e73bbb..e36d90481046 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,8 +1,16 @@ -What's New in IDLE 3.7.2 -Released on 2018-07-31? +What's New in IDLE 3.7.3 +Released on 2019-??-?? ====================================== +bpo-35521: Document the editor code context feature. +Add some internal references within the IDLE doc. + + +What's New in IDLE 3.7.2 +Released on 2018-12-21? +====================================== + bpo-34864: When starting IDLE on MacOS, warn if the system setting "Prefer tabs when opening documents" is "Always". As previous documented for this issue, running IDLE with this setting causes From webhook-mailer at python.org Thu Dec 20 02:34:01 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 20 Dec 2018 07:34:01 -0000 Subject: [Python-checkins] bpo-18085: Update refcounts.dat. (GH-11247) Message-ID: https://github.com/python/cpython/commit/83dd4e87a62311cfea5fdd37e8a945b6b07bccee commit: 83dd4e87a62311cfea5fdd37e8a945b6b07bccee branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-20T09:33:58+02:00 summary: bpo-18085: Update refcounts.dat. (GH-11247) Fixed some errors in refcounts.dat, remove functions removed in Python 3, and add more entries for documented functions. This will add several automatically generated notes about return values. files: M Doc/data/refcounts.dat M Doc/tools/extensions/c_annotations.py diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index add0600676f1..35527c179f34 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -31,29 +31,130 @@ # The parameter names are as they appear in the API manual, not the source # code. +PyAnySet_Check:int::: +PyAnySet_Check:PyObject*:p:0: + +PyAnySet_CheckExact:int::: +PyAnySet_CheckExact:PyObject*:p:0: + +PyBool_Check:int::: +PyBool_Check:PyObject*:o:0: + PyBool_FromLong:PyObject*::+1: -PyBool_FromLong:long:v:0: +PyBool_FromLong:long:v:: + +PyBuffer_FillContiguousStrides:void::: +PyBuffer_FillContiguousStrides:int:ndims:: +PyBuffer_FillContiguousStrides:Py_ssize_t*:shape:: +PyBuffer_FillContiguousStrides:Py_ssize_t*:strides:: +PyBuffer_FillContiguousStrides:int:itemsize:: +PyBuffer_FillContiguousStrides:char:order:: + +PyBuffer_FillInfo:int::: +PyBuffer_FillInfo:Py_buffer*:view:: +PyBuffer_FillInfo:PyObject*:exporter:0: +PyBuffer_FillInfo:void*:buf:: +PyBuffer_FillInfo:Py_ssize_t:len:: +PyBuffer_FillInfo:int:readonly:: +PyBuffer_FillInfo:int:flags:: + +PyBuffer_IsContiguous:int::: +PyBuffer_IsContiguous:Py_buffer*:view:: +PyBuffer_IsContiguous:char:order:: + +PyBuffer_Release:void::: +PyBuffer_Release:Py_buffer*:view:: + +PyBuffer_ToContiguous:int::: +PyBuffer_ToContiguous:void*:buf:: +PyBuffer_ToContiguous:Py_buffer*:src:: +PyBuffer_ToContiguous:Py_ssize_t:len:: +PyBuffer_ToContiguous:char:order:: + +PyByteArray_AS_STRING:char*::: +PyByteArray_AS_STRING:PyObject*:bytearray:0: + +PyByteArray_AsString:char*::: +PyByteArray_AsString:PyObject*:bytearray:0: + +PyByteArray_Check:int::: +PyByteArray_Check:PyObject*:o:0: + +PyByteArray_CheckExact:int::: +PyByteArray_CheckExact:PyObject*:o:0: + +PyByteArray_Concat:PyObject*::+1: +PyByteArray_Concat:PyObject*:a:0: +PyByteArray_Concat:PyObject*:b:0: + +PyByteArray_FromObject:PyObject*::+1: +PyByteArray_FromObject:PyObject*:o:0: + +PyByteArray_FromStringAndSize:PyObject*::+1: +PyByteArray_FromStringAndSize:const char*:string:: +PyByteArray_FromStringAndSize:Py_ssize_t:len:: + +PyByteArray_GET_SIZE:Py_ssize_t::: +PyByteArray_GET_SIZE:PyObject*:bytearray:0: + +PyByteArray_Resize:int::: +PyByteArray_Resize:PyObject*:bytearray:0: +PyByteArray_Resize:Py_ssize_t:len:: + +PyByteArray_Size:Py_ssize_t::: +PyByteArray_Size:PyObject*:bytearray:0: + +PyBytes_AS_STRING:char*::: +PyBytes_AS_STRING:PyObject*:string:0: -PyBuffer_FromObject:PyObject*::+1: -PyBuffer_FromObject:PyObject*:base:+1: -PyBuffer_FromObject:int:offset:: -PyBuffer_FromObject:int:size:: +PyBytes_AsString:char*::: +PyBytes_AsString:PyObject*:o:0: -PyBuffer_FromReadWriteObject:PyObject*::+1: -PyBuffer_FromReadWriteObject:PyObject*:base:+1: -PyBuffer_FromReadWriteObject:int:offset:: -PyBuffer_FromReadWriteObject:int:size:: +PyBytes_AsStringAndSize:int::: +PyBytes_AsStringAndSize:PyObject*:obj:0: +PyBytes_AsStringAndSize:char**:buffer:: +PyBytes_AsStringAndSize:Py_ssize_t*:length:: -PyBuffer_FromMemory:PyObject*::+1: -PyBuffer_FromMemory:void*:ptr:: -PyBuffer_FromMemory:int:size:: +PyBytes_Check:int::: +PyBytes_Check:PyObject*:o:0: -PyBuffer_FromReadWriteMemory:PyObject*::+1: -PyBuffer_FromReadWriteMemory:void*:ptr:: -PyBuffer_FromReadWriteMemory:int:size:: +PyBytes_CheckExact:int::: +PyBytes_CheckExact:PyObject*:o:0: -PyBuffer_New:PyObject*::+1: -PyBuffer_New:int:size:: +PyBytes_Concat:void::: +PyBytes_Concat:PyObject**:bytes:0: +PyBytes_Concat:PyObject*:newpart:0: + +PyBytes_ConcatAndDel:void::: +PyBytes_ConcatAndDel:PyObject**:bytes:0: +PyBytes_ConcatAndDel:PyObject*:newpart:-1: + +PyBytes_FromString:PyObject*::+1: +PyBytes_FromString:const char*:v:: + +PyBytes_FromStringAndSize:PyObject*::+1: +PyBytes_FromStringAndSize:const char*:v:: +PyBytes_FromStringAndSize:Py_ssize_t:len:: + +PyBytes_FromFormat:PyObject*::+1: +PyBytes_FromFormat:const char*:format:: +PyBytes_FromFormat::...:: + +PyBytes_FromFormatV:PyObject*::+1: +PyBytes_FromFormatV:const char*:format:: +PyBytes_FromFormatV:va_list:vargs:: + +PyBytes_FromObject:PyObject*::+1: +PyBytes_FromObject:PyObject*:o:0: + +PyBytes_GET_SIZE:Py_ssize_t::: +PyBytes_GET_SIZE:PyObject*:o:0: + +PyBytes_Size:Py_ssize_t::: +PyBytes_Size:PyObject*:o:0: + +PyCapsule_CheckExact:int::: +PyCapsule_CheckExact:PyObject*:p:0: PyCapsule_GetContext:void *::: PyCapsule_GetContext:PyObject*:self:0: @@ -72,6 +173,10 @@ PyCapsule_Import:void *::: PyCapsule_Import:const char *:name:: PyCapsule_Import:int:no_block:: +PyCapsule_IsValid:int::: +PyCapsule_IsValid:PyObject*:capsule:0: +PyCapsule_IsValid:const char*:name:: + PyCapsule_New:PyObject*::+1: PyCapsule_New:void*:pointer:: PyCapsule_New:const char *:name:: @@ -93,21 +198,8 @@ PyCapsule_SetPointer:int::: PyCapsule_SetPointer:PyObject*:self:0: PyCapsule_SetPointer:void*:pointer:: - -PyCObject_AsVoidPtr:void*::: -PyCObject_AsVoidPtr:PyObject*:self:0: - -PyCObject_FromVoidPtr:PyObject*::+1: -PyCObject_FromVoidPtr:void*:cobj:: -PyCObject_FromVoidPtr::void (* destr)(void* ):: - -PyCObject_FromVoidPtrAndDesc:PyObject*::+1: -PyCObject_FromVoidPtrAndDesc:void*:cobj:: -PyCObject_FromVoidPtrAndDesc:void*:desc:: -PyCObject_FromVoidPtrAndDesc:void(*)(void*,void*):destr:: - -PyCObject_GetDesc:void*::: -PyCObject_GetDesc:PyObject*:self:0: +PyCell_Check:int::: +PyCell_Check::ob:: PyCell_New:PyObject*::+1: PyCell_New:PyObject*:ob:0: @@ -126,19 +218,118 @@ PyCell_Set:int::: PyCell_Set:PyObject*:cell:0: PyCell_Set:PyObject*:value:0: +PyCallIter_Check:int::: +PyCallIter_Check::op:: + PyCallIter_New:PyObject*::+1: -PyCallIter_New:PyObject*:callable:: -PyCallIter_New:PyObject*:sentinel:: +PyCallIter_New:PyObject*:callable:+1: +PyCallIter_New:PyObject*:sentinel:+1: PyCallable_Check:int::: PyCallable_Check:PyObject*:o:0: +PyCode_Check:int::: +PyCode_Check:PyObject*:co:0: + +PyCode_GetNumFree:int::: +PyCode_GetNumFree:PyCodeObject*:co:0: + +PyCode_New:PyCodeObject*::+1: +PyCode_New:int:argcount:: +PyCode_New:int:kwonlyargcount:: +PyCode_New:int:nlocals:: +PyCode_New:int:stacksize:: +PyCode_New:int:flags:: +PyCode_New:PyObject*:code:0: +PyCode_New:PyObject*:consts:0: +PyCode_New:PyObject*:names:0: +PyCode_New:PyObject*:varnames:0: +PyCode_New:PyObject*:freevars:0: +PyCode_New:PyObject*:cellvars:0: +PyCode_New:PyObject*:filename:0: +PyCode_New:PyObject*:name:0: +PyCode_New:int:firstlineno:: +PyCode_New:PyObject*:lnotab:0: + +PyCode_NewEmpty:PyCodeObject*::+1: +PyCode_NewEmpty:const char*:filename:: +PyCode_NewEmpty:const char*:funcname:: +PyCode_NewEmpty:int:firstlineno:: + +PyCodec_Register:int::: +PyCodec_Register:PyObject*:search_function:+1: + +PyCodec_KnownEncoding:int::: +PyCodec_KnownEncoding:const char*:encoding:: + +PyCodec_Encode:PyObject*::+1: +PyCodec_Encode:PyObject*:object:0: +PyCodec_Encode:const char*:encoding:: +PyCodec_Encode:const char*:errors:: + +PyCodec_Decode:PyObject*::+1: +PyCodec_Decode:PyObject*:object:0: +PyCodec_Decode:const char*:encoding:: +PyCodec_Decode:const char*:errors:: + +PyCodec_Encoder:PyObject*::+1: +PyCodec_Encoder:const char*:encoding:: + +PyCodec_Decoder:PyObject*::+1: +PyCodec_Decoder:const char*:encoding:: + +PyCodec_IncrementalEncoder:PyObject*::+1: +PyCodec_IncrementalEncoder:const char*:encoding:: +PyCodec_IncrementalEncoder:const char*:errors:: + +PyCodec_IncrementalDecoder:PyObject*::+1: +PyCodec_IncrementalDecoder:const char*:encoding:: +PyCodec_IncrementalDecoder:const char*:errors:: + +PyCodec_StreamReader:PyObject*::+1: +PyCodec_StreamReader:const char*:encoding:: +PyCodec_StreamReader:PyObject*:stream:0: +PyCodec_StreamReader:const char*:errors:: + +PyCodec_StreamWriter:PyObject*::+1: +PyCodec_StreamWriter:const char*:encoding:: +PyCodec_StreamWriter:PyObject*:stream:0: +PyCodec_StreamWriter:const char*:errors:: + +PyCodec_RegisterError:int::: +PyCodec_RegisterError:const char*:name:: +PyCodec_RegisterError:PyObject*:error:+1: + +PyCodec_LookupError:PyObject*::+1: +PyCodec_LookupError:const char*:name:: + +PyCodec_StrictErrors:PyObject*::null: +PyCodec_StrictErrors:PyObject*:exc:0: + +PyCodec_IgnoreErrors:PyObject*::+1: +PyCodec_IgnoreErrors:PyObject*:exc:0: + +PyCodec_ReplaceErrors:PyObject*::+1: +PyCodec_ReplaceErrors:PyObject*:exc:0: + +PyCodec_XMLCharRefReplaceErrors:PyObject*::+1: +PyCodec_XMLCharRefReplaceErrors:PyObject*:exc:0: + +PyCodec_BackslashReplaceErrors:PyObject*::+1: +PyCodec_BackslashReplaceErrors:PyObject*:exc:0: + +PyCodec_NameReplaceErrors:PyObject*::+1: +PyCodec_NameReplaceErrors:PyObject*:exc:0: + PyComplex_AsCComplex:Py_complex::: PyComplex_AsCComplex:PyObject*:op:0: PyComplex_Check:int::: PyComplex_Check:PyObject*:p:0: +PyComplex_CheckExact:int::: +PyComplex_CheckExact:PyObject*:p:0: + PyComplex_FromCComplex:PyObject*::+1: PyComplex_FromCComplex::Py_complex v:: @@ -193,6 +384,12 @@ PyContextVar_Reset:int::: PyContextVar_Reset:PyObject*:var:0: PyContextVar_Reset:PyObject*:token:-1: +PyDate_Check:int::: +PyDate_Check:PyObject*:ob:0: + +PyDate_CheckExact:int::: +PyDate_CheckExact:PyObject*:ob:0: + PyDate_FromDate:PyObject*::+1: PyDate_FromDate:int:year:: PyDate_FromDate:int:month:: @@ -201,6 +398,12 @@ PyDate_FromDate:int:day:: PyDate_FromTimestamp:PyObject*::+1: PyDate_FromTimestamp:PyObject*:args:0: +PyDateTime_Check:int::: +PyDateTime_Check:PyObject*:ob:0: + +PyDateTime_CheckExact:int::: +PyDateTime_CheckExact:PyObject*:ob:0: + PyDateTime_FromDateAndTime:PyObject*::+1: PyDateTime_FromDateAndTime:int:year:: PyDateTime_FromDateAndTime:int:month:: @@ -213,46 +416,62 @@ PyDateTime_FromDateAndTime:int:usecond:: PyDateTime_FromTimestamp:PyObject*::+1: PyDateTime_FromTimestamp:PyObject*:args:0: +PyDelta_Check:int::: +PyDelta_Check:PyObject*:ob:0: + +PyDelta_CheckExact:int::: +PyDelta_CheckExact:PyObject*:ob:0: + PyDelta_FromDSU:PyObject*::+1: PyDelta_FromDSU:int:days:: PyDelta_FromDSU:int:seconds:: PyDelta_FromDSU:int:useconds:: PyTimeZone_FromOffset:PyObject*::+1: -PyTimeZone_FromOffset:PyDateTime_DeltaType*:offset:+1:Reference count not increased if offset is +00:00 +PyTimeZone_FromOffset:PyObject*:offset:+1:Reference count not increased if offset is +00:00 PyTimeZone_FromOffsetAndName:PyObject*::+1: -PyTimeZone_FromOffsetAndName:PyDateTime_DeltaType*:offset:+1:Reference count not increased if offset is +00:00 and name == NULL -PyTimeZone_FromOffsetAndName:PyUnicode*:name:+1: +PyTimeZone_FromOffsetAndName:PyObject*:offset:+1:Reference count not increased if offset is +00:00 and name == NULL +PyTimeZone_FromOffsetAndName:PyObject*:name:+1: + +PyDescr_IsData:int::: +PyDescr_IsData:PyObject*:descr:0: PyDescr_NewClassMethod:PyObject*::+1: -PyDescr_NewClassMethod:PyTypeObject*:type:: +PyDescr_NewClassMethod:PyTypeObject*:type:+1: PyDescr_NewClassMethod:PyMethodDef*:method:: PyDescr_NewGetSet:PyObject*::+1: -PyDescr_NewGetSet:PyTypeObject*:type:: +PyDescr_NewGetSet:PyTypeObject*:type:+1: PyDescr_NewGetSet:PyGetSetDef*:getset:: PyDescr_NewMember:PyObject*::+1: -PyDescr_NewMember:PyTypeObject*:type:: +PyDescr_NewMember:PyTypeObject*:type:+1: PyDescr_NewMember:PyMemberDef*:member:: PyDescr_NewMethod:PyObject*::+1: -PyDescr_NewMethod:PyTypeObject*:type:: +PyDescr_NewMethod:PyTypeObject*:type:+1: PyDescr_NewMethod:PyMethodDef*:meth:: PyDescr_NewWrapper:PyObject*::+1: -PyDescr_NewWrapper:PyTypeObject*:type:: +PyDescr_NewWrapper:PyTypeObject*:type:+1: PyDescr_NewWrapper:struct wrapperbase*:base:: PyDescr_NewWrapper:void*:wrapped:: PyDict_Check:int::: PyDict_Check:PyObject*:p:0: +PyDict_CheckExact:int::: +PyDict_CheckExact:PyObject*:p:0: + PyDict_Clear:void::: PyDict_Clear:PyObject*:p:0: +PyDict_Contains:int::: +PyDict_Contains:PyObject*:p:0: +PyDict_Contains:PyObject*:key:0: + PyDict_DelItem:int::: PyDict_DelItem:PyObject*:p:0: PyDict_DelItem:PyObject*:key:0: @@ -261,7 +480,7 @@ PyDict_DelItemString:int::: PyDict_DelItemString:PyObject*:p:0: PyDict_DelItemString:const char*:key:: -PyDict_GetItem:PyObject*::0:0 +PyDict_GetItem:PyObject*::0: PyDict_GetItem:PyObject*:p:0: PyDict_GetItem:PyObject*:key:0: @@ -289,9 +508,19 @@ PyDict_New:PyObject*::+1: PyDict_Copy:PyObject*::+1: PyDict_Copy:PyObject*:p:0: +PyDict_Merge:int::: +PyDict_Merge:PyObject*:a:0: +PyDict_Merge:PyObject*:b:0: +PyDict_Merge:int:override:: + +PyDict_MergeFromSeq2:int::: +PyDict_MergeFromSeq2:PyObject*:a:0: +PyDict_MergeFromSeq2:PyObject*:seq2:0: +PyDict_MergeFromSeq2:int:override:: + PyDict_Next:int::: PyDict_Next:PyObject*:p:0: -PyDict_Next:int:ppos:: +PyDict_Next:Py_ssize_t:ppos:: PyDict_Next:PyObject**:pkey:0: PyDict_Next:PyObject**:pvalue:0: @@ -305,8 +534,12 @@ PyDict_SetItemString:PyObject*:p:0: PyDict_SetItemString:const char*:key:: PyDict_SetItemString:PyObject*:val:+1: -PyDict_Size:int::: -PyDict_Size:PyObject*:p:: +PyDict_Size:Py_ssize_t::: +PyDict_Size:PyObject*:p:0: + +PyDict_Update:int::: +PyDict_Update:PyObject*:a:0: +PyDict_Update:PyObject*:b:0: PyDict_Values:PyObject*::+1: PyDict_Values:PyObject*:p:0: @@ -330,6 +563,21 @@ PyErr_Fetch:PyObject**:ptype:0: PyErr_Fetch:PyObject**:pvalue:0: PyErr_Fetch:PyObject**:ptraceback:0: +PyErr_Format:PyObject*::null: +PyErr_Format:PyObject*:exception:+1: +PyErr_Format:const char*:format:: +PyErr_Format::...:: + +PyErr_FormatV:PyObject*::null: +PyErr_FormatV:PyObject*:exception:+1: +PyErr_FormatV:const char*:format:: +PyErr_FormatV:va_list:vargs:: + +PyErr_GetExcInfo:void::: +PyErr_GetExcInfo:PyObject**:ptype:+1: +PyErr_GetExcInfo:PyObject**:pvalue:+1: +PyErr_GetExcInfo:PyObject**:ptraceback:+1: + PyErr_GivenExceptionMatches:int::: PyErr_GivenExceptionMatches:PyObject*:given:0: PyErr_GivenExceptionMatches:PyObject*:exc:0: @@ -356,27 +604,61 @@ PyErr_Occurred:PyObject*::0: PyErr_Print:void::: +PyErr_PrintEx:void::: +PyErr_PrintEx:int:set_sys_last_vars:: + +PyErr_ResourceWarning:int::: +PyErr_ResourceWarning:PyObject*:source:0: +PyErr_ResourceWarning:Py_ssize_t:stack_level:: +PyErr_ResourceWarning:const char*:format:: +PyErr_ResourceWarning::...:: + PyErr_Restore:void::: PyErr_Restore:PyObject*:type:-1: PyErr_Restore:PyObject*:value:-1: PyErr_Restore:PyObject*:traceback:-1: PyErr_SetExcFromWindowsErr:PyObject*::null: -PyErr_SetExcFromWindowsErr:PyObject*:type:0: +PyErr_SetExcFromWindowsErr:PyObject*:type:+1: PyErr_SetExcFromWindowsErr:int:ierr:: PyErr_SetExcFromWindowsErrWithFilename:PyObject*::null: -PyErr_SetExcFromWindowsErrWithFilename:PyObject*:type:0: +PyErr_SetExcFromWindowsErrWithFilename:PyObject*:type:+1: PyErr_SetExcFromWindowsErrWithFilename:int:ierr:: PyErr_SetExcFromWindowsErrWithFilename:const char*:filename:: +PyErr_SetExcFromWindowsErrWithFilenameObject:PyObject*::null: +PyErr_SetExcFromWindowsErrWithFilenameObject:PyObject*:type:+1: +PyErr_SetExcFromWindowsErrWithFilenameObject:int:ierr:: +PyErr_SetExcFromWindowsErrWithFilenameObject:PyObject*:filename:+1: + +PyErr_SetExcFromWindowsErrWithFilenameObjects:PyObject*::null: +PyErr_SetExcFromWindowsErrWithFilenameObjects:PyObject*:type:+1: +PyErr_SetExcFromWindowsErrWithFilenameObjects:int:ierr:: +PyErr_SetExcFromWindowsErrWithFilenameObjects:PyObject*:filename:+1: +PyErr_SetExcFromWindowsErrWithFilenameObjects:PyObject*:filename2:+1: + +PyErr_SetExcInfo:void::: +PyErr_SetExcInfo:PyObject*:type:0: +PyErr_SetExcInfo:PyObject*:value:0: +PyErr_SetExcInfo:PyObject*:traceback:0: + PyErr_SetFromErrno:PyObject*::null: -PyErr_SetFromErrno:PyObject*:type:0: +PyErr_SetFromErrno:PyObject*:type:+1: PyErr_SetFromErrnoWithFilename:PyObject*::null: -PyErr_SetFromErrnoWithFilename:PyObject*:type:0: +PyErr_SetFromErrnoWithFilename:PyObject*:type:+1: PyErr_SetFromErrnoWithFilename:const char*:filename:: +PyErr_SetFromErrnoWithFilenameObject:PyObject*::null: +PyErr_SetFromErrnoWithFilenameObject:PyObject*:type:+1: +PyErr_SetFromErrnoWithFilenameObject:PyObject*:filenameObject:+1: + +PyErr_SetFromErrnoWithFilenameObjects:PyObject*::null: +PyErr_SetFromErrnoWithFilenameObjects:PyObject*:type:+1: +PyErr_SetFromErrnoWithFilenameObjects:PyObject*:filenameObject:+1: +PyErr_SetFromErrnoWithFilenameObjects:PyObject*:filenameObject2:+1: + PyErr_SetFromWindowsErr:PyObject*::null: PyErr_SetFromWindowsErr:int:ierr:: @@ -384,6 +666,16 @@ PyErr_SetFromWindowsErrWithFilename:PyObject*::null: PyErr_SetFromWindowsErrWithFilename:int:ierr:: PyErr_SetFromWindowsErrWithFilename:const char*:filename:: +PyErr_SetImportError:PyObject*::null: +PyErr_SetImportError:PyObject*:msg:+1: +PyErr_SetImportError:PyObject*:name:+1: +PyErr_SetImportError:PyObject*:path:+1: + +PyErr_SetImportErrorSubclass:PyObject*::null: +PyErr_SetImportErrorSubclass:PyObject*:msg:+1: +PyErr_SetImportErrorSubclass:PyObject*:name:+1: +PyErr_SetImportErrorSubclass:PyObject*:path:+1: + PyErr_SetInterrupt:void::: PyErr_SetNone:void::: @@ -397,31 +689,69 @@ PyErr_SetString:void::: PyErr_SetString:PyObject*:type:+1: PyErr_SetString:const char*:message:: -PyErr_Format:PyObject*::null: -PyErr_Format:PyObject*:exception:+1: -PyErr_Format:const char*:format:: -PyErr_Format::...:: +PyErr_SyntaxLocation:void::: +PyErr_SyntaxLocation:const char*:filename:: +PyErr_SyntaxLocation:int:lineno:: -PyErr_FormatV:PyObject*::null: -PyErr_FormatV:PyObject*:exception:+1: -PyErr_FormatV:const char*:format:: -PyErr_FormatV:va_list:vargs:: +PyErr_SyntaxLocationEx:void::: +PyErr_SyntaxLocationEx:const char*:filename:: +PyErr_SyntaxLocationEx:int:lineno:: +PyErr_SyntaxLocationEx:int:col_offset:: + +PyErr_SyntaxLocationObject:void::: +PyErr_SyntaxLocationObject:PyObject*:filename:+1: +PyErr_SyntaxLocationObject:int:lineno:: +PyErr_SyntaxLocationObject:int:col_offset:: PyErr_WarnEx:int::: PyErr_WarnEx:PyObject*:category:0: PyErr_WarnEx:const char*:message:: PyErr_WarnEx:Py_ssize_t:stack_level:: +PyErr_WarnExplicit:int::: +PyErr_WarnExplicit:PyObject*:category:0: +PyErr_WarnExplicit:const char*:message:: +PyErr_WarnExplicit:const char*:filename:: +PyErr_WarnExplicit:int:lineno:: +PyErr_WarnExplicit:const char*:module:: +PyErr_WarnExplicit:PyObject*:registry:0: + +PyErr_WarnExplicitObject:int::: +PyErr_WarnExplicitObject:PyObject*:category:0: +PyErr_WarnExplicitObject:PyObject*:message:0: +PyErr_WarnExplicitObject:PyObject*:filename:0: +PyErr_WarnExplicitObject:int:lineno:: +PyErr_WarnExplicitObject:PyObject*:module:0: +PyErr_WarnExplicitObject:PyObject*:registry:0: + +PyErr_WarnFormat:int::: +PyErr_WarnFormat:PyObject*:category:0: +PyErr_WarnFormat:Py_ssize_t:stack_level:: +PyErr_WarnFormat:const char*:format:: +PyErr_WarnFormat::...:: + +PyErr_WriteUnraisable:void::: +PyErr_WriteUnraisable:PyObject*:obj:0: + PyEval_AcquireLock:void::: PyEval_AcquireThread:void::: PyEval_AcquireThread:PyThreadState*:tstate:: PyEval_GetBuiltins:PyObject*::0: + PyEval_GetLocals:PyObject*::0: + PyEval_GetGlobals:PyObject*::0: + PyEval_GetFrame:PyObject*::0: +PyEval_GetFuncDesc:const char*::: +PyEval_GetFuncDesc:PyObject*:func:0: + +PyEval_GetFuncName:const char*::: +PyEval_GetFuncName:PyObject*:func:0: + PyEval_InitThreads:void::: PyEval_ReleaseLock:void::: @@ -434,61 +764,85 @@ PyEval_RestoreThread:PyThreadState*:tstate:: PyEval_SaveThread:PyThreadState*::: +PyEval_SetProfile:void::: +PyEval_SetProfile:Py_tracefunc:func:: +PyEval_SetProfile:PyObject*:obj:+1: + +PyEval_SetTrace:void::: +PyEval_SetTrace:Py_tracefunc:func:: +PyEval_SetTrace:PyObject*:obj:+1: + PyEval_EvalCode:PyObject*::+1: -PyEval_EvalCode:PyCodeObject*:co:0: +PyEval_EvalCode:PyObject*:co:0: PyEval_EvalCode:PyObject*:globals:0: PyEval_EvalCode:PyObject*:locals:0: -PyException_GetTraceback:PyObject*::+1: +PyEval_EvalCodeEx:PyObject*::+1: +PyEval_EvalCodeEx:PyObject*:co:0: +PyEval_EvalCodeEx:PyObject*:globals:0: +PyEval_EvalCodeEx:PyObject*:locals:0: +PyEval_EvalCodeEx:PyObject*const*:args:: +PyEval_EvalCodeEx:int:argcount:: +PyEval_EvalCodeEx:PyObject*const*:kws:: +PyEval_EvalCodeEx:int:kwcount:: +PyEval_EvalCodeEx:PyObject*const*:defs:: +PyEval_EvalCodeEx:int:defcount:: +PyEval_EvalCodeEx:PyObject*:kwdefs:0: +PyEval_EvalCodeEx:PyObject*:closure:0: -PyFile_AsFile:FILE*::: -PyFile_AsFile:PyFileObject*:p:0: +PyEval_EvalFrame:PyObject*::+1: +PyEval_EvalFrame:PyFrameObject*:f:0: -PyFile_Check:int::: -PyFile_Check:PyObject*:p:0: +PyEval_EvalFrameEx:PyObject*::+1: +PyEval_EvalFrameEx:PyFrameObject*:f:0: +PyEval_EvalFrameEx:int:throwflag:: -PyFile_FromFile:PyObject*::+1: -PyFile_FromFile:FILE*:fp:: -PyFile_FromFile:const char*:name:: -PyFile_FromFile:const char*:mode:: -PyFile_FromFile:int(*:close):: +PyEval_MergeCompilerFlags:int::: +PyEval_MergeCompilerFlags:PyCompilerFlags*:cf:: -PyFile_FromFileEx:PyObject*::+1: -PyFile_FromFileEx:FILE*:fp:: -PyFile_FromFileEx:const char*:name:: -PyFile_FromFileEx:const char*:mode:: -PyFile_FromFileEx:int(*:close):: -PyFile_FromFileEx:int:buffering:: -PyFile_FromFileEx:const char*:encoding:: -PyFile_FromFileEx:const char*:newline:: +PyException_GetCause:PyObject*::+1: +PyException_GetCause:PyObject*:ex:0: -PyFile_FromString:PyObject*::+1: -PyFile_FromString:const char*:name:: -PyFile_FromString:const char*:mode:: +PyException_GetContext:PyObject*::+1: +PyException_GetContext:PyObject*:ex:0: + +PyException_GetTraceback:PyObject*::+1: +PyException_GetTraceback:PyObject*:ex:0: + +PyException_SetCause:void::: +PyException_SetCause:PyObject*:ex:0: +PyException_SetCause:PyObject*:cause:+1: + +PyException_SetContext:void::: +PyException_SetContext:PyObject*:ex:0: +PyException_SetContext:PyObject*:ctx:+1: + +PyException_SetTraceback:int::: +PyException_SetTraceback:PyObject*:ex:0: +PyException_SetTraceback:PyObject*:tb:+1: + +PyFile_FromFd:PyObject*::+1: +PyFile_FromFd:int:fd:: +PyFile_FromFd:const char*:name:: +PyFile_FromFd:const char*:mode:: +PyFile_FromFd:int:buffering:: +PyFile_FromFd:const char*:encoding:: +PyFile_FromFd:const char*:errors:: +PyFile_FromFd:const char*:newline:: +PyFile_FromFd:int:closefd:: PyFile_GetLine:PyObject*::+1: -PyFile_GetLine:PyObject*:p:: +PyFile_GetLine:PyObject*:p:0: PyFile_GetLine:int:n:: -PyFile_Name:PyObject*::0: -PyFile_Name:PyObject*:p:0: - -PyFile_SetBufSize:void::: -PyFile_SetBufSize:PyFileObject*:p:0: -PyFile_SetBufSize:int:n:: - -PyFile_SoftSpace:int::: -PyFile_SoftSpace:PyFileObject*:p:0: -PyFile_SoftSpace:int:newflag:: - PyFile_WriteObject:int::: PyFile_WriteObject:PyObject*:obj:0: -PyFile_WriteObject:PyFileObject*:p:0: +PyFile_WriteObject:PyObject*:p:0: PyFile_WriteObject:int:flags:: PyFile_WriteString:int::: PyFile_WriteString:const char*:s:: -PyFile_WriteString:PyFileObject*:p:0: +PyFile_WriteString:PyObject*:p:0: PyFile_WriteString:int:flags:: PyFloat_AS_DOUBLE:double::: @@ -500,15 +854,33 @@ PyFloat_AsDouble:PyObject*:pyfloat:0: PyFloat_Check:int::: PyFloat_Check:PyObject*:p:0: +PyFloat_CheckExact:int::: +PyFloat_CheckExact:PyObject*:p:0: + PyFloat_FromDouble:PyObject*::+1: PyFloat_FromDouble:double:v:: PyFloat_FromString:PyObject*::+1: PyFloat_FromString:PyObject*:str:0: +PyFloat_GetInfo:PyObject*::+1: +PyFloat_GetInfo::void:: + +PyFrozenSet_Check:int::: +PyFrozenSet_Check:PyObject*:p:0: + +PyFrozenSet_CheckExact:int::: +PyFrozenSet_CheckExact:PyObject*:p:0: + PyFrozenSet_New:PyObject*::+1: PyFrozenSet_New:PyObject*:iterable:0: +PyFunction_Check:int::: +PyFunction_Check:PyObject*:o:0: + +PyFunction_GetAnnotations:PyObject*::0: +PyFunction_GetAnnotations:PyObject*:op:0: + PyFunction_GetClosure:PyObject*::0: PyFunction_GetClosure:PyObject*:op:0: @@ -533,6 +905,10 @@ PyFunction_NewWithQualName:PyObject*:code:+1: PyFunction_NewWithQualName:PyObject*:globals:+1: PyFunction_NewWithQualName:PyObject*:qualname:+1: +PyFunction_SetAnnotations:int::: +PyFunction_SetAnnotations:PyObject*:op:0: +PyFunction_SetAnnotations:PyObject*:annotations:+1: + PyFunction_SetClosure:int::: PyFunction_SetClosure:PyObject*:op:0: PyFunction_SetClosure:PyObject*:closure:+1: @@ -541,30 +917,27 @@ PyFunction_SetDefaults:int::: PyFunction_SetDefaults:PyObject*:op:0: PyFunction_SetDefaults:PyObject*:defaults:+1: +PyGen_Check:int::: +PyGen_Check:PyObject*:ob:0: + +PyGen_CheckExact:int::: +PyGen_CheckExact:PyObject*:ob:0: + PyGen_New:PyObject*::+1: PyGen_New:PyFrameObject*:frame:0: PyGen_NewWithQualName:PyObject*::+1: PyGen_NewWithQualName:PyFrameObject*:frame:0: +PyGen_NewWithQualName:PyObject*:name:0: +PyGen_NewWithQualName:PyObject*:qualname:0: + +PyCoro_CheckExact:int::: +PyCoro_CheckExact:PyObject*:ob:0: PyCoro_New:PyObject*::+1: PyCoro_New:PyFrameObject*:frame:0: - -Py_InitModule:PyObject*::0: -Py_InitModule:const char*:name:: -Py_InitModule:PyMethodDef[]:methods:: - -Py_InitModule3:PyObject*::0: -Py_InitModule3:const char*:name:: -Py_InitModule3:PyMethodDef[]:methods:: -Py_InitModule3:const char*:doc:: - -Py_InitModule4:PyObject*::0: -Py_InitModule4:const char*:name:: -Py_InitModule4:PyMethodDef[]:methods:: -Py_InitModule4:const char*:doc:: -Py_InitModule4:PyObject*:self:: -Py_InitModule4:int:apiver::usually provided by Py_InitModule or Py_InitModule3 +PyCoro_New:PyObject*:name:0: +PyCoro_New:PyObject*:qualname:0: PyImport_AddModule:PyObject*::0:reference borrowed from sys.modules PyImport_AddModule:const char*:name:: @@ -637,39 +1010,26 @@ PyImport_ImportModuleLevelObject:PyObject*:locals:0:??? PyImport_ImportModuleLevelObject:PyObject*:fromlist:0:??? PyImport_ImportModuleLevelObject:int:level:: +PyImport_ImportModuleNoBlock:PyObject*::+1: +PyImport_ImportModuleNoBlock:const char*:name:: + PyImport_ReloadModule:PyObject*::+1: PyImport_ReloadModule:PyObject*:m:0: -PyInstance_New:PyObject*::+1: -PyInstance_New:PyObject*:klass:+1: -PyInstance_New:PyObject*:arg:0: -PyInstance_New:PyObject*:kw:0: - -PyInstance_NewRaw:PyObject*::+1: -PyInstance_NewRaw:PyObject*:klass:+1: -PyInstance_NewRaw:PyObject*:dict:+1: +PyIndex_Check:int::: +PyIndex_Check:PyObject*:o:0: -PyInt_AS_LONG:long::: -PyInt_AS_LONG:PyIntObject*:io:0: +PyInstanceMethod_Check:int::: +PyInstanceMethod_Check:PyObject*:o:0: -PyInt_AsLong:long::: -PyInt_AsLong:PyObject*:io:0: +PyInstanceMethod_Function:PyObject*::0: +PyInstanceMethod_Function:PyObject*:im:0: -PyInt_Check:int::: -PyInt_Check:PyObject*:op:0: +PyInstanceMethod_GET_FUNCTION:PyObject*::0: +PyInstanceMethod_GET_FUNCTION:PyObject*:im:0: -PyInt_FromLong:PyObject*::+1: -PyInt_FromLong:long:ival:: - -PyInt_FromString:PyObject*::+1: -PyInt_FromString:char*:str:0: -PyInt_FromString:char**:pend:0: -PyInt_FromString:int:base:0: - -PyInt_FromSsize_t:PyObject*::+1: -PyInt_FromSsize_t:Py_ssize_t:ival:: - -PyInt_GetMax:long::: +PyInstanceMethod_New:PyObject*::+1: +PyInstanceMethod_New:PyObject*:func:0: PyInterpreterState_Clear:void::: PyInterpreterState_Clear:PyInterpreterState*:interp:: @@ -682,7 +1042,8 @@ PyInterpreterState_GetID:PyInterpreterState*:interp:: PyInterpreterState_New:PyInterpreterState*::: -PyIter_Check:int:o:0: +PyIter_Check:int::: +PyIter_Check:PyObject*:o:0: PyIter_Next:PyObject*::+1: PyIter_Next:PyObject*:o:0: @@ -697,50 +1058,53 @@ PyList_AsTuple:PyObject*:list:0: PyList_Check:int::: PyList_Check:PyObject*:p:0: +PyList_CheckExact:int::: +PyList_CheckExact:PyObject*:p:0: + PyList_GET_ITEM:PyObject*::0: PyList_GET_ITEM:PyObject*:list:0: -PyList_GET_ITEM:int:i:0: +PyList_GET_ITEM:Py_ssize_t:i:: -PyList_GET_SIZE:int::: +PyList_GET_SIZE:Py_ssize_t::: PyList_GET_SIZE:PyObject*:list:0: PyList_GetItem:PyObject*::0: PyList_GetItem:PyObject*:list:0: -PyList_GetItem:int:index:: +PyList_GetItem:Py_ssize_t:index:: PyList_GetSlice:PyObject*::+1: PyList_GetSlice:PyObject*:list:0: -PyList_GetSlice:int:low:: -PyList_GetSlice:int:high:: +PyList_GetSlice:Py_ssize_t:low:: +PyList_GetSlice:Py_ssize_t:high:: PyList_Insert:int::: PyList_Insert:PyObject*:list:0: -PyList_Insert:int:index:: +PyList_Insert:Py_ssize_t:index:: PyList_Insert:PyObject*:item:+1: PyList_New:PyObject*::+1: -PyList_New:int:len:: +PyList_New:Py_ssize_t:len:: PyList_Reverse:int::: PyList_Reverse:PyObject*:list:0: PyList_SET_ITEM:void::: PyList_SET_ITEM:PyObject*:list:0: -PyList_SET_ITEM:int:i:: +PyList_SET_ITEM:Py_ssize_t:i:: PyList_SET_ITEM:PyObject*:o:0: PyList_SetItem:int::: PyList_SetItem:PyObject*:list:0: -PyList_SetItem:int:index:: +PyList_SetItem:Py_ssize_t:index:: PyList_SetItem:PyObject*:item:0: PyList_SetSlice:int::: PyList_SetSlice:PyObject*:list:0: -PyList_SetSlice:int:low:: -PyList_SetSlice:int:high:: +PyList_SetSlice:Py_ssize_t:low:: +PyList_SetSlice:Py_ssize_t:high:: PyList_SetSlice:PyObject*:itemlist:0:but increfs its elements? -PyList_Size:int::: +PyList_Size:Py_ssize_t::: PyList_Size:PyObject*:list:0: PyList_Sort:int::: @@ -752,12 +1116,44 @@ PyLong_AsDouble:PyObject*:pylong:0: PyLong_AsLong:long::: PyLong_AsLong:PyObject*:pylong:0: +PyLong_AsLongAndOverflow:long::: +PyLong_AsLongAndOverflow:PyObject*:obj:0: +PyLong_AsLongAndOverflow:int*:overflow:: + +PyLong_AsLongLong:long long::: +PyLong_AsLongLong:PyObject*:obj:0: + +PyLong_AsLongLongAndOverflow:long long::: +PyLong_AsLongLongAndOverflow:PyObject*:obj:0: +PyLong_AsLongLongAndOverflow:int*:overflow:: + +PyLong_AsSize_t:size_t::: +PyLong_AsSize_t:PyObject*:pylong:0: + +PyLong_AsSsize_t:Py_ssize_t::: +PyLong_AsSsize_t:PyObject*:pylong:0: + PyLong_AsUnsignedLong:unsigned long::: PyLong_AsUnsignedLong:PyObject*:pylong:0: +PyLong_AsUnsignedLongLong:unsigned long long::: +PyLong_AsUnsignedLongLong:PyObject*:pylong:0: + +PyLong_AsUnsignedLongMask:unsigned long::: +PyLong_AsUnsignedLongMask:PyObject*:obj:0: + +PyLong_AsUnsignedLongLongMask:unsigned long long::: +PyLong_AsUnsignedLongLongMask:PyObject*:obj:0: + +PyLong_AsVoidPtr:void*::: +PyLong_AsVoidPtr:PyObject*:pylong:0: + PyLong_Check:int::: PyLong_Check:PyObject*:p:0: +PyLong_CheckExact:int::: +PyLong_CheckExact:PyObject*:p:0: + PyLong_FromDouble:PyObject*::+1: PyLong_FromDouble:double:v:: @@ -770,16 +1166,26 @@ PyLong_FromLongLong:long long:v:: PyLong_FromUnsignedLongLong:PyObject*::+1: PyLong_FromUnsignedLongLong:unsigned long long:v:: +PyLong_FromSize_t:PyObject*::+1: +PyLong_FromSize_t:size_t:v:: + +PyLong_FromSsize_t:PyObject*::+1: +PyLong_FromSsize_t:Py_ssize_t:v:: + PyLong_FromString:PyObject*::+1: PyLong_FromString:const char*:str:: PyLong_FromString:char**:pend:: PyLong_FromString:int:base:: PyLong_FromUnicode:PyObject*::+1: -PyLong_FromUnicode:Py_UNICODE:u:: -PyLong_FromUnicode:int:length:: +PyLong_FromUnicode:Py_UNICODE*:u:: +PyLong_FromUnicode:Py_ssize_t:length:: PyLong_FromUnicode:int:base:: +PyLong_FromUnicodeObject:PyObject*::+1: +PyLong_FromUnicodeObject:PyObject*:u:0: +PyLong_FromUnicodeObject:int:base:: + PyLong_FromUnsignedLong:PyObject*::+1: PyLong_FromUnsignedLong:unsignedlong:v:: @@ -815,7 +1221,7 @@ PyMapping_Items:PyObject*:o:0: PyMapping_Keys:PyObject*::+1: PyMapping_Keys:PyObject*:o:0: -PyMapping_Length:int::: +PyMapping_Length:Py_ssize_t::: PyMapping_Length:PyObject*:o:0: PyMapping_SetItemString:int::: @@ -823,6 +1229,9 @@ PyMapping_SetItemString:PyObject*:o:0: PyMapping_SetItemString:const char*:key:: PyMapping_SetItemString:PyObject*:v:+1: +PyMapping_Size:Py_ssize_t::: +PyMapping_Size:PyObject*:o:0: + PyMapping_Values:PyObject*::+1: PyMapping_Values:PyObject*:o:0: @@ -834,20 +1243,43 @@ PyMarshal_ReadObjectFromFile:FILE*:file:: PyMarshal_ReadObjectFromString:PyObject*::+1: PyMarshal_ReadObjectFromString:const char*:string:: -PyMarshal_ReadObjectFromString:int:len:: +PyMarshal_ReadObjectFromString:Py_ssize_t:len:: PyMarshal_WriteObjectToString:PyObject*::+1: PyMarshal_WriteObjectToString:PyObject*:value:0: +PyMarshal_WriteObjectToString:int:version:: -PyMethod_Class:PyObject*::0: -PyMethod_Class:PyObject*:im:0: +PyMemoryView_Check:int::: +PyMemoryView_Check:PyObject*:obj:0: + +PyMemoryView_FromBuffer:PyObject*::+1: +PyMemoryView_FromBuffer:Py_buffer*:view:: + +PyMemoryView_FromMemory:PyObject*::+1: +PyMemoryView_FromMemory:char*:mem:: +PyMemoryView_FromMemory:Py_ssize_t:size:: +PyMemoryView_FromMemory:int:flags:: + +PyMemoryView_FromObject:PyObject*::+1: +PyMemoryView_FromObject:PyObject*:obj:0: + +PyMemoryView_GET_BASE:Py_buffer*::: +PyMemoryView_GET_BASE:PyObject*:mview:0: + +PyMemoryView_GET_BUFFER:Py_buffer*::: +PyMemoryView_GET_BUFFER:PyObject*:mview:0: + +PyMemoryView_GetContiguous:PyObject*::+1: +PyMemoryView_GetContiguous:PyObject*:obj:0: +PyMemoryView_GetContiguous:int:buffertype:: +PyMemoryView_GetContiguous:char:order:: + +PyMethod_Check:int::: +PyMethod_Check:PyObject*:o:0: PyMethod_Function:PyObject*::0: PyMethod_Function:PyObject*:im:0: -PyMethod_GET_CLASS:PyObject*::0: -PyMethod_GET_CLASS:PyObject*:im:0: - PyMethod_GET_FUNCTION:PyObject*::0: PyMethod_GET_FUNCTION:PyObject*:im:0: @@ -862,18 +1294,93 @@ PyMethod_New:PyObject*:class:0: PyMethod_Self:PyObject*::0: PyMethod_Self:PyObject*:im:0: +PyModule_AddFunctions:int::: +PyModule_AddFunctions:PyObject*:module:0: +PyModule_AddFunctions:PyMethodDef*:functions:: + +PyModule_AddIntConstant:int::: +PyModule_AddIntConstant:PyObject*:module:0: +PyModule_AddIntConstant:const char*:name:: +PyModule_AddIntConstant:long:value:: + +PyModule_AddIntMacro:int::: +PyModule_AddIntMacro:PyObject*:module:0: +PyModule_AddIntMacro::macro:: + +PyModule_AddObject:int::: +PyModule_AddObject:PyObject*:module:0: +PyModule_AddObject:const char*:name:: +PyModule_AddObject:PyObject*:value:+1: + +PyModule_AddStringConstant:int::: +PyModule_AddStringConstant:PyObject*:module:0: +PyModule_AddStringConstant:const char*:name:: +PyModule_AddStringConstant:const char*:value:: + +PyModule_AddStringMacro:int::: +PyModule_AddStringMacro:PyObject*:module:0: +PyModule_AddStringMacro::macro:: + +PyModule_Check:int::: +PyModule_Check:PyObject*:p:0: + +PyModule_CheckExact:int::: +PyModule_CheckExact:PyObject*:p:0: + +PyModule_Create:PyObject*::+1: +PyModule_Create:PyModuleDef*:def:: + +PyModule_Create2:PyObject*::+1: +PyModule_Create2:PyModuleDef*:def:: +PyModule_Create2:int:module_api_version:: + +PyModule_ExecDef:int::: +PyModule_ExecDef:PyObject*:module:0: +PyModule_ExecDef:PyModuleDef*:def:: + +PyModule_FromDefAndSpec:PyObject*::+1: +PyModule_FromDefAndSpec:PyModuleDef*:def:: +PyModule_FromDefAndSpec:PyObject*:spec:0: + +PyModule_FromDefAndSpec2:PyObject*::+1: +PyModule_FromDefAndSpec2:PyModuleDef*:def:: +PyModule_FromDefAndSpec2:PyObject*:spec:0: +PyModule_FromDefAndSpec2:int:module_api_version:: + +PyModule_GetDef:PyModuleDef*::0: +PyModule_GetDef:PyObject*:module:0: + PyModule_GetDict:PyObject*::0: -PyModule_GetDict::PyObject* module:0: +PyModule_GetDict:PyObject*:module:0: PyModule_GetFilename:const char*::: PyModule_GetFilename:PyObject*:module:0: +PyModule_GetFilenameObject:PyObject*::+1: +PyModule_GetFilenameObject:PyObject*:module:0: + PyModule_GetName:const char*::: PyModule_GetName:PyObject*:module:0: +PyModule_GetNameObject:PyObject*::+1: +PyModule_GetNameObject:PyObject*:module:0: + +PyModule_GetState:void*::: +PyModule_GetState:PyObject*:module:0: + PyModule_New:PyObject*::+1: PyModule_New::char* name:: +PyModule_NewObject:PyObject*::+1: +PyModule_NewObject:PyObject*:name:+1: + +PyModule_SetDocString:int::: +PyModule_SetDocString:PyObject*:module:0: +PyModule_SetDocString:const char*:docstring:: + +PyModuleDef_Init:PyObject*::0: +PyModuleDef_Init:PyModuleDef*:def:0: + PyNumber_Absolute:PyObject*::+1: PyNumber_Absolute:PyObject*:o:0: @@ -885,12 +1392,12 @@ PyNumber_And:PyObject*::+1: PyNumber_And:PyObject*:o1:0: PyNumber_And:PyObject*:o2:0: -PyNumber_Check:PyObject*:o:0: -PyNumber_Check:int::: +PyNumber_AsSsize_t:Py_ssize_t::: +PyNumber_AsSsize_t:PyObject*:o:0: +PyNumber_AsSsize_t:PyObject*:exc:0: -PyNumber_Divide:PyObject*::+1: -PyNumber_Divide:PyObject*:o1:0: -PyNumber_Divide:PyObject*:o2:0: +PyNumber_Check:int::: +PyNumber_Check:PyObject*:o:0: PyNumber_Divmod:PyObject*::+1: PyNumber_Divmod:PyObject*:o1:0: @@ -903,6 +1410,9 @@ PyNumber_FloorDivide:PyObject*::+1: PyNumber_FloorDivide:PyObject*:v:0: PyNumber_FloorDivide:PyObject*:w:0: +PyNumber_Index:PyObject*::+1: +PyNumber_Index:PyObject*:o:0: + PyNumber_InPlaceAdd:PyObject*::+1: PyNumber_InPlaceAdd:PyObject*:v:0: PyNumber_InPlaceAdd:PyObject*:w:0: @@ -911,10 +1421,6 @@ PyNumber_InPlaceAnd:PyObject*::+1: PyNumber_InPlaceAnd:PyObject*:v:0: PyNumber_InPlaceAnd:PyObject*:w:0: -PyNumber_InPlaceDivide:PyObject*::+1: -PyNumber_InPlaceDivide:PyObject*:v:0: -PyNumber_InPlaceDivide:PyObject*:w:0: - PyNumber_InPlaceFloorDivide:PyObject*::+1: PyNumber_InPlaceFloorDivide:PyObject*:v:0: PyNumber_InPlaceFloorDivide:PyObject*:w:0: @@ -923,6 +1429,10 @@ PyNumber_InPlaceLshift:PyObject*::+1: PyNumber_InPlaceLshift:PyObject*:v:0: PyNumber_InPlaceLshift:PyObject*:w:0: +PyNumber_InPlaceMatrixMultiply:PyObject*::+1: +PyNumber_InPlaceMatrixMultiply:PyObject*:o1:0: +PyNumber_InPlaceMatrixMultiply:PyObject*:o2:0: + PyNumber_InPlaceMultiply:PyObject*::+1: PyNumber_InPlaceMultiply:PyObject*:v:0: PyNumber_InPlaceMultiply:PyObject*:w:0: @@ -966,6 +1476,10 @@ PyNumber_Lshift:PyObject*::+1: PyNumber_Lshift:PyObject*:o1:0: PyNumber_Lshift:PyObject*:o2:0: +PyNumber_MatrixMultiply:PyObject*::+1: +PyNumber_MatrixMultiply:PyObject*:o1:0: +PyNumber_MatrixMultiply:PyObject*:o2:0: + PyNumber_Multiply:PyObject*::+1: PyNumber_Multiply:PyObject*:o1:0: PyNumber_Multiply:PyObject*:o2:0: @@ -997,6 +1511,10 @@ PyNumber_Subtract:PyObject*::+1: PyNumber_Subtract:PyObject*:o1:0: PyNumber_Subtract:PyObject*:o2:0: +PyNumber_ToBase:PyObject*::+1: +PyNumber_ToBase:PyObject*:n:0: +PyNumber_ToBase:int:base:: + PyNumber_TrueDivide:PyObject*::+1: PyNumber_TrueDivide:PyObject*:v:0: PyNumber_TrueDivide:PyObject*:w:0: @@ -1019,6 +1537,27 @@ PyOS_BeforeFork:void::: PyOS_FSPath:PyObject*::+1: PyOS_FSPath:PyObject*:path:0: +PyObject_ASCII:PyObject*::+1: +PyObject_ASCII:PyObject*:o:0: + +PyObject_AsCharBuffer:int::: +PyObject_AsCharBuffer:PyObject*:obj:0: +PyObject_AsCharBuffer:const char**:buffer:: +PyObject_AsCharBuffer:Py_ssize_t*:buffer_len:: + +PyObject_AsReadBuffer:int::: +PyObject_AsReadBuffer:PyObject*:obj:0: +PyObject_AsReadBuffer:const void**:buffer:: +PyObject_AsReadBuffer:Py_ssize_t*:buffer_len:: + +PyObject_AsWriteBuffer:int::: +PyObject_AsWriteBuffer:PyObject*:obj:0: +PyObject_AsWriteBuffer:void**:buffer:: +PyObject_AsWriteBuffer:Py_ssize_t*:buffer_len:: + +PyObject_Bytes:PyObject*::+1: +PyObject_Bytes:PyObject*:o:0: + PyObject_Call:PyObject*::+1: PyObject_Call:PyObject*:callable_object:0: PyObject_Call:PyObject*:args:0: @@ -1048,14 +1587,11 @@ PyObject_CallObject:PyObject*::+1: PyObject_CallObject:PyObject*:callable_object:0: PyObject_CallObject:PyObject*:args:0: -PyObject_Cmp:int::: -PyObject_Cmp:PyObject*:o1:0: -PyObject_Cmp:PyObject*:o2:0: -PyObject_Cmp:int*:result:: +PyObject_CheckBuffer:int::: +PyObject_CheckBuffer:PyObject*:obj:0: -PyObject_Compare:int::: -PyObject_Compare:PyObject*:o1:0: -PyObject_Compare:PyObject*:o2:0: +PyObject_CheckReadBuffer:int::: +PyObject_CheckReadBuffer:PyObject*:o:0: PyObject_DelAttr:int::: PyObject_DelAttr:PyObject*:o:0: @@ -1072,6 +1608,46 @@ PyObject_DelItem:PyObject*:key:0: PyObject_Dir:PyObject*::+1: PyObject_Dir:PyObject*:o:0: +PyObject_GC_Del:void::: +PyObject_GC_Del:void*:op:: + +PyObject_GC_New:TYPE*::+1: +PyObject_GC_New::TYPE:: +PyObject_GC_New:PyTypeObject*:type:0: + +PyObject_GC_NewVar:TYPE*::+1: +PyObject_GC_NewVar::TYPE:: +PyObject_GC_NewVar:PyTypeObject*:type:0: +PyObject_GC_NewVar:Py_ssize_t:size:: + +PyObject_GC_Resize:TYPE*::0: +PyObject_GC_Resize::TYPE:: +PyObject_GC_Resize:PyVarObject*:op:0: +PyObject_GC_Resize:Py_ssize_t:newsize:: + +PyObject_GC_Track:void::: +PyObject_GC_Track:PyObject*:op:0: + +PyObject_GC_UnTrack:void::: +PyObject_GC_UnTrack:void*:op:: + +PyObject_GenericGetAttr:PyObject*::+1: +PyObject_GenericGetAttr:PyObject*:o:0: +PyObject_GenericGetAttr:PyObject*:name:0: + +PyObject_GenericGetDict:PyObject*::+1: +PyObject_GenericGetDict:PyObject*:o:0: +PyObject_GenericGetDict:void*:context:: + +PyObject_GenericSetAttr:int::: +PyObject_GenericSetAttr:PyObject*:o:0: +PyObject_GenericSetAttr:PyObject*:name:0: +PyObject_GenericSetAttr:PyObject*:value:+1: + +PyObject_GenericSetDict:int::: +PyObject_GenericSetDict:PyObject*:o:+1: +PyObject_GenericSetDict:void*:context:: + PyObject_GetAttr:PyObject*::+1: PyObject_GetAttr:PyObject*:o:0: PyObject_GetAttr:PyObject*:attr_name:0: @@ -1080,6 +1656,11 @@ PyObject_GetAttrString:PyObject*::+1: PyObject_GetAttrString:PyObject*:o:0: PyObject_GetAttrString:const char*:attr_name:: +PyObject_GetBuffer:int::: +PyObject_GetBuffer:PyObject*:exporter:0: +PyObject_GetBuffer:Py_buffer*:view:: +PyObject_GetBuffer:int:flags:: + PyObject_GetItem:PyObject*::+1: PyObject_GetItem:PyObject*:o:0: PyObject_GetItem:PyObject*:key:0: @@ -1093,30 +1674,59 @@ PyObject_HasAttr:PyObject*:attr_name:0: PyObject_HasAttrString:int::: PyObject_HasAttrString:PyObject*:o:0: -PyObject_HasAttrString:const char*:attr_name:0: +PyObject_HasAttrString:const char*:attr_name:: PyObject_Hash:int::: PyObject_Hash:PyObject*:o:0: +PyObject_HashNotImplemented:Py_hash_t::: +PyObject_HashNotImplemented:PyObject*:o:0: + +PyObject_IsInstance:int::: +PyObject_IsInstance:PyObject*:inst:0: +PyObject_IsInstance:PyObject*:cls:0: + +PyObject_IsSubclass:int::: +PyObject_IsSubclass:PyObject*:derived:0: +PyObject_IsSubclass:PyObject*:cls:0: + PyObject_IsTrue:int::: PyObject_IsTrue:PyObject*:o:0: PyObject_Init:PyObject*::0: PyObject_Init:PyObject*:op:0: +PyObject_Init:PyTypeObject*:type:0: PyObject_InitVar:PyVarObject*::0: PyObject_InitVar:PyVarObject*:op:0: -PyObject_Length:int::: +PyObject_Length:Py_ssize_t::: PyObject_Length:PyObject*:o:0: +PyObject_LengthHint:Py_ssize_t::: +PyObject_LengthHint:PyObject*:o:0: +PyObject_LengthHint:Py_ssize_t:default:: + PyObject_NEW:PyObject*::+1: +PyObject_NEW::TYPE:: +PyObject_NEW:PyTypeObject*:type:0: PyObject_New:PyObject*::+1: +PyObject_New::TYPE:: +PyObject_New:PyTypeObject*:type:0: PyObject_NEW_VAR:PyObject*::+1: +PyObject_NEW_VAR::TYPE:: +PyObject_NEW_VAR:PyTypeObject*:type:0: +PyObject_NEW_VAR:Py_ssize_t:size:: PyObject_NewVar:PyObject*::+1: +PyObject_NewVar::TYPE:: +PyObject_NewVar:PyTypeObject*:type:0: +PyObject_NewVar:Py_ssize_t:size:: + +PyObject_Not:int::: +PyObject_Not:PyObject*:o:0: PyObject_Print:int::: PyObject_Print:PyObject*:o:0: @@ -1151,28 +1761,65 @@ PyObject_SetItem:PyObject*:o:0: PyObject_SetItem:PyObject*:key:0: PyObject_SetItem:PyObject*:v:+1: +PyObject_Size:Py_ssize_t::: +PyObject_Size:PyObject*:o:0: + PyObject_Str:PyObject*::+1: PyObject_Str:PyObject*:o:0: PyObject_Type:PyObject*::+1: PyObject_Type:PyObject*:o:0: -PyObject_Unicode:PyObject*::+1: -PyObject_Unicode:PyObject*:o:0: +PyObject_TypeCheck:int::: +PyObject_TypeCheck:PyObject*:o:0: +PyObject_TypeCheck:PyTypeObject*:type:0: PyParser_SimpleParseFile:struct _node*::: PyParser_SimpleParseFile:FILE*:fp:: PyParser_SimpleParseFile:const char*:filename:: PyParser_SimpleParseFile:int:start:: +PyParser_SimpleParseFileFlags:struct _node*::: +PyParser_SimpleParseFileFlags:FILE*:fp:: +PyParser_SimpleParseFileFlags:const char*:filename:: +PyParser_SimpleParseFileFlags:int:start:: +PyParser_SimpleParseFileFlags:int:flags:: + PyParser_SimpleParseString:struct _node*::: PyParser_SimpleParseString:const char*:str:: PyParser_SimpleParseString:int:start:: +PyParser_SimpleParseStringFlags:struct _node*::: +PyParser_SimpleParseStringFlags:const char*:str:: +PyParser_SimpleParseStringFlags:int:start:: +PyParser_SimpleParseStringFlags:int:flags:: + +PyParser_SimpleParseStringFlagsFilename:struct _node*::: +PyParser_SimpleParseStringFlagsFilename:const char*:str:: +PyParser_SimpleParseStringFlagsFilename:const char*:filename:: +PyParser_SimpleParseStringFlagsFilename:int:start:: +PyParser_SimpleParseStringFlagsFilename:int:flags:: + PyRun_AnyFile:int::: PyRun_AnyFile:FILE*:fp:: PyRun_AnyFile:const char*:filename:: +PyRun_AnyFileFlags:int::: +PyRun_AnyFileFlags:FILE*:fp:: +PyRun_AnyFileFlags:const char*:filename:: +PyRun_AnyFileFlags:PyCompilerFlags*:flags:: + +PyRun_AnyFileEx:int::: +PyRun_AnyFileEx:FILE*:fp:: +PyRun_AnyFileEx:const char*:filename:: +PyRun_AnyFileEx:int:closeit:: + +PyRun_AnyFileExFlags:int::: +PyRun_AnyFileExFlags:FILE*:fp:: +PyRun_AnyFileExFlags:const char*:filename:: +PyRun_AnyFileExFlags:int:closeit:: +PyRun_AnyFileExFlags:PyCompilerFlags*:flags:: + PyRun_File:PyObject*::+1:??? -- same as eval_code2() PyRun_File:FILE*:fp:: PyRun_File:const char*:filename:: @@ -1209,17 +1856,42 @@ PyRun_InteractiveLoop:int::: PyRun_InteractiveLoop:FILE*:fp:: PyRun_InteractiveLoop:const char*:filename:: +PyRun_InteractiveLoopFlags:int::: +PyRun_InteractiveLoopFlags:FILE*:fp:: +PyRun_InteractiveLoopFlags:const char*:filename:: +PyRun_InteractiveLoopFlags:PyCompilerFlags*:flags:: + PyRun_InteractiveOne:int::: PyRun_InteractiveOne:FILE*:fp:: PyRun_InteractiveOne:const char*:filename:: +PyRun_InteractiveOneFlags:int::: +PyRun_InteractiveOneFlags:FILE*:fp:: +PyRun_InteractiveOneFlags:const char*:filename:: +PyRun_InteractiveOneFlags:PyCompilerFlags*:flags:: + PyRun_SimpleFile:int::: PyRun_SimpleFile:FILE*:fp:: PyRun_SimpleFile:const char*:filename:: +PyRun_SimpleFileEx:int::: +PyRun_SimpleFileEx:FILE*:fp:: +PyRun_SimpleFileEx:const char*:filename:: +PyRun_SimpleFileEx:int:closeit:: + +PyRun_SimpleFileExFlags:int::: +PyRun_SimpleFileExFlags:FILE*:fp:: +PyRun_SimpleFileExFlags:const char*:filename:: +PyRun_SimpleFileExFlags:int:closeit:: +PyRun_SimpleFileExFlags:PyCompilerFlags*:flags:: + PyRun_SimpleString:int::: PyRun_SimpleString:const char*:command:: +PyRun_SimpleStringFlags:int::: +PyRun_SimpleStringFlags:const char*:command:: +PyRun_SimpleStringFlags:PyCompilerFlags*:flags:: + PyRun_String:PyObject*::+1:??? -- same as eval_code2() PyRun_String:const char*:str:: PyRun_String:int:start:: @@ -1233,6 +1905,9 @@ PyRun_StringFlags:PyObject*:globals:0: PyRun_StringFlags:PyObject*:locals:0: PyRun_StringFlags:PyCompilerFlags*:flags:: +PySeqIter_Check:int::: +PySeqIter_Check::op:: + PySeqIter_New:PyObject*::+1: PySeqIter_New:PyObject*:seq:: @@ -1243,18 +1918,22 @@ PySequence_Concat:PyObject*::+1: PySequence_Concat:PyObject*:o1:0: PySequence_Concat:PyObject*:o2:0: -PySequence_Count:int::: +PySequence_Contains:int::: +PySequence_Contains:PyObject*:o:0: +PySequence_Contains:PyObject*:value:0: + +PySequence_Count:Py_ssize_t::: PySequence_Count:PyObject*:o:0: PySequence_Count:PyObject*:value:0: PySequence_DelItem:int::: PySequence_DelItem:PyObject*:o:0: -PySequence_DelItem:int:i:: +PySequence_DelItem:Py_ssize_t:i:: PySequence_DelSlice:int::: PySequence_DelSlice:PyObject*:o:0: -PySequence_DelSlice:int:i1:: -PySequence_DelSlice:int:i2:: +PySequence_DelSlice:Py_ssize_t:i1:: +PySequence_DelSlice:Py_ssize_t:i2:: PySequence_Fast:PyObject*::+1: PySequence_Fast:PyObject*:v:0: @@ -1262,22 +1941,28 @@ PySequence_Fast:const char*:m:: PySequence_Fast_GET_ITEM:PyObject*::0: PySequence_Fast_GET_ITEM:PyObject*:o:0: -PySequence_Fast_GET_ITEM:int:i:: +PySequence_Fast_GET_ITEM:Py_ssize_t:i:: + +PySequence_Fast_GET_SIZE:Py_ssize_t::: +PySequence_Fast_GET_SIZE:PyObject*:o:0: + +PySequence_Fast_ITEMS:PyObject**::: +PySequence_Fast_ITEMS:PyObject*:o:0: PySequence_GetItem:PyObject*::+1: PySequence_GetItem:PyObject*:o:0: -PySequence_GetItem:int:i:: +PySequence_GetItem:Py_ssize_t:i:: PySequence_GetSlice:PyObject*::+1: PySequence_GetSlice:PyObject*:o:0: -PySequence_GetSlice:int:i1:: -PySequence_GetSlice:int:i2:: +PySequence_GetSlice:Py_ssize_t:i1:: +PySequence_GetSlice:Py_ssize_t:i2:: PySequence_In:int::: PySequence_In:PyObject*:o:0: PySequence_In:PyObject*:value:0: -PySequence_Index:int::: +PySequence_Index:Py_ssize_t::: PySequence_Index:PyObject*:o:0: PySequence_Index:PyObject*:value:0: @@ -1291,22 +1976,25 @@ PySequence_InPlaceRepeat:PyObject*:o:0: PySequence_ITEM:PyObject*::+1: PySequence_ITEM:PyObject*:o:0: -PySequence_ITEM:int:i:: +PySequence_ITEM:Py_ssize_t:i:: PySequence_Repeat:PyObject*::+1: PySequence_Repeat:PyObject*:o:0: -PySequence_Repeat:int:count:: +PySequence_Repeat:Py_ssize_t:count:: PySequence_SetItem:int::: PySequence_SetItem:PyObject*:o:0: -PySequence_SetItem:int:i:: +PySequence_SetItem:Py_ssize_t:i:: PySequence_SetItem:PyObject*:v:+1: PySequence_SetSlice:int::: PySequence_SetSlice:PyObject*:o:0: -PySequence_SetSlice:int:i1:: -PySequence_SetSlice:int:i2:: -PySequence_SetSlice:PyObject*:v:+1: +PySequence_SetSlice:Py_ssize_t:i1:: +PySequence_SetSlice:Py_ssize_t:i2:: +PySequence_SetSlice:PyObject*:v:0: + +PySequence_Size:Py_ssize_t::: +PySequence_Size:PyObject*:o:0: PySequence_List:PyObject*::+1: PySequence_List:PyObject*:o:0: @@ -1314,9 +2002,15 @@ PySequence_List:PyObject*:o:0: PySequence_Tuple:PyObject*::+1: PySequence_Tuple:PyObject*:o:0: -PySet_Append:int::: -PySet_Append:PyObject*:set:0: -PySet_Append:PyObject*:key:+1: +PySet_Add:int::: +PySet_Add:PyObject*:set:0: +PySet_Add:PyObject*:key:+1: + +PySet_Check:int::: +PySet_Check:PyObject*:p:0: + +PySet_Clear:int::: +PySet_Clear:PyObject*:set:0: PySet_Contains:int::: PySet_Contains:PyObject*:anyset:0: @@ -1326,15 +2020,21 @@ PySet_Discard:int::: PySet_Discard:PyObject*:set:0: PySet_Discard:PyObject*:key:-1:no effect if key not found +PySet_GET_SIZE:Py_ssize_t::: +PySet_GET_SIZE:PyObject*:anyset:0: + PySet_New:PyObject*::+1: PySet_New:PyObject*:iterable:0: PySet_Pop:PyObject*::+1:or returns NULL and raises KeyError if set is empty PySet_Pop:PyObject*:set:0: -PySet_Size:int::: +PySet_Size:Py_ssize_t::: PySet_Size:PyObject*:anyset:0: +PySignal_SetWakeupFd:int::: +PySignal_SetWakeupFd:int:fd:: + PySlice_AdjustIndices:Py_ssize_t::: PySlice_AdjustIndices:Py_ssize_t:length:: PySlice_AdjustIndices:Py_ssize_t*:start:: @@ -1344,6 +2044,21 @@ PySlice_AdjustIndices:Py_ssize_t*:step:: PySlice_Check:int::: PySlice_Check:PyObject*:ob:0: +PySlice_GetIndices:int::: +PySlice_GetIndices:PyObject*:slice:0: +PySlice_GetIndices:Py_ssize_t:length:: +PySlice_GetIndices:Py_ssize_t*:start:: +PySlice_GetIndices:Py_ssize_t*:stop:: +PySlice_GetIndices:Py_ssize_t*:step:: + +PySlice_GetIndicesEx:int::: +PySlice_GetIndicesEx:PyObject*:slice:0: +PySlice_GetIndicesEx:Py_ssize_t:length:: +PySlice_GetIndicesEx:Py_ssize_t*:start:: +PySlice_GetIndicesEx:Py_ssize_t*:stop:: +PySlice_GetIndicesEx:Py_ssize_t*:step:: +PySlice_GetIndicesEx:Py_ssize_t*:slicelength:: + PySlice_New:PyObject*::+1: PySlice_New:PyObject*:start:0: PySlice_New:PyObject*:stop:0: @@ -1355,100 +2070,78 @@ PySlice_Unpack:Py_ssize_t*:start:: PySlice_Unpack:Py_ssize_t*:stop:: PySlice_Unpack:Py_ssize_t*:step:: -PyString_AS_STRING:const char*::: -PyString_AS_STRING:PyObject*:string:0: - -PyString_AsDecodedObject:PyObject*::+1: -PyString_AsDecodedObject:PyObject*:str:0: -PyString_AsDecodedObject:const char*:encoding:: -PyString_AsDecodedObject:const char*:errors:: +PyState_AddModule:int::: +PyState_AddModule:PyObject*:module:+1: +PyState_AddModule:PyModuleDef*:def:: -PyString_AsEncodedObject:PyObject*::+1: -PyString_AsEncodedObject:PyObject*:str:0: -PyString_AsEncodedObject:const char*:encoding:: -PyString_AsEncodedObject:const char*:errors:: +PyState_FindModule:PyObject*::0: +PyState_FindModule:PyModuleDef*:def:: -PyString_AsString:const char*::: -PyString_AsString:PyObject*:string:0: +PyState_RemoveModule:int::: +PyState_RemoveModule:PyModuleDef*:def:: -PyString_AsStringAndSize:int::: -PyString_AsStringAndSize:PyObject*:obj:0: -PyString_AsStringAndSize:char**:buffer:: -PyString_AsStringAndSize:int*:length:: +PyStructSequence_GET_ITEM:PyObject*::0: +PyStructSequence_GET_ITEM:PyObject*:p:0: +PyStructSequence_GET_ITEM:Py_ssize_t:pos:: -PyString_Check:int::: -PyString_Check:PyObject*:o:0: +PyStructSequence_GetItem:PyObject*::0: +PyStructSequence_GetItem:PyObject*:p:0: +PyStructSequence_GetItem:Py_ssize_t:pos:: -PyString_Concat:void::: -PyString_Concat:PyObject**:string:0:??? -- replaces w/ new string or NULL -PyString_Concat:PyObject*:newpart:0: +PyStructSequence_InitType:void::: +PyStructSequence_InitType:PyTypeObject*:type:+1: +PyStructSequence_InitType:PyStructSequence_Desc*:desc:: -PyString_ConcatAndDel:void::: -PyString_ConcatAndDel:PyObject**:string:0:??? -- replaces w/ new string or NULL -PyString_ConcatAndDel:PyObject*:newpart:-1: +PyStructSequence_InitType2:int::: +PyStructSequence_InitType2:PyTypeObject*:type:+1: +PyStructSequence_InitType2:PyStructSequence_Desc*:desc:: -PyString_Format:PyObject*::+1: -PyString_Format:PyObject*:format:0: -PyString_Format:PyObject*:args:0: +PyStructSequence_New:PyObject*::+1: +PyStructSequence_New:PyTypeObject*:type:0: -PyString_FromString:PyObject*::+1: -PyString_FromString:const char*:v:: +PyStructSequence_NewType:PyTypeObject*::+1: +PyStructSequence_NewType:PyStructSequence_Desc*:desc:: -PyString_FromStringAndSize:PyObject*::+1: -PyString_FromStringAndSize:const char*:v:: -PyString_FromStringAndSize:int:len:: +PyStructSequence_SET_ITEM:void::: +PyStructSequence_SET_ITEM:PyObject*:p:0: +PyStructSequence_SET_ITEM:Py_ssize_t*:pos:: +PyStructSequence_SET_ITEM:PyObject*:o:0: -PyString_FromFormat:PyObject*::+1: -PyString_FromFormat:const char*:format:: -PyString_FromFormat::...:: - -PyString_FromFormatV:PyObject*::+1: -PyString_FromFormatV:const char*:format:: -PyString_FromFormatV:va_list:vargs:: - -PyString_GET_SIZE:int::: -PyString_GET_SIZE:PyObject*:string:0: - -PyString_InternFromString:PyObject*::+1: -PyString_InternFromString:const char*:v:: - -PyString_InternInPlace:void::: -PyString_InternInPlace:PyObject**:string:+1:??? - -PyString_Size:int::: -PyString_Size:PyObject*:string:0: - -PyString_Decode:PyObject*::+1: -PyString_Decode:const char*:s:: -PyString_Decode:int:size:: -PyString_Decode:const char*:encoding:: -PyString_Decode:const char*:errors:: - -PyString_Encode:PyObject*::+1: -PyString_Encode:const char*:s:: -PyString_Encode:int:size:: -PyString_Encode:const char*:encoding:: -PyString_Encode:const char*:errors:: - -PyString_AsEncodedString:PyObject*::+1: -PyString_AsEncodedString:PyObject*:str:: -PyString_AsEncodedString:const char*:encoding:: -PyString_AsEncodedString:const char*:errors:: +PyStructSequence_SetItem:void::: +PyStructSequence_SetItem:PyObject*:p:0: +PyStructSequence_SetItem:Py_ssize_t:pos:: +PyStructSequence_SetItem:PyObject*:o:0: PySys_AddWarnOption:void::: -PySys_AddWarnOption:const char*:s:: +PySys_AddWarnOption:const wchar_t*:s:: + +PySys_AddWarnOptionUnicode:void::: +PySys_AddWarnOptionUnicode:PyObject*:unicode:0: PySys_AddXOption:void::: PySys_AddXOption:const wchar_t*:s:: +PySys_FormatStderr:void::: +PySys_FormatStderr:const char*:format:: +PySys_FormatStderr::...:: + +PySys_FormatStdout:void::: +PySys_FormatStdout:const char*:format:: +PySys_FormatStdout::...:: + PySys_GetObject:PyObject*::0: PySys_GetObject:const char*:name:: PySys_GetXOptions:PyObject*::0: -PySys_SetArgv:int::: +PySys_SetArgv:void::: PySys_SetArgv:int:argc:: -PySys_SetArgv:char**:argv:: +PySys_SetArgv:wchar_t**:argv:: + +PySys_SetArgvEx:void::: +PySys_SetArgvEx:int:argc:: +PySys_SetArgvEx:wchar_t**:argv:: +PySys_SetArgvEx:int:updatepath:: PySys_SetObject:int::: PySys_SetObject:const char*:name:: @@ -1458,9 +2151,11 @@ PySys_ResetWarnOptions:void::: PySys_WriteStdout:void::: PySys_WriteStdout:const char*:format:: +PySys_WriteStdout::...:: PySys_WriteStderr:void::: PySys_WriteStderr:const char*:format:: +PySys_WriteStderr::...:: PyThreadState_Clear:void::: PyThreadState_Clear:PyThreadState*:tstate:: @@ -1475,6 +2170,10 @@ PyThreadState_GetDict:PyObject*::0: PyThreadState_New:PyThreadState*::: PyThreadState_New:PyInterpreterState*:interp:: +PyThreadState_SetAsyncExc:int::: +PyThreadState_SetAsyncExc:unsigned long:id:: +PyThreadState_SetAsyncExc:PyObject*:exc:+1: + PyThreadState_Swap:PyThreadState*::: PyThreadState_Swap:PyThreadState*:tstate:: @@ -1499,6 +2198,12 @@ PyThread_tss_set:int::: PyThread_tss_set:Py_tss_t*:key:: PyThread_tss_set:void*:value:: +PyTime_Check:int::: +PyTime_Check:PyObject*:ob:0: + +PyTime_CheckExact:int::: +PyTime_CheckExact:PyObject*:ob:0: + PyTime_FromTime:PyObject*::+1: PyTime_FromTime:int:hour:: PyTime_FromTime:int:minute:: @@ -1517,63 +2222,130 @@ PyTraceMalloc_Untrack:uintptr_t:ptr:: PyTuple_Check:int::: PyTuple_Check:PyObject*:p:0: +PyTuple_CheckExact:int::: +PyTuple_CheckExact:PyObject*:p:0: + PyTuple_GET_ITEM:PyObject*::0: -PyTuple_GET_ITEM:PyTupleObject*:p:0: -PyTuple_GET_ITEM:int:pos:: +PyTuple_GET_ITEM:PyObject*:p:0: +PyTuple_GET_ITEM:Py_ssize_t:pos:: PyTuple_GetItem:PyObject*::0: -PyTuple_GetItem:PyTupleObject*:p:0: -PyTuple_GetItem:int:pos:: +PyTuple_GetItem:PyObject*:p:0: +PyTuple_GetItem:Py_ssize_t:pos:: + +PyTuple_GET_SIZE:Py_ssize_t::: +PyTuple_GET_SIZE:PyObject*:p:0: PyTuple_GetSlice:PyObject*::+1: -PyTuple_GetSlice:PyTupleObject*:p:0: -PyTuple_GetSlice:int:low:: -PyTuple_GetSlice:int:high:: +PyTuple_GetSlice:PyObject*:p:0: +PyTuple_GetSlice:Py_ssize_t:low:: +PyTuple_GetSlice:Py_ssize_t:high:: PyTuple_New:PyObject*::+1: -PyTuple_New:int:len:: +PyTuple_New:Py_ssize_t:len:: PyTuple_Pack:PyObject*::+1: -PyTuple_Pack:int:len:: +PyTuple_Pack:Py_ssize_t:len:: PyTuple_Pack:PyObject*:...:0: PyTuple_SET_ITEM:void::: -PyTuple_SET_ITEM:PyTupleObject*:p:0: -PyTuple_SET_ITEM:int:pos:: +PyTuple_SET_ITEM:PyObject*:p:0: +PyTuple_SET_ITEM:Py_ssize_t:pos:: PyTuple_SET_ITEM:PyObject*:o:0: PyTuple_SetItem:int::: -PyTuple_SetItem:PyTupleObject*:p:0: -PyTuple_SetItem:int:pos:: +PyTuple_SetItem:PyObject*:p:0: +PyTuple_SetItem:Py_ssize_t:pos:: PyTuple_SetItem:PyObject*:o:0: -PyTuple_Size:int::: -PyTuple_Size:PyTupleObject*:p:0: +PyTuple_Size:Py_ssize_t::: +PyTuple_Size:PyObject*:p:0: + +PyType_Check:int::: +PyType_Check:PyObject*:o:0: + +PyType_CheckExact:int::: +PyType_CheckExact:PyObject*:o:0: + +PyType_FromSpec:PyObject*::+1: +PyType_FromSpec:PyType_Spec*:spec:: + +PyType_FromSpecWithBases:PyObject*::+1: +PyType_FromSpecWithBases:PyType_Spec*:spec:: +PyType_FromSpecWithBases:PyObject*:bases:0: PyType_GenericAlloc:PyObject*::+1: PyType_GenericAlloc:PyObject*:type:0: -PyType_GenericAlloc:int:nitems:0: +PyType_GenericAlloc:Py_ssize_t:nitems:: PyType_GenericNew:PyObject*::+1: PyType_GenericNew:PyObject*:type:0: PyType_GenericNew:PyObject*:args:0: PyType_GenericNew:PyObject*:kwds:0: +PyType_GetFlags:unsigned long::: +PyType_GetFlags:PyTypeObject*:type:0: + +PyType_GetSlot:void*::: +PyType_GetSlot:PyTypeObject*:type:0: +PyType_GetSlot:int:slot:: + +PyType_HasFeature:int::: +PyType_HasFeature:PyTypeObject*:o:0: +PyType_HasFeature:int:feature:: + +PyType_IS_GC:int::: +PyType_IS_GC:PyTypeObject*:o:0: + +PyType_IsSubtype:int::: +PyType_IsSubtype:PyTypeObject*:a:0: +PyType_IsSubtype:PyTypeObject*:b:0: + +PyType_Modified:void::: +PyType_Modified:PyTypeObject*:type:0: + +PyType_Ready:int::: +PyType_Ready:PyTypeObject*:type:0: + +PyUnicode_1BYTE_DATA:Py_UCS1*::: +PyUnicode_1BYTE_DATA:PyObject*:o:0: + PyUnicode_Check:int::: PyUnicode_Check:PyObject*:o:0: -PyUnicode_GET_SIZE:int::: +PyUnicode_CheckExact:int::: +PyUnicode_CheckExact:PyObject*:o:0: + +PyUnicode_DATA:void*::: +PyUnicode_DATA:PyObject*:o:0: + +PyUnicode_GET_LENGTH:Py_ssize_t::: +PyUnicode_GET_LENGTH:PyObject*:o:0: + +PyUnicode_GET_SIZE:Py_ssize_t::: PyUnicode_GET_SIZE:PyObject*:o:0: -PyUnicode_GET_DATA_SIZE:int::: +PyUnicode_GET_DATA_SIZE:Py_ssize_t::: PyUnicode_GET_DATA_SIZE:PyObject*:o:0: +PyUnicode_KIND:int::: +PyUnicode_KIND:PyObject*:o:0: + +PyUnicode_MAX_CHAR_VALUE:::: +PyUnicode_MAX_CHAR_VALUE:PyObject*:o:0: + PyUnicode_AS_UNICODE:Py_UNICODE*::: PyUnicode_AS_UNICODE:PyObject*:o:0: PyUnicode_AS_DATA:const char*::: PyUnicode_AS_DATA:PyObject*:o:0: +Py_UNICODE_ISALNUM:int::: +Py_UNICODE_ISALNUM:Py_UNICODE:ch:: + +Py_UNICODE_ISALPHA:int::: +Py_UNICODE_ISALPHA:Py_UNICODE:ch:: + Py_UNICODE_ISSPACE:int::: Py_UNICODE_ISSPACE:Py_UNICODE:ch:: @@ -1598,6 +2370,9 @@ Py_UNICODE_ISDIGIT:Py_UNICODE:ch:: Py_UNICODE_ISNUMERIC:int::: Py_UNICODE_ISNUMERIC:Py_UNICODE:ch:: +Py_UNICODE_ISPRINTABLE:int::: +Py_UNICODE_ISPRINTABLE:Py_UNICODE:ch:: + Py_UNICODE_TOLOWER:Py_UNICODE::: Py_UNICODE_TOLOWER:Py_UNICODE:ch:: @@ -1638,10 +2413,10 @@ PyUnicode_GetSize:Py_ssize_t::: PyUnicode_GetSize:PyObject*:unicode:0: PyUnicode_FromObject:PyObject*::+1: -PyUnicode_FromObject:PyObject*:*obj:0: +PyUnicode_FromObject:PyObject*:obj:0: PyUnicode_FromEncodedObject:PyObject*::+1: -PyUnicode_FromEncodedObject:PyObject*:*obj:0: +PyUnicode_FromEncodedObject:PyObject*:obj:0: PyUnicode_FromEncodedObject:const char*:encoding:: PyUnicode_FromEncodedObject:const char*:errors:: @@ -1684,7 +2459,7 @@ PyUnicode_Encode:const char*:encoding:: PyUnicode_Encode:const char*:errors:: PyUnicode_AsEncodedString:PyObject*::+1: -PyUnicode_AsEncodedString:PyObject*:unicode:: +PyUnicode_AsEncodedString:PyObject*:unicode:0: PyUnicode_AsEncodedString:const char*:encoding:: PyUnicode_AsEncodedString:const char*:errors:: @@ -1717,7 +2492,7 @@ PyUnicode_EncodeUTF8:Py_ssize_t:size:: PyUnicode_EncodeUTF8:const char*:errors:: PyUnicode_AsUTF8String:PyObject*::+1: -PyUnicode_AsUTF8String:PyObject*:unicode:: +PyUnicode_AsUTF8String:PyObject*:unicode:0: PyUnicode_AsUTF8AndSize:const char*::: PyUnicode_AsUTF8AndSize:PyObject*:unicode:0: @@ -1739,7 +2514,7 @@ PyUnicode_EncodeUTF16:const char*:errors:: PyUnicode_EncodeUTF16:int:byteorder:: PyUnicode_AsUTF16String:PyObject*::+1: -PyUnicode_AsUTF16String:PyObject*:unicode:: +PyUnicode_AsUTF16String:PyObject*:unicode:0: PyUnicode_DecodeUTF32:PyObject*::+1: PyUnicode_DecodeUTF32:const char*:s:: @@ -1773,7 +2548,7 @@ PyUnicode_EncodeUnicodeEscape:const Py_UNICODE*:s:: PyUnicode_EncodeUnicodeEscape:Py_ssize_t:size:: PyUnicode_AsUnicodeEscapeString:PyObject*::+1: -PyUnicode_AsUnicodeEscapeString:PyObject*:unicode:: +PyUnicode_AsUnicodeEscapeString:PyObject*:unicode:0: PyUnicode_DecodeRawUnicodeEscape:PyObject*::+1: PyUnicode_DecodeRawUnicodeEscape:const char*:s:: @@ -1785,7 +2560,7 @@ PyUnicode_EncodeRawUnicodeEscape:const Py_UNICODE*:s:: PyUnicode_EncodeRawUnicodeEscape:Py_ssize_t:size:: PyUnicode_AsRawUnicodeEscapeString:PyObject*::+1: -PyUnicode_AsRawUnicodeEscapeString:PyObject*:unicode:: +PyUnicode_AsRawUnicodeEscapeString:PyObject*:unicode:0: PyUnicode_DecodeLatin1:PyObject*::+1: PyUnicode_DecodeLatin1:const char*:s:: @@ -1798,7 +2573,7 @@ PyUnicode_EncodeLatin1:Py_ssize_t:size:: PyUnicode_EncodeLatin1:const char*:errors:: PyUnicode_AsLatin1String:PyObject*::+1: -PyUnicode_AsLatin1String:PyObject*:unicode:: +PyUnicode_AsLatin1String:PyObject*:unicode:0: PyUnicode_DecodeASCII:PyObject*::+1: PyUnicode_DecodeASCII:const char*:s:: @@ -1811,7 +2586,7 @@ PyUnicode_EncodeASCII:Py_ssize_t:size:: PyUnicode_EncodeASCII:const char*:errors:: PyUnicode_AsASCIIString:PyObject*::+1: -PyUnicode_AsASCIIString:PyObject*:unicode:: +PyUnicode_AsASCIIString:PyObject*:unicode:0: PyUnicode_DecodeCharmap:PyObject*::+1: PyUnicode_DecodeCharmap:const char*:s:: @@ -1832,7 +2607,7 @@ PyUnicode_AsCharmapString:PyObject*:mapping:0: PyUnicode_TranslateCharmap:PyObject*::+1: PyUnicode_TranslateCharmap:const Py_UNICODE*:s:: PyUnicode_TranslateCharmap:Py_ssize_t:size:: -PyUnicode_TranslateCharmap:PyObject*:table:0: +PyUnicode_TranslateCharmap:PyObject*:mapping:0: PyUnicode_TranslateCharmap:const char*:errors:: PyUnicode_DecodeMBCS:PyObject*::+1: @@ -1840,7 +2615,7 @@ PyUnicode_DecodeMBCS:const char*:s:: PyUnicode_DecodeMBCS:Py_ssize_t:size:: PyUnicode_DecodeMBCS:const char*:errors:: -PyUnicode_DecodeMBCSStateful:PyObject::+1: +PyUnicode_DecodeMBCSStateful:PyObject*::+1: PyUnicode_DecodeMBCSStateful:const char*:s:: PyUnicode_DecodeMBCSStateful:Py_ssize_t:size:: PyUnicode_DecodeMBCSStateful:const char*:errors:: @@ -1857,7 +2632,7 @@ PyUnicode_EncodeMBCS:Py_ssize_t:size:: PyUnicode_EncodeMBCS:const char*:errors:: PyUnicode_AsMBCSString:PyObject*::+1: -PyUnicode_AsMBCSString:PyObject*:unicode:: +PyUnicode_AsMBCSString:PyObject*:unicode:0: PyUnicode_Concat:PyObject*::+1: PyUnicode_Concat:PyObject*:left:0: @@ -1922,7 +2697,7 @@ PyUnicode_CompareWithASCIIString:int::: PyUnicode_CompareWithASCIIString:PyObject*:uni:0: PyUnicode_CompareWithASCIIString:const char*:string:: -PyUnicode_RichCompare:PyObject::+1: +PyUnicode_RichCompare:PyObject*::+1: PyUnicode_RichCompare:PyObject*:left:0: PyUnicode_RichCompare:PyObject*:right:0: PyUnicode_RichCompare:int:op:: @@ -1939,7 +2714,7 @@ PyUnicode_InternInPlace:void::: PyUnicode_InternInPlace:PyObject**:string:+1: PyUnicode_InternFromString:PyObject*::+1: -PyUnicode_InternFromString:const char*:cp:: +PyUnicode_InternFromString:const char*:v:: PyUnicode_New:PyObject*::+1: PyUnicode_New:Py_ssize_t:size:: @@ -1977,19 +2752,37 @@ PyUnicode_CopyCharacters:Py_ssize_t:how_many:: PyUnicode_Fill:Py_ssize_t::: PyUnicode_Fill:PyObject*:unicode:0: -PyUnicode_Fill:Py_ssize_t:index:: +PyUnicode_Fill:Py_ssize_t:start:: PyUnicode_Fill:Py_ssize_t:length:: PyUnicode_Fill:Py_UCS4:fill_char:: +PyUnicode_READ:Py_UCS4::: +PyUnicode_READ:int:kind:: +PyUnicode_READ:void*:data:: +PyUnicode_READ:Py_ssize_t:index:: + +PyUnicode_READ_CHAR:Py_UCS4::: +PyUnicode_READ_CHAR:PyObject*:o:0: +PyUnicode_READ_CHAR:Py_ssize_t:index:: + PyUnicode_ReadChar:Py_UCS4::: PyUnicode_ReadChar:PyObject*:unicode:0: PyUnicode_ReadChar:Py_ssize_t:index:: +PyUnicode_WRITE:void::: +PyUnicode_WRITE:int:kind:: +PyUnicode_WRITE:void*:data:: +PyUnicode_WRITE:Py_ssize_t:index:: +PyUnicode_WRITE:Py_UCS4:value:: + PyUnicode_WriteChar:int::: PyUnicode_WriteChar:PyObject*:unicode:0: PyUnicode_WriteChar:Py_ssize_t:index:: PyUnicode_WriteChar:Py_UCS4:character:: +PyUnicode_READY:int::: +PyUnicode_READY:PyObject*:o:0: + PyUnicode_Substring:PyObject*::+1: PyUnicode_Substring:PyObject*:str:0: PyUnicode_Substring:Py_ssize_t:start:: @@ -2029,11 +2822,72 @@ PyUnicode_DecodeFSDefaultAndSize:PyObject*::+1: PyUnicode_DecodeFSDefaultAndSize:const char*:s:: PyUnicode_DecodeFSDefaultAndSize:Py_ssize_t:size:: -PyUnicode_DecodeFSDefault:PyObject::+1: +PyUnicode_DecodeFSDefault:PyObject*::+1: PyUnicode_DecodeFSDefault:const char*:s:: -PyUnicode_EncodeFSDefault:PyObject::+1: -PyUnicode_EncodeFSDefault:PyObject:unicode:0: +PyUnicode_EncodeFSDefault:PyObject*::+1: +PyUnicode_EncodeFSDefault:PyObject*:unicode:0: + +PyUnicodeDecodeError_Create:PyObject*::+1: +PyUnicodeDecodeError_Create:const char*:encoding:: +PyUnicodeDecodeError_Create:const char*:object:: +PyUnicodeDecodeError_Create:Py_ssize_t:length:: +PyUnicodeDecodeError_Create:Py_ssize_t:start:: +PyUnicodeDecodeError_Create:Py_ssize_t:end:: +PyUnicodeDecodeError_Create:const char*:reason:: + +PyUnicodeDecodeError_GetEncoding:PyObject*::+1: +PyUnicodeDecodeError_GetEncoding:PyObject*:exc:0: + +PyUnicodeDecodeError_GetEnd:Py_ssize_t::: +PyUnicodeDecodeError_GetEnd:PyObject*:exc:0: +PyUnicodeDecodeError_GetEnd:Py_ssize_t*:end:: + +PyUnicodeDecodeError_GetObject:PyObject*::+1: +PyUnicodeDecodeError_GetObject:PyObject*:exc:0: + +PyUnicodeDecodeError_GetReason:PyObject*::+1: +PyUnicodeDecodeError_GetReason:PyObject*:exc:0: + +PyUnicodeDecodeError_GetStart:Py_ssize_t::: +PyUnicodeDecodeError_GetStart:PyObject*:exc:0: +PyUnicodeDecodeError_GetStart:Py_ssize_t*:start:: + +PyUnicodeDecodeError_SetEnd:int::: +PyUnicodeDecodeError_SetEnd:PyObject*:exc:0: +PyUnicodeDecodeError_SetEnd:Py_ssize_t:end:: + +PyUnicodeDecodeError_SetReason:int::: +PyUnicodeDecodeError_SetReason:PyObject*:exc:0: +PyUnicodeDecodeError_SetReason:const char*:reason:: + +PyUnicodeDecodeError_SetStart:int::: +PyUnicodeDecodeError_SetStart:PyObject*:exc:0: +PyUnicodeDecodeError_SetStart:Py_ssize_t:start:: + +PyUnicodeEncodeError_Create:PyObject*::+1: +PyUnicodeEncodeError_Create:const char*:encoding:: +PyUnicodeEncodeError_Create:const Py_UNICODE*:object:: +PyUnicodeEncodeError_Create:Py_ssize_t:length:: +PyUnicodeEncodeError_Create:Py_ssize_t:start:: +PyUnicodeEncodeError_Create:Py_ssize_t:end:: +PyUnicodeEncodeError_Create:const char*:reason:: + +PyUnicodeTranslateError_Create:PyObject*::+1: +PyUnicodeTranslateError_Create:const Py_UNICODE*:object:: +PyUnicodeTranslateError_Create:Py_ssize_t:length:: +PyUnicodeTranslateError_Create:Py_ssize_t:start:: +PyUnicodeTranslateError_Create:Py_ssize_t:end:: +PyUnicodeTranslateError_Create:const char*:reason:: + +PyWeakref_Check:int::: +PyWeakref_Check:PyObject*:ob:: + +PyWeakref_CheckProxy:int::: +PyWeakref_CheckProxy:PyObject*:ob:: + +PyWeakref_CheckRef:int::: +PyWeakref_CheckRef:PyObject*:ob:: PyWeakref_GET_OBJECT:PyObject*::0: PyWeakref_GET_OBJECT:PyObject*:ref:0: @@ -2058,18 +2912,40 @@ Py_AtExit:void (*)():func:: Py_BuildValue:PyObject*::+1: Py_BuildValue:const char*:format:: +Py_BuildValue::...:: + +Py_VaBuildValue:PyObject*::+1: +Py_VaBuildValue:const char*:format:: +Py_VaBuildValue:va_list:vargs:: + +Py_CLEAR:void::: +Py_CLEAR:PyObject*:o:-1: Py_CompileString:PyObject*::+1: Py_CompileString:const char*:str:: Py_CompileString:const char*:filename:: Py_CompileString:int:start:: +Py_CompileStringExFlags:PyObject*::+1: +Py_CompileStringExFlags:const char*:str:: +Py_CompileStringExFlags:const char*:filename:: +Py_CompileStringExFlags:int:start:: +Py_CompileStringExFlags:PyCompilerFlags*:flags:: +Py_CompileStringExFlags:int:optimize:: + Py_CompileStringFlags:PyObject*::+1: Py_CompileStringFlags:const char*:str:: Py_CompileStringFlags:const char*:filename:: Py_CompileStringFlags:int:start:: Py_CompileStringFlags:PyCompilerFlags*:flags:: +Py_CompileStringObject:PyObject*::+1: +Py_CompileStringObject:const char*:str:: +Py_CompileStringObject:PyObject*:filename:0: +Py_CompileStringObject:int:start:: +Py_CompileStringObject:PyCompilerFlags*:flags:: +Py_CompileStringObject:int:optimize:: + Py_DECREF:void::: Py_DECREF:PyObject*:o:-1: @@ -2088,25 +2964,25 @@ Py_FdIsInteractive:const char*:filename:: Py_Finalize:void::: -Py_GetBuildInfoconst:const char*::: +Py_GetBuildInfo:const char*::: -Py_GetCompilerconst:const char*::: +Py_GetCompiler:const char*::: -Py_GetCopyrightconst:const char*::: +Py_GetCopyright:const char*::: -Py_GetExecPrefix:const char*::: +Py_GetExecPrefix:wchar_t*::: -Py_GetPath:const char*::: +Py_GetPath:wchar_t*::: -Py_GetPlatformconst:const char*::: +Py_GetPlatform:const char*::: -Py_GetPrefix:const char*::: +Py_GetPrefix:wchar_t*::: -Py_GetProgramFullPath:const char*::: +Py_GetProgramFullPath:wchar_t*::: -Py_GetProgramName:const char*::: +Py_GetProgramName:wchar_t*::: -Py_GetVersionconst:const char*::: +Py_GetVersion:const char*::: Py_INCREF:void::: Py_INCREF:PyObject*:o:+1: @@ -2117,8 +2993,14 @@ Py_IsInitialized:int::: Py_NewInterpreter:PyThreadState*::: +Py_ReprEnter:int::: +Py_ReprEnter:PyObject*:object:+1: + +Py_ReprLeave:void::: +Py_ReprLeave:PyObject*:object:-1: + Py_SetProgramName:void::: -Py_SetProgramName:const char*:name:: +Py_SetProgramName:const wchar_t*:name:: Py_XDECREF:void::: Py_XDECREF:PyObject*:o:-1:if o is not NULL @@ -2126,32 +3008,24 @@ Py_XDECREF:PyObject*:o:-1:if o is not NULL Py_XINCREF:void::: Py_XINCREF:PyObject*:o:+1:if o is not NULL -_PyImport_FindExtension:PyObject*::0:??? see PyImport_AddModule -_PyImport_FindExtension:const char*::: -_PyImport_FindExtension:const char*::: - _PyImport_Fini:void::: -_PyImport_FixupExtension:PyObject*:::??? -_PyImport_FixupExtension:const char*::: -_PyImport_FixupExtension:const char*::: - _PyImport_Init:void::: _PyObject_New:PyObject*::+1: _PyObject_New:PyTypeObject*:type:0: -_PyObject_NewVar:PyObject*::+1: +_PyObject_NewVar:PyVarObject*::+1: _PyObject_NewVar:PyTypeObject*:type:0: -_PyObject_NewVar:int:size:: +_PyObject_NewVar:Py_ssize_t:size:: -_PyString_Resize:int::: -_PyString_Resize:PyObject**:string:+1: -_PyString_Resize:int:newsize:: +_PyBytes_Resize:int::: +_PyBytes_Resize:PyObject**:bytes:0: +_PyBytes_Resize:Py_ssize_t:newsize:: _PyTuple_Resize:int::: -_PyTuple_Resize:PyTupleObject**:p:+1: -_PyTuple_Resize:int:new:: +_PyTuple_Resize:PyObject**:p:0: +_PyTuple_Resize:Py_ssize_t:new:: _Py_c_diff:Py_complex::: _Py_c_diff:Py_complex:left:: diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index baa39f3b4464..fa8244a8fd31 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -87,7 +87,7 @@ def add_annotations(self, app, doctree): entry = self.get(name) if not entry: continue - elif entry.result_type not in ("PyObject*", "PyVarObject*"): + elif not entry.result_type.endswith("Object*"): continue if entry.result_refs is None: rc = 'Return value: Always NULL.' From webhook-mailer at python.org Thu Dec 20 02:34:55 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 20 Dec 2018 07:34:55 -0000 Subject: [Python-checkins] bpo-5438: Update memory requirements and optimize test_bigmem.py. (GH-11123) Message-ID: https://github.com/python/cpython/commit/b13a20f50789e153c18ed8efb4fbc5eecc50f2cd commit: b13a20f50789e153c18ed8efb4fbc5eecc50f2cd branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-20T09:34:51+02:00 summary: bpo-5438: Update memory requirements and optimize test_bigmem.py. (GH-11123) files: M Lib/test/test_bigmem.py diff --git a/Lib/test/test_bigmem.py b/Lib/test/test_bigmem.py index 6133bbcac52a..6a244dd8c948 100644 --- a/Lib/test/test_bigmem.py +++ b/Lib/test/test_bigmem.py @@ -64,6 +64,7 @@ ascii_char_size = 1 ucs2_char_size = 2 ucs4_char_size = 4 +pointer_size = 4 if sys.maxsize < 2**32 else 8 class BaseStrTest: @@ -372,7 +373,7 @@ def test_split_small(self, size): # suffer for the list size. (Otherwise, it'd cost another 48 times # size in bytes!) Nevertheless, a list of size takes # 8*size bytes. - @bigmemtest(size=_2G + 5, memuse=2 * ascii_char_size + 8) + @bigmemtest(size=_2G + 5, memuse=ascii_char_size * 2 + pointer_size) def test_split_large(self, size): _ = self.from_latin1 s = _(' a') * size + _(' ') @@ -604,15 +605,15 @@ def tearDown(self): for name, memuse in self._adjusted.items(): getattr(type(self), name).memuse = memuse - @bigmemtest(size=_2G, memuse=ucs4_char_size * 3) + @bigmemtest(size=_2G, memuse=ucs4_char_size * 3 + ascii_char_size * 2) def test_capitalize(self, size): self._test_capitalize(size) - @bigmemtest(size=_2G, memuse=ucs4_char_size * 3) + @bigmemtest(size=_2G, memuse=ucs4_char_size * 3 + ascii_char_size * 2) def test_title(self, size): self._test_title(size) - @bigmemtest(size=_2G, memuse=ucs4_char_size * 3) + @bigmemtest(size=_2G, memuse=ucs4_char_size * 3 + ascii_char_size * 2) def test_swapcase(self, size): self._test_swapcase(size) @@ -630,7 +631,7 @@ def test_encode_raw_unicode_escape(self, size): except MemoryError: pass # acceptable on 32-bit - @bigmemtest(size=_4G // 5 + 70, memuse=ascii_char_size + ucs4_char_size + 1) + @bigmemtest(size=_4G // 5 + 70, memuse=ascii_char_size + 8 + 1) def test_encode_utf7(self, size): try: return self.basic_encode_test(size, 'utf7') @@ -820,7 +821,7 @@ class TupleTest(unittest.TestCase): # having more than 2<<31 references to any given object. Hence the # use of different types of objects as contents in different tests. - @bigmemtest(size=_2G + 2, memuse=16) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2) def test_compare(self, size): t1 = ('',) * size t2 = ('',) * size @@ -843,15 +844,15 @@ def basic_concat_test(self, size): t = t + t self.assertEqual(len(t), size * 2) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 3) def test_concat_small(self, size): return self.basic_concat_test(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 3) def test_concat_large(self, size): return self.basic_concat_test(size) - @bigmemtest(size=_2G // 5 + 10, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 10, memuse=pointer_size * 5) def test_contains(self, size): t = (1, 2, 3, 4, 5) * size self.assertEqual(len(t), size * 5) @@ -859,7 +860,7 @@ def test_contains(self, size): self.assertFalse((1, 2, 3, 4, 5) in t) self.assertFalse(0 in t) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_hash(self, size): t1 = (0,) * size h1 = hash(t1) @@ -867,7 +868,7 @@ def test_hash(self, size): t2 = (0,) * (size + 1) self.assertFalse(h1 == hash(t2)) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_index_and_slice(self, size): t = (None,) * size self.assertEqual(len(t), size) @@ -892,11 +893,11 @@ def basic_test_repeat(self, size): t = t * 2 self.assertEqual(len(t), size * 2) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 3) def test_repeat_small(self, size): return self.basic_test_repeat(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 3) def test_repeat_large(self, size): return self.basic_test_repeat(size) @@ -904,48 +905,42 @@ def test_repeat_large(self, size): def test_repeat_large_2(self, size): return self.basic_test_repeat(size) - @bigmemtest(size=_1G - 1, memuse=9) + @bigmemtest(size=_1G - 1, memuse=pointer_size * 2) def test_from_2G_generator(self, size): - self.skipTest("test needs much more memory than advertised, see issue5438") try: - t = tuple(range(size)) + t = tuple(iter([42]*size)) except MemoryError: pass # acceptable on 32-bit else: - count = 0 - for item in t: - self.assertEqual(item, count) - count += 1 - self.assertEqual(count, size) + self.assertEqual(len(t), size) + self.assertEqual(t[:10], (42,) * 10) + self.assertEqual(t[-10:], (42,) * 10) - @bigmemtest(size=_1G - 25, memuse=9) + @bigmemtest(size=_1G - 25, memuse=pointer_size * 2) def test_from_almost_2G_generator(self, size): - self.skipTest("test needs much more memory than advertised, see issue5438") try: - t = tuple(range(size)) - count = 0 - for item in t: - self.assertEqual(item, count) - count += 1 - self.assertEqual(count, size) + t = tuple(iter([42]*size)) except MemoryError: - pass # acceptable, expected on 32-bit + pass # acceptable on 32-bit + else: + self.assertEqual(len(t), size) + self.assertEqual(t[:10], (42,) * 10) + self.assertEqual(t[-10:], (42,) * 10) # Like test_concat, split in two. def basic_test_repr(self, size): - t = (0,) * size + t = (False,) * size s = repr(t) - # The repr of a tuple of 0's is exactly three times the tuple length. - self.assertEqual(len(s), size * 3) - self.assertEqual(s[:5], '(0, 0') - self.assertEqual(s[-5:], '0, 0)') - self.assertEqual(s.count('0'), size) + # The repr of a tuple of Falses is exactly 7 times the tuple length. + self.assertEqual(len(s), size * 7) + self.assertEqual(s[:10], '(False, Fa') + self.assertEqual(s[-10:], 'se, False)') - @bigmemtest(size=_2G // 3 + 2, memuse=8 + 3 * ascii_char_size) + @bigmemtest(size=_2G // 7 + 2, memuse=pointer_size + ascii_char_size * 7) def test_repr_small(self, size): return self.basic_test_repr(size) - @bigmemtest(size=_2G + 2, memuse=8 + 3 * ascii_char_size) + @bigmemtest(size=_2G + 2, memuse=pointer_size + ascii_char_size * 7) def test_repr_large(self, size): return self.basic_test_repr(size) @@ -956,7 +951,7 @@ class ListTest(unittest.TestCase): # lists hold references to various objects to test their refcount # limits. - @bigmemtest(size=_2G + 2, memuse=16) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2) def test_compare(self, size): l1 = [''] * size l2 = [''] * size @@ -979,14 +974,16 @@ def basic_test_concat(self, size): l = l + l self.assertEqual(len(l), size * 2) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 3) def test_concat_small(self, size): return self.basic_test_concat(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 3) def test_concat_large(self, size): return self.basic_test_concat(size) + # XXX This tests suffers from overallocation, just like test_append. + # This should be fixed in future. def basic_test_inplace_concat(self, size): l = [sys.stdout] * size l += l @@ -994,15 +991,15 @@ def basic_test_inplace_concat(self, size): self.assertTrue(l[0] is l[-1]) self.assertTrue(l[size - 1] is l[size + 1]) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 2 * 9/8) def test_inplace_concat_small(self, size): return self.basic_test_inplace_concat(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2 * 9/8) def test_inplace_concat_large(self, size): return self.basic_test_inplace_concat(size) - @bigmemtest(size=_2G // 5 + 10, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 10, memuse=pointer_size * 5) def test_contains(self, size): l = [1, 2, 3, 4, 5] * size self.assertEqual(len(l), size * 5) @@ -1010,12 +1007,12 @@ def test_contains(self, size): self.assertFalse([1, 2, 3, 4, 5] in l) self.assertFalse(0 in l) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_hash(self, size): l = [0] * size self.assertRaises(TypeError, hash, l) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_index_and_slice(self, size): l = [None] * size self.assertEqual(len(l), size) @@ -1079,14 +1076,16 @@ def basic_test_repeat(self, size): l = l * 2 self.assertEqual(len(l), size * 2) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 3) def test_repeat_small(self, size): return self.basic_test_repeat(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 3) def test_repeat_large(self, size): return self.basic_test_repeat(size) + # XXX This tests suffers from overallocation, just like test_append. + # This should be fixed in future. def basic_test_inplace_repeat(self, size): l = [''] l *= size @@ -1099,34 +1098,34 @@ def basic_test_inplace_repeat(self, size): self.assertEqual(len(l), size * 2) self.assertTrue(l[size - 1] is l[-1]) - @bigmemtest(size=_2G // 2 + 2, memuse=16) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 2 * 9/8) def test_inplace_repeat_small(self, size): return self.basic_test_inplace_repeat(size) - @bigmemtest(size=_2G + 2, memuse=16) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2 * 9/8) def test_inplace_repeat_large(self, size): return self.basic_test_inplace_repeat(size) def basic_test_repr(self, size): - l = [0] * size + l = [False] * size s = repr(l) - # The repr of a list of 0's is exactly three times the list length. - self.assertEqual(len(s), size * 3) - self.assertEqual(s[:5], '[0, 0') - self.assertEqual(s[-5:], '0, 0]') - self.assertEqual(s.count('0'), size) + # The repr of a list of Falses is exactly 7 times the list length. + self.assertEqual(len(s), size * 7) + self.assertEqual(s[:10], '[False, Fa') + self.assertEqual(s[-10:], 'se, False]') + self.assertEqual(s.count('F'), size) - @bigmemtest(size=_2G // 3 + 2, memuse=8 + 3 * ascii_char_size) + @bigmemtest(size=_2G // 7 + 2, memuse=pointer_size + ascii_char_size * 7) def test_repr_small(self, size): return self.basic_test_repr(size) - @bigmemtest(size=_2G + 2, memuse=8 + 3 * ascii_char_size) + @bigmemtest(size=_2G + 2, memuse=pointer_size + ascii_char_size * 7) def test_repr_large(self, size): return self.basic_test_repr(size) # list overallocates ~1/8th of the total size (on first expansion) so # the single list.append call puts memuse at 9 bytes per size. - @bigmemtest(size=_2G, memuse=9) + @bigmemtest(size=_2G, memuse=pointer_size * 9/8) def test_append(self, size): l = [object()] * size l.append(object()) @@ -1134,12 +1133,14 @@ def test_append(self, size): self.assertTrue(l[-3] is l[-2]) self.assertFalse(l[-2] is l[-1]) - @bigmemtest(size=_2G // 5 + 2, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 2, memuse=pointer_size * 5) def test_count(self, size): l = [1, 2, 3, 4, 5] * size self.assertEqual(l.count(1), size) self.assertEqual(l.count("1"), 0) + # XXX This tests suffers from overallocation, just like test_append. + # This should be fixed in future. def basic_test_extend(self, size): l = [object] * size l.extend(l) @@ -1147,15 +1148,15 @@ def basic_test_extend(self, size): self.assertTrue(l[0] is l[-1]) self.assertTrue(l[size - 1] is l[size + 1]) - @bigmemtest(size=_2G // 2 + 2, memuse=16) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 2 * 9/8) def test_extend_small(self, size): return self.basic_test_extend(size) - @bigmemtest(size=_2G + 2, memuse=16) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2 * 9/8) def test_extend_large(self, size): return self.basic_test_extend(size) - @bigmemtest(size=_2G // 5 + 2, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 2, memuse=pointer_size * 5) def test_index(self, size): l = [1, 2, 3, 4, 5] * size size *= 5 @@ -1166,7 +1167,7 @@ def test_index(self, size): self.assertRaises(ValueError, l.index, 6) # This tests suffers from overallocation, just like test_append. - @bigmemtest(size=_2G + 10, memuse=9) + @bigmemtest(size=_2G + 10, memuse=pointer_size * 9/8) def test_insert(self, size): l = [1.0] * size l.insert(size - 1, "A") @@ -1185,7 +1186,7 @@ def test_insert(self, size): self.assertEqual(l[:3], [1.0, "C", 1.0]) self.assertEqual(l[size - 3:], ["A", 1.0, "B"]) - @bigmemtest(size=_2G // 5 + 4, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 4, memuse=pointer_size * 5) def test_pop(self, size): l = ["a", "b", "c", "d", "e"] * size size *= 5 @@ -1209,7 +1210,7 @@ def test_pop(self, size): self.assertEqual(item, "c") self.assertEqual(l[-2:], ["b", "d"]) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_remove(self, size): l = [10] * size self.assertEqual(len(l), size) @@ -1229,7 +1230,7 @@ def test_remove(self, size): self.assertEqual(len(l), size) self.assertEqual(l[-2:], [10, 10]) - @bigmemtest(size=_2G // 5 + 2, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 2, memuse=pointer_size * 5) def test_reverse(self, size): l = [1, 2, 3, 4, 5] * size l.reverse() @@ -1237,7 +1238,7 @@ def test_reverse(self, size): self.assertEqual(l[-5:], [5, 4, 3, 2, 1]) self.assertEqual(l[:5], [5, 4, 3, 2, 1]) - @bigmemtest(size=_2G // 5 + 2, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 2, memuse=pointer_size * 5 * 1.5) def test_sort(self, size): l = [1, 2, 3, 4, 5] * size l.sort() From webhook-mailer at python.org Thu Dec 20 02:43:31 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 20 Dec 2018 07:43:31 -0000 Subject: [Python-checkins] bpo-18085: Update refcounts.dat. (GH-11247) Message-ID: https://github.com/python/cpython/commit/73fc14d1f8441aef5ee03be627c63e74a6d915d6 commit: 73fc14d1f8441aef5ee03be627c63e74a6d915d6 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-19T23:43:22-08:00 summary: bpo-18085: Update refcounts.dat. (GH-11247) Fixed some errors in refcounts.dat, remove functions removed in Python 3, and add more entries for documented functions. This will add several automatically generated notes about return values. (cherry picked from commit 83dd4e87a62311cfea5fdd37e8a945b6b07bccee) Co-authored-by: Serhiy Storchaka files: M Doc/data/refcounts.dat M Doc/tools/extensions/c_annotations.py diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index add0600676f1..35527c179f34 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -31,29 +31,130 @@ # The parameter names are as they appear in the API manual, not the source # code. +PyAnySet_Check:int::: +PyAnySet_Check:PyObject*:p:0: + +PyAnySet_CheckExact:int::: +PyAnySet_CheckExact:PyObject*:p:0: + +PyBool_Check:int::: +PyBool_Check:PyObject*:o:0: + PyBool_FromLong:PyObject*::+1: -PyBool_FromLong:long:v:0: +PyBool_FromLong:long:v:: + +PyBuffer_FillContiguousStrides:void::: +PyBuffer_FillContiguousStrides:int:ndims:: +PyBuffer_FillContiguousStrides:Py_ssize_t*:shape:: +PyBuffer_FillContiguousStrides:Py_ssize_t*:strides:: +PyBuffer_FillContiguousStrides:int:itemsize:: +PyBuffer_FillContiguousStrides:char:order:: + +PyBuffer_FillInfo:int::: +PyBuffer_FillInfo:Py_buffer*:view:: +PyBuffer_FillInfo:PyObject*:exporter:0: +PyBuffer_FillInfo:void*:buf:: +PyBuffer_FillInfo:Py_ssize_t:len:: +PyBuffer_FillInfo:int:readonly:: +PyBuffer_FillInfo:int:flags:: + +PyBuffer_IsContiguous:int::: +PyBuffer_IsContiguous:Py_buffer*:view:: +PyBuffer_IsContiguous:char:order:: + +PyBuffer_Release:void::: +PyBuffer_Release:Py_buffer*:view:: + +PyBuffer_ToContiguous:int::: +PyBuffer_ToContiguous:void*:buf:: +PyBuffer_ToContiguous:Py_buffer*:src:: +PyBuffer_ToContiguous:Py_ssize_t:len:: +PyBuffer_ToContiguous:char:order:: + +PyByteArray_AS_STRING:char*::: +PyByteArray_AS_STRING:PyObject*:bytearray:0: + +PyByteArray_AsString:char*::: +PyByteArray_AsString:PyObject*:bytearray:0: + +PyByteArray_Check:int::: +PyByteArray_Check:PyObject*:o:0: + +PyByteArray_CheckExact:int::: +PyByteArray_CheckExact:PyObject*:o:0: + +PyByteArray_Concat:PyObject*::+1: +PyByteArray_Concat:PyObject*:a:0: +PyByteArray_Concat:PyObject*:b:0: + +PyByteArray_FromObject:PyObject*::+1: +PyByteArray_FromObject:PyObject*:o:0: + +PyByteArray_FromStringAndSize:PyObject*::+1: +PyByteArray_FromStringAndSize:const char*:string:: +PyByteArray_FromStringAndSize:Py_ssize_t:len:: + +PyByteArray_GET_SIZE:Py_ssize_t::: +PyByteArray_GET_SIZE:PyObject*:bytearray:0: + +PyByteArray_Resize:int::: +PyByteArray_Resize:PyObject*:bytearray:0: +PyByteArray_Resize:Py_ssize_t:len:: + +PyByteArray_Size:Py_ssize_t::: +PyByteArray_Size:PyObject*:bytearray:0: + +PyBytes_AS_STRING:char*::: +PyBytes_AS_STRING:PyObject*:string:0: -PyBuffer_FromObject:PyObject*::+1: -PyBuffer_FromObject:PyObject*:base:+1: -PyBuffer_FromObject:int:offset:: -PyBuffer_FromObject:int:size:: +PyBytes_AsString:char*::: +PyBytes_AsString:PyObject*:o:0: -PyBuffer_FromReadWriteObject:PyObject*::+1: -PyBuffer_FromReadWriteObject:PyObject*:base:+1: -PyBuffer_FromReadWriteObject:int:offset:: -PyBuffer_FromReadWriteObject:int:size:: +PyBytes_AsStringAndSize:int::: +PyBytes_AsStringAndSize:PyObject*:obj:0: +PyBytes_AsStringAndSize:char**:buffer:: +PyBytes_AsStringAndSize:Py_ssize_t*:length:: -PyBuffer_FromMemory:PyObject*::+1: -PyBuffer_FromMemory:void*:ptr:: -PyBuffer_FromMemory:int:size:: +PyBytes_Check:int::: +PyBytes_Check:PyObject*:o:0: -PyBuffer_FromReadWriteMemory:PyObject*::+1: -PyBuffer_FromReadWriteMemory:void*:ptr:: -PyBuffer_FromReadWriteMemory:int:size:: +PyBytes_CheckExact:int::: +PyBytes_CheckExact:PyObject*:o:0: -PyBuffer_New:PyObject*::+1: -PyBuffer_New:int:size:: +PyBytes_Concat:void::: +PyBytes_Concat:PyObject**:bytes:0: +PyBytes_Concat:PyObject*:newpart:0: + +PyBytes_ConcatAndDel:void::: +PyBytes_ConcatAndDel:PyObject**:bytes:0: +PyBytes_ConcatAndDel:PyObject*:newpart:-1: + +PyBytes_FromString:PyObject*::+1: +PyBytes_FromString:const char*:v:: + +PyBytes_FromStringAndSize:PyObject*::+1: +PyBytes_FromStringAndSize:const char*:v:: +PyBytes_FromStringAndSize:Py_ssize_t:len:: + +PyBytes_FromFormat:PyObject*::+1: +PyBytes_FromFormat:const char*:format:: +PyBytes_FromFormat::...:: + +PyBytes_FromFormatV:PyObject*::+1: +PyBytes_FromFormatV:const char*:format:: +PyBytes_FromFormatV:va_list:vargs:: + +PyBytes_FromObject:PyObject*::+1: +PyBytes_FromObject:PyObject*:o:0: + +PyBytes_GET_SIZE:Py_ssize_t::: +PyBytes_GET_SIZE:PyObject*:o:0: + +PyBytes_Size:Py_ssize_t::: +PyBytes_Size:PyObject*:o:0: + +PyCapsule_CheckExact:int::: +PyCapsule_CheckExact:PyObject*:p:0: PyCapsule_GetContext:void *::: PyCapsule_GetContext:PyObject*:self:0: @@ -72,6 +173,10 @@ PyCapsule_Import:void *::: PyCapsule_Import:const char *:name:: PyCapsule_Import:int:no_block:: +PyCapsule_IsValid:int::: +PyCapsule_IsValid:PyObject*:capsule:0: +PyCapsule_IsValid:const char*:name:: + PyCapsule_New:PyObject*::+1: PyCapsule_New:void*:pointer:: PyCapsule_New:const char *:name:: @@ -93,21 +198,8 @@ PyCapsule_SetPointer:int::: PyCapsule_SetPointer:PyObject*:self:0: PyCapsule_SetPointer:void*:pointer:: - -PyCObject_AsVoidPtr:void*::: -PyCObject_AsVoidPtr:PyObject*:self:0: - -PyCObject_FromVoidPtr:PyObject*::+1: -PyCObject_FromVoidPtr:void*:cobj:: -PyCObject_FromVoidPtr::void (* destr)(void* ):: - -PyCObject_FromVoidPtrAndDesc:PyObject*::+1: -PyCObject_FromVoidPtrAndDesc:void*:cobj:: -PyCObject_FromVoidPtrAndDesc:void*:desc:: -PyCObject_FromVoidPtrAndDesc:void(*)(void*,void*):destr:: - -PyCObject_GetDesc:void*::: -PyCObject_GetDesc:PyObject*:self:0: +PyCell_Check:int::: +PyCell_Check::ob:: PyCell_New:PyObject*::+1: PyCell_New:PyObject*:ob:0: @@ -126,19 +218,118 @@ PyCell_Set:int::: PyCell_Set:PyObject*:cell:0: PyCell_Set:PyObject*:value:0: +PyCallIter_Check:int::: +PyCallIter_Check::op:: + PyCallIter_New:PyObject*::+1: -PyCallIter_New:PyObject*:callable:: -PyCallIter_New:PyObject*:sentinel:: +PyCallIter_New:PyObject*:callable:+1: +PyCallIter_New:PyObject*:sentinel:+1: PyCallable_Check:int::: PyCallable_Check:PyObject*:o:0: +PyCode_Check:int::: +PyCode_Check:PyObject*:co:0: + +PyCode_GetNumFree:int::: +PyCode_GetNumFree:PyCodeObject*:co:0: + +PyCode_New:PyCodeObject*::+1: +PyCode_New:int:argcount:: +PyCode_New:int:kwonlyargcount:: +PyCode_New:int:nlocals:: +PyCode_New:int:stacksize:: +PyCode_New:int:flags:: +PyCode_New:PyObject*:code:0: +PyCode_New:PyObject*:consts:0: +PyCode_New:PyObject*:names:0: +PyCode_New:PyObject*:varnames:0: +PyCode_New:PyObject*:freevars:0: +PyCode_New:PyObject*:cellvars:0: +PyCode_New:PyObject*:filename:0: +PyCode_New:PyObject*:name:0: +PyCode_New:int:firstlineno:: +PyCode_New:PyObject*:lnotab:0: + +PyCode_NewEmpty:PyCodeObject*::+1: +PyCode_NewEmpty:const char*:filename:: +PyCode_NewEmpty:const char*:funcname:: +PyCode_NewEmpty:int:firstlineno:: + +PyCodec_Register:int::: +PyCodec_Register:PyObject*:search_function:+1: + +PyCodec_KnownEncoding:int::: +PyCodec_KnownEncoding:const char*:encoding:: + +PyCodec_Encode:PyObject*::+1: +PyCodec_Encode:PyObject*:object:0: +PyCodec_Encode:const char*:encoding:: +PyCodec_Encode:const char*:errors:: + +PyCodec_Decode:PyObject*::+1: +PyCodec_Decode:PyObject*:object:0: +PyCodec_Decode:const char*:encoding:: +PyCodec_Decode:const char*:errors:: + +PyCodec_Encoder:PyObject*::+1: +PyCodec_Encoder:const char*:encoding:: + +PyCodec_Decoder:PyObject*::+1: +PyCodec_Decoder:const char*:encoding:: + +PyCodec_IncrementalEncoder:PyObject*::+1: +PyCodec_IncrementalEncoder:const char*:encoding:: +PyCodec_IncrementalEncoder:const char*:errors:: + +PyCodec_IncrementalDecoder:PyObject*::+1: +PyCodec_IncrementalDecoder:const char*:encoding:: +PyCodec_IncrementalDecoder:const char*:errors:: + +PyCodec_StreamReader:PyObject*::+1: +PyCodec_StreamReader:const char*:encoding:: +PyCodec_StreamReader:PyObject*:stream:0: +PyCodec_StreamReader:const char*:errors:: + +PyCodec_StreamWriter:PyObject*::+1: +PyCodec_StreamWriter:const char*:encoding:: +PyCodec_StreamWriter:PyObject*:stream:0: +PyCodec_StreamWriter:const char*:errors:: + +PyCodec_RegisterError:int::: +PyCodec_RegisterError:const char*:name:: +PyCodec_RegisterError:PyObject*:error:+1: + +PyCodec_LookupError:PyObject*::+1: +PyCodec_LookupError:const char*:name:: + +PyCodec_StrictErrors:PyObject*::null: +PyCodec_StrictErrors:PyObject*:exc:0: + +PyCodec_IgnoreErrors:PyObject*::+1: +PyCodec_IgnoreErrors:PyObject*:exc:0: + +PyCodec_ReplaceErrors:PyObject*::+1: +PyCodec_ReplaceErrors:PyObject*:exc:0: + +PyCodec_XMLCharRefReplaceErrors:PyObject*::+1: +PyCodec_XMLCharRefReplaceErrors:PyObject*:exc:0: + +PyCodec_BackslashReplaceErrors:PyObject*::+1: +PyCodec_BackslashReplaceErrors:PyObject*:exc:0: + +PyCodec_NameReplaceErrors:PyObject*::+1: +PyCodec_NameReplaceErrors:PyObject*:exc:0: + PyComplex_AsCComplex:Py_complex::: PyComplex_AsCComplex:PyObject*:op:0: PyComplex_Check:int::: PyComplex_Check:PyObject*:p:0: +PyComplex_CheckExact:int::: +PyComplex_CheckExact:PyObject*:p:0: + PyComplex_FromCComplex:PyObject*::+1: PyComplex_FromCComplex::Py_complex v:: @@ -193,6 +384,12 @@ PyContextVar_Reset:int::: PyContextVar_Reset:PyObject*:var:0: PyContextVar_Reset:PyObject*:token:-1: +PyDate_Check:int::: +PyDate_Check:PyObject*:ob:0: + +PyDate_CheckExact:int::: +PyDate_CheckExact:PyObject*:ob:0: + PyDate_FromDate:PyObject*::+1: PyDate_FromDate:int:year:: PyDate_FromDate:int:month:: @@ -201,6 +398,12 @@ PyDate_FromDate:int:day:: PyDate_FromTimestamp:PyObject*::+1: PyDate_FromTimestamp:PyObject*:args:0: +PyDateTime_Check:int::: +PyDateTime_Check:PyObject*:ob:0: + +PyDateTime_CheckExact:int::: +PyDateTime_CheckExact:PyObject*:ob:0: + PyDateTime_FromDateAndTime:PyObject*::+1: PyDateTime_FromDateAndTime:int:year:: PyDateTime_FromDateAndTime:int:month:: @@ -213,46 +416,62 @@ PyDateTime_FromDateAndTime:int:usecond:: PyDateTime_FromTimestamp:PyObject*::+1: PyDateTime_FromTimestamp:PyObject*:args:0: +PyDelta_Check:int::: +PyDelta_Check:PyObject*:ob:0: + +PyDelta_CheckExact:int::: +PyDelta_CheckExact:PyObject*:ob:0: + PyDelta_FromDSU:PyObject*::+1: PyDelta_FromDSU:int:days:: PyDelta_FromDSU:int:seconds:: PyDelta_FromDSU:int:useconds:: PyTimeZone_FromOffset:PyObject*::+1: -PyTimeZone_FromOffset:PyDateTime_DeltaType*:offset:+1:Reference count not increased if offset is +00:00 +PyTimeZone_FromOffset:PyObject*:offset:+1:Reference count not increased if offset is +00:00 PyTimeZone_FromOffsetAndName:PyObject*::+1: -PyTimeZone_FromOffsetAndName:PyDateTime_DeltaType*:offset:+1:Reference count not increased if offset is +00:00 and name == NULL -PyTimeZone_FromOffsetAndName:PyUnicode*:name:+1: +PyTimeZone_FromOffsetAndName:PyObject*:offset:+1:Reference count not increased if offset is +00:00 and name == NULL +PyTimeZone_FromOffsetAndName:PyObject*:name:+1: + +PyDescr_IsData:int::: +PyDescr_IsData:PyObject*:descr:0: PyDescr_NewClassMethod:PyObject*::+1: -PyDescr_NewClassMethod:PyTypeObject*:type:: +PyDescr_NewClassMethod:PyTypeObject*:type:+1: PyDescr_NewClassMethod:PyMethodDef*:method:: PyDescr_NewGetSet:PyObject*::+1: -PyDescr_NewGetSet:PyTypeObject*:type:: +PyDescr_NewGetSet:PyTypeObject*:type:+1: PyDescr_NewGetSet:PyGetSetDef*:getset:: PyDescr_NewMember:PyObject*::+1: -PyDescr_NewMember:PyTypeObject*:type:: +PyDescr_NewMember:PyTypeObject*:type:+1: PyDescr_NewMember:PyMemberDef*:member:: PyDescr_NewMethod:PyObject*::+1: -PyDescr_NewMethod:PyTypeObject*:type:: +PyDescr_NewMethod:PyTypeObject*:type:+1: PyDescr_NewMethod:PyMethodDef*:meth:: PyDescr_NewWrapper:PyObject*::+1: -PyDescr_NewWrapper:PyTypeObject*:type:: +PyDescr_NewWrapper:PyTypeObject*:type:+1: PyDescr_NewWrapper:struct wrapperbase*:base:: PyDescr_NewWrapper:void*:wrapped:: PyDict_Check:int::: PyDict_Check:PyObject*:p:0: +PyDict_CheckExact:int::: +PyDict_CheckExact:PyObject*:p:0: + PyDict_Clear:void::: PyDict_Clear:PyObject*:p:0: +PyDict_Contains:int::: +PyDict_Contains:PyObject*:p:0: +PyDict_Contains:PyObject*:key:0: + PyDict_DelItem:int::: PyDict_DelItem:PyObject*:p:0: PyDict_DelItem:PyObject*:key:0: @@ -261,7 +480,7 @@ PyDict_DelItemString:int::: PyDict_DelItemString:PyObject*:p:0: PyDict_DelItemString:const char*:key:: -PyDict_GetItem:PyObject*::0:0 +PyDict_GetItem:PyObject*::0: PyDict_GetItem:PyObject*:p:0: PyDict_GetItem:PyObject*:key:0: @@ -289,9 +508,19 @@ PyDict_New:PyObject*::+1: PyDict_Copy:PyObject*::+1: PyDict_Copy:PyObject*:p:0: +PyDict_Merge:int::: +PyDict_Merge:PyObject*:a:0: +PyDict_Merge:PyObject*:b:0: +PyDict_Merge:int:override:: + +PyDict_MergeFromSeq2:int::: +PyDict_MergeFromSeq2:PyObject*:a:0: +PyDict_MergeFromSeq2:PyObject*:seq2:0: +PyDict_MergeFromSeq2:int:override:: + PyDict_Next:int::: PyDict_Next:PyObject*:p:0: -PyDict_Next:int:ppos:: +PyDict_Next:Py_ssize_t:ppos:: PyDict_Next:PyObject**:pkey:0: PyDict_Next:PyObject**:pvalue:0: @@ -305,8 +534,12 @@ PyDict_SetItemString:PyObject*:p:0: PyDict_SetItemString:const char*:key:: PyDict_SetItemString:PyObject*:val:+1: -PyDict_Size:int::: -PyDict_Size:PyObject*:p:: +PyDict_Size:Py_ssize_t::: +PyDict_Size:PyObject*:p:0: + +PyDict_Update:int::: +PyDict_Update:PyObject*:a:0: +PyDict_Update:PyObject*:b:0: PyDict_Values:PyObject*::+1: PyDict_Values:PyObject*:p:0: @@ -330,6 +563,21 @@ PyErr_Fetch:PyObject**:ptype:0: PyErr_Fetch:PyObject**:pvalue:0: PyErr_Fetch:PyObject**:ptraceback:0: +PyErr_Format:PyObject*::null: +PyErr_Format:PyObject*:exception:+1: +PyErr_Format:const char*:format:: +PyErr_Format::...:: + +PyErr_FormatV:PyObject*::null: +PyErr_FormatV:PyObject*:exception:+1: +PyErr_FormatV:const char*:format:: +PyErr_FormatV:va_list:vargs:: + +PyErr_GetExcInfo:void::: +PyErr_GetExcInfo:PyObject**:ptype:+1: +PyErr_GetExcInfo:PyObject**:pvalue:+1: +PyErr_GetExcInfo:PyObject**:ptraceback:+1: + PyErr_GivenExceptionMatches:int::: PyErr_GivenExceptionMatches:PyObject*:given:0: PyErr_GivenExceptionMatches:PyObject*:exc:0: @@ -356,27 +604,61 @@ PyErr_Occurred:PyObject*::0: PyErr_Print:void::: +PyErr_PrintEx:void::: +PyErr_PrintEx:int:set_sys_last_vars:: + +PyErr_ResourceWarning:int::: +PyErr_ResourceWarning:PyObject*:source:0: +PyErr_ResourceWarning:Py_ssize_t:stack_level:: +PyErr_ResourceWarning:const char*:format:: +PyErr_ResourceWarning::...:: + PyErr_Restore:void::: PyErr_Restore:PyObject*:type:-1: PyErr_Restore:PyObject*:value:-1: PyErr_Restore:PyObject*:traceback:-1: PyErr_SetExcFromWindowsErr:PyObject*::null: -PyErr_SetExcFromWindowsErr:PyObject*:type:0: +PyErr_SetExcFromWindowsErr:PyObject*:type:+1: PyErr_SetExcFromWindowsErr:int:ierr:: PyErr_SetExcFromWindowsErrWithFilename:PyObject*::null: -PyErr_SetExcFromWindowsErrWithFilename:PyObject*:type:0: +PyErr_SetExcFromWindowsErrWithFilename:PyObject*:type:+1: PyErr_SetExcFromWindowsErrWithFilename:int:ierr:: PyErr_SetExcFromWindowsErrWithFilename:const char*:filename:: +PyErr_SetExcFromWindowsErrWithFilenameObject:PyObject*::null: +PyErr_SetExcFromWindowsErrWithFilenameObject:PyObject*:type:+1: +PyErr_SetExcFromWindowsErrWithFilenameObject:int:ierr:: +PyErr_SetExcFromWindowsErrWithFilenameObject:PyObject*:filename:+1: + +PyErr_SetExcFromWindowsErrWithFilenameObjects:PyObject*::null: +PyErr_SetExcFromWindowsErrWithFilenameObjects:PyObject*:type:+1: +PyErr_SetExcFromWindowsErrWithFilenameObjects:int:ierr:: +PyErr_SetExcFromWindowsErrWithFilenameObjects:PyObject*:filename:+1: +PyErr_SetExcFromWindowsErrWithFilenameObjects:PyObject*:filename2:+1: + +PyErr_SetExcInfo:void::: +PyErr_SetExcInfo:PyObject*:type:0: +PyErr_SetExcInfo:PyObject*:value:0: +PyErr_SetExcInfo:PyObject*:traceback:0: + PyErr_SetFromErrno:PyObject*::null: -PyErr_SetFromErrno:PyObject*:type:0: +PyErr_SetFromErrno:PyObject*:type:+1: PyErr_SetFromErrnoWithFilename:PyObject*::null: -PyErr_SetFromErrnoWithFilename:PyObject*:type:0: +PyErr_SetFromErrnoWithFilename:PyObject*:type:+1: PyErr_SetFromErrnoWithFilename:const char*:filename:: +PyErr_SetFromErrnoWithFilenameObject:PyObject*::null: +PyErr_SetFromErrnoWithFilenameObject:PyObject*:type:+1: +PyErr_SetFromErrnoWithFilenameObject:PyObject*:filenameObject:+1: + +PyErr_SetFromErrnoWithFilenameObjects:PyObject*::null: +PyErr_SetFromErrnoWithFilenameObjects:PyObject*:type:+1: +PyErr_SetFromErrnoWithFilenameObjects:PyObject*:filenameObject:+1: +PyErr_SetFromErrnoWithFilenameObjects:PyObject*:filenameObject2:+1: + PyErr_SetFromWindowsErr:PyObject*::null: PyErr_SetFromWindowsErr:int:ierr:: @@ -384,6 +666,16 @@ PyErr_SetFromWindowsErrWithFilename:PyObject*::null: PyErr_SetFromWindowsErrWithFilename:int:ierr:: PyErr_SetFromWindowsErrWithFilename:const char*:filename:: +PyErr_SetImportError:PyObject*::null: +PyErr_SetImportError:PyObject*:msg:+1: +PyErr_SetImportError:PyObject*:name:+1: +PyErr_SetImportError:PyObject*:path:+1: + +PyErr_SetImportErrorSubclass:PyObject*::null: +PyErr_SetImportErrorSubclass:PyObject*:msg:+1: +PyErr_SetImportErrorSubclass:PyObject*:name:+1: +PyErr_SetImportErrorSubclass:PyObject*:path:+1: + PyErr_SetInterrupt:void::: PyErr_SetNone:void::: @@ -397,31 +689,69 @@ PyErr_SetString:void::: PyErr_SetString:PyObject*:type:+1: PyErr_SetString:const char*:message:: -PyErr_Format:PyObject*::null: -PyErr_Format:PyObject*:exception:+1: -PyErr_Format:const char*:format:: -PyErr_Format::...:: +PyErr_SyntaxLocation:void::: +PyErr_SyntaxLocation:const char*:filename:: +PyErr_SyntaxLocation:int:lineno:: -PyErr_FormatV:PyObject*::null: -PyErr_FormatV:PyObject*:exception:+1: -PyErr_FormatV:const char*:format:: -PyErr_FormatV:va_list:vargs:: +PyErr_SyntaxLocationEx:void::: +PyErr_SyntaxLocationEx:const char*:filename:: +PyErr_SyntaxLocationEx:int:lineno:: +PyErr_SyntaxLocationEx:int:col_offset:: + +PyErr_SyntaxLocationObject:void::: +PyErr_SyntaxLocationObject:PyObject*:filename:+1: +PyErr_SyntaxLocationObject:int:lineno:: +PyErr_SyntaxLocationObject:int:col_offset:: PyErr_WarnEx:int::: PyErr_WarnEx:PyObject*:category:0: PyErr_WarnEx:const char*:message:: PyErr_WarnEx:Py_ssize_t:stack_level:: +PyErr_WarnExplicit:int::: +PyErr_WarnExplicit:PyObject*:category:0: +PyErr_WarnExplicit:const char*:message:: +PyErr_WarnExplicit:const char*:filename:: +PyErr_WarnExplicit:int:lineno:: +PyErr_WarnExplicit:const char*:module:: +PyErr_WarnExplicit:PyObject*:registry:0: + +PyErr_WarnExplicitObject:int::: +PyErr_WarnExplicitObject:PyObject*:category:0: +PyErr_WarnExplicitObject:PyObject*:message:0: +PyErr_WarnExplicitObject:PyObject*:filename:0: +PyErr_WarnExplicitObject:int:lineno:: +PyErr_WarnExplicitObject:PyObject*:module:0: +PyErr_WarnExplicitObject:PyObject*:registry:0: + +PyErr_WarnFormat:int::: +PyErr_WarnFormat:PyObject*:category:0: +PyErr_WarnFormat:Py_ssize_t:stack_level:: +PyErr_WarnFormat:const char*:format:: +PyErr_WarnFormat::...:: + +PyErr_WriteUnraisable:void::: +PyErr_WriteUnraisable:PyObject*:obj:0: + PyEval_AcquireLock:void::: PyEval_AcquireThread:void::: PyEval_AcquireThread:PyThreadState*:tstate:: PyEval_GetBuiltins:PyObject*::0: + PyEval_GetLocals:PyObject*::0: + PyEval_GetGlobals:PyObject*::0: + PyEval_GetFrame:PyObject*::0: +PyEval_GetFuncDesc:const char*::: +PyEval_GetFuncDesc:PyObject*:func:0: + +PyEval_GetFuncName:const char*::: +PyEval_GetFuncName:PyObject*:func:0: + PyEval_InitThreads:void::: PyEval_ReleaseLock:void::: @@ -434,61 +764,85 @@ PyEval_RestoreThread:PyThreadState*:tstate:: PyEval_SaveThread:PyThreadState*::: +PyEval_SetProfile:void::: +PyEval_SetProfile:Py_tracefunc:func:: +PyEval_SetProfile:PyObject*:obj:+1: + +PyEval_SetTrace:void::: +PyEval_SetTrace:Py_tracefunc:func:: +PyEval_SetTrace:PyObject*:obj:+1: + PyEval_EvalCode:PyObject*::+1: -PyEval_EvalCode:PyCodeObject*:co:0: +PyEval_EvalCode:PyObject*:co:0: PyEval_EvalCode:PyObject*:globals:0: PyEval_EvalCode:PyObject*:locals:0: -PyException_GetTraceback:PyObject*::+1: +PyEval_EvalCodeEx:PyObject*::+1: +PyEval_EvalCodeEx:PyObject*:co:0: +PyEval_EvalCodeEx:PyObject*:globals:0: +PyEval_EvalCodeEx:PyObject*:locals:0: +PyEval_EvalCodeEx:PyObject*const*:args:: +PyEval_EvalCodeEx:int:argcount:: +PyEval_EvalCodeEx:PyObject*const*:kws:: +PyEval_EvalCodeEx:int:kwcount:: +PyEval_EvalCodeEx:PyObject*const*:defs:: +PyEval_EvalCodeEx:int:defcount:: +PyEval_EvalCodeEx:PyObject*:kwdefs:0: +PyEval_EvalCodeEx:PyObject*:closure:0: -PyFile_AsFile:FILE*::: -PyFile_AsFile:PyFileObject*:p:0: +PyEval_EvalFrame:PyObject*::+1: +PyEval_EvalFrame:PyFrameObject*:f:0: -PyFile_Check:int::: -PyFile_Check:PyObject*:p:0: +PyEval_EvalFrameEx:PyObject*::+1: +PyEval_EvalFrameEx:PyFrameObject*:f:0: +PyEval_EvalFrameEx:int:throwflag:: -PyFile_FromFile:PyObject*::+1: -PyFile_FromFile:FILE*:fp:: -PyFile_FromFile:const char*:name:: -PyFile_FromFile:const char*:mode:: -PyFile_FromFile:int(*:close):: +PyEval_MergeCompilerFlags:int::: +PyEval_MergeCompilerFlags:PyCompilerFlags*:cf:: -PyFile_FromFileEx:PyObject*::+1: -PyFile_FromFileEx:FILE*:fp:: -PyFile_FromFileEx:const char*:name:: -PyFile_FromFileEx:const char*:mode:: -PyFile_FromFileEx:int(*:close):: -PyFile_FromFileEx:int:buffering:: -PyFile_FromFileEx:const char*:encoding:: -PyFile_FromFileEx:const char*:newline:: +PyException_GetCause:PyObject*::+1: +PyException_GetCause:PyObject*:ex:0: -PyFile_FromString:PyObject*::+1: -PyFile_FromString:const char*:name:: -PyFile_FromString:const char*:mode:: +PyException_GetContext:PyObject*::+1: +PyException_GetContext:PyObject*:ex:0: + +PyException_GetTraceback:PyObject*::+1: +PyException_GetTraceback:PyObject*:ex:0: + +PyException_SetCause:void::: +PyException_SetCause:PyObject*:ex:0: +PyException_SetCause:PyObject*:cause:+1: + +PyException_SetContext:void::: +PyException_SetContext:PyObject*:ex:0: +PyException_SetContext:PyObject*:ctx:+1: + +PyException_SetTraceback:int::: +PyException_SetTraceback:PyObject*:ex:0: +PyException_SetTraceback:PyObject*:tb:+1: + +PyFile_FromFd:PyObject*::+1: +PyFile_FromFd:int:fd:: +PyFile_FromFd:const char*:name:: +PyFile_FromFd:const char*:mode:: +PyFile_FromFd:int:buffering:: +PyFile_FromFd:const char*:encoding:: +PyFile_FromFd:const char*:errors:: +PyFile_FromFd:const char*:newline:: +PyFile_FromFd:int:closefd:: PyFile_GetLine:PyObject*::+1: -PyFile_GetLine:PyObject*:p:: +PyFile_GetLine:PyObject*:p:0: PyFile_GetLine:int:n:: -PyFile_Name:PyObject*::0: -PyFile_Name:PyObject*:p:0: - -PyFile_SetBufSize:void::: -PyFile_SetBufSize:PyFileObject*:p:0: -PyFile_SetBufSize:int:n:: - -PyFile_SoftSpace:int::: -PyFile_SoftSpace:PyFileObject*:p:0: -PyFile_SoftSpace:int:newflag:: - PyFile_WriteObject:int::: PyFile_WriteObject:PyObject*:obj:0: -PyFile_WriteObject:PyFileObject*:p:0: +PyFile_WriteObject:PyObject*:p:0: PyFile_WriteObject:int:flags:: PyFile_WriteString:int::: PyFile_WriteString:const char*:s:: -PyFile_WriteString:PyFileObject*:p:0: +PyFile_WriteString:PyObject*:p:0: PyFile_WriteString:int:flags:: PyFloat_AS_DOUBLE:double::: @@ -500,15 +854,33 @@ PyFloat_AsDouble:PyObject*:pyfloat:0: PyFloat_Check:int::: PyFloat_Check:PyObject*:p:0: +PyFloat_CheckExact:int::: +PyFloat_CheckExact:PyObject*:p:0: + PyFloat_FromDouble:PyObject*::+1: PyFloat_FromDouble:double:v:: PyFloat_FromString:PyObject*::+1: PyFloat_FromString:PyObject*:str:0: +PyFloat_GetInfo:PyObject*::+1: +PyFloat_GetInfo::void:: + +PyFrozenSet_Check:int::: +PyFrozenSet_Check:PyObject*:p:0: + +PyFrozenSet_CheckExact:int::: +PyFrozenSet_CheckExact:PyObject*:p:0: + PyFrozenSet_New:PyObject*::+1: PyFrozenSet_New:PyObject*:iterable:0: +PyFunction_Check:int::: +PyFunction_Check:PyObject*:o:0: + +PyFunction_GetAnnotations:PyObject*::0: +PyFunction_GetAnnotations:PyObject*:op:0: + PyFunction_GetClosure:PyObject*::0: PyFunction_GetClosure:PyObject*:op:0: @@ -533,6 +905,10 @@ PyFunction_NewWithQualName:PyObject*:code:+1: PyFunction_NewWithQualName:PyObject*:globals:+1: PyFunction_NewWithQualName:PyObject*:qualname:+1: +PyFunction_SetAnnotations:int::: +PyFunction_SetAnnotations:PyObject*:op:0: +PyFunction_SetAnnotations:PyObject*:annotations:+1: + PyFunction_SetClosure:int::: PyFunction_SetClosure:PyObject*:op:0: PyFunction_SetClosure:PyObject*:closure:+1: @@ -541,30 +917,27 @@ PyFunction_SetDefaults:int::: PyFunction_SetDefaults:PyObject*:op:0: PyFunction_SetDefaults:PyObject*:defaults:+1: +PyGen_Check:int::: +PyGen_Check:PyObject*:ob:0: + +PyGen_CheckExact:int::: +PyGen_CheckExact:PyObject*:ob:0: + PyGen_New:PyObject*::+1: PyGen_New:PyFrameObject*:frame:0: PyGen_NewWithQualName:PyObject*::+1: PyGen_NewWithQualName:PyFrameObject*:frame:0: +PyGen_NewWithQualName:PyObject*:name:0: +PyGen_NewWithQualName:PyObject*:qualname:0: + +PyCoro_CheckExact:int::: +PyCoro_CheckExact:PyObject*:ob:0: PyCoro_New:PyObject*::+1: PyCoro_New:PyFrameObject*:frame:0: - -Py_InitModule:PyObject*::0: -Py_InitModule:const char*:name:: -Py_InitModule:PyMethodDef[]:methods:: - -Py_InitModule3:PyObject*::0: -Py_InitModule3:const char*:name:: -Py_InitModule3:PyMethodDef[]:methods:: -Py_InitModule3:const char*:doc:: - -Py_InitModule4:PyObject*::0: -Py_InitModule4:const char*:name:: -Py_InitModule4:PyMethodDef[]:methods:: -Py_InitModule4:const char*:doc:: -Py_InitModule4:PyObject*:self:: -Py_InitModule4:int:apiver::usually provided by Py_InitModule or Py_InitModule3 +PyCoro_New:PyObject*:name:0: +PyCoro_New:PyObject*:qualname:0: PyImport_AddModule:PyObject*::0:reference borrowed from sys.modules PyImport_AddModule:const char*:name:: @@ -637,39 +1010,26 @@ PyImport_ImportModuleLevelObject:PyObject*:locals:0:??? PyImport_ImportModuleLevelObject:PyObject*:fromlist:0:??? PyImport_ImportModuleLevelObject:int:level:: +PyImport_ImportModuleNoBlock:PyObject*::+1: +PyImport_ImportModuleNoBlock:const char*:name:: + PyImport_ReloadModule:PyObject*::+1: PyImport_ReloadModule:PyObject*:m:0: -PyInstance_New:PyObject*::+1: -PyInstance_New:PyObject*:klass:+1: -PyInstance_New:PyObject*:arg:0: -PyInstance_New:PyObject*:kw:0: - -PyInstance_NewRaw:PyObject*::+1: -PyInstance_NewRaw:PyObject*:klass:+1: -PyInstance_NewRaw:PyObject*:dict:+1: +PyIndex_Check:int::: +PyIndex_Check:PyObject*:o:0: -PyInt_AS_LONG:long::: -PyInt_AS_LONG:PyIntObject*:io:0: +PyInstanceMethod_Check:int::: +PyInstanceMethod_Check:PyObject*:o:0: -PyInt_AsLong:long::: -PyInt_AsLong:PyObject*:io:0: +PyInstanceMethod_Function:PyObject*::0: +PyInstanceMethod_Function:PyObject*:im:0: -PyInt_Check:int::: -PyInt_Check:PyObject*:op:0: +PyInstanceMethod_GET_FUNCTION:PyObject*::0: +PyInstanceMethod_GET_FUNCTION:PyObject*:im:0: -PyInt_FromLong:PyObject*::+1: -PyInt_FromLong:long:ival:: - -PyInt_FromString:PyObject*::+1: -PyInt_FromString:char*:str:0: -PyInt_FromString:char**:pend:0: -PyInt_FromString:int:base:0: - -PyInt_FromSsize_t:PyObject*::+1: -PyInt_FromSsize_t:Py_ssize_t:ival:: - -PyInt_GetMax:long::: +PyInstanceMethod_New:PyObject*::+1: +PyInstanceMethod_New:PyObject*:func:0: PyInterpreterState_Clear:void::: PyInterpreterState_Clear:PyInterpreterState*:interp:: @@ -682,7 +1042,8 @@ PyInterpreterState_GetID:PyInterpreterState*:interp:: PyInterpreterState_New:PyInterpreterState*::: -PyIter_Check:int:o:0: +PyIter_Check:int::: +PyIter_Check:PyObject*:o:0: PyIter_Next:PyObject*::+1: PyIter_Next:PyObject*:o:0: @@ -697,50 +1058,53 @@ PyList_AsTuple:PyObject*:list:0: PyList_Check:int::: PyList_Check:PyObject*:p:0: +PyList_CheckExact:int::: +PyList_CheckExact:PyObject*:p:0: + PyList_GET_ITEM:PyObject*::0: PyList_GET_ITEM:PyObject*:list:0: -PyList_GET_ITEM:int:i:0: +PyList_GET_ITEM:Py_ssize_t:i:: -PyList_GET_SIZE:int::: +PyList_GET_SIZE:Py_ssize_t::: PyList_GET_SIZE:PyObject*:list:0: PyList_GetItem:PyObject*::0: PyList_GetItem:PyObject*:list:0: -PyList_GetItem:int:index:: +PyList_GetItem:Py_ssize_t:index:: PyList_GetSlice:PyObject*::+1: PyList_GetSlice:PyObject*:list:0: -PyList_GetSlice:int:low:: -PyList_GetSlice:int:high:: +PyList_GetSlice:Py_ssize_t:low:: +PyList_GetSlice:Py_ssize_t:high:: PyList_Insert:int::: PyList_Insert:PyObject*:list:0: -PyList_Insert:int:index:: +PyList_Insert:Py_ssize_t:index:: PyList_Insert:PyObject*:item:+1: PyList_New:PyObject*::+1: -PyList_New:int:len:: +PyList_New:Py_ssize_t:len:: PyList_Reverse:int::: PyList_Reverse:PyObject*:list:0: PyList_SET_ITEM:void::: PyList_SET_ITEM:PyObject*:list:0: -PyList_SET_ITEM:int:i:: +PyList_SET_ITEM:Py_ssize_t:i:: PyList_SET_ITEM:PyObject*:o:0: PyList_SetItem:int::: PyList_SetItem:PyObject*:list:0: -PyList_SetItem:int:index:: +PyList_SetItem:Py_ssize_t:index:: PyList_SetItem:PyObject*:item:0: PyList_SetSlice:int::: PyList_SetSlice:PyObject*:list:0: -PyList_SetSlice:int:low:: -PyList_SetSlice:int:high:: +PyList_SetSlice:Py_ssize_t:low:: +PyList_SetSlice:Py_ssize_t:high:: PyList_SetSlice:PyObject*:itemlist:0:but increfs its elements? -PyList_Size:int::: +PyList_Size:Py_ssize_t::: PyList_Size:PyObject*:list:0: PyList_Sort:int::: @@ -752,12 +1116,44 @@ PyLong_AsDouble:PyObject*:pylong:0: PyLong_AsLong:long::: PyLong_AsLong:PyObject*:pylong:0: +PyLong_AsLongAndOverflow:long::: +PyLong_AsLongAndOverflow:PyObject*:obj:0: +PyLong_AsLongAndOverflow:int*:overflow:: + +PyLong_AsLongLong:long long::: +PyLong_AsLongLong:PyObject*:obj:0: + +PyLong_AsLongLongAndOverflow:long long::: +PyLong_AsLongLongAndOverflow:PyObject*:obj:0: +PyLong_AsLongLongAndOverflow:int*:overflow:: + +PyLong_AsSize_t:size_t::: +PyLong_AsSize_t:PyObject*:pylong:0: + +PyLong_AsSsize_t:Py_ssize_t::: +PyLong_AsSsize_t:PyObject*:pylong:0: + PyLong_AsUnsignedLong:unsigned long::: PyLong_AsUnsignedLong:PyObject*:pylong:0: +PyLong_AsUnsignedLongLong:unsigned long long::: +PyLong_AsUnsignedLongLong:PyObject*:pylong:0: + +PyLong_AsUnsignedLongMask:unsigned long::: +PyLong_AsUnsignedLongMask:PyObject*:obj:0: + +PyLong_AsUnsignedLongLongMask:unsigned long long::: +PyLong_AsUnsignedLongLongMask:PyObject*:obj:0: + +PyLong_AsVoidPtr:void*::: +PyLong_AsVoidPtr:PyObject*:pylong:0: + PyLong_Check:int::: PyLong_Check:PyObject*:p:0: +PyLong_CheckExact:int::: +PyLong_CheckExact:PyObject*:p:0: + PyLong_FromDouble:PyObject*::+1: PyLong_FromDouble:double:v:: @@ -770,16 +1166,26 @@ PyLong_FromLongLong:long long:v:: PyLong_FromUnsignedLongLong:PyObject*::+1: PyLong_FromUnsignedLongLong:unsigned long long:v:: +PyLong_FromSize_t:PyObject*::+1: +PyLong_FromSize_t:size_t:v:: + +PyLong_FromSsize_t:PyObject*::+1: +PyLong_FromSsize_t:Py_ssize_t:v:: + PyLong_FromString:PyObject*::+1: PyLong_FromString:const char*:str:: PyLong_FromString:char**:pend:: PyLong_FromString:int:base:: PyLong_FromUnicode:PyObject*::+1: -PyLong_FromUnicode:Py_UNICODE:u:: -PyLong_FromUnicode:int:length:: +PyLong_FromUnicode:Py_UNICODE*:u:: +PyLong_FromUnicode:Py_ssize_t:length:: PyLong_FromUnicode:int:base:: +PyLong_FromUnicodeObject:PyObject*::+1: +PyLong_FromUnicodeObject:PyObject*:u:0: +PyLong_FromUnicodeObject:int:base:: + PyLong_FromUnsignedLong:PyObject*::+1: PyLong_FromUnsignedLong:unsignedlong:v:: @@ -815,7 +1221,7 @@ PyMapping_Items:PyObject*:o:0: PyMapping_Keys:PyObject*::+1: PyMapping_Keys:PyObject*:o:0: -PyMapping_Length:int::: +PyMapping_Length:Py_ssize_t::: PyMapping_Length:PyObject*:o:0: PyMapping_SetItemString:int::: @@ -823,6 +1229,9 @@ PyMapping_SetItemString:PyObject*:o:0: PyMapping_SetItemString:const char*:key:: PyMapping_SetItemString:PyObject*:v:+1: +PyMapping_Size:Py_ssize_t::: +PyMapping_Size:PyObject*:o:0: + PyMapping_Values:PyObject*::+1: PyMapping_Values:PyObject*:o:0: @@ -834,20 +1243,43 @@ PyMarshal_ReadObjectFromFile:FILE*:file:: PyMarshal_ReadObjectFromString:PyObject*::+1: PyMarshal_ReadObjectFromString:const char*:string:: -PyMarshal_ReadObjectFromString:int:len:: +PyMarshal_ReadObjectFromString:Py_ssize_t:len:: PyMarshal_WriteObjectToString:PyObject*::+1: PyMarshal_WriteObjectToString:PyObject*:value:0: +PyMarshal_WriteObjectToString:int:version:: -PyMethod_Class:PyObject*::0: -PyMethod_Class:PyObject*:im:0: +PyMemoryView_Check:int::: +PyMemoryView_Check:PyObject*:obj:0: + +PyMemoryView_FromBuffer:PyObject*::+1: +PyMemoryView_FromBuffer:Py_buffer*:view:: + +PyMemoryView_FromMemory:PyObject*::+1: +PyMemoryView_FromMemory:char*:mem:: +PyMemoryView_FromMemory:Py_ssize_t:size:: +PyMemoryView_FromMemory:int:flags:: + +PyMemoryView_FromObject:PyObject*::+1: +PyMemoryView_FromObject:PyObject*:obj:0: + +PyMemoryView_GET_BASE:Py_buffer*::: +PyMemoryView_GET_BASE:PyObject*:mview:0: + +PyMemoryView_GET_BUFFER:Py_buffer*::: +PyMemoryView_GET_BUFFER:PyObject*:mview:0: + +PyMemoryView_GetContiguous:PyObject*::+1: +PyMemoryView_GetContiguous:PyObject*:obj:0: +PyMemoryView_GetContiguous:int:buffertype:: +PyMemoryView_GetContiguous:char:order:: + +PyMethod_Check:int::: +PyMethod_Check:PyObject*:o:0: PyMethod_Function:PyObject*::0: PyMethod_Function:PyObject*:im:0: -PyMethod_GET_CLASS:PyObject*::0: -PyMethod_GET_CLASS:PyObject*:im:0: - PyMethod_GET_FUNCTION:PyObject*::0: PyMethod_GET_FUNCTION:PyObject*:im:0: @@ -862,18 +1294,93 @@ PyMethod_New:PyObject*:class:0: PyMethod_Self:PyObject*::0: PyMethod_Self:PyObject*:im:0: +PyModule_AddFunctions:int::: +PyModule_AddFunctions:PyObject*:module:0: +PyModule_AddFunctions:PyMethodDef*:functions:: + +PyModule_AddIntConstant:int::: +PyModule_AddIntConstant:PyObject*:module:0: +PyModule_AddIntConstant:const char*:name:: +PyModule_AddIntConstant:long:value:: + +PyModule_AddIntMacro:int::: +PyModule_AddIntMacro:PyObject*:module:0: +PyModule_AddIntMacro::macro:: + +PyModule_AddObject:int::: +PyModule_AddObject:PyObject*:module:0: +PyModule_AddObject:const char*:name:: +PyModule_AddObject:PyObject*:value:+1: + +PyModule_AddStringConstant:int::: +PyModule_AddStringConstant:PyObject*:module:0: +PyModule_AddStringConstant:const char*:name:: +PyModule_AddStringConstant:const char*:value:: + +PyModule_AddStringMacro:int::: +PyModule_AddStringMacro:PyObject*:module:0: +PyModule_AddStringMacro::macro:: + +PyModule_Check:int::: +PyModule_Check:PyObject*:p:0: + +PyModule_CheckExact:int::: +PyModule_CheckExact:PyObject*:p:0: + +PyModule_Create:PyObject*::+1: +PyModule_Create:PyModuleDef*:def:: + +PyModule_Create2:PyObject*::+1: +PyModule_Create2:PyModuleDef*:def:: +PyModule_Create2:int:module_api_version:: + +PyModule_ExecDef:int::: +PyModule_ExecDef:PyObject*:module:0: +PyModule_ExecDef:PyModuleDef*:def:: + +PyModule_FromDefAndSpec:PyObject*::+1: +PyModule_FromDefAndSpec:PyModuleDef*:def:: +PyModule_FromDefAndSpec:PyObject*:spec:0: + +PyModule_FromDefAndSpec2:PyObject*::+1: +PyModule_FromDefAndSpec2:PyModuleDef*:def:: +PyModule_FromDefAndSpec2:PyObject*:spec:0: +PyModule_FromDefAndSpec2:int:module_api_version:: + +PyModule_GetDef:PyModuleDef*::0: +PyModule_GetDef:PyObject*:module:0: + PyModule_GetDict:PyObject*::0: -PyModule_GetDict::PyObject* module:0: +PyModule_GetDict:PyObject*:module:0: PyModule_GetFilename:const char*::: PyModule_GetFilename:PyObject*:module:0: +PyModule_GetFilenameObject:PyObject*::+1: +PyModule_GetFilenameObject:PyObject*:module:0: + PyModule_GetName:const char*::: PyModule_GetName:PyObject*:module:0: +PyModule_GetNameObject:PyObject*::+1: +PyModule_GetNameObject:PyObject*:module:0: + +PyModule_GetState:void*::: +PyModule_GetState:PyObject*:module:0: + PyModule_New:PyObject*::+1: PyModule_New::char* name:: +PyModule_NewObject:PyObject*::+1: +PyModule_NewObject:PyObject*:name:+1: + +PyModule_SetDocString:int::: +PyModule_SetDocString:PyObject*:module:0: +PyModule_SetDocString:const char*:docstring:: + +PyModuleDef_Init:PyObject*::0: +PyModuleDef_Init:PyModuleDef*:def:0: + PyNumber_Absolute:PyObject*::+1: PyNumber_Absolute:PyObject*:o:0: @@ -885,12 +1392,12 @@ PyNumber_And:PyObject*::+1: PyNumber_And:PyObject*:o1:0: PyNumber_And:PyObject*:o2:0: -PyNumber_Check:PyObject*:o:0: -PyNumber_Check:int::: +PyNumber_AsSsize_t:Py_ssize_t::: +PyNumber_AsSsize_t:PyObject*:o:0: +PyNumber_AsSsize_t:PyObject*:exc:0: -PyNumber_Divide:PyObject*::+1: -PyNumber_Divide:PyObject*:o1:0: -PyNumber_Divide:PyObject*:o2:0: +PyNumber_Check:int::: +PyNumber_Check:PyObject*:o:0: PyNumber_Divmod:PyObject*::+1: PyNumber_Divmod:PyObject*:o1:0: @@ -903,6 +1410,9 @@ PyNumber_FloorDivide:PyObject*::+1: PyNumber_FloorDivide:PyObject*:v:0: PyNumber_FloorDivide:PyObject*:w:0: +PyNumber_Index:PyObject*::+1: +PyNumber_Index:PyObject*:o:0: + PyNumber_InPlaceAdd:PyObject*::+1: PyNumber_InPlaceAdd:PyObject*:v:0: PyNumber_InPlaceAdd:PyObject*:w:0: @@ -911,10 +1421,6 @@ PyNumber_InPlaceAnd:PyObject*::+1: PyNumber_InPlaceAnd:PyObject*:v:0: PyNumber_InPlaceAnd:PyObject*:w:0: -PyNumber_InPlaceDivide:PyObject*::+1: -PyNumber_InPlaceDivide:PyObject*:v:0: -PyNumber_InPlaceDivide:PyObject*:w:0: - PyNumber_InPlaceFloorDivide:PyObject*::+1: PyNumber_InPlaceFloorDivide:PyObject*:v:0: PyNumber_InPlaceFloorDivide:PyObject*:w:0: @@ -923,6 +1429,10 @@ PyNumber_InPlaceLshift:PyObject*::+1: PyNumber_InPlaceLshift:PyObject*:v:0: PyNumber_InPlaceLshift:PyObject*:w:0: +PyNumber_InPlaceMatrixMultiply:PyObject*::+1: +PyNumber_InPlaceMatrixMultiply:PyObject*:o1:0: +PyNumber_InPlaceMatrixMultiply:PyObject*:o2:0: + PyNumber_InPlaceMultiply:PyObject*::+1: PyNumber_InPlaceMultiply:PyObject*:v:0: PyNumber_InPlaceMultiply:PyObject*:w:0: @@ -966,6 +1476,10 @@ PyNumber_Lshift:PyObject*::+1: PyNumber_Lshift:PyObject*:o1:0: PyNumber_Lshift:PyObject*:o2:0: +PyNumber_MatrixMultiply:PyObject*::+1: +PyNumber_MatrixMultiply:PyObject*:o1:0: +PyNumber_MatrixMultiply:PyObject*:o2:0: + PyNumber_Multiply:PyObject*::+1: PyNumber_Multiply:PyObject*:o1:0: PyNumber_Multiply:PyObject*:o2:0: @@ -997,6 +1511,10 @@ PyNumber_Subtract:PyObject*::+1: PyNumber_Subtract:PyObject*:o1:0: PyNumber_Subtract:PyObject*:o2:0: +PyNumber_ToBase:PyObject*::+1: +PyNumber_ToBase:PyObject*:n:0: +PyNumber_ToBase:int:base:: + PyNumber_TrueDivide:PyObject*::+1: PyNumber_TrueDivide:PyObject*:v:0: PyNumber_TrueDivide:PyObject*:w:0: @@ -1019,6 +1537,27 @@ PyOS_BeforeFork:void::: PyOS_FSPath:PyObject*::+1: PyOS_FSPath:PyObject*:path:0: +PyObject_ASCII:PyObject*::+1: +PyObject_ASCII:PyObject*:o:0: + +PyObject_AsCharBuffer:int::: +PyObject_AsCharBuffer:PyObject*:obj:0: +PyObject_AsCharBuffer:const char**:buffer:: +PyObject_AsCharBuffer:Py_ssize_t*:buffer_len:: + +PyObject_AsReadBuffer:int::: +PyObject_AsReadBuffer:PyObject*:obj:0: +PyObject_AsReadBuffer:const void**:buffer:: +PyObject_AsReadBuffer:Py_ssize_t*:buffer_len:: + +PyObject_AsWriteBuffer:int::: +PyObject_AsWriteBuffer:PyObject*:obj:0: +PyObject_AsWriteBuffer:void**:buffer:: +PyObject_AsWriteBuffer:Py_ssize_t*:buffer_len:: + +PyObject_Bytes:PyObject*::+1: +PyObject_Bytes:PyObject*:o:0: + PyObject_Call:PyObject*::+1: PyObject_Call:PyObject*:callable_object:0: PyObject_Call:PyObject*:args:0: @@ -1048,14 +1587,11 @@ PyObject_CallObject:PyObject*::+1: PyObject_CallObject:PyObject*:callable_object:0: PyObject_CallObject:PyObject*:args:0: -PyObject_Cmp:int::: -PyObject_Cmp:PyObject*:o1:0: -PyObject_Cmp:PyObject*:o2:0: -PyObject_Cmp:int*:result:: +PyObject_CheckBuffer:int::: +PyObject_CheckBuffer:PyObject*:obj:0: -PyObject_Compare:int::: -PyObject_Compare:PyObject*:o1:0: -PyObject_Compare:PyObject*:o2:0: +PyObject_CheckReadBuffer:int::: +PyObject_CheckReadBuffer:PyObject*:o:0: PyObject_DelAttr:int::: PyObject_DelAttr:PyObject*:o:0: @@ -1072,6 +1608,46 @@ PyObject_DelItem:PyObject*:key:0: PyObject_Dir:PyObject*::+1: PyObject_Dir:PyObject*:o:0: +PyObject_GC_Del:void::: +PyObject_GC_Del:void*:op:: + +PyObject_GC_New:TYPE*::+1: +PyObject_GC_New::TYPE:: +PyObject_GC_New:PyTypeObject*:type:0: + +PyObject_GC_NewVar:TYPE*::+1: +PyObject_GC_NewVar::TYPE:: +PyObject_GC_NewVar:PyTypeObject*:type:0: +PyObject_GC_NewVar:Py_ssize_t:size:: + +PyObject_GC_Resize:TYPE*::0: +PyObject_GC_Resize::TYPE:: +PyObject_GC_Resize:PyVarObject*:op:0: +PyObject_GC_Resize:Py_ssize_t:newsize:: + +PyObject_GC_Track:void::: +PyObject_GC_Track:PyObject*:op:0: + +PyObject_GC_UnTrack:void::: +PyObject_GC_UnTrack:void*:op:: + +PyObject_GenericGetAttr:PyObject*::+1: +PyObject_GenericGetAttr:PyObject*:o:0: +PyObject_GenericGetAttr:PyObject*:name:0: + +PyObject_GenericGetDict:PyObject*::+1: +PyObject_GenericGetDict:PyObject*:o:0: +PyObject_GenericGetDict:void*:context:: + +PyObject_GenericSetAttr:int::: +PyObject_GenericSetAttr:PyObject*:o:0: +PyObject_GenericSetAttr:PyObject*:name:0: +PyObject_GenericSetAttr:PyObject*:value:+1: + +PyObject_GenericSetDict:int::: +PyObject_GenericSetDict:PyObject*:o:+1: +PyObject_GenericSetDict:void*:context:: + PyObject_GetAttr:PyObject*::+1: PyObject_GetAttr:PyObject*:o:0: PyObject_GetAttr:PyObject*:attr_name:0: @@ -1080,6 +1656,11 @@ PyObject_GetAttrString:PyObject*::+1: PyObject_GetAttrString:PyObject*:o:0: PyObject_GetAttrString:const char*:attr_name:: +PyObject_GetBuffer:int::: +PyObject_GetBuffer:PyObject*:exporter:0: +PyObject_GetBuffer:Py_buffer*:view:: +PyObject_GetBuffer:int:flags:: + PyObject_GetItem:PyObject*::+1: PyObject_GetItem:PyObject*:o:0: PyObject_GetItem:PyObject*:key:0: @@ -1093,30 +1674,59 @@ PyObject_HasAttr:PyObject*:attr_name:0: PyObject_HasAttrString:int::: PyObject_HasAttrString:PyObject*:o:0: -PyObject_HasAttrString:const char*:attr_name:0: +PyObject_HasAttrString:const char*:attr_name:: PyObject_Hash:int::: PyObject_Hash:PyObject*:o:0: +PyObject_HashNotImplemented:Py_hash_t::: +PyObject_HashNotImplemented:PyObject*:o:0: + +PyObject_IsInstance:int::: +PyObject_IsInstance:PyObject*:inst:0: +PyObject_IsInstance:PyObject*:cls:0: + +PyObject_IsSubclass:int::: +PyObject_IsSubclass:PyObject*:derived:0: +PyObject_IsSubclass:PyObject*:cls:0: + PyObject_IsTrue:int::: PyObject_IsTrue:PyObject*:o:0: PyObject_Init:PyObject*::0: PyObject_Init:PyObject*:op:0: +PyObject_Init:PyTypeObject*:type:0: PyObject_InitVar:PyVarObject*::0: PyObject_InitVar:PyVarObject*:op:0: -PyObject_Length:int::: +PyObject_Length:Py_ssize_t::: PyObject_Length:PyObject*:o:0: +PyObject_LengthHint:Py_ssize_t::: +PyObject_LengthHint:PyObject*:o:0: +PyObject_LengthHint:Py_ssize_t:default:: + PyObject_NEW:PyObject*::+1: +PyObject_NEW::TYPE:: +PyObject_NEW:PyTypeObject*:type:0: PyObject_New:PyObject*::+1: +PyObject_New::TYPE:: +PyObject_New:PyTypeObject*:type:0: PyObject_NEW_VAR:PyObject*::+1: +PyObject_NEW_VAR::TYPE:: +PyObject_NEW_VAR:PyTypeObject*:type:0: +PyObject_NEW_VAR:Py_ssize_t:size:: PyObject_NewVar:PyObject*::+1: +PyObject_NewVar::TYPE:: +PyObject_NewVar:PyTypeObject*:type:0: +PyObject_NewVar:Py_ssize_t:size:: + +PyObject_Not:int::: +PyObject_Not:PyObject*:o:0: PyObject_Print:int::: PyObject_Print:PyObject*:o:0: @@ -1151,28 +1761,65 @@ PyObject_SetItem:PyObject*:o:0: PyObject_SetItem:PyObject*:key:0: PyObject_SetItem:PyObject*:v:+1: +PyObject_Size:Py_ssize_t::: +PyObject_Size:PyObject*:o:0: + PyObject_Str:PyObject*::+1: PyObject_Str:PyObject*:o:0: PyObject_Type:PyObject*::+1: PyObject_Type:PyObject*:o:0: -PyObject_Unicode:PyObject*::+1: -PyObject_Unicode:PyObject*:o:0: +PyObject_TypeCheck:int::: +PyObject_TypeCheck:PyObject*:o:0: +PyObject_TypeCheck:PyTypeObject*:type:0: PyParser_SimpleParseFile:struct _node*::: PyParser_SimpleParseFile:FILE*:fp:: PyParser_SimpleParseFile:const char*:filename:: PyParser_SimpleParseFile:int:start:: +PyParser_SimpleParseFileFlags:struct _node*::: +PyParser_SimpleParseFileFlags:FILE*:fp:: +PyParser_SimpleParseFileFlags:const char*:filename:: +PyParser_SimpleParseFileFlags:int:start:: +PyParser_SimpleParseFileFlags:int:flags:: + PyParser_SimpleParseString:struct _node*::: PyParser_SimpleParseString:const char*:str:: PyParser_SimpleParseString:int:start:: +PyParser_SimpleParseStringFlags:struct _node*::: +PyParser_SimpleParseStringFlags:const char*:str:: +PyParser_SimpleParseStringFlags:int:start:: +PyParser_SimpleParseStringFlags:int:flags:: + +PyParser_SimpleParseStringFlagsFilename:struct _node*::: +PyParser_SimpleParseStringFlagsFilename:const char*:str:: +PyParser_SimpleParseStringFlagsFilename:const char*:filename:: +PyParser_SimpleParseStringFlagsFilename:int:start:: +PyParser_SimpleParseStringFlagsFilename:int:flags:: + PyRun_AnyFile:int::: PyRun_AnyFile:FILE*:fp:: PyRun_AnyFile:const char*:filename:: +PyRun_AnyFileFlags:int::: +PyRun_AnyFileFlags:FILE*:fp:: +PyRun_AnyFileFlags:const char*:filename:: +PyRun_AnyFileFlags:PyCompilerFlags*:flags:: + +PyRun_AnyFileEx:int::: +PyRun_AnyFileEx:FILE*:fp:: +PyRun_AnyFileEx:const char*:filename:: +PyRun_AnyFileEx:int:closeit:: + +PyRun_AnyFileExFlags:int::: +PyRun_AnyFileExFlags:FILE*:fp:: +PyRun_AnyFileExFlags:const char*:filename:: +PyRun_AnyFileExFlags:int:closeit:: +PyRun_AnyFileExFlags:PyCompilerFlags*:flags:: + PyRun_File:PyObject*::+1:??? -- same as eval_code2() PyRun_File:FILE*:fp:: PyRun_File:const char*:filename:: @@ -1209,17 +1856,42 @@ PyRun_InteractiveLoop:int::: PyRun_InteractiveLoop:FILE*:fp:: PyRun_InteractiveLoop:const char*:filename:: +PyRun_InteractiveLoopFlags:int::: +PyRun_InteractiveLoopFlags:FILE*:fp:: +PyRun_InteractiveLoopFlags:const char*:filename:: +PyRun_InteractiveLoopFlags:PyCompilerFlags*:flags:: + PyRun_InteractiveOne:int::: PyRun_InteractiveOne:FILE*:fp:: PyRun_InteractiveOne:const char*:filename:: +PyRun_InteractiveOneFlags:int::: +PyRun_InteractiveOneFlags:FILE*:fp:: +PyRun_InteractiveOneFlags:const char*:filename:: +PyRun_InteractiveOneFlags:PyCompilerFlags*:flags:: + PyRun_SimpleFile:int::: PyRun_SimpleFile:FILE*:fp:: PyRun_SimpleFile:const char*:filename:: +PyRun_SimpleFileEx:int::: +PyRun_SimpleFileEx:FILE*:fp:: +PyRun_SimpleFileEx:const char*:filename:: +PyRun_SimpleFileEx:int:closeit:: + +PyRun_SimpleFileExFlags:int::: +PyRun_SimpleFileExFlags:FILE*:fp:: +PyRun_SimpleFileExFlags:const char*:filename:: +PyRun_SimpleFileExFlags:int:closeit:: +PyRun_SimpleFileExFlags:PyCompilerFlags*:flags:: + PyRun_SimpleString:int::: PyRun_SimpleString:const char*:command:: +PyRun_SimpleStringFlags:int::: +PyRun_SimpleStringFlags:const char*:command:: +PyRun_SimpleStringFlags:PyCompilerFlags*:flags:: + PyRun_String:PyObject*::+1:??? -- same as eval_code2() PyRun_String:const char*:str:: PyRun_String:int:start:: @@ -1233,6 +1905,9 @@ PyRun_StringFlags:PyObject*:globals:0: PyRun_StringFlags:PyObject*:locals:0: PyRun_StringFlags:PyCompilerFlags*:flags:: +PySeqIter_Check:int::: +PySeqIter_Check::op:: + PySeqIter_New:PyObject*::+1: PySeqIter_New:PyObject*:seq:: @@ -1243,18 +1918,22 @@ PySequence_Concat:PyObject*::+1: PySequence_Concat:PyObject*:o1:0: PySequence_Concat:PyObject*:o2:0: -PySequence_Count:int::: +PySequence_Contains:int::: +PySequence_Contains:PyObject*:o:0: +PySequence_Contains:PyObject*:value:0: + +PySequence_Count:Py_ssize_t::: PySequence_Count:PyObject*:o:0: PySequence_Count:PyObject*:value:0: PySequence_DelItem:int::: PySequence_DelItem:PyObject*:o:0: -PySequence_DelItem:int:i:: +PySequence_DelItem:Py_ssize_t:i:: PySequence_DelSlice:int::: PySequence_DelSlice:PyObject*:o:0: -PySequence_DelSlice:int:i1:: -PySequence_DelSlice:int:i2:: +PySequence_DelSlice:Py_ssize_t:i1:: +PySequence_DelSlice:Py_ssize_t:i2:: PySequence_Fast:PyObject*::+1: PySequence_Fast:PyObject*:v:0: @@ -1262,22 +1941,28 @@ PySequence_Fast:const char*:m:: PySequence_Fast_GET_ITEM:PyObject*::0: PySequence_Fast_GET_ITEM:PyObject*:o:0: -PySequence_Fast_GET_ITEM:int:i:: +PySequence_Fast_GET_ITEM:Py_ssize_t:i:: + +PySequence_Fast_GET_SIZE:Py_ssize_t::: +PySequence_Fast_GET_SIZE:PyObject*:o:0: + +PySequence_Fast_ITEMS:PyObject**::: +PySequence_Fast_ITEMS:PyObject*:o:0: PySequence_GetItem:PyObject*::+1: PySequence_GetItem:PyObject*:o:0: -PySequence_GetItem:int:i:: +PySequence_GetItem:Py_ssize_t:i:: PySequence_GetSlice:PyObject*::+1: PySequence_GetSlice:PyObject*:o:0: -PySequence_GetSlice:int:i1:: -PySequence_GetSlice:int:i2:: +PySequence_GetSlice:Py_ssize_t:i1:: +PySequence_GetSlice:Py_ssize_t:i2:: PySequence_In:int::: PySequence_In:PyObject*:o:0: PySequence_In:PyObject*:value:0: -PySequence_Index:int::: +PySequence_Index:Py_ssize_t::: PySequence_Index:PyObject*:o:0: PySequence_Index:PyObject*:value:0: @@ -1291,22 +1976,25 @@ PySequence_InPlaceRepeat:PyObject*:o:0: PySequence_ITEM:PyObject*::+1: PySequence_ITEM:PyObject*:o:0: -PySequence_ITEM:int:i:: +PySequence_ITEM:Py_ssize_t:i:: PySequence_Repeat:PyObject*::+1: PySequence_Repeat:PyObject*:o:0: -PySequence_Repeat:int:count:: +PySequence_Repeat:Py_ssize_t:count:: PySequence_SetItem:int::: PySequence_SetItem:PyObject*:o:0: -PySequence_SetItem:int:i:: +PySequence_SetItem:Py_ssize_t:i:: PySequence_SetItem:PyObject*:v:+1: PySequence_SetSlice:int::: PySequence_SetSlice:PyObject*:o:0: -PySequence_SetSlice:int:i1:: -PySequence_SetSlice:int:i2:: -PySequence_SetSlice:PyObject*:v:+1: +PySequence_SetSlice:Py_ssize_t:i1:: +PySequence_SetSlice:Py_ssize_t:i2:: +PySequence_SetSlice:PyObject*:v:0: + +PySequence_Size:Py_ssize_t::: +PySequence_Size:PyObject*:o:0: PySequence_List:PyObject*::+1: PySequence_List:PyObject*:o:0: @@ -1314,9 +2002,15 @@ PySequence_List:PyObject*:o:0: PySequence_Tuple:PyObject*::+1: PySequence_Tuple:PyObject*:o:0: -PySet_Append:int::: -PySet_Append:PyObject*:set:0: -PySet_Append:PyObject*:key:+1: +PySet_Add:int::: +PySet_Add:PyObject*:set:0: +PySet_Add:PyObject*:key:+1: + +PySet_Check:int::: +PySet_Check:PyObject*:p:0: + +PySet_Clear:int::: +PySet_Clear:PyObject*:set:0: PySet_Contains:int::: PySet_Contains:PyObject*:anyset:0: @@ -1326,15 +2020,21 @@ PySet_Discard:int::: PySet_Discard:PyObject*:set:0: PySet_Discard:PyObject*:key:-1:no effect if key not found +PySet_GET_SIZE:Py_ssize_t::: +PySet_GET_SIZE:PyObject*:anyset:0: + PySet_New:PyObject*::+1: PySet_New:PyObject*:iterable:0: PySet_Pop:PyObject*::+1:or returns NULL and raises KeyError if set is empty PySet_Pop:PyObject*:set:0: -PySet_Size:int::: +PySet_Size:Py_ssize_t::: PySet_Size:PyObject*:anyset:0: +PySignal_SetWakeupFd:int::: +PySignal_SetWakeupFd:int:fd:: + PySlice_AdjustIndices:Py_ssize_t::: PySlice_AdjustIndices:Py_ssize_t:length:: PySlice_AdjustIndices:Py_ssize_t*:start:: @@ -1344,6 +2044,21 @@ PySlice_AdjustIndices:Py_ssize_t*:step:: PySlice_Check:int::: PySlice_Check:PyObject*:ob:0: +PySlice_GetIndices:int::: +PySlice_GetIndices:PyObject*:slice:0: +PySlice_GetIndices:Py_ssize_t:length:: +PySlice_GetIndices:Py_ssize_t*:start:: +PySlice_GetIndices:Py_ssize_t*:stop:: +PySlice_GetIndices:Py_ssize_t*:step:: + +PySlice_GetIndicesEx:int::: +PySlice_GetIndicesEx:PyObject*:slice:0: +PySlice_GetIndicesEx:Py_ssize_t:length:: +PySlice_GetIndicesEx:Py_ssize_t*:start:: +PySlice_GetIndicesEx:Py_ssize_t*:stop:: +PySlice_GetIndicesEx:Py_ssize_t*:step:: +PySlice_GetIndicesEx:Py_ssize_t*:slicelength:: + PySlice_New:PyObject*::+1: PySlice_New:PyObject*:start:0: PySlice_New:PyObject*:stop:0: @@ -1355,100 +2070,78 @@ PySlice_Unpack:Py_ssize_t*:start:: PySlice_Unpack:Py_ssize_t*:stop:: PySlice_Unpack:Py_ssize_t*:step:: -PyString_AS_STRING:const char*::: -PyString_AS_STRING:PyObject*:string:0: - -PyString_AsDecodedObject:PyObject*::+1: -PyString_AsDecodedObject:PyObject*:str:0: -PyString_AsDecodedObject:const char*:encoding:: -PyString_AsDecodedObject:const char*:errors:: +PyState_AddModule:int::: +PyState_AddModule:PyObject*:module:+1: +PyState_AddModule:PyModuleDef*:def:: -PyString_AsEncodedObject:PyObject*::+1: -PyString_AsEncodedObject:PyObject*:str:0: -PyString_AsEncodedObject:const char*:encoding:: -PyString_AsEncodedObject:const char*:errors:: +PyState_FindModule:PyObject*::0: +PyState_FindModule:PyModuleDef*:def:: -PyString_AsString:const char*::: -PyString_AsString:PyObject*:string:0: +PyState_RemoveModule:int::: +PyState_RemoveModule:PyModuleDef*:def:: -PyString_AsStringAndSize:int::: -PyString_AsStringAndSize:PyObject*:obj:0: -PyString_AsStringAndSize:char**:buffer:: -PyString_AsStringAndSize:int*:length:: +PyStructSequence_GET_ITEM:PyObject*::0: +PyStructSequence_GET_ITEM:PyObject*:p:0: +PyStructSequence_GET_ITEM:Py_ssize_t:pos:: -PyString_Check:int::: -PyString_Check:PyObject*:o:0: +PyStructSequence_GetItem:PyObject*::0: +PyStructSequence_GetItem:PyObject*:p:0: +PyStructSequence_GetItem:Py_ssize_t:pos:: -PyString_Concat:void::: -PyString_Concat:PyObject**:string:0:??? -- replaces w/ new string or NULL -PyString_Concat:PyObject*:newpart:0: +PyStructSequence_InitType:void::: +PyStructSequence_InitType:PyTypeObject*:type:+1: +PyStructSequence_InitType:PyStructSequence_Desc*:desc:: -PyString_ConcatAndDel:void::: -PyString_ConcatAndDel:PyObject**:string:0:??? -- replaces w/ new string or NULL -PyString_ConcatAndDel:PyObject*:newpart:-1: +PyStructSequence_InitType2:int::: +PyStructSequence_InitType2:PyTypeObject*:type:+1: +PyStructSequence_InitType2:PyStructSequence_Desc*:desc:: -PyString_Format:PyObject*::+1: -PyString_Format:PyObject*:format:0: -PyString_Format:PyObject*:args:0: +PyStructSequence_New:PyObject*::+1: +PyStructSequence_New:PyTypeObject*:type:0: -PyString_FromString:PyObject*::+1: -PyString_FromString:const char*:v:: +PyStructSequence_NewType:PyTypeObject*::+1: +PyStructSequence_NewType:PyStructSequence_Desc*:desc:: -PyString_FromStringAndSize:PyObject*::+1: -PyString_FromStringAndSize:const char*:v:: -PyString_FromStringAndSize:int:len:: +PyStructSequence_SET_ITEM:void::: +PyStructSequence_SET_ITEM:PyObject*:p:0: +PyStructSequence_SET_ITEM:Py_ssize_t*:pos:: +PyStructSequence_SET_ITEM:PyObject*:o:0: -PyString_FromFormat:PyObject*::+1: -PyString_FromFormat:const char*:format:: -PyString_FromFormat::...:: - -PyString_FromFormatV:PyObject*::+1: -PyString_FromFormatV:const char*:format:: -PyString_FromFormatV:va_list:vargs:: - -PyString_GET_SIZE:int::: -PyString_GET_SIZE:PyObject*:string:0: - -PyString_InternFromString:PyObject*::+1: -PyString_InternFromString:const char*:v:: - -PyString_InternInPlace:void::: -PyString_InternInPlace:PyObject**:string:+1:??? - -PyString_Size:int::: -PyString_Size:PyObject*:string:0: - -PyString_Decode:PyObject*::+1: -PyString_Decode:const char*:s:: -PyString_Decode:int:size:: -PyString_Decode:const char*:encoding:: -PyString_Decode:const char*:errors:: - -PyString_Encode:PyObject*::+1: -PyString_Encode:const char*:s:: -PyString_Encode:int:size:: -PyString_Encode:const char*:encoding:: -PyString_Encode:const char*:errors:: - -PyString_AsEncodedString:PyObject*::+1: -PyString_AsEncodedString:PyObject*:str:: -PyString_AsEncodedString:const char*:encoding:: -PyString_AsEncodedString:const char*:errors:: +PyStructSequence_SetItem:void::: +PyStructSequence_SetItem:PyObject*:p:0: +PyStructSequence_SetItem:Py_ssize_t:pos:: +PyStructSequence_SetItem:PyObject*:o:0: PySys_AddWarnOption:void::: -PySys_AddWarnOption:const char*:s:: +PySys_AddWarnOption:const wchar_t*:s:: + +PySys_AddWarnOptionUnicode:void::: +PySys_AddWarnOptionUnicode:PyObject*:unicode:0: PySys_AddXOption:void::: PySys_AddXOption:const wchar_t*:s:: +PySys_FormatStderr:void::: +PySys_FormatStderr:const char*:format:: +PySys_FormatStderr::...:: + +PySys_FormatStdout:void::: +PySys_FormatStdout:const char*:format:: +PySys_FormatStdout::...:: + PySys_GetObject:PyObject*::0: PySys_GetObject:const char*:name:: PySys_GetXOptions:PyObject*::0: -PySys_SetArgv:int::: +PySys_SetArgv:void::: PySys_SetArgv:int:argc:: -PySys_SetArgv:char**:argv:: +PySys_SetArgv:wchar_t**:argv:: + +PySys_SetArgvEx:void::: +PySys_SetArgvEx:int:argc:: +PySys_SetArgvEx:wchar_t**:argv:: +PySys_SetArgvEx:int:updatepath:: PySys_SetObject:int::: PySys_SetObject:const char*:name:: @@ -1458,9 +2151,11 @@ PySys_ResetWarnOptions:void::: PySys_WriteStdout:void::: PySys_WriteStdout:const char*:format:: +PySys_WriteStdout::...:: PySys_WriteStderr:void::: PySys_WriteStderr:const char*:format:: +PySys_WriteStderr::...:: PyThreadState_Clear:void::: PyThreadState_Clear:PyThreadState*:tstate:: @@ -1475,6 +2170,10 @@ PyThreadState_GetDict:PyObject*::0: PyThreadState_New:PyThreadState*::: PyThreadState_New:PyInterpreterState*:interp:: +PyThreadState_SetAsyncExc:int::: +PyThreadState_SetAsyncExc:unsigned long:id:: +PyThreadState_SetAsyncExc:PyObject*:exc:+1: + PyThreadState_Swap:PyThreadState*::: PyThreadState_Swap:PyThreadState*:tstate:: @@ -1499,6 +2198,12 @@ PyThread_tss_set:int::: PyThread_tss_set:Py_tss_t*:key:: PyThread_tss_set:void*:value:: +PyTime_Check:int::: +PyTime_Check:PyObject*:ob:0: + +PyTime_CheckExact:int::: +PyTime_CheckExact:PyObject*:ob:0: + PyTime_FromTime:PyObject*::+1: PyTime_FromTime:int:hour:: PyTime_FromTime:int:minute:: @@ -1517,63 +2222,130 @@ PyTraceMalloc_Untrack:uintptr_t:ptr:: PyTuple_Check:int::: PyTuple_Check:PyObject*:p:0: +PyTuple_CheckExact:int::: +PyTuple_CheckExact:PyObject*:p:0: + PyTuple_GET_ITEM:PyObject*::0: -PyTuple_GET_ITEM:PyTupleObject*:p:0: -PyTuple_GET_ITEM:int:pos:: +PyTuple_GET_ITEM:PyObject*:p:0: +PyTuple_GET_ITEM:Py_ssize_t:pos:: PyTuple_GetItem:PyObject*::0: -PyTuple_GetItem:PyTupleObject*:p:0: -PyTuple_GetItem:int:pos:: +PyTuple_GetItem:PyObject*:p:0: +PyTuple_GetItem:Py_ssize_t:pos:: + +PyTuple_GET_SIZE:Py_ssize_t::: +PyTuple_GET_SIZE:PyObject*:p:0: PyTuple_GetSlice:PyObject*::+1: -PyTuple_GetSlice:PyTupleObject*:p:0: -PyTuple_GetSlice:int:low:: -PyTuple_GetSlice:int:high:: +PyTuple_GetSlice:PyObject*:p:0: +PyTuple_GetSlice:Py_ssize_t:low:: +PyTuple_GetSlice:Py_ssize_t:high:: PyTuple_New:PyObject*::+1: -PyTuple_New:int:len:: +PyTuple_New:Py_ssize_t:len:: PyTuple_Pack:PyObject*::+1: -PyTuple_Pack:int:len:: +PyTuple_Pack:Py_ssize_t:len:: PyTuple_Pack:PyObject*:...:0: PyTuple_SET_ITEM:void::: -PyTuple_SET_ITEM:PyTupleObject*:p:0: -PyTuple_SET_ITEM:int:pos:: +PyTuple_SET_ITEM:PyObject*:p:0: +PyTuple_SET_ITEM:Py_ssize_t:pos:: PyTuple_SET_ITEM:PyObject*:o:0: PyTuple_SetItem:int::: -PyTuple_SetItem:PyTupleObject*:p:0: -PyTuple_SetItem:int:pos:: +PyTuple_SetItem:PyObject*:p:0: +PyTuple_SetItem:Py_ssize_t:pos:: PyTuple_SetItem:PyObject*:o:0: -PyTuple_Size:int::: -PyTuple_Size:PyTupleObject*:p:0: +PyTuple_Size:Py_ssize_t::: +PyTuple_Size:PyObject*:p:0: + +PyType_Check:int::: +PyType_Check:PyObject*:o:0: + +PyType_CheckExact:int::: +PyType_CheckExact:PyObject*:o:0: + +PyType_FromSpec:PyObject*::+1: +PyType_FromSpec:PyType_Spec*:spec:: + +PyType_FromSpecWithBases:PyObject*::+1: +PyType_FromSpecWithBases:PyType_Spec*:spec:: +PyType_FromSpecWithBases:PyObject*:bases:0: PyType_GenericAlloc:PyObject*::+1: PyType_GenericAlloc:PyObject*:type:0: -PyType_GenericAlloc:int:nitems:0: +PyType_GenericAlloc:Py_ssize_t:nitems:: PyType_GenericNew:PyObject*::+1: PyType_GenericNew:PyObject*:type:0: PyType_GenericNew:PyObject*:args:0: PyType_GenericNew:PyObject*:kwds:0: +PyType_GetFlags:unsigned long::: +PyType_GetFlags:PyTypeObject*:type:0: + +PyType_GetSlot:void*::: +PyType_GetSlot:PyTypeObject*:type:0: +PyType_GetSlot:int:slot:: + +PyType_HasFeature:int::: +PyType_HasFeature:PyTypeObject*:o:0: +PyType_HasFeature:int:feature:: + +PyType_IS_GC:int::: +PyType_IS_GC:PyTypeObject*:o:0: + +PyType_IsSubtype:int::: +PyType_IsSubtype:PyTypeObject*:a:0: +PyType_IsSubtype:PyTypeObject*:b:0: + +PyType_Modified:void::: +PyType_Modified:PyTypeObject*:type:0: + +PyType_Ready:int::: +PyType_Ready:PyTypeObject*:type:0: + +PyUnicode_1BYTE_DATA:Py_UCS1*::: +PyUnicode_1BYTE_DATA:PyObject*:o:0: + PyUnicode_Check:int::: PyUnicode_Check:PyObject*:o:0: -PyUnicode_GET_SIZE:int::: +PyUnicode_CheckExact:int::: +PyUnicode_CheckExact:PyObject*:o:0: + +PyUnicode_DATA:void*::: +PyUnicode_DATA:PyObject*:o:0: + +PyUnicode_GET_LENGTH:Py_ssize_t::: +PyUnicode_GET_LENGTH:PyObject*:o:0: + +PyUnicode_GET_SIZE:Py_ssize_t::: PyUnicode_GET_SIZE:PyObject*:o:0: -PyUnicode_GET_DATA_SIZE:int::: +PyUnicode_GET_DATA_SIZE:Py_ssize_t::: PyUnicode_GET_DATA_SIZE:PyObject*:o:0: +PyUnicode_KIND:int::: +PyUnicode_KIND:PyObject*:o:0: + +PyUnicode_MAX_CHAR_VALUE:::: +PyUnicode_MAX_CHAR_VALUE:PyObject*:o:0: + PyUnicode_AS_UNICODE:Py_UNICODE*::: PyUnicode_AS_UNICODE:PyObject*:o:0: PyUnicode_AS_DATA:const char*::: PyUnicode_AS_DATA:PyObject*:o:0: +Py_UNICODE_ISALNUM:int::: +Py_UNICODE_ISALNUM:Py_UNICODE:ch:: + +Py_UNICODE_ISALPHA:int::: +Py_UNICODE_ISALPHA:Py_UNICODE:ch:: + Py_UNICODE_ISSPACE:int::: Py_UNICODE_ISSPACE:Py_UNICODE:ch:: @@ -1598,6 +2370,9 @@ Py_UNICODE_ISDIGIT:Py_UNICODE:ch:: Py_UNICODE_ISNUMERIC:int::: Py_UNICODE_ISNUMERIC:Py_UNICODE:ch:: +Py_UNICODE_ISPRINTABLE:int::: +Py_UNICODE_ISPRINTABLE:Py_UNICODE:ch:: + Py_UNICODE_TOLOWER:Py_UNICODE::: Py_UNICODE_TOLOWER:Py_UNICODE:ch:: @@ -1638,10 +2413,10 @@ PyUnicode_GetSize:Py_ssize_t::: PyUnicode_GetSize:PyObject*:unicode:0: PyUnicode_FromObject:PyObject*::+1: -PyUnicode_FromObject:PyObject*:*obj:0: +PyUnicode_FromObject:PyObject*:obj:0: PyUnicode_FromEncodedObject:PyObject*::+1: -PyUnicode_FromEncodedObject:PyObject*:*obj:0: +PyUnicode_FromEncodedObject:PyObject*:obj:0: PyUnicode_FromEncodedObject:const char*:encoding:: PyUnicode_FromEncodedObject:const char*:errors:: @@ -1684,7 +2459,7 @@ PyUnicode_Encode:const char*:encoding:: PyUnicode_Encode:const char*:errors:: PyUnicode_AsEncodedString:PyObject*::+1: -PyUnicode_AsEncodedString:PyObject*:unicode:: +PyUnicode_AsEncodedString:PyObject*:unicode:0: PyUnicode_AsEncodedString:const char*:encoding:: PyUnicode_AsEncodedString:const char*:errors:: @@ -1717,7 +2492,7 @@ PyUnicode_EncodeUTF8:Py_ssize_t:size:: PyUnicode_EncodeUTF8:const char*:errors:: PyUnicode_AsUTF8String:PyObject*::+1: -PyUnicode_AsUTF8String:PyObject*:unicode:: +PyUnicode_AsUTF8String:PyObject*:unicode:0: PyUnicode_AsUTF8AndSize:const char*::: PyUnicode_AsUTF8AndSize:PyObject*:unicode:0: @@ -1739,7 +2514,7 @@ PyUnicode_EncodeUTF16:const char*:errors:: PyUnicode_EncodeUTF16:int:byteorder:: PyUnicode_AsUTF16String:PyObject*::+1: -PyUnicode_AsUTF16String:PyObject*:unicode:: +PyUnicode_AsUTF16String:PyObject*:unicode:0: PyUnicode_DecodeUTF32:PyObject*::+1: PyUnicode_DecodeUTF32:const char*:s:: @@ -1773,7 +2548,7 @@ PyUnicode_EncodeUnicodeEscape:const Py_UNICODE*:s:: PyUnicode_EncodeUnicodeEscape:Py_ssize_t:size:: PyUnicode_AsUnicodeEscapeString:PyObject*::+1: -PyUnicode_AsUnicodeEscapeString:PyObject*:unicode:: +PyUnicode_AsUnicodeEscapeString:PyObject*:unicode:0: PyUnicode_DecodeRawUnicodeEscape:PyObject*::+1: PyUnicode_DecodeRawUnicodeEscape:const char*:s:: @@ -1785,7 +2560,7 @@ PyUnicode_EncodeRawUnicodeEscape:const Py_UNICODE*:s:: PyUnicode_EncodeRawUnicodeEscape:Py_ssize_t:size:: PyUnicode_AsRawUnicodeEscapeString:PyObject*::+1: -PyUnicode_AsRawUnicodeEscapeString:PyObject*:unicode:: +PyUnicode_AsRawUnicodeEscapeString:PyObject*:unicode:0: PyUnicode_DecodeLatin1:PyObject*::+1: PyUnicode_DecodeLatin1:const char*:s:: @@ -1798,7 +2573,7 @@ PyUnicode_EncodeLatin1:Py_ssize_t:size:: PyUnicode_EncodeLatin1:const char*:errors:: PyUnicode_AsLatin1String:PyObject*::+1: -PyUnicode_AsLatin1String:PyObject*:unicode:: +PyUnicode_AsLatin1String:PyObject*:unicode:0: PyUnicode_DecodeASCII:PyObject*::+1: PyUnicode_DecodeASCII:const char*:s:: @@ -1811,7 +2586,7 @@ PyUnicode_EncodeASCII:Py_ssize_t:size:: PyUnicode_EncodeASCII:const char*:errors:: PyUnicode_AsASCIIString:PyObject*::+1: -PyUnicode_AsASCIIString:PyObject*:unicode:: +PyUnicode_AsASCIIString:PyObject*:unicode:0: PyUnicode_DecodeCharmap:PyObject*::+1: PyUnicode_DecodeCharmap:const char*:s:: @@ -1832,7 +2607,7 @@ PyUnicode_AsCharmapString:PyObject*:mapping:0: PyUnicode_TranslateCharmap:PyObject*::+1: PyUnicode_TranslateCharmap:const Py_UNICODE*:s:: PyUnicode_TranslateCharmap:Py_ssize_t:size:: -PyUnicode_TranslateCharmap:PyObject*:table:0: +PyUnicode_TranslateCharmap:PyObject*:mapping:0: PyUnicode_TranslateCharmap:const char*:errors:: PyUnicode_DecodeMBCS:PyObject*::+1: @@ -1840,7 +2615,7 @@ PyUnicode_DecodeMBCS:const char*:s:: PyUnicode_DecodeMBCS:Py_ssize_t:size:: PyUnicode_DecodeMBCS:const char*:errors:: -PyUnicode_DecodeMBCSStateful:PyObject::+1: +PyUnicode_DecodeMBCSStateful:PyObject*::+1: PyUnicode_DecodeMBCSStateful:const char*:s:: PyUnicode_DecodeMBCSStateful:Py_ssize_t:size:: PyUnicode_DecodeMBCSStateful:const char*:errors:: @@ -1857,7 +2632,7 @@ PyUnicode_EncodeMBCS:Py_ssize_t:size:: PyUnicode_EncodeMBCS:const char*:errors:: PyUnicode_AsMBCSString:PyObject*::+1: -PyUnicode_AsMBCSString:PyObject*:unicode:: +PyUnicode_AsMBCSString:PyObject*:unicode:0: PyUnicode_Concat:PyObject*::+1: PyUnicode_Concat:PyObject*:left:0: @@ -1922,7 +2697,7 @@ PyUnicode_CompareWithASCIIString:int::: PyUnicode_CompareWithASCIIString:PyObject*:uni:0: PyUnicode_CompareWithASCIIString:const char*:string:: -PyUnicode_RichCompare:PyObject::+1: +PyUnicode_RichCompare:PyObject*::+1: PyUnicode_RichCompare:PyObject*:left:0: PyUnicode_RichCompare:PyObject*:right:0: PyUnicode_RichCompare:int:op:: @@ -1939,7 +2714,7 @@ PyUnicode_InternInPlace:void::: PyUnicode_InternInPlace:PyObject**:string:+1: PyUnicode_InternFromString:PyObject*::+1: -PyUnicode_InternFromString:const char*:cp:: +PyUnicode_InternFromString:const char*:v:: PyUnicode_New:PyObject*::+1: PyUnicode_New:Py_ssize_t:size:: @@ -1977,19 +2752,37 @@ PyUnicode_CopyCharacters:Py_ssize_t:how_many:: PyUnicode_Fill:Py_ssize_t::: PyUnicode_Fill:PyObject*:unicode:0: -PyUnicode_Fill:Py_ssize_t:index:: +PyUnicode_Fill:Py_ssize_t:start:: PyUnicode_Fill:Py_ssize_t:length:: PyUnicode_Fill:Py_UCS4:fill_char:: +PyUnicode_READ:Py_UCS4::: +PyUnicode_READ:int:kind:: +PyUnicode_READ:void*:data:: +PyUnicode_READ:Py_ssize_t:index:: + +PyUnicode_READ_CHAR:Py_UCS4::: +PyUnicode_READ_CHAR:PyObject*:o:0: +PyUnicode_READ_CHAR:Py_ssize_t:index:: + PyUnicode_ReadChar:Py_UCS4::: PyUnicode_ReadChar:PyObject*:unicode:0: PyUnicode_ReadChar:Py_ssize_t:index:: +PyUnicode_WRITE:void::: +PyUnicode_WRITE:int:kind:: +PyUnicode_WRITE:void*:data:: +PyUnicode_WRITE:Py_ssize_t:index:: +PyUnicode_WRITE:Py_UCS4:value:: + PyUnicode_WriteChar:int::: PyUnicode_WriteChar:PyObject*:unicode:0: PyUnicode_WriteChar:Py_ssize_t:index:: PyUnicode_WriteChar:Py_UCS4:character:: +PyUnicode_READY:int::: +PyUnicode_READY:PyObject*:o:0: + PyUnicode_Substring:PyObject*::+1: PyUnicode_Substring:PyObject*:str:0: PyUnicode_Substring:Py_ssize_t:start:: @@ -2029,11 +2822,72 @@ PyUnicode_DecodeFSDefaultAndSize:PyObject*::+1: PyUnicode_DecodeFSDefaultAndSize:const char*:s:: PyUnicode_DecodeFSDefaultAndSize:Py_ssize_t:size:: -PyUnicode_DecodeFSDefault:PyObject::+1: +PyUnicode_DecodeFSDefault:PyObject*::+1: PyUnicode_DecodeFSDefault:const char*:s:: -PyUnicode_EncodeFSDefault:PyObject::+1: -PyUnicode_EncodeFSDefault:PyObject:unicode:0: +PyUnicode_EncodeFSDefault:PyObject*::+1: +PyUnicode_EncodeFSDefault:PyObject*:unicode:0: + +PyUnicodeDecodeError_Create:PyObject*::+1: +PyUnicodeDecodeError_Create:const char*:encoding:: +PyUnicodeDecodeError_Create:const char*:object:: +PyUnicodeDecodeError_Create:Py_ssize_t:length:: +PyUnicodeDecodeError_Create:Py_ssize_t:start:: +PyUnicodeDecodeError_Create:Py_ssize_t:end:: +PyUnicodeDecodeError_Create:const char*:reason:: + +PyUnicodeDecodeError_GetEncoding:PyObject*::+1: +PyUnicodeDecodeError_GetEncoding:PyObject*:exc:0: + +PyUnicodeDecodeError_GetEnd:Py_ssize_t::: +PyUnicodeDecodeError_GetEnd:PyObject*:exc:0: +PyUnicodeDecodeError_GetEnd:Py_ssize_t*:end:: + +PyUnicodeDecodeError_GetObject:PyObject*::+1: +PyUnicodeDecodeError_GetObject:PyObject*:exc:0: + +PyUnicodeDecodeError_GetReason:PyObject*::+1: +PyUnicodeDecodeError_GetReason:PyObject*:exc:0: + +PyUnicodeDecodeError_GetStart:Py_ssize_t::: +PyUnicodeDecodeError_GetStart:PyObject*:exc:0: +PyUnicodeDecodeError_GetStart:Py_ssize_t*:start:: + +PyUnicodeDecodeError_SetEnd:int::: +PyUnicodeDecodeError_SetEnd:PyObject*:exc:0: +PyUnicodeDecodeError_SetEnd:Py_ssize_t:end:: + +PyUnicodeDecodeError_SetReason:int::: +PyUnicodeDecodeError_SetReason:PyObject*:exc:0: +PyUnicodeDecodeError_SetReason:const char*:reason:: + +PyUnicodeDecodeError_SetStart:int::: +PyUnicodeDecodeError_SetStart:PyObject*:exc:0: +PyUnicodeDecodeError_SetStart:Py_ssize_t:start:: + +PyUnicodeEncodeError_Create:PyObject*::+1: +PyUnicodeEncodeError_Create:const char*:encoding:: +PyUnicodeEncodeError_Create:const Py_UNICODE*:object:: +PyUnicodeEncodeError_Create:Py_ssize_t:length:: +PyUnicodeEncodeError_Create:Py_ssize_t:start:: +PyUnicodeEncodeError_Create:Py_ssize_t:end:: +PyUnicodeEncodeError_Create:const char*:reason:: + +PyUnicodeTranslateError_Create:PyObject*::+1: +PyUnicodeTranslateError_Create:const Py_UNICODE*:object:: +PyUnicodeTranslateError_Create:Py_ssize_t:length:: +PyUnicodeTranslateError_Create:Py_ssize_t:start:: +PyUnicodeTranslateError_Create:Py_ssize_t:end:: +PyUnicodeTranslateError_Create:const char*:reason:: + +PyWeakref_Check:int::: +PyWeakref_Check:PyObject*:ob:: + +PyWeakref_CheckProxy:int::: +PyWeakref_CheckProxy:PyObject*:ob:: + +PyWeakref_CheckRef:int::: +PyWeakref_CheckRef:PyObject*:ob:: PyWeakref_GET_OBJECT:PyObject*::0: PyWeakref_GET_OBJECT:PyObject*:ref:0: @@ -2058,18 +2912,40 @@ Py_AtExit:void (*)():func:: Py_BuildValue:PyObject*::+1: Py_BuildValue:const char*:format:: +Py_BuildValue::...:: + +Py_VaBuildValue:PyObject*::+1: +Py_VaBuildValue:const char*:format:: +Py_VaBuildValue:va_list:vargs:: + +Py_CLEAR:void::: +Py_CLEAR:PyObject*:o:-1: Py_CompileString:PyObject*::+1: Py_CompileString:const char*:str:: Py_CompileString:const char*:filename:: Py_CompileString:int:start:: +Py_CompileStringExFlags:PyObject*::+1: +Py_CompileStringExFlags:const char*:str:: +Py_CompileStringExFlags:const char*:filename:: +Py_CompileStringExFlags:int:start:: +Py_CompileStringExFlags:PyCompilerFlags*:flags:: +Py_CompileStringExFlags:int:optimize:: + Py_CompileStringFlags:PyObject*::+1: Py_CompileStringFlags:const char*:str:: Py_CompileStringFlags:const char*:filename:: Py_CompileStringFlags:int:start:: Py_CompileStringFlags:PyCompilerFlags*:flags:: +Py_CompileStringObject:PyObject*::+1: +Py_CompileStringObject:const char*:str:: +Py_CompileStringObject:PyObject*:filename:0: +Py_CompileStringObject:int:start:: +Py_CompileStringObject:PyCompilerFlags*:flags:: +Py_CompileStringObject:int:optimize:: + Py_DECREF:void::: Py_DECREF:PyObject*:o:-1: @@ -2088,25 +2964,25 @@ Py_FdIsInteractive:const char*:filename:: Py_Finalize:void::: -Py_GetBuildInfoconst:const char*::: +Py_GetBuildInfo:const char*::: -Py_GetCompilerconst:const char*::: +Py_GetCompiler:const char*::: -Py_GetCopyrightconst:const char*::: +Py_GetCopyright:const char*::: -Py_GetExecPrefix:const char*::: +Py_GetExecPrefix:wchar_t*::: -Py_GetPath:const char*::: +Py_GetPath:wchar_t*::: -Py_GetPlatformconst:const char*::: +Py_GetPlatform:const char*::: -Py_GetPrefix:const char*::: +Py_GetPrefix:wchar_t*::: -Py_GetProgramFullPath:const char*::: +Py_GetProgramFullPath:wchar_t*::: -Py_GetProgramName:const char*::: +Py_GetProgramName:wchar_t*::: -Py_GetVersionconst:const char*::: +Py_GetVersion:const char*::: Py_INCREF:void::: Py_INCREF:PyObject*:o:+1: @@ -2117,8 +2993,14 @@ Py_IsInitialized:int::: Py_NewInterpreter:PyThreadState*::: +Py_ReprEnter:int::: +Py_ReprEnter:PyObject*:object:+1: + +Py_ReprLeave:void::: +Py_ReprLeave:PyObject*:object:-1: + Py_SetProgramName:void::: -Py_SetProgramName:const char*:name:: +Py_SetProgramName:const wchar_t*:name:: Py_XDECREF:void::: Py_XDECREF:PyObject*:o:-1:if o is not NULL @@ -2126,32 +3008,24 @@ Py_XDECREF:PyObject*:o:-1:if o is not NULL Py_XINCREF:void::: Py_XINCREF:PyObject*:o:+1:if o is not NULL -_PyImport_FindExtension:PyObject*::0:??? see PyImport_AddModule -_PyImport_FindExtension:const char*::: -_PyImport_FindExtension:const char*::: - _PyImport_Fini:void::: -_PyImport_FixupExtension:PyObject*:::??? -_PyImport_FixupExtension:const char*::: -_PyImport_FixupExtension:const char*::: - _PyImport_Init:void::: _PyObject_New:PyObject*::+1: _PyObject_New:PyTypeObject*:type:0: -_PyObject_NewVar:PyObject*::+1: +_PyObject_NewVar:PyVarObject*::+1: _PyObject_NewVar:PyTypeObject*:type:0: -_PyObject_NewVar:int:size:: +_PyObject_NewVar:Py_ssize_t:size:: -_PyString_Resize:int::: -_PyString_Resize:PyObject**:string:+1: -_PyString_Resize:int:newsize:: +_PyBytes_Resize:int::: +_PyBytes_Resize:PyObject**:bytes:0: +_PyBytes_Resize:Py_ssize_t:newsize:: _PyTuple_Resize:int::: -_PyTuple_Resize:PyTupleObject**:p:+1: -_PyTuple_Resize:int:new:: +_PyTuple_Resize:PyObject**:p:0: +_PyTuple_Resize:Py_ssize_t:new:: _Py_c_diff:Py_complex::: _Py_c_diff:Py_complex:left:: diff --git a/Doc/tools/extensions/c_annotations.py b/Doc/tools/extensions/c_annotations.py index baa39f3b4464..fa8244a8fd31 100644 --- a/Doc/tools/extensions/c_annotations.py +++ b/Doc/tools/extensions/c_annotations.py @@ -87,7 +87,7 @@ def add_annotations(self, app, doctree): entry = self.get(name) if not entry: continue - elif entry.result_type not in ("PyObject*", "PyVarObject*"): + elif not entry.result_type.endswith("Object*"): continue if entry.result_refs is None: rc = 'Return value: Always NULL.' From webhook-mailer at python.org Thu Dec 20 02:53:01 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 20 Dec 2018 07:53:01 -0000 Subject: [Python-checkins] bpo-5438: Update memory requirements and optimize test_bigmem.py. (GH-11123) Message-ID: https://github.com/python/cpython/commit/a26201cd8ef17dc81431f768846291c9f4337550 commit: a26201cd8ef17dc81431f768846291c9f4337550 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-19T23:52:57-08:00 summary: bpo-5438: Update memory requirements and optimize test_bigmem.py. (GH-11123) (cherry picked from commit b13a20f50789e153c18ed8efb4fbc5eecc50f2cd) Co-authored-by: Serhiy Storchaka files: M Lib/test/test_bigmem.py diff --git a/Lib/test/test_bigmem.py b/Lib/test/test_bigmem.py index 6133bbcac52a..6a244dd8c948 100644 --- a/Lib/test/test_bigmem.py +++ b/Lib/test/test_bigmem.py @@ -64,6 +64,7 @@ ascii_char_size = 1 ucs2_char_size = 2 ucs4_char_size = 4 +pointer_size = 4 if sys.maxsize < 2**32 else 8 class BaseStrTest: @@ -372,7 +373,7 @@ def test_split_small(self, size): # suffer for the list size. (Otherwise, it'd cost another 48 times # size in bytes!) Nevertheless, a list of size takes # 8*size bytes. - @bigmemtest(size=_2G + 5, memuse=2 * ascii_char_size + 8) + @bigmemtest(size=_2G + 5, memuse=ascii_char_size * 2 + pointer_size) def test_split_large(self, size): _ = self.from_latin1 s = _(' a') * size + _(' ') @@ -604,15 +605,15 @@ def tearDown(self): for name, memuse in self._adjusted.items(): getattr(type(self), name).memuse = memuse - @bigmemtest(size=_2G, memuse=ucs4_char_size * 3) + @bigmemtest(size=_2G, memuse=ucs4_char_size * 3 + ascii_char_size * 2) def test_capitalize(self, size): self._test_capitalize(size) - @bigmemtest(size=_2G, memuse=ucs4_char_size * 3) + @bigmemtest(size=_2G, memuse=ucs4_char_size * 3 + ascii_char_size * 2) def test_title(self, size): self._test_title(size) - @bigmemtest(size=_2G, memuse=ucs4_char_size * 3) + @bigmemtest(size=_2G, memuse=ucs4_char_size * 3 + ascii_char_size * 2) def test_swapcase(self, size): self._test_swapcase(size) @@ -630,7 +631,7 @@ def test_encode_raw_unicode_escape(self, size): except MemoryError: pass # acceptable on 32-bit - @bigmemtest(size=_4G // 5 + 70, memuse=ascii_char_size + ucs4_char_size + 1) + @bigmemtest(size=_4G // 5 + 70, memuse=ascii_char_size + 8 + 1) def test_encode_utf7(self, size): try: return self.basic_encode_test(size, 'utf7') @@ -820,7 +821,7 @@ class TupleTest(unittest.TestCase): # having more than 2<<31 references to any given object. Hence the # use of different types of objects as contents in different tests. - @bigmemtest(size=_2G + 2, memuse=16) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2) def test_compare(self, size): t1 = ('',) * size t2 = ('',) * size @@ -843,15 +844,15 @@ def basic_concat_test(self, size): t = t + t self.assertEqual(len(t), size * 2) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 3) def test_concat_small(self, size): return self.basic_concat_test(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 3) def test_concat_large(self, size): return self.basic_concat_test(size) - @bigmemtest(size=_2G // 5 + 10, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 10, memuse=pointer_size * 5) def test_contains(self, size): t = (1, 2, 3, 4, 5) * size self.assertEqual(len(t), size * 5) @@ -859,7 +860,7 @@ def test_contains(self, size): self.assertFalse((1, 2, 3, 4, 5) in t) self.assertFalse(0 in t) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_hash(self, size): t1 = (0,) * size h1 = hash(t1) @@ -867,7 +868,7 @@ def test_hash(self, size): t2 = (0,) * (size + 1) self.assertFalse(h1 == hash(t2)) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_index_and_slice(self, size): t = (None,) * size self.assertEqual(len(t), size) @@ -892,11 +893,11 @@ def basic_test_repeat(self, size): t = t * 2 self.assertEqual(len(t), size * 2) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 3) def test_repeat_small(self, size): return self.basic_test_repeat(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 3) def test_repeat_large(self, size): return self.basic_test_repeat(size) @@ -904,48 +905,42 @@ def test_repeat_large(self, size): def test_repeat_large_2(self, size): return self.basic_test_repeat(size) - @bigmemtest(size=_1G - 1, memuse=9) + @bigmemtest(size=_1G - 1, memuse=pointer_size * 2) def test_from_2G_generator(self, size): - self.skipTest("test needs much more memory than advertised, see issue5438") try: - t = tuple(range(size)) + t = tuple(iter([42]*size)) except MemoryError: pass # acceptable on 32-bit else: - count = 0 - for item in t: - self.assertEqual(item, count) - count += 1 - self.assertEqual(count, size) + self.assertEqual(len(t), size) + self.assertEqual(t[:10], (42,) * 10) + self.assertEqual(t[-10:], (42,) * 10) - @bigmemtest(size=_1G - 25, memuse=9) + @bigmemtest(size=_1G - 25, memuse=pointer_size * 2) def test_from_almost_2G_generator(self, size): - self.skipTest("test needs much more memory than advertised, see issue5438") try: - t = tuple(range(size)) - count = 0 - for item in t: - self.assertEqual(item, count) - count += 1 - self.assertEqual(count, size) + t = tuple(iter([42]*size)) except MemoryError: - pass # acceptable, expected on 32-bit + pass # acceptable on 32-bit + else: + self.assertEqual(len(t), size) + self.assertEqual(t[:10], (42,) * 10) + self.assertEqual(t[-10:], (42,) * 10) # Like test_concat, split in two. def basic_test_repr(self, size): - t = (0,) * size + t = (False,) * size s = repr(t) - # The repr of a tuple of 0's is exactly three times the tuple length. - self.assertEqual(len(s), size * 3) - self.assertEqual(s[:5], '(0, 0') - self.assertEqual(s[-5:], '0, 0)') - self.assertEqual(s.count('0'), size) + # The repr of a tuple of Falses is exactly 7 times the tuple length. + self.assertEqual(len(s), size * 7) + self.assertEqual(s[:10], '(False, Fa') + self.assertEqual(s[-10:], 'se, False)') - @bigmemtest(size=_2G // 3 + 2, memuse=8 + 3 * ascii_char_size) + @bigmemtest(size=_2G // 7 + 2, memuse=pointer_size + ascii_char_size * 7) def test_repr_small(self, size): return self.basic_test_repr(size) - @bigmemtest(size=_2G + 2, memuse=8 + 3 * ascii_char_size) + @bigmemtest(size=_2G + 2, memuse=pointer_size + ascii_char_size * 7) def test_repr_large(self, size): return self.basic_test_repr(size) @@ -956,7 +951,7 @@ class ListTest(unittest.TestCase): # lists hold references to various objects to test their refcount # limits. - @bigmemtest(size=_2G + 2, memuse=16) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2) def test_compare(self, size): l1 = [''] * size l2 = [''] * size @@ -979,14 +974,16 @@ def basic_test_concat(self, size): l = l + l self.assertEqual(len(l), size * 2) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 3) def test_concat_small(self, size): return self.basic_test_concat(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 3) def test_concat_large(self, size): return self.basic_test_concat(size) + # XXX This tests suffers from overallocation, just like test_append. + # This should be fixed in future. def basic_test_inplace_concat(self, size): l = [sys.stdout] * size l += l @@ -994,15 +991,15 @@ def basic_test_inplace_concat(self, size): self.assertTrue(l[0] is l[-1]) self.assertTrue(l[size - 1] is l[size + 1]) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 2 * 9/8) def test_inplace_concat_small(self, size): return self.basic_test_inplace_concat(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2 * 9/8) def test_inplace_concat_large(self, size): return self.basic_test_inplace_concat(size) - @bigmemtest(size=_2G // 5 + 10, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 10, memuse=pointer_size * 5) def test_contains(self, size): l = [1, 2, 3, 4, 5] * size self.assertEqual(len(l), size * 5) @@ -1010,12 +1007,12 @@ def test_contains(self, size): self.assertFalse([1, 2, 3, 4, 5] in l) self.assertFalse(0 in l) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_hash(self, size): l = [0] * size self.assertRaises(TypeError, hash, l) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_index_and_slice(self, size): l = [None] * size self.assertEqual(len(l), size) @@ -1079,14 +1076,16 @@ def basic_test_repeat(self, size): l = l * 2 self.assertEqual(len(l), size * 2) - @bigmemtest(size=_2G // 2 + 2, memuse=24) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 3) def test_repeat_small(self, size): return self.basic_test_repeat(size) - @bigmemtest(size=_2G + 2, memuse=24) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 3) def test_repeat_large(self, size): return self.basic_test_repeat(size) + # XXX This tests suffers from overallocation, just like test_append. + # This should be fixed in future. def basic_test_inplace_repeat(self, size): l = [''] l *= size @@ -1099,34 +1098,34 @@ def basic_test_inplace_repeat(self, size): self.assertEqual(len(l), size * 2) self.assertTrue(l[size - 1] is l[-1]) - @bigmemtest(size=_2G // 2 + 2, memuse=16) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 2 * 9/8) def test_inplace_repeat_small(self, size): return self.basic_test_inplace_repeat(size) - @bigmemtest(size=_2G + 2, memuse=16) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2 * 9/8) def test_inplace_repeat_large(self, size): return self.basic_test_inplace_repeat(size) def basic_test_repr(self, size): - l = [0] * size + l = [False] * size s = repr(l) - # The repr of a list of 0's is exactly three times the list length. - self.assertEqual(len(s), size * 3) - self.assertEqual(s[:5], '[0, 0') - self.assertEqual(s[-5:], '0, 0]') - self.assertEqual(s.count('0'), size) + # The repr of a list of Falses is exactly 7 times the list length. + self.assertEqual(len(s), size * 7) + self.assertEqual(s[:10], '[False, Fa') + self.assertEqual(s[-10:], 'se, False]') + self.assertEqual(s.count('F'), size) - @bigmemtest(size=_2G // 3 + 2, memuse=8 + 3 * ascii_char_size) + @bigmemtest(size=_2G // 7 + 2, memuse=pointer_size + ascii_char_size * 7) def test_repr_small(self, size): return self.basic_test_repr(size) - @bigmemtest(size=_2G + 2, memuse=8 + 3 * ascii_char_size) + @bigmemtest(size=_2G + 2, memuse=pointer_size + ascii_char_size * 7) def test_repr_large(self, size): return self.basic_test_repr(size) # list overallocates ~1/8th of the total size (on first expansion) so # the single list.append call puts memuse at 9 bytes per size. - @bigmemtest(size=_2G, memuse=9) + @bigmemtest(size=_2G, memuse=pointer_size * 9/8) def test_append(self, size): l = [object()] * size l.append(object()) @@ -1134,12 +1133,14 @@ def test_append(self, size): self.assertTrue(l[-3] is l[-2]) self.assertFalse(l[-2] is l[-1]) - @bigmemtest(size=_2G // 5 + 2, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 2, memuse=pointer_size * 5) def test_count(self, size): l = [1, 2, 3, 4, 5] * size self.assertEqual(l.count(1), size) self.assertEqual(l.count("1"), 0) + # XXX This tests suffers from overallocation, just like test_append. + # This should be fixed in future. def basic_test_extend(self, size): l = [object] * size l.extend(l) @@ -1147,15 +1148,15 @@ def basic_test_extend(self, size): self.assertTrue(l[0] is l[-1]) self.assertTrue(l[size - 1] is l[size + 1]) - @bigmemtest(size=_2G // 2 + 2, memuse=16) + @bigmemtest(size=_2G // 2 + 2, memuse=pointer_size * 2 * 9/8) def test_extend_small(self, size): return self.basic_test_extend(size) - @bigmemtest(size=_2G + 2, memuse=16) + @bigmemtest(size=_2G + 2, memuse=pointer_size * 2 * 9/8) def test_extend_large(self, size): return self.basic_test_extend(size) - @bigmemtest(size=_2G // 5 + 2, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 2, memuse=pointer_size * 5) def test_index(self, size): l = [1, 2, 3, 4, 5] * size size *= 5 @@ -1166,7 +1167,7 @@ def test_index(self, size): self.assertRaises(ValueError, l.index, 6) # This tests suffers from overallocation, just like test_append. - @bigmemtest(size=_2G + 10, memuse=9) + @bigmemtest(size=_2G + 10, memuse=pointer_size * 9/8) def test_insert(self, size): l = [1.0] * size l.insert(size - 1, "A") @@ -1185,7 +1186,7 @@ def test_insert(self, size): self.assertEqual(l[:3], [1.0, "C", 1.0]) self.assertEqual(l[size - 3:], ["A", 1.0, "B"]) - @bigmemtest(size=_2G // 5 + 4, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 4, memuse=pointer_size * 5) def test_pop(self, size): l = ["a", "b", "c", "d", "e"] * size size *= 5 @@ -1209,7 +1210,7 @@ def test_pop(self, size): self.assertEqual(item, "c") self.assertEqual(l[-2:], ["b", "d"]) - @bigmemtest(size=_2G + 10, memuse=8) + @bigmemtest(size=_2G + 10, memuse=pointer_size) def test_remove(self, size): l = [10] * size self.assertEqual(len(l), size) @@ -1229,7 +1230,7 @@ def test_remove(self, size): self.assertEqual(len(l), size) self.assertEqual(l[-2:], [10, 10]) - @bigmemtest(size=_2G // 5 + 2, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 2, memuse=pointer_size * 5) def test_reverse(self, size): l = [1, 2, 3, 4, 5] * size l.reverse() @@ -1237,7 +1238,7 @@ def test_reverse(self, size): self.assertEqual(l[-5:], [5, 4, 3, 2, 1]) self.assertEqual(l[:5], [5, 4, 3, 2, 1]) - @bigmemtest(size=_2G // 5 + 2, memuse=8 * 5) + @bigmemtest(size=_2G // 5 + 2, memuse=pointer_size * 5 * 1.5) def test_sort(self, size): l = [1, 2, 3, 4, 5] * size l.sort() From webhook-mailer at python.org Thu Dec 20 03:29:46 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 20 Dec 2018 08:29:46 -0000 Subject: [Python-checkins] bpo-35529: Fix a reference counting bug in PyCFuncPtr_FromDll(). (GH-11229) Message-ID: https://github.com/python/cpython/commit/d77d97c9a1f593fe161afab97e2a3e2292ab88b9 commit: d77d97c9a1f593fe161afab97e2a3e2292ab88b9 branch: master author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-20T10:29:38+02:00 summary: bpo-35529: Fix a reference counting bug in PyCFuncPtr_FromDll(). (GH-11229) "dll" would leak if an error occurred in _validate_paramflags() or GenericPyCData_new(). files: M Modules/_ctypes/_ctypes.c diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 637be4222d98..cc4aab7389b1 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3483,20 +3483,23 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } #endif - Py_INCREF(dll); /* for KeepRef */ - Py_DECREF(ftuple); - if (!_validate_paramflags(type, paramflags)) + if (!_validate_paramflags(type, paramflags)) { + Py_DECREF(ftuple); return NULL; + } self = (PyCFuncPtrObject *)GenericPyCData_new(type, args, kwds); - if (!self) + if (!self) { + Py_DECREF(ftuple); return NULL; + } Py_XINCREF(paramflags); self->paramflags = paramflags; *(void **)self->b_ptr = address; - + Py_INCREF(dll); + Py_DECREF(ftuple); if (-1 == KeepRef((CDataObject *)self, 0, dll)) { Py_DECREF((PyObject *)self); return NULL; From webhook-mailer at python.org Thu Dec 20 03:48:07 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 20 Dec 2018 08:48:07 -0000 Subject: [Python-checkins] bpo-35529: Fix a reference counting bug in PyCFuncPtr_FromDll(). (GH-11229) Message-ID: https://github.com/python/cpython/commit/4b6caaca41def86d80819f1f93c647918e98393f commit: 4b6caaca41def86d80819f1f93c647918e98393f branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-20T00:47:59-08:00 summary: bpo-35529: Fix a reference counting bug in PyCFuncPtr_FromDll(). (GH-11229) "dll" would leak if an error occurred in _validate_paramflags() or GenericPyCData_new(). (cherry picked from commit d77d97c9a1f593fe161afab97e2a3e2292ab88b9) Co-authored-by: Zackery Spytz files: M Modules/_ctypes/_ctypes.c diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 937375a4ea44..f98eabbb370b 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3409,20 +3409,23 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } #endif - Py_INCREF(dll); /* for KeepRef */ - Py_DECREF(ftuple); - if (!_validate_paramflags(type, paramflags)) + if (!_validate_paramflags(type, paramflags)) { + Py_DECREF(ftuple); return NULL; + } self = (PyCFuncPtrObject *)GenericPyCData_new(type, args, kwds); - if (!self) + if (!self) { + Py_DECREF(ftuple); return NULL; + } Py_XINCREF(paramflags); self->paramflags = paramflags; *(void **)self->b_ptr = address; - + Py_INCREF(dll); + Py_DECREF(ftuple); if (-1 == KeepRef((CDataObject *)self, 0, dll)) { Py_DECREF((PyObject *)self); return NULL; From webhook-mailer at python.org Thu Dec 20 03:51:58 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 20 Dec 2018 08:51:58 -0000 Subject: [Python-checkins] bpo-35529: Fix a reference counting bug in PyCFuncPtr_FromDll(). (GH-11229) Message-ID: https://github.com/python/cpython/commit/3752bc96c0ea1ecf28903cc34cdcd75c658e92ce commit: 3752bc96c0ea1ecf28903cc34cdcd75c658e92ce branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-20T00:51:52-08:00 summary: bpo-35529: Fix a reference counting bug in PyCFuncPtr_FromDll(). (GH-11229) "dll" would leak if an error occurred in _validate_paramflags() or GenericPyCData_new(). (cherry picked from commit d77d97c9a1f593fe161afab97e2a3e2292ab88b9) Co-authored-by: Zackery Spytz files: M Modules/_ctypes/_ctypes.c diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 9b289af91be7..9aa252d6e265 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3526,20 +3526,23 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } #endif - Py_INCREF(dll); /* for KeepRef */ - Py_DECREF(ftuple); - if (!_validate_paramflags(type, paramflags)) + if (!_validate_paramflags(type, paramflags)) { + Py_DECREF(ftuple); return NULL; + } self = (PyCFuncPtrObject *)GenericPyCData_new(type, args, kwds); - if (!self) + if (!self) { + Py_DECREF(ftuple); return NULL; + } Py_XINCREF(paramflags); self->paramflags = paramflags; *(void **)self->b_ptr = address; - + Py_INCREF(dll); + Py_DECREF(ftuple); if (-1 == KeepRef((CDataObject *)self, 0, dll)) { Py_DECREF((PyObject *)self); return NULL; From solipsis at pitrou.net Thu Dec 20 04:09:13 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 20 Dec 2018 09:09:13 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=4 Message-ID: <20181220090913.1.D806526F32B4BA0B@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_functools leaked [0, 3, 1] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogMoy5yI', '--timeout', '7200'] From webhook-mailer at python.org Thu Dec 20 06:06:34 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Thu, 20 Dec 2018 11:06:34 -0000 Subject: [Python-checkins] bpo-35521: Add more cross-refs to IDLE docs (#11257) Message-ID: https://github.com/python/cpython/commit/292cd6e33104d9f458232a14998fe5c62f7f7e81 commit: 292cd6e33104d9f458232a14998fe5c62f7f7e81 branch: master author: Terry Jan Reedy committer: GitHub date: 2018-12-20T06:06:29-05:00 summary: bpo-35521: Add more cross-refs to IDLE docs (#11257) Format menu and preferences. files: M Doc/library/idle.rst M Lib/idlelib/help.html diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 449e54f067ec..80a2fdc4bf02 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -160,6 +160,8 @@ Show call tip Show surrounding parens Highlight the surrounding parenthesis. +.. _format-menu: + Format menu (Editor window only) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -268,14 +270,10 @@ 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 (see below). On macOS, - open the configuration dialog by selecting Preferences in the application - menu. To use a new built-in color theme (IDLE Dark) with older IDLEs, - save it as a new custom theme. - - Non-default user settings are saved in a .idlerc directory in the user's - home directory. Problems caused by bad user configuration files are solved - by editing or deleting one or more of the files in .idlerc. + size, additional help sources, and extensions. On macOS, open the + configuration dialog by selecting Preferences in the application + menu. For more, see + :ref:`Setting preferences ` under Help and preferences. Code Context (toggle)(Editor Window only) Open a pane at the top of the edit window which shows the block context @@ -432,7 +430,8 @@ to 4 spaces if they are there. :kbd:`Tab` inserts spaces (in the Python Shell window one tab), number depends on Indent width. Currently, tabs are restricted to four spaces due to Tcl/Tk limitations. -See also the indent/dedent region commands in the edit menu. +See also the indent/dedent region commands on the +:ref:`Format menu `. .. _completions: @@ -817,13 +816,21 @@ that will be opened instead. Selected URLs can be added or removed from the help menu at any time using the General tab of the Configure IDLE dialog . +.. _preferences: + Setting preferences ^^^^^^^^^^^^^^^^^^^ The font preferences, highlighting, keys, and general preferences can be -changed via Configure IDLE on the Option menu. Keys can be user defined; -IDLE ships with four built-in key sets. In addition, a user can create a -custom key set in the Configure IDLE dialog under the keys tab. +changed via Configure IDLE on the Option menu. +Non-default user settings are saved in a .idlerc directory in the user's +home directory. Problems caused by bad user configuration files are solved +by editing or deleting one or more of the files in .idlerc. + +On the Highlights and Keys tab, select a built-in or custom color theme +and key set. To use a newer built-in color theme or key set with older +IDLEs, save it as a new custom theme or key set and it well be accessible +to older IDLEs. IDLE on macOS ^^^^^^^^^^^^^ diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index b5f12d1a6f65..051690bc66a2 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -215,7 +215,7 @@

    Edit menu (Shell and Editor) -

    Format menu (Editor window only)?

    +

    Format menu (Editor window only)?

    Indent Region
    Shift selected lines right by the indent width (default 4 spaces).
    @@ -303,16 +303,12 @@

    Debug menu (Shell window only)?

    Configure IDLE
    -

    Open a configuration dialog and change preferences for the following: +

    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 (see below). On macOS, -open the configuration dialog by selecting Preferences in the application -menu. To use a new built-in color theme (IDLE Dark) with older IDLEs, -save it as a new custom theme.

    -

    Non-default user settings are saved in a .idlerc directory in the user?s -home directory. Problems caused by bad user configuration files are solved -by editing or deleting one or more of the files in .idlerc.

    -
    +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.
    Code Context (toggle)(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 @@ -443,7 +439,8 @@

    Automatic indentationTab inserts spaces (in the Python Shell window one tab), number depends on Indent width. Currently, tabs are restricted to four spaces due to Tcl/Tk limitations.

    -

    See also the indent/dedent region commands in the edit menu.

    +

    See also the indent/dedent region commands on the +Format menu.

    Completions?

    @@ -766,11 +763,16 @@

    Help and preferences

    -

    Setting preferences?

    +

    Setting preferences?

    The font preferences, highlighting, keys, and general preferences can be -changed via Configure IDLE on the Option menu. Keys can be user defined; -IDLE ships with four built-in key sets. In addition, a user can create a -custom key set in the Configure IDLE dialog under the keys tab.

    +changed via Configure IDLE on the Option menu. +Non-default user settings are saved in a .idlerc directory in the user?s +home directory. Problems caused by bad user configuration files are solved +by editing or deleting one or more of the files in .idlerc.

    +

    On the Highlights and Keys tab, select a built-in or custom color theme +and key set. To use a newer built-in color theme or key set with older +IDLEs, save it as a new custom theme or key set and it well be accessible +to older IDLEs.

    IDLE on macOS?

    @@ -916,7 +918,7 @@

    Navigation



    - Last updated on Dec 19, 2018. + Last updated on Dec 20, 2018. Found a bug?
    From webhook-mailer at python.org Thu Dec 20 06:25:12 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 20 Dec 2018 11:25:12 -0000 Subject: [Python-checkins] bpo-35521: Add more cross-refs to IDLE docs (GH-11257) Message-ID: https://github.com/python/cpython/commit/5d0498a6967d45042b7a9345f6d871047edbaa25 commit: 5d0498a6967d45042b7a9345f6d871047edbaa25 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-20T03:25:06-08:00 summary: bpo-35521: Add more cross-refs to IDLE docs (GH-11257) Format menu and preferences. (cherry picked from commit 292cd6e33104d9f458232a14998fe5c62f7f7e81) Co-authored-by: Terry Jan Reedy files: M Doc/library/idle.rst M Lib/idlelib/help.html diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 449e54f067ec..80a2fdc4bf02 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -160,6 +160,8 @@ Show call tip Show surrounding parens Highlight the surrounding parenthesis. +.. _format-menu: + Format menu (Editor window only) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -268,14 +270,10 @@ 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 (see below). On macOS, - open the configuration dialog by selecting Preferences in the application - menu. To use a new built-in color theme (IDLE Dark) with older IDLEs, - save it as a new custom theme. - - Non-default user settings are saved in a .idlerc directory in the user's - home directory. Problems caused by bad user configuration files are solved - by editing or deleting one or more of the files in .idlerc. + size, additional help sources, and extensions. On macOS, open the + configuration dialog by selecting Preferences in the application + menu. For more, see + :ref:`Setting preferences ` under Help and preferences. Code Context (toggle)(Editor Window only) Open a pane at the top of the edit window which shows the block context @@ -432,7 +430,8 @@ to 4 spaces if they are there. :kbd:`Tab` inserts spaces (in the Python Shell window one tab), number depends on Indent width. Currently, tabs are restricted to four spaces due to Tcl/Tk limitations. -See also the indent/dedent region commands in the edit menu. +See also the indent/dedent region commands on the +:ref:`Format menu `. .. _completions: @@ -817,13 +816,21 @@ that will be opened instead. Selected URLs can be added or removed from the help menu at any time using the General tab of the Configure IDLE dialog . +.. _preferences: + Setting preferences ^^^^^^^^^^^^^^^^^^^ The font preferences, highlighting, keys, and general preferences can be -changed via Configure IDLE on the Option menu. Keys can be user defined; -IDLE ships with four built-in key sets. In addition, a user can create a -custom key set in the Configure IDLE dialog under the keys tab. +changed via Configure IDLE on the Option menu. +Non-default user settings are saved in a .idlerc directory in the user's +home directory. Problems caused by bad user configuration files are solved +by editing or deleting one or more of the files in .idlerc. + +On the Highlights and Keys tab, select a built-in or custom color theme +and key set. To use a newer built-in color theme or key set with older +IDLEs, save it as a new custom theme or key set and it well be accessible +to older IDLEs. IDLE on macOS ^^^^^^^^^^^^^ diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index b5f12d1a6f65..051690bc66a2 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -215,7 +215,7 @@

    Edit menu (Shell and Editor) -

    Format menu (Editor window only)?

    +

    Format menu (Editor window only)?

    Indent Region
    Shift selected lines right by the indent width (default 4 spaces).
    @@ -303,16 +303,12 @@

    Debug menu (Shell window only)?

    Configure IDLE
    -

    Open a configuration dialog and change preferences for the following: +

    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 (see below). On macOS, -open the configuration dialog by selecting Preferences in the application -menu. To use a new built-in color theme (IDLE Dark) with older IDLEs, -save it as a new custom theme.

    -

    Non-default user settings are saved in a .idlerc directory in the user?s -home directory. Problems caused by bad user configuration files are solved -by editing or deleting one or more of the files in .idlerc.

    -
    +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.
    Code Context (toggle)(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 @@ -443,7 +439,8 @@

    Automatic indentationTab inserts spaces (in the Python Shell window one tab), number depends on Indent width. Currently, tabs are restricted to four spaces due to Tcl/Tk limitations.

    -

    See also the indent/dedent region commands in the edit menu.

    +

    See also the indent/dedent region commands on the +Format menu.

    Completions?

    @@ -766,11 +763,16 @@

    Help and preferences

    -

    Setting preferences?

    +

    Setting preferences?

    The font preferences, highlighting, keys, and general preferences can be -changed via Configure IDLE on the Option menu. Keys can be user defined; -IDLE ships with four built-in key sets. In addition, a user can create a -custom key set in the Configure IDLE dialog under the keys tab.

    +changed via Configure IDLE on the Option menu. +Non-default user settings are saved in a .idlerc directory in the user?s +home directory. Problems caused by bad user configuration files are solved +by editing or deleting one or more of the files in .idlerc.

    +

    On the Highlights and Keys tab, select a built-in or custom color theme +and key set. To use a newer built-in color theme or key set with older +IDLEs, save it as a new custom theme or key set and it well be accessible +to older IDLEs.

    IDLE on macOS?

    @@ -916,7 +918,7 @@

    Navigation



    - Last updated on Dec 19, 2018. + Last updated on Dec 20, 2018. Found a bug?
    From webhook-mailer at python.org Thu Dec 20 10:03:06 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 20 Dec 2018 15:03:06 -0000 Subject: [Python-checkins] bpo-35257: Avoid leaking LTO linker flags into distutils (GH-10900) (GH-11264) Message-ID: https://github.com/python/cpython/commit/0198f52ea2328fd932622ad2299381f617a041f2 commit: 0198f52ea2328fd932622ad2299381f617a041f2 branch: 3.7 author: Victor Stinner committer: GitHub date: 2018-12-20T16:03:01+01:00 summary: bpo-35257: Avoid leaking LTO linker flags into distutils (GH-10900) (GH-11264) When compiling 3rd party C extensions, the linker flags used by the compiler for the interpreter and the stdlib modules, will get leaked into distutils. In order to avoid that, the PY_CORE_LDFLAGS and PY_LDFLAGS_NODIST are introduced to keep those flags separated. (cherry picked from commit cf10a750f4b50b6775719cfb17bee00bc3a9c60b) files: A Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst M Lib/_osx_support.py M Lib/test/pythoninfo.py M Lib/test/test__osx_support.py M Makefile.pre.in M configure M configure.ac M setup.py diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py index e37852e2536c..db6674ea293f 100644 --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -17,7 +17,7 @@ _UNIVERSAL_CONFIG_VARS = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', - 'PY_CORE_CFLAGS') + 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS') # configuration variables that may contain compiler calls _COMPILER_CONFIG_VARS = ('BLDSHARED', 'LDSHARED', 'CC', 'CXX') diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 9befd12e4095..085c45d9cc04 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -407,7 +407,10 @@ def collect_sysconfig(info_add): 'OPT', 'PY_CFLAGS', 'PY_CFLAGS_NODIST', + 'PY_CORE_LDFLAGS', 'PY_LDFLAGS', + 'PY_LDFLAGS_NODIST', + 'PY_STDMODULE_CFLAGS', 'Py_DEBUG', 'Py_ENABLE_SHARED', 'SHELL', diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py index bcba8caa2964..388a2b1a84b1 100644 --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -24,7 +24,7 @@ def setUp(self): for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', - 'PY_CORE_CFLAGS'): + 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS'): if cv in self.env: self.env.unset(cv) diff --git a/Makefile.pre.in b/Makefile.pre.in index 4ec2d3511fc4..d727288db823 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -85,6 +85,10 @@ CONFIGURE_CFLAGS= @CFLAGS@ # Use it when a compiler flag should _not_ be part of the distutils CFLAGS # once Python is installed (Issue #21121). CONFIGURE_CFLAGS_NODIST=@CFLAGS_NODIST@ +# LDFLAGS_NODIST is used in the same manner as CFLAGS_NODIST. +# Use it when a linker flag should _not_ be part of the distutils LDFLAGS +# once Python is installed (bpo-35257) +CONFIGURE_LDFLAGS_NODIST=@LDFLAGS_NODIST@ CONFIGURE_CPPFLAGS= @CPPFLAGS@ CONFIGURE_LDFLAGS= @LDFLAGS@ # Avoid assigning CFLAGS, LDFLAGS, etc. so users can use them on the @@ -97,6 +101,7 @@ PY_CFLAGS_NODIST=$(CONFIGURE_CFLAGS_NODIST) $(CFLAGS_NODIST) # environment variables PY_CPPFLAGS= $(BASECPPFLAGS) -I. -I$(srcdir)/Include $(CONFIGURE_CPPFLAGS) $(CPPFLAGS) PY_LDFLAGS= $(CONFIGURE_LDFLAGS) $(LDFLAGS) +PY_LDFLAGS_NODIST=$(CONFIGURE_LDFLAGS_NODIST) $(LDFLAGS_NODIST) NO_AS_NEEDED= @NO_AS_NEEDED@ LDLAST= @LDLAST@ SGI_ABI= @SGI_ABI@ @@ -109,6 +114,8 @@ CFLAGSFORSHARED=@CFLAGSFORSHARED@ PY_STDMODULE_CFLAGS= $(PY_CFLAGS) $(PY_CFLAGS_NODIST) $(PY_CPPFLAGS) $(CFLAGSFORSHARED) PY_BUILTIN_MODULE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE_BUILTIN PY_CORE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE +# Linker flags used for building the interpreter object files +PY_CORE_LDFLAGS=$(PY_LDFLAGS) $(PY_LDFLAGS_NODIST) # Strict or non-strict aliasing flags used to compile dtoa.c, see above CFLAGS_ALIASING=@CFLAGS_ALIASING@ @@ -148,7 +155,7 @@ CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(LDVERSION) SHLIB_SUFFIX= @SHLIB_SUFFIX@ EXT_SUFFIX= @EXT_SUFFIX@ LDSHARED= @LDSHARED@ $(PY_LDFLAGS) -BLDSHARED= @BLDSHARED@ $(PY_LDFLAGS) +BLDSHARED= @BLDSHARED@ $(PY_LDFLAGS_NODIST) LDCXXSHARED= @LDCXXSHARED@ DESTSHARED= $(BINLIBDEST)/lib-dynload @@ -497,7 +504,7 @@ profile-run-stamp: touch $@ build_all_generate_profile: - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" run_profile_task: @ # FIXME: can't run for a cross build @@ -511,7 +518,7 @@ build_all_merge_profile: profile-opt: profile-run-stamp @echo "Rebuilding with profile guided optimizations:" -rm -f profile-clean-stamp - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST)" # Compile and run with gcov .PHONY=coverage coverage-lcov coverage-report @@ -568,7 +575,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c # Build the interpreter $(BUILDPYTHON): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) - $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) + $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) platform: $(BUILDPYTHON) pybuilddir.txt $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform @@ -633,7 +640,7 @@ libpython3.so: libpython$(LDVERSION).so $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) - $(CC) -dynamiclib -Wl,-single_module $(PY_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ + $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ libpython$(VERSION).sl: $(LIBRARY_OBJS) @@ -658,7 +665,7 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ $(LIBRARY) \ $(RESSRCDIR)/Info.plist $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION) - $(CC) -o $(LDLIBRARY) $(PY_LDFLAGS) -dynamiclib \ + $(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \ -all_load $(LIBRARY) -Wl,-single_module \ -install_name $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \ -compatibility_version $(VERSION) \ @@ -709,7 +716,7 @@ Modules/Setup: $(srcdir)/Modules/Setup.dist fi Programs/_testembed: Programs/_testembed.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) - $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) + $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) ############################################################################ # Importlib @@ -717,7 +724,7 @@ Programs/_testembed: Programs/_testembed.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) Programs/_freeze_importlib.o: Programs/_freeze_importlib.c Makefile Programs/_freeze_importlib: Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) - $(LINKCC) $(PY_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) + $(LINKCC) $(PY_CORE_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) .PHONY: regen-importlib regen-importlib: Programs/_freeze_importlib @@ -799,7 +806,7 @@ Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile $(IO_OBJS): $(IO_H) $(PGEN): $(PGENOBJS) - $(CC) $(OPT) $(PY_LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN) + $(CC) $(OPT) $(PY_CORE_LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN) .PHONY: regen-grammar regen-grammar: $(PGEN) diff --git a/Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst b/Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst new file mode 100644 index 000000000000..fad252578299 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst @@ -0,0 +1,2 @@ +Avoid leaking the linker flags from Link Time Optimizations (LTO) +into distutils when compiling C extensions. \ No newline at end of file diff --git a/configure b/configure index 4714ae887a67..5e5f974b0e50 100755 --- a/configure +++ b/configure @@ -667,6 +667,7 @@ SHLIB_SUFFIX LIBTOOL_CRUFT OTHER_LIBTOOL_OPT UNIVERSAL_ARCH_FLAGS +LDFLAGS_NODIST CFLAGS_NODIST BASECFLAGS CFLAGS_ALIASING @@ -6671,7 +6672,7 @@ $as_echo "$as_me: llvm-ar found via xcrun: ${LLVM_AR}" >&6;} fi CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" - LDFLAGS="$LDFLAGS $LTOFLAGS" + LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS" fi # Enable PGO flags. @@ -6923,6 +6924,7 @@ fi + # The -arch flags for universal builds on OSX UNIVERSAL_ARCH_FLAGS= diff --git a/configure.ac b/configure.ac index c35aea98e5b2..a7de901e08b7 100644 --- a/configure.ac +++ b/configure.ac @@ -1395,7 +1395,7 @@ if test "$Py_LTO" = 'true' ; then fi CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" - LDFLAGS="$LDFLAGS $LTOFLAGS" + LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS" fi # Enable PGO flags. @@ -1555,6 +1555,7 @@ fi AC_SUBST(BASECFLAGS) AC_SUBST(CFLAGS_NODIST) +AC_SUBST(LDFLAGS_NODIST) # The -arch flags for universal builds on OSX UNIVERSAL_ARCH_FLAGS= diff --git a/setup.py b/setup.py index a97a7559cae7..b4357e39cf19 100644 --- a/setup.py +++ b/setup.py @@ -18,11 +18,16 @@ cross_compiling = "_PYTHON_HOST_PLATFORM" in os.environ -# Add special CFLAGS reserved for building the interpreter and the stdlib -# modules (Issue #21121). -cflags = sysconfig.get_config_var('CFLAGS') -py_cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') -sysconfig.get_config_vars()['CFLAGS'] = cflags + ' ' + py_cflags_nodist +# Set common compiler and linker flags derived from the Makefile, +# reserved for building the interpreter and the stdlib modules. +# See bpo-21121 and bpo-35257 +def set_compiler_flags(compiler_flags, compiler_py_flags_nodist): + flags = sysconfig.get_config_var(compiler_flags) + py_flags_nodist = sysconfig.get_config_var(compiler_py_flags_nodist) + sysconfig.get_config_vars()[compiler_flags] = flags + ' ' + py_flags_nodist + +set_compiler_flags('CFLAGS', 'PY_CFLAGS_NODIST') +set_compiler_flags('LDFLAGS', 'PY_LDFLAGS_NODIST') class Dummy: """Hack for parallel build""" From webhook-mailer at python.org Thu Dec 20 11:00:26 2018 From: webhook-mailer at python.org (Chris Withers) Date: Thu, 20 Dec 2018 16:00:26 -0000 Subject: [Python-checkins] Fix mock_open docstring to use readline (#11176) Message-ID: https://github.com/python/cpython/commit/71f82a2f2085464f5ec99c16bce57bd1631733bd commit: 71f82a2f2085464f5ec99c16bce57bd1631733bd branch: master author: Xtreak committer: Chris Withers date: 2018-12-20T16:00:21Z summary: Fix mock_open docstring to use readline (#11176) files: M Lib/unittest/mock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 38189c9aec2e..3a22a48c997f 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2376,7 +2376,7 @@ def mock_open(mock=None, read_data=''): default) then a `MagicMock` will be created for you, with the API limited to methods or attributes available on standard file handles. - `read_data` is a string for the `read` methoddline`, and `readlines` of the + `read_data` is a string for the `read`, `readline` and `readlines` of the file handle to return. This is an empty string by default. """ def _readlines_side_effect(*args, **kwargs): From webhook-mailer at python.org Thu Dec 20 11:24:01 2018 From: webhook-mailer at python.org (Chris Withers) Date: Thu, 20 Dec 2018 16:24:01 -0000 Subject: [Python-checkins] Fix mock_open docstring to use readline (GH-11266) Message-ID: https://github.com/python/cpython/commit/5fe170d9c1355aa5c34e7c0d2591dc3ff29c6f29 commit: 5fe170d9c1355aa5c34e7c0d2591dc3ff29c6f29 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Chris Withers date: 2018-12-20T16:23:57Z summary: Fix mock_open docstring to use readline (GH-11266) (cherry picked from commit 71f82a2f2085464f5ec99c16bce57bd1631733bd) Co-authored-by: Xtreak files: M Lib/unittest/mock.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 5907e5c240f5..955af5d2b85d 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2352,7 +2352,7 @@ def mock_open(mock=None, read_data=''): default) then a `MagicMock` will be created for you, with the API limited to methods or attributes available on standard file handles. - `read_data` is a string for the `read` methoddline`, and `readlines` of the + `read_data` is a string for the `read`, `readline` and `readlines` of the file handle to return. This is an empty string by default. """ def _readlines_side_effect(*args, **kwargs): From webhook-mailer at python.org Thu Dec 20 12:00:18 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 20 Dec 2018 17:00:18 -0000 Subject: [Python-checkins] bpo-22831: Use "with" to avoid possible fd leaks in distutils. (GH-10921) Message-ID: https://github.com/python/cpython/commit/c5d5dfdb223efb0e668e3f317d31b8b70ae96aa6 commit: c5d5dfdb223efb0e668e3f317d31b8b70ae96aa6 branch: master author: Serhiy Storchaka committer: GitHub date: 2018-12-20T19:00:14+02:00 summary: bpo-22831: Use "with" to avoid possible fd leaks in distutils. (GH-10921) files: M Lib/distutils/archive_util.py M Lib/distutils/command/bdist_msi.py M Lib/distutils/command/config.py M Lib/distutils/command/sdist.py M Lib/distutils/util.py diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py index b002dc3b8456..565a3117b4b5 100644 --- a/Lib/distutils/archive_util.py +++ b/Lib/distutils/archive_util.py @@ -166,21 +166,21 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED) - if base_dir != os.curdir: - path = os.path.normpath(os.path.join(base_dir, '')) - zip.write(path, path) - log.info("adding '%s'", path) - for dirpath, dirnames, filenames in os.walk(base_dir): - for name in dirnames: - path = os.path.normpath(os.path.join(dirpath, name, '')) + with zip: + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) zip.write(path, path) log.info("adding '%s'", path) - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - if os.path.isfile(path): + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) zip.write(path, path) log.info("adding '%s'", path) - zip.close() + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zip.write(path, path) + log.info("adding '%s'", path) return zip_filename diff --git a/Lib/distutils/command/bdist_msi.py b/Lib/distutils/command/bdist_msi.py index 80104c372d9a..f335a3489862 100644 --- a/Lib/distutils/command/bdist_msi.py +++ b/Lib/distutils/command/bdist_msi.py @@ -390,18 +390,18 @@ def add_scripts(self): # entries for each version as the above code does if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") - f = open(scriptfn, "w") - # The batch file will be executed with [PYTHON], so that %1 - # is the path to the Python interpreter; %0 will be the path - # of the batch file. - # rem =""" - # %1 %0 - # exit - # """ - # - f.write('rem ="""\n%1 %0\nexit\n"""\n') - f.write(open(self.pre_install_script).read()) - f.close() + with open(scriptfn, "w") as f: + # The batch file will be executed with [PYTHON], so that %1 + # is the path to the Python interpreter; %0 will be the path + # of the batch file. + # rem =""" + # %1 %0 + # exit + # """ + # + f.write('rem ="""\n%1 %0\nexit\n"""\n') + with open(self.pre_install_script) as fin: + f.write(fin.read()) add_data(self.db, "Binary", [("PreInstall", msilib.Binary(scriptfn)) ]) diff --git a/Lib/distutils/command/config.py b/Lib/distutils/command/config.py index 4ae153d1943f..f511b8885770 100644 --- a/Lib/distutils/command/config.py +++ b/Lib/distutils/command/config.py @@ -106,15 +106,14 @@ def _check_compiler(self): def _gen_temp_sourcefile(self, body, headers, lang): filename = "_configtest" + LANG_EXT[lang] - file = open(filename, "w") - if headers: - for header in headers: - file.write("#include <%s>\n" % header) - file.write("\n") - file.write(body) - if body[-1] != "\n": - file.write("\n") - file.close() + with open(filename, "w") as file: + if headers: + for header in headers: + file.write("#include <%s>\n" % header) + file.write("\n") + file.write(body) + if body[-1] != "\n": + file.write("\n") return filename def _preprocess(self, body, headers, include_dirs, lang): @@ -203,17 +202,16 @@ def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, if isinstance(pattern, str): pattern = re.compile(pattern) - file = open(out) - match = False - while True: - line = file.readline() - if line == '': - break - if pattern.search(line): - match = True - break + with open(out) as file: + match = False + while True: + line = file.readline() + if line == '': + break + if pattern.search(line): + match = True + break - file.close() self._clean() return match diff --git a/Lib/distutils/command/sdist.py b/Lib/distutils/command/sdist.py index 52eaa15d4712..b4996fcb1d27 100644 --- a/Lib/distutils/command/sdist.py +++ b/Lib/distutils/command/sdist.py @@ -407,14 +407,13 @@ def read_manifest(self): distribution. """ log.info("reading manifest file '%s'", self.manifest) - manifest = open(self.manifest) - for line in manifest: - # ignore comments and blank lines - line = line.strip() - if line.startswith('#') or not line: - continue - self.filelist.append(line) - manifest.close() + with open(self.manifest) as manifest: + for line in manifest: + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue + self.filelist.append(line) def make_release_tree(self, base_dir, files): """Create the directory tree that will become the source diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py index 30a21e4afa1f..15cd2ad9a9af 100644 --- a/Lib/distutils/util.py +++ b/Lib/distutils/util.py @@ -378,35 +378,34 @@ def byte_compile (py_files, else: script = open(script_name, "w") - script.write("""\ + with script: + script.write("""\ from distutils.util import byte_compile files = [ """) - # XXX would be nice to write absolute filenames, just for - # safety's sake (script should be more robust in the face of - # chdir'ing before running it). But this requires abspath'ing - # 'prefix' as well, and that breaks the hack in build_lib's - # 'byte_compile()' method that carefully tacks on a trailing - # slash (os.sep really) to make sure the prefix here is "just - # right". This whole prefix business is rather delicate -- the - # problem is that it's really a directory, but I'm treating it - # as a dumb string, so trailing slashes and so forth matter. - - #py_files = map(os.path.abspath, py_files) - #if prefix: - # prefix = os.path.abspath(prefix) - - script.write(",\n".join(map(repr, py_files)) + "]\n") - script.write(""" + # XXX would be nice to write absolute filenames, just for + # safety's sake (script should be more robust in the face of + # chdir'ing before running it). But this requires abspath'ing + # 'prefix' as well, and that breaks the hack in build_lib's + # 'byte_compile()' method that carefully tacks on a trailing + # slash (os.sep really) to make sure the prefix here is "just + # right". This whole prefix business is rather delicate -- the + # problem is that it's really a directory, but I'm treating it + # as a dumb string, so trailing slashes and so forth matter. + + #py_files = map(os.path.abspath, py_files) + #if prefix: + # prefix = os.path.abspath(prefix) + + script.write(",\n".join(map(repr, py_files)) + "]\n") + script.write(""" byte_compile(files, optimize=%r, force=%r, prefix=%r, base_dir=%r, verbose=%r, dry_run=0, direct=1) """ % (optimize, force, prefix, base_dir, verbose)) - script.close() - cmd = [sys.executable] cmd.extend(subprocess._optim_args_from_interpreter_flags()) cmd.append(script_name) From webhook-mailer at python.org Thu Dec 20 12:27:19 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 20 Dec 2018 17:27:19 -0000 Subject: [Python-checkins] bpo-35482: Fixes HTML escaping in CHM index and build location of NEWS file (GH-11224) Message-ID: https://github.com/python/cpython/commit/aae2e85af772a409bf8904bddc17efe9bf809174 commit: aae2e85af772a409bf8904bddc17efe9bf809174 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-20T09:27:12-08:00 summary: bpo-35482: Fixes HTML escaping in CHM index and build location of NEWS file (GH-11224) (cherry picked from commit afe17a7bee1bcb39dc56f2949431204918568ac1) Co-authored-by: Steve Dower files: M Doc/make.bat M Doc/tools/extensions/escape4chm.py M Doc/tools/extensions/pyspecific.py diff --git a/Doc/make.bat b/Doc/make.bat index 077a1bc74069..461c35c5a114 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -16,7 +16,7 @@ if not defined SPHINXBUILD ( %PYTHON% -m pip install sphinx if errorlevel 1 exit /B ) - set SPHINXBUILD=%PYTHON% -c "import sphinx, sys; sys.argv[0] = 'sphinx-build'; sys.exit(sphinx.main())" + set SPHINXBUILD=%PYTHON% -c "import sphinx.cmd.build, sys; sys.exit(sphinx.cmd.build.main())" ) %PYTHON% -c "import python_docs_theme" > nul 2> nul @@ -115,17 +115,16 @@ goto end :build if not exist "%BUILDDIR%" mkdir "%BUILDDIR%" -rem We ought to move NEWS to %BUILDDIR%\NEWS and point -rem Sphinx at the right location. +rem PY_MISC_NEWS_DIR is also used by our Sphinx extension in tools/extensions/pyspecific.py +if not defined PY_MISC_NEWS_DIR set PY_MISC_NEWS_DIR=%BUILDDIR%\%1 if exist ..\Misc\NEWS ( - echo.Copying Misc\NEWS to build\NEWS - if not exist build mkdir build - copy ..\Misc\NEWS build\NEWS > nul + echo.Copying Misc\NEWS to %PY_MISC_NEWS_DIR%\NEWS + copy ..\Misc\NEWS "%PY_MISC_NEWS_DIR%\NEWS" > nul ) else if exist ..\Misc\NEWS.D ( if defined BLURB ( echo.Merging Misc/NEWS with %BLURB% if not exist build mkdir build - %BLURB% merge -f build\NEWS + %BLURB% merge -f "%PY_MISC_NEWS_DIR%\NEWS" ) else ( echo.No Misc/NEWS file and Blurb is not available. exit /B 1 diff --git a/Doc/tools/extensions/escape4chm.py b/Doc/tools/extensions/escape4chm.py index 6f2e35725b37..e99997162517 100644 --- a/Doc/tools/extensions/escape4chm.py +++ b/Doc/tools/extensions/escape4chm.py @@ -8,6 +8,8 @@ import re from html.entities import codepoint2name +from sphinx.util.logging import getLogger + # escape the characters which codepoint > 0x7F def _process(string): def escape(matchobj): @@ -23,7 +25,7 @@ def escape(matchobj): def escape_for_chm(app, pagename, templatename, context, doctree): # only works for .chm output - if not hasattr(app.builder, 'name') or app.builder.name != 'htmlhelp': + if getattr(app.builder, 'name', '') != 'htmlhelp': return # escape the `body` part to 7-bit ASCII @@ -31,9 +33,25 @@ def escape_for_chm(app, pagename, templatename, context, doctree): if body is not None: context['body'] = _process(body) +def fixup_keywords(app, exception): + # only works for .chm output + if getattr(app.builder, 'name', '') != 'htmlhelp' or exception: + return + + getLogger(__name__).info('fixing HTML escapes in keywords file...') + outdir = app.builder.outdir + outname = app.builder.config.htmlhelp_basename + with app.builder.open_file(outdir, outname + '.hhk', 'r') as f: + index = f.read() + with app.builder.open_file(outdir, outname + '.hhk', 'w') as f: + f.write(index.replace(''', ''')) + def setup(app): # `html-page-context` event emitted when the HTML builder has # created a context dictionary to render a template with. app.connect('html-page-context', escape_for_chm) + # `build-finished` event emitted when all the files have been + # output. + app.connect('build-finished', fixup_keywords) return {'version': '1.0', 'parallel_read_safe': True} diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index 3070c6ed461e..466e84c26147 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -11,7 +11,7 @@ import re import io -from os import path +from os import getenv, path from time import asctime from pprint import pformat from docutils.io import StringOutput @@ -292,7 +292,9 @@ def run(self): fname = self.arguments[0] source = self.state_machine.input_lines.source( self.lineno - self.state_machine.input_offset - 1) - source_dir = path.dirname(path.abspath(source)) + source_dir = getenv('PY_MISC_NEWS_DIR') + if not source_dir: + source_dir = path.dirname(path.abspath(source)) fpath = path.join(source_dir, fname) self.state.document.settings.record_dependencies.add(fpath) try: From webhook-mailer at python.org Thu Dec 20 12:38:57 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 20 Dec 2018 17:38:57 -0000 Subject: [Python-checkins] bpo-35504: Fix segfaults and SystemErrors when deleting certain attrs. (GH-11175) (GH-11249) Message-ID: https://github.com/python/cpython/commit/f347c6eb75ca46990cd7ad3efbe02002603d8460 commit: f347c6eb75ca46990cd7ad3efbe02002603d8460 branch: 2.7 author: Zackery Spytz committer: Serhiy Storchaka date: 2018-12-20T19:38:52+02:00 summary: bpo-35504: Fix segfaults and SystemErrors when deleting certain attrs. (GH-11175) (GH-11249) (cherry picked from commit 842acaab1376c5c84fd5966bb6070e289880e1ca) files: A Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst M Lib/ctypes/test/test_strings.py M Lib/sqlite3/test/regression.py M Lib/test/test_io.py M Modules/_ctypes/_ctypes.c M Modules/_io/textio.c M Modules/_sqlite/connection.c M Modules/cjkcodecs/multibytecodec.c M Objects/frameobject.c diff --git a/Lib/ctypes/test/test_strings.py b/Lib/ctypes/test/test_strings.py index 4b58e7ca4ff3..879c58ab627a 100644 --- a/Lib/ctypes/test/test_strings.py +++ b/Lib/ctypes/test/test_strings.py @@ -61,6 +61,13 @@ def test_param_2(self): ## print BUF.from_param(c_char_p("python")) ## print BUF.from_param(BUF(*"pyth")) + def test_del_segfault(self): + BUF = c_char * 4 + buf = BUF() + with self.assertRaises(AttributeError): + del buf.raw + + @need_symbol('c_wchar') class WStringArrayTestCase(unittest.TestCase): def test(self): diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 271afb0a7f03..42fc7c278b8c 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -361,6 +361,10 @@ def callback(*args): del ref support.gc_collect() + def CheckDelIsolation_levelSegfault(self): + with self.assertRaises(AttributeError): + del self.con.isolation_level + class UnhashableFunc: def __hash__(self): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 8152e6174d35..5ec0b7bb1194 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2807,6 +2807,11 @@ def test_rwpair_cleared_before_textio(self): t2.buddy = t1 support.gc_collect() + def test_del__CHUNK_SIZE_SystemError(self): + t = self.TextIOWrapper(self.BytesIO(), encoding='ascii') + with self.assertRaises(AttributeError): + del t._CHUNK_SIZE + maybeRaises = unittest.TestCase.assertRaises diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst b/Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst new file mode 100644 index 000000000000..2a4f0f694fee --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-12-15-14-01-45.bpo-35504.JtKczP.rst @@ -0,0 +1,2 @@ +Fix segfaults and :exc:`SystemError`\ s when deleting certain attributes. +Patch by Zackery Spytz. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 9aa252d6e265..8abcd30753a4 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1216,6 +1216,10 @@ CharArray_set_raw(CDataObject *self, PyObject *value) #if (PY_VERSION_HEX >= 0x02060000) Py_buffer view = { 0 }; #endif + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } if (PyBuffer_Check(value)) { size = Py_TYPE(value)->tp_as_buffer->bf_getreadbuffer(value, 0, (void *)&ptr); if (size < 0) diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 5501da4b3f5c..12a12f9ef02a 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2593,6 +2593,10 @@ textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context) { Py_ssize_t n; CHECK_ATTACHED_INT(self); + if (arg == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } n = PyNumber_AsSsize_t(arg, PyExc_TypeError); if (n == -1 && PyErr_Occurred()) return -1; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 585453a28207..18a6b427c2e1 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1131,6 +1131,10 @@ static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, Py PyObject* begin_statement; char* begin_statement_str; + if (isolation_level == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } Py_XDECREF(self->isolation_level); if (self->begin_statement) { diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index 4b482bf9e251..1ad0ea0f5138 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -138,6 +138,10 @@ codecctx_errors_set(MultibyteStatefulCodecContext *self, PyObject *value, { PyObject *cb; + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } if (!PyString_Check(value)) { PyErr_SetString(PyExc_TypeError, "errors must be a string"); return -1; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 175874503bb2..4c91dd0c0842 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -118,6 +118,10 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) int blockstack_top = 0; /* (ditto) */ unsigned char setup_op = 0; /* (ditto) */ + if (p_new_lineno == NULL) { + PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); + return -1; + } /* f_lineno must be an integer. */ if (!PyInt_Check(p_new_lineno)) { PyErr_SetString(PyExc_ValueError, From webhook-mailer at python.org Thu Dec 20 14:11:54 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 19:11:54 -0000 Subject: [Python-checkins] bpo-35482: Fixes HTML escaping in CHM index and build location of NEWS file (GH-11224) (GH-11251) Message-ID: https://github.com/python/cpython/commit/789b0ee023f14385a2fd635272768c3b55a99773 commit: 789b0ee023f14385a2fd635272768c3b55a99773 branch: 3.6 author: Steve Dower committer: Ned Deily date: 2018-12-20T14:11:39-05:00 summary: bpo-35482: Fixes HTML escaping in CHM index and build location of NEWS file (GH-11224) (GH-11251) files: M Doc/make.bat M Doc/tools/extensions/escape4chm.py M Doc/tools/extensions/pyspecific.py diff --git a/Doc/make.bat b/Doc/make.bat index d28dae78e86d..562beb35ae5b 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -16,7 +16,7 @@ if not defined SPHINXBUILD ( %PYTHON% -m pip install sphinx if errorlevel 1 exit /B ) - set SPHINXBUILD=%PYTHON% -c "import sphinx, sys; sys.argv[0] = 'sphinx-build'; sys.exit(sphinx.main())" + set SPHINXBUILD=%PYTHON% -c "import sphinx.cmd.build, sys; sys.exit(sphinx.cmd.build.main())" ) %PYTHON% -c "import python_docs_theme" > nul 2> nul @@ -115,13 +115,15 @@ goto end :build if not exist "%BUILDDIR%" mkdir "%BUILDDIR%" +rem PY_MISC_NEWS_DIR is also used by our Sphinx extension in tools/extensions/pyspecific.py +if not defined PY_MISC_NEWS_DIR set PY_MISC_NEWS_DIR=%BUILDDIR%\%1 if exist ..\Misc\NEWS ( - echo.Copying Misc\NEWS to build\NEWS - copy ..\Misc\NEWS build\NEWS > nul + echo.Copying Misc\NEWS to %PY_MISC_NEWS_DIR%\NEWS + copy ..\Misc\NEWS "%PY_MISC_NEWS_DIR%\NEWS" > nul ) else if exist ..\Misc\NEWS.D ( if defined BLURB ( echo.Merging Misc/NEWS with %BLURB% - %BLURB% merge -f build\NEWS + %BLURB% merge -f "%PY_MISC_NEWS_DIR%\NEWS" ) else ( echo.No Misc/NEWS file and Blurb is not available. exit /B 1 diff --git a/Doc/tools/extensions/escape4chm.py b/Doc/tools/extensions/escape4chm.py index 6f2e35725b37..e99997162517 100644 --- a/Doc/tools/extensions/escape4chm.py +++ b/Doc/tools/extensions/escape4chm.py @@ -8,6 +8,8 @@ import re from html.entities import codepoint2name +from sphinx.util.logging import getLogger + # escape the characters which codepoint > 0x7F def _process(string): def escape(matchobj): @@ -23,7 +25,7 @@ def escape(matchobj): def escape_for_chm(app, pagename, templatename, context, doctree): # only works for .chm output - if not hasattr(app.builder, 'name') or app.builder.name != 'htmlhelp': + if getattr(app.builder, 'name', '') != 'htmlhelp': return # escape the `body` part to 7-bit ASCII @@ -31,9 +33,25 @@ def escape_for_chm(app, pagename, templatename, context, doctree): if body is not None: context['body'] = _process(body) +def fixup_keywords(app, exception): + # only works for .chm output + if getattr(app.builder, 'name', '') != 'htmlhelp' or exception: + return + + getLogger(__name__).info('fixing HTML escapes in keywords file...') + outdir = app.builder.outdir + outname = app.builder.config.htmlhelp_basename + with app.builder.open_file(outdir, outname + '.hhk', 'r') as f: + index = f.read() + with app.builder.open_file(outdir, outname + '.hhk', 'w') as f: + f.write(index.replace(''', ''')) + def setup(app): # `html-page-context` event emitted when the HTML builder has # created a context dictionary to render a template with. app.connect('html-page-context', escape_for_chm) + # `build-finished` event emitted when all the files have been + # output. + app.connect('build-finished', fixup_keywords) return {'version': '1.0', 'parallel_read_safe': True} diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index 5b2c31563766..dec7f0c779ab 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -11,7 +11,7 @@ import re import codecs -from os import path +from os import getenv, path from time import asctime from pprint import pformat from docutils.io import StringOutput @@ -253,7 +253,9 @@ def run(self): fname = self.arguments[0] source = self.state_machine.input_lines.source( self.lineno - self.state_machine.input_offset - 1) - source_dir = path.dirname(path.abspath(source)) + source_dir = getenv('PY_MISC_NEWS_DIR') + if not source_dir: + source_dir = path.dirname(path.abspath(source)) fpath = path.join(source_dir, fname) self.state.document.settings.record_dependencies.add(fpath) try: From webhook-mailer at python.org Thu Dec 20 14:33:59 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 20 Dec 2018 19:33:59 -0000 Subject: [Python-checkins] bpo-35424: emit ResourceWarning at multiprocessing.Pool destruction (GH-10974) Message-ID: https://github.com/python/cpython/commit/9a8d1d7562b11969034b92217fe66aab7a951fb6 commit: 9a8d1d7562b11969034b92217fe66aab7a951fb6 branch: master author: Victor Stinner committer: GitHub date: 2018-12-20T20:33:51+01:00 summary: bpo-35424: emit ResourceWarning at multiprocessing.Pool destruction (GH-10974) multiprocessing.Pool destructor now emits ResourceWarning if the pool is still running. files: A Misc/NEWS.d/next/Library/2018-12-06-02-02-28.bpo-35424.gXxOJU.rst M Lib/multiprocessing/pool.py M Lib/test/_test_multiprocessing.py diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py index 1e26a9b56f0c..bfb2769ba6ec 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -13,13 +13,14 @@ # Imports # -import threading -import queue -import itertools import collections +import itertools import os +import queue +import threading import time import traceback +import warnings # If threading is available then ThreadPool should be provided. Therefore # we avoid top-level imports which are liable to fail on some systems. @@ -30,6 +31,7 @@ # Constants representing the state of a pool # +INIT = "INIT" RUN = "RUN" CLOSE = "CLOSE" TERMINATE = "TERMINATE" @@ -154,11 +156,15 @@ def Process(self, *args, **kwds): def __init__(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None): + # Attributes initialized early to make sure that they exist in + # __del__() if __init__() raises an exception + self._pool = [] + self._state = INIT + self._ctx = context or get_context() self._setup_queues() self._taskqueue = queue.SimpleQueue() self._cache = {} - self._state = RUN self._maxtasksperchild = maxtasksperchild self._initializer = initializer self._initargs = initargs @@ -172,7 +178,6 @@ def __init__(self, processes=None, initializer=None, initargs=(), raise TypeError('initializer must be a callable') self._processes = processes - self._pool = [] try: self._repopulate_pool() except Exception: @@ -216,6 +221,14 @@ def __init__(self, processes=None, initializer=None, initargs=(), self._result_handler, self._cache), exitpriority=15 ) + self._state = RUN + + # Copy globals as function locals to make sure that they are available + # during Python shutdown when the Pool is destroyed. + def __del__(self, _warn=warnings.warn, RUN=RUN): + if self._state == RUN: + _warn(f"unclosed running multiprocessing pool {self!r}", + ResourceWarning, source=self) def __repr__(self): cls = self.__class__ diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index b5597d5fb129..7341131231a4 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2577,6 +2577,22 @@ def test_enter(self): pass pool.join() + def test_resource_warning(self): + if self.TYPE == 'manager': + self.skipTest("test not applicable to manager") + + pool = self.Pool(1) + pool.terminate() + pool.join() + + # force state to RUN to emit ResourceWarning in __del__() + pool._state = multiprocessing.pool.RUN + + with support.check_warnings(('unclosed running multiprocessing pool', + ResourceWarning)): + pool = None + support.gc_collect() + def raising(): raise KeyError("key") diff --git a/Misc/NEWS.d/next/Library/2018-12-06-02-02-28.bpo-35424.gXxOJU.rst b/Misc/NEWS.d/next/Library/2018-12-06-02-02-28.bpo-35424.gXxOJU.rst new file mode 100644 index 000000000000..db4a336ee17c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-06-02-02-28.bpo-35424.gXxOJU.rst @@ -0,0 +1,2 @@ +:class:`multiprocessing.Pool` destructor now emits :exc:`ResourceWarning` +if the pool is still running. From webhook-mailer at python.org Thu Dec 20 14:46:12 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 19:46:12 -0000 Subject: [Python-checkins] bpo-35499: make profile-opt don't override CFLAGS_NODIST (GH-11164) (GH-11267) Message-ID: https://github.com/python/cpython/commit/782e1d537778d93eb4cba1343f71bfc51e7e3c00 commit: 782e1d537778d93eb4cba1343f71bfc51e7e3c00 branch: 3.6 author: Victor Stinner committer: Ned Deily date: 2018-12-20T14:46:07-05:00 summary: bpo-35499: make profile-opt don't override CFLAGS_NODIST (GH-11164) (GH-11267) "make profile-opt" no longer replaces CFLAGS_NODIST with CFLAGS. It now adds profile-guided optimization (PGO) flags to CFLAGS_NODIST, existing CFLAGS_NODIST flags are kept. (cherry picked from commit 640ed520dd6a43a8bf470b79542f58b5d57af9de) files: A Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst M Makefile.pre.in diff --git a/Makefile.pre.in b/Makefile.pre.in index 258236d36450..af9504dfac7f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -472,7 +472,7 @@ profile-opt: $(MAKE) profile-removal build_all_generate_profile: - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" run_profile_task: : # FIXME: can't run for a cross build @@ -482,7 +482,7 @@ build_all_merge_profile: $(LLVM_PROF_MERGER) build_all_use_profile: - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)" # Compile and run with gcov .PHONY=coverage coverage-lcov coverage-report diff --git a/Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst b/Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst new file mode 100644 index 000000000000..ed730b9d9b4a --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-14-19-36-05.bpo-35499.9yAldM.rst @@ -0,0 +1,3 @@ +``make profile-opt`` no longer replaces ``CFLAGS_NODIST`` with ``CFLAGS``. It +now adds profile-guided optimization (PGO) flags to ``CFLAGS_NODIST``: existing +``CFLAGS_NODIST`` flags are kept. From webhook-mailer at python.org Thu Dec 20 15:26:07 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 20:26:07 -0000 Subject: [Python-checkins] [3.7] bpo-31715 Add mimetype for extension .mjs (GH-3908) (GH-10977) Message-ID: https://github.com/python/cpython/commit/c7b7a43cd8964a90669bb37735cdafc5b0ec60cc commit: c7b7a43cd8964a90669bb37735cdafc5b0ec60cc branch: 3.7 author: Myles Borins committer: Ned Deily date: 2018-12-20T15:26:02-05:00 summary: [3.7] bpo-31715 Add mimetype for extension .mjs (GH-3908) (GH-10977) (cherry picked from commit 0854b92cd2) files: A Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst M Lib/mimetypes.py diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index c86dd6d13457..1206d8e9b6a8 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -448,6 +448,7 @@ def _default_mime_types(): '.mht' : 'message/rfc822', '.mhtml' : 'message/rfc822', '.mif' : 'application/x-mif', + '.mjs' : 'application/javascript', '.mov' : 'video/quicktime', '.movie' : 'video/x-sgi-movie', '.mp2' : 'audio/mpeg', diff --git a/Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst b/Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst new file mode 100644 index 000000000000..eacba28f9fd9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst @@ -0,0 +1 @@ +Associate ``.mjs`` file extension with ``application/javascript`` MIME Type. From webhook-mailer at python.org Thu Dec 20 15:28:33 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 20:28:33 -0000 Subject: [Python-checkins] [3.6] bpo-31715 Add mimetype for extension .mjs (GH-3908) (GH-10976) Message-ID: https://github.com/python/cpython/commit/70db385944ecf2ceed10ed7d91fce68399f9ca8a commit: 70db385944ecf2ceed10ed7d91fce68399f9ca8a branch: 3.6 author: Myles Borins committer: Ned Deily date: 2018-12-20T15:28:28-05:00 summary: [3.6] bpo-31715 Add mimetype for extension .mjs (GH-3908) (GH-10976) (cherry picked from commit 0854b92cd2) files: A Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst M Lib/mimetypes.py diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index 5a7e6493bf51..7321354ccc13 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -450,6 +450,7 @@ def _default_mime_types(): '.mht' : 'message/rfc822', '.mhtml' : 'message/rfc822', '.mif' : 'application/x-mif', + '.mjs' : 'application/javascript', '.mov' : 'video/quicktime', '.movie' : 'video/x-sgi-movie', '.mp2' : 'audio/mpeg', diff --git a/Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst b/Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst new file mode 100644 index 000000000000..eacba28f9fd9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst @@ -0,0 +1 @@ +Associate ``.mjs`` file extension with ``application/javascript`` MIME Type. From webhook-mailer at python.org Thu Dec 20 15:29:37 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 20:29:37 -0000 Subject: [Python-checkins] [2.7] bpo-31715 Add mimetype for extension .mjs (GH-3908) (GH-10978) Message-ID: https://github.com/python/cpython/commit/d9a2aca839b1326e011ecca17ba7b45123c47a64 commit: d9a2aca839b1326e011ecca17ba7b45123c47a64 branch: 2.7 author: Myles Borins committer: Ned Deily date: 2018-12-20T15:29:34-05:00 summary: [2.7] bpo-31715 Add mimetype for extension .mjs (GH-3908) (GH-10978) (cherry picked from 0854b92cd2) files: A Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst M Lib/mimetypes.py diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index caecc11c37cb..afc053f135dd 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -451,6 +451,7 @@ def _default_mime_types(): '.mht' : 'message/rfc822', '.mhtml' : 'message/rfc822', '.mif' : 'application/x-mif', + '.mjs' : 'application/javascript', '.mov' : 'video/quicktime', '.movie' : 'video/x-sgi-movie', '.mp2' : 'audio/mpeg', diff --git a/Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst b/Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst new file mode 100644 index 000000000000..eacba28f9fd9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-08-15-16-22-30.bpo-31715.Iw8jS8.rst @@ -0,0 +1 @@ +Associate ``.mjs`` file extension with ``application/javascript`` MIME Type. From webhook-mailer at python.org Thu Dec 20 15:31:26 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 20:31:26 -0000 Subject: [Python-checkins] [3.6] bpo-35257: Avoid leaking LTO linker flags into distutils (GH-10900) (GH-11265) Message-ID: https://github.com/python/cpython/commit/a21bedf9edeacce6f0de3b2df0783e2ad6aa3b18 commit: a21bedf9edeacce6f0de3b2df0783e2ad6aa3b18 branch: 3.6 author: Victor Stinner committer: Ned Deily date: 2018-12-20T15:31:22-05:00 summary: [3.6] bpo-35257: Avoid leaking LTO linker flags into distutils (GH-10900) (GH-11265) When compiling 3rd party C extensions, the linker flags used by the compiler for the interpreter and the stdlib modules, will get leaked into distutils. In order to avoid that, the PY_CORE_LDFLAGS and PY_LDFLAGS_NODIST are introduced to keep those flags separated. (cherry picked from commit cf10a750f4b50b6775719cfb17bee00bc3a9c60b) files: A Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst M Lib/_osx_support.py M Lib/test/pythoninfo.py M Lib/test/test__osx_support.py M Makefile.pre.in M configure M configure.ac M setup.py diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py index e37852e2536c..db6674ea293f 100644 --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -17,7 +17,7 @@ _UNIVERSAL_CONFIG_VARS = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', - 'PY_CORE_CFLAGS') + 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS') # configuration variables that may contain compiler calls _COMPILER_CONFIG_VARS = ('BLDSHARED', 'LDSHARED', 'CC', 'CXX') diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index c453340b80d7..c5586b45a5ff 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -407,7 +407,9 @@ def collect_sysconfig(info_add): 'OPT', 'PY_CFLAGS', 'PY_CFLAGS_NODIST', + 'PY_CORE_LDFLAGS', 'PY_LDFLAGS', + 'PY_LDFLAGS_NODIST', 'Py_DEBUG', 'Py_ENABLE_SHARED', 'SHELL', diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py index bcba8caa2964..388a2b1a84b1 100644 --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -24,7 +24,7 @@ def setUp(self): for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', - 'PY_CORE_CFLAGS'): + 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS'): if cv in self.env: self.env.unset(cv) diff --git a/Makefile.pre.in b/Makefile.pre.in index af9504dfac7f..da259f89de63 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -84,6 +84,10 @@ CONFIGURE_CFLAGS= @CFLAGS@ # Use it when a compiler flag should _not_ be part of the distutils CFLAGS # once Python is installed (Issue #21121). CONFIGURE_CFLAGS_NODIST=@CFLAGS_NODIST@ +# LDFLAGS_NODIST is used in the same manner as CFLAGS_NODIST. +# Use it when a linker flag should _not_ be part of the distutils LDFLAGS +# once Python is installed (bpo-35257) +CONFIGURE_LDFLAGS_NODIST=@LDFLAGS_NODIST@ CONFIGURE_CPPFLAGS= @CPPFLAGS@ CONFIGURE_LDFLAGS= @LDFLAGS@ # Avoid assigning CFLAGS, LDFLAGS, etc. so users can use them on the @@ -96,6 +100,7 @@ PY_CFLAGS_NODIST=$(CONFIGURE_CFLAGS_NODIST) $(CFLAGS_NODIST) # environment variables PY_CPPFLAGS= $(BASECPPFLAGS) -I. -I$(srcdir)/Include $(CONFIGURE_CPPFLAGS) $(CPPFLAGS) PY_LDFLAGS= $(CONFIGURE_LDFLAGS) $(LDFLAGS) +PY_LDFLAGS_NODIST=$(CONFIGURE_LDFLAGS_NODIST) $(LDFLAGS_NODIST) NO_AS_NEEDED= @NO_AS_NEEDED@ LDLAST= @LDLAST@ SGI_ABI= @SGI_ABI@ @@ -106,6 +111,8 @@ ARFLAGS= @ARFLAGS@ CFLAGSFORSHARED=@CFLAGSFORSHARED@ # C flags used for building the interpreter object files PY_CORE_CFLAGS= $(PY_CFLAGS) $(PY_CFLAGS_NODIST) $(PY_CPPFLAGS) $(CFLAGSFORSHARED) -DPy_BUILD_CORE +# Linker flags used for building the interpreter object files +PY_CORE_LDFLAGS=$(PY_LDFLAGS) $(PY_LDFLAGS_NODIST) # Strict or non-strict aliasing flags used to compile dtoa.c, see above CFLAGS_ALIASING=@CFLAGS_ALIASING@ @@ -145,7 +152,7 @@ CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(LDVERSION) SHLIB_SUFFIX= @SHLIB_SUFFIX@ EXT_SUFFIX= @EXT_SUFFIX@ LDSHARED= @LDSHARED@ $(PY_LDFLAGS) -BLDSHARED= @BLDSHARED@ $(PY_LDFLAGS) +BLDSHARED= @BLDSHARED@ $(PY_LDFLAGS_NODIST) LDCXXSHARED= @LDCXXSHARED@ DESTSHARED= $(BINLIBDEST)/lib-dynload @@ -472,7 +479,7 @@ profile-opt: $(MAKE) profile-removal build_all_generate_profile: - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)" run_profile_task: : # FIXME: can't run for a cross build @@ -482,7 +489,7 @@ build_all_merge_profile: $(LLVM_PROF_MERGER) build_all_use_profile: - $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)" + $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST)" # Compile and run with gcov .PHONY=coverage coverage-lcov coverage-report @@ -543,7 +550,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c # Build the interpreter $(BUILDPYTHON): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) - $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) + $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) platform: $(BUILDPYTHON) pybuilddir.txt $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform @@ -608,7 +615,7 @@ libpython3.so: libpython$(LDVERSION).so $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) - $(CC) -dynamiclib -Wl,-single_module $(PY_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ + $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \ libpython$(VERSION).sl: $(LIBRARY_OBJS) @@ -633,7 +640,7 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ $(LIBRARY) \ $(RESSRCDIR)/Info.plist $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION) - $(CC) -o $(LDLIBRARY) $(PY_LDFLAGS) -dynamiclib \ + $(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \ -all_load $(LIBRARY) -Wl,-single_module \ -install_name $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \ -compatibility_version $(VERSION) \ @@ -686,7 +693,7 @@ Modules/Setup: $(srcdir)/Modules/Setup.dist fi Programs/_testembed: Programs/_testembed.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) - $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) + $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) ############################################################################ # Importlib @@ -694,7 +701,7 @@ Programs/_testembed: Programs/_testembed.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) Programs/_freeze_importlib.o: Programs/_freeze_importlib.c Makefile Programs/_freeze_importlib: Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) - $(LINKCC) $(PY_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) + $(LINKCC) $(PY_CORE_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) .PHONY: regen-importlib regen-importlib: Programs/_freeze_importlib @@ -776,7 +783,7 @@ Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile $(IO_OBJS): $(IO_H) $(PGEN): $(PGENOBJS) - $(CC) $(OPT) $(PY_LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN) + $(CC) $(OPT) $(PY_CORE_LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN) .PHONY: regen-grammar regen-grammar: $(PGEN) diff --git a/Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst b/Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst new file mode 100644 index 000000000000..fad252578299 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2018-12-05-22-28-40.bpo-35257.dmcd_s.rst @@ -0,0 +1,2 @@ +Avoid leaking the linker flags from Link Time Optimizations (LTO) +into distutils when compiling C extensions. \ No newline at end of file diff --git a/configure b/configure index 7286e6eb494d..e39c16eee2b9 100755 --- a/configure +++ b/configure @@ -666,6 +666,7 @@ SHLIB_SUFFIX LIBTOOL_CRUFT OTHER_LIBTOOL_OPT UNIVERSAL_ARCH_FLAGS +LDFLAGS_NODIST CFLAGS_NODIST BASECFLAGS CFLAGS_ALIASING @@ -6716,7 +6717,7 @@ $as_echo "$as_me: llvm-ar found via xcrun: ${LLVM_AR}" >&6;} fi CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" - LDFLAGS="$LDFLAGS $LTOFLAGS" + LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS" fi # Enable PGO flags. @@ -6968,6 +6969,7 @@ fi + # The -arch flags for universal builds on OSX UNIVERSAL_ARCH_FLAGS= diff --git a/configure.ac b/configure.ac index bb14a87e5463..cf280506bd01 100644 --- a/configure.ac +++ b/configure.ac @@ -1401,7 +1401,7 @@ if test "$Py_LTO" = 'true' ; then fi CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS" - LDFLAGS="$LDFLAGS $LTOFLAGS" + LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS" fi # Enable PGO flags. @@ -1561,6 +1561,7 @@ fi AC_SUBST(BASECFLAGS) AC_SUBST(CFLAGS_NODIST) +AC_SUBST(LDFLAGS_NODIST) # The -arch flags for universal builds on OSX UNIVERSAL_ARCH_FLAGS= diff --git a/setup.py b/setup.py index 49193f67a611..fb95fb5298f3 100644 --- a/setup.py +++ b/setup.py @@ -18,11 +18,16 @@ cross_compiling = "_PYTHON_HOST_PLATFORM" in os.environ -# Add special CFLAGS reserved for building the interpreter and the stdlib -# modules (Issue #21121). -cflags = sysconfig.get_config_var('CFLAGS') -py_cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST') -sysconfig.get_config_vars()['CFLAGS'] = cflags + ' ' + py_cflags_nodist +# Set common compiler and linker flags derived from the Makefile, +# reserved for building the interpreter and the stdlib modules. +# See bpo-21121 and bpo-35257 +def set_compiler_flags(compiler_flags, compiler_py_flags_nodist): + flags = sysconfig.get_config_var(compiler_flags) + py_flags_nodist = sysconfig.get_config_var(compiler_py_flags_nodist) + sysconfig.get_config_vars()[compiler_flags] = flags + ' ' + py_flags_nodist + +set_compiler_flags('CFLAGS', 'PY_CFLAGS_NODIST') +set_compiler_flags('LDFLAGS', 'PY_LDFLAGS_NODIST') class Dummy: """Hack for parallel build""" From webhook-mailer at python.org Thu Dec 20 15:50:03 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 20:50:03 -0000 Subject: [Python-checkins] bpo-35475: Add more PyImport* functions in refcounts.dat. (GH-11142) (GH-11199) Message-ID: https://github.com/python/cpython/commit/75f187478603de33c15f501a947207bfe8ba833f commit: 75f187478603de33c15f501a947207bfe8ba833f branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Ned Deily date: 2018-12-20T15:50:00-05:00 summary: bpo-35475: Add more PyImport* functions in refcounts.dat. (GH-11142) (GH-11199) (cherry picked from commit bdabb0737c631835b246c9823852d20331243315) Co-authored-by: Serhiy Storchaka files: M Doc/data/refcounts.dat diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 8b469f4aac9a..0ac62abc7d4c 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -516,6 +516,9 @@ Py_InitModule4:int:apiver::usually provided by Py_InitModule or Py_InitModule3 PyImport_AddModule:PyObject*::0:reference borrowed from sys.modules PyImport_AddModule:const char*:name:: +PyImport_AddModuleObject:PyObject*::0:reference borrowed from sys.modules +PyImport_AddModuleObject:PyObject*:name:0: + PyImport_Cleanup:void::: PyImport_ExecCodeModule:PyObject*::+1: @@ -527,6 +530,21 @@ PyImport_ExecCodeModuleEx:const char*:name:: PyImport_ExecCodeModuleEx:PyObject*:co:0: PyImport_ExecCodeModuleEx:const char*:pathname:: +PyImport_ExecCodeModuleObject:PyObject*::+1: +PyImport_ExecCodeModuleObject:const char*:name:: +PyImport_ExecCodeModuleObject:PyObject*:co:0: +PyImport_ExecCodeModuleObject:PyObject*:pathname:0: +PyImport_ExecCodeModuleObject:PyObject*:cpathname:0: + +PyImport_ExecCodeModuleWithPathnames:PyObject*::+1: +PyImport_ExecCodeModuleWithPathnames:const char*:name:: +PyImport_ExecCodeModuleWithPathnames:PyObject*:co:0: +PyImport_ExecCodeModuleWithPathnames:const char*:pathname:: +PyImport_ExecCodeModuleWithPathnames:const char*:cpathname:: + +PyImport_GetImporter:PyObject*::+1: +PyImport_GetImporter:PyObject*:path:0: + PyImport_GetMagicNumber:long::: PyImport_GetModuleDict:PyObject*::0: @@ -537,6 +555,9 @@ PyImport_Import:PyObject*:name:0: PyImport_ImportFrozenModule:int::: PyImport_ImportFrozenModule:const char*::: +PyImport_ImportFrozenModuleObject:int::: +PyImport_ImportFrozenModuleObject:PyObject*::+1: + PyImport_ImportModule:PyObject*::+1: PyImport_ImportModule:const char*:name:: @@ -553,6 +574,13 @@ PyImport_ImportModuleLevel:PyObject*:locals:0:??? PyImport_ImportModuleLevel:PyObject*:fromlist:0:??? PyImport_ImportModuleLevel:int:level:: +PyImport_ImportModuleLevelObject:PyObject*::+1: +PyImport_ImportModuleLevelObject:PyObject*:name:0: +PyImport_ImportModuleLevelObject:PyObject*:globals:0:??? +PyImport_ImportModuleLevelObject:PyObject*:locals:0:??? +PyImport_ImportModuleLevelObject:PyObject*:fromlist:0:??? +PyImport_ImportModuleLevelObject:int:level:: + PyImport_ReloadModule:PyObject*::+1: PyImport_ReloadModule:PyObject*:m:0: From webhook-mailer at python.org Thu Dec 20 16:11:07 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Thu, 20 Dec 2018 21:11:07 -0000 Subject: [Python-checkins] bpo-35259: Limit `Py_FinalizeEx()` to `Py_LIMITED_API >= 0x03060000`. (GH-10620) Message-ID: https://github.com/python/cpython/commit/3e8f962e63c2f929604443531a9a3aced242f3e8 commit: 3e8f962e63c2f929604443531a9a3aced242f3e8 branch: master author: Arthur Neufeld committer: Serhiy Storchaka date: 2018-12-20T23:11:03+02:00 summary: bpo-35259: Limit `Py_FinalizeEx()` to `Py_LIMITED_API >= 0x03060000`. (GH-10620) files: A Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst M Include/pylifecycle.h diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 5419bc943240..1df114a73de3 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -12,7 +12,9 @@ extern "C" { PyAPI_FUNC(void) Py_Initialize(void); PyAPI_FUNC(void) Py_InitializeEx(int); PyAPI_FUNC(void) Py_Finalize(void); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 PyAPI_FUNC(int) Py_FinalizeEx(void); +#endif PyAPI_FUNC(int) Py_IsInitialized(void); /* Subinterpreter support */ diff --git a/Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst b/Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst new file mode 100644 index 000000000000..1f4801cbfb71 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst @@ -0,0 +1,2 @@ +Conditionally declare :c:func:`Py_FinalizeEx()` (new in 3.6) based on +Py_LIMITED_API. Patch by Arthur Neufeld. From webhook-mailer at python.org Thu Dec 20 16:28:34 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 21:28:34 -0000 Subject: [Python-checkins] bpo-35461: Document C API functions which suppress exceptions. (GH-11119) (GH-11210) Message-ID: https://github.com/python/cpython/commit/ff740db42326082fac7d415ae9aff148628a83ed commit: ff740db42326082fac7d415ae9aff148628a83ed branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Ned Deily date: 2018-12-20T16:28:30-05:00 summary: bpo-35461: Document C API functions which suppress exceptions. (GH-11119) (GH-11210) (cherry picked from commit 3fcc1e08db6fb7e17acc4a8f63be3e42f52f094b) Co-authored-by: Serhiy Storchaka files: M Doc/c-api/buffer.rst M Doc/c-api/codec.rst M Doc/c-api/dict.rst M Doc/c-api/mapping.rst M Doc/c-api/number.rst M Doc/c-api/objbuffer.rst M Doc/c-api/object.rst diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 33abb5bb94d9..c7c1e3cc745a 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -429,7 +429,7 @@ Buffer-related functions Return ``1`` if *obj* supports the buffer interface otherwise ``0``. When ``1`` is returned, it doesn't guarantee that :c:func:`PyObject_GetBuffer` will - succeed. + succeed. This function always succeeds. .. c:function:: int PyObject_GetBuffer(PyObject *exporter, Py_buffer *view, int flags) @@ -470,7 +470,7 @@ Buffer-related functions Return ``1`` if the memory defined by the *view* is C-style (*order* is ``'C'``) or Fortran-style (*order* is ``'F'``) :term:`contiguous` or either one - (*order* is ``'A'``). Return ``0`` otherwise. + (*order* is ``'A'``). Return ``0`` otherwise. This function always succeeds. .. c:function:: int PyBuffer_ToContiguous(void *buf, Py_buffer *src, Py_ssize_t len, char order) diff --git a/Doc/c-api/codec.rst b/Doc/c-api/codec.rst index dfe3d436e5f4..c55f19970e12 100644 --- a/Doc/c-api/codec.rst +++ b/Doc/c-api/codec.rst @@ -13,7 +13,7 @@ Codec registry and support functions .. c:function:: int PyCodec_KnownEncoding(const char *encoding) Return ``1`` or ``0`` depending on whether there is a registered codec for - the given *encoding*. + the given *encoding*. This function always succeeds. .. c:function:: PyObject* PyCodec_Encode(PyObject *object, const char *encoding, const char *errors) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index cfa5e13b9fee..68ca25ca548d 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -95,6 +95,10 @@ Dictionary Objects Return the object from dictionary *p* which has a key *key*. Return *NULL* if the key *key* is not present, but *without* setting an exception. + Note that exceptions which occur while calling :meth:`__hash__` and + :meth:`__eq__` methods will get suppressed. + To get error reporting use :c:func:`PyDict_GetItemWithError()` instead. + .. c:function:: PyObject* PyDict_GetItemWithError(PyObject *p, PyObject *key) @@ -109,6 +113,11 @@ Dictionary Objects This is the same as :c:func:`PyDict_GetItem`, but *key* is specified as a :c:type:`char\*`, rather than a :c:type:`PyObject\*`. + Note that exceptions which occur while calling :meth:`__hash__` and + :meth:`__eq__` methods and creating a temporary string object + will get suppressed. + To get error reporting use :c:func:`PyDict_GetItemWithError()` instead. + .. c:function:: PyObject* PyDict_SetDefault(PyObject *p, PyObject *key, PyObject *default) diff --git a/Doc/c-api/mapping.rst b/Doc/c-api/mapping.rst index c16fcf4439d4..cb68ef82ca83 100644 --- a/Doc/c-api/mapping.rst +++ b/Doc/c-api/mapping.rst @@ -60,6 +60,10 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and This is equivalent to the Python expression ``key in o``. This function always succeeds. + Note that exceptions which occur while calling the :meth:`__getitem__` + method will get suppressed. + To get error reporting use :c:func:`PyObject_GetItem()` instead. + .. c:function:: int PyMapping_HasKeyString(PyObject *o, const char *key) @@ -67,6 +71,10 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and This is equivalent to the Python expression ``key in o``. This function always succeeds. + Note that exceptions which occur while calling the :meth:`__getitem__` + method and creating a temporary string object will get suppressed. + To get error reporting use :c:func:`PyMapping_GetItemString()` instead. + .. c:function:: PyObject* PyMapping_Keys(PyObject *o) diff --git a/Doc/c-api/number.rst b/Doc/c-api/number.rst index 3c7605a67fa2..296b21c132b7 100644 --- a/Doc/c-api/number.rst +++ b/Doc/c-api/number.rst @@ -280,3 +280,4 @@ Number Protocol Returns ``1`` if *o* is an index integer (has the nb_index slot of the tp_as_number structure filled in), and ``0`` otherwise. + This function always succeeds. diff --git a/Doc/c-api/objbuffer.rst b/Doc/c-api/objbuffer.rst index e7f4fde00256..9ad7c571c4d8 100644 --- a/Doc/c-api/objbuffer.rst +++ b/Doc/c-api/objbuffer.rst @@ -39,7 +39,11 @@ an object, and :c:func:`PyBuffer_Release` when the buffer view can be released. .. c:function:: int PyObject_CheckReadBuffer(PyObject *o) Returns ``1`` if *o* supports the single-segment readable buffer interface. - Otherwise returns ``0``. + Otherwise returns ``0``. This function always succeeds. + + Note that this function tries to get and release a buffer, and exceptions + which occur while calling correspoding functions will get suppressed. + To get error reporting use :c:func:`PyObject_GetBuffer()` instead. .. c:function:: int PyObject_AsWriteBuffer(PyObject *obj, void **buffer, Py_ssize_t *buffer_len) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 8692a2c14ca2..ea0aa8df31e6 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -33,6 +33,10 @@ Object Protocol is equivalent to the Python expression ``hasattr(o, attr_name)``. This function always succeeds. + Note that exceptions which occur while calling :meth:`__getattr__` and + :meth:`__getattribute__` methods will get suppressed. + To get error reporting use :c:func:`PyObject_GetAttr()` instead. + .. c:function:: int PyObject_HasAttrString(PyObject *o, const char *attr_name) @@ -40,6 +44,11 @@ Object Protocol is equivalent to the Python expression ``hasattr(o, attr_name)``. This function always succeeds. + Note that exceptions which occur while calling :meth:`__getattr__` and + :meth:`__getattribute__` methods and creating a temporary string object + will get suppressed. + To get error reporting use :c:func:`PyObject_GetAttrString()` instead. + .. c:function:: PyObject* PyObject_GetAttr(PyObject *o, PyObject *attr_name) From webhook-mailer at python.org Thu Dec 20 16:34:26 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 21:34:26 -0000 Subject: [Python-checkins] Fix documented signatures for C API functions. (GH-11236) (GH-11240) Message-ID: https://github.com/python/cpython/commit/3a26b59ed61c0779622f7184f440328858d5d23b commit: 3a26b59ed61c0779622f7184f440328858d5d23b branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Ned Deily date: 2018-12-20T16:34:21-05:00 summary: Fix documented signatures for C API functions. (GH-11236) (GH-11240) (cherry picked from commit 57dd79e6f7f33bb4e6817ac58c9cb91de99675e0) Co-authored-by: Serhiy Storchaka files: M Doc/c-api/tuple.rst M Doc/c-api/unicode.rst diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index 3922d50f80a2..ab8ee9abf4c4 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -209,7 +209,7 @@ type. This function "steals" a reference to *o*. -.. c:function:: PyObject* PyStructSequence_SET_ITEM(PyObject *p, Py_ssize_t *pos, PyObject *o) +.. c:function:: void PyStructSequence_SET_ITEM(PyObject *p, Py_ssize_t *pos, PyObject *o) Macro equivalent of :c:func:`PyStructSequence_SetItem`. diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index b9acaec949b2..55d5b070ff3a 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -925,7 +925,7 @@ wchar_t Support Return *NULL* on failure. -.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyUnicodeObject *unicode, wchar_t *w, Py_ssize_t size) +.. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyObject *unicode, wchar_t *w, Py_ssize_t size) Copy the Unicode object contents into the :c:type:`wchar_t` buffer *w*. At most *size* :c:type:`wchar_t` characters are copied (excluding a possibly trailing @@ -1324,7 +1324,7 @@ These are the "Raw Unicode Escape" codec APIs: .. c:function:: PyObject* PyUnicode_EncodeRawUnicodeEscape(const Py_UNICODE *s, \ - Py_ssize_t size, const char *errors) + Py_ssize_t size) Encode the :c:type:`Py_UNICODE` buffer of the given *size* using Raw-Unicode-Escape and return a bytes object. Return *NULL* if an exception was raised by the codec. @@ -1493,8 +1493,8 @@ the user settings on the machine running the codec. Return *NULL* if an exception was raised by the codec. -.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, int size, \ - const char *errors, int *consumed) +.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, Py_ssize_t size, \ + const char *errors, Py_ssize_t *consumed) If *consumed* is *NULL*, behave like :c:func:`PyUnicode_DecodeMBCS`. If *consumed* is not *NULL*, :c:func:`PyUnicode_DecodeMBCSStateful` will not decode From webhook-mailer at python.org Thu Dec 20 16:39:40 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 20 Dec 2018 21:39:40 -0000 Subject: [Python-checkins] bpo-35259: Limit `Py_FinalizeEx()` to `Py_LIMITED_API >= 0x03060000`. (GH-10620) Message-ID: https://github.com/python/cpython/commit/d1e717588728a23d576c4ead775f7dbd68149696 commit: d1e717588728a23d576c4ead775f7dbd68149696 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-12-20T13:39:35-08:00 summary: bpo-35259: Limit `Py_FinalizeEx()` to `Py_LIMITED_API >= 0x03060000`. (GH-10620) (cherry picked from commit 3e8f962e63c2f929604443531a9a3aced242f3e8) Co-authored-by: Arthur Neufeld files: A Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst M Include/pylifecycle.h diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 8e531cf68baf..05f17dc73f08 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -99,7 +99,9 @@ PyAPI_FUNC(void) Py_InitializeEx(int); PyAPI_FUNC(void) _Py_FatalInitError(_PyInitError err) _Py_NO_RETURN; #endif PyAPI_FUNC(void) Py_Finalize(void); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 PyAPI_FUNC(int) Py_FinalizeEx(void); +#endif PyAPI_FUNC(int) Py_IsInitialized(void); /* Subinterpreter support */ diff --git a/Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst b/Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst new file mode 100644 index 000000000000..1f4801cbfb71 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst @@ -0,0 +1,2 @@ +Conditionally declare :c:func:`Py_FinalizeEx()` (new in 3.6) based on +Py_LIMITED_API. Patch by Arthur Neufeld. From webhook-mailer at python.org Thu Dec 20 16:52:15 2018 From: webhook-mailer at python.org (Ned Deily) Date: Thu, 20 Dec 2018 21:52:15 -0000 Subject: [Python-checkins] bpo-35259: Limit `Py_FinalizeEx()` to `Py_LIMITED_API >= 0x03060000`. (GH-10620) (GH-11269) Message-ID: https://github.com/python/cpython/commit/5241ecff161ccf34083b6a824d1ae6d79917d889 commit: 5241ecff161ccf34083b6a824d1ae6d79917d889 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Ned Deily date: 2018-12-20T16:52:09-05:00 summary: bpo-35259: Limit `Py_FinalizeEx()` to `Py_LIMITED_API >= 0x03060000`. (GH-10620) (GH-11269) (cherry picked from commit 3e8f962e63c2f929604443531a9a3aced242f3e8) Co-authored-by: Arthur Neufeld files: A Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst M Include/pylifecycle.h diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 01abfa9fcd6f..9a528218e481 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -27,7 +27,9 @@ PyAPI_FUNC(void) Py_InitializeEx(int); PyAPI_FUNC(void) _Py_InitializeEx_Private(int, int); #endif PyAPI_FUNC(void) Py_Finalize(void); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 PyAPI_FUNC(int) Py_FinalizeEx(void); +#endif PyAPI_FUNC(int) Py_IsInitialized(void); PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void); PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *); diff --git a/Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst b/Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst new file mode 100644 index 000000000000..1f4801cbfb71 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2018-11-22-13-52-36.bpo-35259.p07c61.rst @@ -0,0 +1,2 @@ +Conditionally declare :c:func:`Py_FinalizeEx()` (new in 3.6) based on +Py_LIMITED_API. Patch by Arthur Neufeld. From solipsis at pitrou.net Fri Dec 21 04:08:17 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 21 Dec 2018 09:08:17 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=-1 Message-ID: <20181221090817.1.2DE7CDE9992B369D@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [0, 0, -7] memory blocks, sum=-7 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_fork leaked [0, 2, 0] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogfBAZTg', '--timeout', '7200'] From webhook-mailer at python.org Fri Dec 21 09:45:27 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 21 Dec 2018 14:45:27 -0000 Subject: [Python-checkins] bpo-34193: Fix pluralization in getargs.c and test cases. (GH-8438) Message-ID: https://github.com/python/cpython/commit/6326278e8a3a4b6ac41a74effa63331b1b9fdf5c commit: 6326278e8a3a4b6ac41a74effa63331b1b9fdf5c branch: master author: Xtreak committer: Serhiy Storchaka date: 2018-12-21T16:45:13+02:00 summary: bpo-34193: Fix pluralization in getargs.c and test cases. (GH-8438) files: A Misc/NEWS.d/next/C API/2018-07-24-11-57-35.bpo-34193.M6ch1Q.rst M Lib/test/test_call.py M Lib/test/test_getargs2.py M Objects/typeobject.c M Python/getargs.c diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 45b34e46a5ab..75a4a9ff8123 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -160,19 +160,22 @@ def test_varargs3(self): msg = r"^from_bytes\(\) takes at most 2 positional arguments \(3 given\)" self.assertRaisesRegex(TypeError, msg, int.from_bytes, b'a', 'little', False) - def test_varargs4(self): + def test_varargs1min(self): msg = r"get expected at least 1 argument, got 0" self.assertRaisesRegex(TypeError, msg, {}.get) - def test_varargs5(self): + msg = r"expected 1 argument, got 0" + self.assertRaisesRegex(TypeError, msg, {}.__delattr__) + + def test_varargs2min(self): msg = r"getattr expected at least 2 arguments, got 0" self.assertRaisesRegex(TypeError, msg, getattr) - def test_varargs6(self): + def test_varargs1max(self): msg = r"input expected at most 1 argument, got 2" self.assertRaisesRegex(TypeError, msg, input, 1, 2) - def test_varargs7(self): + def test_varargs2max(self): msg = r"get expected at most 2 arguments, got 3" self.assertRaisesRegex(TypeError, msg, {}.get, 1, 2, 3) diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 44a01a48105f..d9bcb1d75bd0 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -682,11 +682,11 @@ def test_required_args(self): self.assertEqual(self.getargs(1), (1, -1, -1)) # required positional arg missing with self.assertRaisesRegex(TypeError, - r"function takes at least 1 positional arguments \(0 given\)"): + r"function takes at least 1 positional argument \(0 given\)"): self.getargs() with self.assertRaisesRegex(TypeError, - r"function takes at least 1 positional arguments \(0 given\)"): + r"function takes at least 1 positional argument \(0 given\)"): self.getargs(keyword=3) def test_empty_keyword(self): @@ -1112,7 +1112,7 @@ def test_positional_only(self): parse((1,), {'a': 3}, 'OOO', ['', '', 'a']) parse((1,), {}, 'O|OO', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - r'function takes at least 1 positional arguments \(0 given\)'): + r'function takes at least 1 positional argument \(0 given\)'): parse((), {}, 'O|OO', ['', '', 'a']) parse((1, 2), {'a': 3}, 'OO$O', ['', '', 'a']) with self.assertRaisesRegex(TypeError, @@ -1120,7 +1120,7 @@ def test_positional_only(self): parse((1,), {'a': 3}, 'OO$O', ['', '', 'a']) parse((1,), {}, 'O|O$O', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - r'function takes at least 1 positional arguments \(0 given\)'): + r'function takes at least 1 positional argument \(0 given\)'): parse((), {}, 'O|O$O', ['', '', 'a']) with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'): parse((1,), {}, 'O|$OO', ['', '', 'a']) diff --git a/Misc/NEWS.d/next/C API/2018-07-24-11-57-35.bpo-34193.M6ch1Q.rst b/Misc/NEWS.d/next/C API/2018-07-24-11-57-35.bpo-34193.M6ch1Q.rst new file mode 100644 index 000000000000..da2a6116d44e --- /dev/null +++ b/Misc/NEWS.d/next/C API/2018-07-24-11-57-35.bpo-34193.M6ch1Q.rst @@ -0,0 +1,2 @@ +Fix pluralization in TypeError messages in getargs.c and typeobject.c: +'1 argument' instead of '1 arguments' and '1 element' instead of '1 elements'. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d1f1e8cd24b7..ace45ba5794b 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5434,7 +5434,7 @@ check_num_args(PyObject *ob, int n) return 1; PyErr_Format( PyExc_TypeError, - "expected %d arguments, got %zd", n, PyTuple_GET_SIZE(ob)); + "expected %d argument%s, got %zd", n, n == 1 ? "" : "s", PyTuple_GET_SIZE(ob)); return 0; } diff --git a/Python/getargs.c b/Python/getargs.c index ac8bac3bf50b..00e330dc5d36 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -540,8 +540,10 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags, levels[0] = 0; if (toplevel) { PyOS_snprintf(msgbuf, bufsize, - "expected %d arguments, not %" PY_FORMAT_SIZE_T "d", - n, len); + "expected %d argument%s, not %" PY_FORMAT_SIZE_T "d", + n, + n == 1 ? "" : "s", + len); } else { PyOS_snprintf(msgbuf, bufsize, @@ -1718,12 +1720,14 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, } else { PyErr_Format(PyExc_TypeError, - "%.200s%s takes %s %d positional arguments" + "%.200s%s takes %s %d positional argument%s" " (%d given)", (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", (min != INT_MAX) ? "at most" : "exactly", - max, nargs); + max, + max == 1 ? "" : "s", + nargs); } return cleanreturn(0, &freelist); } @@ -1797,12 +1801,14 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (skip) { PyErr_Format(PyExc_TypeError, - "%.200s%s takes %s %d positional arguments" + "%.200s%s takes %s %d positional argument%s" " (%d given)", (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", (Py_MIN(pos, min) < i) ? "at least" : "exactly", - Py_MIN(pos, min), nargs); + Py_MIN(pos, min), + Py_MIN(pos, min) == 1 ? "" : "s", + nargs); return cleanreturn(0, &freelist); } @@ -2104,11 +2110,13 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, } else { PyErr_Format(PyExc_TypeError, - "%.200s%s takes %s %d positional arguments (%d given)", + "%.200s%s takes %s %d positional argument%s (%d given)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", (parser->min != INT_MAX) ? "at most" : "exactly", - parser->max, nargs); + parser->max, + parser->max == 1 ? "" : "s", + nargs); } return cleanreturn(0, &freelist); } @@ -2152,12 +2160,14 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, if (i < pos) { Py_ssize_t min = Py_MIN(pos, parser->min); PyErr_Format(PyExc_TypeError, - "%.200s%s takes %s %d positional arguments" + "%.200s%s takes %s %d positional argument%s" " (%d given)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", min < parser->max ? "at least" : "exactly", - min, nargs); + min, + min == 1 ? "" : "s", + nargs); } else { keyword = PyTuple_GET_ITEM(kwtuple, i - pos); @@ -2417,9 +2427,9 @@ unpack_stack(PyObject *const *args, Py_ssize_t nargs, const char *name, else PyErr_Format( PyExc_TypeError, - "unpacked tuple should have %s%zd elements," + "unpacked tuple should have %s%zd element%s," " but has %zd", - (min == max ? "" : "at least "), min, nargs); + (min == max ? "" : "at least "), min, min == 1 ? "" : "s", nargs); return 0; } @@ -2436,9 +2446,9 @@ unpack_stack(PyObject *const *args, Py_ssize_t nargs, const char *name, else PyErr_Format( PyExc_TypeError, - "unpacked tuple should have %s%zd elements," + "unpacked tuple should have %s%zd element%s," " but has %zd", - (min == max ? "" : "at most "), max, nargs); + (min == max ? "" : "at most "), max, max == 1 ? "" : "s", nargs); return 0; } From webhook-mailer at python.org Fri Dec 21 10:34:49 2018 From: webhook-mailer at python.org (Serhiy Storchaka) Date: Fri, 21 Dec 2018 15:34:49 -0000 Subject: [Python-checkins] bpo-33830: Fix an example in http.client docs for 404. (GH-7780) Message-ID: https://github.com/python/cpython/commit/f0af4c54e32d963e1ccbac005bcbcab1913e051f commit: f0af4c54e32d963e1ccbac005bcbcab1913e051f branch: master author: Xtreak committer: Serhiy Storchaka date: 2018-12-21T17:34:41+02:00 summary: bpo-33830: Fix an example in http.client docs for 404. (GH-7780) files: M Doc/library/http.client.rst diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index c4b7c79730f4..3408c103e2f3 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -497,6 +497,7 @@ Here is an example session that uses the ``GET`` method:: b'\n