diff -Nru python3.8-3.8.6/configure python3.8-3.8.7/configure --- python3.8-3.8.6/configure 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/configure 2020-12-21 16:25:24.000000000 +0000 @@ -7493,6 +7493,31 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MACOSX_DEPLOYMENT_TARGET" >&5 $as_echo "$MACOSX_DEPLOYMENT_TARGET" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if specified universal architectures work" >&5 +$as_echo_n "checking if specified universal architectures work... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +printf("%d", 42); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "check config.log and use the '--with-universal-archs' option" "$LINENO" 5 + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + # end of Darwin* tests ;; esac @@ -10903,10 +10928,10 @@ main() { pthread_attr_t attr; pthread_t id; - if (pthread_attr_init(&attr)) exit(-1); - if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) exit(-1); - if (pthread_create(&id, &attr, foo, NULL)) exit(-1); - exit(0); + if (pthread_attr_init(&attr)) return (-1); + if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) return (-1); + if (pthread_create(&id, &attr, foo, NULL)) return (-1); + return (0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : @@ -14891,7 +14916,7 @@ int main() { /* Success: exit code 0 */ - exit((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); + return ((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); } _ACEOF @@ -15190,13 +15215,7 @@ fi - -case $ac_sys_system in - Linux*|GNU*|Darwin|VxWorks) - EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX};; - *) - EXT_SUFFIX=${SHLIB_SUFFIX};; -esac +EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX} { $as_echo "$as_me:${as_lineno-$LINENO}: checking LDVERSION" >&5 $as_echo_n "checking LDVERSION... " >&6; } @@ -15236,7 +15255,7 @@ int main() { - exit(((-1)>>3 == -1) ? 0 : 1); + return (((-1)>>3 == -1) ? 0 : 1); } _ACEOF @@ -15706,6 +15725,7 @@ /* end confdefs.h. */ #include +#include int main() { diff -Nru python3.8-3.8.6/configure.ac python3.8-3.8.7/configure.ac --- python3.8-3.8.6/configure.ac 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/configure.ac 2020-12-21 16:25:24.000000000 +0000 @@ -1931,6 +1931,13 @@ EXPORT_MACOSX_DEPLOYMENT_TARGET='' AC_MSG_RESULT($MACOSX_DEPLOYMENT_TARGET) + AC_MSG_CHECKING(if specified universal architectures work) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[printf("%d", 42);]])], + [AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + AC_MSG_ERROR(check config.log and use the '--with-universal-archs' option) + ]) + # end of Darwin* tests ;; esac @@ -3203,10 +3210,10 @@ main() { pthread_attr_t attr; pthread_t id; - if (pthread_attr_init(&attr)) exit(-1); - if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) exit(-1); - if (pthread_create(&id, &attr, foo, NULL)) exit(-1); - exit(0); + if (pthread_attr_init(&attr)) return (-1); + if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) return (-1); + if (pthread_create(&id, &attr, foo, NULL)) return (-1); + return (0); }]])], [ac_cv_pthread_system_supported=yes], [ac_cv_pthread_system_supported=no], @@ -4609,7 +4616,7 @@ int main() { /* Success: exit code 0 */ - exit((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); + return ((((wchar_t) -1) < ((wchar_t) 0)) ? 0 : 1); } ]])], [ac_cv_wchar_t_signed=yes], @@ -4667,12 +4674,7 @@ fi AC_SUBST(EXT_SUFFIX) -case $ac_sys_system in - Linux*|GNU*|Darwin|VxWorks) - EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX};; - *) - EXT_SUFFIX=${SHLIB_SUFFIX};; -esac +EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX} AC_MSG_CHECKING(LDVERSION) LDVERSION='$(VERSION)$(ABIFLAGS)' @@ -4702,7 +4704,7 @@ AC_RUN_IFELSE([AC_LANG_SOURCE([[ int main() { - exit(((-1)>>3 == -1) ? 0 : 1); + return (((-1)>>3 == -1) ? 0 : 1); } ]])], [ac_cv_rshift_extends_sign=yes], @@ -4849,6 +4851,7 @@ AC_CACHE_VAL(ac_cv_broken_poll, AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include +#include int main() { diff -Nru python3.8-3.8.6/debian/changelog python3.8-3.8.7/debian/changelog --- python3.8-3.8.6/debian/changelog 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/changelog 2020-12-21 20:10:35.000000000 +0000 @@ -1,3 +1,9 @@ +python3.8 (3.8.7-1+bionic1) bionic; urgency=medium + + * Python 3.8.7 release. + + -- Anthony Sottile Mon, 21 Dec 2020 12:10:35 -0800 + python3.8 (3.8.6-1+bionic2) bionic; urgency=medium * Use faster pgo. diff -Nru python3.8-3.8.6/debian/libpython.symbols.in python3.8-3.8.7/debian/libpython.symbols.in --- python3.8-3.8.6/debian/libpython.symbols.in 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/libpython.symbols.in 2020-12-21 20:10:35.000000000 +0000 @@ -1624,6 +1624,7 @@ _PySet_NextEntry@Base @SVER@ _PySet_Update@Base @SVER@ _PySignal_AfterFork@Base @SVER@ + _PySignal_Init@Base 3.8.7 _PySlice_FromIndices@Base @SVER@ _PySlice_GetLongIndices@Base @SVER@ _PyStack_AsDict@Base @SVER@ diff -Nru python3.8-3.8.6/debian/patches/0033-sysconfigdata-name.patch python3.8-3.8.7/debian/patches/0033-sysconfigdata-name.patch --- python3.8-3.8.6/debian/patches/0033-sysconfigdata-name.patch 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/patches/0033-sysconfigdata-name.patch 2020-12-21 20:10:35.000000000 +0000 @@ -25,7 +25,7 @@ )) _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py -index d941ca9..cf76cb2 100644 +index a17ba62..abadb95 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -345,9 +345,8 @@ def get_makefile_filename(): @@ -65,7 +65,7 @@ -rm -r $(DESTDIR)$(DESTSHARED)/__pycache__ diff --git a/configure.ac b/configure.ac -index 588faa9..58a7774 100644 +index c50e8d8..30f5cae 100644 --- a/configure.ac +++ b/configure.ac @@ -75,7 +75,7 @@ if test "$cross_compiling" = yes; then diff -Nru python3.8-3.8.6/debian/patches/ctypes-arm.diff python3.8-3.8.7/debian/patches/ctypes-arm.diff --- python3.8-3.8.6/debian/patches/ctypes-arm.diff 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/patches/ctypes-arm.diff 2020-12-21 20:10:35.000000000 +0000 @@ -8,10 +8,10 @@ 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py -index 97973bc..00189c7 100644 +index 0c2510e..3907247 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py -@@ -256,16 +256,27 @@ elif os.name == "posix": +@@ -269,16 +269,27 @@ elif os.name == "posix": def _findSoname_ldconfig(name): import struct diff -Nru python3.8-3.8.6/debian/patches/disable-sem-check.diff python3.8-3.8.7/debian/patches/disable-sem-check.diff --- python3.8-3.8.6/debian/patches/disable-sem-check.diff 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/patches/disable-sem-check.diff 2020-12-21 20:10:35.000000000 +0000 @@ -9,10 +9,10 @@ 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac -index 6f4a9f2..588faa9 100644 +index c32e1a1..c50e8d8 100644 --- a/configure.ac +++ b/configure.ac -@@ -4510,8 +4510,13 @@ int main(void) { +@@ -4517,8 +4517,13 @@ int main(void) { AC_MSG_RESULT($ac_cv_posix_semaphores_enabled) if test $ac_cv_posix_semaphores_enabled = no then @@ -28,7 +28,7 @@ fi # Multiprocessing check for broken sem_getvalue -@@ -4546,8 +4551,13 @@ int main(void){ +@@ -4553,8 +4558,13 @@ int main(void){ AC_MSG_RESULT($ac_cv_broken_sem_getvalue) if test $ac_cv_broken_sem_getvalue = yes then diff -Nru python3.8-3.8.6/debian/patches/distutils-install-layout.diff python3.8-3.8.7/debian/patches/distutils-install-layout.diff --- python3.8-3.8.6/debian/patches/distutils-install-layout.diff 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/patches/distutils-install-layout.diff 2020-12-21 20:10:35.000000000 +0000 @@ -276,7 +276,7 @@ sitepackages.append(prefix) sitepackages.append(os.path.join(prefix, "lib", "site-packages")) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py -index 0100865..f82fba3 100644 +index 6b3dba0..0cb5dee 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -267,10 +267,10 @@ class HelperFunctionsTests(unittest.TestCase): diff -Nru python3.8-3.8.6/debian/patches/lib2to3-no-pickled-grammar.diff python3.8-3.8.7/debian/patches/lib2to3-no-pickled-grammar.diff --- python3.8-3.8.6/debian/patches/lib2to3-no-pickled-grammar.diff 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/patches/lib2to3-no-pickled-grammar.diff 2020-12-21 20:10:35.000000000 +0000 @@ -24,7 +24,7 @@ try: g.dump(gp) diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py -index 3a0e7f4..b160d77 100644 +index aafa09c..6e68057 100644 --- a/Lib/lib2to3/tests/test_parser.py +++ b/Lib/lib2to3/tests/test_parser.py @@ -38,83 +38,6 @@ class TestDriver(support.TestCase): diff -Nru python3.8-3.8.6/debian/patches/link-opt.diff python3.8-3.8.7/debian/patches/link-opt.diff --- python3.8-3.8.6/debian/patches/link-opt.diff 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/patches/link-opt.diff 2020-12-21 20:10:35.000000000 +0000 @@ -9,10 +9,10 @@ 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac -index 18a0446..6f4a9f2 100644 +index fc082a3..c32e1a1 100644 --- a/configure.ac +++ b/configure.ac -@@ -2598,8 +2598,8 @@ then +@@ -2605,8 +2605,8 @@ then fi ;; Linux*|GNU*|QNX*|VxWorks*) @@ -23,7 +23,7 @@ FreeBSD*) if [[ "`$CC -dM -E - {% endif %} diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html -index 0edd391..e2f0397 100644 +index 170999e..e439e92 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html -@@ -25,7 +25,7 @@ +@@ -26,7 +26,7 @@ diff -Nru python3.8-3.8.6/debian/patches/multiarch.diff python3.8-3.8.7/debian/patches/multiarch.diff --- python3.8-3.8.6/debian/patches/multiarch.diff 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/patches/multiarch.diff 2020-12-21 20:10:35.000000000 +0000 @@ -23,10 +23,10 @@ elif os.name == "nt": if python_build: diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py -index b9e2faf..d941ca9 100644 +index 9caf158..a17ba62 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py -@@ -558,6 +558,12 @@ def get_config_vars(*args): +@@ -559,6 +559,12 @@ def get_config_vars(*args): # the init-function. _CONFIG_VARS['userbase'] = _getuserbase() diff -Nru python3.8-3.8.6/debian/patches/tkinter-import.diff python3.8-3.8.7/debian/patches/tkinter-import.diff --- python3.8-3.8.6/debian/patches/tkinter-import.diff 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/patches/tkinter-import.diff 2020-12-21 20:10:35.000000000 +0000 @@ -9,7 +9,7 @@ 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py -index 479eb01..6ff4df5 100644 +index f9ece25..ea2292c 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -33,7 +33,10 @@ tk.mainloop() diff -Nru python3.8-3.8.6/debian/rules python3.8-3.8.7/debian/rules --- python3.8-3.8.6/debian/rules 2020-10-06 03:22:36.000000000 +0000 +++ python3.8-3.8.7/debian/rules 2020-12-21 20:10:35.000000000 +0000 @@ -83,7 +83,7 @@ else echo Unknown; fi) VER=3.8 -SVER=3.8.6 +SVER=3.8.7 NVER=3.9 PVER=python$(VER) EXT_VER=$(subst .,,$(VER)) diff -Nru python3.8-3.8.6/Doc/c-api/dict.rst python3.8-3.8.7/Doc/c-api/dict.rst --- python3.8-3.8.6/Doc/c-api/dict.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/c-api/dict.rst 2020-12-21 16:25:24.000000000 +0000 @@ -81,14 +81,16 @@ .. c:function:: int PyDict_DelItem(PyObject *p, PyObject *key) Remove the entry in dictionary *p* with key *key*. *key* must be hashable; - if it isn't, :exc:`TypeError` is raised. Return ``0`` on success or ``-1`` - on failure. + if it isn't, :exc:`TypeError` is raised. + If *key* is not in the dictionary, :exc:`KeyError` is raised. + Return ``0`` on success or ``-1`` on failure. .. c:function:: int PyDict_DelItemString(PyObject *p, const char *key) - Remove the entry in dictionary *p* which has a key specified by the string - *key*. Return ``0`` on success or ``-1`` on failure. + Remove the entry in dictionary *p* which has a key specified by the string *key*. + If *key* is not in the dictionary, :exc:`KeyError` is raised. + Return ``0`` on success or ``-1`` on failure. .. c:function:: PyObject* PyDict_GetItem(PyObject *p, PyObject *key) diff -Nru python3.8-3.8.6/Doc/c-api/file.rst python3.8-3.8.7/Doc/c-api/file.rst --- python3.8-3.8.6/Doc/c-api/file.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/c-api/file.rst 2020-12-21 16:25:24.000000000 +0000 @@ -82,6 +82,8 @@ This function is safe to call before :c:func:`Py_Initialize`. + .. audit-event:: setopencodehook "" c.PyFile_SetOpenCodeHook + .. versionadded:: 3.8 diff -Nru python3.8-3.8.6/Doc/c-api/type.rst python3.8-3.8.7/Doc/c-api/type.rst --- python3.8-3.8.6/Doc/c-api/type.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/c-api/type.rst 2020-12-21 16:25:24.000000000 +0000 @@ -124,7 +124,8 @@ If *bases* is a tuple, the created heap type contains all types contained in it as base types. - If *bases* is ``NULL``, the *Py_tp_base* slot is used instead. + If *bases* is ``NULL``, the *Py_tp_bases* slot is used instead. + If that also is ``NULL``, the *Py_tp_base* slot is used instead. If that also is ``NULL``, the new type derives from :class:`object`. This function calls :c:func:`PyType_Ready` on the new type. @@ -194,7 +195,8 @@ * :c:member:`~PyBufferProcs.bf_getbuffer` * :c:member:`~PyBufferProcs.bf_releasebuffer` - Setting :c:data:`Py_tp_bases` may be problematic on some platforms. + Setting :c:data:`Py_tp_bases` or :c:data:`Py_tp_base` may be + problematic on some platforms. To avoid issues, use the *bases* argument of :py:func:`PyType_FromSpecWithBases` instead. diff -Nru python3.8-3.8.6/Doc/faq/programming.rst python3.8-3.8.7/Doc/faq/programming.rst --- python3.8-3.8.6/Doc/faq/programming.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/faq/programming.rst 2020-12-21 16:25:24.000000000 +0000 @@ -1121,7 +1121,7 @@ How do I iterate over a sequence in reverse order? -------------------------------------------------- -Use the :func:`reversed` built-in function, which is new in Python 2.4:: +Use the :func:`reversed` built-in function:: for x in reversed(sequence): ... # do something with x ... @@ -1129,11 +1129,6 @@ This won't touch your original sequence, but build a new copy with reversed order to iterate over. -With Python 2.3, you can use an extended slice syntax:: - - for x in sequence[::-1]: - ... # do something with x ... - How do you remove duplicates from a list? ----------------------------------------- @@ -1163,6 +1158,21 @@ into a list. +How do you remove multiple items from a list +-------------------------------------------- + +As with removing duplicates, explicitly iterating in reverse with a +delete condition is one possibility. However, it is easier and faster +to use slice replacement with an implicit or explicit forward iteration. +Here are three variations.:: + + mylist[:] = filter(keep_function, mylist) + mylist[:] = (x for x in mylist if keep_condition) + mylist[:] = [x for x in mylist if keep_condition] + +The list comprehension may be fastest. + + How do you make an array in Python? ----------------------------------- diff -Nru python3.8-3.8.6/Doc/faq/windows.rst python3.8-3.8.7/Doc/faq/windows.rst --- python3.8-3.8.6/Doc/faq/windows.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/faq/windows.rst 2020-12-21 16:25:24.000000000 +0000 @@ -140,7 +140,7 @@ How do I make an executable from a Python script? ------------------------------------------------- -See `cx_Freeze `_ for a distutils extension +See `cx_Freeze `_ for a distutils extension that allows you to create console and GUI executables from Python code. `py2exe `_, the most popular extension for building Python 2.x-based executables, does not yet support Python 3 but a version that @@ -279,7 +279,7 @@ How do I check for a keypress without blocking? ----------------------------------------------- -Use the msvcrt module. This is a standard Windows-specific extension module. +Use the :mod:`msvcrt` module. This is a standard Windows-specific extension module. It defines a function ``kbhit()`` which checks whether a keyboard hit is present, and ``getch()`` which gets one character without echoing it. diff -Nru python3.8-3.8.6/Doc/glossary.rst python3.8-3.8.7/Doc/glossary.rst --- python3.8-3.8.6/Doc/glossary.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/glossary.rst 2020-12-21 16:25:24.000000000 +0000 @@ -308,6 +308,12 @@ keys can be any object with :meth:`__hash__` and :meth:`__eq__` methods. Called a hash in Perl. + dictionary comprehension + A compact way to process all or part of the elements in an iterable and + return a dictionary with the results. ``results = {n: n ** 2 for n in + range(10)}`` generates a dictionary containing key ``n`` mapped to + value ``n ** 2``. See :ref:`comprehensions`. + dictionary view The objects returned from :meth:`dict.keys`, :meth:`dict.values`, and :meth:`dict.items` are called dictionary views. They provide a dynamic @@ -1024,7 +1030,13 @@ :meth:`index`, :meth:`__contains__`, and :meth:`__reversed__`. Types that implement this expanded interface can be registered explicitly using - :func:`~abc.register`. + :func:`~abc.ABCMeta.register`. + + set comprehension + A compact way to process all or part of the elements in an iterable and + return a set with the results. ``results = {c for c in 'abracadabra' if + c not in 'abc'}`` generates the set of strings ``{'r', 'd'}``. See + :ref:`comprehensions`. single dispatch A form of :term:`generic function` dispatch where the implementation is diff -Nru python3.8-3.8.6/Doc/library/asyncio-policy.rst python3.8-3.8.7/Doc/library/asyncio-policy.rst --- python3.8-3.8.6/Doc/library/asyncio-policy.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/asyncio-policy.rst 2020-12-21 16:25:24.000000000 +0000 @@ -209,7 +209,7 @@ It works reliably even when the asyncio event loop is run in a non-main OS thread. There is no noticeable overhead when handling a big number of children (*O(1)* each - time a child terminates), but stating a thread per process requires extra memory. + time a child terminates), but starting a thread per process requires extra memory. This watcher is used by default. @@ -219,7 +219,7 @@ This implementation registers a :py:data:`SIGCHLD` signal handler on instantiation. That can break third-party code that installs a custom handler for - `SIGCHLD`. signal). + :py:data:`SIGCHLD` signal. The watcher avoids disrupting other code spawning processes by polling every process explicitly on a :py:data:`SIGCHLD` signal. diff -Nru python3.8-3.8.6/Doc/library/asyncio-subprocess.rst python3.8-3.8.7/Doc/library/asyncio-subprocess.rst --- python3.8-3.8.6/Doc/library/asyncio-subprocess.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/asyncio-subprocess.rst 2020-12-21 16:25:24.000000000 +0000 @@ -110,10 +110,8 @@ .. note:: - The default asyncio event loop implementation on **Windows** does not - support subprocesses. Subprocesses are available for Windows if a - :class:`ProactorEventLoop` is used. - See :ref:`Subprocess Support on Windows ` + Subprocesses are available for Windows if a :class:`ProactorEventLoop` is + used. See :ref:`Subprocess Support on Windows ` for details. .. seealso:: diff -Nru python3.8-3.8.6/Doc/library/asyncio-task.rst python3.8-3.8.7/Doc/library/asyncio-task.rst --- python3.8-3.8.6/Doc/library/asyncio-task.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/asyncio-task.rst 2020-12-21 16:25:24.000000000 +0000 @@ -501,7 +501,7 @@ return_when=ALL_COMPLETED) Run :ref:`awaitable objects ` in the *aws* - set concurrently and block until the condition specified + iterable concurrently and block until the condition specified by *return_when*. Returns two sets of Tasks/Futures: ``(done, pending)``. @@ -588,9 +588,9 @@ .. function:: as_completed(aws, \*, loop=None, timeout=None) Run :ref:`awaitable objects ` in the *aws* - set concurrently. Return an iterator of coroutines. + iterable concurrently. Return an iterator of coroutines. Each coroutine returned can be awaited to get the earliest next - result from the set of the remaining awaitables. + result from the iterable of the remaining awaitables. Raises :exc:`asyncio.TimeoutError` if the timeout occurs before all Futures are done. diff -Nru python3.8-3.8.6/Doc/library/audit_events.rst python3.8-3.8.7/Doc/library/audit_events.rst --- python3.8-3.8.6/Doc/library/audit_events.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/audit_events.rst 2020-12-21 16:25:24.000000000 +0000 @@ -19,3 +19,29 @@ specific documentation for actual events raised. .. audit-event-table:: + +The following events are raised internally and do not correspond to any +public API of CPython: + ++--------------------------+-------------------------------------------+ +| Audit event | Arguments | ++==========================+===========================================+ +| _winapi.CreateFile | ``file_name``, ``desired_access``, | +| | ``share_mode``, ``creation_disposition``, | +| | ``flags_and_attributes`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreateJunction | ``src_path``, ``dst_path`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreateNamedPipe | ``name``, ``open_mode``, ``pipe_mode`` | ++--------------------------+-------------------------------------------+ +| _winapi.CreatePipe | | ++--------------------------+-------------------------------------------+ +| _winapi.CreateProcess | ``application_name``, ``command_line``, | +| | ``current_directory`` | ++--------------------------+-------------------------------------------+ +| _winapi.OpenProcess | ``process_id``, ``desired_access`` | ++--------------------------+-------------------------------------------+ +| _winapi.TerminateProcess | ``handle``, ``exit_code`` | ++--------------------------+-------------------------------------------+ +| ctypes.PyObj_FromPtr | ``obj`` | ++--------------------------+-------------------------------------------+ diff -Nru python3.8-3.8.6/Doc/library/bz2.rst python3.8-3.8.7/Doc/library/bz2.rst --- python3.8-3.8.6/Doc/library/bz2.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/bz2.rst 2020-12-21 16:25:24.000000000 +0000 @@ -264,7 +264,6 @@ Using :func:`compress` and :func:`decompress` to demonstrate round-trip compression: >>> import bz2 - >>> data = b"""\ ... Donec rhoncus quis sapien sit amet molestie. Fusce scelerisque vel augue ... nec ullamcorper. Nam rutrum pretium placerat. Aliquam vel tristique lorem, @@ -273,11 +272,9 @@ ... Aliquam pharetra lacus non risus vehicula rutrum. Maecenas aliquam leo ... felis. Pellentesque semper nunc sit amet nibh ullamcorper, ac elementum ... dolor luctus. Curabitur lacinia mi ornare consectetur vestibulum.""" - >>> c = bz2.compress(data) >>> len(data) / len(c) # Data compression ratio 1.513595166163142 - >>> d = bz2.decompress(c) >>> data == d # Check equality to original object after round-trip True @@ -285,7 +282,6 @@ Using :class:`BZ2Compressor` for incremental compression: >>> import bz2 - >>> def gen_data(chunks=10, chunksize=1000): ... """Yield incremental blocks of chunksize bytes.""" ... for _ in range(chunks): @@ -308,7 +304,6 @@ Writing and reading a bzip2-compressed file in binary mode: >>> import bz2 - >>> data = b"""\ ... Donec rhoncus quis sapien sit amet molestie. Fusce scelerisque vel augue ... nec ullamcorper. Nam rutrum pretium placerat. Aliquam vel tristique lorem, @@ -317,14 +312,11 @@ ... Aliquam pharetra lacus non risus vehicula rutrum. Maecenas aliquam leo ... felis. Pellentesque semper nunc sit amet nibh ullamcorper, ac elementum ... dolor luctus. Curabitur lacinia mi ornare consectetur vestibulum.""" - >>> with bz2.open("myfile.bz2", "wb") as f: ... # Write compressed data to file ... unused = f.write(data) - >>> with bz2.open("myfile.bz2", "rb") as f: ... # Decompress data from file ... content = f.read() - >>> content == data # Check equality to original object after round-trip True diff -Nru python3.8-3.8.6/Doc/library/concurrent.futures.rst python3.8-3.8.7/Doc/library/concurrent.futures.rst --- python3.8-3.8.6/Doc/library/concurrent.futures.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/concurrent.futures.rst 2020-12-21 16:25:24.000000000 +0000 @@ -224,9 +224,9 @@ An :class:`Executor` subclass that executes calls asynchronously using a pool of at most *max_workers* processes. If *max_workers* is ``None`` or not given, it will default to the number of processors on the machine. - If *max_workers* is lower or equal to ``0``, then a :exc:`ValueError` + If *max_workers* is less than or equal to ``0``, then a :exc:`ValueError` will be raised. - On Windows, *max_workers* must be equal or lower than ``61``. If it is not + On Windows, *max_workers* must be less than or equal to ``61``. If it is not then :exc:`ValueError` will be raised. If *max_workers* is ``None``, then the default chosen will be at most ``61``, even if more processors are available. @@ -238,7 +238,7 @@ each worker process; *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.process.BrokenProcessPool`, - 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.3 When one of the worker processes terminates abruptly, a diff -Nru python3.8-3.8.6/Doc/library/ctypes.rst python3.8-3.8.7/Doc/library/ctypes.rst --- python3.8-3.8.6/Doc/library/ctypes.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/ctypes.rst 2020-12-21 16:25:24.000000000 +0000 @@ -1326,6 +1326,21 @@ libraries use the standard C calling convention, and are assumed to return :c:type:`int`. + On Windows creating a :class:`CDLL` instance may fail even if the DLL name + exists. When a dependent DLL of the loaded DLL is not found, a + :exc:`OSError` error is raised with the message *"[WinError 126] The + specified module could not be found".* This error message does not contain + the name of the missing DLL because the Windows API does not return this + information making this error hard to diagnose. To resolve this error and + determine which DLL is not found, you need to find the list of dependent + DLLs and determine which one is not found using Windows debugging and + tracing tools. + +.. seealso:: + + `Microsoft DUMPBIN tool `_ + -- A tool to find DLL dependents. + .. class:: OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0) @@ -1618,7 +1633,7 @@ ``ctypes.seh_exception`` with argument ``code`` will be raised, allowing an audit hook to replace the exception with its own. -.. audit-event:: ctypes.call_function func_pointer,arguments ctype-foreign-functions +.. audit-event:: ctypes.call_function func_pointer,arguments foreign-functions Some ways to invoke foreign function calls may raise an auditing event ``ctypes.call_function`` with arguments ``function pointer`` and ``arguments``. @@ -2545,4 +2560,3 @@ Returns the object to which to pointer points. Assigning to this attribute changes the pointer to point to the assigned object. - diff -Nru python3.8-3.8.6/Doc/library/functions.rst python3.8-3.8.7/Doc/library/functions.rst --- python3.8-3.8.6/Doc/library/functions.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/functions.rst 2020-12-21 16:25:24.000000000 +0000 @@ -151,8 +151,8 @@ * If it is an *integer*, the array will have that size and will be initialized with null bytes. - * If it is an object conforming to the *buffer* interface, a read-only buffer - of the object will be used to initialize the bytes array. + * If it is an object conforming to the :ref:`buffer interface `, + a read-only buffer of the object will be used to initialize the bytes array. * If it is an *iterable*, it must be an iterable of integers in the range ``0 <= x < 256``, which are used as the initial contents of the array. @@ -767,6 +767,8 @@ .. impl-detail:: This is the address of the object in memory. + .. audit-event:: builtins.id id id + .. function:: input([prompt]) diff -Nru python3.8-3.8.6/Doc/library/imaplib.rst python3.8-3.8.7/Doc/library/imaplib.rst --- python3.8-3.8.6/Doc/library/imaplib.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/imaplib.rst 2020-12-21 16:25:24.000000000 +0000 @@ -163,9 +163,9 @@ .. seealso:: - Documents describing the protocol, and sources and binaries for servers - implementing it, can all be found at the University of Washington's *IMAP - Information Center* (https://www.washington.edu/imap/). + Documents describing the protocol, sources for servers + implementing it, by the University of Washington's IMAP Information Center + can all be found at (**Source Code**) https://github.com/uw-imap/imap (**Not Maintained**). .. _imap4-objects: diff -Nru python3.8-3.8.6/Doc/library/importlib.metadata.rst python3.8-3.8.7/Doc/library/importlib.metadata.rst --- python3.8-3.8.6/Doc/library/importlib.metadata.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/importlib.metadata.rst 2020-12-21 16:25:24.000000000 +0000 @@ -1,8 +1,8 @@ .. _using: -========================== - Using importlib.metadata -========================== +================================= + Using :mod:`!importlib.metadata` +================================= .. note:: This functionality is provisional and may deviate from the usual @@ -12,8 +12,8 @@ package metadata. Built in part on Python's import system, this library intends to replace similar functionality in the `entry point API`_ and `metadata API`_ of ``pkg_resources``. Along with -``importlib.resources`` in `Python 3.7 -and newer`_ (backported as `importlib_resources`_ for older versions of +:mod:`importlib.resources` in Python 3.7 +and newer (backported as `importlib_resources`_ for older versions of Python), this can eliminate the need to use the older and less efficient ``pkg_resources`` package. @@ -21,9 +21,9 @@ Python's ``site-packages`` directory via tools such as `pip `_. Specifically, it means a package with either a discoverable ``dist-info`` or ``egg-info`` -directory, and metadata defined by `PEP 566`_ or its older specifications. +directory, and metadata defined by :pep:`566` or its older specifications. By default, package metadata can live on the file system or in zip archives on -``sys.path``. Through an extension mechanism, the metadata can live almost +:data:`sys.path`. Through an extension mechanism, the metadata can live almost anywhere. @@ -134,7 +134,7 @@ You can also get the full set of files contained within a distribution. The ``files()`` function takes a distribution package name and returns all of the files installed by this distribution. Each file object returned is a -``PackagePath``, a `pathlib.Path`_ derived object with additional ``dist``, +``PackagePath``, a :class:`pathlib.Path` derived object with additional ``dist``, ``size``, and ``hash`` properties as indicated by the metadata. For example:: >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0] # doctest: +SKIP @@ -203,18 +203,18 @@ >>> d.metadata['License'] # doctest: +SKIP 'MIT' -The full set of available metadata is not described here. See `PEP 566 -`_ for additional details. +The full set of available metadata is not described here. See :pep:`566` +for additional details. Extending the search algorithm ============================== -Because package metadata is not available through ``sys.path`` searches, or +Because package metadata is not available through :data:`sys.path` searches, or package loaders directly, the metadata for a package is found through import -system `finders`_. To find a distribution package's metadata, -``importlib.metadata`` queries the list of `meta path finders`_ on -`sys.meta_path`_. +system :ref:`finders `. To find a distribution package's metadata, +``importlib.metadata`` queries the list of :term:`meta path finders ` on +:data:`sys.meta_path`. The default ``PathFinder`` for Python includes a hook that calls into ``importlib.metadata.MetadataPathFinder`` for finding distributions @@ -224,7 +224,7 @@ interface expected of finders by Python's import system. ``importlib.metadata`` extends this protocol by looking for an optional ``find_distributions`` callable on the finders from -``sys.meta_path`` and presents this extended interface as the +:data:`sys.meta_path` and presents this extended interface as the ``DistributionFinder`` abstract base class, which defines this abstract method:: @@ -247,20 +247,13 @@ .. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points .. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api -.. _`Python 3.7 and newer`: https://docs.python.org/3/library/importlib.html#module-importlib.resources .. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html -.. _`PEP 566`: https://www.python.org/dev/peps/pep-0566/ -.. _`finders`: https://docs.python.org/3/reference/import.html#finders-and-loaders -.. _`meta path finders`: https://docs.python.org/3/glossary.html#term-meta-path-finder -.. _`sys.meta_path`: https://docs.python.org/3/library/sys.html#sys.meta_path -.. _`pathlib.Path`: https://docs.python.org/3/library/pathlib.html#pathlib.Path .. rubric:: Footnotes .. [#f1] Technically, the returned distribution metadata object is an - `email.message.Message - `_ + :class:`email.message.EmailMessage` instance, but this is an implementation detail, and not part of the stable API. You should only use dictionary-like methods and syntax to access the metadata contents. diff -Nru python3.8-3.8.6/Doc/library/importlib.rst python3.8-3.8.7/Doc/library/importlib.rst --- python3.8-3.8.6/Doc/library/importlib.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/importlib.rst 2020-12-21 16:25:24.000000000 +0000 @@ -438,8 +438,9 @@ package. This attribute is not set on modules. - :attr:`__package__` - The parent package for the module/package. If the module is - top-level then it has a value of the empty string. The + The fully-qualified name of the package under which the module was + loaded as a submodule (or the empty string for top-level modules). + For packages, it is the same as :attr:`__name__`. The :func:`importlib.util.module_for_loader` decorator can handle the details for :attr:`__package__`. @@ -1310,8 +1311,8 @@ (``__loader__``) - The loader to use for loading. For namespace packages this should be - set to ``None``. + The :term:`Loader ` that should be used when loading + the module. :term:`Finders ` should always set this. .. attribute:: origin @@ -1344,8 +1345,9 @@ (``__package__``) - (Read-only) Fully-qualified name of the package to which the module - belongs as a submodule (or ``None``). + (Read-only) The fully-qualified name of the package under which the module + should be loaded as a submodule (or the empty string for top-level modules). + For packages, it is the same as :attr:`__name__`. .. attribute:: has_location diff -Nru python3.8-3.8.6/Doc/library/logging.rst python3.8-3.8.7/Doc/library/logging.rst --- python3.8-3.8.6/Doc/library/logging.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/logging.rst 2020-12-21 16:25:24.000000000 +0000 @@ -1083,8 +1083,8 @@ suitable value. .. versionchanged:: 3.7 - The *level* parameter was defaulted to level ``CRITICAL``. See Issue - #28524 for more information about this change. + The *level* parameter was defaulted to level ``CRITICAL``. See + :issue:`28524` for more information about this change. .. function:: addLevelName(level, levelName) diff -Nru python3.8-3.8.6/Doc/library/mailbox.rst python3.8-3.8.7/Doc/library/mailbox.rst --- python3.8-3.8.6/Doc/library/mailbox.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/mailbox.rst 2020-12-21 16:25:24.000000000 +0000 @@ -426,17 +426,14 @@ .. seealso:: - `maildir man page from qmail `_ - The original specification of the format. + `maildir man page from Courier `_ + A specification of the format. Describes a common extension for + supporting folders. `Using maildir format `_ Notes on Maildir by its inventor. Includes an updated name-creation scheme and details on "info" semantics. - `maildir man page from Courier `_ - Another specification of the format. Describes a common extension for supporting - folders. - .. _mailbox-mbox: @@ -485,11 +482,8 @@ .. seealso:: - `mbox man page from qmail `_ - A specification of the format and its variations. - `mbox man page from tin `_ - Another specification of the format, with details on locking. + A specification of the format, with details on locking. `Configuring Netscape Mail on Unix: Why The Content-Length Format is Bad `_ An argument for using the original mbox format rather than a variation. diff -Nru python3.8-3.8.6/Doc/library/multiprocessing.rst python3.8-3.8.7/Doc/library/multiprocessing.rst --- python3.8-3.8.6/Doc/library/multiprocessing.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/multiprocessing.rst 2020-12-21 16:25:24.000000000 +0000 @@ -98,7 +98,7 @@ *spawn* The parent process starts a fresh python interpreter process. The child process will only inherit those resources necessary to run - the process objects :meth:`~Process.run` method. In particular, + the process object's :meth:`~Process.run` method. In particular, unnecessary file descriptors and handles from the parent process will not be inherited. Starting a process using this method is rather slow compared to using *fork* or *forkserver*. @@ -2559,9 +2559,9 @@ filesystem. * An ``'AF_PIPE'`` address is a string of the form - :samp:`r'\\\\.\\pipe\\{PipeName}'`. To use :func:`Client` to connect to a named - pipe on a remote computer called *ServerName* one should use an address of the - form :samp:`r'\\\\{ServerName}\\pipe\\{PipeName}'` instead. + :samp:`r'\\\\.\\pipe\\{PipeName}'`. To use :func:`Client` to connect to a named + pipe on a remote computer called *ServerName* one should use an address of the + form :samp:`r'\\\\{ServerName}\\pipe\\{PipeName}'` instead. Note that any string beginning with two backslashes is assumed by default to be an ``'AF_PIPE'`` address rather than an ``'AF_UNIX'`` address. @@ -2651,6 +2651,46 @@ :mod:`multiprocessing.dummy` replicates the API of :mod:`multiprocessing` but is no more than a wrapper around the :mod:`threading` module. +.. currentmodule:: multiprocessing.pool + +In particular, the ``Pool`` function provided by :mod:`multiprocessing.dummy` +returns an instance of :class:`ThreadPool`, which is a subclass of +:class:`Pool` that supports all the same method calls but uses a pool of +worker threads rather than worker processes. + + +.. class:: ThreadPool([processes[, initializer[, initargs]]]) + + A thread pool object which controls a pool of worker threads to which jobs + can be submitted. :class:`ThreadPool` instances are fully interface + compatible with :class:`Pool` instances, and their resources must also be + properly managed, either by using the pool as a context manager or by + calling :meth:`~multiprocessing.pool.Pool.close` and + :meth:`~multiprocessing.pool.Pool.terminate` manually. + + *processes* is the number of worker threads to use. If *processes* is + ``None`` then the number returned by :func:`os.cpu_count` is used. + + If *initializer* is not ``None`` then each worker process will call + ``initializer(*initargs)`` when it starts. + + Unlike :class:`Pool`, *maxtasksperchild* and *context* cannot be provided. + + .. note:: + + A :class:`ThreadPool` shares the same interface as :class:`Pool`, which + is designed around a pool of processes and predates the introduction of + the :class:`concurrent.futures` module. As such, it inherits some + operations that don't make sense for a pool backed by threads, and it + has its own type for representing the status of asynchronous jobs, + :class:`AsyncResult`, that is not understood by any other libraries. + + Users should generally prefer to use + :class:`concurrent.futures.ThreadPoolExecutor`, which has a simpler + interface that was designed around threads from the start, and which + returns :class:`concurrent.futures.Future` instances that are + compatible with many other libraries, including :mod:`asyncio`. + .. _multiprocessing-programming: diff -Nru python3.8-3.8.6/Doc/library/optparse.rst python3.8-3.8.7/Doc/library/optparse.rst --- python3.8-3.8.6/Doc/library/optparse.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/optparse.rst 2020-12-21 16:25:24.000000000 +0000 @@ -55,7 +55,7 @@ -q -foutfile -qfoutfile -Additionally, users can run one of :: +Additionally, users can run one of the following :: -h --help diff -Nru python3.8-3.8.6/Doc/library/os.rst python3.8-3.8.7/Doc/library/os.rst --- python3.8-3.8.6/Doc/library/os.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/os.rst 2020-12-21 16:25:24.000000000 +0000 @@ -3664,8 +3664,8 @@ The positional-only arguments *path*, *args*, and *env* are similar to :func:`execve`. - The *path* parameter is the path to the executable file.The *path* should - contain a directory.Use :func:`posix_spawnp` to pass an executable file + The *path* parameter is the path to the executable file. The *path* should + contain a directory. Use :func:`posix_spawnp` to pass an executable file without directory. The *file_actions* argument may be a sequence of tuples describing actions diff -Nru python3.8-3.8.6/Doc/library/pathlib.rst python3.8-3.8.7/Doc/library/pathlib.rst --- python3.8-3.8.6/Doc/library/pathlib.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/pathlib.rst 2020-12-21 16:25:24.000000000 +0000 @@ -954,6 +954,10 @@ >>> target.open().read() 'some text' + The target path may be absolute or relative. Relative paths are interpreted + relative to the current working directory, *not* the directory of the Path + object. + .. versionchanged:: 3.8 Added return value, return the new Path instance. @@ -964,6 +968,10 @@ instance pointing to *target*. If *target* points to an existing file or directory, it will be unconditionally replaced. + The target path may be absolute or relative. Relative paths are interpreted + relative to the current working directory, *not* the directory of the Path + object. + .. versionchanged:: 3.8 Added return value, return the new Path instance. diff -Nru python3.8-3.8.6/Doc/library/platform.rst python3.8-3.8.7/Doc/library/platform.rst --- python3.8-3.8.6/Doc/library/platform.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/platform.rst 2020-12-21 16:25:24.000000000 +0000 @@ -209,13 +209,6 @@ which means the OS version uses debugging code, i.e. code that checks arguments, ranges, etc. - .. note:: - - This function works best with Mark Hammond's - :mod:`win32all` package installed, but also on Python 2.3 and - later (support for this was added in Python 2.6). It obviously - only runs on Win32 compatible platforms. - .. function:: win32_edition() Returns a string representing the current Windows edition. Possible diff -Nru python3.8-3.8.6/Doc/library/poplib.rst python3.8-3.8.7/Doc/library/poplib.rst --- python3.8-3.8.6/Doc/library/poplib.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/poplib.rst 2020-12-21 16:25:24.000000000 +0000 @@ -64,7 +64,7 @@ .. audit-event:: poplib.connect self,host,port poplib.POP3_SSL - .. audit-event:: poplib.putline self,line popplib.POP3_SSL + .. audit-event:: poplib.putline self,line poplib.POP3_SSL All commands will raise an :ref:`auditing event ` ``poplib.putline`` with arguments ``self`` and ``line``, diff -Nru python3.8-3.8.6/Doc/library/secrets.rst python3.8-3.8.7/Doc/library/secrets.rst --- python3.8-3.8.6/Doc/library/secrets.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/secrets.rst 2020-12-21 16:25:24.000000000 +0000 @@ -21,7 +21,7 @@ random numbers suitable for managing data such as passwords, account authentication, security tokens, and related secrets. -In particularly, :mod:`secrets` should be used in preference to the +In particular, :mod:`secrets` should be used in preference to the default pseudo-random number generator in the :mod:`random` module, which is designed for modelling and simulation, not security or cryptography. diff -Nru python3.8-3.8.6/Doc/library/shutil.rst python3.8-3.8.7/Doc/library/shutil.rst --- python3.8-3.8.6/Doc/library/shutil.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/shutil.rst 2020-12-21 16:25:24.000000000 +0000 @@ -158,9 +158,9 @@ .. function:: copy(src, dst, *, follow_symlinks=True) Copies the file *src* to the file or directory *dst*. *src* and *dst* - should be strings. If *dst* specifies a directory, the file will be - copied into *dst* using the base filename from *src*. Returns the - path to the newly created file. + should be :term:`path-like objects ` or strings. If + *dst* specifies a directory, the file will be copied into *dst* using the + base filename from *src*. Returns the path to the newly created file. If *follow_symlinks* is false, and *src* is a symbolic link, *dst* will be created as a symbolic link. If *follow_symlinks* @@ -349,7 +349,7 @@ will be created in or as *dst* and *src* will be removed. If *copy_function* is given, it must be a callable that takes two arguments - *src* and *dst*, and will be used to copy *src* to *dest* if + *src* and *dst*, and will be used to copy *src* to *dst* if :func:`os.rename` cannot be used. If the source is a directory, :func:`copytree` is called, passing it the :func:`copy_function`. The default *copy_function* is :func:`copy2`. Using :func:`~shutil.copy` as the diff -Nru python3.8-3.8.6/Doc/library/signal.rst python3.8-3.8.7/Doc/library/signal.rst --- python3.8-3.8.6/Doc/library/signal.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/signal.rst 2020-12-21 16:25:24.000000000 +0000 @@ -117,7 +117,7 @@ Child process stopped or terminated. - .. availability:: Windows. + .. availability:: Unix. .. data:: SIGCLD diff -Nru python3.8-3.8.6/Doc/library/site.rst python3.8-3.8.7/Doc/library/site.rst --- python3.8-3.8.6/Doc/library/site.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/site.rst 2020-12-21 16:25:24.000000000 +0000 @@ -231,7 +231,9 @@ Return the path of the user-specific site-packages directory, :data:`USER_SITE`. If it is not initialized yet, this function will also set - it, respecting :envvar:`PYTHONNOUSERSITE` and :data:`USER_BASE`. + it, respecting :data:`USER_BASE`. To determine if the user-specific + site-packages was added to ``sys.path`` :data:`ENABLE_USER_SITE` should be + used. .. versionadded:: 3.2 diff -Nru python3.8-3.8.6/Doc/library/socket.rst python3.8-3.8.7/Doc/library/socket.rst --- python3.8-3.8.6/Doc/library/socket.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/socket.rst 2020-12-21 16:25:24.000000000 +0000 @@ -1046,6 +1046,19 @@ .. versionchanged:: 3.8 Windows support was added. + .. note:: + + On Windows network interfaces have different names in different contexts + (all names are examples): + + * UUID: ``{FB605B73-AAC2-49A6-9A2F-25416AEA0573}`` + * name: ``ethernet_32770`` + * friendly name: ``vEthernet (nat)`` + * description: ``Hyper-V Virtual Ethernet Adapter`` + + This function returns names of the second form from the list, ``ethernet_32770`` + in this example case. + .. function:: if_nametoindex(if_name) @@ -1060,6 +1073,9 @@ .. versionchanged:: 3.8 Windows support was added. + .. seealso:: + "Interface name" is a name as documented in :func:`if_nameindex`. + .. function:: if_indextoname(if_index) @@ -1074,6 +1090,9 @@ .. versionchanged:: 3.8 Windows support was added. + .. seealso:: + "Interface name" is a name as documented in :func:`if_nameindex`. + .. _socket-objects: diff -Nru python3.8-3.8.6/Doc/library/sqlite3.rst python3.8-3.8.7/Doc/library/sqlite3.rst --- python3.8-3.8.6/Doc/library/sqlite3.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/sqlite3.rst 2020-12-21 16:25:24.000000000 +0000 @@ -197,7 +197,9 @@ *detect_types* defaults to 0 (i. e. off, no type detection), you can set it to any combination of :const:`PARSE_DECLTYPES` and :const:`PARSE_COLNAMES` to turn - type detection on. + type detection on. Due to SQLite behaviour, types can't be detected for generated + fields (for example ``max(data)``), even when *detect_types* parameter is set. In + such case, the returned type is :class:`str`. By default, *check_same_thread* is :const:`True` and only the creating thread may use the connection. If set :const:`False`, the returned connection may be shared diff -Nru python3.8-3.8.6/Doc/library/stdtypes.rst python3.8-3.8.7/Doc/library/stdtypes.rst --- python3.8-3.8.6/Doc/library/stdtypes.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/stdtypes.rst 2020-12-21 16:25:24.000000000 +0000 @@ -3994,6 +3994,12 @@ objects. If *iterable* is not specified, a new empty set is returned. + Sets can be created by several means: + + * Use a comma-separated list of elements within braces: ``{'jack', 'sjoerd'}`` + * Use a set comprehension: ``{c for c in 'abracadabra' if c not in 'abc'}`` + * Use the type constructor: ``set()``, ``set('foobar')``, ``set(['a', 'b', 'foo'])`` + Instances of :class:`set` and :class:`frozenset` provide the following operations: @@ -4186,6 +4192,14 @@ Return a new dictionary initialized from an optional positional argument and a possibly empty set of keyword arguments. + Dictionaries can be created by several means: + + * Use a comma-separated list of ``key: value`` pairs within braces: + ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098: 'jack', 4127: 'sjoerd'}`` + * Use a dict comprehension: ``{}``, ``{x: x ** 2 for x in range(10)}`` + * Use the type constructor: ``dict()``, + ``dict([('foo', 100), ('bar', 200)])``, ``dict(foo=100, bar=200)`` + If no positional argument is given, an empty dictionary is created. If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, diff -Nru python3.8-3.8.6/Doc/library/string.rst python3.8-3.8.7/Doc/library/string.rst --- python3.8-3.8.6/Doc/library/string.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/string.rst 2020-12-21 16:25:24.000000000 +0000 @@ -384,10 +384,10 @@ The ``'#'`` option causes the "alternate form" to be used for the conversion. The alternate form is defined differently for different -types. This option is only valid for integer, float, complex and -Decimal types. For integers, when binary, octal, or hexadecimal output +types. This option is only valid for integer, float and complex +types. For integers, when binary, octal, or hexadecimal output is used, this option adds the prefix respective ``'0b'``, ``'0o'``, or -``'0x'`` to the output value. For floats, complex and Decimal the +``'0x'`` to the output value. For float and complex the alternate form causes the result of the conversion to always contain a decimal-point character, even if no digits follow it. Normally, a decimal-point character appears in the result of these conversions @@ -476,20 +476,36 @@ ``'n'`` and ``None``). When doing so, :func:`float` is used to convert the integer to a floating point number before formatting. -The available presentation types for floating point and decimal values are: +The available presentation types for :class:`float` and +:class:`~decimal.Decimal` values are: +---------+----------------------------------------------------------+ | Type | Meaning | +=========+==========================================================+ - | ``'e'`` | Exponent notation. Prints the number in scientific | - | | notation using the letter 'e' to indicate the exponent. | - | | The default precision is ``6``. | - +---------+----------------------------------------------------------+ - | ``'E'`` | Exponent notation. Same as ``'e'`` except it uses an | - | | upper case 'E' as the separator character. | - +---------+----------------------------------------------------------+ - | ``'f'`` | Fixed-point notation. Displays the number as a | - | | fixed-point number. The default precision is ``6``. | + | ``'e'`` | Scientific notation. For a given precision ``p``, | + | | formats the number in scientific notation with the | + | | letter 'e' separating the coefficient from the exponent. | + | | The coefficient has one digit before and ``p`` digits | + | | after the decimal point, for a total of ``p + 1`` | + | | significant digits. With no precision given, uses a | + | | precision of ``6`` digits after the decimal point for | + | | :class:`float`, and shows all coefficient digits | + | | for :class:`~decimal.Decimal`. If no digits follow the | + | | decimal point, the decimal point is also removed unless | + | | the ``#`` option is used. | + +---------+----------------------------------------------------------+ + | ``'E'`` | Scientific notation. Same as ``'e'`` except it uses | + | | an upper case 'E' as the separator character. | + +---------+----------------------------------------------------------+ + | ``'f'`` | Fixed-point notation. For a given precision ``p``, | + | | formats the number as a decimal number with exactly | + | | ``p`` digits following the decimal point. With no | + | | precision given, uses a precision of ``6`` digits after | + | | the decimal point for :class:`float`, and uses a | + | | precision large enough to show all coefficient digits | + | | for :class:`~decimal.Decimal`. If no digits follow the | + | | decimal point, the decimal point is also removed unless | + | | the ``#`` option is used. | +---------+----------------------------------------------------------+ | ``'F'`` | Fixed-point notation. Same as ``'f'``, but converts | | | ``nan`` to ``NAN`` and ``inf`` to ``INF``. | @@ -498,6 +514,8 @@ | | this rounds the number to ``p`` significant digits and | | | then formats the result in either fixed-point format | | | or in scientific notation, depending on its magnitude. | + | | A precision of ``0`` is treated as equivalent to a | + | | precision of ``1``. | | | | | | The precise rules are as follows: suppose that the | | | result formatted with presentation type ``'e'`` and | @@ -512,13 +530,19 @@ | | removed if there are no remaining digits following it, | | | unless the ``'#'`` option is used. | | | | + | | With no precision given, uses a precision of ``6`` | + | | significant digits for :class:`float`. For | + | | :class:`~decimal.Decimal`, the coefficient of the result | + | | is formed from the coefficient digits of the value; | + | | scientific notation is used for values smaller than | + | | ``1e-6`` in absolute value and values where the place | + | | value of the least significant digit is larger than 1, | + | | and fixed-point notation is used otherwise. | + | | | | | Positive and negative infinity, positive and negative | | | zero, and nans, are formatted as ``inf``, ``-inf``, | | | ``0``, ``-0`` and ``nan`` respectively, regardless of | | | the precision. | - | | | - | | A precision of ``0`` is treated as equivalent to a | - | | precision of ``1``. The default precision is ``6``. | +---------+----------------------------------------------------------+ | ``'G'`` | General format. Same as ``'g'`` except switches to | | | ``'E'`` if the number gets too large. The | @@ -531,12 +555,18 @@ | ``'%'`` | Percentage. Multiplies the number by 100 and displays | | | in fixed (``'f'``) format, followed by a percent sign. | +---------+----------------------------------------------------------+ - | None | Similar to ``'g'``, except that fixed-point notation, | - | | when used, has at least one digit past the decimal point.| - | | The default precision is as high as needed to represent | - | | the particular value. The overall effect is to match the | - | | output of :func:`str` as altered by the other format | - | | modifiers. | + | None | For :class:`float` this is the same as ``'g'``, except | + | | that when fixed-point notation is used to format the | + | | result, it always includes at least one digit past the | + | | decimal point. The precision used is as large as needed | + | | to represent the given value faithfully. | + | | | + | | For :class:`~decimal.Decimal`, this is the same as | + | | either ``'g'`` or ``'G'`` depending on the value of | + | | ``context.capitals`` for the current decimal context. | + | | | + | | The overall effect is to match the output of :func:`str` | + | | as altered by the other format modifiers. | +---------+----------------------------------------------------------+ diff -Nru python3.8-3.8.6/Doc/library/sys.rst python3.8-3.8.7/Doc/library/sys.rst --- python3.8-3.8.6/Doc/library/sys.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/sys.rst 2020-12-21 16:25:24.000000000 +0000 @@ -31,16 +31,22 @@ When an auditing event is raised through the :func:`sys.audit` function, each hook will be called in the order it was added with the event name and the tuple of arguments. Native hooks added by :c:func:`PySys_AddAuditHook` are - called first, followed by hooks added in the current interpreter. + called first, followed by hooks added in the current interpreter. Hooks + can then log the event, raise an exception to abort the operation, + or terminate the process entirely. .. audit-event:: sys.addaudithook "" sys.addaudithook - Raise an auditing event ``sys.addaudithook`` with no arguments. If any + Calling :func:`sys.addaudithook` will itself raise an auditing event + named ``sys.addaudithook`` with no arguments. If any existing hooks raise an exception derived from :class:`RuntimeError`, the new hook will not be added and the exception suppressed. As a result, callers cannot assume that their hook has been added unless they control all existing hooks. + See the :ref:`audit events table ` for all events raised by + CPython, and :pep:`578` for the original design discussion. + .. versionadded:: 3.8 .. versionchanged:: 3.8.1 @@ -79,14 +85,23 @@ .. index:: single: auditing - Raise an auditing event with any active hooks. The event name is a string - identifying the event and its associated schema, which is the number and - types of arguments. The schema for a given event is considered public and - stable API and should not be modified between releases. - - This function will raise the first exception raised by any hook. In general, - these errors should not be handled and should terminate the process as - quickly as possible. + Raise an auditing event and trigger any active auditing hooks. + *event* is a string identifying the event, and *args* may contain + optional arguments with more information about the event. The + number and types of arguments for a given event are considered a + public and stable API and should not be modified between releases. + + For example, one auditing event is named ``os.chdir``. This event has + one argument called *path* that will contain the requested new + working directory. + + :func:`sys.audit` will call the existing auditing hooks, passing + the event name and arguments, and will re-raise the first exception + from any hook. In general, if an exception is raised, it should not + be handled and the process should be terminated as quickly as + possible. This allows hook implementations to decide how to respond + to particular events: they can merely log the event or abort the + operation by raising an exception. Hooks are added using the :func:`sys.addaudithook` or :c:func:`PySys_AddAuditHook` functions. diff -Nru python3.8-3.8.6/Doc/library/types.rst python3.8-3.8.7/Doc/library/types.rst --- python3.8-3.8.6/Doc/library/types.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/types.rst 2020-12-21 16:25:24.000000000 +0000 @@ -109,6 +109,11 @@ The type of user-defined functions and functions created by :keyword:`lambda` expressions. + .. audit-event:: function.__new__ code types.FunctionType + + The audit event only occurs for direct instantiation of function objects, + and is not raised for normal compilation. + .. data:: GeneratorType @@ -138,10 +143,11 @@ The type for code objects such as returned by :func:`compile`. - .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags CodeType + .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags types.CodeType Note that the audited arguments may not match the names or positions - required by the initializer. + required by the initializer. The audit event only occurs for direct + instantiation of code objects, and is not raised for normal compilation. .. method:: CodeType.replace(**kwargs) @@ -349,7 +355,9 @@ return "{}({})".format(type(self).__name__, ", ".join(items)) def __eq__(self, other): - return self.__dict__ == other.__dict__ + if isinstance(self, SimpleNamespace) and isinstance(other, SimpleNamespace): + return self.__dict__ == other.__dict__ + return NotImplemented ``SimpleNamespace`` may be useful as a replacement for ``class NS: pass``. However, for a structured record type use :func:`~collections.namedtuple` diff -Nru python3.8-3.8.6/Doc/library/typing.rst python3.8-3.8.7/Doc/library/typing.rst --- python3.8-3.8.6/Doc/library/typing.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/typing.rst 2020-12-21 16:25:24.000000000 +0000 @@ -402,10 +402,10 @@ a class ``B`` is expected if and only if ``A`` is a subclass of ``B``. This requirement previously also applied to abstract base classes, such as -:class:`Iterable`. The problem with this approach is that a class had +:class:`~collections.abc.Iterable`. The problem with this approach is that a class had to be explicitly marked to support them, which is unpythonic and unlike what one would normally do in idiomatic dynamically typed Python code. -For example, this conforms to the :pep:`484`:: +For example, this conforms to :pep:`484`:: from typing import Sized, Iterable, Iterator @@ -570,7 +570,7 @@ :ref:`type variables `, and unions of any of these types. For example:: - def new_non_team_user(user_class: Type[Union[BaseUser, ProUser]]): ... + def new_non_team_user(user_class: Type[Union[BasicUser, ProUser]]): ... ``Type[Any]`` is equivalent to ``Type`` which in turn is equivalent to ``type``, which is the root of Python's metaclass hierarchy. @@ -1167,7 +1167,7 @@ Such a protocol can be used with :func:`isinstance` and :func:`issubclass`. This raises :exc:`TypeError` when applied to a non-protocol class. This allows a simple-minded structural check, very similar to "one trick ponies" - in :mod:`collections.abc` such as :class:`Iterable`. For example:: + in :mod:`collections.abc` such as :class:`~collections.abc.Iterable`. For example:: @runtime_checkable class Closable(Protocol): diff -Nru python3.8-3.8.6/Doc/library/unittest.rst python3.8-3.8.7/Doc/library/unittest.rst --- python3.8-3.8.6/Doc/library/unittest.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/unittest.rst 2020-12-21 16:25:24.000000000 +0000 @@ -593,8 +593,9 @@ .. decorator:: expectedFailure - 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. + Mark the test as an expected failure or error. If the test fails or errors + it will be considered a success. If the test passes, it will be considered + a failure. .. exception:: SkipTest(reason) @@ -896,8 +897,7 @@ .. method:: assertIs(first, second, msg=None) assertIsNot(first, second, msg=None) - Test that *first* and *second* evaluate (or don't evaluate) to the - same object. + Test that *first* and *second* are (or are not) the same object. .. versionadded:: 3.1 @@ -1088,7 +1088,8 @@ If given, *logger* should be a :class:`logging.Logger` object or a :class:`str` giving the name of a logger. The default is the root - logger, which will catch all messages. + logger, which will catch all messages that were not blocked by a + non-propagating descendent logger. If given, *level* should be either a numeric logging level or its string equivalent (for example either ``"ERROR"`` or @@ -1945,7 +1946,7 @@ A list containing 2-tuples of :class:`TestCase` instances and strings holding formatted tracebacks. Each tuple represents an expected failure - of the test case. + or error of the test case. .. attribute:: unexpectedSuccesses @@ -2071,8 +2072,8 @@ .. method:: addExpectedFailure(test, err) - Called when the test case *test* fails, but was marked with the - :func:`expectedFailure` decorator. + Called when the test case *test* fails or errors, but was marked with + the :func:`expectedFailure` decorator. The default implementation appends a tuple ``(test, formatted_err)`` to the instance's :attr:`expectedFailures` attribute, where *formatted_err* diff -Nru python3.8-3.8.6/Doc/library/xml.etree.elementtree.rst python3.8-3.8.7/Doc/library/xml.etree.elementtree.rst --- python3.8-3.8.6/Doc/library/xml.etree.elementtree.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/library/xml.etree.elementtree.rst 2020-12-21 16:25:24.000000000 +0000 @@ -249,12 +249,18 @@ remove all countries with a rank higher than 50:: >>> for country in root.findall('country'): + ... # using root.findall() to avoid removal during traversal ... rank = int(country.find('rank').text) ... if rank > 50: ... root.remove(country) ... >>> tree.write('output.xml') +Note that concurrent modification while iterating can lead to problems, +just like when iterating and modifying Python lists or dicts. +Therefore, the example first collects all matching elements with +``root.findall()``, and only then iterates over the list of matches. + Our XML now looks like this: .. code-block:: xml diff -Nru python3.8-3.8.6/Doc/reference/compound_stmts.rst python3.8-3.8.7/Doc/reference/compound_stmts.rst --- python3.8-3.8.6/Doc/reference/compound_stmts.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/reference/compound_stmts.rst 2020-12-21 16:25:24.000000000 +0000 @@ -254,7 +254,8 @@ expression, that expression is evaluated, and the clause matches the exception if the resulting object is "compatible" with the exception. An object is compatible with an exception if it is the class or a base class of the exception -object or a tuple containing an item compatible with the exception. +object, or a tuple containing an item that is the class or a base class of +the exception object. If no except clause matches the exception, the search for an exception handler continues in the surrounding code and on the invocation stack. [#]_ diff -Nru python3.8-3.8.6/Doc/reference/datamodel.rst python3.8-3.8.7/Doc/reference/datamodel.rst --- python3.8-3.8.6/Doc/reference/datamodel.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/reference/datamodel.rst 2020-12-21 16:25:24.000000000 +0000 @@ -182,6 +182,24 @@ related to mathematical numbers, but subject to the limitations of numerical representation in computers. + The string representations of the numeric classes, computed by + :meth:`__repr__` and :meth:`__str__`, have the following + properties: + + * They are valid numeric literals which, when passed to their + class constructor, produce an object having the value of the + original numeric. + + * The representation is in base 10, when possible. + + * Leading zeros, possibly excepting a single zero before a + decimal point, are not shown. + + * Trailing zeros, possibly excepting a single zero after a + decimal point, are not shown. + + * A sign is shown only when the number is negative. + Python distinguishes between integers, floating point numbers, and complex numbers: @@ -1372,12 +1390,14 @@ context (e.g., in the condition of an ``if`` statement), Python will call :func:`bool` on the value to determine if the result is true or false. - By default, :meth:`__ne__` delegates to :meth:`__eq__` and - inverts the result unless it is ``NotImplemented``. There are no other - implied relationships among the comparison operators, for example, - the truth of ``(x` ``object.__getattr__`` with arguments + ``obj`` and ``name``. + .. method:: object.__setattr__(self, name, value) @@ -1546,12 +1572,24 @@ call the base class method with the same name, for example, ``object.__setattr__(self, name, value)``. + .. audit-event:: object.__setattr__ obj,name,value object.__setattr__ + + For certain sensitive attribute assignments, raises an + :ref:`auditing event ` ``object.__setattr__`` with arguments + ``obj``, ``name``, ``value``. + .. method:: object.__delattr__(self, name) Like :meth:`__setattr__` but for attribute deletion instead of assignment. This should only be implemented if ``del obj.name`` is meaningful for the object. + .. audit-event:: object.__delattr__ obj,name object.__delattr__ + + For certain sensitive attribute deletions, raises an + :ref:`auditing event ` ``object.__delattr__`` with arguments + ``obj`` and ``name``. + .. method:: object.__dir__(self) @@ -2125,7 +2163,7 @@ .. index:: pair: call; instance Called when the instance is "called" as a function; if this method is defined, - ``x(arg1, arg2, ...)`` is a shorthand for ``x.__call__(arg1, arg2, ...)``. + ``x(arg1, arg2, ...)`` roughly translates to ``type(x).__call__(x, arg1, ...)``. .. _sequence-types: @@ -2371,10 +2409,11 @@ .. note:: - If the right operand's type is a subclass of the left operand's type and that - subclass provides the reflected method for the operation, this method will be - called before the left operand's non-reflected method. This behavior allows - subclasses to override their ancestors' operations. + If the right operand's type is a subclass of the left operand's type and + that subclass provides a different implementation of the reflected method + for the operation, this method will be called before the left operand's + non-reflected method. This behavior allows subclasses to override their + ancestors' operations. .. method:: object.__iadd__(self, other) @@ -2773,6 +2812,6 @@ method—that will instead have the opposite effect of explicitly *blocking* such fallback. -.. [#] For operands of the same type, it is assumed that if the non-reflected method - (such as :meth:`__add__`) fails the operation is not supported, which is why the - reflected method is not called. +.. [#] For operands of the same type, it is assumed that if the non-reflected + method -- such as :meth:`__add__` -- fails then the overall operation is not + supported, which is why the reflected method is not called. diff -Nru python3.8-3.8.6/Doc/reference/expressions.rst python3.8-3.8.7/Doc/reference/expressions.rst --- python3.8-3.8.6/Doc/reference/expressions.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/reference/expressions.rst 2020-12-21 16:25:24.000000000 +0000 @@ -162,6 +162,8 @@ Displays for lists, sets and dictionaries ----------------------------------------- +.. index:: single: comprehensions + For constructing a list, a set or a dictionary Python provides special syntax called "displays", each of them in two flavors: @@ -260,6 +262,7 @@ .. index:: pair: set; display + pair: set; comprehensions object: set single: {} (curly brackets); set expression single: , (comma); expression list @@ -287,6 +290,7 @@ .. index:: pair: dictionary; display + pair: dictionary; comprehensions key, datum, key/datum pair object: dictionary single: {} (curly brackets); dictionary expression diff -Nru python3.8-3.8.6/Doc/reference/import.rst python3.8-3.8.7/Doc/reference/import.rst --- python3.8-3.8.6/Doc/reference/import.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/reference/import.rst 2020-12-21 16:25:24.000000000 +0000 @@ -202,6 +202,8 @@ reinitialise the module contents by rerunning the module's code. +.. _finders-and-loaders: + Finders and loaders ------------------- @@ -678,7 +680,7 @@ Cached bytecode invalidation ---------------------------- -Before Python loads cached bytecode from ``.pyc`` file, it checks whether the +Before Python loads cached bytecode from a ``.pyc`` file, it checks whether the cache is up-to-date with the source ``.py`` file. By default, Python does this by storing the source's last-modified timestamp and size in the cache file when writing it. At runtime, the import system then validates the cache file by @@ -855,9 +857,8 @@ This spec will always have "loader" set (with one exception). To indicate to the import machinery that the spec represents a namespace -:term:`portion`, the path entry finder sets "loader" on the spec to -``None`` and "submodule_search_locations" to a list containing the -portion. +:term:`portion`, the path entry finder sets "submodule_search_locations" to +a list containing the portion. .. versionchanged:: 3.4 :meth:`~importlib.abc.PathEntryFinder.find_spec` replaced @@ -873,18 +874,7 @@ :meth:`~importlib.abc.PathEntryFinder.find_loader` takes one argument, the fully qualified name of the module being imported. ``find_loader()`` returns a 2-tuple where the first item is the loader and the second item - is a namespace :term:`portion`. When the first item (i.e. the loader) is - ``None``, this means that while the path entry finder does not have a - loader for the named module, it knows that the path entry contributes to - a namespace portion for the named module. This will almost always be the - case where Python is asked to import a namespace package that has no - physical presence on the file system. When a path entry finder returns - ``None`` for the loader, the second item of the 2-tuple return value must - be a sequence, although it can be empty. - - If ``find_loader()`` returns a non-``None`` loader value, the portion is - ignored and the loader is returned from the path based finder, terminating - the search through the path entries. + is a namespace :term:`portion`. For backwards compatibility with other implementations of the import protocol, many path entry finders also support the same, diff -Nru python3.8-3.8.6/Doc/reference/simple_stmts.rst python3.8-3.8.7/Doc/reference/simple_stmts.rst --- python3.8-3.8.6/Doc/reference/simple_stmts.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/reference/simple_stmts.rst 2020-12-21 16:25:24.000000000 +0000 @@ -874,8 +874,8 @@ * blank lines, and * other future statements. -The only feature in Python 3.7 that requires using the future statement is -``annotations``. +The only feature that requires using the future statement is +``annotations`` (see :pep:`563`). All historical features enabled by the future statement are still recognized by Python 3. The list includes ``absolute_import``, ``division``, diff -Nru python3.8-3.8.6/Doc/tools/extensions/c_annotations.py python3.8-3.8.7/Doc/tools/extensions/c_annotations.py --- python3.8-3.8.6/Doc/tools/extensions/c_annotations.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/tools/extensions/c_annotations.py 2020-12-21 16:25:24.000000000 +0000 @@ -79,9 +79,9 @@ classes=['stableabi'])) if par['objtype'] != 'function': continue - if not par[0].has_key('names') or not par[0]['names']: + if not par[0].has_key('ids') or not par[0]['ids']: continue - name = par[0]['names'][0] + name = par[0]['ids'][0] if name.startswith("c."): name = name[2:] entry = self.get(name) diff -Nru python3.8-3.8.6/Doc/tools/static/switchers.js python3.8-3.8.7/Doc/tools/static/switchers.js --- python3.8-3.8.6/Doc/tools/static/switchers.js 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/tools/static/switchers.js 2020-12-21 16:25:24.000000000 +0000 @@ -15,7 +15,6 @@ '3.8': '3.8', '3.7': '3.7', '3.6': '3.6', - '3.5': '3.5', '2.7': '2.7', }; diff -Nru python3.8-3.8.6/Doc/tools/susp-ignored.csv python3.8-3.8.7/Doc/tools/susp-ignored.csv --- python3.8-3.8.6/Doc/tools/susp-ignored.csv 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/tools/susp-ignored.csv 2020-12-21 16:25:24.000000000 +0000 @@ -12,7 +12,6 @@ extending/extending,,:set,"if (PyArg_ParseTuple(args, ""O:set_callback"", &temp)) {" extending/newtypes,,:call,"if (!PyArg_ParseTuple(args, ""sss:call"", &arg1, &arg2, &arg3)) {" faq/programming,,:chr,">=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(" -faq/programming,,::,for x in sequence[::-1]: faq/programming,,:reduce,"print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y," faq/programming,,:reduce,"Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro," faq/windows,,:d48eceb,"Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32" diff -Nru python3.8-3.8.6/Doc/tools/templates/indexsidebar.html python3.8-3.8.7/Doc/tools/templates/indexsidebar.html --- python3.8-3.8.6/Doc/tools/templates/indexsidebar.html 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/tools/templates/indexsidebar.html 2020-12-21 16:25:24.000000000 +0000 @@ -7,7 +7,6 @@
  • {% trans %}Python 3.8 (stable){% endtrans %}
  • {% trans %}Python 3.7 (stable){% endtrans %}
  • {% trans %}Python 3.6 (security-fixes){% endtrans %}
  • -
  • {% trans %}Python 3.5 (security-fixes){% endtrans %}
  • {% trans %}Python 2.7 (EOL){% endtrans %}
  • {% trans %}All versions{% endtrans %}
  • diff -Nru python3.8-3.8.6/Doc/tutorial/datastructures.rst python3.8-3.8.7/Doc/tutorial/datastructures.rst --- python3.8-3.8.6/Doc/tutorial/datastructures.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/tutorial/datastructures.rst 2020-12-21 16:25:24.000000000 +0000 @@ -78,7 +78,7 @@ Return the number of times *x* appears in the list. -.. method:: list.sort(key=None, reverse=False) +.. method:: list.sort(*, key=None, reverse=False) :noindex: Sort the items of the list in place (the arguments can be used for sort diff -Nru python3.8-3.8.6/Doc/tutorial/inputoutput.rst python3.8-3.8.7/Doc/tutorial/inputoutput.rst --- python3.8-3.8.6/Doc/tutorial/inputoutput.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/tutorial/inputoutput.rst 2020-12-21 16:25:24.000000000 +0000 @@ -329,11 +329,16 @@ If you're not using the :keyword:`with` keyword, then you should call ``f.close()`` to close the file and immediately free up any system -resources used by it. If you don't explicitly close a file, Python's -garbage collector will eventually destroy the object and close the -open file for you, but the file may stay open for a while. Another -risk is that different Python implementations will do this clean-up at -different times. +resources used by it. + +.. warning:: + Calling ``f.write()`` without using the :keyword:`!with` keyword or calling + ``f.close()`` **might** result in the arguments + of ``f.write()`` not being completely written to the disk, even if the + program exits successfully. + +.. + See also https://bugs.python.org/issue17852 After a file object is closed, either by a :keyword:`with` statement or by calling ``f.close()``, attempts to use the file object will diff -Nru python3.8-3.8.6/Doc/using/cmdline.rst python3.8-3.8.7/Doc/using/cmdline.rst --- python3.8-3.8.6/Doc/using/cmdline.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/using/cmdline.rst 2020-12-21 16:25:24.000000000 +0000 @@ -551,7 +551,7 @@ the interactive session. You can also change the prompts :data:`sys.ps1` and :data:`sys.ps2` and the hook :data:`sys.__interactivehook__` in this file. - .. audit-event:: cpython.run_startup filename PYTHONSTARTUP + .. audit-event:: cpython.run_startup filename envvar-PYTHONSTARTUP Raises an :ref:`auditing event ` ``cpython.run_startup`` with the filename as the argument when called on startup. diff -Nru python3.8-3.8.6/Doc/using/windows.rst python3.8-3.8.7/Doc/using/windows.rst --- python3.8-3.8.6/Doc/using/windows.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/using/windows.rst 2020-12-21 16:25:24.000000000 +0000 @@ -103,9 +103,9 @@ In the latest versions of Windows, this limitation can be expanded to approximately 32,000 characters. Your administrator will need to activate the -"Enable Win32 long paths" group policy, or set the registry value -``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem@LongPathsEnabled`` -to ``1``. +"Enable Win32 long paths" group policy, or set ``LongPathsEnabled`` to ``1`` +in the registry key +``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem``. This allows the :func:`open` function, the :mod:`os` module and most other path functionality to accept and return paths longer than 260 characters. @@ -422,6 +422,12 @@ automatically via Windows Update, and can be detected by finding ``ucrtbase.dll`` in the system directory. +.. note:: + + When running on Windows 7, Python 3.8 requires the KB2533623 update to be + installed. The embeddable distribution does not detect this update, and may + fail at runtime. Later versions of Windows include this update. + 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 diff -Nru python3.8-3.8.6/Doc/whatsnew/3.8.rst python3.8-3.8.7/Doc/whatsnew/3.8.rst --- python3.8-3.8.6/Doc/whatsnew/3.8.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Doc/whatsnew/3.8.rst 2020-12-21 16:25:24.000000000 +0000 @@ -872,8 +872,18 @@ (Many people worked on this for eight years but the problem was finally solved by Serhiy Storchaka in :issue:`13153`.) +New in 3.8.1: + +Add option to toggle cursor blink off. (Contributed by Zackery Spytz +in :issue:`4603`.) + +Escape key now closes IDLE completion windows. (Contributed by Johnny +Najera in :issue:`38944`.) + The changes above have been backported to 3.7 maintenance releases. +Add keywords to module name completion list. (Contributed by Terry J. +Reedy in :issue:`37765`.) inspect ------- @@ -2105,9 +2115,6 @@ (Contributed by Antoine Pitrou in :issue:`32388`.) -* The :c:func:`PyCode_New` has a new parameter in the second position (*posonlyargcount*) - to support :pep:`570`, indicating the number of positional-only arguments. - * The functions :c:func:`PyNode_AddChild` and :c:func:`PyParser_AddToken` now accept two additional ``int`` arguments *end_lineno* and *end_col_offset*. diff -Nru python3.8-3.8.6/Include/cpython/fileobject.h python3.8-3.8.7/Include/cpython/fileobject.h --- python3.8-3.8.6/Include/cpython/fileobject.h 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Include/cpython/fileobject.h 2020-12-21 16:25:24.000000000 +0000 @@ -8,14 +8,6 @@ PyAPI_FUNC(char *) Py_UniversalNewlineFgets(char *, int, FILE*, PyObject *); -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 -PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; -#endif - -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 -PyAPI_DATA(int) Py_UTF8Mode; -#endif - /* The std printer acts as a preliminary sys.stderr until the new io infrastructure is in place. */ PyAPI_FUNC(PyObject *) PyFile_NewStdPrinter(int); diff -Nru python3.8-3.8.6/Include/fileobject.h python3.8-3.8.7/Include/fileobject.h --- python3.8-3.8.6/Include/fileobject.h 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Include/fileobject.h 2020-12-21 16:25:24.000000000 +0000 @@ -20,8 +20,15 @@ If non-NULL, this is different than the default encoding for strings */ PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000 +PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors; +#endif PyAPI_DATA(int) Py_HasFileSystemDefaultEncoding; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +PyAPI_DATA(int) Py_UTF8Mode; +#endif + /* A routine to check if a file descriptor can be select()-ed. */ #ifdef _MSC_VER /* On Windows, any socket fd can be select()-ed, no matter how high */ diff -Nru python3.8-3.8.6/Include/internal/pycore_pylifecycle.h python3.8-3.8.7/Include/internal/pycore_pylifecycle.h --- python3.8-3.8.6/Include/internal/pycore_pylifecycle.h 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Include/internal/pycore_pylifecycle.h 2020-12-21 16:25:24.000000000 +0000 @@ -69,6 +69,8 @@ extern void PySet_Fini(void); extern void PyBytes_Fini(void); extern void PyFloat_Fini(void); + +extern int _PySignal_Init(int install_signal_handlers); extern void PyOS_FiniInterrupts(void); extern void PySlice_Fini(void); extern void PyAsyncGen_Fini(void); diff -Nru python3.8-3.8.6/Include/patchlevel.h python3.8-3.8.7/Include/patchlevel.h --- python3.8-3.8.6/Include/patchlevel.h 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Include/patchlevel.h 2020-12-21 16:25:24.000000000 +0000 @@ -18,12 +18,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 8 -#define PY_MICRO_VERSION 6 +#define PY_MICRO_VERSION 7 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.8.6" +#define PY_VERSION "3.8.7" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff -Nru python3.8-3.8.6/Lib/ast.py python3.8-3.8.7/Lib/ast.py --- python3.8-3.8.6/Lib/ast.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/ast.py 2020-12-21 16:25:24.000000000 +0000 @@ -285,7 +285,7 @@ def _pad_whitespace(source): - """Replace all chars except '\f\t' in a line with spaces.""" + r"""Replace all chars except '\f\t' in a line with spaces.""" result = '' for c in source: if c in '\f\t': diff -Nru python3.8-3.8.6/Lib/asyncio/base_futures.py python3.8-3.8.7/Lib/asyncio/base_futures.py --- python3.8-3.8.6/Lib/asyncio/base_futures.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/asyncio/base_futures.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,6 +1,7 @@ __all__ = () import reprlib +from _thread import get_ident from . import format_helpers @@ -41,6 +42,16 @@ return f'cb=[{cb}]' +# bpo-42183: _repr_running is needed for repr protection +# when a Future or Task result contains itself directly or indirectly. +# The logic is borrowed from @reprlib.recursive_repr decorator. +# Unfortunately, the direct decorator usage is impossible because of +# AttributeError: '_asyncio.Task' object has no attribute '__module__' error. +# +# After fixing this thing we can return to the decorator based approach. +_repr_running = set() + + def _future_repr_info(future): # (Future) -> str """helper function for Future.__repr__""" @@ -49,9 +60,17 @@ if future._exception is not None: info.append(f'exception={future._exception!r}') else: - # use reprlib to limit the length of the output, especially - # for very long strings - result = reprlib.repr(future._result) + key = id(future), get_ident() + if key in _repr_running: + result = '...' + else: + _repr_running.add(key) + try: + # use reprlib to limit the length of the output, especially + # for very long strings + result = reprlib.repr(future._result) + finally: + _repr_running.discard(key) info.append(f'result={result}') if future._callbacks: info.append(_format_callbacks(future._callbacks)) diff -Nru python3.8-3.8.6/Lib/asyncio/exceptions.py python3.8-3.8.7/Lib/asyncio/exceptions.py --- python3.8-3.8.6/Lib/asyncio/exceptions.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/asyncio/exceptions.py 2020-12-21 16:25:24.000000000 +0000 @@ -34,8 +34,9 @@ - expected: total number of expected bytes (or None if unknown) """ def __init__(self, partial, expected): + r_expected = 'undefined' if expected is None else repr(expected) super().__init__(f'{len(partial)} bytes read on a total of ' - f'{expected!r} expected bytes') + f'{r_expected} expected bytes') self.partial = partial self.expected = expected diff -Nru python3.8-3.8.6/Lib/asyncio/tasks.py python3.8-3.8.7/Lib/asyncio/tasks.py --- python3.8-3.8.6/Lib/asyncio/tasks.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/asyncio/tasks.py 2020-12-21 16:25:24.000000000 +0000 @@ -394,7 +394,7 @@ async def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED): """Wait for the Futures and coroutines given by fs to complete. - The sequence futures must not be empty. + The fs iterable must not be empty. Coroutines will be wrapped in Tasks. @@ -484,7 +484,10 @@ return fut.result() else: fut.remove_done_callback(cb) - fut.cancel() + # We must ensure that the task is not running + # after wait_for() returns. + # See https://bugs.python.org/issue32751 + await _cancel_and_wait(fut, loop=loop) raise if fut.done(): @@ -580,7 +583,7 @@ Note: The futures 'f' are not necessarily members of fs. """ if futures.isfuture(fs) or coroutines.iscoroutine(fs): - raise TypeError(f"expect a list of futures, not {type(fs).__name__}") + raise TypeError(f"expect an iterable of futures, not {type(fs).__name__}") from .queues import Queue # Import here to avoid circular import problem. done = Queue(loop=loop) diff -Nru python3.8-3.8.6/Lib/asyncio/unix_events.py python3.8-3.8.7/Lib/asyncio/unix_events.py --- python3.8-3.8.6/Lib/asyncio/unix_events.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/asyncio/unix_events.py 2020-12-21 16:25:24.000000000 +0000 @@ -1152,13 +1152,15 @@ def close(self): self._callbacks.clear() - if self._saved_sighandler is not None: - handler = signal.getsignal(signal.SIGCHLD) - if handler != self._sig_chld: - logger.warning("SIGCHLD handler was changed by outside code") - else: - signal.signal(signal.SIGCHLD, self._saved_sighandler) - self._saved_sighandler = None + if self._saved_sighandler is None: + return + + handler = signal.getsignal(signal.SIGCHLD) + if handler != self._sig_chld: + logger.warning("SIGCHLD handler was changed by outside code") + else: + signal.signal(signal.SIGCHLD, self._saved_sighandler) + self._saved_sighandler = None def __enter__(self): return self @@ -1185,15 +1187,17 @@ # The reason to do it here is that attach_loop() is called from # unix policy only for the main thread. # Main thread is required for subscription on SIGCHLD signal + if self._saved_sighandler is not None: + return + + self._saved_sighandler = signal.signal(signal.SIGCHLD, self._sig_chld) if self._saved_sighandler is None: - self._saved_sighandler = signal.signal(signal.SIGCHLD, self._sig_chld) - if self._saved_sighandler is None: - logger.warning("Previous SIGCHLD handler was set by non-Python code, " - "restore to default handler on watcher close.") - self._saved_sighandler = signal.SIG_DFL + logger.warning("Previous SIGCHLD handler was set by non-Python code, " + "restore to default handler on watcher close.") + self._saved_sighandler = signal.SIG_DFL - # Set SA_RESTART to limit EINTR occurrences. - signal.siginterrupt(signal.SIGCHLD, False) + # Set SA_RESTART to limit EINTR occurrences. + signal.siginterrupt(signal.SIGCHLD, False) def _do_waitpid_all(self): for pid in list(self._callbacks): diff -Nru python3.8-3.8.6/Lib/binhex.py python3.8-3.8.7/Lib/binhex.py --- python3.8-3.8.6/Lib/binhex.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/binhex.py 2020-12-21 16:25:24.000000000 +0000 @@ -100,12 +100,12 @@ first = 0 while first <= len(self.hqxdata) - self.linelen: last = first + self.linelen - self.ofp.write(self.hqxdata[first:last] + b'\n') + self.ofp.write(self.hqxdata[first:last] + b'\r') self.linelen = LINELEN first = last self.hqxdata = self.hqxdata[first:] if force: - self.ofp.write(self.hqxdata + b':\n') + self.ofp.write(self.hqxdata + b':\r') def close(self): if self.data: diff -Nru python3.8-3.8.6/Lib/cProfile.py python3.8-3.8.7/Lib/cProfile.py --- python3.8-3.8.6/Lib/cProfile.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/cProfile.py 2020-12-21 16:25:24.000000000 +0000 @@ -168,6 +168,11 @@ (options, args) = parser.parse_args() sys.argv[:] = args + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + if len(args) > 0: if options.module: code = "run_module(modname, run_name='__main__')" diff -Nru python3.8-3.8.6/Lib/ctypes/test/test_find.py python3.8-3.8.7/Lib/ctypes/test/test_find.py --- python3.8-3.8.6/Lib/ctypes/test/test_find.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/ctypes/test/test_find.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,4 +1,5 @@ import unittest +import unittest.mock import os.path import sys import test.support @@ -72,7 +73,7 @@ @unittest.skipUnless(sys.platform.startswith('linux'), 'Test only valid for Linux') -class LibPathFindTest(unittest.TestCase): +class FindLibraryLinux(unittest.TestCase): def test_find_on_libpath(self): import subprocess import tempfile @@ -111,6 +112,15 @@ # LD_LIBRARY_PATH) self.assertEqual(find_library(libname), 'lib%s.so' % libname) + def test_find_library_with_gcc(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + + def test_find_library_with_ld(self): + with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None), \ + unittest.mock.patch("ctypes.util._findLib_gcc", lambda *args: None): + self.assertNotEqual(find_library('c'), None) + if __name__ == "__main__": unittest.main() diff -Nru python3.8-3.8.6/Lib/ctypes/test/test_wintypes.py python3.8-3.8.7/Lib/ctypes/test/test_wintypes.py --- python3.8-3.8.6/Lib/ctypes/test/test_wintypes.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/ctypes/test/test_wintypes.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,12 +1,13 @@ -import sys import unittest +# also work on POSIX + from ctypes import * +from ctypes import wintypes + -@unittest.skipUnless(sys.platform.startswith('win'), 'Windows-only test') class WinTypesTest(unittest.TestCase): def test_variant_bool(self): - from ctypes import wintypes # reads 16-bits from memory, anything non-zero is True for true_value in (1, 32767, 32768, 65535, 65537): true = POINTER(c_int16)(c_int16(true_value)) @@ -37,5 +38,6 @@ vb.value = [] self.assertIs(vb.value, False) + if __name__ == "__main__": unittest.main() diff -Nru python3.8-3.8.6/Lib/ctypes/util.py python3.8-3.8.7/Lib/ctypes/util.py --- python3.8-3.8.6/Lib/ctypes/util.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/ctypes/util.py 2020-12-21 16:25:24.000000000 +0000 @@ -93,6 +93,12 @@ # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump import re, tempfile + def _is_elf(filename): + "Return True if the given file is an ELF file" + elf_header = b'\x7fELF' + with open(filename, 'br') as thefile: + return thefile.read(4) == elf_header + def _findLib_gcc(name): # Run GCC's linker with the -t (aka --trace) option and examine the # library name it prints out. The GCC command will fail because we @@ -130,10 +136,17 @@ # Raised if the file was already removed, which is the normal # behaviour of GCC if linking fails pass - res = re.search(expr, trace) + res = re.findall(expr, trace) if not res: return None - return os.fsdecode(res.group(0)) + + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) if sys.platform == "sunos5": @@ -299,17 +312,22 @@ stderr=subprocess.PIPE, universal_newlines=True) out, _ = p.communicate() - res = re.search(expr, os.fsdecode(out)) - if res: - result = res.group(0) - except Exception as e: + res = re.findall(expr, os.fsdecode(out)) + for file in res: + # Check if the given file is an elf file: gcc can report + # some files that are linker scripts and not actual + # shared objects. See bpo-41976 for more details + if not _is_elf(file): + continue + return os.fsdecode(file) + except Exception: pass # result will be None return result def find_library(name): # See issue #9998 return _findSoname_ldconfig(name) or \ - _get_soname(_findLib_gcc(name) or _findLib_ld(name)) + _get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name)) ################################################################ # test code diff -Nru python3.8-3.8.6/Lib/datetime.py python3.8-3.8.7/Lib/datetime.py --- python3.8-3.8.6/Lib/datetime.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/datetime.py 2020-12-21 16:25:24.000000000 +0000 @@ -1418,7 +1418,8 @@ part is omitted if self.microsecond == 0. The optional argument timespec specifies the number of additional - terms of the time to include. + terms of the time to include. Valid options are 'auto', 'hours', + 'minutes', 'seconds', 'milliseconds' and 'microseconds'. """ s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) @@ -1544,7 +1545,7 @@ self._tzinfo = tzinfo def __reduce_ex__(self, protocol): - return (time, self._getstate(protocol)) + return (self.__class__, self._getstate(protocol)) def __reduce__(self): return self.__reduce_ex__(2) @@ -1902,7 +1903,8 @@ time, default 'T'. The optional argument timespec specifies the number of additional - terms of the time to include. + terms of the time to include. Valid options are 'auto', 'hours', + 'minutes', 'seconds', 'milliseconds' and 'microseconds'. """ s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + _format_time(self._hour, self._minute, self._second, diff -Nru python3.8-3.8.6/Lib/email/generator.py python3.8-3.8.7/Lib/email/generator.py --- python3.8-3.8.6/Lib/email/generator.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/email/generator.py 2020-12-21 16:25:24.000000000 +0000 @@ -186,7 +186,11 @@ # If we munged the cte, copy the message again and re-fix the CTE. if munge_cte: msg = deepcopy(msg) - msg.replace_header('content-transfer-encoding', munge_cte[0]) + # Preserve the header order if the CTE header already exists. + if msg.get('content-transfer-encoding') is None: + msg['Content-Transfer-Encoding'] = munge_cte[0] + else: + msg.replace_header('content-transfer-encoding', munge_cte[0]) msg.replace_header('content-type', munge_cte[1]) # Write the headers. First we see if the message object wants to # handle that itself. If not, we'll do it generically. diff -Nru python3.8-3.8.6/Lib/email/message.py python3.8-3.8.7/Lib/email/message.py --- python3.8-3.8.6/Lib/email/message.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/email/message.py 2020-12-21 16:25:24.000000000 +0000 @@ -141,7 +141,7 @@ header. For backward compatibility reasons, if maxheaderlen is not specified it defaults to 0, so you must override it explicitly if you want a different maxheaderlen. 'policy' is passed to the - Generator instance used to serialize the mesasge; if it is not + Generator instance used to serialize the message; if it is not specified the policy associated with the message instance is used. If the message object contains binary data that is not encoded @@ -958,7 +958,7 @@ header. maxheaderlen is retained for backward compatibility with the base Message class, but defaults to None, meaning that the policy value for max_line_length controls the header maximum length. 'policy' is - passed to the Generator instance used to serialize the mesasge; if it + passed to the Generator instance used to serialize the message; if it is not specified the policy associated with the message instance is used. """ Binary files /tmp/tmpgwkoGW/C7ZJ71KE1Z/python3.8-3.8.6/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl and /tmp/tmpgwkoGW/uSs3611ZLj/python3.8-3.8.7/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl differ Binary files /tmp/tmpgwkoGW/C7ZJ71KE1Z/python3.8-3.8.6/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl and /tmp/tmpgwkoGW/uSs3611ZLj/python3.8-3.8.7/Lib/ensurepip/_bundled/pip-20.2.3-py2.py3-none-any.whl differ diff -Nru python3.8-3.8.6/Lib/ensurepip/__init__.py python3.8-3.8.7/Lib/ensurepip/__init__.py --- python3.8-3.8.6/Lib/ensurepip/__init__.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/ensurepip/__init__.py 2020-12-21 16:25:24.000000000 +0000 @@ -4,6 +4,7 @@ import sys import runpy import tempfile +import subprocess __all__ = ["version", "bootstrap"] @@ -11,7 +12,7 @@ _SETUPTOOLS_VERSION = "49.2.1" -_PIP_VERSION = "20.2.1" +_PIP_VERSION = "20.2.3" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), @@ -20,22 +21,18 @@ def _run_pip(args, additional_paths=None): - # Add our bundled software to the sys.path so we can import it - if additional_paths is not None: - sys.path = additional_paths + sys.path - - # Invoke pip as if it's the main module, and catch the exit. - backup_argv = sys.argv[:] - sys.argv[1:] = args - try: - # run_module() alters sys.modules and sys.argv, but restores them at exit - runpy.run_module("pip", run_name="__main__", alter_sys=True) - except SystemExit as exc: - return exc.code - finally: - sys.argv[:] = backup_argv - - raise SystemError("pip did not exit, this should never happen") + # Run the bootstraping in a subprocess to avoid leaking any state that happens + # after pip has executed. Particulary, this avoids the case when pip holds onto + # the files in *additional_paths*, preventing us to remove them at the end of the + # invocation. + code = f""" +import runpy +import sys +sys.path = {additional_paths or []} + sys.path +sys.argv[1:] = {args} +runpy.run_module("pip", run_name="__main__", alter_sys=True) +""" + return subprocess.run([sys.executable, "-c", code], check=True).returncode def version(): diff -Nru python3.8-3.8.6/Lib/enum.py python3.8-3.8.7/Lib/enum.py --- python3.8-3.8.6/Lib/enum.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/enum.py 2020-12-21 16:25:24.000000000 +0000 @@ -10,31 +10,41 @@ def _is_descriptor(obj): - """Returns True if obj is a descriptor, False otherwise.""" + """ + Returns True if obj is a descriptor, False otherwise. + """ return ( hasattr(obj, '__get__') or hasattr(obj, '__set__') or - hasattr(obj, '__delete__')) - + hasattr(obj, '__delete__') + ) def _is_dunder(name): - """Returns True if a __dunder__ name, False otherwise.""" - return (len(name) > 4 and + """ + Returns True if a __dunder__ name, False otherwise. + """ + return ( + len(name) > 4 and name[:2] == name[-2:] == '__' and name[2] != '_' and - name[-3] != '_') - + name[-3] != '_' + ) def _is_sunder(name): - """Returns True if a _sunder_ name, False otherwise.""" - return (len(name) > 2 and + """ + Returns True if a _sunder_ name, False otherwise. + """ + return ( + len(name) > 2 and name[0] == name[-1] == '_' and name[1:2] != '_' and - name[-2:-1] != '_') - + name[-2:-1] != '_' + ) def _make_class_unpicklable(cls): - """Make the given class un-picklable.""" + """ + Make the given class un-picklable. + """ def _break_on_call_reduce(self, proto): raise TypeError('%r cannot be pickled' % self) cls.__reduce_ex__ = _break_on_call_reduce @@ -49,11 +59,11 @@ class _EnumDict(dict): - """Track enum member order and ensure member names are not reused. + """ + Track enum member order and ensure member names are not reused. EnumMeta will use the names found in self._member_names as the enumeration member names. - """ def __init__(self): super().__init__() @@ -63,13 +73,13 @@ self._auto_called = False def __setitem__(self, key, value): - """Changes anything not dundered or not a descriptor. + """ + Changes anything not dundered or not a descriptor. If an enum member name is used twice, an error is raised; duplicate values are not checked for. Single underscore (sunder) names are reserved. - """ if _is_sunder(key): if key not in ( @@ -90,7 +100,10 @@ self._ignore = value already = set(value) & set(self._member_names) if already: - raise ValueError('_ignore_ cannot specify already set names: %r' % (already, )) + raise ValueError( + '_ignore_ cannot specify already set names: %r' + % (already, ) + ) elif _is_dunder(key): if key == '__order__': key = '_order_' @@ -105,7 +118,12 @@ raise TypeError('%r already defined as: %r' % (key, self[key])) if isinstance(value, auto): if value.value == _auto_null: - value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:]) + value.value = self._generate_next_value( + key, + 1, + len(self._member_names), + self._last_values[:], + ) self._auto_called = True value = value.value self._member_names.append(key) @@ -118,9 +136,10 @@ # This is also why there are checks in EnumMeta like `if Enum is not None` Enum = None - class EnumMeta(type): - """Metaclass for Enum""" + """ + Metaclass for Enum + """ @classmethod def __prepare__(metacls, cls, bases): # check that previous enum members do not exist @@ -130,7 +149,9 @@ # inherit previous flags and _generate_next_value_ function member_type, first_enum = metacls._get_mixins_(cls, bases) if first_enum is not None: - enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) + enum_dict['_generate_next_value_'] = getattr( + first_enum, '_generate_next_value_', None, + ) return enum_dict def __new__(metacls, cls, bases, classdict): @@ -145,8 +166,9 @@ for key in ignore: classdict.pop(key, None) member_type, first_enum = metacls._get_mixins_(cls, bases) - __new__, save_new, use_args = metacls._find_new_(classdict, member_type, - first_enum) + __new__, save_new, use_args = metacls._find_new_( + classdict, member_type, first_enum, + ) # save enum items into separate mapping so they don't get baked into # the new class @@ -175,9 +197,11 @@ # save DynamicClassAttribute attributes from super classes so we know # if we can take the shortcut of storing members in the class dict - dynamic_attributes = {k for c in enum_class.mro() - for k, v in c.__dict__.items() - if isinstance(v, DynamicClassAttribute)} + dynamic_attributes = { + k for c in enum_class.mro() + for k, v in c.__dict__.items() + if isinstance(v, DynamicClassAttribute) + } # Reverse value->name map for hashable values. enum_class._value2member_map_ = {} @@ -287,7 +311,8 @@ return True def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1): - """Either returns an existing member, or creates a new enum class. + """ + Either returns an existing member, or creates a new enum class. This method is used both when an enum class is given a value to match to an enumeration member (i.e. Color(3)) and for the functional API @@ -309,12 +334,18 @@ not correct, unpickling will fail in some circumstances. `type`, if set, will be mixed in as the first base class. - """ if names is None: # simple value lookup return cls.__new__(cls, value) # otherwise, functional API: we're creating a new Enum type - return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start) + return cls._create_( + value, + names, + module=module, + qualname=qualname, + type=type, + start=start, + ) def __contains__(cls, member): if not isinstance(member, Enum): @@ -327,22 +358,23 @@ # nicer error message when someone tries to delete an attribute # (see issue19025). if attr in cls._member_map_: - raise AttributeError( - "%s: cannot delete Enum member." % cls.__name__) + raise AttributeError("%s: cannot delete Enum member." % cls.__name__) super().__delattr__(attr) def __dir__(self): - return (['__class__', '__doc__', '__members__', '__module__'] + - self._member_names_) + return ( + ['__class__', '__doc__', '__members__', '__module__'] + + self._member_names_ + ) def __getattr__(cls, name): - """Return the enum member matching `name` + """ + Return the enum member matching `name` We use __getattr__ instead of descriptors or inserting into the enum class' __dict__ in order to support `name` and `value` being both properties for enum members (which live in the class' __dict__) and enum members themselves. - """ if _is_dunder(name): raise AttributeError(name) @@ -355,6 +387,9 @@ return cls._member_map_[name] def __iter__(cls): + """ + Returns members in definition order. + """ return (cls._member_map_[name] for name in cls._member_names_) def __len__(cls): @@ -362,11 +397,11 @@ @property def __members__(cls): - """Returns a mapping of member name->value. + """ + Returns a mapping of member name->value. This mapping lists all enum members, including aliases. Note that this is a read-only view of the internal mapping. - """ return MappingProxyType(cls._member_map_) @@ -374,15 +409,18 @@ return "" % cls.__name__ def __reversed__(cls): + """ + Returns members in reverse definition order. + """ return (cls._member_map_[name] for name in reversed(cls._member_names_)) def __setattr__(cls, name, value): - """Block attempts to reassign Enum members. + """ + Block attempts to reassign Enum members. A simple assignment to the class namespace only changes one of the several possible ways to get an Enum member from the Enum class, resulting in an inconsistent Enumeration. - """ member_map = cls.__dict__.get('_member_map_', {}) if name in member_map: @@ -390,7 +428,8 @@ super().__setattr__(name, value) def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1): - """Convenience method to create a new Enum class. + """ + Convenience method to create a new Enum class. `names` can be: @@ -399,7 +438,6 @@ * An iterable of member names. Values are incremented by 1 from `start`. * An iterable of (member name, value) pairs. * A mapping of member name -> value pairs. - """ metacls = cls.__class__ bases = (cls, ) if type is None else (type, cls) @@ -486,15 +524,18 @@ for chain in bases: for base in chain.__mro__: if issubclass(base, Enum) and base._member_names_: - raise TypeError("%s: cannot extend enumeration %r" % (class_name, base.__name__)) + raise TypeError( + "%s: cannot extend enumeration %r" + % (class_name, base.__name__) + ) @staticmethod def _get_mixins_(class_name, bases): - """Returns the type for creating enum members, and the first inherited + """ + Returns the type for creating enum members, and the first inherited enum class. bases: the tuple of bases that was given to __new__ - """ if not bases: return object, Enum @@ -506,12 +547,16 @@ for base in chain.__mro__: if base is object: continue + elif issubclass(base, Enum): + if base._member_type_ is not object: + data_types.append(base._member_type_) + break elif '__new__' in base.__dict__: if issubclass(base, Enum): continue data_types.append(candidate or base) break - elif not issubclass(base, Enum): + else: candidate = base if len(data_types) > 1: raise TypeError('%r: too many data types: %r' % (class_name, data_types)) @@ -533,12 +578,12 @@ @staticmethod def _find_new_(classdict, member_type, first_enum): - """Returns the __new__ to be used for creating the enum members. + """ + Returns the __new__ to be used for creating the enum members. classdict: the class dictionary given to __new__ member_type: the data type whose __new__ will be used by default first_enum: enumeration to check for an overriding __new__ - """ # now find the correct __new__, checking to see of one was defined # by the user; also check earlier enum classes in case a __new__ was @@ -578,10 +623,10 @@ class Enum(metaclass=EnumMeta): - """Generic enumeration. + """ + Generic enumeration. Derive from this class to define new enumerations. - """ def __new__(cls, value): # all enum instances are actually created during class construction @@ -624,6 +669,14 @@ raise exc def _generate_next_value_(name, start, count, last_values): + """ + Generate the next value when not given. + + name: the name of the member + start: the initial start value or None + count: the number of existing members + last_value: the last value assigned or None + """ for last_value in reversed(last_values): try: return last_value + 1 @@ -644,21 +697,27 @@ return "%s.%s" % (self.__class__.__name__, self._name_) def __dir__(self): + """ + Returns all members and all public methods + """ added_behavior = [ m for cls in self.__class__.mro() for m in cls.__dict__ if m[0] != '_' and m not in self._member_map_ - ] + ] + [m for m in self.__dict__ if m[0] != '_'] return (['__class__', '__doc__', '__module__'] + added_behavior) def __format__(self, format_spec): + """ + Returns format using actual value type unless __str__ has been overridden. + """ # mixed-in Enums should use the mixed-in type's __format__, otherwise # we can get strange results with the Enum name showing up instead of # the value # pure Enum branch, or branch with __str__ explicitly overridden - str_overridden = type(self).__str__ != Enum.__str__ + str_overridden = type(self).__str__ not in (Enum.__str__, Flag.__str__) if self._member_type_ is object or str_overridden: cls = str val = str(self) @@ -700,7 +759,9 @@ return self.name class Flag(Enum): - """Support for flags""" + """ + Support for flags + """ def _generate_next_value_(name, start, count, last_values): """ @@ -723,6 +784,9 @@ @classmethod def _missing_(cls, value): + """ + Returns member (possibly creating it) if one can be found for value. + """ original_value = value if value < 0: value = ~value @@ -752,6 +816,9 @@ return pseudo_member def __contains__(self, other): + """ + Returns True if self has at least the same flags set as other. + """ if not isinstance(other, self.__class__): raise TypeError( "unsupported operand type(s) for 'in': '%s' and '%s'" % ( @@ -810,10 +877,15 @@ class IntFlag(int, Flag): - """Support for integer-based Flags""" + """ + Support for integer-based Flags + """ @classmethod def _missing_(cls, value): + """ + Returns member (possibly creating it) if one can be found for value. + """ if not isinstance(value, int): raise ValueError("%r is not a valid %s" % (value, cls.__name__)) new_member = cls._create_pseudo_member_(value) @@ -821,6 +893,9 @@ @classmethod def _create_pseudo_member_(cls, value): + """ + Create a composite member iff value contains only members. + """ pseudo_member = cls._value2member_map_.get(value, None) if pseudo_member is None: need_to_create = [value] @@ -875,11 +950,15 @@ def _high_bit(value): - """returns index of highest bit, or -1 if value is zero or negative""" + """ + returns index of highest bit, or -1 if value is zero or negative + """ return value.bit_length() - 1 def unique(enumeration): - """Class decorator for enumerations ensuring unique member values.""" + """ + Class decorator for enumerations ensuring unique member values. + """ duplicates = [] for name, member in enumeration.__members__.items(): if name != member.name: @@ -892,7 +971,9 @@ return enumeration def _decompose(flag, value): - """Extract all members from the value.""" + """ + Extract all members from the value. + """ # _decompose is only called if the value is not named not_covered = value negative = value < 0 diff -Nru python3.8-3.8.6/Lib/http/client.py python3.8-3.8.7/Lib/http/client.py --- python3.8-3.8.6/Lib/http/client.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/http/client.py 2020-12-21 16:25:24.000000000 +0000 @@ -846,7 +846,7 @@ the endpoint passed to `set_tunnel`. This done by sending an HTTP CONNECT request to the proxy server when the connection is established. - This method must be called before the HTML connection has been + This method must be called before the HTTP connection has been established. The headers argument should be a mapping of extra HTTP headers to send diff -Nru python3.8-3.8.6/Lib/idlelib/calltip.py python3.8-3.8.7/Lib/idlelib/calltip.py --- python3.8-3.8.6/Lib/idlelib/calltip.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/calltip.py 2020-12-21 16:25:24.000000000 +0000 @@ -55,18 +55,50 @@ self.open_calltip(False) def open_calltip(self, evalfuncs): - self.remove_calltip_window() + """Maybe close an existing calltip and maybe open a new calltip. + Called from (force_open|try_open|refresh)_calltip_event functions. + """ hp = HyperParser(self.editwin, "insert") sur_paren = hp.get_surrounding_brackets('(') + + # If not inside parentheses, no calltip. if not sur_paren: + self.remove_calltip_window() return + + # If a calltip is shown for the current parentheses, do + # nothing. + if self.active_calltip: + opener_line, opener_col = map(int, sur_paren[0].split('.')) + if ( + (opener_line, opener_col) == + (self.active_calltip.parenline, self.active_calltip.parencol) + ): + return + hp.set_index(sur_paren[0]) - expression = hp.get_expression() + try: + expression = hp.get_expression() + except ValueError: + expression = None if not expression: + # No expression before the opening parenthesis, e.g. + # because it's in a string or the opener for a tuple: + # Do nothing. return + + # At this point, the current index is after an opening + # parenthesis, in a section of code, preceded by a valid + # expression. If there is a calltip shown, it's not for the + # same index and should be closed. + self.remove_calltip_window() + + # Simple, fast heuristic: If the preceding expression includes + # an opening parenthesis, it likely includes a function call. if not evalfuncs and (expression.find('(') != -1): return + argspec = self.fetch_tip(expression) if not argspec: return @@ -133,6 +165,7 @@ ob_call = ob.__call__ except BaseException: # Buggy user object could raise anything. return '' # No popup for non-callables. + # For Get_argspecTest.test_buggy_getattr_class, CallA() & CallB(). fob = ob_call if isinstance(ob_call, types.MethodType) else ob # Initialize argspec and wrap it to get lines. @@ -153,10 +186,7 @@ if len(argspec) > _MAX_COLS else [argspec] if argspec else []) # Augment lines from docstring, if any, and join to get argspec. - if isinstance(ob_call, types.MethodType): - doc = ob_call.__doc__ - else: - doc = getattr(ob, "__doc__", "") + doc = inspect.getdoc(ob) if doc: for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]: line = line.strip() diff -Nru python3.8-3.8.6/Lib/idlelib/codecontext.py python3.8-3.8.7/Lib/idlelib/codecontext.py --- python3.8-3.8.6/Lib/idlelib/codecontext.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/codecontext.py 2020-12-21 16:25:24.000000000 +0000 @@ -7,11 +7,14 @@ enclosing block. The number of hint lines is determined by the maxlines variable in the codecontext section of config-extensions.def. Lines which do not open blocks are not shown in the context hints pane. + +For EditorWindows, <> is bound to CodeContext(self). +toggle_code_context_event. """ import re from sys import maxsize as INFINITY -import tkinter +from tkinter import Frame, Text, TclError from tkinter.constants import NSEW, SUNKEN from idlelib.config import idleConf @@ -83,7 +86,7 @@ if self.t1 is not None: try: self.text.after_cancel(self.t1) - except tkinter.TclError: # pragma: no cover + except TclError: # pragma: no cover pass self.t1 = None @@ -111,7 +114,7 @@ padx += widget.tk.getint(info['padx']) padx += widget.tk.getint(widget.cget('padx')) border += widget.tk.getint(widget.cget('border')) - context = self.context = tkinter.Text( + context = self.context = Text( self.editwin.text_frame, height=1, width=1, # Don't request more than we get. @@ -127,7 +130,7 @@ line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'linenumber') - self.cell00 = tkinter.Frame(self.editwin.text_frame, + self.cell00 = Frame(self.editwin.text_frame, bg=line_number_colors['background']) self.cell00.grid(row=0, column=0, sticky=NSEW) menu_status = 'Hide' @@ -221,7 +224,7 @@ """ try: self.context.index("sel.first") - except tkinter.TclError: + except TclError: lines = len(self.info) if lines == 1: # No context lines are showing. newtop = 1 diff -Nru python3.8-3.8.6/Lib/idlelib/configdialog.py python3.8-3.8.7/Lib/idlelib/configdialog.py --- python3.8-3.8.6/Lib/idlelib/configdialog.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/configdialog.py 2020-12-21 16:25:24.000000000 +0000 @@ -67,7 +67,6 @@ if not _utest: self.withdraw() - self.configure(borderwidth=5) self.title(title or 'IDLE Preferences') x = parent.winfo_rootx() + 20 y = parent.winfo_rooty() + (30 if not _htest else 150) @@ -97,6 +96,7 @@ """Create and place widgets for tabbed dialog. Widgets Bound to self: + frame: encloses all other widgets note: Notebook highpage: HighPage fontpage: FontPage @@ -109,7 +109,9 @@ load_configs: Load pages except for extensions. activate_config_changes: Tell editors to reload. """ - self.note = note = Notebook(self) + self.frame = frame = Frame(self, padding="5px") + self.frame.grid(sticky="nwes") + self.note = note = Notebook(frame) self.highpage = HighPage(note) self.fontpage = FontPage(note, self.highpage) self.keyspage = KeysPage(note) @@ -148,7 +150,7 @@ padding_args = {} else: padding_args = {'padding': (6, 3)} - outer = Frame(self, padding=2) + outer = Frame(self.frame, padding=2) buttons_frame = Frame(outer, padding=2) self.buttons = {} for txt, cmd in ( @@ -687,7 +689,7 @@ def __init__(self, master): super().__init__(master) - self.cd = master.master + self.cd = master.winfo_toplevel() self.style = Style(master) self.create_page_highlight() self.load_theme_cfg() @@ -1346,7 +1348,7 @@ def __init__(self, master): super().__init__(master) - self.cd = master.master + self.cd = master.winfo_toplevel() self.create_page_keys() self.load_key_cfg() diff -Nru python3.8-3.8.6/Lib/idlelib/help.html python3.8-3.8.7/Lib/idlelib/help.html --- python3.8-3.8.6/Lib/idlelib/help.html 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/help.html 2020-12-21 16:25:24.000000000 +0000 @@ -1,23 +1,24 @@ - + - IDLE — Python 3.10.0a0 documentation + + IDLE — Python 3.10.0a1 documentation - - - - - + + + + + - + @@ -71,11 +72,12 @@
  • - 3.10.0a0 Documentation » + 3.10.0a1 Documentation »
  • +
  • @@ -426,30 +428,30 @@ the Command key on macOS.

    • Backspace deletes to the left; Del deletes to the right

    • -
    • C-Backspace delete word left; C-Del delete word to the right

    • -
    • Arrow keys and Page Up/Page Down to move around

    • -
    • C-LeftArrow and C-RightArrow moves by words

    • +
    • C-Backspace delete word left; C-Del delete word to the right

    • +
    • Arrow keys and Page Up/Page Down to move around

    • +
    • C-LeftArrow and C-RightArrow moves by words

    • Home/End go to begin/end of line

    • -
    • C-Home/C-End go to begin/end of file

    • +
    • C-Home/C-End go to begin/end of file

    • Some useful Emacs bindings are inherited from Tcl/Tk:

        -
      • C-a beginning of line

      • -
      • C-e end of line

      • -
      • C-k kill line (but doesn’t put it in clipboard)

      • -
      • C-l center window around the insertion point

      • -
      • C-b go backward one character without deleting (usually you can +

      • C-a beginning of line

      • +
      • C-e end of line

      • +
      • C-k kill line (but doesn’t put it in clipboard)

      • +
      • C-l center window around the insertion point

      • +
      • C-b go backward one character without deleting (usually you can also use the cursor key for this)

      • -
      • C-f go forward one character without deleting (usually you can +

      • C-f go forward one character without deleting (usually you can also use the cursor key for this)

      • -
      • C-p go up one line (usually you can also use the cursor key for +

      • C-p go up one line (usually you can also use the cursor key for this)

      • -
      • C-d delete next character

      • +
      • C-d delete next character

    -

    Standard keybindings (like C-c to copy and C-v to paste) +

    Standard keybindings (like C-c to copy and C-v to paste) may work. Keybindings are selected in the Configure IDLE dialog.

    @@ -486,7 +488,7 @@ directory name and a separator.

    Instead of waiting, or after a box is closed, open a completion box immediately with Show Completions on the Edit menu. The default hot -key is C-space. If one types a prefix for the desired name +key is C-space. If one types a prefix for the desired name before opening the box, the first match or near miss is made visible. The result is the same as if one enters a prefix after the box is displayed. Show Completions after a quote completes @@ -559,14 +561,14 @@

    The editing features described in previous subsections work when entering code interactively. IDLE’s Shell window also responds to the following keys.

      -
    • C-c interrupts executing command

    • -
    • C-d sends end-of-file; closes window if typed at a >>> prompt

    • -
    • Alt-/ (Expand word) is also useful to reduce typing

      +
    • C-c interrupts executing command

    • +
    • C-d sends end-of-file; closes window if typed at a >>> prompt

    • +
    • Alt-/ (Expand word) is also useful to reduce typing

      Command history

        -
      • Alt-p retrieves previous command matching what you have typed. On -macOS use C-p.

      • -
      • Alt-n retrieves next. On macOS use C-n.

      • +
      • Alt-p retrieves previous command matching what you have typed. On +macOS use C-p.

      • +
      • Alt-n retrieves next. On macOS use C-n.

      • Return while on any previous command retrieves that command

    • @@ -852,6 +854,7 @@
    +
    @@ -947,11 +950,12 @@
  • - 3.10.0a0 Documentation » + 3.10.0a1 Documentation »
  • +
  • @@ -978,11 +982,11 @@

    - Last updated on Sep 22, 2020. + Last updated on Oct 20, 2020. Found a bug?
    - Created using Sphinx 2.1.1. + Created using Sphinx 3.2.1. diff -Nru python3.8-3.8.6/Lib/idlelib/help.py python3.8-3.8.7/Lib/idlelib/help.py --- python3.8-3.8.6/Lib/idlelib/help.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/help.py 2020-12-21 16:25:24.000000000 +0000 @@ -28,8 +28,8 @@ from os.path import abspath, dirname, isfile, join from platform import python_version -from tkinter import Toplevel, Frame, Text, Menu -from tkinter.ttk import Menubutton, Scrollbar +from tkinter import Toplevel, Text, Menu +from tkinter.ttk import Frame, Menubutton, Scrollbar, Style from tkinter import font as tkfont from idlelib.config import idleConf @@ -212,7 +212,9 @@ def __init__(self, parent, filename): Frame.__init__(self, parent) self.text = text = HelpText(self, filename) - self['background'] = text['background'] + self.style = Style(parent) + self['style'] = 'helpframe.TFrame' + self.style.configure('helpframe.TFrame', background=text['background']) self.toc = toc = self.toc_menu(text) self.scroll = scroll = Scrollbar(self, command=text.yview) text['yscrollcommand'] = scroll.set Binary files /tmp/tmpgwkoGW/C7ZJ71KE1Z/python3.8-3.8.6/Lib/idlelib/Icons/idle.ico and /tmp/tmpgwkoGW/uSs3611ZLj/python3.8-3.8.7/Lib/idlelib/Icons/idle.ico differ diff -Nru python3.8-3.8.6/Lib/idlelib/Icons/README.txt python3.8-3.8.7/Lib/idlelib/Icons/README.txt --- python3.8-3.8.6/Lib/idlelib/Icons/README.txt 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/Icons/README.txt 2020-12-21 16:25:24.000000000 +0000 @@ -7,3 +7,7 @@ Various different formats and sizes are available at this GitHub Pull Request: https://github.com/python/cpython/pull/17473 + +The idle.ico file was created with ImageMagick: + + $ convert idle_16.png idle_32.png idle_48.png idle_256.png idle.ico diff -Nru python3.8-3.8.6/Lib/idlelib/idle_test/mock_tk.py python3.8-3.8.7/Lib/idlelib/idle_test/mock_tk.py --- python3.8-3.8.6/Lib/idlelib/idle_test/mock_tk.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/idle_test/mock_tk.py 2020-12-21 16:25:24.000000000 +0000 @@ -3,6 +3,9 @@ A gui object is anything with a master or parent parameter, which is typically required in spite of what the doc strings say. """ +import re +from _tkinter import TclError + class Event: '''Minimal mock with attributes for testing event handlers. @@ -22,6 +25,7 @@ "Create event with attributes needed for test" self.__dict__.update(kwds) + class Var: "Use for String/Int/BooleanVar: incomplete" def __init__(self, master=None, value=None, name=None): @@ -33,6 +37,7 @@ def get(self): return self.value + class Mbox_func: """Generic mock for messagebox functions, which all have the same signature. @@ -50,6 +55,7 @@ self.kwds = kwds return self.result # Set by tester for ask functions + class Mbox: """Mock for tkinter.messagebox with an Mbox_func for each function. @@ -85,7 +91,6 @@ showinfo = Mbox_func() # None showwarning = Mbox_func() # None -from _tkinter import TclError class Text: """A semi-functional non-gui replacement for tkinter.Text text editors. @@ -154,6 +159,8 @@ if char.endswith(' lineend') or char == 'end': return line, linelength # Tk requires that ignored chars before ' lineend' be valid int + if m := re.fullmatch(r'end-(\d*)c', char, re.A): # Used by hyperparser. + return line, linelength - int(m.group(1)) # Out of bounds char becomes first or last index of line char = int(char) @@ -177,7 +184,6 @@ n -= 1 return n, len(self.data[n]) + endflag - def insert(self, index, chars): "Insert chars before the character at index." @@ -193,7 +199,6 @@ self.data[line+1:line+1] = chars[1:] self.data[line+len(chars)-1] += after - def get(self, index1, index2=None): "Return slice from index1 to index2 (default is 'index1+1')." @@ -212,7 +217,6 @@ lines.append(self.data[endline][:endchar]) return ''.join(lines) - def delete(self, index1, index2=None): '''Delete slice from index1 to index2 (default is 'index1+1'). @@ -297,6 +301,7 @@ "Bind to this widget at event sequence a call to function func." pass + class Entry: "Mock for tkinter.Entry." def focus_set(self): diff -Nru python3.8-3.8.6/Lib/idlelib/idle_test/test_calltip.py python3.8-3.8.7/Lib/idlelib/idle_test/test_calltip.py --- python3.8-3.8.6/Lib/idlelib/idle_test/test_calltip.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/idle_test/test_calltip.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,10 +1,12 @@ -"Test calltip, coverage 60%" +"Test calltip, coverage 76%" from idlelib import calltip import unittest +from unittest.mock import Mock import textwrap import types import re +from idlelib.idle_test.mock_tk import Text # Test Class TC is used in multiple get_argspec test methods @@ -97,7 +99,12 @@ (width=70, initial_indent='', subsequent_indent='', expand_tabs=True, replace_whitespace=True, fix_sentence_endings=False, break_long_words=True, drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None, - placeholder=' [...]')''') + placeholder=' [...]') +Object for wrapping/filling text. The public interface consists of +the wrap() and fill() methods; the other methods are just there for +subclasses to override in order to tweak the default behaviour. +If you want to completely replace the main wrapping algorithm, +you\'ll probably have to override _wrap_chunks().''') def test_properly_formated(self): @@ -239,7 +246,7 @@ __class__ = property({}.__getitem__, {}.__setitem__) class Object(metaclass=Type): __slots__ = '__class__' - for meth, mtip in ((Type, default_tip), (Object, default_tip), + for meth, mtip in ((Type, get_spec(type)), (Object, default_tip), (Object(), '')): with self.subTest(meth=meth, mtip=mtip): self.assertEqual(get_spec(meth), mtip) @@ -257,5 +264,100 @@ self.assertIs(calltip.get_entity('int'), int) +# Test the 9 Calltip methods. +# open_calltip is about half the code; the others are fairly trivial. +# The default mocks are what are needed for open_calltip. + +class mock_Shell(): + "Return mock sufficient to pass to hyperparser." + def __init__(self, text): + text.tag_prevrange = Mock(return_value=None) + self.text = text + self.prompt_last_line = ">>> " + self.indentwidth = 4 + self.tabwidth = 8 + + +class mock_TipWindow: + def __init__(self): + pass + + def showtip(self, text, parenleft, parenright): + self.args = parenleft, parenright + self.parenline, self.parencol = map(int, parenleft.split('.')) + + +class WrappedCalltip(calltip.Calltip): + def _make_tk_calltip_window(self): + return mock_TipWindow() + + def remove_calltip_window(self, event=None): + if self.active_calltip: # Setup to None. + self.active_calltip = None + self.tips_removed += 1 # Setup to 0. + + def fetch_tip(self, expression): + return 'tip' + + +class CalltipTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.text = Text() + cls.ct = WrappedCalltip(mock_Shell(cls.text)) + + def setUp(self): + self.text.delete('1.0', 'end') # Insert and call + self.ct.active_calltip = None + # Test .active_calltip, +args + self.ct.tips_removed = 0 + + def open_close(self, testfunc): + # Open-close template with testfunc called in between. + opentip = self.ct.open_calltip + self.text.insert(1.0, 'f(') + opentip(False) + self.tip = self.ct.active_calltip + testfunc(self) ### + self.text.insert('insert', ')') + opentip(False) + self.assertIsNone(self.ct.active_calltip, None) + + def test_open_close(self): + def args(self): + self.assertEqual(self.tip.args, ('1.1', '1.end')) + self.open_close(args) + + def test_repeated_force(self): + def force(self): + for char in 'abc': + self.text.insert('insert', 'a') + self.ct.open_calltip(True) + self.ct.open_calltip(True) + self.assertIs(self.ct.active_calltip, self.tip) + self.open_close(force) + + def test_repeated_parens(self): + def parens(self): + for context in "a", "'": + with self.subTest(context=context): + self.text.insert('insert', context) + for char in '(()())': + self.text.insert('insert', char) + self.assertIs(self.ct.active_calltip, self.tip) + self.text.insert('insert', "'") + self.open_close(parens) + + def test_comment_parens(self): + def comment(self): + self.text.insert('insert', "# ") + for char in '(()())': + self.text.insert('insert', char) + self.assertIs(self.ct.active_calltip, self.tip) + self.text.insert('insert', "\n") + self.open_close(comment) + + if __name__ == '__main__': unittest.main(verbosity=2) diff -Nru python3.8-3.8.6/Lib/idlelib/idle_test/test_searchbase.py python3.8-3.8.7/Lib/idlelib/idle_test/test_searchbase.py --- python3.8-3.8.6/Lib/idlelib/idle_test/test_searchbase.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/idle_test/test_searchbase.py 2020-12-21 16:25:24.000000000 +0000 @@ -76,7 +76,7 @@ def test_make_entry(self): equal = self.assertEqual self.dialog.row = 0 - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) entry, label = self.dialog.make_entry("Test:", 'hello') equal(label['text'], 'Test:') @@ -89,7 +89,7 @@ equal(self.dialog.row, 1) def test_create_entries(self): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.row = 0 self.engine.setpat('hello') self.dialog.create_entries() @@ -97,7 +97,7 @@ def test_make_frame(self): self.dialog.row = 0 - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) frame, label = self.dialog.make_frame() self.assertEqual(label, '') self.assertEqual(str(type(frame)), "") @@ -108,7 +108,7 @@ self.assertEqual(label['text'], 'testlabel') def btn_test_setup(self, meth): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.row = 0 return meth() @@ -140,13 +140,13 @@ self.assertEqual(var.get(), state) def test_make_button(self): - self.dialog.top = self.root - self.dialog.buttonframe = Frame(self.dialog.top) + self.dialog.frame = Frame(self.root) + self.dialog.buttonframe = Frame(self.dialog.frame) btn = self.dialog.make_button('Test', self.dialog.close) self.assertEqual(btn['text'], 'Test') def test_create_command_buttons(self): - self.dialog.top = self.root + self.dialog.frame = Frame(self.root) self.dialog.create_command_buttons() # Look for close button command in buttonframe closebuttoncommand = '' diff -Nru python3.8-3.8.6/Lib/idlelib/idle_test/test_searchengine.py python3.8-3.8.7/Lib/idlelib/idle_test/test_searchengine.py --- python3.8-3.8.6/Lib/idlelib/idle_test/test_searchengine.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/idle_test/test_searchengine.py 2020-12-21 16:25:24.000000000 +0000 @@ -175,11 +175,13 @@ engine.setpat('') Equal(engine.getprog(), None) + Equal(Mbox.showerror.message, + 'Error: Empty regular expression') engine.setpat('+') engine.revar.set(1) Equal(engine.getprog(), None) - self.assertEqual(Mbox.showerror.message, - 'Error: nothing to repeat at position 0\nPattern: +') + Equal(Mbox.showerror.message, + 'Error: nothing to repeat\nPattern: +\nOffset: 0') def test_report_error(self): showerror = Mbox.showerror diff -Nru python3.8-3.8.6/Lib/idlelib/NEWS.txt python3.8-3.8.7/Lib/idlelib/NEWS.txt --- python3.8-3.8.6/Lib/idlelib/NEWS.txt 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/NEWS.txt 2020-12-21 16:25:24.000000000 +0000 @@ -1,13 +1,28 @@ -What's New in IDLE 3.8.6 -Released on 2020-09-14? +What's New in IDLE 3.8.7 +Released on 2020-12-?? ====================================== +bpo-42426: Fix reporting offset of the RE error in searchengine. + +bpo-42416: Get docstrings for IDLE calltips more often +by using inspect.getdoc. + +bpo-33987: Mostly finish using ttk widgets, mainly for editor, +settings, and searches. Some patches by Mark Roseman. + +bpo-41775: Make 'IDLE Shell' the shell title. + bpo-35764: Rewrite the Calltips doc section. bpo-40181: In calltips, stop reminding that '/' marks the end of positional-only arguments. + +What's New in IDLE 3.8.6 +Released on 2020-09-?? +====================================== + bpo-41468: Improve IDLE run crash error message (which users should never see). diff -Nru python3.8-3.8.6/Lib/idlelib/pyshell.py python3.8-3.8.7/Lib/idlelib/pyshell.py --- python3.8-3.8.6/Lib/idlelib/pyshell.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/pyshell.py 2020-12-21 16:25:24.000000000 +0000 @@ -757,7 +757,7 @@ def runcode(self, code): "Override base class method" if self.tkconsole.executing: - self.interp.restart_subprocess() + self.restart_subprocess() self.checklinecache() debugger = self.debugger try: @@ -833,7 +833,7 @@ class PyShell(OutputWindow): - shell_title = "Python " + python_version() + " Shell" + shell_title = "IDLE Shell " + python_version() # Override classes ColorDelegator = ModifiedColorDelegator @@ -1061,8 +1061,10 @@ (sys.version, sys.platform, self.COPYRIGHT, nosub)) self.text.focus_force() self.showprompt() + # User code should use separate default Tk root window import tkinter - tkinter._default_root = None # 03Jan04 KBK What's this? + tkinter._support_default_root = True + tkinter._default_root = None return True def stop_readline(self): diff -Nru python3.8-3.8.6/Lib/idlelib/searchbase.py python3.8-3.8.7/Lib/idlelib/searchbase.py --- python3.8-3.8.6/Lib/idlelib/searchbase.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/searchbase.py 2020-12-21 16:25:24.000000000 +0000 @@ -33,6 +33,7 @@ '''Initialize root, engine, and top attributes. top (level widget): set in create_widgets() called from open(). + frame: container for all widgets in dialog. text (Text searched): set in open(), only used in subclasses(). ent (ry): created in make_entry() called from create_entry(). row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). @@ -83,10 +84,14 @@ top.wm_title(self.title) top.wm_iconname(self.icon) self.top = top + self.frame = Frame(top, padding="5px") + self.frame.grid(sticky="nwes") + top.grid_columnconfigure(0, weight=100) + top.grid_rowconfigure(0, weight=100) self.row = 0 - self.top.grid_columnconfigure(0, pad=2, weight=0) - self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100) + self.frame.grid_columnconfigure(0, pad=2, weight=0) + self.frame.grid_columnconfigure(1, pad=2, minsize=100, weight=100) self.create_entries() # row 0 (and maybe 1), cols 0, 1 self.create_option_buttons() # next row, cols 0, 1 @@ -99,9 +104,9 @@ entry - gridded labeled Entry for text entry. label - Label widget, returned for testing. ''' - label = Label(self.top, text=label_text) + label = Label(self.frame, text=label_text) label.grid(row=self.row, column=0, sticky="nw") - entry = Entry(self.top, textvariable=var, exportselection=0) + entry = Entry(self.frame, textvariable=var, exportselection=0) entry.grid(row=self.row, column=1, sticky="nwe") self.row = self.row + 1 return entry, label @@ -117,11 +122,11 @@ label - Label widget, returned for testing. ''' if labeltext: - label = Label(self.top, text=labeltext) + label = Label(self.frame, text=labeltext) label.grid(row=self.row, column=0, sticky="nw") else: label = '' - frame = Frame(self.top) + frame = Frame(self.frame) frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe") self.row = self.row + 1 return frame, label @@ -171,7 +176,7 @@ def create_command_buttons(self): "Place buttons in vertical command frame gridded on right." - f = self.buttonframe = Frame(self.top) + f = self.buttonframe = Frame(self.frame) f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) b = self.make_button("Close", self.close) diff -Nru python3.8-3.8.6/Lib/idlelib/searchengine.py python3.8-3.8.7/Lib/idlelib/searchengine.py --- python3.8-3.8.6/Lib/idlelib/searchengine.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/searchengine.py 2020-12-21 16:25:24.000000000 +0000 @@ -84,20 +84,17 @@ flags = flags | re.IGNORECASE try: prog = re.compile(pat, flags) - except re.error as what: - args = what.args - msg = args[0] - col = args[1] if len(args) >= 2 else -1 - self.report_error(pat, msg, col) + except re.error as e: + self.report_error(pat, e.msg, e.pos) return None return prog - def report_error(self, pat, msg, col=-1): + def report_error(self, pat, msg, col=None): # Derived class could override this with something fancier msg = "Error: " + str(msg) if pat: msg = msg + "\nPattern: " + str(pat) - if col >= 0: + if col is not None: msg = msg + "\nOffset: " + str(col) tkMessageBox.showerror("Regular expression error", msg, master=self.root) diff -Nru python3.8-3.8.6/Lib/idlelib/statusbar.py python3.8-3.8.7/Lib/idlelib/statusbar.py --- python3.8-3.8.6/Lib/idlelib/statusbar.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/idlelib/statusbar.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,4 +1,4 @@ -from tkinter import Frame, Label +from tkinter.ttk import Label, Frame class MultiStatusBar(Frame): @@ -20,7 +20,8 @@ def _multistatus_bar(parent): # htest # - from tkinter import Toplevel, Frame, Text, Button + from tkinter import Toplevel, Text + from tkinter.ttk import Frame, Button top = Toplevel(parent) x, y = map(int, parent.geometry().split('+')[1:]) top.geometry("+%d+%d" %(x, y + 175)) diff -Nru python3.8-3.8.6/Lib/importlib/metadata.py python3.8-3.8.7/Lib/importlib/metadata.py --- python3.8-3.8.6/Lib/importlib/metadata.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/importlib/metadata.py 2020-12-21 16:25:24.000000000 +0000 @@ -408,8 +408,8 @@ names = zip_path.root.namelist() self.joinpath = zip_path.joinpath - return ( - posixpath.split(child)[0] + return dict.fromkeys( + child.split(posixpath.sep, 1)[0] for child in names ) @@ -475,7 +475,6 @@ ) - class PathDistribution(Distribution): def __init__(self, path): """Construct a distribution from a path to the metadata directory. diff -Nru python3.8-3.8.6/Lib/inspect.py python3.8-3.8.7/Lib/inspect.py --- python3.8-3.8.6/Lib/inspect.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/inspect.py 2020-12-21 16:25:24.000000000 +0000 @@ -837,7 +837,12 @@ lnum = object.co_firstlineno - 1 pat = re.compile(r'^(\s*def\s)|(\s*async\s+def\s)|(.*(? 0: - if pat.match(lines[lnum]): break + try: + line = lines[lnum] + except IndexError: + raise OSError('lineno is out of bounds') + if pat.match(line): + break lnum = lnum - 1 return lines, lnum raise OSError('could not find code object') @@ -899,6 +904,7 @@ self.indecorator = False self.decoratorhasargs = False self.last = 1 + self.body_col0 = None def tokeneater(self, type, token, srowcol, erowcol, line): if not self.started and not self.indecorator: @@ -930,6 +936,8 @@ elif self.passline: pass elif type == tokenize.INDENT: + if self.body_col0 is None and self.started: + self.body_col0 = erowcol[1] self.indent = self.indent + 1 self.passline = True elif type == tokenize.DEDENT: @@ -939,6 +947,10 @@ # not e.g. for "if: else:" or "try: finally:" blocks) if self.indent <= 0: raise EndOfBlock + elif type == tokenize.COMMENT: + if self.body_col0 is not None and srowcol[1] >= self.body_col0: + # Include comments if indented at least as much as the block + self.last = srowcol[0] elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL): # any other token on the same indentation level end the previous # block as well, except the pseudo-tokens COMMENT and NL. diff -Nru python3.8-3.8.6/Lib/lib2to3/Grammar.txt python3.8-3.8.7/Lib/lib2to3/Grammar.txt --- python3.8-3.8.6/Lib/lib2to3/Grammar.txt 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/lib2to3/Grammar.txt 2020-12-21 16:25:24.000000000 +0000 @@ -18,15 +18,55 @@ async_funcdef: ASYNC funcdef funcdef: 'def' NAME parameters ['->' test] ':' suite parameters: '(' [typedargslist] ')' -typedargslist: ((tfpdef ['=' test] ',')* - ('*' [tname] (',' tname ['=' test])* [',' ['**' tname [',']]] | '**' tname [',']) - | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) + +# The following definition for typedarglist is equivalent to this set of rules: +# +# arguments = argument (',' argument)* +# argument = tfpdef ['=' test] +# kwargs = '**' tname [','] +# args = '*' [tname] +# kwonly_kwargs = (',' argument)* [',' [kwargs]] +# args_kwonly_kwargs = args kwonly_kwargs | kwargs +# poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]] +# typedargslist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs +# typedarglist = arguments ',' '/' [',' [typedargslist_no_posonly]])|(typedargslist_no_posonly)" +# +# It needs to be fully expanded to allow our LL(1) parser to work on it. + +typedargslist: tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [ + ',' [((tfpdef ['=' test] ',')* ('*' [tname] (',' tname ['=' test])* + [',' ['**' tname [',']]] | '**' tname [',']) + | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])] + ] | ((tfpdef ['=' test] ',')* ('*' [tname] (',' tname ['=' test])* + [',' ['**' tname [',']]] | '**' tname [',']) + | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) + tname: NAME [':' test] tfpdef: tname | '(' tfplist ')' tfplist: tfpdef (',' tfpdef)* [','] -varargslist: ((vfpdef ['=' test] ',')* - ('*' [vname] (',' vname ['=' test])* [',' ['**' vname [',']]] | '**' vname [',']) - | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) + +# The following definition for varargslist is equivalent to this set of rules: +# +# arguments = argument (',' argument )* +# argument = vfpdef ['=' test] +# kwargs = '**' vname [','] +# args = '*' [vname] +# kwonly_kwargs = (',' argument )* [',' [kwargs]] +# args_kwonly_kwargs = args kwonly_kwargs | kwargs +# poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]] +# vararglist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs +# varargslist = arguments ',' '/' [','[(vararglist_no_posonly)]] | (vararglist_no_posonly) +# +# It needs to be fully expanded to allow our LL(1) parser to work on it. + +varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ + ((vfpdef ['=' test] ',')* ('*' [vname] (',' vname ['=' test])* + [',' ['**' vname [',']]] | '**' vname [',']) + | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) + ]] | ((vfpdef ['=' test] ',')* + ('*' [vname] (',' vname ['=' test])* [',' ['**' vname [',']]]| '**' vname [',']) + | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) + vname: NAME vfpdef: vname | '(' vfplist ')' vfplist: vfpdef (',' vfpdef)* [','] diff -Nru python3.8-3.8.6/Lib/lib2to3/tests/test_parser.py python3.8-3.8.7/Lib/lib2to3/tests/test_parser.py --- python3.8-3.8.6/Lib/lib2to3/tests/test_parser.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/lib2to3/tests/test_parser.py 2020-12-21 16:25:24.000000000 +0000 @@ -272,6 +272,12 @@ def test_dict_display_2(self): self.validate("""{**{}, 3:4, **{5:6, 7:8}}""") + def test_complex_star_expression(self): + self.validate("func(* [] or [1])") + + def test_complex_double_star_expression(self): + self.validate("func(**{1: 3} if False else {x: x for x in range(3)})") + def test_argument_unpacking_1(self): self.validate("""f(a, *b, *c, d)""") @@ -630,6 +636,7 @@ class TestNamedAssignments(GrammarTest): + """Also known as the walrus operator.""" def test_named_assignment_if(self): driver.parse_string("if f := x(): pass\n") @@ -644,6 +651,30 @@ driver.parse_string("[(lastNum := num) == 1 for num in [1, 2, 3]]\n") +class TestPositionalOnlyArgs(GrammarTest): + + def test_one_pos_only_arg(self): + driver.parse_string("def one_pos_only_arg(a, /): pass\n") + + def test_all_markers(self): + driver.parse_string( + "def all_markers(a, b=2, /, c, d=4, *, e=5, f): pass\n") + + def test_all_with_args_and_kwargs(self): + driver.parse_string( + """def all_markers_with_args_and_kwargs( + aa, b, /, _cc, d, *args, e, f_f, **kwargs, + ): + pass\n""") + + def test_lambda_soup(self): + driver.parse_string( + "lambda a, b, /, c, d, *args, e, f, **kw: kw\n") + + def test_only_positional_or_keyword(self): + driver.parse_string("def func(a,b,/,*,g,e=3): pass\n") + + class TestPickleableException(unittest.TestCase): def test_ParseError(self): err = ParseError('msg', 2, None, (1, 'context')) diff -Nru python3.8-3.8.6/Lib/logging/__init__.py python3.8-3.8.7/Lib/logging/__init__.py --- python3.8-3.8.6/Lib/logging/__init__.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/logging/__init__.py 2020-12-21 16:25:24.000000000 +0000 @@ -515,7 +515,7 @@ responsible for converting a LogRecord to (usually) a string which can be interpreted by either a human or an external system. The base Formatter allows a formatting string to be specified. If none is supplied, the - the style-dependent default value, "%(message)s", "{message}", or + style-dependent default value, "%(message)s", "{message}", or "${message}", is used. The Formatter can be initialized with a format string which makes use of @@ -753,8 +753,8 @@ """ Determine if the specified record is to be logged. - Is the specified record to be logged? Returns 0 for no, nonzero for - yes. If deemed appropriate, the record may be modified in-place. + Returns True if the record should be logged, or False otherwise. + If deemed appropriate, the record may be modified in-place. """ if self.nlen == 0: return True @@ -1269,6 +1269,14 @@ self.loggerClass = None self.logRecordFactory = None + @property + def disable(self): + return self._disable + + @disable.setter + def disable(self, value): + self._disable = _checkLevel(value) + def getLogger(self, name): """ Get a logger with the specified name (channel name), creating it diff -Nru python3.8-3.8.6/Lib/pathlib.py python3.8-3.8.7/Lib/pathlib.py --- python3.8-3.8.6/Lib/pathlib.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/pathlib.py 2020-12-21 16:25:24.000000000 +0000 @@ -1353,8 +1353,13 @@ def rename(self, target): """ - Rename this path to the given path, - and return a new Path instance pointing to the given path. + Rename this path to the target path. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. """ if self._closed: self._raise_closed() @@ -1363,9 +1368,13 @@ def replace(self, target): """ - Rename this path to the given path, clobbering the existing - destination if it exists, and return a new Path instance - pointing to the given path. + Rename this path to the target path, overwriting if that path exists. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. """ if self._closed: self._raise_closed() diff -Nru python3.8-3.8.6/Lib/pickle.py python3.8-3.8.7/Lib/pickle.py --- python3.8-3.8.6/Lib/pickle.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/pickle.py 2020-12-21 16:25:24.000000000 +0000 @@ -340,7 +340,9 @@ # Protect the iteration by using a list copy of sys.modules against dynamic # modules that trigger imports of other modules upon calls to getattr. for module_name, module in sys.modules.copy().items(): - if module_name == '__main__' or module is None: + if (module_name == '__main__' + or module_name == '__mp_main__' # bpo-42406 + or module is None): continue try: if _getattribute(module, name)[0] is obj: diff -Nru python3.8-3.8.6/Lib/plistlib.py python3.8-3.8.7/Lib/plistlib.py --- python3.8-3.8.6/Lib/plistlib.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/plistlib.py 2020-12-21 16:25:24.000000000 +0000 @@ -285,9 +285,16 @@ self.parser.StartElementHandler = self.handle_begin_element self.parser.EndElementHandler = self.handle_end_element self.parser.CharacterDataHandler = self.handle_data + self.parser.EntityDeclHandler = self.handle_entity_decl self.parser.ParseFile(fileobj) return self.root + def handle_entity_decl(self, entity_name, is_parameter_entity, value, base, system_id, public_id, notation_name): + # Reject plist files with entity declarations to avoid XML vulnerabilies in expat. + # Regular plist files don't contain those declerations, and Apple's plutil tool does not + # accept them either. + raise InvalidFileException("XML entity declarations are not supported in plist files") + def handle_begin_element(self, element, attrs): self.data = [] handler = getattr(self, "begin_" + element, None) @@ -357,7 +364,11 @@ self.add_object(False) def end_integer(self): - self.add_object(int(self.get_data())) + raw = self.get_data() + if raw.startswith('0x') or raw.startswith('0X'): + self.add_object(int(raw, 16)) + else: + self.add_object(int(raw)) def end_real(self): self.add_object(float(self.get_data())) @@ -589,7 +600,7 @@ return self._read_object(top_object) except (OSError, IndexError, struct.error, OverflowError, - UnicodeDecodeError): + ValueError): raise InvalidFileException() def _get_size(self, tokenL): @@ -605,7 +616,7 @@ def _read_ints(self, n, size): data = self._fp.read(size * n) if size in _BINARY_FORMAT: - return struct.unpack('>' + _BINARY_FORMAT[size] * n, data) + return struct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data) else: if not size or len(data) != size * n: raise InvalidFileException() @@ -664,18 +675,25 @@ elif tokenH == 0x40: # data s = self._get_size(tokenL) - if self._use_builtin_types: - result = self._fp.read(s) - else: - result = Data(self._fp.read(s)) + result = self._fp.read(s) + if len(result) != s: + raise InvalidFileException() + if not self._use_builtin_types: + result = Data(result) elif tokenH == 0x50: # ascii string s = self._get_size(tokenL) - result = self._fp.read(s).decode('ascii') + data = self._fp.read(s) + if len(data) != s: + raise InvalidFileException() + result = data.decode('ascii') elif tokenH == 0x60: # unicode string - s = self._get_size(tokenL) - result = self._fp.read(s * 2).decode('utf-16be') + s = self._get_size(tokenL) * 2 + data = self._fp.read(s) + if len(data) != s: + raise InvalidFileException() + result = data.decode('utf-16be') elif tokenH == 0x80: # UID # used by Key-Archiver plist files @@ -700,9 +718,11 @@ obj_refs = self._read_refs(s) result = self._dict_type() self._objects[ref] = result - for k, o in zip(key_refs, obj_refs): - result[self._read_object(k)] = self._read_object(o) - + try: + for k, o in zip(key_refs, obj_refs): + result[self._read_object(k)] = self._read_object(o) + except TypeError: + raise InvalidFileException() else: raise InvalidFileException() @@ -716,7 +736,7 @@ elif count < 1 << 16: return 2 - elif count << 1 << 32: + elif count < 1 << 32: return 4 else: diff -Nru python3.8-3.8.6/Lib/profile.py python3.8-3.8.7/Lib/profile.py --- python3.8-3.8.6/Lib/profile.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/profile.py 2020-12-21 16:25:24.000000000 +0000 @@ -587,6 +587,11 @@ (options, args) = parser.parse_args() sys.argv[:] = args + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + if len(args) > 0: if options.module: import runpy diff -Nru python3.8-3.8.6/Lib/pydoc_data/topics.py python3.8-3.8.7/Lib/pydoc_data/topics.py --- python3.8-3.8.6/Lib/pydoc_data/topics.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/pydoc_data/topics.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Wed Sep 23 14:35:51 2020 +# Autogenerated by Sphinx on Mon Dec 21 17:22:46 2020 topics = {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -99,27 +99,26 @@ 'assigned,\n' ' from left to right, to the corresponding targets.\n' '\n' - ' * If the target list contains one target prefixed with an\n' - ' asterisk, called a “starred” target: The object must be ' - 'an\n' - ' iterable with at least as many items as there are targets ' - 'in the\n' - ' target list, minus one. The first items of the iterable ' - 'are\n' - ' assigned, from left to right, to the targets before the ' + ' * If the target list contains one target prefixed with an ' + 'asterisk,\n' + ' called a “starred” target: The object must be an iterable ' + 'with at\n' + ' least as many items as there are targets in the target ' + 'list, minus\n' + ' one. The first items of the iterable are assigned, from ' + 'left to\n' + ' right, to the targets before the starred target. The ' + 'final items\n' + ' of the iterable are assigned to the targets after the ' 'starred\n' - ' target. The final items of the iterable are assigned to ' - 'the\n' - ' targets after the starred target. A list of the remaining ' - 'items\n' - ' in the iterable is then assigned to the starred target ' - '(the list\n' - ' can be empty).\n' + ' target. A list of the remaining items in the iterable is ' + 'then\n' + ' assigned to the starred target (the list can be empty).\n' '\n' ' * Else: The object must be an iterable with the same number ' - 'of\n' - ' items as there are targets in the target list, and the ' - 'items are\n' + 'of items\n' + ' as there are targets in the target list, and the items ' + 'are\n' ' assigned, from left to right, to the corresponding ' 'targets.\n' '\n' @@ -135,10 +134,10 @@ 'in the\n' ' current local namespace.\n' '\n' - ' * Otherwise: the name is bound to the object in the global\n' - ' namespace or the outer namespace determined by ' - '"nonlocal",\n' - ' respectively.\n' + ' * Otherwise: the name is bound to the object in the global ' + 'namespace\n' + ' or the outer namespace determined by "nonlocal", ' + 'respectively.\n' '\n' ' The name is rebound if it was already bound. This may cause ' 'the\n' @@ -225,26 +224,27 @@ 'called with\n' ' appropriate arguments.\n' '\n' - '* If the target is a slicing: The primary expression in the\n' - ' reference is evaluated. It should yield a mutable sequence ' - 'object\n' - ' (such as a list). The assigned object should be a sequence ' - 'object\n' - ' of the same type. Next, the lower and upper bound ' - 'expressions are\n' - ' evaluated, insofar they are present; defaults are zero and ' - 'the\n' - ' sequence’s length. The bounds should evaluate to integers. ' - 'If\n' - ' either bound is negative, the sequence’s length is added to ' - 'it. The\n' - ' resulting bounds are clipped to lie between zero and the ' + '* If the target is a slicing: The primary expression in the ' + 'reference\n' + ' is evaluated. It should yield a mutable sequence object ' + '(such as a\n' + ' list). The assigned object should be a sequence object of ' + 'the same\n' + ' type. Next, the lower and upper bound expressions are ' + 'evaluated,\n' + ' insofar they are present; defaults are zero and the ' 'sequence’s\n' - ' length, inclusive. Finally, the sequence object is asked to ' - 'replace\n' - ' the slice with the items of the assigned sequence. The ' - 'length of\n' - ' the slice may be different from the length of the assigned ' + ' length. The bounds should evaluate to integers. If either ' + 'bound is\n' + ' negative, the sequence’s length is added to it. The ' + 'resulting\n' + ' bounds are clipped to lie between zero and the sequence’s ' + 'length,\n' + ' inclusive. Finally, the sequence object is asked to replace ' + 'the\n' + ' slice with the items of the assigned sequence. The length ' + 'of the\n' + ' slice may be different from the length of the assigned ' 'sequence,\n' ' thus changing the length of the target sequence, if the ' 'target\n' @@ -544,13 +544,17 @@ '\n' '-[ Footnotes ]-\n' '\n' - '[1] The exception is propagated to the invocation stack unless\n' - ' there is a "finally" clause which happens to raise another\n' - ' exception. That new exception causes the old one to be lost.\n' - '\n' - '[2] A string literal appearing as the first statement in the\n' - ' function body is transformed into the function’s "__doc__"\n' - ' attribute and therefore the function’s *docstring*.\n' + '[1] The exception is propagated to the invocation stack unless ' + 'there\n' + ' is a "finally" clause which happens to raise another ' + 'exception.\n' + ' That new exception causes the old one to be lost.\n' + '\n' + '[2] A string literal appearing as the first statement in the ' + 'function\n' + ' body is transformed into the function’s "__doc__" attribute ' + 'and\n' + ' therefore the function’s *docstring*.\n' '\n' '[3] A string literal appearing as the first statement in the class\n' ' body is transformed into the namespace’s "__doc__" item and\n' @@ -688,11 +692,18 @@ 'needs, for\n' ' example, "object.__getattribute__(self, name)".\n' '\n' - ' Note: This method may still be bypassed when looking ' - 'up special\n' - ' methods as the result of implicit invocation via ' - 'language syntax\n' - ' or built-in functions. See Special method lookup.\n' + ' Note:\n' + '\n' + ' This method may still be bypassed when looking up ' + 'special methods\n' + ' as the result of implicit invocation via language ' + 'syntax or\n' + ' built-in functions. See Special method lookup.\n' + '\n' + ' For certain sensitive attribute accesses, raises an ' + 'auditing event\n' + ' "object.__getattr__" with arguments "obj" and ' + '"name".\n' '\n' 'object.__setattr__(self, name, value)\n' '\n' @@ -710,6 +721,11 @@ 'for example,\n' ' "object.__setattr__(self, name, value)".\n' '\n' + ' For certain sensitive attribute assignments, raises ' + 'an auditing\n' + ' event "object.__setattr__" with arguments "obj", ' + '"name", "value".\n' + '\n' 'object.__delattr__(self, name)\n' '\n' ' Like "__setattr__()" but for attribute deletion ' @@ -718,6 +734,11 @@ 'obj.name" is\n' ' meaningful for the object.\n' '\n' + ' For certain sensitive attribute deletions, raises an ' + 'auditing event\n' + ' "object.__delattr__" with arguments "obj" and ' + '"name".\n' + '\n' 'object.__dir__(self)\n' '\n' ' Called when "dir()" is called on the object. A ' @@ -776,15 +797,16 @@ '\n' ' sys.modules[__name__].__class__ = VerboseModule\n' '\n' - 'Note: Defining module "__getattr__" and setting module ' - '"__class__"\n' - ' only affect lookups made using the attribute access ' - 'syntax –\n' - ' directly accessing the module globals (whether by code ' - 'within the\n' - ' module, or via a reference to the module’s globals ' - 'dictionary) is\n' - ' unaffected.\n' + 'Note:\n' + '\n' + ' Defining module "__getattr__" and setting module ' + '"__class__" only\n' + ' affect lookups made using the attribute access syntax ' + '– directly\n' + ' accessing the module globals (whether by code within ' + 'the module, or\n' + ' via a reference to the module’s globals dictionary) is ' + 'unaffected.\n' '\n' 'Changed in version 3.5: "__class__" module attribute is ' 'now writable.\n' @@ -867,12 +889,14 @@ 'created. The\n' ' descriptor has been assigned to *name*.\n' '\n' - ' Note: "__set_name__()" is only called implicitly as ' - 'part of the\n' - ' "type" constructor, so it will need to be called ' - 'explicitly with\n' - ' the appropriate parameters when a descriptor is ' - 'added to a class\n' + ' Note:\n' + '\n' + ' "__set_name__()" is only called implicitly as part ' + 'of the "type"\n' + ' constructor, so it will need to be called ' + 'explicitly with the\n' + ' appropriate parameters when a descriptor is added ' + 'to a class\n' ' after initial creation:\n' '\n' ' class A:\n' @@ -1032,10 +1056,9 @@ '--------------------------\n' '\n' '* When inheriting from a class without *__slots__*, the ' - '*__dict__*\n' - ' and *__weakref__* attribute of the instances will ' - 'always be\n' - ' accessible.\n' + '*__dict__* and\n' + ' *__weakref__* attribute of the instances will always ' + 'be accessible.\n' '\n' '* Without a *__dict__* variable, instances cannot be ' 'assigned new\n' @@ -1050,14 +1073,12 @@ ' declaration.\n' '\n' '* Without a *__weakref__* variable for each instance, ' - 'classes\n' - ' defining *__slots__* do not support weak references to ' - 'its\n' - ' instances. If weak reference support is needed, then ' - 'add\n' - ' "\'__weakref__\'" to the sequence of strings in the ' - '*__slots__*\n' - ' declaration.\n' + 'classes defining\n' + ' *__slots__* do not support weak references to its ' + 'instances. If weak\n' + ' reference support is needed, then add ' + '"\'__weakref__\'" to the\n' + ' sequence of strings in the *__slots__* declaration.\n' '\n' '* *__slots__* are implemented at the class level by ' 'creating\n' @@ -1070,24 +1091,23 @@ ' attribute would overwrite the descriptor assignment.\n' '\n' '* The action of a *__slots__* declaration is not limited ' - 'to the\n' - ' class where it is defined. *__slots__* declared in ' - 'parents are\n' - ' available in child classes. However, child subclasses ' - 'will get a\n' - ' *__dict__* and *__weakref__* unless they also define ' - '*__slots__*\n' - ' (which should only contain names of any *additional* ' - 'slots).\n' + 'to the class\n' + ' where it is defined. *__slots__* declared in parents ' + 'are available\n' + ' in child classes. However, child subclasses will get a ' + '*__dict__*\n' + ' and *__weakref__* unless they also define *__slots__* ' + '(which should\n' + ' only contain names of any *additional* slots).\n' '\n' '* If a class defines a slot also defined in a base ' - 'class, the\n' - ' instance variable defined by the base class slot is ' - 'inaccessible\n' - ' (except by retrieving its descriptor directly from the ' - 'base class).\n' - ' This renders the meaning of the program undefined. In ' - 'the future, a\n' + 'class, the instance\n' + ' variable defined by the base class slot is ' + 'inaccessible (except by\n' + ' retrieving its descriptor directly from the base ' + 'class). This\n' + ' renders the meaning of the program undefined. In the ' + 'future, a\n' ' check may be added to prevent this.\n' '\n' '* Nonempty *__slots__* does not work for classes derived ' @@ -1096,9 +1116,9 @@ '"bytes" and "tuple".\n' '\n' '* Any non-string iterable may be assigned to ' - '*__slots__*. Mappings\n' - ' may also be used; however, in the future, special ' - 'meaning may be\n' + '*__slots__*. Mappings may\n' + ' also be used; however, in the future, special meaning ' + 'may be\n' ' assigned to the values corresponding to each key.\n' '\n' '* *__class__* assignment works only if both classes have ' @@ -1114,9 +1134,9 @@ ' raise "TypeError".\n' '\n' '* If an iterator is used for *__slots__* then a ' - 'descriptor is\n' - ' created for each of the iterator’s values. However, ' - 'the *__slots__*\n' + 'descriptor is created\n' + ' for each of the iterator’s values. However, the ' + '*__slots__*\n' ' attribute will be an empty iterator.\n', 'attribute-references': 'Attribute references\n' '********************\n' @@ -1458,8 +1478,8 @@ '\n' ' Called when the instance is “called” as a function; if ' 'this method\n' - ' is defined, "x(arg1, arg2, ...)" is a shorthand for\n' - ' "x.__call__(arg1, arg2, ...)".\n', + ' is defined, "x(arg1, arg2, ...)" roughly translates to\n' + ' "type(x).__call__(x, arg1, ...)".\n', 'calls': 'Calls\n' '*****\n' '\n' @@ -1877,10 +1897,10 @@ ' != x" is true. This behavior is compliant with IEEE 754.\n' '\n' '* "None" and "NotImplemented" are singletons. **PEP 8** ' - 'advises\n' - ' that comparisons for singletons should always be done with ' - '"is" or\n' - ' "is not", never the equality operators.\n' + 'advises that\n' + ' comparisons for singletons should always be done with "is" ' + 'or "is\n' + ' not", never the equality operators.\n' '\n' '* Binary sequences (instances of "bytes" or "bytearray") can ' 'be\n' @@ -1896,15 +1916,15 @@ '\n' ' Strings and binary sequences cannot be directly compared.\n' '\n' - '* Sequences (instances of "tuple", "list", or "range") can ' - 'be\n' - ' compared only within each of their types, with the ' - 'restriction that\n' - ' ranges do not support order comparison. Equality ' - 'comparison across\n' - ' these types results in inequality, and ordering comparison ' - 'across\n' - ' these types raises "TypeError".\n' + '* Sequences (instances of "tuple", "list", or "range") can be ' + 'compared\n' + ' only within each of their types, with the restriction that ' + 'ranges do\n' + ' not support order comparison. Equality comparison across ' + 'these\n' + ' types results in inequality, and ordering comparison across ' + 'these\n' + ' types raises "TypeError".\n' '\n' ' Sequences compare lexicographically using comparison of\n' ' corresponding elements. The built-in containers typically ' @@ -1928,8 +1948,8 @@ ' false because the type is not the same).\n' '\n' ' * Collections that support order comparison are ordered the ' - 'same\n' - ' as their first unequal elements (for example, "[1,2,x] <= ' + 'same as\n' + ' their first unequal elements (for example, "[1,2,x] <= ' '[1,2,y]"\n' ' has the same value as "x <= y"). If a corresponding ' 'element does\n' @@ -1947,8 +1967,8 @@ '"TypeError".\n' '\n' '* Sets (instances of "set" or "frozenset") can be compared ' - 'within\n' - ' and across their types.\n' + 'within and\n' + ' across their types.\n' '\n' ' They define order comparison operators to mean subset and ' 'superset\n' @@ -1967,8 +1987,8 @@ ' Comparison of sets enforces reflexivity of its elements.\n' '\n' '* Most other built-in types have no comparison methods ' - 'implemented,\n' - ' so they inherit the default comparison behavior.\n' + 'implemented, so\n' + ' they inherit the default comparison behavior.\n' '\n' 'User-defined classes that customize their comparison behavior ' 'should\n' @@ -2017,10 +2037,10 @@ ' "total_ordering()" decorator.\n' '\n' '* The "hash()" result should be consistent with equality. ' - 'Objects\n' - ' that are equal should either have the same hash value, or ' - 'be marked\n' - ' as unhashable.\n' + 'Objects that\n' + ' are equal should either have the same hash value, or be ' + 'marked as\n' + ' unhashable.\n' '\n' 'Python does not enforce these consistency rules. In fact, ' 'the\n' @@ -2294,10 +2314,11 @@ ':= a to b do"; e.g., "list(range(3))" returns the list "[0, 1, ' '2]".\n' '\n' - 'Note: There is a subtlety when the sequence is being modified by ' - 'the\n' - ' loop (this can only occur for mutable sequences, e.g. lists). ' - 'An\n' + 'Note:\n' + '\n' + ' There is a subtlety when the sequence is being modified by the ' + 'loop\n' + ' (this can only occur for mutable sequences, e.g. lists). An\n' ' internal counter is used to keep track of which item is used ' 'next,\n' ' and this is incremented on each iteration. When this counter ' @@ -2355,8 +2376,9 @@ 'compatible\n' 'with an exception if it is the class or a base class of the ' 'exception\n' - 'object or a tuple containing an item compatible with the ' - 'exception.\n' + 'object, or a tuple containing an item that is the class or a ' + 'base\n' + 'class of the exception object.\n' '\n' 'If no except clause matches the exception, the search for an ' 'exception\n' @@ -2520,8 +2542,8 @@ 'follows:\n' '\n' '1. The context expression (the expression given in the ' - '"with_item")\n' - ' is evaluated to obtain a context manager.\n' + '"with_item") is\n' + ' evaluated to obtain a context manager.\n' '\n' '2. The context manager’s "__enter__()" is loaded for later use.\n' '\n' @@ -2529,13 +2551,15 @@ '\n' '4. The context manager’s "__enter__()" method is invoked.\n' '\n' - '5. If a target was included in the "with" statement, the return\n' - ' value from "__enter__()" is assigned to it.\n' + '5. If a target was included in the "with" statement, the return ' + 'value\n' + ' from "__enter__()" is assigned to it.\n' + '\n' + ' Note:\n' '\n' - ' Note: The "with" statement guarantees that if the ' - '"__enter__()"\n' - ' method returns without an error, then "__exit__()" will ' - 'always be\n' + ' The "with" statement guarantees that if the "__enter__()" ' + 'method\n' + ' returns without an error, then "__exit__()" will always be\n' ' called. Thus, if an error occurs during the assignment to ' 'the\n' ' target list, it will be treated the same as an error ' @@ -3040,14 +3064,17 @@ '\n' '-[ Footnotes ]-\n' '\n' - '[1] The exception is propagated to the invocation stack unless\n' - ' there is a "finally" clause which happens to raise another\n' - ' exception. That new exception causes the old one to be ' - 'lost.\n' - '\n' - '[2] A string literal appearing as the first statement in the\n' - ' function body is transformed into the function’s "__doc__"\n' - ' attribute and therefore the function’s *docstring*.\n' + '[1] The exception is propagated to the invocation stack unless ' + 'there\n' + ' is a "finally" clause which happens to raise another ' + 'exception.\n' + ' That new exception causes the old one to be lost.\n' + '\n' + '[2] A string literal appearing as the first statement in the ' + 'function\n' + ' body is transformed into the function’s "__doc__" attribute ' + 'and\n' + ' therefore the function’s *docstring*.\n' '\n' '[3] A string literal appearing as the first statement in the ' 'class\n' @@ -3146,8 +3173,8 @@ ' complex;\n' '\n' '* otherwise, if either argument is a floating point number, ' - 'the\n' - ' other is converted to floating point;\n' + 'the other\n' + ' is converted to floating point;\n' '\n' '* otherwise, both must be integers and no conversion is ' 'necessary.\n' @@ -3257,7 +3284,9 @@ 'for\n' ' objects that still exist when the interpreter exits.\n' '\n' - ' Note: "del x" doesn’t directly call "x.__del__()" — the ' + ' Note:\n' + '\n' + ' "del x" doesn’t directly call "x.__del__()" — the ' 'former\n' ' decrements the reference count for "x" by one, and the ' 'latter is\n' @@ -3281,13 +3310,15 @@ '\n' ' See also: Documentation for the "gc" module.\n' '\n' - ' Warning: Due to the precarious circumstances under ' - 'which\n' - ' "__del__()" methods are invoked, exceptions that occur ' - 'during\n' - ' their execution are ignored, and a warning is printed ' - 'to\n' - ' "sys.stderr" instead. In particular:\n' + ' Warning:\n' + '\n' + ' Due to the precarious circumstances under which ' + '"__del__()"\n' + ' methods are invoked, exceptions that occur during ' + 'their execution\n' + ' are ignored, and a warning is printed to "sys.stderr" ' + 'instead.\n' + ' In particular:\n' '\n' ' * "__del__()" can be invoked when arbitrary code is ' 'being\n' @@ -3300,22 +3331,20 @@ ' that gets interrupted to execute "__del__()".\n' '\n' ' * "__del__()" can be executed during interpreter ' - 'shutdown. As\n' - ' a consequence, the global variables it needs to ' - 'access\n' - ' (including other modules) may already have been ' - 'deleted or set\n' - ' to "None". Python guarantees that globals whose name ' - 'begins\n' - ' with a single underscore are deleted from their ' - 'module before\n' - ' other globals are deleted; if no other references to ' - 'such\n' - ' globals exist, this may help in assuring that ' - 'imported modules\n' - ' are still available at the time when the "__del__()" ' - 'method is\n' - ' called.\n' + 'shutdown. As a\n' + ' consequence, the global variables it needs to access ' + '(including\n' + ' other modules) may already have been deleted or set ' + 'to "None".\n' + ' Python guarantees that globals whose name begins ' + 'with a single\n' + ' underscore are deleted from their module before ' + 'other globals\n' + ' are deleted; if no other references to such globals ' + 'exist, this\n' + ' may help in assuring that imported modules are still ' + 'available\n' + ' at the time when the "__del__()" method is called.\n' '\n' 'object.__repr__(self)\n' '\n' @@ -3434,16 +3463,21 @@ ' on the value to determine if the result is true or ' 'false.\n' '\n' - ' By default, "__ne__()" delegates to "__eq__()" and ' - 'inverts the\n' - ' result unless it is "NotImplemented". There are no ' - 'other implied\n' - ' relationships among the comparison operators, for ' - 'example, the\n' - ' truth of "(x>> 'Py' in 'Python'\n" ' True\n' @@ -10015,8 +10157,9 @@ ' formatting options that can be specified in format ' 'strings.\n' '\n' - ' Note: When formatting a number ("int", "float", ' - '"complex",\n' + ' Note:\n' + '\n' + ' When formatting a number ("int", "float", "complex",\n' ' "decimal.Decimal" and subclasses) with the "n" type ' '(ex:\n' ' "\'{:n}\'.format(1234)"), the function temporarily ' @@ -10863,17 +11006,20 @@ '\n' '2. Unlike in Standard C, exactly two hex digits are required.\n' '\n' - '3. In a bytes literal, hexadecimal and octal escapes denote the\n' - ' byte with the given value. In a string literal, these escapes\n' - ' denote a Unicode character with the given value.\n' + '3. In a bytes literal, hexadecimal and octal escapes denote the ' + 'byte\n' + ' with the given value. In a string literal, these escapes ' + 'denote a\n' + ' Unicode character with the given value.\n' '\n' '4. Changed in version 3.3: Support for name aliases [1] has been\n' ' added.\n' '\n' '5. Exactly four hex digits are required.\n' '\n' - '6. Any Unicode character can be encoded this way. Exactly eight\n' - ' hex digits are required.\n' + '6. Any Unicode character can be encoded this way. Exactly eight ' + 'hex\n' + ' digits are required.\n' '\n' 'Unlike Standard C, all unrecognized escape sequences are left in ' 'the\n' @@ -11026,7 +11172,8 @@ 'object is “compatible” with the exception. An object is compatible\n' 'with an exception if it is the class or a base class of the ' 'exception\n' - 'object or a tuple containing an item compatible with the exception.\n' + 'object, or a tuple containing an item that is the class or a base\n' + 'class of the exception object.\n' '\n' 'If no except clause matches the exception, the search for an ' 'exception\n' @@ -11207,6 +11354,27 @@ 'representation\n' ' in computers.\n' '\n' + ' The string representations of the numeric classes, computed by\n' + ' "__repr__()" and "__str__()", have the following properties:\n' + '\n' + ' * They are valid numeric literals which, when passed to their ' + 'class\n' + ' constructor, produce an object having the value of the ' + 'original\n' + ' numeric.\n' + '\n' + ' * The representation is in base 10, when possible.\n' + '\n' + ' * Leading zeros, possibly excepting a single zero before a ' + 'decimal\n' + ' point, are not shown.\n' + '\n' + ' * Trailing zeros, possibly excepting a single zero after a ' + 'decimal\n' + ' point, are not shown.\n' + '\n' + ' * A sign is shown only when the number is negative.\n' + '\n' ' Python distinguishes between integers, floating point numbers, ' 'and\n' ' complex numbers:\n' @@ -12258,6 +12426,21 @@ 'positional\n' ' argument and a possibly empty set of keyword arguments.\n' '\n' + ' Dictionaries can be created by several means:\n' + '\n' + ' * Use a comma-separated list of "key: value" pairs within ' + 'braces:\n' + ' "{\'jack\': 4098, \'sjoerd\': 4127}" or "{4098: ' + "'jack', 4127:\n" + ' \'sjoerd\'}"\n' + '\n' + ' * Use a dict comprehension: "{}", "{x: x ** 2 for x in ' + 'range(10)}"\n' + '\n' + ' * Use the type constructor: "dict()", "dict([(\'foo\', ' + "100), ('bar',\n" + ' 200)])", "dict(foo=100, bar=200)"\n' + '\n' ' If no positional argument is given, an empty dictionary ' 'is created.\n' ' If a positional argument is given and it is a mapping ' @@ -12540,9 +12723,11 @@ '\n' ' Changed in version 3.8: Dictionaries are now reversible.\n' '\n' - 'See also: "types.MappingProxyType" can be used to create a ' - 'read-only\n' - ' view of a "dict".\n' + 'See also:\n' + '\n' + ' "types.MappingProxyType" can be used to create a read-only ' + 'view of a\n' + ' "dict".\n' '\n' '\n' 'Dictionary view objects\n' @@ -12926,13 +13111,14 @@ '"None", it\n' ' is treated like "1".\n' '\n' - '6. Concatenating immutable sequences always results in a new\n' - ' object. This means that building up a sequence by repeated\n' - ' concatenation will have a quadratic runtime cost in the ' - 'total\n' - ' sequence length. To get a linear runtime cost, you must ' - 'switch to\n' - ' one of the alternatives below:\n' + '6. Concatenating immutable sequences always results in a new ' + 'object.\n' + ' This means that building up a sequence by repeated ' + 'concatenation\n' + ' will have a quadratic runtime cost in the total sequence ' + 'length.\n' + ' To get a linear runtime cost, you must switch to one of the\n' + ' alternatives below:\n' '\n' ' * if concatenating "str" objects, you can build a list and ' 'use\n' @@ -12950,24 +13136,25 @@ ' * for other types, investigate the relevant class ' 'documentation\n' '\n' - '7. Some sequence types (such as "range") only support item\n' - ' sequences that follow specific patterns, and hence don’t ' - 'support\n' - ' sequence concatenation or repetition.\n' - '\n' - '8. "index" raises "ValueError" when *x* is not found in *s*. ' - 'Not\n' - ' all implementations support passing the additional arguments ' - '*i*\n' - ' and *j*. These arguments allow efficient searching of ' - 'subsections\n' - ' of the sequence. Passing the extra arguments is roughly ' - 'equivalent\n' - ' to using "s[i:j].index(x)", only without copying any data and ' - 'with\n' - ' the returned index being relative to the start of the ' + '7. Some sequence types (such as "range") only support item ' + 'sequences\n' + ' that follow specific patterns, and hence don’t support ' 'sequence\n' - ' rather than the start of the slice.\n' + ' concatenation or repetition.\n' + '\n' + '8. "index" raises "ValueError" when *x* is not found in *s*. Not ' + 'all\n' + ' implementations support passing the additional arguments *i* ' + 'and\n' + ' *j*. These arguments allow efficient searching of subsections ' + 'of\n' + ' the sequence. Passing the extra arguments is roughly ' + 'equivalent to\n' + ' using "s[i:j].index(x)", only without copying any data and ' + 'with the\n' + ' returned index being relative to the start of the sequence ' + 'rather\n' + ' than the start of the slice.\n' '\n' '\n' 'Immutable Sequence Types\n' @@ -13095,17 +13282,17 @@ '1. *t* must have the same length as the slice it is replacing.\n' '\n' '2. The optional argument *i* defaults to "-1", so that by ' - 'default\n' - ' the last item is removed and returned.\n' + 'default the\n' + ' last item is removed and returned.\n' '\n' '3. "remove()" raises "ValueError" when *x* is not found in *s*.\n' '\n' - '4. The "reverse()" method modifies the sequence in place for\n' - ' economy of space when reversing a large sequence. To remind ' - 'users\n' - ' that it operates by side effect, it does not return the ' - 'reversed\n' - ' sequence.\n' + '4. The "reverse()" method modifies the sequence in place for ' + 'economy\n' + ' of space when reversing a large sequence. To remind users ' + 'that it\n' + ' operates by side effect, it does not return the reversed ' + 'sequence.\n' '\n' '5. "clear()" and "copy()" are included for consistency with the\n' ' interfaces of mutable containers that don’t support slicing\n' @@ -13142,9 +13329,9 @@ ' * Using a pair of square brackets to denote the empty list: ' '"[]"\n' '\n' - ' * Using square brackets, separating items with commas: ' - '"[a]",\n' - ' "[a, b, c]"\n' + ' * Using square brackets, separating items with commas: "[a]", ' + '"[a,\n' + ' b, c]"\n' '\n' ' * Using a list comprehension: "[x for x in iterable]"\n' '\n' @@ -13447,9 +13634,9 @@ '\n' 'See also:\n' '\n' - ' * The linspace recipe shows how to implement a lazy version ' - 'of\n' - ' range suitable for floating point applications.\n', + ' * The linspace recipe shows how to implement a lazy version of ' + 'range\n' + ' suitable for floating point applications.\n', 'typesseq-mutable': 'Mutable Sequence Types\n' '**********************\n' '\n' @@ -13560,19 +13747,18 @@ 'replacing.\n' '\n' '2. The optional argument *i* defaults to "-1", so that ' - 'by default\n' - ' the last item is removed and returned.\n' + 'by default the\n' + ' last item is removed and returned.\n' '\n' '3. "remove()" raises "ValueError" when *x* is not found ' 'in *s*.\n' '\n' '4. The "reverse()" method modifies the sequence in place ' - 'for\n' - ' economy of space when reversing a large sequence. To ' - 'remind users\n' - ' that it operates by side effect, it does not return ' - 'the reversed\n' - ' sequence.\n' + 'for economy\n' + ' of space when reversing a large sequence. To remind ' + 'users that it\n' + ' operates by side effect, it does not return the ' + 'reversed sequence.\n' '\n' '5. "clear()" and "copy()" are included for consistency ' 'with the\n' @@ -13655,8 +13841,9 @@ 'The execution of the "with" statement with one “item” proceeds as\n' 'follows:\n' '\n' - '1. The context expression (the expression given in the "with_item")\n' - ' is evaluated to obtain a context manager.\n' + '1. The context expression (the expression given in the "with_item") ' + 'is\n' + ' evaluated to obtain a context manager.\n' '\n' '2. The context manager’s "__enter__()" is loaded for later use.\n' '\n' @@ -13664,12 +13851,15 @@ '\n' '4. The context manager’s "__enter__()" method is invoked.\n' '\n' - '5. If a target was included in the "with" statement, the return\n' - ' value from "__enter__()" is assigned to it.\n' + '5. If a target was included in the "with" statement, the return ' + 'value\n' + ' from "__enter__()" is assigned to it.\n' + '\n' + ' Note:\n' '\n' - ' Note: The "with" statement guarantees that if the "__enter__()"\n' - ' method returns without an error, then "__exit__()" will always ' - 'be\n' + ' The "with" statement guarantees that if the "__enter__()" ' + 'method\n' + ' returns without an error, then "__exit__()" will always be\n' ' called. Thus, if an error occurs during the assignment to the\n' ' target list, it will be treated the same as an error occurring\n' ' within the suite would be. See step 6 below.\n' diff -Nru python3.8-3.8.6/Lib/shutil.py python3.8-3.8.7/Lib/shutil.py --- python3.8-3.8.6/Lib/shutil.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/shutil.py 2020-12-21 16:25:24.000000000 +0000 @@ -53,6 +53,9 @@ _USE_CP_SENDFILE = hasattr(os, "sendfile") and sys.platform.startswith("linux") _HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile") # macOS +# CMD defaults in Windows 10 +_WIN_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC" + __all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", "copytree", "move", "rmtree", "Error", "SpecialFileError", "ExecError", "make_archive", "get_archive_formats", @@ -708,7 +711,7 @@ try: fd = os.open(path, os.O_RDONLY) except Exception: - onerror(os.lstat, path, sys.exc_info()) + onerror(os.open, path, sys.exc_info()) return try: if os.path.samestat(orig_st, os.fstat(fd)): @@ -1400,7 +1403,9 @@ path.insert(0, curdir) # PATHEXT is necessary to check on Windows. - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + pathext_source = os.getenv("PATHEXT") or _WIN_DEFAULT_PATHEXT + pathext = [ext for ext in pathext_source.split(os.pathsep) if ext] + if use_bytes: pathext = [os.fsencode(ext) for ext in pathext] # See if the given file matches any of the expected path extensions. diff -Nru python3.8-3.8.6/Lib/smtplib.py python3.8-3.8.7/Lib/smtplib.py --- python3.8-3.8.6/Lib/smtplib.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/smtplib.py 2020-12-21 16:25:24.000000000 +0000 @@ -333,8 +333,6 @@ raise OSError("nonnumeric port") if not port: port = self.default_port - if self.debuglevel > 0: - self._print_debug('connect:', (host, port)) sys.audit("smtplib.connect", self, host, port) self.sock = self._get_socket(host, port, self.timeout) self.file = None diff -Nru python3.8-3.8.6/Lib/sqlite3/test/dbapi.py python3.8-3.8.7/Lib/sqlite3/test/dbapi.py --- python3.8-3.8.6/Lib/sqlite3/test/dbapi.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/sqlite3/test/dbapi.py 2020-12-21 16:25:24.000000000 +0000 @@ -276,7 +276,7 @@ self.assertEqual(row[0], "foo") def CheckExecuteParamSequence(self): - class L(object): + class L: def __len__(self): return 1 def __getitem__(self, x): @@ -288,6 +288,18 @@ row = self.cu.fetchone() self.assertEqual(row[0], "foo") + def CheckExecuteParamSequenceBadLen(self): + # Issue41662: Error in __len__() was overridden with ProgrammingError. + class L: + def __len__(self): + 1/0 + def __getitem__(slf, x): + raise AssertionError + + self.cu.execute("insert into test(name) values ('foo')") + with self.assertRaises(ZeroDivisionError): + self.cu.execute("select name from test where name=?", L()) + def CheckExecuteDictMapping(self): self.cu.execute("insert into test(name) values ('foo')") self.cu.execute("select name from test where name=:name", {"name": "foo"}) diff -Nru python3.8-3.8.6/Lib/sqlite3/test/regression.py python3.8-3.8.7/Lib/sqlite3/test/regression.py --- python3.8-3.8.6/Lib/sqlite3/test/regression.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/sqlite3/test/regression.py 2020-12-21 16:25:24.000000000 +0000 @@ -133,6 +133,19 @@ con.execute("insert into foo(bar) values (5)") con.execute(SELECT) + def CheckBindMutatingList(self): + # Issue41662: Crash when mutate a list of parameters during iteration. + class X: + def __conform__(self, protocol): + parameters.clear() + return "..." + parameters = [X(), 0] + con = sqlite.connect(":memory:",detect_types=sqlite.PARSE_DECLTYPES) + con.execute("create table foo(bar X, baz integer)") + # Should not crash + with self.assertRaises(IndexError): + con.execute("insert into foo(bar, baz) values (?, ?)", parameters) + def CheckErrorMsgDecodeError(self): # When porting the module to Python 3.0, the error message about # decoding errors disappeared. This verifies they're back again. diff -Nru python3.8-3.8.6/Lib/symtable.py python3.8-3.8.7/Lib/symtable.py --- python3.8-3.8.6/Lib/symtable.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/symtable.py 2020-12-21 16:25:24.000000000 +0000 @@ -34,7 +34,7 @@ _newSymbolTable = SymbolTableFactory() -class SymbolTable(object): +class SymbolTable: def __init__(self, raw_table, filename): self._table = raw_table @@ -47,7 +47,7 @@ else: kind = "%s " % self.__class__.__name__ - if self._table.name == "global": + if self._table.name == "top": return "<{0}SymbolTable for module {1}>".format(kind, self._filename) else: return "<{0}SymbolTable for {1} in {2}>".format(kind, @@ -94,7 +94,9 @@ if sym is None: flags = self._table.symbols[name] namespaces = self.__check_children(name) - sym = self._symbols[name] = Symbol(name, flags, namespaces) + module_scope = (self._table.name == "top") + sym = self._symbols[name] = Symbol(name, flags, namespaces, + module_scope=module_scope) return sym def get_symbols(self): @@ -167,13 +169,14 @@ return self.__methods -class Symbol(object): +class Symbol: - def __init__(self, name, flags, namespaces=None): + def __init__(self, name, flags, namespaces=None, *, module_scope=False): self.__name = name self.__flags = flags self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope() self.__namespaces = namespaces or () + self.__module_scope = module_scope def __repr__(self): return "".format(self.__name) @@ -188,7 +191,10 @@ return bool(self.__flags & DEF_PARAM) def is_global(self): - return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)) + """Return *True* if the sysmbol is global. + """ + return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT) + or (self.__module_scope and self.__flags & DEF_BOUND)) def is_nonlocal(self): return bool(self.__flags & DEF_NONLOCAL) @@ -197,7 +203,10 @@ return bool(self.__scope == GLOBAL_EXPLICIT) def is_local(self): - return bool(self.__scope in (LOCAL, CELL)) + """Return *True* if the symbol is local. + """ + return bool(self.__scope in (LOCAL, CELL) + or (self.__module_scope and self.__flags & DEF_BOUND)) def is_annotated(self): return bool(self.__flags & DEF_ANNOT) diff -Nru python3.8-3.8.6/Lib/sysconfig.py python3.8-3.8.7/Lib/sysconfig.py --- python3.8-3.8.6/Lib/sysconfig.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/sysconfig.py 2020-12-21 16:25:24.000000000 +0000 @@ -426,10 +426,11 @@ def _init_non_posix(vars): """Initialize the module as appropriate for NT""" # set basic install directories + import _imp vars['LIBDEST'] = get_path('stdlib') vars['BINLIBDEST'] = get_path('platstdlib') vars['INCLUDEPY'] = get_path('include') - vars['EXT_SUFFIX'] = '.pyd' + vars['EXT_SUFFIX'] = _imp.extension_suffixes()[0] vars['EXE'] = '.exe' vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) diff -Nru python3.8-3.8.6/Lib/tarfile.py python3.8-3.8.7/Lib/tarfile.py --- python3.8-3.8.6/Lib/tarfile.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tarfile.py 2020-12-21 16:25:24.000000000 +0000 @@ -420,6 +420,8 @@ self.__write(b"\037\213\010\010" + timestamp + b"\002\377") if self.name.endswith(".gz"): self.name = self.name[:-3] + # Honor "directory components removed" from RFC1952 + self.name = os.path.basename(self.name) # RFC1952 says we must use ISO-8859-1 for the FNAME field. self.__write(self.name.encode("iso-8859-1", "replace") + NUL) @@ -2226,6 +2228,9 @@ try: # For systems that support symbolic and hard links. if tarinfo.issym(): + if os.path.lexists(targetpath): + # Avoid FileExistsError on following os.symlink. + os.unlink(targetpath) os.symlink(tarinfo.linkname, targetpath) else: # See extract(). diff -Nru python3.8-3.8.6/Lib/test/datetimetester.py python3.8-3.8.7/Lib/test/datetimetester.py --- python3.8-3.8.6/Lib/test/datetimetester.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/datetimetester.py 2020-12-21 16:25:24.000000000 +0000 @@ -1750,6 +1750,7 @@ green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassDate)) def test_backdoor_resistance(self): # For fast unpickling, the constructor accepts a pickle byte string. @@ -2277,6 +2278,7 @@ green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassDatetime)) def test_compat_unpickle(self): tests = [ @@ -3326,6 +3328,7 @@ green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertTrue(isinstance(derived, SubclassTime)) def test_compat_unpickle(self): tests = [ diff -Nru python3.8-3.8.6/Lib/test/inspect_fodder.py python3.8-3.8.7/Lib/test/inspect_fodder.py --- python3.8-3.8.6/Lib/test/inspect_fodder.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/inspect_fodder.py 2020-12-21 16:25:24.000000000 +0000 @@ -91,3 +91,25 @@ custom_method = Callable().as_method_of(42) del Callable + +# line 95 +class WhichComments: + # line 97 + # before f + def f(self): + # line 100 + # start f + return 1 + # line 103 + # end f + # line 105 + # after f + + # before asyncf - line 108 + async def asyncf(self): + # start asyncf + return 2 + # end asyncf + # after asyncf - line 113 + # end of WhichComments - line 114 + # after WhichComments - line 115 diff -Nru python3.8-3.8.6/Lib/test/multibytecodec_support.py python3.8-3.8.7/Lib/test/multibytecodec_support.py --- python3.8-3.8.6/Lib/test/multibytecodec_support.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/multibytecodec_support.py 2020-12-21 16:25:24.000000000 +0000 @@ -305,29 +305,23 @@ self._test_mapping_file_plain() def _test_mapping_file_plain(self): - unichrs = lambda s: ''.join(map(chr, map(eval, s.split('+')))) + def unichrs(s): + return ''.join(chr(int(x, 16)) for x in s.split('+')) + urt_wa = {} with self.open_mapping_file() as f: for line in f: if not line: break - data = line.split('#')[0].strip().split() + data = line.split('#')[0].split() if len(data) != 2: continue - csetval = eval(data[0]) - if csetval <= 0x7F: - csetch = bytes([csetval & 0xff]) - elif csetval >= 0x1000000: - csetch = bytes([(csetval >> 24), ((csetval >> 16) & 0xff), - ((csetval >> 8) & 0xff), (csetval & 0xff)]) - elif csetval >= 0x10000: - csetch = bytes([(csetval >> 16), ((csetval >> 8) & 0xff), - (csetval & 0xff)]) - elif csetval >= 0x100: - csetch = bytes([(csetval >> 8), (csetval & 0xff)]) - else: + if data[0][:2] != '0x': + self.fail(f"Invalid line: {line!r}") + csetch = bytes.fromhex(data[0][2:]) + if len(csetch) == 1 and 0x80 <= csetch[0]: continue unich = unichrs(data[1]) diff -Nru python3.8-3.8.6/Lib/test/test_asyncio/test_asyncio_waitfor.py python3.8-3.8.7/Lib/test/test_asyncio/test_asyncio_waitfor.py --- python3.8-3.8.6/Lib/test/test_asyncio/test_asyncio_waitfor.py 1970-01-01 00:00:00.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_asyncio/test_asyncio_waitfor.py 2020-12-21 16:25:24.000000000 +0000 @@ -0,0 +1,61 @@ +import asyncio +import unittest +import time + +def tearDownModule(): + asyncio.set_event_loop_policy(None) + + +class SlowTask: + """ Task will run for this defined time, ignoring cancel requests """ + TASK_TIMEOUT = 0.2 + + def __init__(self): + self.exited = False + + async def run(self): + exitat = time.monotonic() + self.TASK_TIMEOUT + + while True: + tosleep = exitat - time.monotonic() + if tosleep <= 0: + break + + try: + await asyncio.sleep(tosleep) + except asyncio.CancelledError: + pass + + self.exited = True + +class AsyncioWaitForTest(unittest.TestCase): + + async def atest_asyncio_wait_for_cancelled(self): + t = SlowTask() + + waitfortask = asyncio.create_task(asyncio.wait_for(t.run(), t.TASK_TIMEOUT * 2)) + await asyncio.sleep(0) + waitfortask.cancel() + await asyncio.wait({waitfortask}) + + self.assertTrue(t.exited) + + def test_asyncio_wait_for_cancelled(self): + asyncio.run(self.atest_asyncio_wait_for_cancelled()) + + async def atest_asyncio_wait_for_timeout(self): + t = SlowTask() + + try: + await asyncio.wait_for(t.run(), t.TASK_TIMEOUT / 2) + except asyncio.TimeoutError: + pass + + self.assertTrue(t.exited) + + def test_asyncio_wait_for_timeout(self): + asyncio.run(self.atest_asyncio_wait_for_timeout()) + + +if __name__ == '__main__': + unittest.main() diff -Nru python3.8-3.8.6/Lib/test/test_asyncio/test_events.py python3.8-3.8.7/Lib/test/test_asyncio/test_events.py --- python3.8-3.8.6/Lib/test/test_asyncio/test_events.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_asyncio/test_events.py 2020-12-21 16:25:24.000000000 +0000 @@ -291,11 +291,8 @@ self.loop.stop() self.loop.call_later(0.1, callback, 'hello world') - t0 = time.monotonic() self.loop.run_forever() - t1 = time.monotonic() self.assertEqual(results, ['hello world']) - self.assertTrue(0.08 <= t1-t0 <= 0.8, t1-t0) def test_call_soon(self): results = [] @@ -1071,6 +1068,7 @@ ssl=sslcontext_client, server_hostname='localhost') client, pr = self.loop.run_until_complete(f_c) + self.loop.run_until_complete(proto.connected) # close connection proto.transport.close() @@ -1096,6 +1094,7 @@ ssl=sslcontext_client, server_hostname='localhost') client, pr = self.loop.run_until_complete(f_c) + self.loop.run_until_complete(proto.connected) # extra info is available self.check_ssl_extra_info(client, peername=(host, port), diff -Nru python3.8-3.8.6/Lib/test/test_asyncio/test_futures2.py python3.8-3.8.7/Lib/test/test_asyncio/test_futures2.py --- python3.8-3.8.6/Lib/test/test_asyncio/test_futures2.py 1970-01-01 00:00:00.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_asyncio/test_futures2.py 2020-12-21 16:25:24.000000000 +0000 @@ -0,0 +1,18 @@ +# IsolatedAsyncioTestCase based tests +import asyncio +import unittest + + +class FutureTests(unittest.IsolatedAsyncioTestCase): + async def test_recursive_repr_for_pending_tasks(self): + # The call crashes if the guard for recursive call + # in base_futures:_future_repr_info is absent + # See Also: https://bugs.python.org/issue42183 + + async def func(): + return asyncio.all_tasks() + + # The repr() call should not raise RecursiveError at first. + # The check for returned string is not very reliable but + # exact comparison for the whole string is even weaker. + self.assertIn('...', repr(await asyncio.wait_for(func(), timeout=10))) diff -Nru python3.8-3.8.6/Lib/test/test_asyncio/test_streams.py python3.8-3.8.7/Lib/test/test_asyncio/test_streams.py --- python3.8-3.8.6/Lib/test/test_asyncio/test_streams.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_asyncio/test_streams.py 2020-12-21 16:25:24.000000000 +0000 @@ -452,12 +452,14 @@ def test_readuntil_eof(self): stream = asyncio.StreamReader(loop=self.loop) - stream.feed_data(b'some dataAA') + data = b'some dataAA' + stream.feed_data(data) stream.feed_eof() - with self.assertRaises(asyncio.IncompleteReadError) as cm: + with self.assertRaisesRegex(asyncio.IncompleteReadError, + 'undefined expected bytes') as cm: self.loop.run_until_complete(stream.readuntil(b'AAA')) - self.assertEqual(cm.exception.partial, b'some dataAA') + self.assertEqual(cm.exception.partial, data) self.assertIsNone(cm.exception.expected) self.assertEqual(b'', stream._buffer) diff -Nru python3.8-3.8.6/Lib/test/test_binhex.py python3.8-3.8.7/Lib/test/test_binhex.py --- python3.8-3.8.6/Lib/test/test_binhex.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_binhex.py 2020-12-21 16:25:24.000000000 +0000 @@ -45,6 +45,18 @@ self.assertRaises(binhex.Error, binhex.binhex, self.fname3, self.fname2) + def test_binhex_line_endings(self): + # bpo-29566: Ensure the line endings are those for macOS 9 + with open(self.fname1, 'wb') as f: + f.write(self.DATA) + + binhex.binhex(self.fname1, self.fname2) + + with open(self.fname2, 'rb') as fp: + contents = fp.read() + + self.assertNotIn(b'\n', contents) + def test_main(): support.run_unittest(BinHexTestCase) diff -Nru python3.8-3.8.6/Lib/test/test_builtin.py python3.8-3.8.7/Lib/test/test_builtin.py --- python3.8-3.8.6/Lib/test/test_builtin.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_builtin.py 2020-12-21 16:25:24.000000000 +0000 @@ -6,6 +6,7 @@ import collections import decimal import fractions +import gc import io import locale import os @@ -27,7 +28,7 @@ from operator import neg from test.support import ( EnvironmentVarGuard, TESTFN, check_warnings, swap_attr, unlink, - maybe_get_event_loop_policy) + maybe_get_event_loop_policy, cpython_only) from test.support.script_helper import assert_python_ok from unittest.mock import MagicMock, patch try: @@ -1573,6 +1574,18 @@ self.assertIs(cm.exception, exception) + @cpython_only + def test_zip_result_gc(self): + # bpo-42536: zip's tuple-reuse speed trick breaks the GC's assumptions + # about what can be untracked. Make sure we re-track result tuples + # whenever we reuse them. + it = zip([[]]) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None,). Make sure it's re-tracked when + # it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + def test_format(self): # Test the basic machinery of the format() builtin. Don't test # the specifics of the various formatters diff -Nru python3.8-3.8.6/Lib/test/test_clinic.py python3.8-3.8.7/Lib/test/test_clinic.py --- python3.8-3.8.6/Lib/test/test_clinic.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_clinic.py 2020-12-21 16:25:24.000000000 +0000 @@ -801,17 +801,29 @@ maxDiff = None def test_external(self): + # bpo-42398: Test that the destination file is left unchanged if the + # content does not change. Moreover, check also that the file + # modification time does not change in this case. source = support.findfile('clinic.test') with open(source, 'r', encoding='utf-8') as f: - original = f.read() - with support.temp_dir() as testdir: - testfile = os.path.join(testdir, 'clinic.test.c') + orig_contents = f.read() + + with support.temp_dir() as tmp_dir: + testfile = os.path.join(tmp_dir, 'clinic.test.c') with open(testfile, 'w', encoding='utf-8') as f: - f.write(original) - clinic.parse_file(testfile, force=True) + f.write(orig_contents) + old_mtime_ns = os.stat(testfile).st_mtime_ns + + clinic.parse_file(testfile) + with open(testfile, 'r', encoding='utf-8') as f: - result = f.read() - self.assertEqual(result, original) + new_contents = f.read() + new_mtime_ns = os.stat(testfile).st_mtime_ns + + self.assertEqual(new_contents, orig_contents) + # Don't change the file modification time + # if the content does not change + self.assertEqual(new_mtime_ns, old_mtime_ns) if __name__ == "__main__": diff -Nru python3.8-3.8.6/Lib/test/test_codecs.py python3.8-3.8.7/Lib/test/test_codecs.py --- python3.8-3.8.6/Lib/test/test_codecs.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_codecs.py 2020-12-21 16:25:24.000000000 +0000 @@ -2183,6 +2183,18 @@ ("", len(allbytes)) ) + self.assertRaisesRegex(TypeError, + "character mapping must be in range\\(0x110000\\)", + codecs.charmap_decode, + b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: -2} + ) + + self.assertRaisesRegex(TypeError, + "character mapping must be in range\\(0x110000\\)", + codecs.charmap_decode, + b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: 999999999} + ) + def test_decode_with_int2int_map(self): a = ord('a') b = ord('b') diff -Nru python3.8-3.8.6/Lib/test/test_dict.py python3.8-3.8.7/Lib/test/test_dict.py --- python3.8-3.8.6/Lib/test/test_dict.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_dict.py 2020-12-21 16:25:24.000000000 +0000 @@ -1377,6 +1377,25 @@ d = CustomReversedDict(pairs) self.assertEqual(pairs[::-1], list(dict(d).items())) + @support.cpython_only + def test_dict_items_result_gc(self): + # bpo-42536: dict.items's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = iter({None: []}.items()) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None, None). Make sure it's re-tracked + # when it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_dict_items_result_gc(self): + # Same as test_dict_items_result_gc above, but reversed. + it = reversed({None: []}.items()) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + class CAPITest(unittest.TestCase): diff -Nru python3.8-3.8.6/Lib/test/test_email/test_email.py python3.8-3.8.7/Lib/test/test_email/test_email.py --- python3.8-3.8.6/Lib/test/test_email/test_email.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_email/test_email.py 2020-12-21 16:25:24.000000000 +0000 @@ -311,6 +311,41 @@ g.flatten(msg) self.assertEqual(fullrepr, s.getvalue()) + def test_nonascii_as_string_without_cte(self): + m = textwrap.dedent("""\ + MIME-Version: 1.0 + Content-type: text/plain; charset="iso-8859-1" + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + Föö bär + """) + source = m.encode('iso-8859-1') + expected = textwrap.dedent("""\ + MIME-Version: 1.0 + Content-type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + F=F6=F6 b=E4r + """) + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + + def test_nonascii_as_string_without_content_type_and_cte(self): + m = textwrap.dedent("""\ + MIME-Version: 1.0 + + Test if non-ascii messages with no Content-Type nor + Content-Transfer-Encoding set can be as_string'd: + Föö bär + """) + source = m.encode('iso-8859-1') + expected = source.decode('ascii', 'replace') + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + def test_as_bytes(self): msg = self._msgobj('msg_01.txt') with openfile('msg_01.txt') as fp: diff -Nru python3.8-3.8.6/Lib/test/test_enumerate.py python3.8-3.8.7/Lib/test/test_enumerate.py --- python3.8-3.8.6/Lib/test/test_enumerate.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_enumerate.py 2020-12-21 16:25:24.000000000 +0000 @@ -2,6 +2,7 @@ import operator import sys import pickle +import gc from test import support @@ -134,6 +135,18 @@ self.assertEqual(len(set(map(id, list(enumerate(self.seq))))), len(self.seq)) self.assertEqual(len(set(map(id, enumerate(self.seq)))), min(1,len(self.seq))) + @support.cpython_only + def test_enumerate_result_gc(self): + # bpo-42536: enumerate's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = self.enum([[]]) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None, None). Make sure it's re-tracked + # when it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + class MyEnum(enumerate): pass diff -Nru python3.8-3.8.6/Lib/test/test_enum.py python3.8-3.8.7/Lib/test/test_enum.py --- python3.8-3.8.6/Lib/test/test_enum.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_enum.py 2020-12-21 16:25:24.000000000 +0000 @@ -214,6 +214,18 @@ set(['__class__', '__doc__', '__module__', 'name', 'value', 'invisible']), ) + def test_dir_on_sub_with_behavior_including_instance_dict_on_super(self): + # see issue40084 + class SuperEnum(IntEnum): + def __new__(cls, value, description=""): + obj = int.__new__(cls, value) + obj._value_ = value + obj.description = description + return obj + class SubEnum(SuperEnum): + sample = 5 + self.assertTrue({'description'} <= set(dir(SubEnum.sample))) + def test_enum_in_enum_out(self): Season = self.Season self.assertIs(Season(Season.WINTER), Season.WINTER) @@ -417,7 +429,6 @@ green = 2 blue = 3 - def test_enum_with_value_name(self): class Huh(Enum): name = 1 @@ -569,12 +580,15 @@ class Test1Enum(MyMethodEnum, int, MyStrEnum): One = 1 Two = 2 + self.assertTrue(Test1Enum._member_type_ is int) self.assertEqual(str(Test1Enum.One), 'MyStr') + self.assertEqual(format(Test1Enum.One, ''), 'MyStr') # class Test2Enum(MyStrEnum, MyMethodEnum): One = 1 Two = 2 self.assertEqual(str(Test2Enum.One), 'MyStr') + self.assertEqual(format(Test1Enum.One, ''), 'MyStr') def test_inherited_data_type(self): class HexInt(int): @@ -2000,6 +2014,32 @@ REVERT_ALL = "REVERT_ALL" RETRY = "RETRY" + def test_multiple_mixin_inherited(self): + class MyInt(int): + def __new__(cls, value): + return super().__new__(cls, value) + + class HexMixin: + def __repr__(self): + return hex(self) + + class MyIntEnum(HexMixin, MyInt, enum.Enum): + pass + + class Foo(MyIntEnum): + TEST = 1 + self.assertTrue(isinstance(Foo.TEST, MyInt)) + self.assertEqual(repr(Foo.TEST), "0x1") + + class Fee(MyIntEnum): + TEST = 1 + def __new__(cls, value): + value += 1 + member = int.__new__(cls, value) + member._value_ = value + return member + self.assertEqual(Fee.TEST, 2) + def test_empty_globals(self): # bpo-35717: sys._getframe(2).f_globals['__name__'] fails with KeyError # when using compile and exec because f_globals is empty @@ -2146,6 +2186,11 @@ self.assertEqual(repr(~(Open.RO | Open.CE)), '') self.assertEqual(repr(~(Open.WO | Open.CE)), '') + def test_format(self): + Perm = self.Perm + self.assertEqual(format(Perm.R, ''), 'Perm.R') + self.assertEqual(format(Perm.R | Perm.X, ''), 'Perm.R|X') + def test_or(self): Perm = self.Perm for i in Perm: @@ -2478,6 +2523,7 @@ def test_type(self): Perm = self.Perm + self.assertTrue(Perm._member_type_ is int) Open = self.Open for f in Perm: self.assertTrue(isinstance(f, Perm)) @@ -2557,6 +2603,11 @@ self.assertEqual(repr(~(Open.WO | Open.CE)), '') self.assertEqual(repr(Open(~4)), '') + def test_format(self): + Perm = self.Perm + self.assertEqual(format(Perm.R, ''), '4') + self.assertEqual(format(Perm.R | Perm.X, ''), '5') + def test_or(self): Perm = self.Perm for i in Perm: diff -Nru python3.8-3.8.6/Lib/test/test_epoll.py python3.8-3.8.7/Lib/test/test_epoll.py --- python3.8-3.8.6/Lib/test/test_epoll.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_epoll.py 2020-12-21 16:25:24.000000000 +0000 @@ -160,44 +160,42 @@ self.fail("epoll on closed fd didn't raise EBADF") def test_control_and_wait(self): + # create the epoll object client, server = self._connected_pair() - ep = select.epoll(16) ep.register(server.fileno(), select.EPOLLIN | select.EPOLLOUT | select.EPOLLET) ep.register(client.fileno(), select.EPOLLIN | select.EPOLLOUT | select.EPOLLET) + # EPOLLOUT now = time.monotonic() events = ep.poll(1, 4) then = time.monotonic() self.assertFalse(then - now > 0.1, then - now) - events.sort() expected = [(client.fileno(), select.EPOLLOUT), (server.fileno(), select.EPOLLOUT)] - expected.sort() - - self.assertEqual(events, expected) + self.assertEqual(sorted(events), sorted(expected)) - events = ep.poll(timeout=2.1, maxevents=4) + # no event + events = ep.poll(timeout=0.1, maxevents=4) self.assertFalse(events) - client.send(b"Hello!") - server.send(b"world!!!") + # send: EPOLLIN and EPOLLOUT + client.sendall(b"Hello!") + server.sendall(b"world!!!") now = time.monotonic() - events = ep.poll(1, 4) + events = ep.poll(1.0, 4) then = time.monotonic() self.assertFalse(then - now > 0.01) - events.sort() expected = [(client.fileno(), select.EPOLLIN | select.EPOLLOUT), (server.fileno(), select.EPOLLIN | select.EPOLLOUT)] - expected.sort() - - self.assertEqual(events, expected) + self.assertEqual(sorted(events), sorted(expected)) + # unregister, modify ep.unregister(client.fileno()) ep.modify(server.fileno(), select.EPOLLOUT) now = time.monotonic() diff -Nru python3.8-3.8.6/Lib/test/test_finalization.py python3.8-3.8.7/Lib/test/test_finalization.py --- python3.8-3.8.6/Lib/test/test_finalization.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_finalization.py 2020-12-21 16:25:24.000000000 +0000 @@ -16,6 +16,15 @@ raise TypeError('requires _testcapi.with_tp_del') return C +try: + from _testcapi import without_gc +except ImportError: + def without_gc(cls): + class C: + def __new__(cls, *args, **kwargs): + raise TypeError('requires _testcapi.without_gc') + return C + from test import support @@ -94,9 +103,11 @@ assert self.id_ == id(self) +@without_gc class NonGC(NonGCSimpleBase): __slots__ = () +@without_gc class NonGCResurrector(NonGCSimpleBase): __slots__ = () @@ -109,8 +120,14 @@ class Simple(SimpleBase): pass -class SimpleResurrector(NonGCResurrector, SimpleBase): - pass +# Can't inherit from NonGCResurrector, in case importing without_gc fails. +class SimpleResurrector(SimpleBase): + + def side_effect(self): + """ + Resurrect self by storing self in a class-wide list. + """ + self.survivors.append(self) class TestBase: @@ -178,6 +195,7 @@ self.assert_survivors([]) self.assertIs(wr(), None) + @support.cpython_only def test_non_gc(self): with SimpleBase.test(): s = NonGC() @@ -191,6 +209,7 @@ self.assert_del_calls(ids) self.assert_survivors([]) + @support.cpython_only def test_non_gc_resurrect(self): with SimpleBase.test(): s = NonGCResurrector() diff -Nru python3.8-3.8.6/Lib/test/test_format.py python3.8-3.8.7/Lib/test/test_format.py --- python3.8-3.8.6/Lib/test/test_format.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_format.py 2020-12-21 16:25:24.000000000 +0000 @@ -427,13 +427,16 @@ localeconv = locale.localeconv() sep = localeconv['thousands_sep'] point = localeconv['decimal_point'] + grouping = localeconv['grouping'] text = format(123456789, "n") - self.assertIn(sep, text) + if grouping: + self.assertIn(sep, text) self.assertEqual(text.replace(sep, ''), '123456789') text = format(1234.5, "n") - self.assertIn(sep, text) + if grouping: + self.assertIn(sep, text) self.assertIn(point, text) self.assertEqual(text.replace(sep, ''), '1234' + point + '5') finally: diff -Nru python3.8-3.8.6/Lib/test/test_gc.py python3.8-3.8.7/Lib/test/test_gc.py --- python3.8-3.8.6/Lib/test/test_gc.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_gc.py 2020-12-21 16:25:24.000000000 +0000 @@ -576,9 +576,9 @@ self.assertTrue(gc.is_tracked(UserInt())) self.assertTrue(gc.is_tracked([])) self.assertTrue(gc.is_tracked(set())) - self.assertFalse(gc.is_tracked(UserClassSlots())) - self.assertFalse(gc.is_tracked(UserFloatSlots())) - self.assertFalse(gc.is_tracked(UserIntSlots())) + self.assertTrue(gc.is_tracked(UserClassSlots())) + self.assertTrue(gc.is_tracked(UserFloatSlots())) + self.assertTrue(gc.is_tracked(UserIntSlots())) def test_bug1055820b(self): # Corresponds to temp2b.py in the bug report. diff -Nru python3.8-3.8.6/Lib/test/test_gdb.py python3.8-3.8.7/Lib/test/test_gdb.py --- python3.8-3.8.6/Lib/test/test_gdb.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_gdb.py 2020-12-21 16:25:24.000000000 +0000 @@ -51,11 +51,6 @@ "embedding. Saw %s.%s:\n%s" % (gdb_major_version, gdb_minor_version, gdb_version)) -if (gdb_major_version, gdb_minor_version) >= (9, 2): - # gdb 9.2 on Fedora Rawhide is not reliable, see: - # * https://bugs.python.org/issue41473 - # * https://bugzilla.redhat.com/show_bug.cgi?id=1866884 - raise unittest.SkipTest("https://bugzilla.redhat.com/show_bug.cgi?id=1866884") if not sysconfig.is_python_build(): raise unittest.SkipTest("test_gdb only works on source builds at the moment.") diff -Nru python3.8-3.8.6/Lib/test/test_httplib.py python3.8-3.8.7/Lib/test/test_httplib.py --- python3.8-3.8.6/Lib/test/test_httplib.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_httplib.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,5 +1,5 @@ import errno -from http import client +from http import client, HTTPStatus import io import itertools import os @@ -515,6 +515,10 @@ class BasicTest(TestCase): + def test_dir_with_added_behavior_on_status(self): + # see issue40084 + self.assertTrue({'description', 'name', 'phrase', 'value'} <= set(dir(HTTPStatus(404)))) + def test_status_lines(self): # Test HTTP status lines diff -Nru python3.8-3.8.6/Lib/test/test_idle.py python3.8-3.8.7/Lib/test/test_idle.py --- python3.8-3.8.6/Lib/test/test_idle.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_idle.py 2020-12-21 16:25:24.000000000 +0000 @@ -20,5 +20,5 @@ if __name__ == '__main__': tk.NoDefaultRoot() unittest.main(exit=False) - tk._support_default_root = 1 + tk._support_default_root = True tk._default_root = None diff -Nru python3.8-3.8.6/Lib/test/test_importlib/test_zip.py python3.8-3.8.7/Lib/test/test_importlib/test_zip.py --- python3.8-3.8.6/Lib/test/test_importlib/test_zip.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_importlib/test_zip.py 2020-12-21 16:25:24.000000000 +0000 @@ -3,11 +3,15 @@ from contextlib import ExitStack from importlib.metadata import ( - distribution, entry_points, files, PackageNotFoundError, version, + distribution, entry_points, files, PackageNotFoundError, + version, distributions, ) from importlib.resources import path +from test.support import requires_zlib + +@requires_zlib class TestZip(unittest.TestCase): root = 'test.test_importlib.data' @@ -46,7 +50,12 @@ path = str(file.dist.locate_file(file)) assert '.whl/' in path, path + def test_one_distribution(self): + dists = list(distributions(path=sys.path[:1])) + assert len(dists) == 1 + +@requires_zlib class TestEgg(TestZip): def setUp(self): # Find the path to the example-*.egg so we can add it to the front of diff -Nru python3.8-3.8.6/Lib/test/test_inspect.py python3.8-3.8.7/Lib/test/test_inspect.py --- python3.8-3.8.6/Lib/test/test_inspect.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_inspect.py 2020-12-21 16:25:24.000000000 +0000 @@ -392,6 +392,7 @@ ('ParrotDroppings', mod.ParrotDroppings), ('StupidGit', mod.StupidGit), ('Tit', mod.MalodorousPervert), + ('WhichComments', mod.WhichComments), ]) tree = inspect.getclasstree([cls[1] for cls in classes]) self.assertEqual(tree, @@ -405,7 +406,8 @@ [(mod.FesteringGob, (mod.MalodorousPervert, mod.ParrotDroppings)) ] - ] + ], + (mod.WhichComments, (object,),) ] ]) tree = inspect.getclasstree([cls[1] for cls in classes], True) @@ -417,7 +419,8 @@ [(mod.FesteringGob, (mod.MalodorousPervert, mod.ParrotDroppings)) ] - ] + ], + (mod.WhichComments, (object,),) ] ]) @@ -647,6 +650,18 @@ # as argument to another function. self.assertSourceEqual(mod2.anonymous, 55, 55) +class TestBlockComments(GetSourceBase): + fodderModule = mod + + def test_toplevel_class(self): + self.assertSourceEqual(mod.WhichComments, 96, 114) + + def test_class_method(self): + self.assertSourceEqual(mod.WhichComments.f, 99, 104) + + def test_class_async_method(self): + self.assertSourceEqual(mod.WhichComments.asyncf, 109, 112) + class TestBuggyCases(GetSourceBase): fodderModule = mod2 @@ -698,6 +713,17 @@ self.assertRaises(IOError, inspect.findsource, co) self.assertRaises(IOError, inspect.getsource, co) + def test_findsource_with_out_of_bounds_lineno(self): + mod_len = len(inspect.getsource(mod)) + src = '\n' * 2* mod_len + "def f(): pass" + co = compile(src, mod.__file__, "exec") + g, l = {}, {} + eval(co, g, l) + func = l['f'] + self.assertEqual(func.__code__.co_firstlineno, 1+2*mod_len) + with self.assertRaisesRegex(IOError, "lineno is out of bounds"): + inspect.findsource(func) + def test_getsource_on_method(self): self.assertSourceEqual(mod2.ClassWithMethod.method, 118, 119) @@ -3970,8 +3996,8 @@ def test_main(): run_unittest( - TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases, - TestInterpreterStack, TestClassesAndFunctions, TestPredicates, + TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBlockComments, + TestBuggyCases, TestInterpreterStack, TestClassesAndFunctions, TestPredicates, TestGetcallargsFunctions, TestGetcallargsMethods, TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState, TestNoEOL, TestSignatureObject, TestSignatureBind, TestParameterObject, diff -Nru python3.8-3.8.6/Lib/test/test_isinstance.py python3.8-3.8.7/Lib/test/test_isinstance.py --- python3.8-3.8.6/Lib/test/test_isinstance.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_isinstance.py 2020-12-21 16:25:24.000000000 +0000 @@ -271,6 +271,16 @@ self.assertEqual(True, issubclass(B(), int)) + def test_infinite_recursion_in_bases(self): + class X: + @property + def __bases__(self): + return self.__bases__ + + self.assertRaises(RecursionError, issubclass, X(), int) + self.assertRaises(RecursionError, issubclass, int, X()) + self.assertRaises(RecursionError, isinstance, 1, X()) + def blowstack(fxn, arg, compare_to): # Make sure that calling isinstance with a deeply nested tuple for its diff -Nru python3.8-3.8.6/Lib/test/test_itertools.py python3.8-3.8.7/Lib/test/test_itertools.py --- python3.8-3.8.6/Lib/test/test_itertools.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_itertools.py 2020-12-21 16:25:24.000000000 +0000 @@ -12,6 +12,8 @@ import sys import struct import threading +import gc + maxsize = support.MAX_Py_ssize_t minsize = -maxsize-1 @@ -1554,6 +1556,51 @@ self.assertRaises(StopIteration, next, f(lambda x:x, [])) self.assertRaises(StopIteration, next, f(lambda x:x, StopNow())) + @support.cpython_only + def test_combinations_result_gc(self): + # bpo-42536: combinations's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = combinations([None, []], 1) + next(it) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which has the value (None,). Make sure it's re-tracked when + # it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_combinations_with_replacement_result_gc(self): + # Ditto for combinations_with_replacement. + it = combinations_with_replacement([None, []], 1) + next(it) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_permutations_result_gc(self): + # Ditto for permutations. + it = permutations([None, []], 1) + next(it) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_product_result_gc(self): + # Ditto for product. + it = product([None, []]) + next(it) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + + @support.cpython_only + def test_zip_longest_result_gc(self): + # Ditto for zip_longest. + it = zip_longest([[]]) + gc.collect() + self.assertTrue(gc.is_tracked(next(it))) + + class TestExamples(unittest.TestCase): def test_accumulate(self): diff -Nru python3.8-3.8.6/Lib/test/test__locale.py python3.8-3.8.7/Lib/test/test__locale.py --- python3.8-3.8.6/Lib/test/test__locale.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test__locale.py 2020-12-21 16:25:24.000000000 +0000 @@ -72,6 +72,10 @@ 'ps_AF': ('\u066b', '\u066c'), } +if sys.platform == 'win32': + # ps_AF doesn't work on Windows: see bpo-38324 (msg361830) + del known_numerics['ps_AF'] + class _LocaleTests(unittest.TestCase): def setUp(self): diff -Nru python3.8-3.8.6/Lib/test/test_logging.py python3.8-3.8.7/Lib/test/test_logging.py --- python3.8-3.8.6/Lib/test/test_logging.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_logging.py 2020-12-21 16:25:24.000000000 +0000 @@ -1177,22 +1177,27 @@ class MockRaceConditionHandler: def __init__(self, mem_hdlr): self.mem_hdlr = mem_hdlr + self.threads = [] def removeTarget(self): self.mem_hdlr.setTarget(None) def handle(self, msg): - t = threading.Thread(target=self.removeTarget) - t.daemon = True - t.start() + thread = threading.Thread(target=self.removeTarget) + self.threads.append(thread) + thread.start() target = MockRaceConditionHandler(self.mem_hdlr) - self.mem_hdlr.setTarget(target) + try: + self.mem_hdlr.setTarget(target) - for _ in range(10): - time.sleep(0.005) - self.mem_logger.info("not flushed") - self.mem_logger.warning("flushed") + for _ in range(10): + time.sleep(0.005) + self.mem_logger.info("not flushed") + self.mem_logger.warning("flushed") + finally: + for thread in target.threads: + support.join_thread(thread) class ExceptionFormatter(logging.Formatter): @@ -4165,6 +4170,15 @@ logging.disable(83) self.assertEqual(logging.root.manager.disable, 83) + self.assertRaises(ValueError, logging.disable, "doesnotexists") + + class _NotAnIntOrString: + pass + + self.assertRaises(TypeError, logging.disable, _NotAnIntOrString()) + + logging.disable("WARN") + # test the default value introduced in 3.7 # (Issue #28524) logging.disable() diff -Nru python3.8-3.8.6/Lib/test/test_ordered_dict.py python3.8-3.8.7/Lib/test/test_ordered_dict.py --- python3.8-3.8.6/Lib/test/test_ordered_dict.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_ordered_dict.py 2020-12-21 16:25:24.000000000 +0000 @@ -654,6 +654,17 @@ support.check_free_after_iterating(self, lambda d: iter(d.values()), self.OrderedDict) support.check_free_after_iterating(self, lambda d: iter(d.items()), self.OrderedDict) + @support.cpython_only + def test_ordered_dict_items_result_gc(self): + # bpo-42536: OrderedDict.items's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = iter(self.OrderedDict({None: []}).items()) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None, None). Make sure it's re-tracked + # when it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase): diff -Nru python3.8-3.8.6/Lib/test/test_plistlib.py python3.8-3.8.7/Lib/test/test_plistlib.py --- python3.8-3.8.6/Lib/test/test_plistlib.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_plistlib.py 2020-12-21 16:25:24.000000000 +0000 @@ -2,6 +2,7 @@ import copy import operator import pickle +import struct import unittest import plistlib import os @@ -105,6 +106,298 @@ AAABOQ=='''), } +XML_PLIST_WITH_ENTITY=b'''\ + + + ]> + + + A + &entity; + + +''' + +INVALID_BINARY_PLISTS = [ + ('too short data', + b'' + ), + ('too large offset_table_offset and offset_size = 1', + b'\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x2a' + ), + ('too large offset_table_offset and nonstandard offset_size', + b'\x00\x00\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x03\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x2c' + ), + ('integer overflow in offset_table_offset', + b'\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\xff\xff\xff\xff\xff\xff\xff\xff' + ), + ('too large top_object', + b'\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('integer overflow in top_object', + b'\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\xff\xff\xff\xff\xff\xff\xff\xff' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('too large num_objects and offset_size = 1', + b'\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\xff' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('too large num_objects and nonstandard offset_size', + b'\x00\x00\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x03\x01' + b'\x00\x00\x00\x00\x00\x00\x00\xff' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('extremally large num_objects (32 bit)', + b'\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x7f\xff\xff\xff' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('extremally large num_objects (64 bit)', + b'\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\xff\xff\xff\xff\xff' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('integer overflow in num_objects', + b'\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\xff\xff\xff\xff\xff\xff\xff\xff' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('offset_size = 0', + b'\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('ref_size = 0', + b'\xa1\x01\x00\x08\x0a' + b'\x00\x00\x00\x00\x00\x00\x01\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0b' + ), + ('too large offset', + b'\x00\x2a' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('integer overflow in offset', + b'\x00\xff\xff\xff\xff\xff\xff\xff\xff' + b'\x00\x00\x00\x00\x00\x00\x08\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x09' + ), + ('too large array size', + b'\xaf\x00\x01\xff\x00\x08\x0c' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0d' + ), + ('extremally large array size (32-bit)', + b'\xaf\x02\x7f\xff\xff\xff\x01\x00\x08\x0f' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x10' + ), + ('extremally large array size (64-bit)', + b'\xaf\x03\x00\x00\x00\xff\xff\xff\xff\xff\x01\x00\x08\x13' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x14' + ), + ('integer overflow in array size', + b'\xaf\x03\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x08\x13' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x14' + ), + ('too large reference index', + b'\xa1\x02\x00\x08\x0a' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0b' + ), + ('integer overflow in reference index', + b'\xa1\xff\xff\xff\xff\xff\xff\xff\xff\x00\x08\x11' + b'\x00\x00\x00\x00\x00\x00\x01\x08' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x12' + ), + ('too large bytes size', + b'\x4f\x00\x23\x41\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0c' + ), + ('extremally large bytes size (32-bit)', + b'\x4f\x02\x7f\xff\xff\xff\x41\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0f' + ), + ('extremally large bytes size (64-bit)', + b'\x4f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x13' + ), + ('integer overflow in bytes size', + b'\x4f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x13' + ), + ('too large ASCII size', + b'\x5f\x00\x23\x41\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0c' + ), + ('extremally large ASCII size (32-bit)', + b'\x5f\x02\x7f\xff\xff\xff\x41\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0f' + ), + ('extremally large ASCII size (64-bit)', + b'\x5f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x13' + ), + ('integer overflow in ASCII size', + b'\x5f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x13' + ), + ('invalid ASCII', + b'\x51\xff\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0a' + ), + ('too large UTF-16 size', + b'\x6f\x00\x13\x20\xac\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0e' + ), + ('extremally large UTF-16 size (32-bit)', + b'\x6f\x02\x4f\xff\xff\xff\x20\xac\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x11' + ), + ('extremally large UTF-16 size (64-bit)', + b'\x6f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x20\xac\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x15' + ), + ('integer overflow in UTF-16 size', + b'\x6f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x20\xac\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x15' + ), + ('invalid UTF-16', + b'\x61\xd8\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0b' + ), + ('non-hashable key', + b'\xd1\x01\x01\xa0\x08\x0b' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x0c' + ), + ('too large datetime (datetime overflow)', + b'\x33\x42\x50\x00\x00\x00\x00\x00\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x11' + ), + ('too large datetime (timedelta overflow)', + b'\x33\x42\xe0\x00\x00\x00\x00\x00\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x11' + ), + ('invalid datetime (Infinity)', + b'\x33\x7f\xf0\x00\x00\x00\x00\x00\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x11' + ), + ('invalid datetime (NaN)', + b'\x33\x7f\xf8\x00\x00\x00\x00\x00\x00\x08' + b'\x00\x00\x00\x00\x00\x00\x01\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x11' + ), +] + class TestPlistlib(unittest.TestCase): @@ -486,6 +779,19 @@ self.assertRaises(ValueError, plistlib.loads, b"not real") + def test_integer_notations(self): + pl = b"456" + value = plistlib.loads(pl) + self.assertEqual(value, 456) + + pl = b"0xa" + value = plistlib.loads(pl) + self.assertEqual(value, 10) + + pl = b"0123" + value = plistlib.loads(pl) + self.assertEqual(value, 123) + def test_xml_encodings(self): base = TESTDATA[plistlib.FMT_XML] @@ -525,9 +831,29 @@ with self.assertRaises(OverflowError): plistlib.dumps(huge_uid, fmt=plistlib.FMT_BINARY) + def test_xml_plist_with_entity_decl(self): + with self.assertRaisesRegex(plistlib.InvalidFileException, + "XML entity declarations are not supported"): + plistlib.loads(XML_PLIST_WITH_ENTITY, fmt=plistlib.FMT_XML) + class TestBinaryPlistlib(unittest.TestCase): + @staticmethod + def decode(*objects, offset_size=1, ref_size=1): + data = [b'bplist00'] + offset = 8 + offsets = [] + for x in objects: + offsets.append(offset.to_bytes(offset_size, 'big')) + data.append(x) + offset += len(x) + tail = struct.pack('>6xBBQQQ', offset_size, ref_size, + len(objects), 0, offset) + data.extend(offsets) + data.append(tail) + return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY) + def test_nonstandard_refs_size(self): # Issue #21538: Refs and offsets are 24-bit integers data = (b'bplist00' @@ -542,7 +868,7 @@ def test_dump_duplicates(self): # Test effectiveness of saving duplicated objects - for x in (None, False, True, 12345, 123.45, 'abcde', b'abcde', + for x in (None, False, True, 12345, 123.45, 'abcde', 'абвгд', b'abcde', datetime.datetime(2004, 10, 26, 10, 33, 33), plistlib.Data(b'abcde'), bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}): @@ -581,6 +907,20 @@ b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY)) self.assertIs(b['x'], b) + def test_deep_nesting(self): + for N in [300, 100000]: + chunks = [b'\xa1' + (i + 1).to_bytes(4, 'big') for i in range(N)] + try: + result = self.decode(*chunks, b'\x54seed', offset_size=4, ref_size=4) + except RecursionError: + pass + else: + for i in range(N): + self.assertIsInstance(result, list) + self.assertEqual(len(result), 1) + result = result[0] + self.assertEqual(result, 'seed') + def test_large_timestamp(self): # Issue #26709: 32-bit timestamp out of range for ts in -2**31-1, 2**31: @@ -590,55 +930,37 @@ data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY) self.assertEqual(plistlib.loads(data), d) + def test_load_singletons(self): + self.assertIs(self.decode(b'\x00'), None) + self.assertIs(self.decode(b'\x08'), False) + self.assertIs(self.decode(b'\x09'), True) + self.assertEqual(self.decode(b'\x0f'), b'') + + def test_load_int(self): + self.assertEqual(self.decode(b'\x10\x00'), 0) + self.assertEqual(self.decode(b'\x10\xfe'), 0xfe) + self.assertEqual(self.decode(b'\x11\xfe\xdc'), 0xfedc) + self.assertEqual(self.decode(b'\x12\xfe\xdc\xba\x98'), 0xfedcba98) + self.assertEqual(self.decode(b'\x13\x01\x23\x45\x67\x89\xab\xcd\xef'), + 0x0123456789abcdef) + self.assertEqual(self.decode(b'\x13\xfe\xdc\xba\x98\x76\x54\x32\x10'), + -0x123456789abcdf0) + + def test_unsupported(self): + unsupported = [*range(1, 8), *range(10, 15), + 0x20, 0x21, *range(0x24, 0x33), *range(0x34, 0x40)] + for i in [0x70, 0x90, 0xb0, 0xc0, 0xe0, 0xf0]: + unsupported.extend(i + j for j in range(16)) + for token in unsupported: + with self.subTest(f'token {token:02x}'): + with self.assertRaises(plistlib.InvalidFileException): + self.decode(bytes([token]) + b'\x00'*16) + def test_invalid_binary(self): - for data in [ - # too short data - b'', - # too large offset_table_offset and nonstandard offset_size - b'\x00\x08' - b'\x00\x00\x00\x00\x00\x00\x03\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x2a', - # integer overflow in offset_table_offset - b'\x00\x08' - b'\x00\x00\x00\x00\x00\x00\x01\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x00' - b'\xff\xff\xff\xff\xff\xff\xff\xff', - # offset_size = 0 - b'\x00\x08' - b'\x00\x00\x00\x00\x00\x00\x00\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x09', - # ref_size = 0 - b'\xa1\x01\x00\x08\x0a' - b'\x00\x00\x00\x00\x00\x00\x01\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x02' - b'\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x0b', - # integer overflow in offset - b'\x00\xff\xff\xff\xff\xff\xff\xff\xff' - b'\x00\x00\x00\x00\x00\x00\x08\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x09', - # invalid ASCII - b'\x51\xff\x08' - b'\x00\x00\x00\x00\x00\x00\x01\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x0a', - # invalid UTF-16 - b'\x61\xd8\x00\x08' - b'\x00\x00\x00\x00\x00\x00\x01\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x01' - b'\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x0b', - ]: - with self.assertRaises(plistlib.InvalidFileException): - plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY) + for name, data in INVALID_BINARY_PLISTS: + with self.subTest(name): + with self.assertRaises(plistlib.InvalidFileException): + plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY) class TestPlistlibDeprecated(unittest.TestCase): diff -Nru python3.8-3.8.6/Lib/test/test_profile.py python3.8-3.8.7/Lib/test/test_profile.py --- python3.8-3.8.6/Lib/test/test_profile.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_profile.py 2020-12-21 16:25:24.000000000 +0000 @@ -6,7 +6,7 @@ import os from difflib import unified_diff from io import StringIO -from test.support import TESTFN, run_unittest, unlink +from test.support import TESTFN, run_unittest, unlink, temp_dir, change_cwd from contextlib import contextmanager import profile @@ -111,6 +111,20 @@ assert_python_ok('-m', self.profilermodule.__name__, '-m', 'timeit', '-n', '1') + def test_output_file_when_changing_directory(self): + with temp_dir() as tmpdir, change_cwd(tmpdir): + os.mkdir('dest') + with open('demo.py', 'w') as f: + f.write('import os; os.chdir("dest")') + + assert_python_ok( + '-m', self.profilermodule.__name__, + '-o', 'out.pstats', + 'demo.py', + ) + + self.assertTrue(os.path.exists('out.pstats')) + def regenerate_expected_output(filename, cls): filename = filename.rstrip('co') diff -Nru python3.8-3.8.6/Lib/test/test_runpy.py python3.8-3.8.7/Lib/test/test_runpy.py --- python3.8-3.8.6/Lib/test/test_runpy.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_runpy.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,14 +1,17 @@ # Test the runpy module -import unittest -import os +import contextlib +import importlib.machinery, importlib.util import os.path -import sys +import pathlib +import py_compile import re +import signal +import subprocess +import sys import tempfile -import importlib, importlib.machinery, importlib.util -import py_compile +import textwrap +import unittest import warnings -import pathlib from test.support import ( forget, make_legacy_pyc, unload, verbose, no_tracing, create_empty_file, temp_dir) @@ -752,5 +755,82 @@ self.assertEqual(result['s'], "non-ASCII: h\xe9") +class TestExit(unittest.TestCase): + STATUS_CONTROL_C_EXIT = 0xC000013A + EXPECTED_CODE = ( + STATUS_CONTROL_C_EXIT + if sys.platform == "win32" + else -signal.SIGINT + ) + @staticmethod + @contextlib.contextmanager + def tmp_path(*args, **kwargs): + with temp_dir() as tmp_fn: + yield pathlib.Path(tmp_fn) + + + def run(self, *args, **kwargs): + with self.tmp_path() as tmp: + self.ham = ham = tmp / "ham.py" + ham.write_text( + textwrap.dedent( + """\ + raise KeyboardInterrupt + """ + ) + ) + super().run(*args, **kwargs) + + def assertSigInt(self, *args, **kwargs): + proc = subprocess.run(*args, **kwargs, text=True, stderr=subprocess.PIPE) + self.assertTrue(proc.stderr.endswith("\nKeyboardInterrupt\n")) + self.assertEqual(proc.returncode, self.EXPECTED_CODE) + + def test_pymain_run_file(self): + self.assertSigInt([sys.executable, self.ham]) + + def test_pymain_run_file_runpy_run_module(self): + tmp = self.ham.parent + run_module = tmp / "run_module.py" + run_module.write_text( + textwrap.dedent( + """\ + import runpy + runpy.run_module("ham") + """ + ) + ) + self.assertSigInt([sys.executable, run_module], cwd=tmp) + + def test_pymain_run_file_runpy_run_module_as_main(self): + tmp = self.ham.parent + run_module_as_main = tmp / "run_module_as_main.py" + run_module_as_main.write_text( + textwrap.dedent( + """\ + import runpy + runpy._run_module_as_main("ham") + """ + ) + ) + self.assertSigInt([sys.executable, run_module_as_main], cwd=tmp) + + def test_pymain_run_command_run_module(self): + self.assertSigInt( + [sys.executable, "-c", "import runpy; runpy.run_module('ham')"], + cwd=self.ham.parent, + ) + + def test_pymain_run_command(self): + self.assertSigInt([sys.executable, "-c", "import ham"], cwd=self.ham.parent) + + def test_pymain_run_stdin(self): + self.assertSigInt([sys.executable], input="import ham", cwd=self.ham.parent) + + def test_pymain_run_module(self): + ham = self.ham + self.assertSigInt([sys.executable, "-m", ham.stem], cwd=ham.parent) + + if __name__ == "__main__": unittest.main() diff -Nru python3.8-3.8.6/Lib/test/test_shutil.py python3.8-3.8.7/Lib/test/test_shutil.py --- python3.8-3.8.6/Lib/test/test_shutil.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_shutil.py 2020-12-21 16:25:24.000000000 +0000 @@ -1830,6 +1830,23 @@ rv = shutil.which(program, path=self.temp_dir) self.assertEqual(rv, temp_filexyz.name) + # Issue 40592: See https://bugs.python.org/issue40592 + @unittest.skipUnless(sys.platform == "win32", 'test specific to Windows') + def test_pathext_with_empty_str(self): + ext = ".xyz" + temp_filexyz = tempfile.NamedTemporaryFile(dir=self.temp_dir, + prefix="Tmp2", suffix=ext) + self.addCleanup(temp_filexyz.close) + + # strip path and extension + program = os.path.basename(temp_filexyz.name) + program = os.path.splitext(program)[0] + + with support.EnvironmentVarGuard() as env: + env['PATHEXT'] = f"{ext};" # note the ; + rv = shutil.which(program, path=self.temp_dir) + self.assertEqual(rv, temp_filexyz.name) + class TestWhichBytes(TestWhich): def setUp(self): diff -Nru python3.8-3.8.6/Lib/test/test_site.py python3.8-3.8.7/Lib/test/test_site.py --- python3.8-3.8.6/Lib/test/test_site.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_site.py 2020-12-21 16:25:24.000000000 +0000 @@ -492,8 +492,6 @@ @test.support.requires_resource('network') @test.support.system_must_validate_cert - @unittest.skipUnless(sys.version_info[3] == 'final', - 'only for released versions') @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license') def test_license_exists_at_url(self): @@ -501,6 +499,8 @@ # string displayed by license in the absence of a LICENSE file. url = license._Printer__data.split()[1] req = urllib.request.Request(url, method='HEAD') + # Reset global urllib.request._opener + self.addCleanup(urllib.request.urlcleanup) try: with test.support.transient_internet(url): with urllib.request.urlopen(req) as data: diff -Nru python3.8-3.8.6/Lib/test/test_ssl.py python3.8-3.8.7/Lib/test/test_ssl.py --- python3.8-3.8.6/Lib/test/test_ssl.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_ssl.py 2020-12-21 16:25:24.000000000 +0000 @@ -3826,6 +3826,7 @@ @requires_minimum_version @requires_tls_version('TLSv1_2') + @requires_tls_version('TLSv1') def test_min_max_version_mismatch(self): client_context, server_context, hostname = testing_context() # client 1.0, server 1.2 (mismatch) diff -Nru python3.8-3.8.6/Lib/test/test_symtable.py python3.8-3.8.7/Lib/test/test_symtable.py --- python3.8-3.8.6/Lib/test/test_symtable.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_symtable.py 2020-12-21 16:25:24.000000000 +0000 @@ -11,6 +11,8 @@ glob = 42 some_var = 12 +some_non_assigned_global_var = 11 +some_assigned_global_var = 11 class Mine: instance_var = 24 @@ -19,6 +21,8 @@ def spam(a, b, *var, **kw): global bar + global some_assigned_global_var + some_assigned_global_var = 12 bar = 47 some_var = 10 x = 23 @@ -82,14 +86,14 @@ def test_lineno(self): self.assertEqual(self.top.get_lineno(), 0) - self.assertEqual(self.spam.get_lineno(), 12) + self.assertEqual(self.spam.get_lineno(), 14) def test_function_info(self): func = self.spam self.assertEqual(sorted(func.get_parameters()), ["a", "b", "kw", "var"]) expected = ['a', 'b', 'internal', 'kw', 'other_internal', 'some_var', 'var', 'x'] self.assertEqual(sorted(func.get_locals()), expected) - self.assertEqual(sorted(func.get_globals()), ["bar", "glob"]) + self.assertEqual(sorted(func.get_globals()), ["bar", "glob", "some_assigned_global_var"]) self.assertEqual(self.internal.get_frees(), ("x",)) def test_globals(self): @@ -100,6 +104,9 @@ self.assertFalse(self.internal.lookup("x").is_global()) self.assertFalse(self.Mine.lookup("instance_var").is_global()) self.assertTrue(self.spam.lookup("bar").is_global()) + # Module-scope globals are both global and local + self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_global()) + self.assertTrue(self.top.lookup("some_assigned_global_var").is_global()) def test_nonlocal(self): self.assertFalse(self.spam.lookup("some_var").is_nonlocal()) @@ -110,6 +117,9 @@ def test_local(self): self.assertTrue(self.spam.lookup("x").is_local()) self.assertFalse(self.spam.lookup("bar").is_local()) + # Module-scope globals are both global and local + self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_local()) + self.assertTrue(self.top.lookup("some_assigned_global_var").is_local()) def test_free(self): self.assertTrue(self.internal.lookup("x").is_free()) @@ -228,6 +238,10 @@ top = symtable.symtable(code, "?", "exec") self.assertIsNotNone(find_block(top, "\u017d")) + def test_symtable_repr(self): + self.assertEqual(str(self.top), "") + self.assertEqual(str(self.spam), "") + if __name__ == '__main__': unittest.main() diff -Nru python3.8-3.8.6/Lib/test/test_sysconfig.py python3.8-3.8.7/Lib/test/test_sysconfig.py --- python3.8-3.8.6/Lib/test/test_sysconfig.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_sysconfig.py 2020-12-21 16:25:24.000000000 +0000 @@ -358,10 +358,12 @@ @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, 'EXT_SUFFIX required for this test') - def test_SO_in_vars(self): + def test_EXT_SUFFIX_in_vars(self): + import _imp vars = sysconfig.get_config_vars() self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) + self.assertEqual(vars['EXT_SUFFIX'], _imp.extension_suffixes()[0]) @unittest.skipUnless(sys.platform == 'linux' and hasattr(sys.implementation, '_multiarch'), diff -Nru python3.8-3.8.6/Lib/test/test_tarfile.py python3.8-3.8.7/Lib/test/test_tarfile.py --- python3.8-3.8.6/Lib/test/test_tarfile.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_tarfile.py 2020-12-21 16:25:24.000000000 +0000 @@ -1314,10 +1314,10 @@ f.write('something\n') os.symlink(source_file, target_file) with tarfile.open(temparchive, 'w') as tar: - tar.add(source_file) - tar.add(target_file) + tar.add(source_file, arcname="source") + tar.add(target_file, arcname="symlink") # Let's extract it to the location which contains the symlink - with tarfile.open(temparchive) as tar: + with tarfile.open(temparchive, errorlevel=2) as tar: # this should not raise OSError: [Errno 17] File exists try: tar.extractall(path=tempdir) @@ -1384,12 +1384,15 @@ pax_headers={'non': 'empty'}) self.assertFalse(f.closed) + class GzipWriteTest(GzipTest, WriteTest): pass + class Bz2WriteTest(Bz2Test, WriteTest): pass + class LzmaWriteTest(LzmaTest, WriteTest): pass @@ -1432,8 +1435,17 @@ finally: os.umask(original_umask) + class GzipStreamWriteTest(GzipTest, StreamWriteTest): - pass + def test_source_directory_not_leaked(self): + """ + Ensure the source directory is not included in the tar header + per bpo-41316. + """ + tarfile.open(tmpname, self.mode).close() + payload = pathlib.Path(tmpname).read_text(encoding='latin-1') + assert os.path.dirname(tmpname) not in payload + class Bz2StreamWriteTest(Bz2Test, StreamWriteTest): decompressor = bz2.BZ2Decompressor if bz2 else None diff -Nru python3.8-3.8.6/Lib/test/test_traceback.py python3.8-3.8.7/Lib/test/test_traceback.py --- python3.8-3.8.6/Lib/test/test_traceback.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_traceback.py 2020-12-21 16:25:24.000000000 +0000 @@ -1083,6 +1083,78 @@ self.assertEqual(exc_info[0], exc.exc_type) self.assertEqual(str(exc_info[1]), str(exc)) + def test_no_refs_to_exception_and_traceback_objects(self): + try: + 1/0 + except Exception: + exc_info = sys.exc_info() + + refcnt1 = sys.getrefcount(exc_info[1]) + refcnt2 = sys.getrefcount(exc_info[2]) + exc = traceback.TracebackException(*exc_info) + self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1) + self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2) + + def test_comparison_basic(self): + try: + 1/0 + except Exception: + exc_info = sys.exc_info() + exc = traceback.TracebackException(*exc_info) + exc2 = traceback.TracebackException(*exc_info) + self.assertIsNot(exc, exc2) + self.assertEqual(exc, exc2) + class MyObject: + pass + self.assertNotEqual(exc, MyObject()) + + def test_comparison_params_variations(self): + def raise_exc(): + try: + raise ValueError('bad value') + except: + raise + + def raise_with_locals(): + x, y = 1, 2 + raise_exc() + + try: + raise_with_locals() + except Exception: + exc_info = sys.exc_info() + + exc = traceback.TracebackException(*exc_info) + exc1 = traceback.TracebackException(*exc_info, limit=10) + exc2 = traceback.TracebackException(*exc_info, limit=2) + + self.assertEqual(exc, exc1) # limit=10 gets all frames + self.assertNotEqual(exc, exc2) # limit=2 truncates the output + + # locals change the output + exc3 = traceback.TracebackException(*exc_info, capture_locals=True) + self.assertNotEqual(exc, exc3) + + # there are no locals in the innermost frame + exc4 = traceback.TracebackException(*exc_info, limit=-1) + exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True) + self.assertEqual(exc4, exc5) + + # there are locals in the next-to-innermost frame + exc6 = traceback.TracebackException(*exc_info, limit=-2) + exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True) + self.assertNotEqual(exc6, exc7) + + def test_comparison_equivalent_exceptions_are_equal(self): + excs = [] + for _ in range(2): + try: + 1/0 + except: + excs.append(traceback.TracebackException(*sys.exc_info())) + self.assertEqual(excs[0], excs[1]) + self.assertEqual(list(excs[0].format()), list(excs[1].format())) + def test_unhashable(self): class UnhashableException(Exception): def __eq__(self, other): @@ -1124,7 +1196,7 @@ f = test_frame(c, None, None) tb = test_tb(f, 6, None) exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False) - self.assertEqual({}, linecache.cache) + self.assertEqual(linecache.cache, {}) linecache.updatecache('/foo.py', globals()) self.assertEqual(exc.stack[0].line, "import sys") diff -Nru python3.8-3.8.6/Lib/test/test_urllib2.py python3.8-3.8.7/Lib/test/test_urllib2.py --- python3.8-3.8.6/Lib/test/test_urllib2.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_urllib2.py 2020-12-21 16:25:24.000000000 +0000 @@ -1444,6 +1444,18 @@ bypass = {'exclude_simple': True, 'exceptions': []} self.assertTrue(_proxy_bypass_macosx_sysconf('test', bypass)) + # Check that invalid prefix lengths are ignored + bypass = { + 'exclude_simple': False, + 'exceptions': [ '10.0.0.0/40', '172.19.10.0/24' ] + } + host = '172.19.10.5' + self.assertTrue(_proxy_bypass_macosx_sysconf(host, bypass), + 'expected bypass of %s to be True' % host) + host = '10.0.1.5' + self.assertFalse(_proxy_bypass_macosx_sysconf(host, bypass), + 'expected bypass of %s to be False' % host) + def check_basic_auth(self, headers, realm): with self.subTest(realm=realm, headers=headers): opener = OpenerDirector() diff -Nru python3.8-3.8.6/Lib/test/test_urllib.py python3.8-3.8.7/Lib/test/test_urllib.py --- python3.8-3.8.6/Lib/test/test_urllib.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_urllib.py 2020-12-21 16:25:24.000000000 +0000 @@ -1245,6 +1245,12 @@ self.assertEqual(expect, result, "using unquote(): %r != %r" % (expect, result)) + def test_unquoting_with_bytes_input(self): + # Bytes not supported yet + with self.assertRaisesRegex(TypeError, 'Expected str, got bytes'): + given = b'bl\xc3\xa5b\xc3\xa6rsyltet\xc3\xb8y' + urllib.parse.unquote(given) + class urlencode_Tests(unittest.TestCase): """Tests for urlencode()""" diff -Nru python3.8-3.8.6/Lib/test/test_xml_etree.py python3.8-3.8.7/Lib/test/test_xml_etree.py --- python3.8-3.8.6/Lib/test/test_xml_etree.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_xml_etree.py 2020-12-21 16:25:24.000000000 +0000 @@ -3701,6 +3701,14 @@ #self.assertEqual(c14n_roundtrip(""), #'') + # Namespace issues + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + def test_c14n_exclusion(self): xml = textwrap.dedent("""\ diff -Nru python3.8-3.8.6/Lib/test/test_zipfile.py python3.8-3.8.7/Lib/test/test_zipfile.py --- python3.8-3.8.6/Lib/test/test_zipfile.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/test/test_zipfile.py 2020-12-21 16:25:24.000000000 +0000 @@ -1844,11 +1844,14 @@ self.assertEqual(zipf.comment, b"an updated comment") # check that comments are correctly shortened in append mode + # and the file is indeed truncated with zipfile.ZipFile(TESTFN,mode="w") as zipf: zipf.comment = b"original comment that's longer" zipf.writestr("foo.txt", "O, for a Muse of Fire!") + original_zip_size = os.path.getsize(TESTFN) with zipfile.ZipFile(TESTFN,mode="a") as zipf: zipf.comment = b"shorter comment" + self.assertTrue(original_zip_size > os.path.getsize(TESTFN)) with zipfile.ZipFile(TESTFN,mode="r") as zipf: self.assertEqual(zipf.comment, b"shorter comment") diff -Nru python3.8-3.8.6/Lib/tkinter/commondialog.py python3.8-3.8.7/Lib/tkinter/commondialog.py --- python3.8-3.8.6/Lib/tkinter/commondialog.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/commondialog.py 2020-12-21 16:25:24.000000000 +0000 @@ -16,10 +16,10 @@ command = None def __init__(self, master=None, **options): - self.master = master + if not master: + master = options.get('parent') + self.master = master self.options = options - if not master and options.get('parent'): - self.master = options['parent'] def _fixoptions(self): pass # hook diff -Nru python3.8-3.8.6/Lib/tkinter/font.py python3.8-3.8.7/Lib/tkinter/font.py --- python3.8-3.8.6/Lib/tkinter/font.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/font.py 2020-12-21 16:25:24.000000000 +0000 @@ -68,7 +68,7 @@ def __init__(self, root=None, font=None, name=None, exists=False, **options): if not root: - root = tkinter._default_root + root = tkinter._get_default_root('use font') tk = getattr(root, 'tk', root) if font: # get actual settings corresponding to the given font @@ -177,7 +177,7 @@ def families(root=None, displayof=None): "Get font families (as a tuple)" if not root: - root = tkinter._default_root + root = tkinter._get_default_root('use font.families()') args = () if displayof: args = ('-displayof', displayof) @@ -187,7 +187,7 @@ def names(root=None): "Get names of defined fonts (as a tuple)" if not root: - root = tkinter._default_root + root = tkinter._get_default_root('use font.names()') return root.tk.splitlist(root.tk.call("font", "names")) diff -Nru python3.8-3.8.6/Lib/tkinter/__init__.py python3.8-3.8.7/Lib/tkinter/__init__.py --- python3.8-3.8.6/Lib/tkinter/__init__.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/__init__.py 2020-12-21 16:25:24.000000000 +0000 @@ -185,8 +185,7 @@ Deactivate = '37' MouseWheel = '38' - def __str__(self): - return self.name + __str__ = str.__str__ class Event: @@ -266,12 +265,12 @@ 'num', 'delta', 'focus', 'x', 'y', 'width', 'height') return '<%s event%s>' % ( - self.type, + getattr(self.type, 'name', self.type), ''.join(' %s=%s' % (k, attrs[k]) for k in keys if k in attrs) ) -_support_default_root = 1 +_support_default_root = True _default_root = None @@ -281,13 +280,26 @@ Call this function to inhibit that the first instance of Tk is used for windows without an explicit parent window. """ - global _support_default_root - _support_default_root = 0 - global _default_root + global _support_default_root, _default_root + _support_default_root = False + # Delete, so any use of _default_root will immediately raise an exception. + # Rebind before deletion, so repeated calls will not fail. _default_root = None del _default_root +def _get_default_root(what=None): + if not _support_default_root: + raise RuntimeError("No master specified and tkinter is " + "configured to not support default root") + if not _default_root: + if what: + raise RuntimeError(f"Too early to {what}: no default root window") + root = Tk() + assert _default_root is root + return _default_root + + def _tkerror(err): """Internal function.""" pass @@ -331,7 +343,7 @@ raise TypeError("name must be a string") global _varnum if not master: - master = _default_root + master = _get_default_root('create variable') self._root = master._root() self._tk = master.tk if name: @@ -590,7 +602,7 @@ def mainloop(n=0): """Run the main loop of Tcl.""" - _default_root.tk.mainloop(n) + _get_default_root('run the main loop').tk.mainloop(n) getint = int @@ -599,9 +611,9 @@ def getboolean(s): - """Convert true and false to integer values 1 and 0.""" + """Convert Tcl object to True or False.""" try: - return _default_root.tk.getboolean(s) + return _get_default_root('use getboolean()').tk.getboolean(s) except TclError: raise ValueError("invalid literal for getboolean()") @@ -2247,7 +2259,7 @@ is the name of the widget class.""" self.master = None self.children = {} - self._tkloaded = 0 + self._tkloaded = False # to avoid recursions in the getattr code in case of failure, we # ensure that self.tk is always _something_. self.tk = None @@ -2271,7 +2283,7 @@ self._loadtk() def _loadtk(self): - self._tkloaded = 1 + self._tkloaded = True global _default_root # Version sanity checks tk_version = self.tk.getvar('tk_version') @@ -2520,12 +2532,8 @@ def _setup(self, master, cnf): """Internal function. Sets up information about children.""" - if _support_default_root: - global _default_root - if not master: - if not _default_root: - _default_root = Tk() - master = _default_root + if not master: + master = _get_default_root() self.master = master self.tk = master.tk name = None @@ -3989,9 +3997,7 @@ def __init__(self, imgtype, name=None, cnf={}, master=None, **kw): self.name = None if not master: - master = _default_root - if not master: - raise RuntimeError('Too early to create image') + master = _get_default_root('create image') self.tk = getattr(master, 'tk', master) if not name: Image._last_id += 1 @@ -4145,11 +4151,13 @@ def image_names(): - return _default_root.tk.splitlist(_default_root.tk.call('image', 'names')) + tk = _get_default_root('use image_names()').tk + return tk.splitlist(tk.call('image', 'names')) def image_types(): - return _default_root.tk.splitlist(_default_root.tk.call('image', 'types')) + tk = _get_default_root('use image_types()').tk + return tk.splitlist(tk.call('image', 'types')) class Spinbox(Widget, XView): diff -Nru python3.8-3.8.6/Lib/tkinter/simpledialog.py python3.8-3.8.7/Lib/tkinter/simpledialog.py --- python3.8-3.8.6/Lib/tkinter/simpledialog.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/simpledialog.py 2020-12-21 16:25:24.000000000 +0000 @@ -24,9 +24,7 @@ """ from tkinter import * -from tkinter import messagebox - -import tkinter # used at _QueryDialog for tkinter._default_root +from tkinter import messagebox, _get_default_root class SimpleDialog: @@ -128,13 +126,17 @@ title -- the dialog title ''' - Toplevel.__init__(self, parent) + master = parent + if not master: + master = _get_default_root('create dialog window') + + Toplevel.__init__(self, master) self.withdraw() # remain invisible for now - # If the master is not viewable, don't + # If the parent is not viewable, don't # make the child transient, or else it # would be opened withdrawn - if parent.winfo_viewable(): + if parent is not None and parent.winfo_viewable(): self.transient(parent) if title: @@ -155,7 +157,7 @@ self.protocol("WM_DELETE_WINDOW", self.cancel) - if self.parent is not None: + if parent is not None: self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50)) @@ -259,9 +261,6 @@ minvalue = None, maxvalue = None, parent = None): - if not parent: - parent = tkinter._default_root - self.prompt = prompt self.minvalue = minvalue self.maxvalue = maxvalue diff -Nru python3.8-3.8.6/Lib/tkinter/test/support.py python3.8-3.8.7/Lib/tkinter/test/support.py --- python3.8-3.8.6/Lib/tkinter/test/support.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/support.py 2020-12-21 16:25:24.000000000 +0000 @@ -36,6 +36,33 @@ w.destroy() self.root.withdraw() + +class AbstractDefaultRootTest: + + def setUp(self): + self._old_support_default_root = tkinter._support_default_root + destroy_default_root() + tkinter._support_default_root = True + self.wantobjects = tkinter.wantobjects + + def tearDown(self): + destroy_default_root() + tkinter._default_root = None + tkinter._support_default_root = self._old_support_default_root + + def _test_widget(self, constructor): + # no master passing + x = constructor() + self.assertIsNotNone(tkinter._default_root) + self.assertIs(x.master, tkinter._default_root) + self.assertIs(x.tk, tkinter._default_root.tk) + x.destroy() + destroy_default_root() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, constructor) + self.assertFalse(hasattr(tkinter, '_default_root')) + + def destroy_default_root(): if getattr(tkinter, '_default_root', None): tkinter._default_root.update_idletasks() diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_font.py python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_font.py --- python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_font.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_font.py 2020-12-21 16:25:24.000000000 +0000 @@ -2,7 +2,7 @@ import tkinter from tkinter import font from test.support import requires, run_unittest, gc_collect -from tkinter.test.support import AbstractTkTest +from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest requires('gui') @@ -100,7 +100,38 @@ self.assertTrue(name) self.assertIn(fontname, names) -tests_gui = (FontTest, ) + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_families(self): + self.assertRaises(RuntimeError, font.families) + root = tkinter.Tk() + families = font.families() + self.assertIsInstance(families, tuple) + self.assertTrue(families) + for family in families: + self.assertIsInstance(family, str) + self.assertTrue(family) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, font.families) + + def test_names(self): + self.assertRaises(RuntimeError, font.names) + root = tkinter.Tk() + names = font.names() + self.assertIsInstance(names, tuple) + self.assertTrue(names) + for name in names: + self.assertIsInstance(name, str) + self.assertTrue(name) + self.assertIn(fontname, names) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, font.names) + + +tests_gui = (FontTest, DefaultRootTest) if __name__ == "__main__": run_unittest(*tests_gui) diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_images.py python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_images.py --- python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_images.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_images.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,7 +1,7 @@ import unittest import tkinter from test import support -from tkinter.test.support import AbstractTkTest, requires_tcl +from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest, requires_tcl support.requires('gui') @@ -19,6 +19,47 @@ self.assertIsInstance(image_names, tuple) +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_image_types(self): + self.assertRaises(RuntimeError, tkinter.image_types) + root = tkinter.Tk() + image_types = tkinter.image_types() + self.assertIsInstance(image_types, tuple) + self.assertIn('photo', image_types) + self.assertIn('bitmap', image_types) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.image_types) + + def test_image_names(self): + self.assertRaises(RuntimeError, tkinter.image_names) + root = tkinter.Tk() + image_names = tkinter.image_names() + self.assertIsInstance(image_names, tuple) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.image_names) + + def test_image_create_bitmap(self): + self.assertRaises(RuntimeError, tkinter.BitmapImage) + root = tkinter.Tk() + image = tkinter.BitmapImage() + self.assertIn(image.name, tkinter.image_names()) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.BitmapImage) + + def test_image_create_photo(self): + self.assertRaises(RuntimeError, tkinter.PhotoImage) + root = tkinter.Tk() + image = tkinter.PhotoImage() + self.assertIn(image.name, tkinter.image_names()) + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.PhotoImage) + + class BitmapImageTest(AbstractTkTest, unittest.TestCase): @classmethod @@ -330,7 +371,7 @@ self.assertEqual(image.transparency_get(4, 6), False) -tests_gui = (MiscTest, BitmapImageTest, PhotoImageTest,) +tests_gui = (MiscTest, DefaultRootTest, BitmapImageTest, PhotoImageTest,) if __name__ == "__main__": support.run_unittest(*tests_gui) diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_misc.py python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_misc.py --- python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_misc.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_misc.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,7 +1,7 @@ import unittest import tkinter from test import support -from tkinter.test.support import AbstractTkTest +from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest support.requires('gui') @@ -178,8 +178,134 @@ with self.assertRaises(tkinter.TclError): root.clipboard_get() + def test_event_repr_defaults(self): + e = tkinter.Event() + e.serial = 12345 + e.num = '??' + e.height = '??' + e.keycode = '??' + e.state = 0 + e.time = 123456789 + e.width = '??' + e.x = '??' + e.y = '??' + e.char = '' + e.keysym = '??' + e.keysym_num = '??' + e.type = '100' + e.widget = '??' + e.x_root = '??' + e.y_root = '??' + e.delta = 0 + self.assertEqual(repr(e), '<100 event>') + + def test_event_repr(self): + e = tkinter.Event() + e.serial = 12345 + e.num = 3 + e.focus = True + e.height = 200 + e.keycode = 65 + e.state = 0x30405 + e.time = 123456789 + e.width = 300 + e.x = 10 + e.y = 20 + e.char = 'A' + e.send_event = True + e.keysym = 'Key-A' + e.keysym_num = ord('A') + e.type = tkinter.EventType.Configure + e.widget = '.text' + e.x_root = 1010 + e.y_root = 1020 + e.delta = -1 + self.assertEqual(repr(e), + "") + + def test_getboolean(self): + for v in 'true', 'yes', 'on', '1', 't', 'y', 1, True: + self.assertIs(self.root.getboolean(v), True) + for v in 'false', 'no', 'off', '0', 'f', 'n', 0, False: + self.assertIs(self.root.getboolean(v), False) + self.assertRaises(ValueError, self.root.getboolean, 'yea') + self.assertRaises(ValueError, self.root.getboolean, '') + self.assertRaises(TypeError, self.root.getboolean, None) + self.assertRaises(TypeError, self.root.getboolean, ()) -tests_gui = (MiscTest, ) + def test_mainloop(self): + log = [] + def callback(): + log.append(1) + self.root.after(100, self.root.quit) + self.root.after(100, callback) + self.root.mainloop(1) + self.assertEqual(log, []) + self.root.mainloop(0) + self.assertEqual(log, [1]) + self.assertTrue(self.root.winfo_exists()) + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_default_root(self): + self.assertIs(tkinter._support_default_root, True) + self.assertIsNone(tkinter._default_root) + root = tkinter.Tk() + root2 = tkinter.Tk() + root3 = tkinter.Tk() + self.assertIs(tkinter._default_root, root) + root2.destroy() + self.assertIs(tkinter._default_root, root) + root.destroy() + self.assertIsNone(tkinter._default_root) + root3.destroy() + self.assertIsNone(tkinter._default_root) + + def test_no_default_root(self): + self.assertIs(tkinter._support_default_root, True) + self.assertIsNone(tkinter._default_root) + root = tkinter.Tk() + self.assertIs(tkinter._default_root, root) + tkinter.NoDefaultRoot() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + # repeated call is no-op + tkinter.NoDefaultRoot() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + root.destroy() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + root = tkinter.Tk() + self.assertIs(tkinter._support_default_root, False) + self.assertFalse(hasattr(tkinter, '_default_root')) + root.destroy() + + def test_getboolean(self): + self.assertRaises(RuntimeError, tkinter.getboolean, '1') + root = tkinter.Tk() + self.assertIs(tkinter.getboolean('1'), True) + self.assertRaises(ValueError, tkinter.getboolean, 'yea') + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.getboolean, '1') + + def test_mainloop(self): + self.assertRaises(RuntimeError, tkinter.mainloop) + root = tkinter.Tk() + root.after_idle(root.quit) + tkinter.mainloop() + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, tkinter.mainloop) + + +tests_gui = (MiscTest, DefaultRootTest) if __name__ == "__main__": support.run_unittest(*tests_gui) diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_simpledialog.py python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_simpledialog.py --- python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_simpledialog.py 1970-01-01 00:00:00.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_simpledialog.py 2020-12-21 16:25:24.000000000 +0000 @@ -0,0 +1,25 @@ +import unittest +import tkinter +from test.support import requires, run_unittest, swap_attr +from tkinter.test.support import AbstractDefaultRootTest +from tkinter.simpledialog import Dialog, askinteger + +requires('gui') + + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_askinteger(self): + self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number") + root = tkinter.Tk() + with swap_attr(Dialog, 'wait_window', lambda self, w: w.destroy()): + askinteger("Go To Line", "Line number") + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number") + + +tests_gui = (DefaultRootTest,) + +if __name__ == "__main__": + run_unittest(*tests_gui) diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_variables.py python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_variables.py --- python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_variables.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_variables.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,7 +1,9 @@ import unittest import gc +import tkinter from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl, TclError) +from tkinter.test.support import AbstractDefaultRootTest class Var(Variable): @@ -301,8 +303,21 @@ v.get() +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_variable(self): + self.assertRaises(RuntimeError, Variable) + root = tkinter.Tk() + v = Variable() + v.set("value") + self.assertEqual(v.get(), "value") + root.destroy() + tkinter.NoDefaultRoot() + self.assertRaises(RuntimeError, Variable) + + tests_gui = (TestVariable, TestStringVar, TestIntVar, - TestDoubleVar, TestBooleanVar) + TestDoubleVar, TestBooleanVar, DefaultRootTest) if __name__ == "__main__": diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_widgets.py python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_widgets.py --- python3.8-3.8.6/Lib/tkinter/test/test_tkinter/test_widgets.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_tkinter/test_widgets.py 2020-12-21 16:25:24.000000000 +0000 @@ -6,7 +6,8 @@ from test.support import requires from tkinter.test.support import (tcl_version, requires_tcl, - get_tk_patchlevel, widget_eq) + get_tk_patchlevel, widget_eq, + AbstractDefaultRootTest) from tkinter.test.widget_tests import ( add_standard_options, noconv, pixels_round, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, @@ -940,7 +941,8 @@ def test_from(self): widget = self.create() - self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=float_round) + conv = False if get_tk_patchlevel() >= (8, 6, 10) else float_round + self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv) def test_label(self): widget = self.create() @@ -1297,12 +1299,21 @@ self.checkIntegerParam(widget, 'aspect', 250, 0, -300) +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_frame(self): + self._test_widget(tkinter.Frame) + + def test_label(self): + self._test_widget(tkinter.Label) + + tests_gui = ( ButtonTest, CanvasTest, CheckbuttonTest, EntryTest, FrameTest, LabelFrameTest,LabelTest, ListboxTest, MenubuttonTest, MenuTest, MessageTest, OptionMenuTest, PanedWindowTest, RadiobuttonTest, ScaleTest, ScrollbarTest, - SpinboxTest, TextTest, ToplevelTest, + SpinboxTest, TextTest, ToplevelTest, DefaultRootTest, ) if __name__ == '__main__': diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_ttk/test_extensions.py python3.8-3.8.7/Lib/tkinter/test/test_ttk/test_extensions.py --- python3.8-3.8.6/Lib/tkinter/test/test_ttk/test_extensions.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_ttk/test_extensions.py 2020-12-21 16:25:24.000000000 +0000 @@ -2,8 +2,8 @@ import unittest import tkinter from tkinter import ttk -from test.support import requires, run_unittest, swap_attr -from tkinter.test.support import AbstractTkTest, destroy_default_root +from test.support import requires, run_unittest +from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest requires('gui') @@ -46,20 +46,6 @@ if hasattr(sys, 'last_type'): self.assertNotEqual(sys.last_type, tkinter.TclError) - - def test_initialization_no_master(self): - # no master passing - with swap_attr(tkinter, '_default_root', None), \ - swap_attr(tkinter, '_support_default_root', True): - try: - x = ttk.LabeledScale() - self.assertIsNotNone(tkinter._default_root) - self.assertEqual(x.master, tkinter._default_root) - self.assertEqual(x.tk, tkinter._default_root.tk) - x.destroy() - finally: - destroy_default_root() - def test_initialization(self): # master passing master = tkinter.Frame(self.root) @@ -114,7 +100,6 @@ def test_horizontal_range(self): lscale = ttk.LabeledScale(self.root, from_=0, to=10) lscale.pack() - lscale.wait_visibility() lscale.update() linfo_1 = lscale.label.place_info() @@ -144,7 +129,6 @@ def test_variable_change(self): x = ttk.LabeledScale(self.root) x.pack() - x.wait_visibility() x.update() curr_xcoord = x.scale.coords()[0] @@ -187,7 +171,6 @@ def test_resize(self): x = ttk.LabeledScale(self.root) x.pack(expand=True, fill='both') - x.wait_visibility() x.update() width, height = x.master.winfo_width(), x.master.winfo_height() @@ -268,7 +251,6 @@ # check that variable is updated correctly optmenu.pack() - optmenu.wait_visibility() optmenu['menu'].invoke(0) self.assertEqual(optmenu._variable.get(), items[0]) @@ -299,9 +281,7 @@ textvar2 = tkinter.StringVar(self.root) optmenu2 = ttk.OptionMenu(self.root, textvar2, default, *items) optmenu.pack() - optmenu.wait_visibility() optmenu2.pack() - optmenu2.wait_visibility() optmenu['menu'].invoke(1) optmenu2['menu'].invoke(2) optmenu_stringvar_name = optmenu['menu'].entrycget(0, 'variable') @@ -317,7 +297,13 @@ optmenu2.destroy() -tests_gui = (LabeledScaleTest, OptionMenuTest) +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_labeledscale(self): + self._test_widget(ttk.LabeledScale) + + +tests_gui = (LabeledScaleTest, OptionMenuTest, DefaultRootTest) if __name__ == "__main__": run_unittest(*tests_gui) diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_ttk/test_functions.py python3.8-3.8.7/Lib/tkinter/test/test_ttk/test_functions.py --- python3.8-3.8.6/Lib/tkinter/test/test_ttk/test_functions.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_ttk/test_functions.py 2020-12-21 16:25:24.000000000 +0000 @@ -137,6 +137,9 @@ result = ttk._format_mapdict(opts) self.assertEqual(result, ('-üñíćódè', 'á vãl')) + self.assertEqual(ttk._format_mapdict({'opt': [('value',)]}), + ('-opt', '{} value')) + # empty states valid = {'opt': [('', '', 'hi')]} self.assertEqual(ttk._format_mapdict(valid), ('-opt', '{ } hi')) @@ -159,10 +162,6 @@ opts = {'a': None} self.assertRaises(TypeError, ttk._format_mapdict, opts) - # items in the value must have size >= 2 - self.assertRaises(IndexError, ttk._format_mapdict, - {'a': [('invalid', )]}) - def test_format_elemcreate(self): self.assertTrue(ttk._format_elemcreate(None), (None, ())) diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_ttk/test_style.py python3.8-3.8.7/Lib/tkinter/test/test_ttk/test_style.py --- python3.8-3.8.6/Lib/tkinter/test/test_ttk/test_style.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_ttk/test_style.py 2020-12-21 16:25:24.000000000 +0000 @@ -1,11 +1,23 @@ import unittest +import sys import tkinter from tkinter import ttk +from test import support from test.support import requires, run_unittest from tkinter.test.support import AbstractTkTest requires('gui') +CLASS_NAMES = [ + '.', 'ComboboxPopdownFrame', 'Heading', + 'Horizontal.TProgressbar', 'Horizontal.TScale', 'Item', 'Sash', + 'TButton', 'TCheckbutton', 'TCombobox', 'TEntry', + 'TLabelframe', 'TLabelframe.Label', 'TMenubutton', + 'TNotebook', 'TNotebook.Tab', 'Toolbutton', 'TProgressbar', + 'TRadiobutton', 'Treeview', 'TScale', 'TScrollbar', 'TSpinbox', + 'Vertical.TProgressbar', 'Vertical.TScale' +] + class StyleTest(AbstractTkTest, unittest.TestCase): def setUp(self): @@ -23,11 +35,36 @@ def test_map(self): style = self.style - style.map('TButton', background=[('active', 'background', 'blue')]) - self.assertEqual(style.map('TButton', 'background'), - [('active', 'background', 'blue')] if self.wantobjects else - [('active background', 'blue')]) - self.assertIsInstance(style.map('TButton'), dict) + + # Single state + for states in ['active'], [('active',)]: + with self.subTest(states=states): + style.map('TButton', background=[(*states, 'white')]) + expected = [('active', 'white')] + self.assertEqual(style.map('TButton', 'background'), expected) + m = style.map('TButton') + self.assertIsInstance(m, dict) + self.assertEqual(m['background'], expected) + + # Multiple states + for states in ['pressed', '!disabled'], ['pressed !disabled'], [('pressed', '!disabled')]: + with self.subTest(states=states): + style.map('TButton', background=[(*states, 'black')]) + expected = [('pressed', '!disabled', 'black')] + self.assertEqual(style.map('TButton', 'background'), expected) + m = style.map('TButton') + self.assertIsInstance(m, dict) + self.assertEqual(m['background'], expected) + + # Default state + for states in [], [''], [()]: + with self.subTest(states=states): + style.map('TButton', background=[(*states, 'grey')]) + expected = [('grey',)] + self.assertEqual(style.map('TButton', 'background'), expected) + m = style.map('TButton') + self.assertIsInstance(m, dict) + self.assertEqual(m['background'], expected) def test_lookup(self): @@ -86,6 +123,58 @@ self.style.theme_use(curr_theme) + def test_configure_custom_copy(self): + style = self.style + + curr_theme = self.style.theme_use() + self.addCleanup(self.style.theme_use, curr_theme) + for theme in self.style.theme_names(): + self.style.theme_use(theme) + for name in CLASS_NAMES: + default = style.configure(name) + if not default: + continue + with self.subTest(theme=theme, name=name): + if support.verbose >= 2: + print('configure', theme, name, default) + if (theme in ('vista', 'xpnative') + and sys.getwindowsversion()[:2] == (6, 1)): + # Fails on the Windows 7 buildbot + continue + newname = f'C.{name}' + self.assertEqual(style.configure(newname), None) + style.configure(newname, **default) + self.assertEqual(style.configure(newname), default) + for key, value in default.items(): + self.assertEqual(style.configure(newname, key), value) + + + def test_map_custom_copy(self): + style = self.style + + curr_theme = self.style.theme_use() + self.addCleanup(self.style.theme_use, curr_theme) + for theme in self.style.theme_names(): + self.style.theme_use(theme) + for name in CLASS_NAMES: + default = style.map(name) + if not default: + continue + with self.subTest(theme=theme, name=name): + if support.verbose >= 2: + print('map', theme, name, default) + if (theme in ('vista', 'xpnative') + and sys.getwindowsversion()[:2] == (6, 1)): + # Fails on the Windows 7 buildbot + continue + newname = f'C.{name}' + self.assertEqual(style.map(newname), {}) + style.map(newname, **default) + self.assertEqual(style.map(newname), default) + for key, value in default.items(): + self.assertEqual(style.map(newname, key), value) + + tests_gui = (StyleTest, ) if __name__ == "__main__": diff -Nru python3.8-3.8.6/Lib/tkinter/test/test_ttk/test_widgets.py python3.8-3.8.7/Lib/tkinter/test/test_ttk/test_widgets.py --- python3.8-3.8.6/Lib/tkinter/test/test_ttk/test_widgets.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/test/test_ttk/test_widgets.py 2020-12-21 16:25:24.000000000 +0000 @@ -6,7 +6,7 @@ from tkinter.test.test_ttk.test_functions import MockTclObj from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel, - simulate_mouse_click) + simulate_mouse_click, AbstractDefaultRootTest) from tkinter.test.widget_tests import (add_standard_options, noconv, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, setUpModule) @@ -60,11 +60,10 @@ super().setUp() self.widget = ttk.Button(self.root, width=0, text="Text") self.widget.pack() - self.widget.wait_visibility() def test_identify(self): - self.widget.update_idletasks() + self.widget.update() self.assertEqual(self.widget.identify( int(self.widget.winfo_width() / 2), int(self.widget.winfo_height() / 2) @@ -326,8 +325,7 @@ def test_identify(self): self.entry.pack() - self.entry.wait_visibility() - self.entry.update_idletasks() + self.entry.update() # bpo-27313: macOS Cocoa widget differs from X, allow either if sys.platform == 'darwin': @@ -437,11 +435,12 @@ def _show_drop_down_listbox(self): width = self.combo.winfo_width() - self.combo.event_generate('', x=width - 5, y=5) - self.combo.event_generate('', x=width - 5, y=5) + x, y = width - 5, 5 + self.assertRegex(self.combo.identify(x, y), r'.*downarrow\Z') + self.combo.event_generate('', x=x, y=y) + self.combo.event_generate('', x=x, y=y) self.combo.update_idletasks() - def test_virtual_event(self): success = [] @@ -449,7 +448,7 @@ self.combo.bind('<>', lambda evt: success.append(True)) self.combo.pack() - self.combo.wait_visibility() + self.combo.update() height = self.combo.winfo_height() self._show_drop_down_listbox() @@ -465,7 +464,7 @@ self.combo['postcommand'] = lambda: success.append(True) self.combo.pack() - self.combo.wait_visibility() + self.combo.update() self._show_drop_down_listbox() self.assertTrue(success) @@ -665,7 +664,6 @@ self.assertRaises(tkinter.TclError, self.paned.sashpos, 1) self.paned.pack(expand=True, fill='both') - self.paned.wait_visibility() curr_pos = self.paned.sashpos(0) self.paned.sashpos(0, 1000) @@ -933,7 +931,7 @@ self.nb.add(self.child1, text='a') self.nb.pack() - self.nb.wait_visibility() + self.nb.update() if sys.platform == 'darwin': tb_idx = "@20,5" else: @@ -1041,7 +1039,7 @@ def test_select(self): self.nb.pack() - self.nb.wait_visibility() + self.nb.update() success = [] tab_changed = [] @@ -1084,10 +1082,11 @@ def test_traversal(self): self.nb.pack() - self.nb.wait_visibility() + self.nb.update() self.nb.select(0) + self.assertEqual(self.nb.identify(5, 5), 'focus') simulate_mouse_click(self.nb, 5, 5) self.nb.focus_force() self.nb.event_generate('') @@ -1102,6 +1101,7 @@ self.nb.tab(self.child1, text='a', underline=0) self.nb.enable_traversal() self.nb.focus_force() + self.assertEqual(self.nb.identify(5, 5), 'focus') simulate_mouse_click(self.nb, 5, 5) if sys.platform == 'darwin': self.nb.event_generate('') @@ -1132,6 +1132,7 @@ height = self.spin.winfo_height() x = width - 5 y = height//2 - 5 + self.assertRegex(self.spin.identify(x, y), r'.*uparrow\Z') self.spin.event_generate('', x=x, y=y) self.spin.event_generate('', x=x, y=y) self.spin.update_idletasks() @@ -1141,6 +1142,7 @@ height = self.spin.winfo_height() x = width - 5 y = height//2 + 4 + self.assertRegex(self.spin.identify(x, y), r'.*downarrow\Z') self.spin.event_generate('', x=x, y=y) self.spin.event_generate('', x=x, y=y) self.spin.update_idletasks() @@ -1342,7 +1344,6 @@ def test_bbox(self): self.tv.pack() self.assertEqual(self.tv.bbox(''), '') - self.tv.wait_visibility() self.tv.update() item_id = self.tv.insert('', 'end') @@ -1530,13 +1531,15 @@ def test_heading_callback(self): def simulate_heading_click(x, y): + if tcl_version >= (8, 6): + self.assertEqual(self.tv.identify_column(x), '#0') + self.assertEqual(self.tv.identify_region(x, y), 'heading') simulate_mouse_click(self.tv, x, y) self.tv.update() success = [] # no success for now self.tv.pack() - self.tv.wait_visibility() self.tv.heading('#0', command=lambda: success.append(True)) self.tv.column('#0', width=100) self.tv.update() @@ -1784,7 +1787,6 @@ lambda evt: events.append(2)) self.tv.pack() - self.tv.wait_visibility() self.tv.update() pos_y = set() @@ -1858,12 +1860,22 @@ def create(self, **kwargs): return ttk.Sizegrip(self.root, **kwargs) + +class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): + + def test_frame(self): + self._test_widget(ttk.Frame) + + def test_label(self): + self._test_widget(ttk.Label) + + tests_gui = ( ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest, FrameTest, LabelFrameTest, LabelTest, MenubuttonTest, NotebookTest, PanedWindowTest, ProgressbarTest, RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest, - SizegripTest, SpinboxTest, TreeviewTest, WidgetTest, + SizegripTest, SpinboxTest, TreeviewTest, WidgetTest, DefaultRootTest, ) if __name__ == "__main__": diff -Nru python3.8-3.8.6/Lib/tkinter/tix.py python3.8-3.8.7/Lib/tkinter/tix.py --- python3.8-3.8.6/Lib/tkinter/tix.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/tix.py 2020-12-21 16:25:24.000000000 +0000 @@ -387,9 +387,7 @@ # These are missing from Tkinter def image_create(self, imgtype, cnf={}, master=None, **kw): if not master: - master = tkinter._default_root - if not master: - raise RuntimeError('Too early to create image') + master = self if kw and cnf: cnf = _cnfmerge((cnf, kw)) elif kw: cnf = kw options = () @@ -475,10 +473,7 @@ elif 'refwindow' in cnf: master = cnf['refwindow'] else: - master = tkinter._default_root - if not master: - raise RuntimeError("Too early to create display style: " - "no root window") + master = tkinter._get_default_root('create display style') self.tk = master.tk self.stylename = self.tk.call('tixDisplayStyle', itemtype, *self._options(cnf,kw) ) diff -Nru python3.8-3.8.6/Lib/tkinter/ttk.py python3.8-3.8.7/Lib/tkinter/ttk.py --- python3.8-3.8.6/Lib/tkinter/ttk.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/tkinter/ttk.py 2020-12-21 16:25:24.000000000 +0000 @@ -81,8 +81,6 @@ # ['active selected', 'grey', 'focus', [1, 2, 3, 4]] opt_val = [] for *state, val in items: - # hacks for backward compatibility - state[0] # raise IndexError if empty if len(state) == 1: # if it is empty (something that evaluates to False), then # format it to Tcl code to denote the "normal" state @@ -243,19 +241,22 @@ def _list_from_statespec(stuple): """Construct a list from the given statespec tuple according to the accepted statespec accepted by _format_mapdict.""" - nval = [] - for val in stuple: - typename = getattr(val, 'typename', None) - if typename is None: - nval.append(val) - else: # this is a Tcl object + if isinstance(stuple, str): + return stuple + result = [] + it = iter(stuple) + for state, val in zip(it, it): + if hasattr(state, 'typename'): # this is a Tcl object + state = str(state).split() + elif isinstance(state, str): + state = state.split() + elif not isinstance(state, (tuple, list)): + state = (state,) + if hasattr(val, 'typename'): val = str(val) - if typename == 'StateSpec': - val = val.split() - nval.append(val) + result.append((*state, val)) - it = iter(nval) - return [_flatten(spec) for spec in zip(it, it)] + return result def _list_from_layouttuple(tk, ltuple): """Construct a list from the tuple returned by ttk::layout, this is @@ -348,12 +349,7 @@ If it is not allowed to use the default root and master is None, RuntimeError is raised.""" if master is None: - if tkinter._support_default_root: - master = tkinter._default_root or tkinter.Tk() - else: - raise RuntimeError( - "No master specified and tkinter is " - "configured to not support default root") + master = tkinter._get_default_root() return master @@ -395,13 +391,12 @@ or something else of your preference. A statespec is compound of one or more states and then a value.""" if query_opt is not None: - return _list_from_statespec(self.tk.splitlist( - self.tk.call(self._name, "map", style, '-%s' % query_opt))) + result = self.tk.call(self._name, "map", style, '-%s' % query_opt) + return _list_from_statespec(self.tk.splitlist(result)) - return _splitdict( - self.tk, - self.tk.call(self._name, "map", style, *_format_mapdict(kw)), - conv=_tclobj_to_py) + result = self.tk.call(self._name, "map", style, *_format_mapdict(kw)) + return {k: _list_from_statespec(self.tk.splitlist(v)) + for k, v in _splitdict(self.tk, result).items()} def lookup(self, style, option, state=None, default=None): @@ -1538,7 +1533,10 @@ scale_side = 'bottom' if self._label_top else 'top' label_side = 'top' if scale_side == 'bottom' else 'bottom' self.scale.pack(side=scale_side, fill='x') - tmp = Label(self).pack(side=label_side) # place holder + # Dummy required to make frame correct height + dummy = Label(self) + dummy.pack(side=label_side) + dummy.lower() self.label.place(anchor='n' if label_side == 'top' else 's') # update the label as scale or variable changes diff -Nru python3.8-3.8.6/Lib/traceback.py python3.8-3.8.7/Lib/traceback.py --- python3.8-3.8.6/Lib/traceback.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/traceback.py 2020-12-21 16:25:24.000000000 +0000 @@ -500,7 +500,6 @@ _seen=_seen) else: context = None - self.exc_traceback = exc_traceback self.__cause__ = cause self.__context__ = context self.__suppress_context__ = \ @@ -608,7 +607,7 @@ not self.__suppress_context__): yield from self.__context__.format(chain=chain) yield _context_message - if self.exc_traceback is not None: + if self.stack: yield 'Traceback (most recent call last):\n' - yield from self.stack.format() + yield from self.stack.format() yield from self.format_exception_only() diff -Nru python3.8-3.8.6/Lib/unittest/async_case.py python3.8-3.8.7/Lib/unittest/async_case.py --- python3.8-3.8.6/Lib/unittest/async_case.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/unittest/async_case.py 2020-12-21 16:25:24.000000000 +0000 @@ -102,9 +102,9 @@ ret = await awaitable if not fut.cancelled(): fut.set_result(ret) - except asyncio.CancelledError: + except (SystemExit, KeyboardInterrupt): raise - except Exception as ex: + except (BaseException, asyncio.CancelledError) as ex: if not fut.cancelled(): fut.set_exception(ex) diff -Nru python3.8-3.8.6/Lib/unittest/test/test_async_case.py python3.8-3.8.7/Lib/unittest/test/test_async_case.py --- python3.8-3.8.6/Lib/unittest/test/test_async_case.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/unittest/test/test_async_case.py 2020-12-21 16:25:24.000000000 +0000 @@ -190,6 +190,33 @@ 'async_cleanup 2', 'sync_cleanup 1']) + def test_base_exception_from_async_method(self): + events = [] + class Test(unittest.IsolatedAsyncioTestCase): + async def test_base(self): + events.append("test_base") + raise BaseException() + events.append("not it") + + async def test_no_err(self): + events.append("test_no_err") + + async def test_cancel(self): + raise asyncio.CancelledError() + + test = Test("test_base") + output = test.run() + self.assertFalse(output.wasSuccessful()) + + test = Test("test_no_err") + test.run() + self.assertEqual(events, ['test_base', 'test_no_err']) + + test = Test("test_cancel") + output = test.run() + self.assertFalse(output.wasSuccessful()) + + if __name__ == "__main__": unittest.main() diff -Nru python3.8-3.8.6/Lib/urllib/parse.py python3.8-3.8.7/Lib/urllib/parse.py --- python3.8-3.8.6/Lib/urllib/parse.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/urllib/parse.py 2020-12-21 16:25:24.000000000 +0000 @@ -631,6 +631,8 @@ unquote('abc%20def') -> 'abc def'. """ + if isinstance(string, bytes): + raise TypeError('Expected str, got bytes') if '%' not in string: string.split return string diff -Nru python3.8-3.8.6/Lib/urllib/request.py python3.8-3.8.7/Lib/urllib/request.py --- python3.8-3.8.6/Lib/urllib/request.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/urllib/request.py 2020-12-21 16:25:24.000000000 +0000 @@ -2604,6 +2604,11 @@ mask = 8 * (m.group(1).count('.') + 1) else: mask = int(mask[1:]) + + if mask < 0 or mask > 32: + # System libraries ignore invalid prefix lengths + continue + mask = 32 - mask if (hostIP >> mask) == (base >> mask): diff -Nru python3.8-3.8.6/Lib/webbrowser.py python3.8-3.8.7/Lib/webbrowser.py --- python3.8-3.8.6/Lib/webbrowser.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/webbrowser.py 2020-12-21 16:25:24.000000000 +0000 @@ -550,7 +550,7 @@ cmd = "xdg-settings get default-web-browser".split() raw_result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) result = raw_result.decode().strip() - except (FileNotFoundError, subprocess.CalledProcessError, PermissionError) : + except (FileNotFoundError, subprocess.CalledProcessError, PermissionError, NotADirectoryError) : pass else: global _os_preferred_browser diff -Nru python3.8-3.8.6/Lib/xml/etree/ElementTree.py python3.8-3.8.7/Lib/xml/etree/ElementTree.py --- python3.8-3.8.6/Lib/xml/etree/ElementTree.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/xml/etree/ElementTree.py 2020-12-21 16:25:24.000000000 +0000 @@ -1849,6 +1849,11 @@ self._declared_ns_stack[-1].append((uri, prefix)) return f'{prefix}:{tag}' if prefix else tag, tag, uri + if not uri: + # As soon as a default namespace is defined, + # anything that has no namespace (and thus, no prefix) goes there. + return tag, tag, uri + raise ValueError(f'Namespace "{uri}" is not declared in scope') def data(self, data): diff -Nru python3.8-3.8.6/Lib/zipfile.py python3.8-3.8.7/Lib/zipfile.py --- python3.8-3.8.6/Lib/zipfile.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Lib/zipfile.py 2020-12-21 16:25:24.000000000 +0000 @@ -1942,6 +1942,8 @@ centDirSize, centDirOffset, len(self._comment)) self.fp.write(endrec) self.fp.write(self._comment) + if self.mode == "a": + self.fp.truncate() self.fp.flush() def _fpclose(self, fp): diff -Nru python3.8-3.8.6/Mac/BuildScript/build-installer.py python3.8-3.8.7/Mac/BuildScript/build-installer.py --- python3.8-3.8.6/Mac/BuildScript/build-installer.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Mac/BuildScript/build-installer.py 2020-12-21 16:25:24.000000000 +0000 @@ -307,9 +307,9 @@ ), ), dict( - name="SQLite 3.32.3", - url="https://sqlite.org/2020/sqlite-autoconf-3320300.tar.gz", - checksum='2e3911a3c15e85c2f2d040154bbe5ce3', + name="SQLite 3.33.0", + url="https://sqlite.org/2020/sqlite-autoconf-3330000.tar.gz", + checksum='842a8a100d7b01b09e543deb2b7951dd', extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS4 ' diff -Nru python3.8-3.8.6/Misc/ACKS python3.8-3.8.7/Misc/ACKS --- python3.8-3.8.6/Misc/ACKS 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Misc/ACKS 2020-12-21 16:25:24.000000000 +0000 @@ -67,6 +67,7 @@ Emmanuel Arias Alicia Arlen Jeffrey Armstrong +Justin Turner Arthur Jason Asbahr David Ascher Ammar Askar @@ -186,6 +187,7 @@ Carl Friedrich Bolz-Tereick Forest Bond Gregory Bond +Angelin Booz Médéric Boquien Matias Bordese Jonas Borgström @@ -236,6 +238,7 @@ Erik de Bueger Jan-Hein Bührman Lars Buitinck +Artem Bulgakov Dick Bulterman Bill Bumgarner Jimmy Burgett @@ -366,6 +369,7 @@ Hakan Celik Jason Curtis Paul Dagnelie +Florian Dahlitz Lisandro Dalcin Darren Dale Andrew Dalke @@ -759,6 +763,7 @@ Peter Ingebretson Tony Ingraldi John Interrante +Dean Inwood Bob Ippolito Roger Irwin Atsuo Ishimoto @@ -1787,6 +1792,7 @@ Barry Warsaw Steve Waterbury Bob Watson +Colin Watson David Watson Aaron Watters Henrik Weber diff -Nru python3.8-3.8.6/Misc/NEWS python3.8-3.8.7/Misc/NEWS --- python3.8-3.8.6/Misc/NEWS 2020-09-23 13:54:27.000000000 +0000 +++ python3.8-3.8.7/Misc/NEWS 2020-12-21 17:18:55.000000000 +0000 @@ -2,6 +2,378 @@ Python News +++++++++++ +What's New in Python 3.8.7 final? +================================= + +*Release date: 2020-12-21* + +Core and Builtins +----------------- + +- bpo-32381: Fix encoding name when running a ``.pyc`` file on Windows: + :c:func:`PyRun_SimpleFileExFlags()` now uses the correct encoding to + decode the filename. + +- bpo-42536: Several built-in and standard library types now ensure that + their internal result tuples are always tracked by the :term:`garbage + collector `: + + - :meth:`collections.OrderedDict.items() ` + + - :meth:`dict.items` + + - :func:`enumerate` + + - :func:`functools.reduce` + + - :func:`itertools.combinations` + + - :func:`itertools.combinations_with_replacement` + + - :func:`itertools.permutations` + + - :func:`itertools.product` + + - :func:`itertools.zip_longest` + + - :func:`zip` + + Previously, they could have become untracked by a prior garbage + collection. Patch by Brandt Bucher. + +Library +------- + +- bpo-42630: :mod:`tkinter` functions and constructors which need a default + root window raise now :exc:`RuntimeError` with descriptive message instead + of obscure :exc:`AttributeError` or :exc:`NameError` if it is not created + yet or cannot be created automatically. + +- bpo-42644: `logging.disable` will now validate the types and value of its + parameter. It also now accepts strings representing the levels (as does + `loging.setLevel`) instead of only the numerical values. + +- bpo-36541: Fixed lib2to3.pgen2 to be able to parse PEP-570 positional only + argument syntax. + +- bpo-42375: subprocess module update for DragonFlyBSD support. + +- bpo-39825: Windows: Change ``sysconfig.get_config_var('EXT_SUFFIX')`` to + the expected full ``platform_tag.extension`` format. Previously it was + hard-coded to ``.pyd``, now it is compatible with ``distutils.sysconfig`` + and will result in something like ``.cp38-win_amd64.pyd``. This brings + windows into conformance with the other platforms. + +- bpo-39101: Fixed tests using IsolatedAsyncioTestCase from hanging on + BaseExceptions. + +- bpo-41907: fix `format()` behavior for `IntFlag` + +- bpo-41889: Enum: fix regression involving inheriting a multiply-inherited + enum + +- bpo-41891: Ensure asyncio.wait_for waits for task completion + +- bpo-40219: Lowered :class:`tkinter.ttk.LabeledScale` dummy widget to + prevent hiding part of the content label. + +- bpo-40084: Fix ``Enum.__dir__``: dir(Enum.member) now includes attributes + as well as methods. + +Documentation +------------- + +- bpo-17140: Add documentation for the + :class:`multiprocessing.pool.ThreadPool` class. + +Build +----- + +- bpo-42604: Now all platforms use a value for the "EXT_SUFFIX" build + variable derived from SOABI (for instance in freeBSD, "EXT_SUFFIX" is now + ".cpython-310d.so" instead of ".so"). Previosuly only Linux, Mac and + VxWorks were using a value for "EXT_SUFFIX" that included "SOABI". + +- bpo-42598: Fix implicit function declarations in configure which could + have resulted in incorrect configuration checks. Patch contributed by + Joshua Root. + +Tools/Demos +----------- + +- bpo-42613: Fix ``freeze.py`` tool to use the prope config and library + directories. Patch by Victor Stinner. + + +What's New in Python 3.8.7 release candidate 1? +=============================================== + +*Release date: 2020-12-07* + +Security +-------- + +- bpo-42103: Prevented potential DoS attack via CPU and RAM exhaustion when + processing malformed Apple Property List files in binary format. + +- bpo-42051: The :mod:`plistlib` module no longer accepts entity + declarations in XML plist files to avoid XML vulnerabilities. This should + not affect users as entity declarations are not used in regular plist + files. + +- bpo-40791: Add ``volatile`` to the accumulator variable in + ``hmac.compare_digest``, making constant-time-defeating optimizations less + likely. + +Core and Builtins +----------------- + +- bpo-41686: On Windows, the ``SIGINT`` event, ``_PyOS_SigintEvent()``, is + now created even if Python is configured to not install signal handlers + (if :c:member:`PyConfig.install_signal_handlers` equals to 0, or + ``Py_InitializeEx(0)``). + +- bpo-42143: Fix handling of errors during creation of ``PyFunctionObject``, + which resulted in operations on uninitialized memory. Patch by Yonatan + Goldschmidt. + +- bpo-41984: The garbage collector now tracks all user-defined classes. + Patch by Brandt Bucher. + +- bpo-41909: Fixed stack overflow in :func:`issubclass` and + :func:`isinstance` when getting the ``__bases__`` attribute leads to + infinite recursion. + +- bpo-41894: When loading a native module and a load failure occurs, prevent + a possible UnicodeDecodeError when not running in a UTF-8 locale by + decoding the load error message using the current locale's encoding. + +Library +------- + +- bpo-17735: :func:`inspect.findsource` now raises :exc:`OSError` instead of + :exc:`IndexError` when :attr:`co_lineno` of a code object is greater than + the file length. This can happen, for example, when a file is edited after + it was imported. PR by Irit Katriel. + +- bpo-42116: Fix handling of trailing comments by :func:`inspect.getsource`. + +- bpo-42482: :class:`~traceback.TracebackException` no longer holds a + reference to the exception's traceback object. Consequently, instances of + TracebackException for equivalent but non-equal exceptions now compare as + equal. + +- bpo-42406: We fixed an issue in `pickle.whichmodule` in which importing + `multiprocessing` could change the how pickle identifies which module an + object belongs to, potentially breaking the unpickling of those objects. + +- bpo-42328: Fixed :meth:`tkinter.ttk.Style.map`. The function accepts now + the representation of the default state as empty sequence (as returned by + ``Style.map()``). The structure of the result is now the same on all + platform and does not depend on the value of ``wantobjects``. + +- bpo-42014: The ``onerror`` callback from ``shutil.rmtree`` now receives + correct function when ``os.open`` fails. + +- bpo-42237: Fix `os.sendfile()` on illumos. + +- bpo-42249: Fixed writing binary Plist files larger than 4 GiB. + +- bpo-35455: On Solaris, :func:`~time.thread_time` is now implemented with + ``gethrvtime()`` because ``clock_gettime(CLOCK_THREAD_CPUTIME_ID)`` is not + always available. Patch by Jakub Kulik. + +- bpo-41754: webbrowser: Ignore *NotADirectoryError* when calling + ``xdg-settings``. + +- bpo-29566: ``binhex.binhex()`` consisently writes macOS 9 line endings. + +- bpo-42183: Fix a stack overflow error for asyncio Task or Future repr(). + + The overflow occurs under some circumstances when a Task or Future + recursively returns itself. + +- bpo-42103: :exc:`~plistlib.InvalidFileException` and :exc:`RecursionError` + are now the only errors caused by loading malformed binary Plist file + (previously ValueError and TypeError could be raised in some specific + cases). + +- bpo-41491: plistlib: fix parsing XML plists with hexadecimal integer + values + +- bpo-32498: Clearer exception message when passing an argument of type + bytes to :func:`urllib.parse.unquote`. This is only for 3.8; in 3.9 and + later this function accepts bytes inputs as well. PR by Irit Katriel. + +- bpo-42065: Fix an incorrectly formatted error from + :meth:`_codecs.charmap_decode` when called with a mapped value outside the + range of valid Unicode code points. PR by Max Bernstein. + +- bpo-41966: Fix pickling pure Python :class:`datetime.time` subclasses. + Patch by Dean Inwood. + +- bpo-41976: Fixed a bug that was causing :func:`ctypes.util.find_library` + to return ``None`` when triying to locate a library in an environment when + gcc>=9 is available and ``ldconfig`` is not. Patch by Pablo Galindo + +- bpo-41900: C14N 2.0 serialisation in xml.etree.ElementTree failed for + unprefixed attributes when a default namespace was defined. + +- bpo-41855: In ``importlib.metadata``, fix issue where multiple children + can be returned from ``FastPath.zip_children()``. Backport of + `python-devs/importlib_metadata#117 + `_. + +- bpo-41840: Fix a bug in the :mod:`symtable` module that was causing + module-scope global variables to not be reported as both local and global. + Patch by Pablo Galindo. + +- bpo-41831: ``str()`` for the ``type`` attribute of the ``tkinter.Event`` + object always returns now the numeric code returned by Tk instead of the + name of the event type. + +- bpo-41662: No longer override exceptions raised in ``__len__()`` of a + sequence of parameters in :mod:`sqlite3` with + :exc:`~sqlite3.ProgrammingError`. + +- bpo-41662: Fixed crash when mutate list of parameters during iteration in + :mod:`sqlite3`. + +- bpo-34215: Clarify the error message for + :exc:`asyncio.IncompleteReadError` when ``expected`` is ``None``. + +- bpo-41316: Fix the :mod:`tarfile` module to write only basename of TAR + file to GZIP compression header. + +- bpo-12800: Extracting a symlink from a tarball should succeed and + overwrite the symlink if it already exists. The fix is to remove the + existing file or symlink before extraction. Based on patch by Chris AtLee, + Jeffrey Kintscher, and Senthil Kumaran. + +- bpo-16936: Allow ``ctypes.wintypes`` to be imported on non-Windows + systems. + +- bpo-40592: :func:`shutil.which` now ignores empty entries in + :envvar:`PATHEXT` instead of treating them as a match. + +- bpo-40492: Fix ``--outfile`` for :mod:`cProfile` / :mod:`profile` not + writing the output file in the original directory when the program being + profiled changes the working directory. PR by Anthony Sottile. + +- bpo-40105: ZipFile truncates files to avoid corruption when a shorter + comment is provided in append ("a") mode. Patch by Jan Mazur. + +- bpo-27321: Fixed KeyError exception when flattening an email to a string + attempts to replace a non-existent Content-Transfer-Encoding header. + +- bpo-32793: Fix a duplicated debug message when + :meth:`smtplib.SMTP.connect` is called. + +Documentation +------------- + +- bpo-42153: Fix the URL for the IMAP protocol documents. + +- bpo-41910: Document the default implementation of `object.__eq__`. + +- bpo-41774: In Programming FAQ "Sequences (Tuples/Lists)" section, add "How + do you remove multiple items from a list". + +- bpo-39416: Document some restrictions on the default string + representations of numeric classes. + +Tests +----- + +- bpo-41473: Reenable test_gdb on gdb 9.2 and newer: + https://bugzilla.redhat.com/show_bug.cgi?id=1866884 bug is fixed in gdb + 10.1. + +- bpo-42553: Fix ``test_asyncio.test_call_later()`` race condition: don't + measure asyncio performance in the ``call_later()`` unit test. The test + failed randomly on the CI. + +- bpo-40754: Include ``_testinternalcapi`` module in Windows installer for + test suite + +- bpo-41739: Fix test_logging.test_race_between_set_target_and_flush(): the + test now waits until all threads complete to avoid leaking running + threads. + +- bpo-41944: Tests for CJK codecs no longer call ``eval()`` on content + received via HTTP. + +- bpo-41939: Fix test_site.test_license_exists_at_url(): call + ``urllib.request.urlcleanup()`` to reset the global + ``urllib.request._opener``. Patch by Victor Stinner. + +- bpo-41561: test_ssl: skip test_min_max_version_mismatch when TLS 1.0 is + not available + +- bpo-41602: Add tests for SIGINT handling in the runpy module. + +- bpo-41306: Fixed a failure in ``test_tk.test_widgets.ScaleTest`` happening + when executing the test with Tk 8.6.10. + +Build +----- + +- bpo-42398: Fix a race condition in "make regen-all" when make -jN option + is used to run jobs in parallel. The clinic.py script now only use atomic + write to write files. Moveover, generated files are now left unchanged if + the content does not change, to not change the file modification time. + +Windows +------- + +- bpo-42120: Remove macro definition of ``copysign`` (to ``_copysign``) in + headers. + +- bpo-38439: Updates the icons for IDLE in the Windows Store package. + +- bpo-41557: Update Windows installer to use SQLite 3.33.0. + +- bpo-38324: Avoid Unicode errors when accessing certain locale data on + Windows. + +macOS +----- + +- bpo-38443: The ``--enable-universalsdk`` and ``--with-universal-archs`` + options for the configure script now check that the specified + architectures can be used. + +- bpo-41471: Ignore invalid prefix lengths in system proxy excludes. + +- bpo-41557: Update macOS installer to use SQLite 3.33.0. + +IDLE +---- + +- bpo-42426: Fix reporting offset of the RE error in searchengine. + +- bpo-42415: Get docstrings for IDLE calltips more often by using + inspect.getdoc. + +- bpo-33987: Mostly finish using ttk widgets, mainly for editor, settings, + and searches. Some patches by Mark Roseman. + +- bpo-41775: Use 'IDLE Shell' as shell title + +- bpo-40511: Typing opening and closing parentheses inside the parentheses + of a function call will no longer cause unnecessary "flashing" off and on + of an existing open call-tip, e.g. when typed in a string literal. + +- bpo-38439: Add a 256×256 pixel IDLE icon to the Windows .ico file. Created + by Andrew Clover. Remove the low-color gif variations from the .ico file. + +C API +----- + +- bpo-41986: :c:data:`Py_FileSystemDefaultEncodeErrors` and + :c:data:`Py_UTF8Mode` are available again in limited API. + + What's New in Python 3.8.6 final? ================================= @@ -651,7 +1023,7 @@ - bpo-39871: Fix a possible :exc:`SystemError` in ``math.{atan2,copysign,remainder}()`` when the first argument cannot be - converted to a :class:`float`. Patch by Zachary Spytz. + converted to a :class:`float`. Patch by Zackery Spytz. - bpo-39776: Fix race condition where threads created by PyGILState_Ensure() could get a duplicate id. diff -Nru python3.8-3.8.6/Modules/_ctypes/cfield.c python3.8-3.8.7/Modules/_ctypes/cfield.c --- python3.8-3.8.6/Modules/_ctypes/cfield.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/_ctypes/cfield.c 2020-12-21 16:25:24.000000000 +0000 @@ -695,7 +695,11 @@ return PyLong_FromLong(val); } -#ifdef MS_WIN32 +#ifndef MS_WIN32 +/* http://msdn.microsoft.com/en-us/library/cc237864.aspx */ +#define VARIANT_FALSE 0x0000 +#define VARIANT_TRUE 0xFFFF +#endif /* short BOOL - VARIANT_BOOL */ static PyObject * vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size) @@ -717,7 +721,6 @@ { return PyBool_FromLong((long)*(short int *)ptr); } -#endif static PyObject * bool_set(void *ptr, PyObject *value, Py_ssize_t size) @@ -1544,8 +1547,8 @@ #endif #ifdef MS_WIN32 { 'X', BSTR_set, BSTR_get, &ffi_type_pointer}, - { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort}, #endif + { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort}, #if SIZEOF__BOOL == 1 { '?', bool_set, bool_get, &ffi_type_uchar}, /* Also fallback for no native _Bool support */ #elif SIZEOF__BOOL == SIZEOF_SHORT diff -Nru python3.8-3.8.6/Modules/_datetimemodule.c python3.8-3.8.7/Modules/_datetimemodule.c --- python3.8-3.8.6/Modules/_datetimemodule.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/_datetimemodule.c 2020-12-21 16:25:24.000000000 +0000 @@ -4539,7 +4539,10 @@ {"isoformat", (PyCFunction)(void(*)(void))time_isoformat, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Return string in ISO 8601 format, [HH[:MM[:SS[.mmm[uuu]]]]]" "[+HH:MM].\n\n" - "timespec specifies what components of the time to include.\n")}, + "The optional argument timespec specifies the number " + "of additional terms\nof the time to include. Valid " + "options are 'auto', 'hours', 'minutes',\n'seconds', " + "'milliseconds' and 'microseconds'.\n")}, {"strftime", (PyCFunction)(void(*)(void))time_strftime, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("format -> strftime() style string.")}, @@ -6246,9 +6249,10 @@ "YYYY-MM-DDT[HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM].\n" "sep is used to separate the year from the time, and " "defaults to 'T'.\n" - "timespec specifies what components of the time to include" - " (allowed values are 'auto', 'hours', 'minutes', 'seconds'," - " 'milliseconds', and 'microseconds').\n")}, + "The optional argument timespec specifies the number " + "of additional terms\nof the time to include. Valid " + "options are 'auto', 'hours', 'minutes',\n'seconds', " + "'milliseconds' and 'microseconds'.\n")}, {"utcoffset", (PyCFunction)datetime_utcoffset, METH_NOARGS, PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, diff -Nru python3.8-3.8.6/Modules/_functoolsmodule.c python3.8-3.8.7/Modules/_functoolsmodule.c --- python3.8-3.8.6/Modules/_functoolsmodule.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/_functoolsmodule.c 2020-12-21 16:25:24.000000000 +0000 @@ -3,6 +3,7 @@ #include "pycore_pystate.h" #include "pycore_tupleobject.h" #include "structmember.h" +#include "pycore_object.h" // _PyObject_GC_TRACK /* _functools module written and maintained by Hye-Shik Chang @@ -633,6 +634,11 @@ if ((result = PyObject_Call(func, args, NULL)) == NULL) { goto Fail; } + // bpo-42536: The GC may have untracked this args tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(args)) { + _PyObject_GC_TRACK(args); + } } } diff -Nru python3.8-3.8.6/Modules/itertoolsmodule.c python3.8-3.8.7/Modules/itertoolsmodule.c --- python3.8-3.8.6/Modules/itertoolsmodule.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/itertoolsmodule.c 2020-12-21 16:25:24.000000000 +0000 @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_tupleobject.h" #include "structmember.h" +#include "pycore_object.h" // _PyObject_GC_TRACK() /* Itertools module written and maintained by Raymond D. Hettinger @@ -2255,6 +2256,11 @@ lz->result = result; Py_DECREF(old_result); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + else if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } /* Now, we've got the only copy so we can update it in-place */ assert (npools==0 || Py_REFCNT(result) == 1); @@ -2580,6 +2586,11 @@ co->result = result; Py_DECREF(old_result); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + else if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } /* Now, we've got the only copy so we can update it in-place * CPython's empty tuple is a singleton and cached in * PyTuple's freelist. @@ -2916,6 +2927,11 @@ co->result = result; Py_DECREF(old_result); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + else if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } /* Now, we've got the only copy so we can update it in-place CPython's empty tuple is a singleton and cached in PyTuple's freelist. */ assert(r == 0 || Py_REFCNT(result) == 1); @@ -3259,6 +3275,11 @@ po->result = result; Py_DECREF(old_result); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + else if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } /* Now, we've got the only copy so we can update it in-place */ assert(r == 0 || Py_REFCNT(result) == 1); @@ -4536,6 +4557,11 @@ PyTuple_SET_ITEM(result, i, item); Py_DECREF(olditem); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(tuplesize); if (result == NULL) diff -Nru python3.8-3.8.6/Modules/_localemodule.c python3.8-3.8.7/Modules/_localemodule.c --- python3.8-3.8.6/Modules/_localemodule.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/_localemodule.c 2020-12-21 16:25:24.000000000 +0000 @@ -131,6 +131,7 @@ static int locale_decode_monetary(PyObject *dict, struct lconv *lc) { +#ifndef MS_WINDOWS int change_locale; change_locale = (!locale_is_ascii(lc->int_curr_symbol) || !locale_is_ascii(lc->currency_symbol) @@ -166,12 +167,18 @@ } } +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#else /* MS_WINDOWS */ +/* Use _W_* fields of Windows struct lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#endif /* MS_WINDOWS */ + int res = -1; #define RESULT_STRING(ATTR) \ do { \ PyObject *obj; \ - obj = PyUnicode_DecodeLocale(lc->ATTR, NULL); \ + obj = GET_LOCALE_STRING(ATTR); \ if (obj == NULL) { \ goto done; \ } \ @@ -187,14 +194,17 @@ RESULT_STRING(mon_decimal_point); RESULT_STRING(mon_thousands_sep); #undef RESULT_STRING +#undef GET_LOCALE_STRING res = 0; done: +#ifndef MS_WINDOWS if (loc != NULL) { setlocale(LC_CTYPE, oldloc); } PyMem_Free(oldloc); +#endif return res; } @@ -230,9 +240,15 @@ Py_DECREF(obj); \ } while (0) +#ifdef MS_WINDOWS +/* Use _W_* fields of Windows struct lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#else +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#endif #define RESULT_STRING(s)\ do { \ - x = PyUnicode_DecodeLocale(lc->s, NULL); \ + x = GET_LOCALE_STRING(s); \ RESULT(#s, x); \ } while (0) @@ -261,8 +277,10 @@ RESULT_INT(n_sign_posn); /* Numeric information: LC_NUMERIC encoding */ - PyObject *decimal_point, *thousands_sep; + PyObject *decimal_point = NULL, *thousands_sep = NULL; if (_Py_GetLocaleconvNumeric(lc, &decimal_point, &thousands_sep) < 0) { + Py_XDECREF(decimal_point); + Py_XDECREF(thousands_sep); goto failed; } @@ -291,6 +309,7 @@ #undef RESULT #undef RESULT_STRING #undef RESULT_INT +#undef GET_LOCALE_STRING } #if defined(HAVE_WCSCOLL) diff -Nru python3.8-3.8.6/Modules/main.c python3.8-3.8.7/Modules/main.c --- python3.8-3.8.6/Modules/main.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/main.c 2020-12-21 16:25:24.000000000 +0000 @@ -299,7 +299,11 @@ Py_DECREF(module); return pymain_exit_err_print(); } + _Py_UnhandledKeyboardInterrupt = 0; result = PyObject_Call(runmodule, runargs, NULL); + if (!result && PyErr_Occurred() == PyExc_KeyboardInterrupt) { + _Py_UnhandledKeyboardInterrupt = 1; + } Py_DECREF(runpy); Py_DECREF(runmodule); Py_DECREF(module); diff -Nru python3.8-3.8.6/Modules/_operator.c python3.8-3.8.7/Modules/_operator.c --- python3.8-3.8.6/Modules/_operator.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/_operator.c 2020-12-21 16:25:24.000000000 +0000 @@ -735,7 +735,7 @@ volatile const unsigned char *left; volatile const unsigned char *right; Py_ssize_t i; - unsigned char result; + volatile unsigned char result; /* loop count depends on length of b */ length = len_b; diff -Nru python3.8-3.8.6/Modules/posixmodule.c python3.8-3.8.7/Modules/posixmodule.c --- python3.8-3.8.6/Modules/posixmodule.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/posixmodule.c 2020-12-21 16:25:24.000000000 +0000 @@ -9281,9 +9281,27 @@ if (!Py_off_t_converter(offobj, &offset)) return NULL; + +#if defined(__sun) && defined(__SVR4) + // On illumos specifically sendfile() may perform a partial write but + // return -1/an error (in one confirmed case the destination socket + // had a 5 second timeout set and errno was EAGAIN) and it's on the client + // code to check if the offset parameter was modified by sendfile(). + // + // We need this variable to track said change. + off_t original_offset = offset; +#endif + do { Py_BEGIN_ALLOW_THREADS ret = sendfile(out, in, &offset, count); +#if defined(__sun) && defined(__SVR4) + // This handles illumos-specific sendfile() partial write behavior, + // see a comment above for more details. + if (ret < 0 && offset != original_offset) { + ret = offset - original_offset; + } +#endif Py_END_ALLOW_THREADS } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); if (ret < 0) diff -Nru python3.8-3.8.6/Modules/_posixsubprocess.c python3.8-3.8.7/Modules/_posixsubprocess.c --- python3.8-3.8.6/Modules/_posixsubprocess.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/_posixsubprocess.c 2020-12-21 16:25:24.000000000 +0000 @@ -41,7 +41,7 @@ # endif #endif -#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)) +#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__DragonFly__) # define FD_DIR "/dev/fd" #else # define FD_DIR "/proc/self/fd" @@ -88,9 +88,9 @@ } -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__DragonFly__) /* When /dev/fd isn't mounted it is often a static directory populated - * with 0 1 2 or entries for 0 .. 63 on FreeBSD, NetBSD and OpenBSD. + * with 0 1 2 or entries for 0 .. 63 on FreeBSD, NetBSD, OpenBSD and DragonFlyBSD. * NetBSD and OpenBSD have a /proc fs available (though not necessarily * mounted) and do not have fdescfs for /dev/fd. MacOS X has a devfs * that properly supports /dev/fd. @@ -343,7 +343,7 @@ ++start_fd; #endif -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__DragonFly__) if (!_is_fdescfs_mounted_on_dev_fd()) proc_fd_dir = NULL; else diff -Nru python3.8-3.8.6/Modules/signalmodule.c python3.8-3.8.7/Modules/signalmodule.c --- python3.8-3.8.6/Modules/signalmodule.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/signalmodule.c 2020-12-21 16:25:24.000000000 +0000 @@ -1598,11 +1598,6 @@ goto finally; #endif -#ifdef MS_WINDOWS - /* Create manual-reset event, initially unset */ - sigint_event = CreateEvent(NULL, TRUE, FALSE, FALSE); -#endif - if (PyErr_Occurred()) { Py_DECREF(m); m = NULL; @@ -1726,6 +1721,53 @@ } } + +static int +signal_install_handlers(void) +{ +#ifdef SIGPIPE + PyOS_setsig(SIGPIPE, SIG_IGN); +#endif +#ifdef SIGXFZ + PyOS_setsig(SIGXFZ, SIG_IGN); +#endif +#ifdef SIGXFSZ + PyOS_setsig(SIGXFSZ, SIG_IGN); +#endif + + // Import _signal to install the Python SIGINT handler + PyObject *module = PyImport_ImportModule("_signal"); + if (!module) { + return -1; + } + Py_DECREF(module); + + return 0; +} + + +int +_PySignal_Init(int install_signal_handlers) +{ +#ifdef MS_WINDOWS + /* Create manual-reset event, initially unset */ + sigint_event = CreateEvent(NULL, TRUE, FALSE, FALSE); + if (sigint_event == NULL) { + PyErr_SetFromWindowsErr(0); + return -1; + } +#endif + + if (install_signal_handlers) { + if (signal_install_handlers() < 0) { + return -1; + } + } + + return 0; +} + + void PyOS_FiniInterrupts(void) { diff -Nru python3.8-3.8.6/Modules/_sqlite/statement.c python3.8-3.8.7/Modules/_sqlite/statement.c --- python3.8-3.8.6/Modules/_sqlite/statement.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/_sqlite/statement.c 2020-12-21 16:25:24.000000000 +0000 @@ -225,6 +225,9 @@ num_params = PyList_GET_SIZE(parameters); } else { num_params = PySequence_Size(parameters); + if (num_params == -1) { + return; + } } if (num_params != num_params_needed) { PyErr_Format(pysqlite_ProgrammingError, @@ -236,9 +239,9 @@ for (i = 0; i < num_params; i++) { if (PyTuple_CheckExact(parameters)) { current_param = PyTuple_GET_ITEM(parameters, i); - Py_XINCREF(current_param); + Py_INCREF(current_param); } else if (PyList_CheckExact(parameters)) { - current_param = PyList_GET_ITEM(parameters, i); + current_param = PyList_GetItem(parameters, i); Py_XINCREF(current_param); } else { current_param = PySequence_GetItem(parameters, i); diff -Nru python3.8-3.8.6/Modules/_ssl.c python3.8-3.8.7/Modules/_ssl.c --- python3.8-3.8.6/Modules/_ssl.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/_ssl.c 2020-12-21 16:25:24.000000000 +0000 @@ -901,6 +901,7 @@ if (ip == NULL) { if (!SSL_set_tlsext_host_name(self->ssl, server_hostname)) { _setSSLError(NULL, 0, __FILE__, __LINE__); + goto error; } } if (self->ctx->check_hostname) { diff -Nru python3.8-3.8.6/Modules/_testcapimodule.c python3.8-3.8.7/Modules/_testcapimodule.c --- python3.8-3.8.6/Modules/_testcapimodule.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/_testcapimodule.c 2020-12-21 16:25:24.000000000 +0000 @@ -3511,6 +3511,25 @@ return obj; } +static PyObject * +without_gc(PyObject *Py_UNUSED(self), PyObject *obj) +{ + PyTypeObject *tp = (PyTypeObject*)obj; + if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { + return PyErr_Format(PyExc_TypeError, "heap type expected, got %R", obj); + } + if (PyType_IS_GC(tp)) { + // Don't try this at home, kids: + tp->tp_flags -= Py_TPFLAGS_HAVE_GC; + tp->tp_free = PyObject_Del; + tp->tp_traverse = NULL; + tp->tp_clear = NULL; + } + assert(!PyType_IS_GC(tp)); + Py_INCREF(obj); + return obj; +} + static PyMethodDef ml; static PyObject * @@ -5325,6 +5344,7 @@ #endif {"write_unraisable_exc", test_write_unraisable_exc, METH_VARARGS}, {"pynumber_tobase", pynumber_tobase, METH_VARARGS}, + {"without_gc", without_gc, METH_O}, {NULL, NULL} /* sentinel */ }; diff -Nru python3.8-3.8.6/Modules/timemodule.c python3.8-3.8.7/Modules/timemodule.c --- python3.8-3.8.6/Modules/timemodule.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Modules/timemodule.c 2020-12-21 16:25:24.000000000 +0000 @@ -1344,6 +1344,23 @@ return 0; } +#elif defined(__sun) && defined(__SVR4) +#define HAVE_THREAD_TIME +static int +_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) +{ + /* bpo-35455: On Solaris, CLOCK_THREAD_CPUTIME_ID clock is not always + available; use gethrvtime() to substitute this functionality. */ + if (info) { + info->implementation = "gethrvtime()"; + info->resolution = 1e-9; + info->monotonic = 1; + info->adjustable = 0; + } + *tp = _PyTime_FromNanoseconds(gethrvtime()); + return 0; +} + #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) #define HAVE_THREAD_TIME static int diff -Nru python3.8-3.8.6/Objects/abstract.c python3.8-3.8.7/Objects/abstract.c --- python3.8-3.8.6/Objects/abstract.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Objects/abstract.c 2020-12-21 16:25:24.000000000 +0000 @@ -2316,9 +2316,7 @@ _Py_IDENTIFIER(__bases__); PyObject *bases; - Py_ALLOW_RECURSION (void)_PyObject_LookupAttrId(cls, &PyId___bases__, &bases); - Py_END_ALLOW_RECURSION if (bases != NULL && !PyTuple_Check(bases)) { Py_DECREF(bases); return NULL; diff -Nru python3.8-3.8.6/Objects/dictobject.c python3.8-3.8.7/Objects/dictobject.c --- python3.8-3.8.6/Objects/dictobject.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Objects/dictobject.c 2020-12-21 16:25:24.000000000 +0000 @@ -3769,6 +3769,11 @@ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(2); @@ -3884,6 +3889,11 @@ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); + // bpo-42536: The GC may have untracked this result tuple. Since + // we're recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(2); diff -Nru python3.8-3.8.6/Objects/enumobject.c python3.8-3.8.7/Objects/enumobject.c --- python3.8-3.8.6/Objects/enumobject.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Objects/enumobject.c 2020-12-21 16:25:24.000000000 +0000 @@ -1,6 +1,7 @@ /* enumerate object */ #include "Python.h" +#include "pycore_object.h" // _PyObject_GC_TRACK() #include "clinic/enumobject.c.h" @@ -130,6 +131,11 @@ PyTuple_SET_ITEM(result, 1, next_item); Py_DECREF(old_index); Py_DECREF(old_item); + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } return result; } result = PyTuple_New(2); @@ -175,6 +181,11 @@ PyTuple_SET_ITEM(result, 1, next_item); Py_DECREF(old_index); Py_DECREF(old_item); + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } return result; } result = PyTuple_New(2); diff -Nru python3.8-3.8.6/Objects/funcobject.c python3.8-3.8.7/Objects/funcobject.c --- python3.8-3.8.6/Objects/funcobject.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Objects/funcobject.c 2020-12-21 16:25:24.000000000 +0000 @@ -22,9 +22,23 @@ return NULL; } + /* __module__: If module name is in globals, use it. + Otherwise, use None. */ + module = PyDict_GetItemWithError(globals, __name__); + if (module) { + Py_INCREF(module); + } + else if (PyErr_Occurred()) { + return NULL; + } + op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); - if (op == NULL) + if (op == NULL) { + Py_XDECREF(module); return NULL; + } + /* Note: No failures from this point on, since func_dealloc() does not + expect a partially-created object. */ op->func_weakreflist = NULL; Py_INCREF(code); @@ -37,6 +51,7 @@ op->func_kwdefaults = NULL; /* No keyword only defaults */ op->func_closure = NULL; op->vectorcall = _PyFunction_Vectorcall; + op->func_module = module; consts = ((PyCodeObject *)code)->co_consts; if (PyTuple_Size(consts) >= 1) { @@ -50,20 +65,8 @@ op->func_doc = doc; op->func_dict = NULL; - op->func_module = NULL; op->func_annotations = NULL; - /* __module__: If module name is in globals, use it. - Otherwise, use None. */ - module = PyDict_GetItemWithError(globals, __name__); - if (module) { - Py_INCREF(module); - op->func_module = module; - } - else if (PyErr_Occurred()) { - Py_DECREF(op); - return NULL; - } if (qualname) op->func_qualname = qualname; else diff -Nru python3.8-3.8.6/Objects/odictobject.c python3.8-3.8.7/Objects/odictobject.c --- python3.8-3.8.6/Objects/odictobject.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Objects/odictobject.c 2020-12-21 16:25:24.000000000 +0000 @@ -1766,6 +1766,11 @@ Py_INCREF(result); Py_DECREF(PyTuple_GET_ITEM(result, 0)); /* borrowed */ Py_DECREF(PyTuple_GET_ITEM(result, 1)); /* borrowed */ + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(2); diff -Nru python3.8-3.8.6/Objects/typeobject.c python3.8-3.8.7/Objects/typeobject.c --- python3.8-3.8.6/Objects/typeobject.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Objects/typeobject.c 2020-12-21 16:25:24.000000000 +0000 @@ -2577,10 +2577,10 @@ slots = NULL; /* Initialize tp_flags */ + // All heap types need GC, since we can create a reference cycle by storing + // an instance on one of its parents: type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | - Py_TPFLAGS_BASETYPE; - if (base->tp_flags & Py_TPFLAGS_HAVE_GC) - type->tp_flags |= Py_TPFLAGS_HAVE_GC; + Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC; /* Initialize essential fields */ type->tp_as_async = &et->as_async; @@ -2777,21 +2777,11 @@ } type->tp_dealloc = subtype_dealloc; - /* Enable GC unless this class is not adding new instance variables and - the base class did not use GC. */ - if ((base->tp_flags & Py_TPFLAGS_HAVE_GC) || - type->tp_basicsize > base->tp_basicsize) - type->tp_flags |= Py_TPFLAGS_HAVE_GC; - /* Always override allocation strategy to use regular heap */ type->tp_alloc = PyType_GenericAlloc; - if (type->tp_flags & Py_TPFLAGS_HAVE_GC) { - type->tp_free = PyObject_GC_Del; - type->tp_traverse = subtype_traverse; - type->tp_clear = subtype_clear; - } - else - type->tp_free = PyObject_Del; + type->tp_free = PyObject_GC_Del; + type->tp_traverse = subtype_traverse; + type->tp_clear = subtype_clear; /* store type in class' cell if one is supplied */ cell = _PyDict_GetItemIdWithError(dict, &PyId___classcell__); @@ -2904,26 +2894,40 @@ base = slot->pfunc; else if (slot->slot == Py_tp_bases) { bases = slot->pfunc; - Py_INCREF(bases); } } - if (!bases) + if (!bases) { bases = PyTuple_Pack(1, base); - if (!bases) + if (!bases) + goto fail; + } + else if (!PyTuple_Check(bases)) { + PyErr_SetString(PyExc_SystemError, "Py_tp_bases is not a tuple"); goto fail; + } + else { + Py_INCREF(bases); + } } - else + else if (!PyTuple_Check(bases)) { + PyErr_SetString(PyExc_SystemError, "bases is not a tuple"); + goto fail; + } + else { Py_INCREF(bases); + } /* Calculate best base, and check that all bases are type objects */ base = best_base(bases); if (base == NULL) { + Py_DECREF(bases); goto fail; } if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { PyErr_Format(PyExc_TypeError, "type '%.100s' is not an acceptable base type", base->tp_name); + Py_DECREF(bases); goto fail; } @@ -2935,7 +2939,6 @@ type->tp_as_buffer = &res->as_buffer; /* Set tp_base and tp_bases */ type->tp_bases = bases; - bases = NULL; Py_INCREF(base); type->tp_base = base; diff -Nru python3.8-3.8.6/Objects/unicodeobject.c python3.8-3.8.7/Objects/unicodeobject.c --- python3.8-3.8.6/Objects/unicodeobject.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Objects/unicodeobject.c 2020-12-21 16:25:24.000000000 +0000 @@ -7998,7 +7998,7 @@ goto Undefined; if (value < 0 || value > MAX_UNICODE) { PyErr_Format(PyExc_TypeError, - "character mapping must be in range(0x%lx)", + "character mapping must be in range(0x%x)", (unsigned long)MAX_UNICODE + 1); goto onError; } @@ -15285,9 +15285,7 @@ return; } } - Py_ALLOW_RECURSION t = PyDict_SetDefault(interned, s, s); - Py_END_ALLOW_RECURSION if (t == NULL) { PyErr_Clear(); return; Binary files /tmp/tmpgwkoGW/C7ZJ71KE1Z/python3.8-3.8.6/PC/icons/idlex150.png and /tmp/tmpgwkoGW/uSs3611ZLj/python3.8-3.8.7/PC/icons/idlex150.png differ Binary files /tmp/tmpgwkoGW/C7ZJ71KE1Z/python3.8-3.8.6/PC/icons/idlex44.png and /tmp/tmpgwkoGW/uSs3611ZLj/python3.8-3.8.7/PC/icons/idlex44.png differ diff -Nru python3.8-3.8.6/PC/layout/support/appxmanifest.py python3.8-3.8.7/PC/layout/support/appxmanifest.py --- python3.8-3.8.6/PC/layout/support/appxmanifest.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PC/layout/support/appxmanifest.py 2020-12-21 16:25:24.000000000 +0000 @@ -67,8 +67,8 @@ 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", + Square150x150Logo="_resources/idlex150.png", + Square44x44Logo="_resources/idlex44.png", BackgroundColor="transparent", ) @@ -498,6 +498,11 @@ src = icons / "pythonwx{}.png".format(px) yield f"_resources/pythonwx{px}.png", src yield f"_resources/pythonwx{px}$targetsize-{px}_altform-unplated.png", src + if ns.include_idle and ns.include_launchers: + for px in [44, 150]: + src = icons / "idlex{}.png".format(px) + yield f"_resources/idlex{px}.png", src + yield f"_resources/idlex{px}$targetsize-{px}_altform-unplated.png", src yield f"_resources/py.png", icons / "py.png" sccd = ns.source / SCCD_FILENAME if sccd.is_file(): diff -Nru python3.8-3.8.6/PC/pyconfig.h python3.8-3.8.7/PC/pyconfig.h --- python3.8-3.8.6/PC/pyconfig.h 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PC/pyconfig.h 2020-12-21 16:25:24.000000000 +0000 @@ -193,7 +193,6 @@ #define Py_IS_NAN _isnan #define Py_IS_INFINITY(X) (!_finite(X) && !_isnan(X)) #define Py_IS_FINITE(X) _finite(X) -#define copysign _copysign /* Side by Side assemblies supported in VS 2005 and VS 2008 but not 2010*/ #if _MSC_VER >= 1400 && _MSC_VER < 1600 diff -Nru python3.8-3.8.6/PCbuild/env.bat python3.8-3.8.7/PCbuild/env.bat --- python3.8-3.8.6/PCbuild/env.bat 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PCbuild/env.bat 2020-12-21 16:25:24.000000000 +0000 @@ -9,8 +9,19 @@ echo Build environments: x86, amd64, x86_amd64 echo. -set VSTOOLS=%VS140COMNTOOLS% -if "%VSTOOLS%"=="" set VSTOOLS=%VS120COMNTOOLS% -if "%VSTOOLS%"=="" set VSTOOLS=%VS110COMNTOOLS% -if "%VSTOOLS%"=="" set VSTOOLS=%VS100COMNTOOLS% -call "%VSTOOLS%..\..\VC\vcvarsall.bat" %* +set _ARGS=%* +if NOT DEFINED _ARGS set _ARGS=x86 + +if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere +set VSTOOLS= +for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64') DO @(set VSTOOLS=%%i\VC\Auxiliary\Build\vcvarsall.bat) +if not defined VSTOOLS goto :skip_vswhere +call "%VSTOOLS%" %_ARGS% +exit /B 0 + +:skip_vswhere +if not defined VSTOOLS set VSTOOLS=%VS140COMNTOOLS% +if not defined VSTOOLS set VSTOOLS=%VS120COMNTOOLS% +if not defined VSTOOLS set VSTOOLS=%VS110COMNTOOLS% +if not defined VSTOOLS set VSTOOLS=%VS100COMNTOOLS% +call "%VSTOOLS%..\..\VC\vcvarsall.bat" %_ARGS% diff -Nru python3.8-3.8.6/PCbuild/env.ps1 python3.8-3.8.7/PCbuild/env.ps1 --- python3.8-3.8.6/PCbuild/env.ps1 1970-01-01 00:00:00.000000000 +0000 +++ python3.8-3.8.7/PCbuild/env.ps1 2020-12-21 16:25:24.000000000 +0000 @@ -0,0 +1,2 @@ +$pcbuild = $script:MyInvocation.MyCommand.Path | Split-Path -parent; +& cmd /K "$pcbuild\env.bat" $args diff -Nru python3.8-3.8.6/PCbuild/get_externals.bat python3.8-3.8.7/PCbuild/get_externals.bat --- python3.8-3.8.6/PCbuild/get_externals.bat 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PCbuild/get_externals.bat 2020-12-21 16:25:24.000000000 +0000 @@ -54,7 +54,7 @@ set libraries=%libraries% bzip2-1.0.6 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.3.0-rc0-r1 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1g -set libraries=%libraries% sqlite-3.32.3.0 +set libraries=%libraries% sqlite-3.33.0.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 diff -Nru python3.8-3.8.6/PCbuild/idle.bat python3.8-3.8.7/PCbuild/idle.bat --- python3.8-3.8.6/PCbuild/idle.bat 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PCbuild/idle.bat 2020-12-21 16:25:24.000000000 +0000 @@ -4,12 +4,24 @@ rem -d Run Debug build (python_d.exe). Else release build. setlocal -set exe=win32\python +set PCBUILD=%~dp0 +set exedir=%PCBUILD%\win32 +set exe=python PATH %PATH%;..\externals\tcltk\bin -if "%1"=="-d" (set exe=%exe%_d) & shift +:CheckOpts +if "%1"=="-d" (set exe=%exe%_d) & shift & goto :CheckOpts +if "%1"=="-p" (call :SetExeDir %2) & shift & shift & goto :CheckOpts -set cmd=%exe% ../Lib/idlelib/idle.py %1 %2 %3 %4 %5 %6 %7 %8 %9 +set cmd=%exedir%\%exe% %PCBUILD%\..\Lib\idlelib\idle.py %1 %2 %3 %4 %5 %6 %7 %8 %9 echo on %cmd% +exit /B %LASTERRORCODE% + +:SetExeDir +if /I %1 EQU Win32 (set exedir=%PCBUILD%\win32) +if /I %1 EQU x64 (set exedir=%PCBUILD%\amd64) +if /I %1 EQU ARM (set exedir=%PCBUILD%\arm32) +if /I %1 EQU ARM64 (set exedir=%PCBUILD%\arm64) +exit /B 0 diff -Nru python3.8-3.8.6/PCbuild/prepare_libffi.bat python3.8-3.8.7/PCbuild/prepare_libffi.bat --- python3.8-3.8.6/PCbuild/prepare_libffi.bat 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PCbuild/prepare_libffi.bat 2020-12-21 16:25:24.000000000 +0000 @@ -22,10 +22,10 @@ echo. echo. echo.Available flags: -echo. -x64 build for x64 -echo. -x86 build for x86 -echo. -arm32 build for arm32 -echo. -arm64 build for arm64 +echo. -x64 enable x64 build +echo. -x86 enable x86 build +echo. -arm32 enable arm32 build +echo. -arm64 enable arm64 build echo. -? this help echo. --install-cygwin install cygwin to c:\cygwin exit /b 127 @@ -44,6 +44,7 @@ if "%1"=="" goto :CheckOptsDone if /I "%1"=="-x64" (set BUILD_X64=1) & shift & goto :CheckOpts if /I "%1"=="-x86" (set BUILD_X86=1) & shift & goto :CheckOpts +if /I "%1"=="-win32" (set BUILD_X86=1) & shift & goto :CheckOpts if /I "%1"=="-arm32" (set BUILD_ARM32=1) & shift & goto :CheckOpts if /I "%1"=="-arm64" (set BUILD_ARM64=1) & shift & goto :CheckOpts if /I "%1"=="-pdb" (set BUILD_PDB=-g) & shift & goto :CheckOpts @@ -67,9 +68,7 @@ if NOT DEFINED SH if exist c:\cygwin\bin\sh.exe set SH=c:\cygwin\bin\sh.exe if NOT DEFINED VCVARSALL ( - if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ( - set VCVARSALL="C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" - ) + for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64') DO @(set VCVARSALL="%%i\VC\Auxiliary\Build\vcvarsall.bat") ) if ^%VCVARSALL:~0,1% NEQ ^" SET VCVARSALL="%VCVARSALL%" diff -Nru python3.8-3.8.6/PCbuild/pyproject.props python3.8-3.8.7/PCbuild/pyproject.props --- python3.8-3.8.6/PCbuild/pyproject.props 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PCbuild/pyproject.props 2020-12-21 16:25:24.000000000 +0000 @@ -192,8 +192,8 @@ $(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot81)\bin\x86 $(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot)\bin\x86 $(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A@InstallationFolder)\Bin\ - <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificate)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /a /n "$(SigningCertificate)" /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d "Python $(PythonVersion)" - <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificateSha1)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /a /sha1 "$(SigningCertificateSha1)" /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d "Python $(PythonVersion)" + <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificate)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /a /n "$(SigningCertificate)" /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d "Python $(PythonVersion)" + <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificateSha1)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /a /sha1 "$(SigningCertificateSha1)" /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d "Python $(PythonVersion)" <_MakeCatCommand Condition="Exists($(SdkBinPath))">"$(SdkBinPath)\makecat.exe" diff -Nru python3.8-3.8.6/PCbuild/python.props python3.8-3.8.7/PCbuild/python.props --- python3.8-3.8.6/PCbuild/python.props 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PCbuild/python.props 2020-12-21 16:25:24.000000000 +0000 @@ -56,7 +56,7 @@ $(EXTERNALS_DIR) $([System.IO.Path]::GetFullPath(`$(PySourcePath)externals`)) $(ExternalsDir)\ - $(ExternalsDir)sqlite-3.32.3.0\ + $(ExternalsDir)sqlite-3.33.0.0\ $(ExternalsDir)bzip2-1.0.6\ $(ExternalsDir)xz-5.2.2\ $(ExternalsDir)libffi\ diff -Nru python3.8-3.8.6/PCbuild/readme.txt python3.8-3.8.7/PCbuild/readme.txt --- python3.8-3.8.6/PCbuild/readme.txt 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PCbuild/readme.txt 2020-12-21 16:25:24.000000000 +0000 @@ -184,7 +184,7 @@ again when building. _sqlite3 - Wraps SQLite 3.32.3.0, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.33.0, which is itself built by sqlite3.vcxproj Homepage: http://www.sqlite.org/ _tkinter diff -Nru python3.8-3.8.6/PCbuild/rt.bat python3.8-3.8.7/PCbuild/rt.bat --- python3.8-3.8.6/PCbuild/rt.bat 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/PCbuild/rt.bat 2020-12-21 16:25:24.000000000 +0000 @@ -6,8 +6,9 @@ rem -q "quick" -- normally the tests are run twice, the first time rem after deleting all the .pyc files reachable from Lib/. rem -q runs the tests just once, and without deleting .pyc files. -rem -x64 Run the 64-bit build of python (or python_d if -d was specified) -rem When omitted, uses %PREFIX% if set or the 32-bit build +rem -p or -win32, -x64, -arm32, -arm64 +rem Run the specified architecture of python (or python_d if -d +rem was specified). If omitted, uses %PREFIX% if set or 32-bit. rem All leading instances of these switches are shifted off, and rem whatever remains (up to 9 arguments) is passed to regrtest.py. rem For example, @@ -38,9 +39,11 @@ if "%1"=="-O" (set dashO=-O) & shift & goto CheckOpts if "%1"=="-q" (set qmode=yes) & shift & goto CheckOpts if "%1"=="-d" (set suffix=_d) & shift & goto CheckOpts +if "%1"=="-win32" (set prefix=%pcbuild%win32) & shift & goto CheckOpts if "%1"=="-x64" (set prefix=%pcbuild%amd64) & shift & goto CheckOpts if "%1"=="-arm64" (set prefix=%pcbuild%arm64) & shift & goto CheckOpts if "%1"=="-arm32" (set prefix=%pcbuild%arm32) & shift & goto CheckOpts +if "%1"=="-p" (call :SetPlatform %~2) & shift & shift & goto CheckOpts if NOT "%1"=="" (set regrtestargs=%regrtestargs% %1) & shift & goto CheckOpts if not defined prefix set prefix=%pcbuild%win32 @@ -60,6 +63,15 @@ echo About to run again without deleting .pyc first: pause +goto Qmode + +:SetPlatform +if /I %1 EQU Win32 (set prefix=%pcbuild%win32) & exit /B 0 +if /I %1 EQU x64 (set prefix=%pcbuild%amd64) & exit /B 0 +if /I %1 EQU ARM64 (set prefix=%pcbuild%arm64) & exit /B 0 +if /I %1 EQU ARM (set prefix=%pcbuild%arm32) & exit /B 0 +echo Invalid platform "%1" +exit /B 1 :Qmode echo on diff -Nru python3.8-3.8.6/Python/bltinmodule.c python3.8-3.8.7/Python/bltinmodule.c --- python3.8-3.8.6/Python/bltinmodule.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Python/bltinmodule.c 2020-12-21 16:25:24.000000000 +0000 @@ -4,6 +4,7 @@ #include #include "ast.h" #undef Yield /* undefine macro conflicting with */ +#include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_pystate.h" #include "pycore_tupleobject.h" @@ -2618,6 +2619,11 @@ PyTuple_SET_ITEM(result, i, item); Py_DECREF(olditem); } + // bpo-42536: The GC may have untracked this result tuple. Since we're + // recycling it, make sure it's tracked again: + if (!_PyObject_GC_IS_TRACKED(result)) { + _PyObject_GC_TRACK(result); + } } else { result = PyTuple_New(tuplesize); if (result == NULL) diff -Nru python3.8-3.8.6/Python/dynload_aix.c python3.8-3.8.7/Python/dynload_aix.c --- python3.8-3.8.6/Python/dynload_aix.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Python/dynload_aix.c 2020-12-21 16:25:24.000000000 +0000 @@ -144,10 +144,16 @@ ERRBUF_APPEND(message[i]); ERRBUF_APPEND("\n"); } - errbuf[strlen(errbuf)-1] = '\0'; /* trim off last newline */ - pathname_ob = PyUnicode_FromString(pathname); - errbuf_ob = PyUnicode_FromString(errbuf); - PyErr_SetImportError(errbuf_ob, NULL, pathname); + /* Subtract 1 from the length to trim off trailing newline */ + errbuf_ob = PyUnicode_DecodeLocaleAndSize(errbuf, strlen(errbuf)-1, "surrogateescape"); + if (errbuf_ob == NULL) + return; + pathname_ob = PyUnicode_DecodeFSDefault(pathname); + if (pathname_ob == NULL) { + Py_DECREF(errbuf_ob); + return; + } + PyErr_SetImportError(errbuf_ob, NULL, pathname_ob); Py_DECREF(pathname_ob); Py_DECREF(errbuf_ob); return; diff -Nru python3.8-3.8.6/Python/dynload_hpux.c python3.8-3.8.7/Python/dynload_hpux.c --- python3.8-3.8.6/Python/dynload_hpux.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Python/dynload_hpux.c 2020-12-21 16:25:24.000000000 +0000 @@ -37,9 +37,20 @@ char buf[256]; PyOS_snprintf(buf, sizeof(buf), "Failed to load %.200s", pathname); - PyObject *buf_ob = PyUnicode_FromString(buf); + PyObject *buf_ob = PyUnicode_DecodeFSDefault(buf); + if (buf_ob == NULL) + return NULL; PyObject *shortname_ob = PyUnicode_FromString(shortname); - PyObject *pathname_ob = PyUnicode_FromString(pathname); + if (shortname_ob == NULL) { + Py_DECREF(buf_ob); + return NULL; + } + PyObject *pathname_ob = PyUnicode_DecodeFSDefault(pathname); + if (pathname_ob == NULL) { + Py_DECREF(buf_ob); + Py_DECREF(shortname_ob); + return NULL; + } PyErr_SetImportError(buf_ob, shortname_ob, pathname_ob); Py_DECREF(buf_ob); Py_DECREF(shortname_ob); diff -Nru python3.8-3.8.6/Python/dynload_shlib.c python3.8-3.8.7/Python/dynload_shlib.c --- python3.8-3.8.6/Python/dynload_shlib.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Python/dynload_shlib.c 2020-12-21 16:25:24.000000000 +0000 @@ -105,7 +105,7 @@ const char *error = dlerror(); if (error == NULL) error = "unknown dlopen() error"; - error_ob = PyUnicode_FromString(error); + error_ob = PyUnicode_DecodeLocale(error, "surrogateescape"); if (error_ob == NULL) return NULL; mod_name = PyUnicode_FromString(shortname); @@ -113,7 +113,7 @@ Py_DECREF(error_ob); return NULL; } - path = PyUnicode_FromString(pathname); + path = PyUnicode_DecodeFSDefault(pathname); if (path == NULL) { Py_DECREF(error_ob); Py_DECREF(mod_name); diff -Nru python3.8-3.8.6/Python/fileutils.c python3.8-3.8.7/Python/fileutils.c --- python3.8-3.8.6/Python/fileutils.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Python/fileutils.c 2020-12-21 16:25:24.000000000 +0000 @@ -1933,6 +1933,7 @@ assert(decimal_point != NULL); assert(thousands_sep != NULL); +#ifndef MS_WINDOWS int change_locale = 0; if ((strlen(lc->decimal_point) > 1 || ((unsigned char)lc->decimal_point[0]) > 127)) { change_locale = 1; @@ -1971,14 +1972,20 @@ } } +#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL) +#else /* MS_WINDOWS */ +/* Use _W_* fields of Windows strcut lconv */ +#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1) +#endif /* MS_WINDOWS */ + int res = -1; - *decimal_point = PyUnicode_DecodeLocale(lc->decimal_point, NULL); + *decimal_point = GET_LOCALE_STRING(decimal_point); if (*decimal_point == NULL) { goto done; } - *thousands_sep = PyUnicode_DecodeLocale(lc->thousands_sep, NULL); + *thousands_sep = GET_LOCALE_STRING(thousands_sep); if (*thousands_sep == NULL) { goto done; } @@ -1986,9 +1993,13 @@ res = 0; done: +#ifndef MS_WINDOWS if (loc != NULL) { setlocale(LC_CTYPE, oldloc); } PyMem_Free(oldloc); +#endif return res; + +#undef GET_LOCALE_STRING } diff -Nru python3.8-3.8.6/Python/initconfig.c python3.8-3.8.7/Python/initconfig.c --- python3.8-3.8.6/Python/initconfig.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Python/initconfig.c 2020-12-21 16:25:24.000000000 +0000 @@ -2593,7 +2593,7 @@ if (ch == L'\'') { PySys_WriteStderr("\\'"); } else if (0x20 <= ch && ch < 0x7f) { - PySys_WriteStderr("%lc", ch); + PySys_WriteStderr("%c", ch); } else if (ch <= 0xff) { PySys_WriteStderr("\\x%02x", ch); diff -Nru python3.8-3.8.6/Python/pylifecycle.c python3.8-3.8.7/Python/pylifecycle.c --- python3.8-3.8.6/Python/pylifecycle.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Python/pylifecycle.c 2020-12-21 16:25:24.000000000 +0000 @@ -63,7 +63,6 @@ static PyStatus add_main_module(PyInterpreterState *interp); static PyStatus init_import_size(void); static PyStatus init_sys_streams(PyInterpreterState *interp); -static PyStatus init_signals(void); static void call_py_exitfuncs(PyInterpreterState *); static void wait_for_thread_shutdown(void); static void call_ll_exitfuncs(_PyRuntimeState *runtime); @@ -952,11 +951,8 @@ return status; } - if (config->install_signal_handlers) { - status = init_signals(); - if (_PyStatus_EXCEPTION(status)) { - return status; - } + if (_PySignal_Init(config->install_signal_handlers) < 0) { + return _PyStatus_ERR("can't initialize signals"); } if (_PyTraceMalloc_Init(config->tracemalloc) < 0) { @@ -2299,25 +2295,6 @@ exit(sts); } -static PyStatus -init_signals(void) -{ -#ifdef SIGPIPE - PyOS_setsig(SIGPIPE, SIG_IGN); -#endif -#ifdef SIGXFZ - PyOS_setsig(SIGXFZ, SIG_IGN); -#endif -#ifdef SIGXFSZ - PyOS_setsig(SIGXFSZ, SIG_IGN); -#endif - PyOS_InitInterrupts(); /* May imply init_signals() */ - if (PyErr_Occurred()) { - return _PyStatus_ERR("can't import signal"); - } - return _PyStatus_OK(); -} - /* Restore signals that the interpreter has called SIG_IGN on to SIG_DFL. * diff -Nru python3.8-3.8.6/Python/pythonrun.c python3.8-3.8.7/Python/pythonrun.c --- python3.8-3.8.6/Python/pythonrun.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Python/pythonrun.c 2020-12-21 16:25:24.000000000 +0000 @@ -63,11 +63,15 @@ static void flush_io(void); static PyObject *run_mod(mod_ty, PyObject *, PyObject *, PyObject *, PyCompilerFlags *, PyArena *); -static PyObject *run_pyc_file(FILE *, const char *, PyObject *, PyObject *, +static PyObject *run_pyc_file(FILE *, PyObject *, PyObject *, PyCompilerFlags *); static void err_input(perrdetail *); static void err_free(perrdetail *); static int PyRun_InteractiveOneObjectEx(FILE *, PyObject *, PyCompilerFlags *); +static PyObject* pyrun_file(FILE *fp, PyObject *filename, int start, + PyObject *globals, PyObject *locals, int closeit, + PyCompilerFlags *flags); + /* Parse input from a file and execute it */ int @@ -300,82 +304,89 @@ the file type, and, if we may close it, at the first few bytes. */ static int -maybe_pyc_file(FILE *fp, const char* filename, const char* ext, int closeit) +maybe_pyc_file(FILE *fp, PyObject *filename, int closeit) { - if (strcmp(ext, ".pyc") == 0) + PyObject *ext = PyUnicode_FromString(".pyc"); + if (ext == NULL) { + return -1; + } + Py_ssize_t endswith = PyUnicode_Tailmatch(filename, ext, 0, PY_SSIZE_T_MAX, +1); + Py_DECREF(ext); + if (endswith) { return 1; + } /* Only look into the file if we are allowed to close it, since it then should also be seekable. */ - if (closeit) { - /* Read only two bytes of the magic. If the file was opened in - text mode, the bytes 3 and 4 of the magic (\r\n) might not - be read as they are on disk. */ - unsigned int halfmagic = PyImport_GetMagicNumber() & 0xFFFF; - unsigned char buf[2]; - /* Mess: In case of -x, the stream is NOT at its start now, - and ungetc() was used to push back the first newline, - which makes the current stream position formally undefined, - and a x-platform nightmare. - Unfortunately, we have no direct way to know whether -x - was specified. So we use a terrible hack: if the current - stream position is not 0, we assume -x was specified, and - give up. Bug 132850 on SourceForge spells out the - hopelessness of trying anything else (fseek and ftell - don't work predictably x-platform for text-mode files). - */ - int ispyc = 0; - if (ftell(fp) == 0) { - if (fread(buf, 1, 2, fp) == 2 && - ((unsigned int)buf[1]<<8 | buf[0]) == halfmagic) - ispyc = 1; - rewind(fp); - } - return ispyc; + if (!closeit) { + return 0; } - return 0; + + /* Read only two bytes of the magic. If the file was opened in + text mode, the bytes 3 and 4 of the magic (\r\n) might not + be read as they are on disk. */ + unsigned int halfmagic = PyImport_GetMagicNumber() & 0xFFFF; + unsigned char buf[2]; + /* Mess: In case of -x, the stream is NOT at its start now, + and ungetc() was used to push back the first newline, + which makes the current stream position formally undefined, + and a x-platform nightmare. + Unfortunately, we have no direct way to know whether -x + was specified. So we use a terrible hack: if the current + stream position is not 0, we assume -x was specified, and + give up. Bug 132850 on SourceForge spells out the + hopelessness of trying anything else (fseek and ftell + don't work predictably x-platform for text-mode files). + */ + int ispyc = 0; + if (ftell(fp) == 0) { + if (fread(buf, 1, 2, fp) == 2 && + ((unsigned int)buf[1]<<8 | buf[0]) == halfmagic) + ispyc = 1; + rewind(fp); + } + return ispyc; } + static int -set_main_loader(PyObject *d, const char *filename, const char *loader_name) +set_main_loader(PyObject *d, PyObject *filename, const char *loader_name) { - PyObject *filename_obj, *bootstrap, *loader_type = NULL, *loader; - int result = 0; - - filename_obj = PyUnicode_DecodeFSDefault(filename); - if (filename_obj == NULL) - return -1; PyInterpreterState *interp = _PyInterpreterState_Get(); - bootstrap = PyObject_GetAttrString(interp->importlib, - "_bootstrap_external"); - if (bootstrap != NULL) { - loader_type = PyObject_GetAttrString(bootstrap, loader_name); - Py_DECREF(bootstrap); + PyObject *bootstrap = PyObject_GetAttrString(interp->importlib, + "_bootstrap_external"); + if (bootstrap == NULL) { + return -1; } + + PyObject *loader_type = PyObject_GetAttrString(bootstrap, loader_name); + Py_DECREF(bootstrap); if (loader_type == NULL) { - Py_DECREF(filename_obj); return -1; } - loader = PyObject_CallFunction(loader_type, "sN", "__main__", filename_obj); + + PyObject *loader = PyObject_CallFunction(loader_type, + "sO", "__main__", filename); Py_DECREF(loader_type); if (loader == NULL) { return -1; } + if (PyDict_SetItemString(d, "__loader__", loader) < 0) { - result = -1; + Py_DECREF(loader); + return -1; } Py_DECREF(loader); - return result; + return 0; } -int -PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, - PyCompilerFlags *flags) + +static int +pyrun_simple_file(FILE *fp, PyObject *filename, int closeit, + PyCompilerFlags *flags) { PyObject *m, *d, *v; - const char *ext; int set_file_name = 0, ret = -1; - size_t len; m = PyImport_AddModule("__main__"); if (m == NULL) @@ -383,29 +394,29 @@ Py_INCREF(m); d = PyModule_GetDict(m); if (PyDict_GetItemString(d, "__file__") == NULL) { - PyObject *f; - f = PyUnicode_DecodeFSDefault(filename); - if (f == NULL) - goto done; - if (PyDict_SetItemString(d, "__file__", f) < 0) { - Py_DECREF(f); + if (PyDict_SetItemString(d, "__file__", filename) < 0) { goto done; } if (PyDict_SetItemString(d, "__cached__", Py_None) < 0) { - Py_DECREF(f); goto done; } set_file_name = 1; - Py_DECREF(f); } - len = strlen(filename); - ext = filename + len - (len > 4 ? 4 : 0); - if (maybe_pyc_file(fp, filename, ext, closeit)) { + + int pyc = maybe_pyc_file(fp, filename, closeit); + if (pyc < 0) { + goto done; + } + + if (pyc) { FILE *pyc_fp; /* Try to run a pyc file. First, re-open in binary */ - if (closeit) + if (closeit) { fclose(fp); - if ((pyc_fp = _Py_fopen(filename, "rb")) == NULL) { + } + + pyc_fp = _Py_fopen_obj(filename, "rb"); + if (pyc_fp == NULL) { fprintf(stderr, "python: Can't reopen .pyc file\n"); goto done; } @@ -416,17 +427,17 @@ fclose(pyc_fp); goto done; } - v = run_pyc_file(pyc_fp, filename, d, d, flags); + v = run_pyc_file(pyc_fp, d, d, flags); } else { /* When running from stdin, leave __main__.__loader__ alone */ - if (strcmp(filename, "") != 0 && + if (PyUnicode_CompareWithASCIIString(filename, "") != 0 && set_main_loader(d, filename, "SourceFileLoader") < 0) { fprintf(stderr, "python: failed to set __main__.__loader__\n"); ret = -1; goto done; } - v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d, - closeit, flags); + v = pyrun_file(fp, filename, Py_file_input, d, d, + closeit, flags); } flush_io(); if (v == NULL) { @@ -449,6 +460,21 @@ return ret; } + +int +PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, + PyCompilerFlags *flags) +{ + PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename); + if (filename_obj == NULL) { + return -1; + } + int res = pyrun_simple_file(fp, filename_obj, closeit, flags); + Py_DECREF(filename_obj); + return res; +} + + int PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags) { @@ -1036,39 +1062,53 @@ return ret; } -PyObject * -PyRun_FileExFlags(FILE *fp, const char *filename_str, int start, PyObject *globals, - PyObject *locals, int closeit, PyCompilerFlags *flags) -{ - PyObject *ret = NULL; - mod_ty mod; - PyArena *arena = NULL; - PyObject *filename; - filename = PyUnicode_DecodeFSDefault(filename_str); - if (filename == NULL) - goto exit; - - arena = PyArena_New(); - if (arena == NULL) - goto exit; +static PyObject * +pyrun_file(FILE *fp, PyObject *filename, int start, PyObject *globals, + PyObject *locals, int closeit, PyCompilerFlags *flags) +{ + PyArena *arena = PyArena_New(); + if (arena == NULL) { + return NULL; + } + mod_ty mod; mod = PyParser_ASTFromFileObject(fp, filename, NULL, start, 0, 0, flags, NULL, arena); - if (closeit) + if (closeit) { fclose(fp); - if (mod == NULL) { - goto exit; } - ret = run_mod(mod, filename, globals, locals, flags, arena); -exit: - Py_XDECREF(filename); - if (arena != NULL) - PyArena_Free(arena); + PyObject *ret; + if (mod != NULL) { + ret = run_mod(mod, filename, globals, locals, flags, arena); + } + else { + ret = NULL; + } + PyArena_Free(arena); + return ret; } + +PyObject * +PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals, + PyObject *locals, int closeit, PyCompilerFlags *flags) +{ + PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename); + if (filename_obj == NULL) { + return NULL; + } + + PyObject *res = pyrun_file(fp, filename_obj, start, globals, + locals, closeit, flags); + Py_DECREF(filename_obj); + return res; + +} + + static void flush_io(void) { @@ -1150,8 +1190,8 @@ } static PyObject * -run_pyc_file(FILE *fp, const char *filename, PyObject *globals, - PyObject *locals, PyCompilerFlags *flags) +run_pyc_file(FILE *fp, PyObject *globals, PyObject *locals, + PyCompilerFlags *flags) { PyCodeObject *co; PyObject *v; diff -Nru python3.8-3.8.6/Python/traceback.c python3.8-3.8.7/Python/traceback.c --- python3.8-3.8.6/Python/traceback.c 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Python/traceback.c 2020-12-21 16:25:24.000000000 +0000 @@ -623,7 +623,8 @@ return err; } -/* Reverse a string. For example, "abcd" becomes "dcba". +/* Format an integer in range [0; 0xffffffff] to decimal and write it + into the file fd. This function is signal safe. */ diff -Nru python3.8-3.8.6/README.rst python3.8-3.8.7/README.rst --- python3.8-3.8.6/README.rst 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/README.rst 2020-12-21 16:25:24.000000000 +0000 @@ -1,4 +1,4 @@ -This is Python version 3.8.6 +This is Python version 3.8.7 ============================ .. image:: https://travis-ci.org/python/cpython.svg?branch=3.8 diff -Nru python3.8-3.8.6/Tools/clinic/clinic.py python3.8-3.8.7/Tools/clinic/clinic.py --- python3.8-3.8.6/Tools/clinic/clinic.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Tools/clinic/clinic.py 2020-12-21 16:25:24.000000000 +0000 @@ -1756,6 +1756,30 @@ # The callable should not call builtins.print. return_converters = {} + +def write_file(filename, new_contents): + try: + with open(filename, 'r', encoding="utf-8") as fp: + old_contents = fp.read() + + if old_contents == new_contents: + # no change: avoid modifying the file modification time + return + except FileNotFoundError: + pass + + # Atomic write using a temporary file and os.replace() + filename_new = "{}.new".format(filename) + with open(filename_new, "w", encoding="utf-8") as fp: + fp.write(new_contents) + + try: + os.replace(filename_new, filename) + except: + os.unlink(filename_new) + raise + + clinic = None class Clinic: @@ -1802,7 +1826,7 @@ """ - def __init__(self, language, printer=None, *, force=False, verify=True, filename=None): + def __init__(self, language, printer=None, *, verify=True, filename=None): # maps strings to Parser objects. # (instantiated from the "parsers" global.) self.parsers = {} @@ -1811,7 +1835,6 @@ fail("Custom printers are broken right now") self.printer = printer or BlockPrinter(language) self.verify = verify - self.force = force self.filename = filename self.modules = collections.OrderedDict() self.classes = collections.OrderedDict() @@ -1944,8 +1967,7 @@ block.input = 'preserve\n' printer_2 = BlockPrinter(self.language) printer_2.print_block(block) - with open(destination.filename, "wt") as f: - f.write(printer_2.f.getvalue()) + write_file(destination.filename, printer_2.f.getvalue()) continue text = printer.f.getvalue() @@ -1997,7 +2019,10 @@ return module, cls -def parse_file(filename, *, force=False, verify=True, output=None, encoding='utf-8'): +def parse_file(filename, *, verify=True, output=None): + if not output: + output = filename + extension = os.path.splitext(filename)[1][1:] if not extension: fail("Can't extract file type for file " + repr(filename)) @@ -2007,7 +2032,7 @@ except KeyError: fail("Can't identify file type for file " + repr(filename)) - with open(filename, 'r', encoding=encoding) as f: + with open(filename, 'r', encoding="utf-8") as f: raw = f.read() # exit quickly if there are no clinic markers in the file @@ -2015,19 +2040,10 @@ if not find_start_re.search(raw): return - clinic = Clinic(language, force=force, verify=verify, filename=filename) + clinic = Clinic(language, verify=verify, filename=filename) cooked = clinic.parse(raw) - if (cooked == raw) and not force: - return - - directory = os.path.dirname(filename) or '.' - with tempfile.TemporaryDirectory(prefix="clinic", dir=directory) as tmpdir: - bytes = cooked.encode(encoding) - tmpfilename = os.path.join(tmpdir, os.path.basename(filename)) - with open(tmpfilename, "wb") as f: - f.write(bytes) - os.replace(tmpfilename, output or filename) + write_file(output, cooked) def compute_checksum(input, length=None): @@ -5033,7 +5049,7 @@ path = os.path.join(root, filename) if ns.verbose: print(path) - parse_file(path, force=ns.force, verify=not ns.force) + parse_file(path, verify=not ns.force) return if not ns.filename: @@ -5049,7 +5065,7 @@ for filename in ns.filename: if ns.verbose: print(filename) - parse_file(filename, output=ns.output, force=ns.force, verify=not ns.force) + parse_file(filename, output=ns.output, verify=not ns.force) if __name__ == "__main__": diff -Nru python3.8-3.8.6/Tools/freeze/freeze.py python3.8-3.8.7/Tools/freeze/freeze.py --- python3.8-3.8.6/Tools/freeze/freeze.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Tools/freeze/freeze.py 2020-12-21 16:25:24.000000000 +0000 @@ -93,6 +93,7 @@ import getopt import os import sys +import sysconfig # Import the freeze-private modules @@ -226,7 +227,7 @@ extensions_c = 'frozen_extensions.c' if ishome: print("(Using Python source directory)") - binlib = exec_prefix + configdir = exec_prefix incldir = os.path.join(prefix, 'Include') config_h_dir = exec_prefix config_c_in = os.path.join(prefix, 'Modules', 'config.c.in') @@ -235,22 +236,21 @@ if win: frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c') else: - binlib = os.path.join(exec_prefix, - 'lib', 'python%s' % version, - 'config-%s' % flagged_version) + configdir = sysconfig.get_config_var('LIBPL') incldir = os.path.join(prefix, 'include', 'python%s' % flagged_version) config_h_dir = os.path.join(exec_prefix, 'include', 'python%s' % flagged_version) - config_c_in = os.path.join(binlib, 'config.c.in') - frozenmain_c = os.path.join(binlib, 'frozenmain.c') - makefile_in = os.path.join(binlib, 'Makefile') - frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c') + config_c_in = os.path.join(configdir, 'config.c.in') + frozenmain_c = os.path.join(configdir, 'frozenmain.c') + makefile_in = os.path.join(configdir, 'Makefile') + frozendllmain_c = os.path.join(configdir, 'frozen_dllmain.c') + libdir = sysconfig.get_config_var('LIBDIR') supp_sources = [] defines = [] includes = ['-I' + incldir, '-I' + config_h_dir] # sanity check of directories and files - check_dirs = [prefix, exec_prefix, binlib, incldir] + check_dirs = [prefix, exec_prefix, configdir, incldir] if not win: # These are not directories on Windows. check_dirs = check_dirs + extensions @@ -457,7 +457,7 @@ cflags = ['$(OPT)'] cppflags = defines + includes - libs = [os.path.join(binlib, '$(LDLIBRARY)')] + libs = [os.path.join(libdir, '$(LDLIBRARY)')] somevars = {} if os.path.exists(makefile_in): diff -Nru python3.8-3.8.6/Tools/msi/sdktools.psm1 python3.8-3.8.7/Tools/msi/sdktools.psm1 --- python3.8-3.8.6/Tools/msi/sdktools.psm1 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Tools/msi/sdktools.psm1 2020-12-21 16:25:24.000000000 +0000 @@ -37,11 +37,11 @@ foreach ($a in $files) { if ($certsha1) { - SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + SignTool sign /sha1 $certsha1 /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d $description $a } elseif ($certname) { - SignTool sign /a /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + SignTool sign /a /n $certname /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d $description $a } elseif ($certfile) { - SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a + SignTool sign /f $certfile /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d $description $a } } } diff -Nru python3.8-3.8.6/Tools/msi/test/test_files.wxs python3.8-3.8.7/Tools/msi/test/test_files.wxs --- python3.8-3.8.6/Tools/msi/test/test_files.wxs 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Tools/msi/test/test_files.wxs 2020-12-21 16:25:24.000000000 +0000 @@ -1,6 +1,6 @@ - + - + diff -Nru python3.8-3.8.6/Tools/pynche/PyncheWidget.py python3.8-3.8.7/Tools/pynche/PyncheWidget.py --- python3.8-3.8.6/Tools/pynche/PyncheWidget.py 2020-09-23 12:36:32.000000000 +0000 +++ python3.8-3.8.7/Tools/pynche/PyncheWidget.py 2020-12-21 16:25:24.000000000 +0000 @@ -36,15 +36,11 @@ else: # Is there already a default root for Tk, say because we're # running under Guido's IDE? :-) Two conditions say no, either the - # import fails or _default_root is None. - tkroot = None - try: - from Tkinter import _default_root - tkroot = self.__tkroot = _default_root - except ImportError: - pass + # _default_root is None or it is unset. + tkroot = getattr(tkinter, '_default_root', None) if not tkroot: - tkroot = self.__tkroot = Tk(className='Pynche') + tkroot = Tk(className='Pynche') + self.__tkroot = tkroot # but this isn't our top level widget, so make it invisible tkroot.withdraw() # create the menubar